• STM32的USART


    STM32串口介绍

    STM32F103串口

    STM32F103ZET6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA等。

    • 三个通用同步异步收发器USART
    • 两个通用异步收发器UART
      • 二者区别就在于一根时钟线,UART少一根线
        在这里插入图片描述

    STM32F407串口

    最多可提供 6 路串口

    串口连接

    USART主机接USART从机

    在这里插入图片描述

    USART主机接电脑USB

    在这里插入图片描述
    一般的单片机硬件设计都会预留一个USART连接电脑,用于在调试程序时可以将调试信息打印在电脑端的串口调试助手。
    但单片机的USART无法直接与PC通讯,需要借助CH340或CP2102等芯片实现USB与USART的相互转化。

    STM32F103 板载的 USB 串口和 STM32F103ZET6 的串口是通过 P4 连接起来的
    在这里插入图片描述

    P4 是 PA9 和 PA10 的引出口。这里我们把 P4 的 RXD 和 TXD 用跳线帽与 PA9 和 PA10 连接起来。
    在这里插入图片描述

    TXD/RXD 是相对 CH340G 来说的,也就是 USB 串口的发送和接收引脚。

    • 这样设计的好处就是使用上非常灵活。比如需要用到外部 TTL 串口和 STM32 通信的时候,只需要拔了跳线帽,通过杜邦线连接外部 TTL 串口,就可以实现和外部设备的串口通信了;

    USART主机接RS232从机

    在这里插入图片描述
    单片机的USART无法直接与232设备通讯,需要借助MAX232等芯片实现232与USART的相互转化。
    在这里插入图片描述

    USART主机接RS485从机

    在这里插入图片描述

    串口相关寄存器

    波特率寄存器(USART_BRR)

    在这里插入图片描述

    状态寄存器 USART_SR

    在这里插入图片描述
    关注第 5、6 位 RXNE 和 TC。

    • RXNE(读数据寄存器非空),当该位被置 1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。
    • TC(发送完成),当该位被置位的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:1)读 USART_SR,写USART_DR。2)直接向该位写 0。
      串口状态

    数据寄存器(USART_DR)

    在这里插入图片描述在这里插入图片描述

    控制寄存器 1(USART_CR1)

    在这里插入图片描述
    在这里插入图片描述

    控制寄存器 2(USART_CR2)

    在这里插入图片描述
    在这里插入图片描述

    控制寄存器 3(USART_CR3)

    在这里插入图片描述
    在这里插入图片描述

    串口初始化设置

    配置中断优先级

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设 置 NVIC 中 断 分 组 2
    
    • 1

    串口时钟使能,GPIO时钟使能

    串口1是挂载在APB2 下面的外设,所以使能函数为:

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能 USART1,GPIOA 时钟
    
    • 1

    串口复位

    当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置这个外设达到让其重新工作的目的。
    一般在系统刚开始配置外设的时候,都会先执行复位该外设的操作。复位的是在函数 USART_DeInit()中完成:
    void USART_DeInit(USART_TypeDef* USARTx);//串口复位
    比如我们要复位串口 1,方法为:
    USART_DeInit(USART1); //复位串口 1

    GPIO 端口模式设置

    STM32F103串口端口设置

    //GPIO 端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    接下来的两段代码就是将 TX(PA9)设置为推挽复用输出模式,将 RX(PA10)设置为浮空输入模式:

    //USART1_TX PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.0 发送端
    //USART1_RX PA.10 浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.10 接收端
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    中文参考手册:
    在这里插入图片描述

    STM32F407串口设置

    串口参数初始化

    usart.c里面包含了2个函数
    一个是void USART1_IRQHandler(void);
    另外一个是void uart_init(u32bound);

    串口初始化是通过 USART_Init()函数实现的,
    void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

    结构体指针变量 USART_InitStructure分析

    typedef struct
    {
    uint32_t USART_BaudRate;
    uint16_t USART_WordLength;
    uint16_t USART_StopBits;
    uint16_t USART_Parity;
    uint16_t USART_Mode;
    uint16_t USART_HardwareFlowControl;
    } USART_InitTypeDef;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这个结构体有 6 个成员变量,所以我们有 6 个参数需要初始化。

    • USART_BaudRate 为串口波特率,波特率可以说是串口最重要的参数了。参见波特率寄存器(USART_BRR)
    • USART_WordLength 为字长,这里我们设置为 8 位字长数据格式。参见 控制寄存器 1(USART_CR1)
    • USART_StopBits 为停止位设置,我们设置为 1 位停止位。参见控制寄存器 2(USART_CR2)第12位
    • USART_Parity 设定是否需要奇偶校验,我们设定为无奇偶校验位。参见控制寄存器 1(USART_CR1)第9位。
    • USART_Mode 为串口模式,我们设置为全双工收发模式。参见 控制寄存器 3(USART_CR3)第三位
    • 第六个参数为是否支持硬件流控制,我们设置为无硬件流控制。
    USART_InitTypeDef USART_InitStructure;
    //USART 初始化设置,接下来我们要设置串口 1 的初始化参数:
    USART_InitStructure.USART_BaudRate = bound;//波特率设置;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl=
    USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx |USART_Mode_Tx;//收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    开启串口响应中断

    如果需要开启中断的话。
    使能串口中断的函数是:
    void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)
    函数的第二个入口参数标识使能串口的类型,也就是使能哪种中断。
    比如在接收到数据的时候(RXNE 读数据寄存器非空)要产生中断,那么我们开启中断的方法是:
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
    我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:
    USART_ITConfig(USART1,USART_IT_TC,ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;
    紧接着,我们要进行 usart1 的中断初始化,设置抢占优先级值和子优先级的值:

    //Usart1 NVIC 中断配置 
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //对应中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级 3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
    NVIC_Init(&NVIC_InitStructure); //中断优先级配置
    //完成串口中断优先级以及串口初始化之后,接下来就是开启串口中断以及使能串口了:
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    串口使能

    串口使能是通过函数 USART_Cmd()来实现的,这个很容易理解,使用方法是:
    USART_Cmd(USART1, ENABLE); //使能串口

    编写中断处理函数

    中断编写

    void USART1_IRQHandler(void)函数是串口 1 的中断响应函数,当串口 1 发生了相应的中断后,就会跳到该函数执行。

    EN_USART1_RX 和 USART_REC_LEN 都是在 usart.h 文件里面定义的
    当需要使用串口接收的时候,我们只要在 usart.h 里面设置 EN_USART1_RX 为 1 就可以了。
    不使用的时候,设置,EN_USART1_RX 为 0 即可,
    默认是设置 EN_USART1_RX为 1,也就是开启串口接收的。

    这里我们设计了一个小小的接收协议:
    该函数,配合一个数组 USART_RX_BUF[],一个接收状态寄存器 USART_RX_STA(此寄存器其实就是一个全局变量,由作者自行添加。由于它起到类似寄存器的功能,这里暂且称之为寄存器)实现对串口数据的接收管理。
    USART_RX_BUF 的大小由 USART_REC_LEN 定义,也就是一次接收的数据最大不能超过USART_REC_LEN 个字节。
    在这里插入图片描述

    当接收到从电脑发过来的数据,把接收到的数据保存在 USART_RX_BUF 中
    同时在接收状态寄存器(USART_RX_STA)中计数接收到的有效数据个数
    当收到回车(回车的表示由 2个字节组成:0X0D 和 0X0A)的第一个字节 0X0D 时,计数器将不再增加,等待 0X0A 的到来,而如果 0X0A 没有来到,则认为这次接收失败,重新开始下一次接收。
    如果顺利接收到 0X0A,则标记 USART_RX_STA 的第 15 位,这样完成一次接收
    等待该位被其他程序清除,从而开始下一次的接收。
    如果迟迟没有收到 0X0D,那么在接收数据超过 USART_REC_LEN 的时候,会丢弃前面的数据,重新接收。

    具体函数:

    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);//(USART1->DR); //读取接收到的数据
    if((USART_RX_STA&0x8000)==0)//接收未完成,后边清零器,第十六位是0则0,是1则1;
    {
    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;
    //接收数据错误,重新开始接收
    }
    }
    }
    }
    #if SYSTEM_SUPPORT_OS //如果 SYSTEM_SUPPORT_OS 为真,则需要支持 OS.
    OSIntExit();
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    数据发送与接收

    STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。
    当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。

    STM32 库函数操作 USART_DR 寄存器发送数据的函数是:
    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    通过该函数向串口寄存器 USART_DR 写入一个数据。

    STM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:
    uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
    通过该函数可以读取串口接受到的数据。
    Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
    RS485发送和接收数据

    串口状态

    串口的状态可以通过状态寄存器 USART_SR 读取。
    状态寄存器 USART_SR

    在固件库函数里面,读取串口状态的函数是:
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态
    例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:
    USART_GetFlagStatus(USART1, USART_FLAG_RXNE);
    我们要判断发送是否完成(TC),操作库函数的方法是:
    USART_GetFlagStatus(USART1, USART_FLAG_TC);

    这些标识在库函数都有定义

    #define USART_IT_PE ((uint16_t)0x0028)
    #define USART_IT_TXE ((uint16_t)0x0727)
    #define USART_IT_TC ((uint16_t)0x0626)
    #define USART_IT_RXNE ((uint16_t)0x0525)
    #define USART_IT_IDLE ((uint16_t)0x0424)
    #define USART_IT_LBD ((uint16_t)0x0846)
    #define USART_IT_CTS ((uint16_t)0x096A)
    #define USART_IT_ERR ((uint16_t)0x0060)
    #define USART_IT_ORE ((uint16_t)0x0360)
    #define USART_IT_NE ((uint16_t)0x0260)
    #define USART_IT_FE ((uint16_t)0x0160)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    获取相应中断状态

    当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄存器中的某个标志位。
    经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:

    ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
    比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这个函数来判断到底是否是串口发送完成中断,方法是:
    USART_GetITStatus(USART1, USART_IT_TC)
    返回值是 SET,说明是串口发送完成中断发生。
    或者通过函数:
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    判断是否接受中断,如果是串口接受中断,则读取串口接受到的数据:
    RS485发送和接收数据

    主函数调用

    回到 main.c,在 main.c 里面编写如下代码:

    #include "led.h"
    #include "delay.h"
    #include "key.h"
    #include "sys.h"
    #include "usart.h"
    int main(void)
    {
    u8 t;
    u8 len;
    u16 times=0;
    delay_init(); //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设 置 NVIC 中 断 分 组 2
    uart_init(115200); //串口初始化波特率为 115200
    LED_Init(); //LED 端口初始化
    KEY_Init(); //初始化与按键连接的硬件接口
    while(1)
    {
    if(USART_RX_STA&0x8000)
    { len=USART_RX_STA&0x3f; //得到此次接收到的数据长度
    printf("\r\n 您发送的消息为:\r\n\r\n");
    for(t=0;t<len;t++)
    { USART_SendData(USART1, USART_RX_BUF[t]); //向串口 1 发送数据
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
    //等待发送结束
    }
    printf("\r\n\r\n"); //插入换行
    USART_RX_STA=0;
    }else
    { times++;
    if(times%5000==0)
    { printf("\r\n 战舰 STM32 开发板 串口实验\r\n");
    printf("正点原子@ALIENTEK\r\n\r\n");
    }
    if(times%200==0)printf("请输入数据,以回车键结束\n");
    if(times%30==0)LED0=!LED0; //闪烁 LED,提示系统正在运行.
    delay_ms(10);
    }
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    串口使用总结

    • 如果用到了中断,首先要配置嵌套中断向量组
    • 嵌套中断向量组配置了就该配置中断优先级和中断通道
    • 中断初始化,中断使能
    • 根据串口挂载使能相应时钟,根据串口发送接收线使能相应端口
    • 端口初始化
    • 串口复位
    • 设置串口参数(波特率,帧结构,收发模式)
    • 串口初始化,串口使能
    • 中断处理函数

    端口重映射

    为了使不同器件封装的外设 IO 功能数量达到最优,可以把一些复用功能重新映射到其他一些引脚上(remap)。
    即一个外设的引脚除了具有默认的端口外,还可以通过设置重映射寄存器的方式,把这个外设的引脚映射到其它的端口。
    简单的讲就是把管脚的外设功能映射到另一个管脚,但不是可以随便映射的,具体对应关系《STM32 中文参考手册 V10》
    拿串口 1 为例来讲解。
    在这里插入图片描述

    从表中可以看出,默认情况下,串口 1 复用的时候的引脚位 PA9,PA10,同时我们可以将 TX 和 RX 重新映射到管脚 PB6 和 PB7 上面去。

    串口重映射步骤

    重映射我们同样要使能复用功能的时候讲解的 2 个时钟外,还要使能 AFIO 功能时钟,然后调用重映射函数。
    1)使能 GPIOB 时钟:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    2)使能串口 1 时钟:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    3)使能 AFIO 时钟:
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    4)开启重映射:
    GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
    USART1 只有一种重映射,而对于 USART3,存在部分重映射和完全重映射。所谓部分重映射就是部分管脚和默认的是一样的,而部分管脚是重新映射到其他管脚。而完全重映射就是所有管脚都重新映射到其他管脚。
    在这里插入图片描述

  • 相关阅读:
    校园跑腿小程序市场需要和功能分析!
    wireguard windows版本的配置
    java毕业设计超市商品管理mybatis+源码+调试部署+系统+数据库+lw
    开源的全能维护 U 盘工具:Ventoy
    易基因|DNA甲基化和单细胞RNA-seq联合揭示空气污染对复发性流产的表观遗传影响
    HandlerAdapter接口类的简介说明
    数据库-多表查询-子查询
    安科瑞为工业能效提升行动计划提供EMS解决方案-安科瑞黄安南
    Java基础(二)
    实现物联网的技术要素
  • 原文地址:https://blog.csdn.net/qq_45578181/article/details/126533370