uart_open 函数用于打开指定的串口,它完成了串口设备回调函数设置、串口设备的开启和事件的初始化。源码如下:
rt_err_tuart_open(constchar*name)
{
rt_err_tres;
/* 查找系统中的串口设备 */
uart_device=rt_device_find(name);
/* 查找到设备后将其打开 */
if(uart_device!=RT_NULL)
{
res=rt_device_set_rx_indicate(uart_device,uart_intput);
/* 检查返回值 */
if(res!=RT_EOK)
{
rt_kprintf("set %s rx indicate error.%dn",name,res);
return-RT_ERROR;
}
/* 打开设备,以可读写、中断方式 */
res=rt_device_open(uart_device,RT_DEVICE_OFLAG_RDWR|
RT_DEVICE_FLAG_INT_RX);
/* 检查返回值 */
if(res!=RT_EOK)
{
rt_kprintf("open %s device error.%dn",name,res);
return-RT_ERROR;
}
}
else
{
rt_kprintf("can't find %s device.n",name);
return-RT_ERROR;
}
/* 初始化事件对象 */
rt_event_init(&event,"event",RT_IPC_FLAG_FIFO);
returnRT_EOK;
}
简要流程如下:
uart_open 函数使用到的设备操作接口有:rt_device_find、rt_device_set_rx_indicate、rt_device_open。uart_open 函数首先调用 rt_device_find 根据串口名字获得串口句柄,保存在静态全局变量 uart_device 中,后面关于串口的操作都是基于这个串口句柄。这里的名字是在 drv_usart.c 中调用注册函数 rt_hw_serial_register 决定的,该函数将串口硬件驱动和 RT-Thread 设备管理框架联系起来了。
/* register UART2 device */
rt_hw_serial_register(&serial2,
"uart2",
RT_DEVICE_FLAG_RDWR|RT_DEVICE_FLAG_INT_RX,
uart);
接着调用 rt_device_set_rx_indicate 设置串口接收中断的回调函数。最后调用 rt_device_open 以可读写、中断接收方式打开串口。它的第二个参数为标志,与上面提到的注册函数 rt_hw_serial_register 保持一致即可。
rt_device_open(uart_device,RT_DEVICE_OFLAG_RDWR|RT_DEVICE_FLAG_INT_RX);
最后调用 rt_event_init 初始化事件。RT-Thread 中默认开启了自动初始化机制,因此用户不需要在应用程序中手动调用串口的初始化函数(drv_usart.c 中的 INIT_BOARD_EXPORT 实现了自动初始化)。用户实现的由宏 RT_USING_UARTx 选定的串口硬件驱动将自动关联到 RT-Thread 中来(drv_usart.c 中的 rt_hw_serial_register 实现了串口硬件注册)。
串口发送
uart_putchar 函数用于发送 1 字节数据。uart_putchar 函数实际上调用的是 rt_device_write 来发送一个字节,并采取了防出错处理,即检查返回值,失败则重新发送,并限定了超时。源码如下:
voiduart_putchar(constrt_uint8_tc)
{
rt_size_tlen=0;
rt_uint32_ttimeout=0;
do
{
len=rt_device_write(uart_device,0,&c,1);
timeout++;
}
while(len!=1&&timeout<500);
}
调用 uart_putchar 发生的数据流向示意图如下:
应用程序调用 uart_putchar 时,实际调用关系为:rt_device_write ==> rt_serial_write ==> drv_putc,最终数据通过串口数据寄存器发送出去。
串口接收
uart_getchar 函数用于接收数据,uart_getchar 函数的实现采用了串口接收中断回调机制和事件用于异步
通信,它具有阻塞特性。相关源码如下:
/* 串口接收事件标志 */
#defineUART_RX_EVENT(1<<0)
/* 事件控制块 */
staticstructrt_eventevent;
/* 设备句柄 */
staticrt_device_tuart_device=RT_NULL;
/* 回调函数 */
staticrt_err_tuart_intput(rt_device_tdev,rt_size_tsize)
{
/* 发送事件 */
rt_event_send(&event,UART_RX_EVENT);
returnRT_EOK;
}
rt_uint8_tuart_getchar(void)
{
rt_uint32_te;
rt_uint8_tch;
/* 读取 1 字节数据 */
while(rt_device_read(uart_device,0,&ch,1)!=1)
{
/* 接收事件 */
rt_event_recv(&event,UART_RX_EVENT,RT_EVENT_FLAG_AND|
RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,&e);
}
returnch;
}
uart_getchar 函数内部有一个 while() 循环,先调用 rt_device_read 去读取一字节数据,没有读到则调用 rt_event_recv 等待事件标志,挂起调用线程;串口接收到一字节数据后产生中断,调用回调函数 uart_intput,回调函数里面调用了 rt_event_send 发送事件标志以唤醒等待该 event 事件的线程。调用 uart_getchar 函数发生的数据流向示意图如下:
应用程序调用 uart_getchar 时,实际调用关系为:rt_device_read ==> rt_serial_read ==> drv_getc,最终从串口数据寄存器读取到数据。
I/O 设备管理框架和串口的联系
RT-Thread 自动初始化功能依次调用 hw_usart_init ==> rt_hw_serial_register ==> rt_device_register 完成了串口硬件初始化,从而将设备操作接口和串口驱动联系起来,我们就可以使用设备操作接口来对串口进行操作。