STM32的串口编程实验理解分析

usart—通用同步异步收发接收器,是一个串行通信设备,可以和外部设备进行灵活的全双工数据交换,有别于usart还有一个uart(在原来的基础上裁剪掉了同步通信功能(时钟同步)),串行通信一般是以帧格式传输数据,一帧一帧的传。
协议层:串口通信的一个数据包包含从起始信号开始,直到停止信号的结束 起始信号:一个逻辑0数据位表示。 停止信号:0.5,1,1.5或2个逻辑1的数据位表示。 0.5个停止位:智能卡模式下的接收数据时使用。 1个停止位:停止位的默认数值 1.5个停止位:智能卡模式下的手法数据和接收数据时使用 2个停止位:常规usart模式,单线模式以及调制解调器的模式。 有效数据的基本长度被约定为5,6,7,8. 奇偶检验(设置usart-cr1 的ps位) 偶检验:数据=00110101,里面数据1的个数位为偶数位,检验位置“0”,当数据检验和偶数相同的时候,证明没有出错,反之则错误 奇检验:数据 = 01110101,里面数据1的个数为奇检位,检验位置“1”,当数据检验和奇数相同,则证明没有出错,反之错误。 当然也会存在同时两个位一块出现错误,导致无法判断是否位奇偶检验的错误,但发生的概率很低。
下面是对代码的理解: 可以看出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是你需要设置的波特率。
usartx-brr: 前四位为小数部分 ,后12位是整数部分,假设算出来的mantissa = 39.5,小数部分相当于把1分成了16份,所以相当于把0.5*16转化为二进制存入。
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(“
您发送的消息为:
”);
for(t=0;t《len;t++)
{
usart1-》dr=usart_rx_buf[t];
while((usart1-》sr&0x40)==0);//等待发送结束
}
printf(“
”);//插入换行
usart_rx_sta=0;
}else
{
times++;
if(times%5000==0)
{
printf(“
alientek ministm32开发板 串口实验
”);
printf(“正点原子@alientek
”);
}
if(times%200==0)printf(“请输入数据,以回车键结束
”);
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);//等待发送结束


双方“专利战”不断,董明珠:奥克斯天天在格力“挖人”
魅族Lipro LED智能吸顶灯两款产品将在Q1推出
集成电路型号查询
芯华章汽车芯片应用案例斩获国家级奖项
京东获全国首张陕西无人机空域书面批文
STM32的串口编程实验理解分析
康鹏科技成功登陆科创板,上市大涨60.51%
汽车智造浪潮已然袭来,小米还在等什么?
探索汽车PREEvision中的以太网设计
华润水泥大排砂石矿无人运输运营项目举行设备进场仪式
国行华为Mate20系列高清图集
努比亚红魔Mars电竞手机评测 到底好不好用
调参心得:如何优化超参数的,如何证实方法是有效的
美的、碧桂园对机器人领域虎视眈眈,会给机器人行业带来怎样的变革呢?
闪测仪案例|手机零部件尺寸轻松测量
磐石测控:PS-9604S系列手机侧键手感测试机的产品详情?
220v转5v阻容降压电路原理
微电路具有自动关机和低电池锁定功能-Micropower C
Synopsys最新旗舰版IC Compiler™ II布局布线系统发布
RTT大赛作品:AB32VG1开发板—开发环境搭建