18.7
实验3:基于环形队列的UART收发回显
在实际项目开发中,由于有些串口不具备FIFO(如SCI1和SCI2)或FIFO的buffer比较小,这可能会在数据处理速度小于数据接收速度的时候,导致数据的丢失。因此我们可以设计一个队列来避免这一问题。在本实验中,我们使用环形队列来实现实验1的串口收发回显,将串口接收到的数据暂存在队列中,待完成一次接收后再将队列中的数据全部发出去。
队列是一种特殊的线性表,只允许在队列头(head)删除元素,在队列尾(tail)添加元素。当队列添加一个元素,队列尾向后移动,当队列删除一个元素,同样,删除一个元素,队列头向后移动,如图19_18。

图19-18 队列图示
由于存储空间是有限的,如果使用线性队列,删除元素后就会空出一段存储空间,这会造成很大的浪费。因此实际上我们更多使用环形队列。并不是说这段存储空间是环形的,而是头指针和尾指针到达存储空间末尾后会回到存储空间起点。因此在逻辑上这是循环的,如图19_19。

图19-19 环形队列
18.7.1
硬件设计
本实验使用到开发板的串口和LED灯,其原理图在本章实验1和前面的点亮LED灯章节有介绍,这里不进行赘述。
18.7.2
软件设计
18.7.2.1
新建工程
因为本节的UART实验例程与上一个实验例程的FSP配置以及UART相关的一些代码基本一致,因此我们可以直接以前面的“19_UART_Receive_Send”工程为基础进行修改。
对于e2 studio开发环境:拷贝一份我们之前的e2s工程模板“19_UART_Receive_Send”,然后将工程文件夹重命名为“19_UART_Circular_Queue”,最后再将它导入到我们的e2 studio工作空间中。
对于Keil开发环境:拷贝一份我们之前的Keil工程模板“19_UART_Receive_Send”,然后将工程文件夹重命名为“19_UART_Circular_Queue”,并进入该文件夹里面双击Keil工程文件,打开该工程。
FSP配置完全一致,因此我们省略掉这部分。
18.7.2.2
环形队列的实现
列表8:代码清单19-7:环形队列头文件内容
左右滑动查看完整内容
#defineDATA_LEN 300//队列缓存大小
typedefstruct
{
uint16_thead; //头指针
uint16_ttail; //尾指针
uint8_t data[DATA_LEN]; //队列数据
}Circular_queue_t;
externCircular_queue_tCircular_queue;//环形队列全局变量
boolQueue_Init(Circular_queue_t*circular_queue); //初始化队列
boolQueue_isEmpty(Circular_queue_t*circular_queue);//判断队列是否为空
boolQueue_isFull(Circular_queue_t*circular_queue); //判断队列是否已满
boolQueue_Wirte(Circular_queue_t*circular_queue,uint8_t *string, uint16_
→t len);//写数据
boolQueue_Read(Circular_queue_t*circular_queue, uint8_t*string,uint16_t␣
→len); //读数据
uint16_tQueue_HadUse(Circular_queue_t *circular_queue); //返回队列中数据的长度
uint16_tQueue_NoUse(Circular_queue_t*circular_queue); //返回未使用数据的长度
环形队列相关函数的具体定义可以在例程的circular_queue.c中查看,这里就不赘述了。
18.7.2.3
串口中断回调函数
debug_uart4_callback串口中断回调函数的内容修改成如下所示。
列表9:代码清单19-8:串口中断回调函数
左右滑动查看完整内容
/*串口中断回调*/
voiddebug_uart4_callback(uart_callback_args_t* p_args)
{
switch(p_args->event)
{
caseUART_EVENT_RX_CHAR:
{
/*接收到数据后马上写入队列中*/
Queue_Wirte(&Circular_queue,(uint8_t*) &p_args->data, 1);
break;
}
caseUART_EVENT_TX_COMPLETE:
{
uart_send_complete_flag= true;
break;
}
default:
break;
}
}
18.7.2.4
hal_entry入口函数
hal_entry入口函数的内容修改成如下所示。
列表10:代码清单19-9:hal_entry入口函数
左右滑动查看完整内容
voidhal_entry(void)
{
/* TODO: add your own code here */
uint8_t Read_Buffer[DATA_LEN];
uint16_t Read_Length;
LED_Init();
// LED 初始化
Debug_UART4_Init(); // SCI4 UART 调试串口初始化
Queue_Init((Circular_queue_t*)&Circular_queue); //环形队列初始化
printf("这是一个串口环形队列例程
");
printf("打开串口助手发送数据 5 个及以上的数据,接收窗口会打印所发送的数据
");
while(1)
{
if (Queue_isEmpty(&Circular_queue) == false) //判断队列中的数据不为空
{
Read_Length = Queue_HadUse(&Circular_queue);
if( Read_Length >= 5)
// 如果队列中的数据大于等于 5 个,开始打印
队列中的所有数据
{
printf("Read_Length=%d: ", Read_Length);
memset(Read_Buffer, 0, DATA_LEN);
/* 读出 Read_Length 个数据 */
Queue_Read(&Circular_queue, Read_Buffer, Read_Length);
printf("%s
", Read_Buffer);
}
}
R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
}
#if BSP_TZ_SECURE_BUILD
/* Enter non-secure code */
R_BSP_NonSecureEnter();
#endif
}
18.7.3
下载验证
保证开发板相关硬件连接正确,用Type-CUSB线连接开发板“USBTOUART”接口跟电脑。本次实验需要使用到串口调试助手,配置好串口参数并打开串口后,在调试助手的发送区域输入超过5个的任意字符并点击发送,即可在接收区看见返回字符,不一定马上全部返回全部的已发送的字符,可以多发送几次数据观察,数据并没有丢失。
全部0条评论
快来发表一下你的评论吧 !