当前位置:网站首页>深度理解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);//等待发送结束
}边栏推荐
- Is it safe to use flush mobile phones to speculate in stocks? How to fry stocks with flush
- Unity使用SteamVRPlugin时如何不让其他Camera位置和旋转收到SteamVRPlugin控制
- Oracle11g 启动数据库时报错 ORA-27154: post/wait create failed
- 携程机票 App KMM 跨端 KV 存储库 MMKV-Kotlin | 开源
- Alibaba cloud OSS - object storage service (tool)
- 【深度学习理论】(7) 长短时记忆网络 LSTM
- Getting started with postman
- Basic MySQL
- [Beiyou orchard microprocessor design] 10 serial communication serial communication notes
- MySQL performance monitoring and SQL statements
猜你喜欢

机器学习SVM——实验报告

That is to say, "live broadcast" is launched! One stop live broadcast service with full link upgrade

MySQL模糊查询详解

Nuxt. JS - learning notes

Docker中实现MySQL主从复制

JWT (SSO scheme) + three ways of identity authentication

Linux下安裝Mysql【詳細】

Win10 start FTP service and set login authentication

Linux下安装Mysql【详细】

Vscode environment setup: synchronous configuration
随机推荐
机器学习PCA——实验报告
Linux下安装Mysql【详细】
Alibaba cloud OSS - object storage service (tool)
Consumer microservice Governance Center stepping on the pit
[online simulation] Arduino uno PWM controls the speed of DC motor
服务器单、双向可调一键互信脚本!
Introduction to sysbench Basics
CentOS installs redis multi master multi slave cluster
laravel 安装报错 Uncaught ReflectionException: Class view does not exist
Common regular expressions - tool classes (mobile number, email, QQ, fax)
机器学习线性回归——实验报告
互联网对抗神器之漏洞扫描与反渗透
Tape library simple record 1
nacos2.x.x启动报错信息Error creating bean with name ‘grpcClusterServer‘;
[deep learning theory] (6) recurrent neural network RNN
Idea remote debugger
ceph运维常用指令
Grain Mall - distributed Foundation
Fabric.js 上划线、中划线(删除线)、下划线
Update mysql5.6 to 5.7 under Windows