代码文件:
链接:https://pan.baidu.com/s/1ZjbgVoDO5RGnTeN7z6pC_Q?pwd=91c7
提取码:91c7
目录
一、UART基本原理
UART:通用异步通信 USART:通用同步异步通信
异步通信:通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。
同步通信:通信要确保双方时钟完全相同
由于一般我们只用异步模式,所以本章为UART,也只介绍异步通信的使用
串口部分电路图
发送阶段:先将要发送的舒服放入TDR中,再由TDR转入移位寄存器,最后再一位一位通过TX引脚发出。
接收阶段:RX引脚一位一位存入移位寄存器,再一次性转入RDR寄存器。
重点标志位:
TXE:TDR寄存器中的数据已经被转移到移位寄存器(1)
TC:移位寄存器中的数据发送完成(1)
RXNE:接收完数据(1)
二、cubemx配置
1.基本(阻塞方式)发送/接收
阻塞模式:发送和接收函数会一直等待,直到数据发送或接收完成,因此会影响效率。
第一步:选择串口
第二部:选择异步模式
第三步:设置参数
baud rate:波特率 可以理解为发送数据的速度
word length:字长 可以理解为一次性发送多少位的二进制码
parity:奇偶校验
stop bits:停止位
data direction:数据放心 选择收发模式
over sampling:过采样 用串口发送的n倍速率来采样,可以滤除干扰信号
阻塞模式配置只需要配置这些就可以用了
2.中断模式发送/接收
中断模式:发送和接收函数是非阻塞的,中断后立即返回,程序在等待数据传输的同时执行其他任务
cubemx中打开串口中断即可
三、代码实现
1.基本(阻塞方式)发送/接收
定义数据数组
定时发送数据、接收到数据后立刻返回
总代码:
uint8_t message[] = "hello world";
uint8_t receiveData;
HAL_UART_Transmit(&huart1,message,12,200);
HAL_Delay(500);
//参数1:串口结构体;参数2:数据数组地址;参数3:数据长度;参数4:超时时间
HAL_UART_Receive(&huart1,&receiveData,2,HAL_MAX_DELAY);
//参数1:串口结构体;参数2:发送数据;参数3:数据长度;参数4:超时时间
HAL_UART_Transmit(&huart1,&receiveData,2,200);
连接好串口线之后,确认参数选择无误打开串口,即可看到发送的数据了!
注意:
1.连接串口线的时候,要注意单片机的TX连接到电脑的RX,单片机的RX连接到电脑的TX
2.串口助手使用时,要取消RTS,否则,串口助手会让单片机始终保持复位状态,直接卡住什么现象也没有(困扰我1小时。。。。。)
2.printf重定向
printf重定向会让我们的代码调试、日志输出、人机交互等更加便捷
第一步:包含stdio文件
#include "stdio.h"
第二步: 重定向fputc
//printf重定向
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
第三步:勾选Use MicroLIB
这一步很重要!!!也要注意,cubemx重新生成程序后会给这个勾选取消,如果没有重新勾选,工程可能会直接卡死,debug里需要点击三次run才能进入main,如要使用printf,时刻检查!!!
测试代码:
HAL_Delay(500);
printf("ZTL\r\n");
效果:
3.中断模式发送/接收
发送:
直接调用函数即可,无需设置停止时间
参数1:串口号;参数2:发送信息的地址;参数3:字节数,直接用sizeof获取
HAL_UART_Transmit_IT(&huart1,message,sizeof(message));//中断方式发送数据
HAL_Delay(500);
接收:
中断接收函数:
参数1:串口号;参数2:接收到的数据缓冲区地址;参数3:接收多少数据
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
HAL_UART_RECEIVE_IT执行过程可以理解为:打开串口中断,返回主函数,后台自动接收Size个数据,当接收满Size个数据后触发中断!如果你设置Size=10,然而只发了123456不能触发中断,还得再发7890才能触发中断,触发中断后调用一系列函数,调用过程中,“关闭了接收中断”,然后跳转到回调函数(用户编写的处理数据函数),所以,我们需要在while前先调用一次来打开接收中断,再在回调函数重新调用一次,用来重新打开串口接收中断。
不定长代码收发逻辑:
由上面可知,HAL库的串口中断需要接收size个数据才能进入callback回调函数,那我们可以直接设置size=1,那么每收到1位数据就可以进入这个中断,来对这1位数据操作,比如存入一个数组中。如发送了12345,收到1后,进入中断
那么现在我们就要想办法怎么一位一位的存起来再拿出来。解决方法就是设置一个数组,并规定数组最大值为100,再设置一个下标索引,来不断指示当前的数据应该存在数组的哪一个位置,再设置一个停止字符,当最后一位是该字符,则可以开始处理整个字符串了。
第一步:定义接收字符缓冲区
第二步:打开串口接收中断
while前调用一次receive_it,这时,下标为0,数据存在buffer【0】
由于HAL库封装的原因,当我们开启串口中断后,其具体的中断还没有被打开,比如你想使用串口接收中断,它还没有被激活,我们需要调用接收中断函数手动激活。
第三步:不定长收发
先判断引起中断引脚号是不是串口1,再判断当前缓存区有没有空间了,如果有,则从寄存器取出来数据,存入缓冲区,第一次执行的话,则存入了buffer【0】,然后下标加1,接着判断这个字符是不是结束字符,如果不是,则重新开启中断,再往后接收,直到这个字符是结束字符,那我们仍然把它存入buffer,然后对数据进行处理,处理结束后清空缓冲区,防止对下一次传输的数据进行干扰。最后,如果缓冲区不足,比如定义最多存100个字符,结果从0发到了200个,那么存到第101个的时候,就不够用了,那么我们需要报错,并且清空缓冲区,让对方重新发。
由于赶工可能写的不是很好,如果有bug或者其他问题,可以直接发到评论区