前言
最近学明白了一个比较实用的队列算法在串口通信方面的应用,记录一下。
本文开发环境
软件配置
1.串口基础配置
创建hal_usart.c进行串口配置,从规格书来看,STM8L101F3只有一个串口,串口的配置按照官方给的函数static void USART_Config(void)
进行修改为如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void hal_Usart_Config(void) { CLK_MasterPrescalerConfig(CLK_MasterPrescaler_HSIDiv1); GPIO_ExternalPullUpConfig(GPIOC,GPIO_Pin_3|GPIO_Pin_4, ENABLE); CLK_PeripheralClockConfig(CLK_Peripheral_USART, ENABLE);
USART_DeInit();
USART_Init((uint32_t)115200, USART_WordLength_8D, USART_StopBits_1,USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Rx | USART_Mode_Tx)); USART_ITConfig(USART_IT_TXE, DISABLE); USART_ITConfig(USART_IT_RXNE, ENABLE); enableInterrupts();
}
|
修改参数:
把stm8l10x_it.c里面的串口中断处理函数INTERRUPT_HANDLER(USART_TX_IRQHandler, 27)
,INTERRUPT_HANDLER(USART_RX_IRQHandler, 28)
移到hal_usart.c里。
2.加入消息队列模块
首先添加queue.c和queue.h文件,在hal_usart.c里引入queue头文件。
消息队列的使用流程:
下面进行以串口收发相同的功能进行实战演练:
定义变量
1 2 3 4 5 6
| Queue256 UsartRxMsg;//定义256个字节的串口接收消息队列 Queue256 UsartTxMsg;//定义256个字节的串口发送消息队列 unsigned char TxBuffer[256];//串口数据发送数据的缓存 unsigned int TxCounter = 0;//0表示没有发送数据 其他值表示发送数据的下标 unsigned int TxDataSize = 0;//串口需要发送的有效数据的长度
|
修改void hal_Usart_Config(void)
函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void hal_Usart_Config(void) { CLK_MasterPrescalerConfig(CLK_MasterPrescaler_HSIDiv1); GPIO_ExternalPullUpConfig(GPIOC,GPIO_Pin_3|GPIO_Pin_4, ENABLE); CLK_PeripheralClockConfig(CLK_Peripheral_USART, ENABLE); USART_DeInit();
USART_Init((uint32_t)115200, USART_WordLength_8D, USART_StopBits_1,USART_Parity_No, (USART_Mode_TypeDef)(USART_Mode_Rx | USART_Mode_Tx)); USART_ITConfig(USART_IT_TXE, DISABLE); USART_ITConfig(USART_IT_RXNE, ENABLE); enableInterrupts();
QueueEmpty(UsartRxMsg);//清空队列 QueueEmpty(UsartTxMsg); TxCounter = 0; TxDataSize = 0; //串口发送数据的长度 }
|
现在假设PC端向单片机发送“1234”,下面就要对串口接收到的字节进行“消息入列”到UsartRxMsg中进行存储,代码如下:
1 2 3 4 5 6 7
| INTERRUPT_HANDLER(USART_RX_IRQHandler, 28) { unsigned char dat; dat = (uint8_t)USART_ReceiveData8(); // 参数:定义的队列的名称,需要入列的数据的指针地址,入列数据的长度 QueueDataIn(UsartRxMsg,&dat,1); }
|
紧接着我们要让单片机将收到的信息原封不动的发回PC端,因此我们要定义static void hal_Usart_RxHandler(void)
函数先把UsartRxMsg的数据传输给UsartTxMsg,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static void hal_Usart_RxHandler(void) { unsigned char len,i; unsigned char usartRxBuffer[128]; //返回值:消息中有效数据的长度 形参:需要查询的队列 len = QueueDataLen(UsartRxMsg); if(len) { //消息处理函数 for(i=0;i<len;i++) { //形参:需要出列的消息队列 指针变量:用来保存出列的数据 QueueDataOut(UsartRxMsg,&usartRxBuffer[i]); } QueueDataIn(UsartTxMsg,usartRxBuffer,len); } }
|
之后要把UsartTxMsg的数据取出来,再通过串口发送给PC端,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| static void hal_Usart_TxHandler(void) { unsigned char len,i; if(TxCounter == 0) {///表示无线发送数据空闲 len = QueueDataLen(UsartTxMsg); if(len) { for(i=0;i<len;i++) { QueueDataOut(UsartTxMsg,&TxBuffer[i]); } USART_SendData8(TxBuffer[0]); TxCounter = 1; TxDataSize = len; //串口发送数据的长度 USART_ITConfig(USART_IT_TXE, ENABLE); } } } INTERRUPT_HANDLER(USART_TX_IRQHandler, 27) { if (TxCounter == TxDataSize) { USART_ITConfig(USART_IT_TXE, DISABLE); TxCounter = 0; } else { USART_SendData8(TxBuffer[TxCounter++]); } }
|
注意这里的两个静态函数static void hal_Usart_RxHandler(void)
和static void hal_Usart_TxHandler(void)
,为了能让外部调用我们需要封装一下:
1 2 3 4 5
| void hal_Usart_Pro(void) { hal_Usart_RxHandler(); hal_Usart_TxHandler(); }
|
最后在main.c文件内调用相应代码完成串口收发相同的功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include "stm8l10x.h" #include "hal_tim4.h" #include "hal_usart.h"
//定义systick __IO uint8_t systick;
void main(void) { CLK_MasterPrescalerConfig(CLK_MasterPrescaler_HSIDiv1);//16M hal_TIM4_Config(); hal_Usart_Config(); systick = 0; while (1) { if(systick == 1) { systick = 0; hal_Usart_Pro(); } } }
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 25) { systick = 1; ///1MS TIM4_ClearITPendingBit(TIM4_IT_Update); }
|
结语
虽然看起来好像代码量增加了、逻辑更绕了、数据传递的路径更长了,但是加入消息队列的好处:可以避免消息数据的丢失、更加方便程序逻辑的处理。因此,在延迟可以容忍的情况下保障信息安全完整、可靠,应该是要优先考虑的。