串口通信USART(标准库)_usart串口

常使用MA3232芯片对TTL及RS-232电平的信号进行互相转换。

  • DB9接口

DB9接口就是常说的串口。

在目前的工业控制中使用的串口一般只使用RXD、TXD以及GND三条信号线,直接传输数据信号。

(2)协议层

串口通讯的协议层中规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成。

通讯双方的数据包格式要约定一致才能正常收发数据。

  • 波特率

串口异步通讯中由于没有时钟信号,所以两个设备直接需要约定好波特率。常见的波特率为4800/9600/115200等。

  • 通讯的起始和停止信号

数据包的起始信号由一个逻辑0的数据位表示,数据包的停止信号可由0.5/1/1.5/2个逻辑1的数据表示。

  • 有效数据

有效数据的长度常被约定为5/6/7或8位长。

  • 数据校验

有效数据之后,有一个可选的数据校验位。校验方法有奇校验(odd)。偶校验(even)、0校验(space)、1校验(Mark)以及无校验(noparity)。

3、STM32的USART

STM32芯片具有多个USART外设用于串口通信。

USART,即通用同步异步收发器。可以灵活地与外部设备进行全双工数据交换。

有别于USART,STM32还具有UART外设,UART在USART的基础上裁剪了同步通信功能,只有异步通讯。

我们平时用的串口通信基本都是UART。

USART满足外部设备对工业标准NRZ异步穿行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率。

USART支持同步单向通信和半双工单线通信,还支持局与互联网LIN,智能卡(SmartCard)协议和IrDA(红外线数据协会)SIR ENDEC规范。

USART支持使用DMA,可实现高速数据通信。

STM32的USART输出的是TTL电平信号,若需要RS-232标准的信号可使用MAX3232芯片进行转换。

(1)功能引脚

TX:发送数据输出引脚

RX:接收数据输入引脚

SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。

nRTS:请求已发送,n表示低电平有效。

nCTS:清除以发送,n表示低电平有效。

SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。

(2)数据寄存器

USART_DR数据寄存器,低9位有效,且第9位数据是否有效取决于控制寄存器1USART_CR1的M位设置,当M位为0表示8位数据字长,当M位为1时表示9位数据字长。一般使用8位数据字长。

USART_DR包含了已发送的数据或者接受的数据。USART_DR实际包含有两个寄存器,一个是专门用于发送的可写TDR,一个专门用于接受的可读RDR。

(3)控制器

USART有专门控制发送的发送器、控制接受的接收器,还有唤醒单元,中断控制等。

发送器

USART_CR1寄存器 UE位置1使能USART.

M位置0,8位数据字长,M位置1,9位数据字长。

TE位置1启动数据发送,发送移位寄存器的数据在TX引脚输出,同步模式时SCLK也输出时钟信号。

TCIE位置1,将产生中断。

USART_CR2寄存器 STOP盘[1:0]控制停止位的长短。默认1停止位,2个停止位适用于正常USART模式、单线模式和调制解调器模式。0.5个和1.5个停止位用于智能卡模式。

USART_SR USART状态寄存器。 在USART_DR寄存器写入最后一位数据之后,USART_SR的TC位置1,表示数据传输完成。

几个重要的标志位:

TE 发送使能

TXE 发送寄存器为空,发送单个字节的时候使用

TC 发送完成,发送多个字节数据的时候使用

TXIE 发送完成中断使能

接收器

USART_CR1的RE位置1,使能USART接受。

RX线开始搜索起始位。确定起始位后根据RX线电平状态把数据存放在接受移位寄存器内。

接受完后将接受移位寄存器的数据移到RDR内,并将USART_SR寄存器的RXNE位置1,如果USART_CR2寄存器的RXNEIE置1的话可以产生中断。

(4)小数波特率生成

波特率指数据信号对载波的调制速率。

USART波特率和比特率相等,波特率越大,传输速率越快。

USART的发送器和接收器使用相同的波特率。

波特率 = f_PLCK /(8*(2-OVER8)*USARTDIV)

f_PLCK为USART时钟。OVER8为USART_CR1寄存器的OVER8位对应的值。USARTDIV是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。

其中DIV_Mantissa[11:0]位定义USARTDIV的整数部分。DIV_Function[3:0]位定义USARTDIV的小数部分,DIV_Function[3]位只有在OVER8位为0时有效,否则必须为零。

例如:STM32F407ZGT6芯片的USART1和USART6使用APB2总线时钟,最高可达84MHz,即f_PLCK = 84MHz.

当使用16倍采样即OVER8 = 0,为得到115200bps波特率,此时:

115200 = 840000000 / 8*2 * USARTDIV.

USARTDIV = 45.57.DIV_Function = 0.57*16 = 9.12取整9,DIV_Function = 0x9,DIV_Mantissa= 45 = 0x2d.

即设置USART_BRR的值为0x2d9.

(5)校验控制/中断控制

USART_CR1的PCE位置1就可以启动奇偶校验控制,奇偶校验有硬件自动完成。当接受数据时如果出现奇偶校验位验证失败,会见USART_SR寄存器的PE位置1,并可以产生奇偶校验中断。

4、USART通信实践

(1)实验设计

实现开发板与电脑通信,在开发板上电时通过USART发送一串字符串给电脑,然后开发板进入中断接受等待状态,如果电脑有发送数据来,开发板产生中断,在中断服务函数中接受数据,并马上返回给电脑。

(2)硬件

CH340G芯片 USB转USART、USB转IrDA红外或者USB转打印机接口。

USART1的TX线连接 PA9/PB6,RX线连接PA10/PB7。这里选择 PA9/PA10.

(3)库函数编程要点
  • 使能RX和TX引脚的GPIO时钟和USART时钟

    • 串口时钟使能 RCC_APBxPeriphClockCmd();

    串口1和串口6是 APB2

    串口2/3/4/5是 APB1

    • GPIO时钟使能 RCC_AHB1PeriphClockCmd();
  • 初始化GPIO,并将GPIO复用到USART上

    • GPIO TX引脚初始为复用推挽输出 RX引脚初始化为浮空输入
    • GPIO_PinAFConfig();
    void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
    
    • GPIO_Init();
  • 配置USART参数

    • USART配置通过USART_InitTypeDef结构体实现

    初始化结构体定义在stm32f4xx_usart.h文件中,初始化库函数定义在stm32f4xx_usart.c文件中。

typedef struct
{
  uint32_t USART_BaudRate;  /*波特率 一般设置为2400,9600,19200,115200,标准库会自动计算并设置 */
  uint16_t USART_WordLength;/*字长 可选8位或9位,使能了奇偶校验一般设置9位,没有使能奇偶校验一般设置8位*/
  uint16_t USART_StopBits;  /*停止位 可选0.5个,1个,1.5个,2个。一般选择1个停止位*/
  uint16_t USART_Parity;    /*校验位 
                                    USART_Parity_No(无校验)
                                    USART_Parity_Even(偶校验)
                                    USART_Parity_Odd(奇校验)*/
  uint16_t USART_Mode;     /*USART模式 USART_Mode_Rx/USART_Mode_Tx,允许使用逻辑或运算选择两个*/
  uint16_t USART_HardwareFlowControl; /*硬件流控制*/
} USART_InitTypeDef;
+ USART\_Init();
+ USART\_Cmd();
  • 配置中断控制寄存器并使能USART接受中断

    • NVIC配置通过NVIC_InitTypeDef结构体实现
    typedef struct
    {
      uint8_t NVIC_IRQChannel;                    
        /*!< 中断源 中断源在IRQn_Type结构体中定义,
            USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                   
            USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                   
            USART3_IRQn                 = 39,     /*!< USART3 global Interrupt
            UART4_IRQn                  = 52,     /*!< UART4 global Interrupt                     
            UART5_IRQn                  = 53,     /*!< UART5 global Interrupt
            USART6_IRQn                 = 71,     /*!< USART6 global interrupt      
        */
    ​
      uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< 抢占优先级*/
    ​
      uint8_t NVIC_IRQChannelSubPriority;         /*!< 次优先级*/
    ​
      FunctionalState NVIC_IRQChannelCmd;         /*!< 中断使能或者失能*/   
    } NVIC_InitTypeDef;
    
      - NVIC\_Init();
      - USART\_ITConfig();
      
       
      ```
      void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
      ```
    
  • 在USART接受中断服务函数实现数据接受和发送

    • USARTx_IRQHerdler();
    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
    uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);
    
  • 同步模式时需要配置SCLK引脚输出脉冲的属性,一般使用USART_ClockInitTypeDef来设置。

typedef struct
{
  uint16_t USART_Clock;   /*时钟使能控制 
                            USART_Clock_Disable 禁止时钟输出
                            USART_Clock_Enabkle 开启时钟输出*/
  uint16_t USART_CPOL;    /*时钟极性 可设置空闲时SCLK引脚
                            USART_CPOL_Low 低电平
                            USART_CPOL_Hight 高电平*/
  uint16_t USART_CPHA;    /*时钟相位 设置在时钟的第一个变化沿捕获数据或时钟第二个变化沿捕获数据*/
  uint16_t USART_LastBit; /*最尾位时钟脉冲 选择发送最后一个数据位的时候脉冲是否在SCLK引脚输出
                            USART_LastBit_Disable 不输出脉冲
                            USART_LastBit_Enable 输出脉冲*/
} USART_ClockInitTypeDef;
(4)代码实例
void USART_Init_xd()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
​
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1,&USART_InitStructure);
    USART_Cmd(USART1,ENABLE);
    
    USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
    
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
}
​
void usart1_send_data(char ch)
{
    USART_SendData(USART1,ch);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
void usart1_send_str(char *str)
{
    unsigned int k = 0;
    do{
        usart1_send_data(*(str+k));
        k++;
    }while(*(str+k) != '\0');
    while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET)
    {}
}
void USART1_IRQHandler(void)
{
    uint8_t res;
    if(USART_GetITStatus(USART1,USART_IT_RXNE))
    {
        res = USART_ReceiveData(USART1);
        USART_SendData(USART1,res);
    }
}
int main(void)
{
    char *str = "hello USART1!";
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    USART_Init_xd();
    while(1)
    {
        usart1_send_str(str);
        Delay();
    }
}
(5)注意事项

1、注意GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF),函数中的第二个参数类型是

uint16_t GPIO_PinSource,用于指定复用功能的引脚,具体值是GPIO_PinSourcex(x = 0-15)。区别于GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)等函数中的参数 uint16_t GPIO_Pin。

2、设置 的串口波特率与 实际接受需要的波特率不一致的问题。

现象:设置串口波特率为115200,但是在实际串口接收是会出现乱码,但是把接收时的串口波特率设置为38400时可以正常接收。

故障原因:由于在系统文件stm32f4xx.h中定义的HSE的频率与实际板载的晶振频率不一致导致。

/**
 * @brief In the following line adjust the value of External High Speed oscillator (HSE)
   used in your application 
   
   Tip: To avoid modifying this file each time you need to use different HSE, you
        can define the HSE value in your toolchain compiler preprocessor.
  */           
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx)  || defined(STM32F429_439xx) || defined(STM32F401xx) || \
    defined(STM32F410xx) || defined(STM32F411xE) || defined(STM32F469_479xx)
 #if !defined  (HSE_VALUE) 
  #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
 #endif /* HSE_VALUE */

在上面的代码中可以看出,系统文件默认的HSE频率是25MHz,但是实际板载的晶振频率是8MHz。

解决方案:将上面代码中的 #define HSE_VALUE ((uint32_t)25000000) 改为 #define HSE_VALUE ((uint32_t)8000000)

5、标准库中的函数简介

//同步通信时钟设置
typedef struct
{
​
  uint16_t USART_Clock;   /*!< 描述同步通信USART时钟是启用还是禁用*/
                          //USART_Clock_DIsable
                          //USART_Clock_Enable
​
  uint16_t USART_CPOL;    /*!< 配置串口空闲位高电平、低电平.(极性) */
                          //USART_CPOL_LOW
                          //USART_CPOL_Hight
​
  uint16_t USART_CPHA;    /*!< 配置选择边沿。(相位)*/
                          //USART_CPHA_1Edge
                          //USART_CPHA_2Edge
​
  uint16_t USART_LastBit; /*!< 最后一个时钟(配置使能还是关闭) */
                          //USART_LastBit_Disable 
                          //USART_LastBit_Enable 
} USART_ClockInitTypeDef;
/*  Function used to set the USART configuration to the default reset state ***/ 
/*用于将串口初始化为默认的初始状态*/
//关闭串口的RCC时钟
void USART_DeInit(USART_TypeDef* USARTx);
​
/* Initialization and Configuration functions *********************************/
/*初始化和配置功能*********************************/
/*以USART_InitStruct结构体中的参数初始化USARTx外设*/
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
/*用默认值初始化USART_Struct结构体*/
//波特率9600 数据位8位 停止位1位 无校验位 发送/接受模式  无流控 
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
/*以USART_ClockInitStruct结构体中的参数初始化USART外设同步通信时钟*/
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数嵌入式工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

![img](https://img-blog.csdnimg.cn/img_convert/d2af625319aa0440efe77beb9f10a94c.png)

![img](https://img-blog.csdnimg.cn/img_convert/aa2e5026b3f9fb09d0b53e0186a34d8c.jpeg)

![img](https://img-blog.csdnimg.cn/img_convert/a95a82bc00ca7d5b0fd504ef97cff6bf.png)

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

![img](https://img-blog.csdnimg.cn/img_convert/52e1b03b9a24242a6526e7a019fac41e.png)

![img](https://img-blog.csdnimg.cn/img_convert/d6740bf6fd6d181952a71cfb26a02b53.png)

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

2365332651)]

 **既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**

[外链图片转存中...(img-8FmaJiXn-1712365332652)]

[外链图片转存中...(img-dLKxaV6c-1712365332653)]

 

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以+V:Vip1104z获取!!! (备注:嵌入式)**

<img src="https://img-community.csdnimg.cn/images/73bb5de17851459088c6af944156ee24.jpg" alt="img" style="zoom: 67%;" />



# 最后

**资料整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

**你的支持,我的动力;祝各位前程似锦,offer不断,步步高升!!!**

**[更多资料点击此处获qu!!](https://bbs.csdn.net/topics/618376385)**
  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值