STM32学习心得十四:串口通信相关知识及配置方法

记录一下,方便以后翻阅~
主要内容:
1) 串行通信接口背景知识;
2) STM32F1串口框图讲解;
3) STM32串口部分常用寄存器和库函数;
4) 串口配置一般方法;
5) ALIENTEK提供的公用代码usart.c和usart.h解读。
官方资料:《STM32中文参考手册V10》第25章——通用同步异步收发器(USART)
1. 处理器与外部设备通信的两种方式
1.1 并行通信,传输原理:数据各个位同时传输。优点:速度快。缺点:占用引脚资源多。
1.2 串行通信,传输原理:数据按位顺序传输。优点:占用引脚资源少。缺点:速度相对较慢。
2. 串行通信(按照数据传送方向可分为)
2.1 单工:数据传输只支持数据在一个方向上传输(图a);
2.2 半双工:允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向上传输,实际上是一种切换方向的单工通信(图b);
2.3 全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力(图c)。
在这里插入图片描述
3. 串行通信的通信方式
3.1 同步通信:带时钟同步信号传输,包括SPI,IIC通信接口;
3.2 异步通信:不带时钟同步信号,包括UART(通用异步收发器,双方需事先约定好波特率),单总线。
4. 常见的串行通讯接口
在这里插入图片描述
5. STM32的串口通信接口
5.1 UART:通用异步收发器;
5.2 USART:通用同步异步收发器。
备注:大容量STM32F10x系列芯片,包含3个USART和2个UART。
6. UART异步通信方式引脚连接方法
6.1 RXD:数据输入引脚。数据接受;
6.2 TXD:数据发送引脚。数据发送。
在这里插入图片描述
6.3 开发版上的串口引脚如下表所示:
在这里插入图片描述
7. UART异步通信方式特点
7.1 全双工异步通信;
7.2 分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
7.3 可编程的数据字长度(8位或者9位);
7.4 可配置的停止位(支持1或者2位停止位);
7.5 可配置的使用DMA多缓冲器通信;
7.6 单独的发送器和接收器使能位;
7.7 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志;
7.8 多个带标志的中断源。触发中断;
7.9 其他:校验控制,四个错误检测标志。
8. 串口通信过程
在这里插入图片描述
9. STM32串口异步通信需要定义的参数
9.1 起始位;
9.2 数据位(8位或者9位);
9.3 奇偶校验位(第9位);
9.4 停止位(1,15,2位);
9.5 波特率设置。
在这里插入图片描述
10. STM32串口框图
10.1 对于大容量的STM32F10x芯片,有5个串口,串口1时钟来源PCLK2,串口2~4时钟来源PCLK1。
在这里插入图片描述
11. 常用的串口相关寄存器
11.1 USART_SR状态寄存器(串口通信的状态位);
11.2 USART_DR数据寄存器(读写都通过该寄存器);
11.3 USART_BRR波特率寄存器。
12. 串口操作相关库函数

void USART_Init();                      //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能//
void USART_Cmd();                       //使能串口//
void USART_ITConfig();                  //使能相关中断//
void USART_SendData();                  //发送数据到串口,针对DR寄存器//
uint16_t USART_ReceiveData();           //接受数据,从DR读取接受到的数据,针对DR寄存器//
FlagStatus USART_GetFlagStatus();       //获取状态标志位,针对SR寄存器//
void USART_ClearFlag();                 //清除状态标志位,针对SR寄存器//
ITStatus USART_GetITStatus();           //获取中断状态标志位,针对SR寄存器//
void USART_ClearITPendingBit();         //清除中断状态标志位,针对SR寄存器//

13. 串口常用寄存器解读
13.1 状态寄存器(USART_SR)

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

在这里插入图片描述
13.2 数据寄存器(USART_DR)

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

在这里插入图片描述
13.3 波特比率寄存器(USART_BRR)

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

在这里插入图片描述
14. 波特率计算方法
在这里插入图片描述
15. 串口配置一般步骤
15.1 串口时钟使能,GPIO时钟使能;

RCC_APB2PeriphClockCmd();     //USART1位于RCC_APB2PeriphClockCmd中//

15.2 串口复位;

USART_DeInit();               //这一步不是必须的//

15.3 GPIO端口模式设置;

GPIO_Init();                  //TX模式设置为GPIO_Mode_AF_PP,RX模式设置为GPIO_Mode_IN_FLOATING //

15.4 串口参数初始化;

USART_Init();

15.5 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤);

NVIC_Init();
USART_ITConfig();

15.6 使能串口;

USART_Cmd();

15.7 编写中断处理函数;

USARTx_IRQHandler();

15.8 串口数据收发;

void USART_SendData();                      //发送数据到串口,DR//
uint16_t USART_ReceiveData();               //接受数据,从DR读取接受到的数据//

15.9 串口传输状态获取;

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

在这里插入图片描述

void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

在这里插入图片描述
16. 串口配置案例

//案例说明:设置PA9和PA10位USART1的TX和RX,开启USART_IT_RXNE接收中断,中断服务函数目的为,当USART_IT_RXNE发生,USART1接收数据并发送回去,需配合串口调试助手进行实验//
#include "stm32f10x.h"
//编写我的初始化函数//
void My_USART1_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStruct;
 USART_InitTypeDef  USART_InitStruct;
 NVIC_InitTypeDef NVIC_InitStruct;
 //第一步,使能相关时钟//
 //使能GPIOA时钟,RCC_APB2PeriphClockCmd()位于stm32f10x.rcc.c文件中//
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); 
 //使能USART1时钟// 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);                      
 //第二步,串口复位//
 //将外设 USARTx 寄存器重设为缺省值,复位USART1,USART_DeInit()位于stm32f10x.usart.c文件中//
 USART_DeInit(USART1);                                                            
 //第三步,GPIO端口模式设置,PA9对应RXD,PA10对应TXD//
 //先对PA9进行设置,模式为复用推挽输出,引脚9,速度随意//
 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;         //复用推挽输出//
 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;               //引脚9//
 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;       //随意设置//
 //根据上述GPIO_InitStruct中指定的参数初始化外设GPIOA寄存器,,GPIO_Init()位于stm32f10x.gpio.c文件中//
 GPIO_Init(GPIOA,&GPIO_InitStruct);                                          
 //再对PA10进行设置,模式为浮空输入或带上拉输入,引脚10,速度随意//
 GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING;   //浮空输入//
 GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;              //引脚9//
 GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz;       //随意设置//
 //根据GPIO_InitStruct中指定的参数初始化外设GPIOA寄存器,GPIO_Init()位于stm32f10x.gpio.c文件中//
 GPIO_Init(GPIOA,&GPIO_InitStruct);                                           
 //第四步,串口参数初始化,在电脑上进行串口通信时,串口调试软件也需按下述参数设置//
 USART_InitStruct.USART_BaudRate=115200;                                    //波特率为115200//
 USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用硬件流控制//
 USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;                 //收发都使能,可用或运算//
 USART_InitStruct.USART_Parity=USART_Parity_No;                             //不用奇偶校验位//                           
 USART_InitStruct.USART_StopBits=USART_StopBits_1;                          //设置1个停止位//
 USART_InitStruct.USART_WordLength=USART_WordLength_8b;                     //设字长为8,因为不用奇偶校验//
 //根据上述USART_InitStruct中指定的参数初始化外设USART1寄存器,USART_Init()位于stm32f10x.usart.c文件中//
 USART_Init(USART1,&USART_InitStruct); 
 //第五步,开启指定中断,并初始化NVIC//   
 //NVIC初始化// 
 NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;            //选择位于stm32f10x.h文件中STM32F10X_HD中的USART1_IRQn//
 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;              //使能上述中断通道//
 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;    //因为没有别的中断,根据中断分组2,参数可设0~3之间//
 NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;           //因为没有别的中断,根据中断分组2,参数可设0~3之间//
 //根据上述NVIC_InitStruct中指定的参数初始化外设NVIC寄存器,NVIC_Init()位于misc.c文件中//
 NVIC_Init(&NVIC_InitStruct);            
 //使能USART1的USART_IT_RXNE中断,RXNE是状态寄存器USART_SR的第5位,意思是接收中断// 
 USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); 
 //第六步,使能串口//  
 //使能USART1外设,USART_Cmd()位于stm32f10x.usart.c文件中// 
 USART_Cmd(USART1,ENABLE);   
}
 //第七步,编写中断服务函数//
 //函数名称USART1_IRQHandler是由位于启动文件startup_stm32f10x_hs.s文件里定义的//
void USART1_IRQHandler(void)                                                   
{
 u8 data;
 //第八步,串口状态获取//
 //检查USART1的接受中断USART_IT_RXNE发生与否,USART_GetITStatus()函数位于stm32f10x_usart.c文件中//
 if(USART_GetITStatus(USART1,USART_IT_RXNE))                                  
 {
 //第九步,串口数据收发,较为常用//
 //返回USART1最近接收到的数据,USART_ReceiveData()函数位于stm32f10x_usart.c文件中//
 data=USART_ReceiveData(USART1); 
 //通过外设USART1发送单个数据,USART_SendData()函数位于stm32f10x_usart.c文件中//  
 USART_SendData(USART1,data);                                               
 }
}
 //编写主函数//
 int main(void)
 { 
  //设置优先级分组:先占优先级和从优先级//
  //设置分组2,即2位抢占优先级,2位响应优先级,NVIC_PriorityGroupConfig()位于misc.c文件中//
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                             
  My_USART1_Init();
  while(1)
  {
  }
 }

17. usart.h代码讲解

#ifndef __USART_H
#define __USART_H
#include "stdio.h"    
#include "sys.h"
#define USART_REC_LEN    200            //定义最大接收字节数 200//
#define EN_USART1_RX     1              //使能(1)/禁止(0)串口1接收//
extern u8 USART_RX_BUF[USART_REC_LEN];  //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符// 
extern u16  USART_RX_STA;               //接收状态标记//       
//如果想串口中断接收,请不要注释以下宏定义//
void uart_init(u32 bound);              //设置波特率//
#endif

18. usart.c里中断服务函数USART1_IRQHandler函数代码讲解

void USART1_IRQHandler(void)      //串口1中断服务程序//
 {
   u8 Res;                          //定义一个变量Res,用来接收数据//
   #if SYSTEM_SUPPORT_OS                //如果SYSTEM_SUPPORT_OS为真,则需要支持OS//
       OSIntEnter();    
   #endif
   //*判断接收中断USART_IT_RXNE是否发生,发生则为1,不发生为*0//
   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)   //RESET=0//
      {
      //*若接收中断USART_IT_RXNE发生,Res读取接收到的数据*//
        Res =USART_ReceiveData(USART1);         
      //判断变量U16 USART_RX_STA的位15是否为0,当USART_RX_STA的位15为0时,则与运算后值为0,if返回1(真),反之if返回0(假)*//
        if((USART_RX_STA&0x8000)==0)                  
           {
           //*若USART_RX_STA的位15为0时,判断USART_RX_STA的位14是否为0*//
              if(USART_RX_STA&0x4000)                  
                 {
                 //*若USART_RX_STA的位14为1时,则判断Res接收的数据是否为0x0A,0x0A为换行标志*//
                    if(Res!=0x0A)    
                       //*若Res接收的数据不是换行标志,则接收错误,重新开始*//             
                       USART_RX_STA=0;   
                    //*若Res接收的数据是换行标志,则USART_RX_STA的位15至1* //       
                    else USART_RX_STA|=0x8000;      
                  }
               //*若USART_RX_STA的位14为0时,执行else *//
               else                                
                  {     
                    //*判断Res是否等于(接收到)0x0D,0x0D为回车标志*//
                    if(Res==0x0d) 
                       //*若Res接收到回车标志,if为1(真),则USART_RX_STA位14至1 //                      
                       USART_RX_STA|=0x4000;
                    //*若Res未接收到回车标志,if为0(假),执行else//
                    else                               
                         {
                          USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;
                          USART_RX_STA++;
                              //*若USART_RX_STA值大于所设最大接收字节数,则接收数据错误,重新开始接收*//   
                              if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;                              
                          }
                   }
           }                  
     }

上述代码中,变量U16 USART_RX_STA用法如下图所示:
在这里插入图片描述
19. main.c文件里主函数代码讲解

int main(void)
 {            
  u16 t;  
  u16 len;    
  u16 times=0;
  delay_init();                    //*延时函数初始化*//
  //设置NVIC中断分组2:2位抢占优先级,2位响应优先级//
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  uart_init(115200);            //串口初始化为115200//
  LED_Init();                       //LED端口初始化//
  KEY_Init();               //初始化与按键连接的硬件接口//
  while(1)
       {
         //*判断USART_RX_STA的位15是否为1*//
         if(USART_RX_STA&0x8000)
           {     
            //*若USART_RX_STA的位15为1*//                          
            len=USART_RX_STA&0x3fff;        //得到此次接收到的数据长度//
            printf("\r\n您发送的消息为:\r\n\r\n");
            for(t=0;t<len;t++)
               {
                  USART_SendData(USART1, USART_RX_BUF[t]);   //向串口1发送第t个数据//
                  //*等待发送结束,USART_FLAG_TC意思是Transmission Complete flag*//
                  while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
                }
             printf("\r\n\r\n");                                      //插入换行//
                     USART_RX_STA=0;
           }else
                {
                times++;
                if(times%5000==0)                                     //*times为5000的整数倍时*//
                  {
                    printf("\r\n战舰STM32开发板 串口实验\r\n");
                    printf("正点原子@ALIENTEK\r\n\r\n");
                   }
                 if(times%200==0)printf("请输入数据,以回车键结束\n");   //*times为200的整数倍时*//
                     //*times为30的整数倍时,闪烁LED,提示系统正在运行*//
                     if(times%30==0)LED0=!LED0;          
                     delay_ms(10);   
                 }
           }     
  }

20. usart.c里中断服务函数printf()函数代码讲解

#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数//                 
struct __FILE 
{ 
       int handle; 
}; 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式//   
_sys_exit(int x)
{ 
    x = x; 
} 
//重定义fputc函数//
int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕//  
    USART1->DR = (u8) ch;      
    return ch;
}
#endif
//若要对其他串口进行操作,只需将上述代码中的USART1改成USARTx即可。这段代码方便开发过程中查看代码执行情况以及一些变量值,该代码不需要修改,引入到 usart.h 即可//

旧知识点:
1)理解新建工程模板中, ALIENTEK提供的公用代码usart.c和usart.h的大致含义,可参考STM32学习心得二:新建工程模板
2)复习基于库函数的初始化函数的一般格式,可参考STM32学习心得三:GPIO实验-基于库函数
3)复习寄存器地址,可参考STM32学习心得四:GPIO实验-基于寄存器STM32学习心得五:GPIO实验-基于位操作
4)复习寄存器地址名称映射,可参考STM32学习心得六:相关C语言学习及寄存器地址名称映射解读
5)复习时钟使能相关函数,可参考STM32学习心得七:STM32时钟系统框图解读及相关函数
6)复习如何对GPIO进行复用,可参考STM32学习心得十二:端口复用和重映射
7)复习中断相关知识,可参考STM32学习心得十三:NVIC中断优先级管理

发布了30 篇原创文章 · 获赞 4 · 访问量 5130

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/105338401
今日推荐