关于串口数据接收处理有多种方法,下面主要总结两种:
1.引入接收状态标记USART_RX_STA
int main(void)
{
u8 len;
u16 times = 0;
HAL_Init();
SystemClock_Config(); //初始化系统时钟为80M
delay_init(80); //初始化延时函数 80M系统时钟
uart_init(115200); //初始化串口,波特率为115200
LED_Init(); //初始化LED
KEY_Init(); //初始化KEY
while(1)
{
if(USART_RX_STA & 0x8000)
{
len = USART_RX_STA & 0x3fff; //得到此次接收到的数据长度
//printf("\r\n发送的消息为:\r\n");
HAL_UART_Transmit(&UART1_Handler, (uint8_t*)USART_RX_BUF, len, 1000); //发送接收到的数据
while(__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_TC) != SET); //等待发送结束
printf("\r\n\r\n");//插入换行
USART_RX_STA = 0;
}
else
{
times++;
if(times % 5000 == 0)
{
//printf("\r\nALIENTEK 潘多拉 STM32L475 IOT开发板 串口实验\r\n");
//printf("正点原子@ALIENTEK\r\n\r\n\r\n");
printf("%ld",times);
}
//if(times % 200 == 0)printf("请输入数据,以回车键结束\r\n");
if(times % 30 == 0)LED_B_TogglePin; //闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}
//定义变量USART_RX_STA,接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
/**
* @brief 串口1中断服务程序
*
* @remark 下面代码直接把中断控制逻辑写在中断服务函数内部
* 说明:采用HAL库处理逻辑,效率不高。
*
* @param void
*
* @return void
*/
void USART1_IRQHandler(void)
{
u8 Res;
if((__HAL_UART_GET_FLAG(&UART1_Handler, UART_FLAG_RXNE) != RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
HAL_UART_Receive(&UART1_Handler, &Res, 1, 1000);
if((USART_RX_STA & 0x8000) == 0) //接收未完成
{
if(USART_RX_STA & 0x4000) //接收到了0x0d
{
if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始
else USART_RX_STA |= 0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res == 0x0d)USART_RX_STA |= 0x4000;
else
{
USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;
USART_RX_STA++;
if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收 USART_REC_LEN为定义的最大接收字节数
}
}
}
}
HAL_UART_IRQHandler(&UART1_Handler);//处理串口中断请求
}
函数:HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint16_t *pData, uint16_t Size, uint32_t Timeout)
大致流程:判断是否忙–>锁住–>标记接收忙–>获取tick计数–>赋值RxXferCount有多少数据要接收–>每次从DR内获取一个Byte存在pData指向的空间
这个接收函数是阻塞式的。参数Timeout是超时时间,代表某次执行函数最多占用串口的时间,单位是毫秒。简单来说,在本次数据接收完之前,不能处理其他数据。所以,调用函数的时候要指明参数,本次接收占用多长时间,在此期间,串口资源被独占。如果在规定的时间内,数据接收完毕,那就释放占用的串口资源;如果到了时间,即便数据还没有接收完毕,(比如数据量很大),仍需要归还串口资源的控制权,供其他外设使用。
/**
* @brief Receive an amount of data in blocking mode.
* @note When FIFO mode is enabled, the RXFNE flag is set as long as the RXFIFO
* is not empty. Read operations from the RDR register are performed when
* RXFNE flag is set. From hardware perspective, RXFNE flag and
* RXNE are mapped on the same bit-field.
* @param huart UART handle.
* @param pData Pointer to data buffer.
* @param Size Amount of data to be received.
* @param Timeout Timeout duration.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint16_t *tmp;
uint16_t uhMask;
uint32_t tickstart = 0;
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Init tickstart for timeout managment*/
tickstart = HAL_GetTick();
huart->RxXferSize = Size;
huart->RxXferCount = Size;
/* Computation of UART mask to apply to RDR register */
UART_MASK_COMPUTATION(huart);
uhMask = huart->Mask;
/* as long as data have to be received */
while (huart->RxXferCount > 0U)
{
if (UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_RXNE, RESET, tickstart, Timeout) != HAL_OK)
{
return HAL_TIMEOUT;
}
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t *) pData ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
pData += 2U;
}
else
{
*pData++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
}
huart->RxXferCount--;
}
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
/* Process Unlocked */
__HAL_UNLOCK(huart);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
2.使用回调函数
串口中断的执行过程:
USART2_IRQHandler(void) //启动文件中的中断向量,处理串口的总中断
HAL_UART_IRQHandler(UART_HandleTypeDef *huart) //处理具体某个串口中断请求
UART_Receive_IT(UART_HandleTypeDef *huart) //从数据寄存器读取一个字节数据存储在一个变量里,在接收完数据量为RxXferCount的数据后失能接收中断、赋值相应的标志位 接着执行回调函数
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
首先在启动文件中找到中断向量,然后依次go to definition 就可以顺藤摸瓜捋清串口中断的执行步骤。
接下来是回调函数的处理:
/**
* 函数功能: 串口接收完成回调函数
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
HAL_UART_Transmit(&husartx,&aRxBuffer,3,0xffff);
HAL_UART_Receive_IT(&husartx,&aRxBuffer,3);//再次使能串口接收中断
//HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
//用户定义一个缓存区,大小随意,然后通过该函数把这个缓存区对应到串口的接收,
//上面函数的意思就是把aRxBuffer作为缓存区,
//然后usart1接收数据的时候就放到aRxBuffer这个地址及其递增的地址中
//当接收到3Bytes数据之后调用一次callback函数,(可以设置接收固定长度数据后进入回调函数处理)
//并使能串口接收中断功能。
}
显然使用回调函数程序逻辑清晰、代码简洁易懂,执行效率较高。
引用正点原子的流程图:
3.串口发送中断
串口发送中断使用较少,这里简单补充说明。
串口发送中断的过程大致如下:
USART2_IRQHandler(void) //启动文件中的中断向量,处理串口的总中断
HAL_UART_IRQHandler(UART_HandleTypeDef *huart) //处理具体某个串口中断请求
UART_EndTransmit_IT(UART_HandleTypeDef *huart) //发送完成中断,失能发送完成中断,之后调用回调函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成后执行回调函数
使能中断发送:发送指定长度的数据并使能发送中断
HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)