【STM32F4+CubeMX零基础快速入门】串口收发全攻略_哔哩哔哩_bilibili
什么是串口通信
串口通信是一种通信协议
串口通信需要至少两根线形成电势差区分高低电平以表达逻辑1/0
双方GND相接,另一根传输信号,发送方为TX,接收方为RX
同理加一根反向线即可实现双向传输
电脑和单片机进行通信时由于电脑没有接线端,所以需要使用到usb转ttl模块
信号传输
在闲置状态下,发送端保持高电平
当需要发送数据的时候,发送端拉低为低电平作为起始信号以让接收端做好准备,称之为起始位
接着开始发送数据,发送端以一定频率发送高低电平,接收端以一定频率进行采样
发送完数据后进入校验阶段,留下一位作为校验位验证传输是否正确
最后发送端将电平拉高表示发送完成
参数选择
数据位:可以选5-8位
校验方式:分为奇校验,偶校验,无校验
奇校验表示数据位和校验位中1的个数总数为奇数,偶校验同理,接收端以此判断数据传输是否出错
停止位:1-2位,停止位越长传输越稳定,但是速度越慢
传输速度(波特率bit/s):典型值:9600及其各种分频/倍频。波特率越高传输速度越快,但越不稳定更容易出错。
(发送端和接收端配置必须配置一样才能传输数据)
CUBEMX配置
UART只支持异步串口
USART支持同步/异步串口
模式设置为异步串口
从上至下即以此为波特率,数据位,校验方式,停止位
最后检查GPIO口是否正确
代码部分
阻塞式
串口发送
阻塞式发送:当发送的数据发送完了函数才会结束
HAL_UART_Transmit(&huart1,(uint8_t*)"Hello World!",12,0xFFFF);
第一个参数:操作的端口
第二个参数:发送数据的首地址。这里加入强制类型转换(uint8_t*)以消除警告
第三个参数:发送首地址及以后的多少位
第四个参数:超过(某16进制数)ms后若未完成传输视为超时强制停止
将电脑接上usb转ttl模块,正确接线(注意电脑的TX,RX和单片机的RX,TX相互依次对应)
printf发送(本质上还是阻塞式发送):
使用时需要调用标准库函数<stdio.h>
然后在用户定义函数区域重写fputc函数配合printf
int fputc(int c,FILE *stream)
{
uint8_t ch1[]={c}; //定义字符数组存放单个字符
HAL_UART_Transmit(&huart1,ch1,1,0xFFFF); //发送出去
return c; //fputc要求如果没有发送错误返回值会是发送的字符
}
然后printf就可以作为发送整个字符串的函数
数据接收
定义一个数组作为接收容器
uint8_t buf[5];
HAL_UART_Receive(&huart1,buf,3,0xFFFF);
第一个参数:操作端口
第二个参数:接收首地址
第三个参数:接收长度
第四个参数:超时时间
配合上述发送函数即可检验是否接收成功
同理重写fgetc也可用scanf来实现数据接收
由于scanf靠回车检测输入,因此发送时候需要勾选上发送新行
中断式
阻塞式浪费cpu,大部分时间都浪费在了等待硬件传输的过程中。
中断处开启串口中断
HAL_UART_Transmit_IT(&huart1,(uint8_t*)"Hello World!",12);
代码参数意义与前面相同,只是少了一个超时时间
每当调用该函数的时候就会开启一个中断,每发送完一个字节就进入一次中断。在中断中,hal库就会自动触发下一个字节的发送,直到发完所有数据,所以这个函数不需要等待所有数据发送完成才退出。他只是触发了中断发送的流程。
此外所有数据发送完成后还会触发一个回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
}
中断接收同理,需要先准备一个存储空间,然后函数就会开启一个中断,每接收到一个字节就会进入一次中断,在中断中,hal库就会把这个字节保存在接收区内。接收完之后同样会触发一个回调函数。
注意:此时准备的存储空间应该为一个全局变量,否则可能在接收到数据的时候作为局部变量已经被销毁了。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
}
应用实例
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
HAL_UART_Transmit_IT(&huart1,buf,5);//接收完后发送出来确认接收正确
HAL_UART_Receive_IT(&huart1,buf,5);//随后开启下一个接收中断准备下一次接收
}
HAL_UART_Receive_IT(&huart1,buf,5);//while外开启首次接收中断
DMA方式串口接收
DMA用于帮助CPU在内存和内存,内存和外设之间搬运数据,CPU只需要告诉DMA搬运哪些数据即可,不用亲自操作字节。
DMA可以把内存中的一部分数据依次通过串口发出,也可将串口收到的数据自动地保存到一片内存空间内。CPU只需要为DMA指定好内存空间即可。
DMA配置
在串口处的DMA设置上添加接收和发送的DMA即可
代码部分
使用DMA进行发送和接收的函数
HAL_UART_Transmit_DMA(&huart1,(uint8_t*)"Hello World!",12);
HAL_UART_Receive_DMA(&huart1,buf,5);
形式上区别只有在把IT换成DMA,实际上区别为中断式每个字节都会触发一次中断而DMA不需要。
同理DMA也会触发中断回调函数,所以应用案例与中断处相似
区别与问题:DMA进行接收的时候,假设设置接收为5个字节,当实际发送过来的字符不满5个字节的时候不会触发中断回调函数。
为了解决不定长接收的问题引入空闲中断,当传输线进入空闲状态的时候就会进入这个中断
先删除原来的中断回调函数的重写,另外开启串口中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
其中的UART_IT_IDLE即是开启空闲中断
由于HAL库没有提供空闲中断的中断回调函数,需要手动编写
打开it.c文件找到串口1的中断服务函数,当串口1产生任何中断的时候都会进入该函数。
首先判断是否为空闲中断:查看中断标志位
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t buf[5]; //注意在it.c里面要将buf作为外部变量引入
/* USER CODE END PV */
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1);
uint8_t len = 5 - __HAL_DMA_GET_COUNTER(huart1.hdmarx); //huart1.hdmarx为串口1的接收DMA句柄
HAL_UART_Transmit_DMA(&huart1,buf,len);
HAL_UART_Receive_DMA(&huart1,buf,5);
}
__HAL_UART_GET_FLAG获取串口的中断标志
第一个参数:串口
第二个参数:获取的状态,此处为空闲中断状态
当空闲中断状态不为0即说明触发了空闲中断。
进入判断后首先清除标志位
然后停止DMA的接收,代表此时已经接收完了
然后利用DMA内部的计数器判断接收位数。当使用DMA接收函数的时候内部就会设置一个计数器,大小为预设的接收字节大小,DMA每接收到一个字节就会-1,所以其计数就是剩余的字节。故用预设的接收大小,获取当前接收函数的计数器计数值相减,就可以得到实际获取到的字节数
将实际获取到的字节数发送出去(对不定长接收作出反应)
最后重新开启下一次的接收