OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4892|回复: 14

STM32关于串口编程的心得

[复制链接]

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
发表于 2018-11-13 10:20:09 | 显示全部楼层 |阅读模式
本帖最后由 七剑天山 于 2018-11-13 11:16 编辑

初学STM32,每一个环节都是新大陆。所以每一个环节都是新的挑战,我想这其实对每一个新手都有这样的过程。
首先接触到了串口通讯。我也是这几天才接触到这个内容,看了原子的书籍,例子程序,均没有办法进行正常所需要的通讯,可以说达到了让人生不如死的崩溃地步,但是最终还是成功了。所以把我的经验进行分享。

首先用实验3串口通讯例程。

第一步了解串口通讯的结构。
USART1,硬件收发接口为PA9,PA10,
在文件usart.c中就能找到设置端口的代码相应的设置函数uart_init(u32 pclk2,u32 bound);
RCC->AHB1ENR|=1<<0;           
        RCC->APB2ENR|=1<<4;         
        GPIO_Set(GPIOA,PIN9|PIN10,GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_50M,GPIO_PUPD_PU);//PA9,PA10,
        GPIO_AF_Set(GPIOA,9,7);        //PA9,AF7
        GPIO_AF_Set(GPIOA,10,7);      //PA10,AF7         
其它代码顺序往下看就可以了。
从设置中可以看到用于串口收发的硬件端口为PA9,PA10,所以串口调试线等接到这两个端口上就可以了。可以根据需求设置到其它有通讯接口的端口。

1、首先说串口数据的接收。
当所设定的相应的硬件端口有数据接收进来时会自动触发串口接收中断函数程序。void USART1_IRQHandler(void),
他在 startup_stm32f767xx.s等相应的启动文件中被定义成中断函数。
131               DCD     USART1_IRQHandler                 ; USART1  

所以数据的接收过程是怎么运行的就可以去看此函数的程序。
void USART1_IRQHandler(void)
{
        u8 res;        
#if SYSTEM_SUPPORT_OS                 
        OSIntEnter();   
#endif
        if(USART1->ISR&(1<<5)
        {         
                res=USART1->RDR;
                if((USART_RX_STA&0x8000)==0)
                {
                        if(USART_RX_STA&0x4000)
                        {
                                if(res!=0x0a)USART_RX_STA=0;
                                else USART_RX_STA|=0x8000;
                        }else
                        {        
                                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;
                                }                 
                        }
                }                                                                                                
        }


从程序中可以看到,数据的传输在两个位置;
1、res=USART1->RDR;    //接收的数据传给res
2、USART_RX_BUF[USART_RX_STA&0X3FFF]=res;      //res再传给数组中从低到高依次存储。


再看主程序调用:
int main(void)
{
        u8 t;
        u8 len;        
        u16 times=0;  
        u8 led0sta=1;                        
    delay_init(216);               
        uart_init(108,115200);        
        LED_Init();                                 
        while(1)
        {
                if(USART_RX_STA&0x8000)
                {                                          
                        len=USART_RX_STA&0x3fff;
                         printf();
                        for(t=0;t<len;t++)
                        {
                                USART1->TDR=USART_RX_BUF[t];
                                while((USART1->ISR&0X40)==0);
                        }
                        printf("\r\n\r\n");
                        USART_RX_STA=0;
                }else
                {
                        times++;
                        if(times%5000==0)
                        {
                                printf("");
                                printf();
                        }
                        if(times%200==0)printf("");  
                        if(times%30==0)LED0(led0sta^=1);
                        delay_ms(10);   
                }
        }
}

那么讲到了这里,上面那么多是不是很复杂繁琐?所以我们要学会化繁为简的方法。
1、知道端口的对应设置
2、知道接收数组函数USART_RX_BUF[0~n]
那么在主程序内任意的地方就可以通过此函数进行使用了

如串口接收到3个字符985,那么在程序中就可以这样用

a=USART_RX_BUF[0];       //数组的第零位的数传给a
b=USART_RX_BUF[1];       //数组的第1位的数传给b
c=USART_RX_BUF[3];       //数组的第1位的数传给c
运行后
a=9
b=8
c=5

记得运行完成后把数组清空以便下一次接收数据
USART_RX_BUF[0]=0x0;     //数组第零位就清空了。
USART_RX_BUF[1]=0x0;     //数组第1位就清空了。
USART_RX_BUF[2]=0x0;     //数组第2位就清空了。
接收过程完成后再做最终的扫尾工作
USART_RX_STA=0;            //将接收状态标志清零,以便下次接收数据使用。

下面是一个重要的问题一定要注意,上述函数接收程序所传输的是ascii字符。

字符0~9对应的十进制数是48~57,两者相差数48.如接收到的字符数5,那么对应的十进制数是53,那么就该减去48后变成十进制的5.那么上面的函数接收程序就可以如下编写。
a=(USART_RX_BUF[0]-48)*100+(USART_RX_BUF[1]-48)*10+(USART_RX_BUF[2]-48)    //百位乘100,十位乘10,
这样接收到的字符串"985"就转换成了十进制985了。就可以通过它进行十进制的加减乘除等运算了。

最后讲串口的发送。
只记住一条:
在主程序中的任意的地方插入发送语句:
USART1->TDR=m;      //将m中的数通过串口输出
或者
USART1->TDR=0x15;      //直接将数15通过串口输出
等均可。

//---------------------------------------------
//最终总结:熟悉我上面说的方法后最终如下在主程序内需要的任何位置简单方便使用就可以了,而不需要漫天飘雪的丈二摸不清头脑乱抓一气。
//---------------------------------------------
接收:
a=USART_RX_BUF[0];       //数组的第零位的数传给a  
USART_RX_BUF[0]=0x0;     //数组第零位清空。
USART_RX_STA=0;            //将接收状态标志清零,以便下次接收数据使用。
发送:
USART1->TDR=m;      //将m中的数通过串口输出

整个串口通讯最终浓缩成了这4条语句,是不是非常精简,高效,实用。





正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

633

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1331
金钱
1331
注册时间
2016-8-1
在线时间
229 小时
发表于 2018-11-13 16:29:03 | 显示全部楼层
谢谢分享  不错,撸代码不是问题
Loto虚拟示波器 官方qq群: 706769836 https://shop296209296.taobao.com/shop/view_shop.htm?tracelog=t
回复 支持 反对

使用道具 举报

109

主题

5564

帖子

0

精华

资深版主

Rank: 8Rank: 8

积分
10571
金钱
10571
注册时间
2017-2-18
在线时间
1914 小时
发表于 2018-11-13 22:25:10 | 显示全部楼层
多谢分享
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
 楼主| 发表于 2018-11-14 23:21:49 | 显示全部楼层
本帖最后由 七剑天山 于 2018-11-14 23:28 编辑

继续学习中。
回复 支持 反对

使用道具 举报

6

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
189
金钱
189
注册时间
2018-8-17
在线时间
30 小时
发表于 2018-11-15 00:23:19 | 显示全部楼层
楼主的你这个只能正确发送和接受三位数  如果一个东西需要0-999   按照这个程序  当串口发送数据1的时候输出的也是100   输入100 输出也是100   (我也在用学串口 但是一直搞不懂这个数据传送咋搞的)
学习一点都不快乐
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
 楼主| 发表于 2018-11-15 00:25:32 | 显示全部楼层
本帖最后由 七剑天山 于 2018-11-15 10:12 编辑

下面是我学习后正在编写的串口接收工程文件
//--------
//串口接收,有串口接收数据时自动调用接收中断函数 USART1_IRQHandler(void)
//--------  
      if(USART_RX_STA&0x8000)                      //判断接收状态标志STA,为1说明串口中断程序已经把数据接收完毕,可以进行数组中的数据提取了。STA为1后才能进入if下面的语句中在数组中提取数据,否则会出现误码。
                      {                                                                                                      
                      w=USART_RX_BUF[1];            //用于返回PC字符提示信息
                      h=USART_RX_BUF[2];             //用于返回PC字符提示信息    对照ascii码表,收到的数字为ascii码的对应1~9字符(对应十进制48~57),而不是十进制的1~9.
                      y=(USART_RX_BUF[0]-48);      //功能码                        ascii码表中字符的0~9与十进制的0~9相差48,所以要把字符的0~9转换成10进制的0~9就减去48.        
                      q=(USART_RX_BUF[1]-48)*10+(USART_RX_BUF[2]-48);                   // 将十位ascii码字符0~9减48变成十进制0~9,再乘以10变成十位数,个位的ascii码字符0~9减48变成十进制0~9,再相加组合成两位十进制数
                      USART_RX_STA=0;                 //数据提取后把状态标志清零,为下一次接收做准备。
                      }
                   else
                      {
                       y=0;     //功能码y复位
                      }

在反复测试中发现接收程序中下面的STA接收状态标志判断语句必须要,不能省掉,否则易出现误码丢数据的现象。
  if(USART_RX_STA&0x8000)
    {
    }
   else
   {
   }
回复 支持 反对

使用道具 举报

6

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
189
金钱
189
注册时间
2018-8-17
在线时间
30 小时
发表于 2018-11-15 00:30:00 | 显示全部楼层
七剑天山 发表于 2018-11-15 00:25
下面是我学习后正在编写的串口接收工程文件
//--------
//串口接收,有串口接收数据时自动调用接收中断函 ...

这个可以接受0--999的数据了吗??不太能看懂
学习一点都不快乐
回复 支持 反对

使用道具 举报

6

主题

30

帖子

0

精华

初级会员

Rank: 2

积分
189
金钱
189
注册时间
2018-8-17
在线时间
30 小时
发表于 2018-11-15 00:40:31 | 显示全部楼层
七剑天山 发表于 2018-11-15 00:25
下面是我学习后正在编写的串口接收工程文件
//--------
//串口接收,有串口接收数据时自动调用接收中断函 ...

您的这个还是不太对
学习一点都不快乐
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
 楼主| 发表于 2018-11-15 09:29:43 | 显示全部楼层
1157243379 发表于 2018-11-15 00:30
这个可以接受0--999的数据了吗??不太能看懂

我的是用在特殊通讯场合,功能码+0~99,要传输更多位数及其它功能的,就如法炮制,每个工程的应用需求不同,所以做到举一反三,重在方法就对了。
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
 楼主| 发表于 2018-11-15 09:33:52 | 显示全部楼层
本帖最后由 七剑天山 于 2018-11-15 10:16 编辑

6楼上面部分是应用案例,下面部分是精简的应用程序框架结构总结。这个框架中,原子例程是在这个框架外再用while(1)死循环接收。结构如下。
while(1)                                                 //无限循环操作下面的过程
        {
         if(USART_RX_STA&0x8000)            //判断接收状态标志STA,为1说明串口中断程序已经把数据接收完毕,可以进行数组中的数据提取了。
              {
                ---------------------------             //数组数据提取处
               USART_RX_STA=0;                  //数据提取后把状态标志清零,为下一次接收做准备。            
              }
         else
              {                           
               -----------------------------           //没有新数据可以在这里提示,或者做其它事情,复位其它功能码,闲着喝茶玩等均在这里。
              }
         }

上面的框架是原子程序为了做演示程序方便学习者使用,而在实际应用中,我把while(1){}循环去掉了,根据接收到的相应功能码去执行相应的模块后再通过返回指令回到此串口接收程序功能码判断处重新等待串口数据的接收。






回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手上路

积分
40
金钱
40
注册时间
2018-11-17
在线时间
8 小时
发表于 2018-11-17 13:38:57 | 显示全部楼层
如果我需要接收一个字符串,有可能包含汉字,字母及符号,我应该则么拼接成一个字符串,和现有的字符串进行比较呢?
[mw_shl_code=applescript,true]例如:发送 LED点亮  
if(xx == "LED")  //LED操作指令
{
    if(xx2=="点亮")
       {。。。。。}
    if (xx2=="熄灭")
        {.。。。。}
}[/mw_shl_code]
这个则么实现呢?
回复 支持 反对

使用道具 举报

29

主题

135

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
329
金钱
329
注册时间
2018-10-19
在线时间
28 小时
发表于 2018-11-17 15:40:36 | 显示全部楼层
支持一下
回复 支持 反对

使用道具 举报

29

主题

135

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
329
金钱
329
注册时间
2018-10-19
在线时间
28 小时
发表于 2018-11-17 16:01:16 | 显示全部楼层
1157243379 发表于 2018-11-15 00:23
楼主的你这个只能正确发送和接受三位数  如果一个东西需要0-999   按照这个程序  当串口发送数据1的时候输 ...

0D)P337{Y{%R46{@T%OIJ.png
试一试,收发一下就知道了,我也加深了认识

回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2018-10-28
在线时间
7 小时
发表于 2018-11-18 15:08:49 | 显示全部楼层
我想问一下。两个串口怎么实现?
回复 支持 反对

使用道具 举报

1

主题

9

帖子

0

精华

新手上路

积分
20
金钱
20
注册时间
2018-10-25
在线时间
6 小时
 楼主| 发表于 2018-11-18 17:20:54 | 显示全部楼层
本帖最后由 七剑天山 于 2018-11-18 17:24 编辑
xiaomo123 发表于 2018-11-18 15:08
我想问一下。两个串口怎么实现?

两个串口还没有试过,应该都是在timer.c里面对应添加另外的串口的中断程序及初始化相应的串口及IO,参照对比着原子程序做应该就可以成功。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-8 18:01

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表