2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

   

通信的两种方式:

并行通信

  -传输原理:数据各个位同时传输。
  -优点:速度快
   -缺点:占用引脚资源多

串行通信

  -传输原理:数据按位顺序传输。
  -优点:占用引脚资源少
  -缺点:速度相对较慢

串行通信分类(按照数据传送方向)

  单工(a):
     数据传输只支持数据在一个方向上传输

  半双工(b):
     允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;

  全双工(c):
     允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

串行通信的通信方式

同步通信(带时钟同步信号传输):

     SPI,IIC通信接口

异步通信(不带时钟同步信号传输):

    UART(通用异步收发器),单总线 约定传输速度

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

STM32的串口通信接口

  UART:通用异步收发器。
  USART:通用同步异步收发器。
    大容量STM32F10x系列芯片,包含3个USART和2个UART

UART异步通信方式引脚连接方法:

  -RXD:数据输入引脚。数据接受。
  -TXD:数据发送引脚。数据发送。

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

UART异步通信方式特点:

  全双工异步通信。

  分数波特率发生器系统,提供精确的波特率。
 -发送和接受共用的可编程波特率,最高可达4.5Mbits/s
  可编程的数据字长度(8位或者9位);
  可配置的停止位(支持1或者2位停止位);
  可配置的使用DMA多缓冲器通信。
  单独的发送器和接收器使能位。
  检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
  多个带标志的中断源。触发中断。
  其他:校验控制,四个错误检测标志。

 串口通信过程

 2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

 发送数据:通过总线往USARTx控制器中的DR寄存器(TDR),写入数据,USARTx控制器会自动通过Tx管脚发送出去。

 接收数据:USARTx控制通过Rx管脚接收到位数据,组合成8位数据,存在DR寄存器(RDR),直接读取DR寄存器。

 

STM32串口异步通信需要定义的参数

 

1 起始位
2 数据位(8位或者9位)
3 奇偶校验位(第9位)
4 停止位(1,15,2位)
5 波特率设置

 

协议规定了什么?硬件层、电平标准、通信数据格式、传输速率

                     开始位   +数据位 +奇偶校验位 +停止位

位数      1       5~8       0~1        1

电平      0       0/1        0/1        1

开始位:低电平 -- 设备检测下降沿,代表开始

数据位:5 -- 0000 0101   5~8位 -- 8

奇偶校验位:校验一帧数据是否完整

奇偶校验:数据位中1的个数+奇偶位中1的个数之和

如果是奇校验:个数之和必须为奇数。

如果是偶校验:个数之和必须为偶数。

例如:发送方:0110 0011 -- 0x63   采用奇校验     奇偶校验位为1

接收方:0110 0011    1   --- 正确  

                0100 0011    1   --- 错误

               0000 0011    1   --- 奇偶校验正确,数据错误    -- 现在采用CRC校验。

停止位:高电平 -- 总线空闲状态为高电平

常用的帧格式:1+8+0+1 -- 1个开始位+8个数据位+0个奇偶校验位+1个停止位

通信速率:波特率   bps  每秒钟发送的位数,常见的波特率:9600  115200等等

进行通信的两个设备,波特率必须一样

 

常用的串口相关寄存器

  USART_SR状态寄存器
  USART_DR数据寄存器
  USART_BRR波特率寄存器 (填写下面计算后的数值)
  USART_CR1控制寄存器
根据该图可了解串行通信的相关配置。根据下半图,可得到波特率的计算方法:

 

 2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

 2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

 2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

 

 

串口操作相关库函数

void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断

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

FlagStatus USART_GetFlagStatus();//获取状态标志位,SR
void USART_ClearFlag();//清除状态标志位,SR
ITStatus USART_GetITStatus();//获取中断状态标志位,SR
void USART_ClearITPendingBit();//清除中断状态标志位,SR

实验4串口实验的代码中FWLib文件夹下stm32f10x_usart.c下的stm32f10x_usart.h下,可找到相关函数的声明与定义,然后可再进行追溯。

串口配置一般步骤

 ①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
  ②串口复位:USART_DeInit(); 这一步不是必须的
  ③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
  ④串口参数初始化:USART_Init();
  ⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
    NVIC_Init();
    USART_ITConfig();
  ⑥使能串口:USART_Cmd();
  ⑦编写中断处理函数:USARTx_IRQHandler();
  ⑧串口数据收发:
    void USART_SendData();//发送数据到串口,DR
    uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

 具体实现

第一步:新建模版,并使能串口时钟和GPIO时钟。

跟之前一样,先建立一个简单的模版。
  在USER文件夹下找到system_stm32f10x.c下的stm32f10x_rcc.h.中找到void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);之前就知道这个函数是用来使能的,然后先Go To xxx找到函数的定义后,在函数中assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));

查看函数中的第一个参数RCC_APB2PeriphGo To xxx后我们可以看到,该函数即可使能GPIOA又可使能USART1。故使能语句可这么编写

void My_USART1_Init(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
}

串口复位非必须,故可省略。

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

第二步:GPIO端口模式设置

在USER文件夹下找到system_stm32f10x.c下的stm32f10x_gpio.h.中找到void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);该函数之前已经讲解过了,程序可修改为:


2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

 另外一个USARTX_RX是输入 ,根据上面的图知道设为上拉输入或者浮空

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

    //2. 配置模式
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
void My_USART1_Init(){
    GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);    //使能GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);    //使能串口时钟
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;    //设置为复用推挽输出
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;    //引脚9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式设置
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;    //引脚10
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式设置
    
}

 

 

第三步:串口参数初始化和使能串口

在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);、void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);,程序可修改为:

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

/* 配置USART模式 */
    //1. 时钟使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //2. 模式配置
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = brr;                                        //波特率   (数据传输速度)
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //非硬件流 (如何决定收发的时机)
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 //收发模式 (全双工)
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     //RS232协议(数据格式):数据位     8个
    USART_InitStructure.USART_Parity = USART_Parity_No;                                //RS232协议(数据格式):奇偶校验位 0个
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                            //RS232协议(数据格式):停止位     1个
    USART_Init(USART1, &USART_InitStructure);

 

void My_USART1_Init(){
    GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
    USART_InitTypeDef USART_InitStructure;//定义USART结构体
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);    //使能GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);    //使能串口时钟
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;    //设置为复用推挽输出
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;    //引脚9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式设置
    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;    //引脚10
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;    //速度10MHz
    GPIO_Init(GPIOA,&GPIO_InitStructure);    //GPIOA模式设置
    
    
    USART_InitStructure.USART_BaudRate=115200;//波特率
    USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
    USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
    USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
    USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
    USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
    
    
    USART_Init(USART1,&USART_InitStructure);//串口参数初始化
    USART_Cmd(USART1,ENABLE);    //使能串口
}

第四步:开启中断并且初始化NVIC

  在FWLIB文件夹下找到misc下找到void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup),函数中找到;assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));,在Go To xxx,查看填写格式,选择NVIC_PriorityGroup_2,即:两位响应优先级和两位抢占优先级。

  在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

写NVIC函数

  //3. 配置接收中断(串口回显时注释掉)
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);            //接收中断打开
    NVIC_InitTypeDef NVIC_InitStruct = {0};
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_Init(&NVIC_InitStruct);
//4. 初始状态 -- 使能
    USART_Cmd(USART1, ENABLE);

第五步:编写中断处理函数

在文件stm32f10x_usart.h文件下打开:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

void USART1_IRQHandler(void){
    u8 res;
    if(USART_GetITStatus(USART1,USART_IT_RXNE)){//若是接收到中断
        res=USART_ReceiveData(USART1);//接收数据
        USART_SendData(USART1,res);//接收到在发送数据,才可以在串口监视器中看到数据
        
    }
}

最后编译就通过了,注意要把模版中文件夹SYSTEM中的uart删除,因为定义重复了。

 

之后就可以上传程序,然后利用串口调试器,注意调试器中的设置要跟程序一样,波特率为115200,停止位1,等等。最终的实验现象就是发送什么,最后在调试软件中就会看到什么。

 

SYSTEM文件夹下,usart.c文件中,可看到以下代码(实验4串口实验):

 

main函数中调用函数

 

 

void USART1_IRQHandler(void)                    //串口1中断服务程序
    {
    u8 Res;
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
        Res =USART_ReceiveData(USART1);    //读取接收到的数据
        
        if((USART_RX_STA&0x8000)==0)//接收未完成,判断最高位是不是0,是0表示接收未完成,则往下执行。若为1则不往下执行(上一次接收没清空)
            {
            if(USART_RX_STA&0x4000)//接收到了0x0d,若第二位为1则表示接收到0x0d,在往下判断下一位是不是0x0a,若不是重新开始,是的话则则给第一位置1
                {
                if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
                else USART_RX_STA|=0x8000;    //接收完成了 
                }
            else //还没收到0X0D,第二位不是1,则判断什么时候接收到0x0d时,将第二位置为1
                {    
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else//若一直没收到的话,则一直讲Res变量中的数值给变量,且通过位与来判断该位是否溢出(若第1、2位有数据给他清零)
                    {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收      
                    }         
                }
            }            
     } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntExit();                                               
#endif
} 
#endif    

 

实验4代码详细解释

usart.h

 

 

#ifndef __USART_H
#define __USART_H
#include "stdio.h"    
#include "sys.h" 
//////////////////////////////////////////////////////////////////////////////////     
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//串口1初始化           
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/8/18
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改说明 
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//V1.4修改说明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字节数为2的14次方
//3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改说明
//1,增加了对UCOSII的支持
#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

 

bound 是波特率

extern 定义了一些变量 外部变量

 

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

 

接收从电脑传来的数据存入buf

 

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 0x0D=回车  0x0A=换行     二个结束符

接收完成 bit15-1  bit14-1  bit13~0 ----接收的位数

void USART1_IRQHandler(void)                    //串口1中断服务程序
{
    u8 Res; 
#if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntEnter();    
#endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
        {
        Res =USART_ReceiveData(USART1);    //读取接收到的数据
        
        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 ;//=0x3fff最多2^13-1数据
                    USART_RX_STA++;//有效数据个数++
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收      
                    }         //有效的数据个数不能大于定义的数据个数
                }
            }            
     } 
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
    OSIntExit();                                               
#endif
} 
#endif    

2021/10/15 智能家具 嵌入式实训 第五天 串口通信 (1)

 

 

工程文件可供参考:

https://wwa.lanzoui.com/iG84Jvh1loj

 

上一篇:2021/10/15 智能家具 嵌入式实训 第五天 串口通信发送指令控制部件运行 (led beep)


下一篇:STM32F429 CubeMX DMA+空闲中断实现RS485不定长度自动收发