首先感谢发烧友社区提供的机会,感谢RT-Thread社区。
一、I/O设备模型
RT-Thread 提供了一套简单的 I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分成三层,从上到下分别是 I/O 设备管理层、设备驱动框架层、设备驱动层。
应用程序通过 I/O 设备管理接口来访问硬件设备,当设备驱动实现后,应用程序就可以访问该硬件。
模型提供如下操作函数:
rt_err_t rt_device_init (rt_device_t dev ) ;
rt_device_t rt_device_find(const char* name);
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflags);
rt_err_t rt_device_close(rt_device_t dev);
rt_err_t rt_device_control(rt_device_t dev, rt_uint8_t cmd, void* arg);
二、UART设备简介
UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。是在应用程序开发过程中使用频率最高的数据总线。
- 起始位:表示数据传输的开始,电平逻辑为 “0” 。
- 数据位:可能值有 5、6、7、8、9,表示传输这几个 bit 位数据。一般取值为 8,一个 ASCII 字符值为 8 位。
- 奇偶校验位:用于接收方对接收到的数据进行校验,校验 “1” 的位数为偶数 (偶校验) 或奇数(奇校验),以此来校验数据传送的正确性,使用时不需要此位也可以。
- 停止位: 表示一帧数据的结束。电平逻辑为 “1”。
- 波特率:串口通信时的速率,它用单位时间内传输的二进制代码的有效位 (bit) 数来表示,其单位为每秒比特数 bit/s(bps)。
三、UART数据R/W
一个典型的串口操作流程图如下,首先查找串口设备获取设备句柄,初始化回调函数发送使用的信号量,然后以读写及中断接收方式打开串口设备,1. 设置串口设备的接收回调函数,之后发送字符串,并创建读取数据线程。
本次介绍UART数据的R/W,基于发送数据介绍串口的相关功能:
rt_size_t rt_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size);
顾名思义,该函数会把缓冲区 buffer 中的数据写入到设备 dev 中,写入数据的大小是 size。
#define SAMPLE_UART_NAME "uart1"
static rt_device_t serial;
char str[] = "hello RT-Thread!\r\n";
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
serial = rt_device_find(SAMPLE_UART_NAME);
rt_device_open(serial, RT_DEVICE_FLAG_RX_NON_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING);
rt_device_write(serial, 0, str, (sizeof(str) - 1));
基于读取数据介绍串口的相关功能:
rt_size_t rt_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size);
该函数从指定设备的缓冲区 buffer 中读取大小为 size的数据。
static rt_device_t serial;
static struct rt_semaphore rx_sem;
static void serial_thread_entry(void *parameter)
{
char ch;
while (1)
{
while (rt_device_read(serial, -1, &ch, 1) != 1)
{
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
ch = ch + 1;
rt_device_write(serial, 0, &ch, 1);
}
}
四、测试案例
基于上述内容,本次创建了UART1 和 UART2两个串口,一般串口创建直接在系统配置中选择即可。
以国民技术的MSP为例:
void n32_msp_usart_init(void *Instance)
{
GPIO_InitType GPIO_InitCtlStruct;
USART_Module *USARTx = (USART_Module *)Instance;
GPIO_InitStruct(&GPIO_InitCtlStruct);
GPIO_InitCtlStruct.GPIO_Speed = GPIO_Speed_50MHz;
#ifdef BSP_USING_UART1
if(USART1 == USARTx)
{
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_USART1, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitCtlStruct.Pin = GPIO_PIN_9;
GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitCtlStruct.Pin = GPIO_PIN_10;
GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct);
}
#endif
#ifdef BSP_USING_UART2
if(USART2 == USARTx)
{
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitCtlStruct.Pin = GPIO_PIN_2;
GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitCtlStruct.Pin = GPIO_PIN_3;
GPIO_InitPeripheral(GPIOA, &GPIO_InitCtlStruct);
}
#endif
#ifdef BSP_USING_UART3
if(USART3 == USARTx)
{
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART3, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitCtlStruct.Pin = GPIO_PIN_10;
GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitCtlStruct.Pin = GPIO_PIN_11;
GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct);
}
#endif
#ifdef BSP_USING_UART4
if(UART4 == USARTx)
{
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_UART4, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitCtlStruct.Pin = GPIO_PIN_10;
GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct);
GPIO_InitCtlStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitCtlStruct.Pin = GPIO_PIN_11;
GPIO_InitPeripheral(GPIOB, &GPIO_InitCtlStruct);
}
#endif
}
#endif
RT-Thread启动后,msh中list_device即可查看: