首先是串口初始化,这里初始化的是usart3
void esp8266_init(void)
{huart_wifi.Instance=ESP8266; //uart3huart_wifi.Init.BaudRate=115200; // 设置波特率为115200huart_wifi.Init.WordLength=UART_WORDLENGTH_8B; // 设置数据位长度为8位huart_wifi.Init.StopBits=UART_STOPBITS_1; // 设置停止位为1位huart_wifi.Init.Parity=UART_PARITY_NONE; // 设置奇偶校验为无huart_wifi.Init.Mode=UART_MODE_TX_RX; // 设置模式为接收和发送huart_wifi.Init.HwFlowCtl=UART_HWCONTROL_NONE; // 设置硬件流控制为无huart_wifi.Init.OverSampling=UART_OVERSAMPLING_16; // 设置过采样为16if(HAL_UART_Init(&huart_wifi)!=HAL_OK){Error_Handler();}/* 该函数会开启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量 */HAL_UART_Receive_IT(&huart_wifi, (uint8_t *)uart_recv_it_buf, WIFI_IRQ_RECV_LEN);}
其中,HAL_UART_Receive_IT开启标志位UART_IT_RXNE后,我们就可以配置uart3的中断函数
void ESP8266_IRQHandler(void)
{HAL_UART_IRQHandler(&huart_wifi);
}
函数中用的是hal库的中断函数代码,函数实现比较复杂,我们可以自己写一个中断函数,配合实现。
这边是按照官方回调函数,在void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart),中定义何时结束数据传输。
先讲一下调用结构
由于 HAL_UART_Receive_IT开启接收中断,会调用我们写好的ESP8266_IRQHandler,进入到
HAL_UART_IRQHandler(&huart_wifi); 第一个if语句判断状态寄存器sr中的RXNE:读取数据寄存器不为空 (Read data register not empty)。和控制寄存器cr中的RXNEIE:RXNE 中断使能 (RXNE interrupt enable),当rxne不为空切使能rxne中断时候,进入UART_Receive_IT(huart);
点击跳转,找到如下
这个函数的实现官方是弱定义,我们可以重写。
调用结构: 调用 → 被调用
HAL_UART_IRQHandler → UART_Receive_IT → HAL_UART_RxCpltCallback。
下面我们看一下正点原子是怎么实现自定义的串口通讯格式。
/*** @brief Rx传输回调函数* @param huart: UART句柄类型指针* @retval 无*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{//g_usart_rx_sta 0-13位表示交收到if(huart->Instance == ESP8266) /* 如果是串口3 */{if((g_wifi_rx_sta & (1<<15)) == 0) /* 接收未完成 */{if(g_wifi_rx_sta & (1<<14) ) /* 接收到了0x0d,换行符是由 2个字节组成:0x0D 和 0x0A */{if(uart_recv_it_buf[0] != 0x0a) {g_wifi_rx_sta = 0; /* 接收错误,重新开始 */}else {g_wifi_rx_sta |= (1<<15); /* 接收完成了 */}}else /* 还没收到0X0D */{if(uart_recv_it_buf[0] == 0x0d){g_wifi_rx_sta |= (1<<14);}else{rx_wifi_msg[g_wifi_rx_sta & 0X3FFF] = uart_recv_it_buf[0] ;g_wifi_rx_sta++;if(g_wifi_rx_sta > (WIFI_RX_LEN - 1)){g_wifi_rx_sta = 0; /* 接收数据错误,重新开始接收 */}}}}HAL_UART_Receive_IT(&huart_wifi, (uint8_t *)uart_recv_it_buf, WIFI_IRQ_RECV_LEN);}}
打开正点原子f407开发指南,我们看到
原子的开发手册对接收中断回调函数代码解读非常完整,清晰,大家可以去看下。
注意的是,用串口助手一定要勾选发送新行(结尾自动发送回车符号),因为代码涉及到对 回车的判断( 0x0d + 0x0a 表示回车符 )。
g_wifi_rx_sta,表示接受状态标识符,uint16_t类型。
下面我说一下我理解的代码思路
1.在回调函数中判断是否已经接收完成(bit 15 位为1) g_wifi_rx_sta & (1<<15) ==0
如果接收完成,调用接收中断函数HAL_UART_Receive_IT(&huart_wifi, (uint8_t *)uart_recv_it_buf, WIFI_IRQ_RECV_LEN);
2,如果已经接收到了 回车的前半部分 :0x0d g_wifi_rx_sta & (1<<14) ==1
如果 后面接上的是后半部分 0x0a,那么就判定为接收完成(g_wifi_rx_sta |=(1<<15)),相反如果不是,那么g_wifi_rx_sta
置0,重新开始计数。
3,如果还没有接收到回车的前半部分,那么就先判定是否中断里面接受到的buffer:uart_recv_it_buf[0]已经是0x0d,如果是的话,那么将bit14置1,g_wifi_rx_sta |= (1<<14)
4,如果uart_recv_it_buf[0]不是是0x0d,那么就接收字符,原子在这里设置了一个 rx_wifi_msg[ buffer_len ] , buffer_len 是你设置的最大接受缓冲区长度,我这里设置最大128 。
前面我们在初始化esp8266已经开启了 HAL_UART_Receive_IT(&huart_wifi, (uint8_t *)uart_recv_it_buf, WIFI_IRQ_RECV_LEN); 意思是每次中断接收 1个字节的数据。
rx_wifi_msg[ g_wifi_rx_sta & 0x3fff ]=uart_recv_it_buf[0] ,0x3fff因为是0-13位表示接受数据位数,
每次都接受1位数据,直到设定的最大接收值。
g_wifi_rx_sta ++(每次进入中断一次,而且没有接收到回车判定符号 0x0d,那么就会 自加)。
然后就是判定是否接收长度大于设定长度,如果大于,就将 g_wifi_rx_sta=0;。
main函数部分代码。
if (g_wifi_rx_sta & (1<<15) ) /* 接收到了数据? */{len = g_wifi_rx_sta & 0x3fff; /* 得到此次接收到的数据长度 */printf("\r\n您发送的消息为:\r\n");HAL_UART_Transmit(&huart_wifi,(uint8_t*)rx_wifi_msg,len,1000); /* 发送接收到的数据 */while(__HAL_UART_GET_FLAG(&huart_wifi,UART_FLAG_TC)!=SET); /* 等待发送结束 */printf("\r\n\r\n"); /* 插入换行 */g_wifi_rx_sta = 0;}else{times++;if (times % 5000 == 0){printf("\r\n正点原子 STM32开发板 串口实验\r\n");printf("正点原子@ALIENTEK\r\n\r\n\r\n");}if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");if (times % 30 == 0) LED0_TOGGLE(); /* 闪烁LED,提示系统正在运行. */delay_ms(10);}}