当前位置:网站首页>深度理解STM32的串口实验(寄存器)【保姆级教程】
深度理解STM32的串口实验(寄存器)【保姆级教程】
2022-06-26 10:13:00 【夜路难行々】

首先先看这个图,,可以看出USART_RX_STA类似与一个16位的寄存器,前14位存储的是数据,后面两个分别检测0X0D和0X0A。
接下里分析:
void uart_init(u32 pclk2,u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp=(float)(pclk2*1000000)/(bound*16);//得到USARTDIV
mantissa=temp; //得到整数部分
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;
mantissa+=fraction;
RCC->APB2ENR|=1<<2; //使能PORTA口时钟
RCC->APB2ENR|=1<<14; //使能串口时钟
GPIOA->CRH&=0XFFFFF00F;//IO状态设置
GPIOA->CRH|=0X000008B0;//IO状态设置
RCC->APB2RSTR|=1<<14; //复位串口1
RCC->APB2RSTR&=~(1<<14);//停止复位
//波特率设置
USART1->BRR=mantissa; // 波特率设置
USART1->CR1|=0X200C; //1位停止,无校验位.
#if EN_USART1_RX //如果使能了接收
//使能接收中断
USART1->CR1|=1<<5; //接收缓冲区非空中断使能
MY_NVIC_Init(3,3,USART1_IRQn,2);//组2,最低优先级
#endif
}temp=(float)(pclk2*1000000)/(bound*16);这是一个计算公式,因为使能的是串口1,而串口1是在APB2ENR寄存器里面(其余串口均在寄存器APB1ENR里面),因为APB2的频率一般位72M,而APB1的频率一般位36M。
所以这里的pclk2为72M,而bound是你需要设置的波特率。
mantissa = temp的作用仅仅是:为了接下来将小数部分求出来
fraction=(temp-mantissa)*16; //得到小数部分
mantissa<<=4;这两行代码是为将十进制的整数部分和小数部分,分别转化为16进制。然后存入到波特率寄存器里面。紧接着使能串口1和PORTA时钟(串口一对应的IO口是PA9,PA10,需要拿跳帽连接在一起).
然后将IO口置零,然后分别进行设置成一个输入一个输出,
USART1->CR1|=0X200C; 设置成使能串口8个字长1个停止位(USART_CR2中[13:12]默认为“0”)
MY_NVIC_Init(3,3,USART1_IRQn,2)
将其分在组2里面,此时的抢占优先级:响应优先级为 = 2:2,即(00-11)四种情况,而3:3的安排选择了组2优先级最小的一种情况。这样可以先执行上面的波特率赋值,以及串口使能等等操作,最后再进行这行代码运行。
接下来看下一部分:
u16 USART_RX_STA=0; //接收状态标记
void USART1_IRQHandler(void)
{
u8 res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART1->SR&(1<<5)) //接收到数据
{
res=USART1->DR;
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_RX_STA=0,对接受状态的标记。
先通过状态寄存器SR的RXNE是否为1,是1则接收到了数据,反之则没有。紧接这定义一个res变量来接收从数据寄存器的一个字节,然后此时USART_RX_STA为0,与0X8000进行&运算,结果为0,则未接受到,接着继续进行判断,0X4000进行与运算,看是否为0,也是判断是否接受道路0X0D,如果没有接受到,则将这个res变量存放在数组里面,此时的USART_RX_STA为 0 与0X3fff进行&运算,大家算算会发现,因为他的前14位是数据位,所以你会发现第一个变量就会存放在BUF[0]里面,大概逻辑是这样的:

所以每个字节都会被存放到具体的数组位上 。
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
当数组越界的时候,则会重新开始。
接下来就会一直循环,当数据位存满后,接下来res里面接受的就是0X0D,先和上面一样判断USART_RX_STA是否接受到了0X0A和0X0D。
接着执行:
if(res==0x0d)USART_RX_STA|=0x4000;将USART_RX_STA的第十五位变为1,,接下来进行下一次循环,这一次res接受到的值为0X0A,
然后进行判断进入到
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}所以执行USART_RX_STA|=0x8000,使得USART_RX_STA的第十六位变为1。
接下来看主函数部分:
int main(void)
{
u8 t;
u8 len;
u16 times=0;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,9600); //串口初始化为9600
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n");
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK MiniSTM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}if(USART_RX_STA&0x8000) 判断是否接收到了0X0A
len=USART_RX_STA&0x3fff;举个简单的例子此时USART_RX_STA为1100000000000011和0X3fff进行&运算,得到的结果是3,自然就表示了当前数组的大小。
最后阶段,重点理解以下两行代码:
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束分析如下:将每个组内的信息存入到数据寄存器,此时数据寄存器将数据给TDR,发送信息的时候,是一位一位发送的,每一数据帧都有起始位,数据位,以及停止位,当检测到数据寄存器的细信息发送完了(完全给了TDR),此时状态寄存器的TXE便变为1,当检测到TXE为1后,TC也会变为1(系统自动进行)。所以第二行才会检测这个状态寄存器的第6位是否为1来判断是否发送成功了这个字节。
由此推出,直接判断TXE也可以判断发送是否完成
所以代码可以改为:
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X80)==0);//等待发送结束
}边栏推荐
- 机器学习SVM——实验报告
- Recent work report
- Swiftui development experience: data layer of application design for offline priority
- Plookup table in appliedzkp zkevm (8)
- Redis (basic) - learning notes
- Using baijiafan to automatically generate API calls: progress in JS (II)
- 机器学习PCA——实验报告
- SVN 安装配置
- Bit operation n & (n-1), leetcode231, interview question 05.06
- 8- creating leecode algorithm with pictures and texts - algorithm solution of minimum stack and LRU caching mechanism
猜你喜欢

Which PHP open source works deserve attention

Linux下安装Mysql【详细】

Easyx-----c语言实现2048

Docker中实现MySQL主从复制

Query online users and forced withdrawal users based on oauth2

Basic MySQL

Unity使用SteamVRPlugin时如何不让其他Camera位置和旋转收到SteamVRPlugin控制

Qixia housing and Urban Rural Development Bureau and fire rescue brigade carried out fire safety training

Adaptiveavgpool2d does not support onnx export. Customize a class to replace adaptiveavgpool2d

Win10 start FTP service and set login authentication
随机推荐
【北邮果园微处理器设计】10 Serial Communication 串口通信笔记
openresty 概述
Redis (IV) redis association table caching
MySQL performance monitoring and SQL statements
Installing MySQL under Linux [details]
哪些PHP开源作品值得关注
laravel-admin隐藏按钮, 及设置按钮显示, 默认序列, form 表单的不可修改值
dd命令测试华为鲲鹏&宏衫固态存储磁盘读写速度
UDP Flood攻击防御原理
sliding window
Which PHP open source works deserve attention
DataBinding使用与原理分析
(Typora图床)阿里云oss搭建图床+Picgo上传图片详细教程
2021 Q3-Q4 Kotlin Multiplatform 使用现状 | 调查报告
What does ack attack mean? How to defend against ack attacks?
Origin of b+ tree index
Win10 start FTP service and set login authentication
Oracle sqlplus 查询结果显示优化
Bit operation n & (n-1), leetcode231, interview question 05.06
MySQL backup and restore command