本期来分析一下串口接受不定长度数据的源码,这个demo也在手册里面,可以去手册上结合着API说明观看。先把代码粘出来,后面对重点代码进行分析。
/*
* 程序清单:这是一个串口设备接收不定长数据的示例代码
* 例程导出了 uart_dma_sample 命令到控制终端
* 命令调用格式:uart_dma_sample uart2
* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备
* 程序功能:通过串口 uart2 输出字符串"hello RT-Thread!",并通过串口 uart2 输入一串字符(不定长),再通过数据解析后,使用控制台显示有效数据。
*/
#include <rtthread.h>
#define SAMPLE_UART_NAME "uart2"
#define DATA_CMD_END '\r' /* 结束位设置为 \r,即回车符 */
#define ONE_DATA_MAXLEN 20 /* 不定长数据的最大长度 */
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
/* 接收数据回调函数 */
static rt_err_t uart_rx_ind(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
if (size > 0)
{
rt_sem_release(&rx_sem);
}
return RT_EOK;
}
static char uart_sample_get_char(void)
{
char ch;
while (rt_device_read(serial, 0, &ch, 1) == 0)
{
rt_sem_control(&rx_sem, RT_IPC_CMD_RESET, RT_NULL);
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
return ch;
}
/* 数据解析线程 */
static void data_parsing(void)
{
char ch;
char data[ONE_DATA_MAXLEN];
static char i = 0;
while (1)
{
ch = uart_sample_get_char();
rt_device_write(serial, 0, &ch, 1);
if(ch == DATA_CMD_END)
{
data[i++] = '\0';
rt_kprintf("data=%s\r\n",data);
i = 0;
continue;
}
i = (i >= ONE_DATA_MAXLEN-1) ? ONE_DATA_MAXLEN-1 : i;
data[i++] = ch;
}
}
static int uart_data_sample(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
char uart_name[RT_NAME_MAX];
char str[] = "hello RT-Thread!\r\n";
if (argc == 2)
{
rt_strncpy(uart_name, argv[1], RT_NAME_MAX);
}
else
{
rt_strncpy(uart_name, SAMPLE_UART_NAME, RT_NAME_MAX);
}
/* 查找系统中的串口设备 */
serial = rt_device_find(uart_name);
if (!serial)
{
rt_kprintf("find %s failed!\n", uart_name);
return RT_ERROR;
}
/* 初始化信号量 */
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
/* 以中断接收及轮询发送模式打开串口设备 */
rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);
/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_rx_ind);
/* 发送字符串 */
rt_device_write(serial, 0, str, (sizeof(str) - 1));
/* 创建 serial 线程 */
rt_thread_t thread = rt_thread_create("serial", (void (*)(void *parameter))data_parsing, RT_NULL, 1024, 25, 10);
/* 创建成功则启动线程 */
if (thread != RT_NULL)
{
rt_thread_startup(thread);
}
else
{
ret = RT_ERROR;
}
return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_data_sample, uart device sample);
1.代码分析
uart_data_sample();
咱们学习RTT看代码首先就看MSH命令导出的函数,然后通过分析这个函数一级一级的跳转到其他子函数来分析。
首先在函数开头进行变量的赋值,还有对串口名字数的限制和建立一个字符串数组用来串口输出用的。再往下去就是和前两期一样的了,都使用了判断调用函数时是否填写了参数指定某个串口,如果没有就默认使用uart2。
继续往下看就是初始化串口设备获取设备句柄以便后期调用相关串口功能函数。其次就是初始化信号量另信号量为0就是在没有reless之前都不能take,让线程一直挂起在take的那个点。然后继续设置uart2串口接受回调函数入口,做完这些就通过uart2发送”hello RT-Thread!“。
来到线程创建环节和启动线程这时跳转到data_parsing();解析函数去。
首先也是定义一个字符串”ch“,定义一个字符串数组用来限制接受数据的最大长度(这里设定的是20)。这时进入while循环执行uart_sample_get_char();函数,跳转过去。
可以看到这个函数是用来判断uart2是否接受到数据的,首先看到while循环,条件是uart2没有读取到数据就进入循环,先初始化信号量然后开始take,但是这时无法take因为信号量的值为0,如何relea呢,这就涉及到了一开始串口中断接受回调函数了,每次接受到数据后进入中断relea一次。
结合上面的函数来看如果接受到了数据例如数据为”123/r“,这时侯take到了信号量,代码由原来卡在下图位置:
此时又跳转到解析线程函数这里,继续往下运行,这时候通过rt_device_write();函数通过uart2显示uart2接受到的数据,现在开始正式的解析了。数据在输入也就是123在输入的时候是一位一位进行存储的这时就不会进入if因为没有按下回车也就是没有输入/r,开始判断是否i是大于了我们设定最大长度,如果超过最大长度20就显示到20,如果没有超过20就显示有效的几位,咱们是123那就是显示123三位,然后就是将每一位”ch“赋值到data[]数组里面等待回车进行输出显示了。最后,也就是识别到了回车进入if后就在data[]数组最后一位加上"\0",不然无法联合成为一个字符串进行输出。然后就是调用rt_kprintf(”data=%s\r\n“,data);进行uart1的终端muh界面进行输出。然后清空i跳出循环,继续等待下一次数据输入触发中断回调releas信号量。