目录
一、串口通讯协议简介
一、物理层
![](https://img-blog.csdnimg.cn/84e494b7377b47f3b74772ba56d47b54.png)
1.电平标准
![](https://img-blog.csdnimg.cn/b76d6e4df2884c8b92a0687b8cff25a3.png)
二、协议层
![](https://img-blog.csdnimg.cn/de3b7df938c94b8aa7a08db268f4b353.png)
1.波特率
2.通讯的启示和停止信号
3.数据校验
三、STM32的串口简介
![](https://img-blog.csdnimg.cn/20190730203737688.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNzQzNzYy,size_16,color_FFFFFF,t_70)
四、软件实现
1.通过图形化软件CubeMX配置
![](https://img-blog.csdnimg.cn/67a9812684664c979b7ab7b049abd7ee.png)
![](https://img-blog.csdnimg.cn/934e3e472f704a7a8607b4f8a5eecd5f.png)
![](https://img-blog.csdnimg.cn/77e27e57c0e64817889e60bb56a04456.png)
2.程序实现
![](https://img-blog.csdnimg.cn/44bf316601044c1096bf3e520edb5ea3.png)
(1)使用阻塞模式,发送数据
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t Data[] = "HELLO WORLD !\n";
/* USER CODE END 1 */
/* USER CODE BEGIN 2 */
//参数1:使用的串口,2:要发送的数据,3:数据大小,4:发送的超时时间
HAL_UART_Transmit(&huart1,Data,sizeof(Data),1000);
/* USER CODE END 2 */
}
编译下载程序可在串口助手中看到:
在使用串口发送数据时,常用 printf() 语句进行发送。而在STM32中使用printf() 语句需要进行printf() 语句的重定向。代码如下:
在 usart.h 文件中添加
/* USER CODE BEGIN Includes */
#include <stdio.h>/* USER CODE END Includes */
在 usart.c 文件中添加:
/* USER CODE BEGIN 0 */
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}/* USER CODE END 0 */
做完以上步骤后即可实现 printf() 语句的重定向
/* USER CODE BEGIN 2 */
//参数1:使用的串口,2:要发送的数据,3:数据大小,4:发送的超时时间
HAL_UART_Transmit(&huart1,Data,sizeof(Data),1000);
printf("HELLO STM32 !\n");
/* USER CODE END 2 */
(2)使用中断模式,接收数据
1.在main函数中添加:
/* USER CODE BEGIN 2 */
//参数说明, 1:使用的串口,2:接收数据的缓存区,3:接收数据的字节数
HAL_UART_Receive_IT(&huart1,Rx_Data,3);
2. 调用中断回调函数,将接收到的数据发送出去
uint8_t Rx_Data[3];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1,Rx_Data,sizeof(Rx_Data),100);
HAL_UART_Receive_IT(&huart1,Rx_Data,3); //接收结束后需要重新调用该函数,//不然只能接收一次
}
}
实验现象如下:
//-----更新-----//
一、如何使用帧头帧尾,处理串口数据
在串口通信过程中,如果需要发送不同的数据表示不同的操作,这就需要对串口数据进行约束,也就是添加帧头帧尾,并且串口数据有时也会出错,不对串口数据进行有效处理,有可能产生误操作。当然想要接受到的串口数据正确性,也不止这一个办法,使用校验和也是其一。下面介绍的使用帧头帧尾是一种较为简单的方法。
废话不多说,直接上程序:
void USART3_IRQHandler(void)
{
static uint8_t RxState = 0;
static uint8_t pRxPacket = 0;
if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
{
uint8_t RxData = USART_ReceiveData(USART3);
if (RxState == 0)
{
if (RxData == 0x2C)
{
RxState = 1;
pRxPacket = 0;
}
}
else if (RxState == 1)
{
Serial_Rx[pRxPacket] = RxData;
pRxPacket ++;
if (pRxPacket >= 3)
{
RxState = 2;
}
}
else if (RxState == 2)
{
if (RxData == 0x5B)
{
RxState = 0;
Serial_RxFlag = 1;
}
}
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
}
}
帧头:0X2C
有效数据:date1、date2、date3
帧尾:0X5B
上述代码利用串口中断,每接收1字节串口数据,对其进行判断,当数据符合我们所设定的帧头时,接收后续到数据,接收完所要的数据后,再次判断帧尾也正确时,所接收到的串口数据就是我们想要的数据了。此代码还能再次优化,笔者在这里将这个留给各位读者,发挥自己到能力。代码不关乎平台,锻炼出写代码逻辑思维才是目的。
当然,此办法很简单,所以适用的场景受限制,适用于已经知道接收数据的长度,发送的是什么数据。除此之外还有很多方法,其一就是利用串口空闲中断,它还可以对不定数据帧进行接收判断。(详见主页文章)