usart_usart hmi - CSDN
精华内容
参与话题
  • STM32串口USART通讯

    千次阅读 2017-07-18 19:06:53
    1. USART和UARTUSART(Universal Synchronous Asynchronous Receiver and Transmitter)即通用同步异步收发器,它是一个串行通信设备,与外部设备可灵活进行全双工数据交换。在这之前我们常用到的是UART(Universal ...

    1. USART和UART

    USART(Universal Synchronous Asynchronous Receiver and Transmitter)即通用同步异步收发器,它是一个串行通信设备,与外部设备可灵活进行全双工数据交换。在这之前我们常用到的是UART(Universal Asynchronous Receiver and Transmitter),它是在USART基础上裁剪掉了同步通信功能,只有异步通信。区分同步和异步最简单的方法就是看通信时需不需要对外提供时钟输出,我们平时使用的串口通信基本都是UART。关于串口通信其他基本概念,在上一篇文章http://blog.csdn.net/qq_29344757/article/details/75246263已有详细解析,这里不再赘述。

    2. STM32中USART功能框图

    这里写图片描述

    2.1 功能引脚

    (1)TX:输出引脚,用于发送数据
    (2)RX:输入引脚,用于接收数据
    (3)SW_RX:输入引脚,用于单线和智能卡模式,属于内部引脚,没有具体外部引脚引出
    (4)IRDA_OUT:输出引脚,用于发送红外数据
    (5)IRDA_IN:输入引脚,用于接收红外数据
    (6)nRTS:请求以发送(Request To Send),n表低电平有效。若使能RTS硬件流控制,当USART接收器准备好接收数据时,nRTS有效,即它为低电平;当接收寄存器已满时,nRTS被设置为高电平
    (7)nCTS:请求以发送(Clean To Send),n表低电平有效。若使能CTS硬件流控制,发送器在发送下一帧数据前会检测nCTS引脚状态,若为低电平表可以发送数据,若为高电平则在发送完当前数据帧之后停止发送。
    (8)SCLK:发送器时钟输出引脚,仅适用于同步模式。

    2.2 数据寄存器

    USART数据寄存器(USART_DR)只有低9位有效,
    这里写图片描述
    第9位数据是否有效要取决于USART_CR1(USART控制寄存器1)的M位设置,
    这里写图片描述
    当M位为0时表示8位数据字长,当M位为1时表9位数据字长,一般我们使用8位数据字长。
    USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内,当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的:发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。

    2.3 控制器

    STM32的USART有专门用于控制发送、接收、唤醒单元和中断等的寄存器。例如:
    (1)USART_CR1寄存器的UE位用于开启/关闭给串口的时钟源的,使用USART之前向该位写1用于使能USART。
    (2)USART_CR1寄存器的M位用于控制发送/接收数据字长可选8位/9位。
    (3)USART_CR1寄存器的TE位用于启停数据发送,向该位写1时发送移位寄存器上的数据会从TX引脚输出,低位在前,高位在后。如果设置USART为同步模式,SCLK引脚将会输出时钟信号。
    (4)USART_CR1寄存器的RE位用于开启/关闭USART的接收,若为1,接收器在RX线开始接收数据帧的起始位,确定到起始位后就根据RX线电平状态把数据存放在接收移位寄存器内,接收完成后就要接收移位寄存器移到RDR内,并将USART_SR寄存器的RXNE位置1(若USART_CR2寄存器的RXNEIE置1的话此时可以产生中断)

    一般在我们编程中较为重要的USART_CR1寄存器标志位有:
    在发送数据时,

    TE:发送使能
    TXE:发送寄存器为空
    TC: 发送完成
    TXIE:发送完成中断使能

    接收数据时,

    RE:接收使能
    RXNE:读数据寄存器非空
    RXNEIE:发送完成中断使能

    2.4 波特率设置

    USART的发送器和接收器使用相同的波特率,计算公式为:

    Tx/Rx波特率 = USART的时钟频率 / (16 * USARTDIV)

    USARTDIV是一个存放波特率寄存器(USART_BRR)的无符号浮点数
    这里写图片描述
    DIV_Mantissa[11:0]表示USARTDIV的整数部分
    DIV_Fraction[3:0]表示USARTDIV的小数部分。
    例如:DIV_Mantissa = 26(0x1a),DIV_Fraction = 12(0x0c),那么USART_CRR的值为0x26c。USARTDIV的小数位12 / 16 = 0.75,整数位26,最终USARTDIV的值为26.75。
    假设知道USARTDIV的值为22.63,那么,DIV_Fraction = 16 * 0.63 = 10.08,最接近的整数位10,即DIV_Fraction = 0x0a,DIV_Mantissa = 22,即为0x16。
    以USART1为例,它是挂接在APB2总线上的:
    这里写图片描述
    即USART1的时钟源频率为72MHz,那么如果要得到115200的波特率,即:
    115200 = 72000000 / (16 * USARTDIV),USARTDIV = 39.0625,
    同理计算可得,DIV_Fraction = 0.0625 * 16 = 1,DIV_Mantissa = 39,USART_BRR的值设置为0x271。

    2.5 校验控制

    STM32F103系列单片机的USART支持奇偶校验,USART_CR1寄存器的PCE位置1就可以启动奇偶校验,奇偶检验由硬件自动完成。使能奇偶校验控制后,每个字符帧的格式变为:

    起始位 + 数据帧 + 校验位 + 停止位

    注意,当使用校验位时,USART_CR1寄存器的M位需要设置为1,即9位模式,因为串口传输的长度为8位数据帧加上1位校验位共9位。启动奇偶校验控制后,在发送数据帧时会自动添加校验位,接收数据帧时自动验证校验位。接收数据时若出现奇偶校验位验证失败,USART_SR寄存器的PE位会被置1并可以产生奇偶校验中断。

    2.6 中断控制

    USART有多个中断请求事件,常用中断请求有:
    这里写图片描述

    3. USART初始化数据结构

    有了标准库,我们的编程变得十分简单,若上面讲到的寄存器操作及原理不清晰,也不是我们要动手编程的阻碍了,不过了解下总归是好的。
    初始化结构体定义在标准外设库的stm32f10x_usart.h文件中:

    typedef struct
    {
      uint32_t USART_BaudRate;            //波特率,标准库会根据此值计算得到USARTDIV的值,从而设置USART_BRR的值。
      uint16_t USART_WordLength;          //字长,可选8位或9位(多一位表带奇偶校验)
      uint16_t USART_StopBits;            //停止位,0.5、1、1.5、2个停止位可选。设置在USART_CR2的STOP[1:0]位
      uint16_t USART_Parity;              //校验位,可设置USART_Parity_No,USART_Parity_Even(偶校验),USART_Parity_Odd(奇校验)。
                                          //设置在USART_CR1的PCE、PS位
      uint16_t USART_Mode;                //USART模式,USART_Mode_Rx和USART_Mode_Tx可选,可或
      uint16_t USART_HardwareFlowControl; //硬件流控制,可使能RTS/使能CTS/同时使能RTS和CTS,不使能硬件流
    } USART_InitTypeDef;

    4. USART编程使用到的外设库函数

    4.1 USART时钟使能

    以USART1为例,它是挂接在APB2总线在的外设,使能函数为

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);

    4.2 USART复位

    当外设出现异常时可通过复位设置实现对外设的复位,然后重新配置该外设使其重新工作。一般系统在刚开始配置外设时,都会先执行复位外设操作。USART的复位函数为

    void USART_DeInit(USART_TypeDef* USARTx);

    复位USART1时

    USART_DeInit(USART1);

    4.3 串口初始化

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

    参数一为待初始化的串口标号
    参数二为USART_InitTypeDef类型的结构体指针,原型及其意义在前面已讲

    4.4 USART发送数据

    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);

    该函数向USART_DR写入一个数据,串口会自动发送。

    4.5 接收数据

    uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

    该函数是操作USART_DR读取串口接收到的数据

    4.6 串口状态

    串口的状态是保存在USART_SR寄存器中,读取串口状态寄存器的函数为】

    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

    参数二表示我们要查看串口的哪种状态,可取值为:

    USART_FLAG_LBD:  LIN Break detection flag
    USART_FLAG_TXE:  Transmit data register empty flag(传输数据寄存器为空标志)
    USART_FLAG_TC:   Transmission Complete flag(传输完成标志)
    USART_FLAG_RXNE: Receive data register not empty flag
    USART_FLAG_IDLE: Idle Line detection flag
    USART_FLAG_ORE:  OverRun Error flag
    arg USART_FLAG_NE:   Noise Error flag
    arg USART_FLAG_FE:   Framing Error flag
    arg USART_FLAG_PE:   Parity Error flag

    4.7 串口使能

    void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

    4.8 串口响应中断

    void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

    参数二表示使能串口哪种中断。串口能产生中断的中断源有许多,取值为:

    USART_IT_CTS:  CTS change interrupt (not available for UART4 and UART5)
    USART_IT_LBD:  LIN Break detection interrupt
    USART_IT_TXE:  Transmit Data Register empty interrupt
    USART_IT_TC:   Transmission complete interrupt
    USART_IT_RXNE: Receive Data register not empty interrupt(接收数据中断)
    USART_IT_IDLE: Idle line detection interrupt
    USART_IT_PE:   Parity Error interrupt
    USART_IT_ERR:  Error interrupt(Frame error, noise error, overrun error)

    4.9 获取相应中断状态

    使能某中断后,当该中断发生了就会设置状态寄存器的某个标志位。在中断服务函数中我们经常要去判断是哪个中断源,

    ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

    若使能了串口发送完成中断,判断是否产生该中断时用

    USART_GetITStatus(USART1, USART_IT_TC);

    返回值等于SET,说明发生串口发送完成中断。

    5. 编程实践

    硬件平台采用MiniSTM32开发板,要实现板载USART和计算机通讯,需要用到一个USB转USART的芯片,MiniSTM32开发板采用的转换芯片为CH340G。硬件接线图为:
    这里写图片描述
    需要注意的是,STM32的USART1的引脚和CH340的接线。USART1的发送、接收引脚分别是PA9和PA10:
    这里写图片描述
    通过跳线帽将PA9(U1_TXD)、PA10(U1_RXD)连接到CH340G的RXD、TXD。这个在配置GPIO功能的时候要注意是以STM32芯片原理图上的引脚功能为准。

    程序实现功能:开发板上电后通过USART发送字符串到计算机(计算机用串口调试助手接收),进而开发板进入等待中断状态,若计算机通过串口调试助手向开发板发数据就会触发STM32的接收数据终中断,在中断服务函数中接收数据并将数据发回给计算机。

    STM32的串口编程关键点在于:
    (1)使能RX和TX引脚GPIO时钟和USART时钟
    (2)初始化GPIO,并将GPIO复用到USART上。参照《STM32中文参考手册_V10.pdf》-P110 外设的GPIO配置-USART部分
    这里写图片描述
    (3)配置USART初始化参数
    (4)配置终端控制器NVIC并使能USART接收中断
    (5)使能USART
    (6)在USART1中断服务函数中实现数据接收和发送

    编程环境采用MDK5,工程结构如图:
    这里写图片描述

    BSP/BSP_USART.c中实现对USART的所有操作及NVIC初始化操作,相关声明在BSP/BSP_USART.h中:

    //BSP_USART.h
    #ifndef __BSP_UART_H__
    #define __BSP_UART_H__
    
    #include <stm32f10x_conf.h>
    
    void NVIC_Configuration(void);
    void USART_Configuration(void);
    void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c);
    void USART_SendString(USART_TypeDef* pUSARTx, char* str);
    
    #endif /* __BSP_UART_H__ */
    
    //BSP_USART.c
    #include "BSP_USART.h"
    
    //配置NVIC
    void NVIC_Configuration(void)
    {
        NVIC_InitTypeDef NVIC_InitStu;
    
        //设置中断分组寄存器
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
        NVIC_InitStu.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStu.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStu.NVIC_IRQChannelSubPriority = 1;
        NVIC_InitStu.NVIC_IRQChannelCmd = ENABLE;
    
        NVIC_Init(&NVIC_InitStu);
    }
    
    void USART_Configuration(void)
    {
        GPIO_InitTypeDef GPIO_InitStu;
        USART_InitTypeDef USART_InitStu;
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
        //PA9用作串口1输入引脚,配置为浮空输入
        GPIO_InitStu.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStu.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStu.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStu);
    
        //PA10用作串口1输出引脚,配置为复用推挽输出
        GPIO_InitStu.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_InitStu.GPIO_Pin = GPIO_Pin_10;
        GPIO_Init(GPIOA, &GPIO_InitStu);
    
        //设置USART初始化结构体
        USART_InitStu.USART_BaudRate = 115200;
        USART_InitStu.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
        USART_InitStu.USART_Parity = USART_Parity_No;
        USART_InitStu.USART_StopBits = USART_StopBits_1;
        USART_InitStu.USART_WordLength = USART_WordLength_8b;
        USART_InitStu.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_Init(USART1, &USART_InitStu);
    
        //配置NVIC
        NVIC_Configuration();
    
        //使能串口接收中断
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
        //开启
        USART_Cmd(USART1, ENABLE);
    }
    
    //发送一个字符
    void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c)
    {
        USART_SendData(pUSARTx, c);
    
        //等待发送结束标志
        while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    }
    
    //发送字符串
    void USART_SendString(USART_TypeDef* pUSARTx, char* str)
    {
        uint32_t n = 0;
    
        while (*(str + n) != '\0')
        {
            USART_SendChar(pUSARTx, *(str + n));
            n++;
        }
        //等待发送结束标志
        while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    }

    USART1的接收数据产生的中断的中断服务函数在stm32f10x_it.c中实现:

    void USART1_IRQHandler(void)
    {
        uint16_t c;
    
        //判断是否为USART1的接收中断
        if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {
            c = USART_ReceiveData(USART1);
            USART_SendData(USART1, c);
        }   
    }

    在main函数中:

    #include <BSP_USART.h>
    
    int main(void)
    {
    
        USART_Configuration();
        USART_SendString(USART1, "HelloWorld\n");
        //USART_SendChar(USART1, 'h');
        while (1);  
    
        return 0;
    }

    发送完”HelloWorld\n”后进入等待中断中。

    下载运行,注意若使用板载的串口下载方式下载程序,就会占用了USART1发送/接收数据用的串口,所以需要下载完程序后再用串口调试助手测试。另外,计算机串口调试助手的波特率、停止位、数据位等要跟在代码中的设置一致。运行如图:

    这里写图片描述

    展开全文
  • 1.串口通信协议的简介 物理层:规定通讯系统中具有机械.电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬件部分。 协议层:协议层主要规定通讯逻辑,统一收发双方的数据打包 解包标准。...

    20.1 串口通信协议简介
    物理层:规定通讯系统中具有机械 电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬件部分。
    协议层:协议层主要规定通讯逻辑,统一收发双方的数据打包与解包标准。其实就是软件部分。
    简单来说物理层规定我们是用嘴巴还是用肢体交流,协议层则规定我们是用中文还是用英文进行交流。
    20.1.1 物理层
    串口通信的物理层有很多标准及变种,主要了解RS-232标准.使用RS-232标准的串口设备常见的通信结构如下:
    在这里插入图片描述
    两个通信设备的"DB9接口"之间通过串口信号线建立起连接,串口信号线中使用"RS-232标准"传输数据信号.由于RS-232电平标准的信号不能被控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL标准"的电平信号,才能实现通信.
    1.电平标准
    根据通信使用的电平标准不同,串口通信可分为TTL标准及RS-232标准.因为控制器一般使用TTL电平标准,所以常常会使用MAX232芯片将TTL及RS-232电平的信号进行互相转换.
    2.RS-232信号线
    20.1.2 协议层
    在这里插入图片描述
    起始位:由1个逻辑0的数据位表示。
    结束位:由0.5 1 1.5 或2个逻辑1的数据位表示。
    有效数据:在起始位后紧接着的就是有效数据,有效数据的长度被约定为5 6 7 8位长。
    奇校验(odd):有效数据和校验位中“1”的个数为奇数。比如一个8位长的有效数据为:0110 1100,此时总共有4个“1”,为达到奇校验效果,校验位为1,最后传输的数据将是8位的有效数据加上1位的校验总共9位。
    偶校验(even):有效数据和校验位中的”1“的个数为偶数。比如1个8位长的有效数据为0110 1001,此时总共有4个“1”,为达到偶校验的效果,校验位为0,最后传输的数据将是8位的有效数据加上1位的校验位共9位。
    0校验是不管有效数据中的内容是什么,校验位总为“0”
    1校验是校验位总为“1”。
    无校验就是数据包中不包含校验位。
    20.2 STM32的USART简介
    平时用的串口通信基本上都是UART(通用异步收发器).串口通信一般是以帧格式传输数据,即一帧一帧地传输,每帧包含有起始信号、有效数据、校验数据、停止信号。
    20.3 串口功能框图讲解
    在这里插入图片描述
    1.功能引脚
    TX:发送数据输出引脚
    RX:接收数据输入引脚
    nRTS:请求以发送(Request To Send),n表示低电平有效。
    nCTS:清除以发送(Clear To Send),n表示低电平有效。
    SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
    在这里插入图片描述
    2.数据寄存器
    3.控制器
    (1)发送器
    (2)接收器
    在接收数据时,几个重要的标志位:
    RE:接收使能
    RXNE:读数据寄存器非空
    RXNEIE:发送完成中断使能
    4.小数波特率生成
    5.校验控制
    6.中断控制
    UART有多个中断请求事件,如下:
    在这里插入图片描述
    20.4 USART初始化结构体详解

    typedef struct
    {
      uint32_t USART_BaudRate;      //波特率   配置的寄存器:BRR
      uint16_t USART_WordLength;    //字长     配置的寄存器:CR1_M
      uint16_t USART_StopBits;      //停止位   配置的寄存器:CR2_STOP
      uint16_t USART_Parity;        //校验控制 配置的寄存器: CR1_PCE、CR1_PS
      uint16_t USART_Mode;          //模式选择 配置的寄存器:CR1_TE、CR1_RE
      uint16_t USART_HardwareFlowControl;// 硬件流选择 CR3_CTSE、CR3_RTSE
    } USART_InitTypeDef;
    

    当使用同步模式时,需要配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置,该结构体内容也只有在同步模式下才需要设置。

    typedef struct
    {
      uint16_t USART_Clock;      // 同步时钟 CR2_CLKEN
      uint16_t USART_CPOL;       // 极性 CR2_CPOL
      uint16_t USART_CPHA;       // 相位 CR2_CPHA
      uint16_t USART_LastBit;    //最后一个位的时钟脉冲 CR2_LBC
    } USART_ClockInitTypeDef;
    
    

    20.5 串口1收发通信实验
    第一步:单片机给电脑发送数据,上位机把数据打印出来;第二步:上位机给单片机发数据,单片机接收到数据之后(产生中断)立马发回给电脑,并打印出来。
    20.5.1硬件设计
    在这里插入图片描述
    20.5.2 软件设计
    1.编程要点
    1-初始化串口需要用到的GPIO
    2-初始化串口USART_InitTypeDef
    3-中断配置(接收中断,中断优先级)
    4-使能串口
    5-编写发送和接收函数
    6-编写中断服务函数
    2.代码分析
    bsp_usart.c

    #include "bsp_usart.h"
    
    static void NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
      
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init(&NVIC_InitStructure);
    }
    
    
    void USART_Config(void)
    {
    	GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
    
    	// 打开串口GPIO的时钟
    	// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE)
    	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
    	
    	// 打开串口外设的时钟
    	// RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE)
    	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
    
    	// 将USART Tx(PA9)的GPIO配置为推挽复用模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
    
      // 将USART Rx(PA10)的GPIO配置为浮空输入模式
    	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    	
    	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    	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(DEBUG_USARTx, &USART_InitStructure);
    	
    	// 串口中断优先级配置
    	NVIC_Configuration();
    	// 使能串口接收中断
    	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
    
    	USART_Cmd(DEBUG_USARTx, ENABLE);	    
    }
    
    /*****************  发送一个字节(串口只能8位8位的发)**************/
    void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
    {
    	/* 发送一个字节数据到USART */
    	USART_SendData(pUSARTx,ch);
    		
    	/* 等待发送数据寄存器(TXE)为空 */
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
    }
    
    /****************** 发送8位的数组 ************************/
    void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
    {
      uint8_t i;
    	
    	for(i=0; i<num; i++)
      {
    	    /* 发送一个字节数据到USART */
    	    Usart_SendByte(pUSARTx,array[i]);	
      
      }
    	/* 等待发送完成 */
    	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
    }
    
    /*****************  发送字符串 **********************/
    void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
    {
    	unsigned int k=0;
      do 
      {
          Usart_SendByte( pUSARTx, *(str + k) );
          k++;
      } while(*(str + k)!='\0');
      
      /* 等待发送完成 */
      while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
      {}
    }
    
    /*****************  发送一个16位数 **********************/
    void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
    {
    	uint8_t temp_h, temp_l;
    	
    	/* 取出高八位 */
    	temp_h = (ch&0XFF00)>>8;
    	/* 取出低八位 */
    	temp_l = ch&0XFF;
    	
    	/* 发送高八位 */
    	USART_SendData(pUSARTx,temp_h);	
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    	
    	/* 发送低八位 */
    	USART_SendData(pUSARTx,temp_l);	
    	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
    }
    
    ///重定向c库函数printf到串口,重定向后可使用printf函数
    int fputc(int ch, FILE *f)
    {
    		/* 发送一个字节数据到串口 */
    		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
    		
    		/* 等待发送完毕 */
    		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
    	
    		return (ch);
    }
    
    ///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
    int fgetc(FILE *f)
    {
    		/* 等待串口输入数据 */
    		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
    
    		return (int)USART_ReceiveData(DEBUG_USARTx);
    }
    

    bsp_usart.h

    #ifndef __USART_H
    #define	__USART_H
    
    
    #include "stm32f10x.h"
    #include <stdio.h>
    
    
    	
    //串口-USART1
    #define  DEBUG_USARTx                   USART1
    #define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
    #define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
    #define  DEBUG_USART_BAUDRATE           115200
    
    // USART GPIO  引脚定义
    #define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
    #define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
        
    #define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
    #define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
    #define  DEBUG_USART_RX_GPIO_PORT       GPIOA
    #define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10
    
    #define  DEBUG_USART_IRQ                USART1_IRQn
    #define  DEBUG_USART_IRQHandler         USART1_IRQHandler
    
    // 串口2 USART2
    //#define  DEBUG_USARTx                   USART2
    //#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
    //#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
    //#define  DEBUG_USART_BAUDRATE           115200
    
     USART GPIO 引脚宏定义
    //#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
    //#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    //    
    //#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
    //#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
    //#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
    //#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3
    
    //#define  DEBUG_USART_IRQ                USART2_IRQn
    //#define  DEBUG_USART_IRQHandler         USART2_IRQHandler
    
    // 串口3-USART3
    //#define  DEBUG_USARTx                   USART3
    //#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
    //#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
    //#define  DEBUG_USART_BAUDRATE           115200
    
     USART GPIO 引脚定义
    //#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
    //#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
     
    //#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
    //#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
    //#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
    //#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11
    
    //#define  DEBUG_USART_IRQ                USART3_IRQn
    //#define  DEBUG_USART_IRQHandler         USART3_IRQHandler
    
    // 串口4-UART4
    //#define  DEBUG_USARTx                   UART4
    //#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
    //#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
    //#define  DEBUG_USART_BAUDRATE           115200
    
     USART GPIO引脚定义
    //#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
    //#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    //    
    //#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
    //#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
    //#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
    //#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11
    
    //#define  DEBUG_USART_IRQ                UART4_IRQn
    //#define  DEBUG_USART_IRQHandler         UART4_IRQHandler
    
    // 串口5-UART5
    //#define  DEBUG_USARTx                   UART5
    //#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
    //#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
    //#define  DEBUG_USART_BAUDRATE           115200
    
     USART GPIO引脚定义
    //#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
    //#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    //    
    //#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
    //#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
    //#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
    //#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2
    
    //#define  DEBUG_USART_IRQ                UART5_IRQn
    //#define  DEBUG_USART_IRQHandler         UART5_IRQHandler
    
    void USART_Config(void);
    void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
    void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
    void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
    void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
    
    #endif /* __USART_H */
    

    main.c

    #include "stm32f10x.h"
    #include "bsp_usart.h"
    
    
    int main(void)
    {	
    	unsigned char  a[2]={'1','2'};
        
        /*初始化USART  配置模式为 115200 8-N-1 中断接收*/
        USART_Config();
    
    	/*发送数组*/
    	USART_SendArray(DEBUG_USARTx,a,2);
    	
    	/* 发送一个字符串 */
    	Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\n");
    
    	
      while(1)
    	{			
    	}	
    }
    
    
    // 串口中断服务函数
    void DEBUG_USART_IRQHandler(void)
    {
      uint8_t ucTemp;
    	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
    	{		
    	ucTemp = USART_ReceiveData(DEBUG_USARTx);
        USART_SendData(DEBUG_USARTx,ucTemp);    
    	}	 
    }
    

    20.6使用串口控制RGB彩灯实验
    20.6.1硬件设计
    20.6.2软件设计
    1.编程要点
    不使用中断接收,而是通过查询中断标志位的方式来实现接收。
    led.h

    #ifndef __LED_H
    #define	__LED_H
    
    
    #include "stm32f10x.h"
    
    // R-红色
    #define LED1_GPIO_PORT    	GPIOB			             
    #define LED1_GPIO_CLK 	    RCC_APB2Periph_GPIOB		
    #define LED1_GPIO_PIN		GPIO_Pin_5			        
    
    // G-绿色
    #define LED2_GPIO_PORT    	GPIOB			              
    #define LED2_GPIO_CLK 	    RCC_APB2Periph_GPIOB		
    #define LED2_GPIO_PIN		GPIO_Pin_0			       
    
    // B-蓝色
    #define LED3_GPIO_PORT    	GPIOB			             
    #define LED3_GPIO_CLK 	    RCC_APB2Periph_GPIOB		
    #define LED3_GPIO_PIN		GPIO_Pin_1			       
    
    
    
    #define ON  0
    #define OFF 1
    
    
    #define LED1(a)	if (a)	\
    					GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
    					else		\
    					GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)
    
    #define LED2(a)	if (a)	\
    					GPIO_SetBits(LED2_GPIO_PORT,LED2_GPIO_PIN);\
    					else		\
    					GPIO_ResetBits(LED2_GPIO_PORT,LED2_GPIO_PIN)
    
    #define LED3(a)	if (a)	\
    					GPIO_SetBits(LED3_GPIO_PORT,LED3_GPIO_PIN);\
    					else		\
    					GPIO_ResetBits(LED3_GPIO_PORT,LED3_GPIO_PIN)
    
    
    /* 直接操作寄存器的方法控制IO */
    #define	digitalHi(p,i)		 {p->BSRR=i;}	 //输出为高电平		
    #define digitalLo(p,i)		 {p->BRR=i;}	 //输出低电平
    #define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
    
    
    /* 定义控制IO的宏 */
    #define LED1_TOGGLE		 digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
    #define LED1_OFF		   digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
    #define LED1_ON			   digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
    
    #define LED2_TOGGLE		 digitalToggle(LED2_GPIO_PORT,LED2_GPIO_PIN)
    #define LED2_OFF		   digitalHi(LED2_GPIO_PORT,LED2_GPIO_PIN)
    #define LED2_ON			   digitalLo(LED2_GPIO_PORT,LED2_GPIO_PIN)
    
    #define LED3_TOGGLE		 digitalToggle(LED3_GPIO_PORT,LED3_GPIO_PIN)
    #define LED3_OFF		   digitalHi(LED3_GPIO_PORT,LED3_GPIO_PIN)
    #define LED3_ON			   digitalLo(LED3_GPIO_PORT,LED3_GPIO_PIN)
    
    /* 基本混色,后面高级用法使用PWM可混出全彩颜色,且效果更好 */
    
    //红
    #define LED_RED  \
    					LED1_ON;\
    					LED2_OFF\
    					LED3_OFF
    
    //绿
    #define LED_GREEN		\
    					LED1_OFF;\
    					LED2_ON\
    					LED3_OFF
    
    //蓝
    #define LED_BLUE	\
    					LED1_OFF;\
    					LED2_OFF\
    					LED3_ON
    
    					
    //黄(红+绿)					
    #define LED_YELLOW	\
    					LED1_ON;\
    					LED2_ON\
    					LED3_OFF
    //紫(红+蓝)
    #define LED_PURPLE	\
    					LED1_ON;\
    					LED2_OFF\
    					LED3_ON
    
    //青(绿+蓝)
    #define LED_CYAN \
    					LED1_OFF;\
    					LED2_ON\
    					LED3_ON
    					
    //白(红+绿+蓝)
    #define LED_WHITE	\
    					LED1_ON;\
    					LED2_ON\
    					LED3_ON
    					
    //黑(全部关闭)
    #define LED_RGBOFF	\
    					LED1_OFF;\
    					LED2_OFF\
    					LED3_OFF
    
    void LED_GPIO_Config(void);
    
    #endif /* __LED_H */
    

    led.c

    #include "bsp_led.h"   
    void LED_GPIO_Config(void)
    {		
    		GPIO_InitTypeDef GPIO_InitStructure;
    
    		
    	RCC_APB2PeriphClockCmd( LED1_GPIO_CLK | LED2_GPIO_CLK | LED3_GPIO_CLK, ENABLE);
    		
    		GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
    		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   
    		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    		GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);	
    		
    		GPIO_InitStructure.GPIO_Pin = LED2_GPIO_PIN;
    		GPIO_Init(LED2_GPIO_PORT, &GPIO_InitStructure);
    		
    		GPIO_InitStructure.GPIO_Pin = LED3_GPIO_PIN;
    		GPIO_Init(LED3_GPIO_PORT, &GPIO_InitStructure
    		
    		GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
    		GPIO_SetBits(LED2_GPIO_PORT, LED2_GPIO_PIN);	 
    		GPIO_SetBits(LED3_GPIO_PORT, LED3_GPIO_PIN);
    }
    

    main.c

    int main(void)
    {	
    	uint8_t ch;//接受电脑给单片机发送的数据
    	USART_Config();
    	LED_GPIO_Config();
    	
    	printf( "这是一个串口控制RGB灯的程序\n" );
     
    	while (1)
    	{
    		ch = getchar();
    	  printf( "ch=%c\n",ch );
    		
    		switch(ch)
           {
    			case '1': LED_RED;
    				break;
    		 
    			case '2': LED_GREEN;
    			  break;
    		 
    			case '3': LED_BLUE;
    			  break;
    		 
    			default: LED_RGBOFF;
    		    break;
    	 }
    	}
    }
    
    展开全文
  • STM32_USART 串口通讯详解

    千次阅读 多人点赞 2019-01-04 21:58:12
    对51单片机有了解的都知道51单片机的串口通讯工作原理,我们单片机使用的电平TTL电平,为了使我们的的单片机与PC进行通信,就需要一个电平转换芯片,把TTL电平转换为USB电平...下面就对USART尽行具体的介绍。 U...

    对51单片机有了解的都知道51单片机的串口通讯工作原理,我们单片机使用的电平TTL电平,为了使我们的的单片机与PC进行通信,就需要一个电平转换芯片,把TTL电平转换为USB电平(使用的USB接口,如果使用的DB9接口,电平转换芯片则为TTL转RS232电平芯片),然后通过对SBUF寄存器的读写操作来实现PC与MCU的通信。STM32的串口通讯原理与51相同。下面就对USART尽行具体的介绍。

    USART(Universal synchronous asynchronous receivertransmitter )通用同步异步收发器,是STM32上基于串口通讯协议来实现与外部通信的一个外设,因为串口通讯协议的简单,便捷,所以在如今大多数的产品中都带支持串口通信,通过串口通信我们可以实现与单片机的通信,能够监控到我们MCU的收发数据,在进行产品的调试与开发时起着至关重要的作用。

    在进行USART讲解之前,先讲解一下通讯的基本概念。

    1、串行通信与并行通信

    通讯按照数据传输的方式可分为串行通信与并行通信,串行通讯是指设备之间通过少量数据信号线(一般是 8根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用 8、16、32 及 64根或更多的数据线进行传输,数据位是同时传输的通讯方式,就好比这时有8辆车,串行通信就相当于只有一条马路8辆车一辆一辆过,而并行通信相当于有8条马路,8辆车同时通过,方式如下。

    串行通信与并行通信的特性对比如下

    由于并行传输对同步要求较高,且随着通讯速率的提高,信号干扰的问题会显著影响通讯性能,现在随着技术的发展,越来越多的应用场合采用高速率的串行差分传输。

    2、全双工、半双工及单工

    根据通讯方向的不同,通讯方式又可以分为全双工、半双工及单工三种方式。

     全双工:在同一时刻允许两个设备的任何一个都可以同时进行数据的发送与接收,就好比双向车道,两个方向的车辆不会有任何的干扰。

    半双工:在同一时刻,只允许两个设备进行接收或者发送,不允许在同一时刻单个设备既接收又发送,就好比在一条乡间小道上同一时刻只允许一辆车通过。

    单工:在任何时刻,数据的传输方向都是不变的,数据只允许一个固定的方向传输,接收双方是固定的,一个固定为发送设备,一个固定为接收设备。就好比是单行道,只允许一个方向的行驶。

    3、同步通讯与异步通讯

      根据通讯的数据同步方式又分为同步和异步两种通讯方式。主要区别在于当数据传输时是否有使用到时钟信号来进行区分。

    同步通讯:在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,同步数据,通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。如下图所示

    异步通讯:在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,某些通讯中还需要双方约定数据的传输速率,以便更好地同步。

    在同步通讯中,数据信号所传输的内容绝大部分就是有效数据,而异步通讯中会包含有帧的各种标识符,所以同步通讯的效率更高,但是同步通讯双方的时钟允许误差较小,而异步通讯双方的时钟允许误差较大。

    4、通讯速率

    衡量通讯性能的一个非常重要的参数就是通讯速率,通常以比特率(Bitrate)来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易与比特率混淆的概念是“波特率”(Baudrate),它表示每秒钟传输了多少个码元。而码元是通讯信号调制的概念,通讯中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。如常见的通讯传输中,用 0V 表示数字 0,5V 表示数字 1,那么一个码元可以表示两种状态 0和 1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;如果在通讯传输中,有 0V、2V、4V 以及 6V 分别表示二进制数 00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率。

    以上四个就是通讯的基本知识,下面开始就是USART的部分

    文章开头有提到串口通讯是一种非常常用的通讯方式,所以想要使用串口通讯收发双方设备就需要符合串口通讯协议的物理规定也就是串口的机械电气特性,同时接受发送时也要符合规定通讯逻辑,统一收发双方的数据打包、解包标准。也就是要统一收发双方的物理层与协议层。

    我们先来讲一下物理层,也就是实现串口通讯协议大多数用以下几种方式

    1、RS232标准

    2、USB转串口

    3、原生的串口到串口

    1、RS232标准串口通信结构图如下图所示

    如果两个通讯设备之间使用的是RS232串行通讯协议标准,那么MCU出来的TTL电平信号先经过一个TTL转RS232电平转换芯片,然后经过一个DB9接口与外部设备进行连接(RS232串行通讯标准COM口俗称DB9接口),再经过一个电平转换芯片转换成CPU可以识别的TTL电平信号来实现通讯。为什么要使用电平转换芯片呢?是因为TTL电平与RS232电平不兼容,RS232接口任何一条信号线的电压均为负逻辑关系,即

    RS232:+3v~+15v为二进制的逻辑0,(-3)v~(-15)v为二进制的逻辑1,

    TTL:+2.4~+5v为逻辑1,  0~+0.5为逻辑0

    RS232因为其抗干扰能力强一般用于工业通信而TTL当通讯距离大于40cm时,传输数据就会收到干扰。

    DB9标准的公头与母头的接法如下图所示

    实物如下图下图所示

    2、USB转串口的串行通信方式

    USB转串口的通讯方式就是我文章开头所提到的我当时学习51单片机的UART时的串行通信方式,通讯原理图如下

    USB转串口的串行通讯协议主要用于MCU与PC间的通讯,并且在通讯时,PC要安装电平转换芯片的驱动。

    3、原生串口到串口的串行通讯方式

    原生的串口通信主要是控制器跟串口的设备戒者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信,比如GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块等。原理图如下

    在应用串口通讯协议时,首先要保证物理层也就是收发双方的机械电气特性符合通讯协议的规定,接下来就是要同一收发双方的数据打包、解包标准。也就是要统一收发双方的协议层。

    我们上边讲通讯的基本概念有提到串行通讯是指设备之间通过少量数据信号线(一般是 8根以下),地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式,收发双方都有两个端口,一个RXD 一个TXD , 具体连接如下

    数据总是从TXD引脚到RXD引脚,与并行通讯不同在串口传输数据中包括起始位、数据帧、校验位和停止位。通讯双方的数据包格式要约定一致才能正常收发数据。这里主要讲解异步串行通信,所以没有时钟信号,收发双方只要约定好波特率,即每个码元的长度(下图两个虚线代表一个码元),就可以完成解码。串口数据包组成如下

    起始位: 由1个逻辑 0 的数据位表示
    结束位: 由 0.5、 1、 1.5 戒 2 个逻辑 1 的数据位表示
    有效数据 :在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 戒 8 位长。

    校验位:有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无校验(noparity),它们介绍如下:

    奇校验:奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为:01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8位的有效数据加上 1位的校验位总共 9位。

    偶校验:偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4个,所以偶校验位为“0”。

     0校验:0 校验是不管有效数据中的内容是什么,校验位总为“0”。

    1 校验:1校验是不管数据中内容为什么校验位总为1 。

    无校验:没有校验位。

    要想实现串口通信协议1、配置好物理层2、统一规定收发双方波特率、起始位、数据帧、校验位和停止位。

    关于串口通讯的物理层与协议层就是以上内容,接下来实际讲解STM32如何去配置实现串口通讯

    1、STM32_USART简介

    STM32芯片具有多个 USART 外设用于串口通讯,它是 Universal SynchronousAsynchronous Receiver and Transmitter的缩写,即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于 USART,它还有具有 UART 外设(Universal AsynchronousReceiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。USART 满足外部设备对工业标准 NRZ异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC 规范。USART 在 STM32应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一个 USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、指出运行出错位置等等。STM32的 USART 输出的是 TTL电平信号,若需要 RS-232标准的信号可使用MAX3232芯片进行转换。

    那我们如何对STM32的USART进行配置呢,我们如何来规定串口通讯的起始位那些呢,这就需要我们对STM32的USART的功能框图有一定的了解,如下图所示

    功能框图大致分为4部分

    1、功能引脚

    TX:发送数据输出引脚。

    RX:接收数据输入引脚。

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

    nRTS:请求以发送(Request To Send),n表示低电平有效。如果使能 RTS 流控制,当USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制。也就是使用硬件来控制数据的接收与发送。

    nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。

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

    2、数据寄存器(USART_DR)   ********************* 收发原理                                                                                                              USART 数据寄存器(USART_DR)只有低 9位有效,并且第 9 位数据是否有效要取决于USART 控制寄存器 1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8位数据字长,当 M位为 1表示 9位数据字长,我们一般使用 8位数据字长。

    USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR 写入数据会自动存储在 TDR内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR数据。

    TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。

    3、控制器   ********************接下来是如何配置收发

    USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用 USART 之前需要向 USART_CR1寄存器的 UE 位置 1 使能 USART。发送或者接收数据字长可选 8位或 9位,由 USART_CR1的 M 位控制。

    (1)发送器

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

    一个字符帧发送需要三个部分:起始位+数据帧+停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8位或 9位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。

    停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2)的 STOP[1:0]位控制,可选 0.5个、1个、1.5个和 2个停止位。默认使用 1个停止位。2个停止位适用于正常USART 模式、单线模式和调制解调器模式。0.5 个和 1.5个停止位用于智能卡模式。

    当选择 8位字长,使用 1个停止位时,具体发送字符时序图如下

    当发送使能位 TE 置 1之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器(USART_SR)的 TC位为 1,表示数据传输完成,如果
    USART_CR1寄存器的 TCIE 位置 1,将产生中断。

    在发送数据时,编程的时候有几个比较重要的标志位如下表所示

    (2)接收器

    如果将 USART_CR1寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到 RDR内,并把 USART_SR 寄存器的 RXNE 位置1,同时如果 USART_CR2寄存器的 RXNEIE 置 1的话可以产生中断。

    在接收数据时,编程的时候有几个比较重要的标志位如下表所示

    为得到一个信号真实情况,需要用一个比这个信号频率高的采样信号去检测,称为过采样,这个采样信号的频率大小决定最后得到源信号准确度,一般频率越高得到的准确度越高,但为了得到越高频率采样信号越也困难,运算和功耗等等也会增加,所以一般选择合适就好。
    接收器可配置为不同过采样技术,以实现从噪声中提取有效的数据。USART_CR1寄存器的 OVER8位用来选择不同的采样采样方法,如果 OVER8位设置为 1采用 8倍过采样,即用 8个采样信号采样一位数据;如果 OVER8 位设置为 0采用 16 倍过采样,即用 16 个采样信号采样一位数据。

    4、小数波特率生成

    波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。对于 USART 波特率与比特率相等,以后不区分这两个概念。波特率越大,传输速率越快。USART 的发送器和接收器使用相同的波特率。计算公式如下:。                                                           其中,f PLCK 为 USART 时钟,OVER8 为 USART_CR1 寄存器的 OVER8位对应的值,USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中 DIV_Mantissa[11:0]位定义 USARTDIV 的整数部分,DIV_Fraction[3:0]位定义USARTDIV 的小数部分,DIV_Fraction[3]位只有在 OVER8 位为 0 时有效,否则必须清零。当设置波特率时直接调用库函数即可,原理只需了解。

    5、校验控制

    STM32F4xx 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将是 8位的数据帧加上 1位的校验位总共 9位,此时 USART_CR1寄存器的 M 位需要设置为1,即 9 数据位。将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存器的 PE 位置 1,并可以产生奇偶校验中断。使能了奇偶校验控制后,每个字符帧的格式将变成:起始位+数据帧+校验位+停止位。

    6、中断控制

    USART 有多个中断请求事件,具体如下图所示。

    以上就是有关于STM32的USART的基本介绍。关于USART的应用,我会在下一篇中进行操作。

    展开全文
  • STM32系统学习——USART(串口通信)

    万次阅读 多人点赞 2019-03-19 10:59:45
    串口通信是一种设备间非常常用的串行通行方式,其简单便捷,大部分电子设备都支持。 一、物理层 常用RS-232标准,主要规定了信号的用途、通信接口以及信号的电平标准。 “DB9接口”之间通过串口信号线建立...

    串口通信是一种设备间非常常用的串行通行方式,其简单便捷,大部分电子设备都支持。
    一、物理层
    常用RS-232标准,主要规定了信号的用途、通信接口以及信号的电平标准。
    这里写图片描述
    “DB9接口”之间通过串口信号线建立起连接,串口信号线使用”RS-232标准“传输数据信号,这些信号通过记过电平转换芯片转换成控制器能识别的TLL标准的电平信号,才能实现通信。
    1.电平标准
    可分为TTL标准以及RS-232标准。
    常见的电子电路中常见TTL的电平标准,理想状态使用5V表示二进制逻辑1,0V表示逻辑0;而为了增加串口通信的远距离传输以及抗干扰能力,RS-232使用-15V表示逻辑1,+15V表示逻辑0。
    这里写图片描述
    因为控制器一般使用TTL电平标准,所以常常使用MA3232芯片将TTL以及RS-232电平的信号进行互相转换。

    2.RS-232信号线
    最初RS-232串口标准常用于计算机、路由与调制调节器(“猫”)之间通信,设备被分为数据终端设备DTE(计算机、路由)和数据通信设备DCE(调制调节器)。旧台式计算机,一般都有RS-232标准的COM口,也称DB9接口。

    								DB9信号线说明
    

    这里写图片描述
    公头标准接法,只要使用直通型串口线连接起来即可。


    二、协议层
    协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通信双方的数据包格式要约定一致才能正常收发数据 。
    1、波特率
    异步通信中由于没有时钟信号,所以2个通信设备需约定好波特率,常见的有4800、9600、115200等。
    2、通信的起始和停止信号
    串口通信的一个数据包从起始信号开始,知道停止信号结束。数据包的起始信号由一个逻辑0的数据位表示,而数据包的停止信号可由0.5、1、1.5或2个逻辑1的数据位表示,只要双方约定一致即可。
    3、有效数据
    在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效
    数据的长度常被约定为 5、6、7或 8位长
    4、数据校验
    在有效数据之后,有一个可选的数据校验位。由于数据通信相对容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无校验(noparity)。
    奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为:01101001,此时总共有 4 个“1”,为达到奇校验效果,校验位为“1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位。
    偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。
    0 校验是不管有效数据中的内容是什么,校验位总为“0”,1 校验是校验位总为“1”。


    三、STM32的USART简介
    通用同步异步收发器是一个串行通信设备,可以灵活的与外部设备进行全双工数据交换。有别与USART,还有一个UART,它在USART基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。
    串口通信一般是以帧格式传输数据,即一帧一帧传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。
    USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC规范。
    USART支持使用 DMA,可实现高速数据通信。


    四、USART功能框图剖析
    这里写图片描述
    下文结合图片看加深理解。
    1、功能引脚
    TX:发送数据输出引脚。
    RX:接收。
    SW_RX:数据接收引脚,属于内部引脚。
    nRTS:请求以发送,n表示低电平有效。如果使能 RTS 流控制,当USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。该引脚只适用于硬件流控制。
    nCTS:清除以发送(Clear To Send),n表示低电平有效。如果使能 CTS流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
    SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
    USART:下图是STM32F103VET6芯片的USART引脚
    这里写图片描述
    USART1的时钟来源于APB2总线时钟,最大频率为72MHZ,其他4个时钟来源于APB1总线时钟,最大频率36MHZ。UART只有异步传输功能,没有SCLK、nCTS和nRTS功能引脚。

    2.数据寄存器
    USART说数据寄存器(USART_DR)只有低 9 位有效,并且第 9 位数据是否有效要取决于USART 控制寄存器 1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M位为 1 表示 9 位数据字长,我们一般使用 8位数据字长。
    USART_DR包含了已发送的数据或者接收到的数据。USART_DR实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR写入数据会自动存储在 TDR内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。
    TDR和RDR都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
    USART 支持 DMA 传输,可以实现高速数据传输。

    3.控制器
    USART有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等。
    使用USART之前需要向USART_CR1寄存器的UE位置1使能USART,UE位用于开启供给串口的时钟。发送或者接收数据字长可选8或9位,由USARTT_CR1的M位控制。
    1)发送器
    当USART_CR1寄存器的发送使能位TE置1时,启动数据发送,发送移位寄存器的数据会在TX引脚输出,低位在前,高位在后。如果是同步模式SCLK也输出时钟信号。
    一个字符帧发送需要3部分:起始位、数据帧、停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间 ;数据帧就是我们要发送的8或9位数据,数据是最低位开始传输的;停止位是一定时间周期的高电平。
    停止位的时间长短可以通过USART控制寄存器2(USART_CR2)的STOP[1:0]位控制,可选0.5个、1个、1.5个、2个停止位。默认使用1个停止位。2个停止位适用于正常USART模式、单线模式和调制解调器模式。0.5和1.5个停止位用于智能卡模式。
    当发使能位TE置1之后,发送器开始会发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往USART_DR寄存器写入要发送的数据。在写入最后一个数据后,需等待USART状态寄存器(USART_SR)的TC位为1,表示数据传输完成。USART_CR1寄存器的TCIE位置1,则产生中断。
    发送数据时,几个重要的标志位如下:
    TE:发送使能。
    TXE:发送寄存器为空,发送单个字节时使用。
    TC:发送完成,发送多个字节数据时候使用。
    TXIE:发送完成中断使能。
    2)接收器
    将CR1寄存器的RE位置1,使能USART接收,使得接收器在RX线开始搜索起始位。在确定起始位后,就根据RX线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器的数据移到PDR内,并把USART_SR寄存器的RXNE位置。如果USART_CR2寄存器的RXNEIE置1可以产生中断。
    接收数据时,几个重要的标志位如下:
    RE: 接收使能。
    RXNE:读数据寄存器非空。
    RXNEIE:发送完成中断使能。

    4.小数波特率生成
    USART 的发送器和接收器使用相同的波特率。计算公式如下:
    这里写图片描述
    其中,f PLCK 为 USART 时钟, USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中 DIV_Mantissa[11:0]位定义 USARTDIV 的整数部分,DIV_Fraction[3:0]位定义 USARTDIV 的小数部分。
    例如:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此时 USART_BRR 值为0x18A;那么USARTDIV的小数位10/16=0.625;整数位24,最终USARTDIV的值为24.625。
    如果知道 USARTDIV 值为 27.68,那么 DIV_Fraction=16*0.68=10.88,最接近的正整数为 11,所以 DIV_Fraction[3:0]为 0xB;DIV_Mantissa=整数(27.68)=27,即为 0x1B。
    波特率的常用值有 2400、9600、19200、115200。下面以实例讲解如何设定寄存器值得到波特率的值。
    我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,其他 USART 的最高频率为 36MHz。我们选取 USART1 作为实例讲解,即 f PLCK =72MHz。为得到 115200bps 的波特率,此时:

    115200 =72000000/(16 ∗ USARTDIV)

    解 得 USARTDIV=39.0625 , 可 算 得 DIV_Fraction=0.0625*16=1=0x01 ,DIV_Mantissa=39=0x27,即应该设置 USART_BRR 的值为 0x171。

    5.校验控制
    STM32F103系列控制器USART支持奇偶校验。使用校验位时,串口传输的长度将在8位数据帧上加上1位的校验位,总共9位,此时USART_CR1寄存器的M位需要设置位1,即9数据位。将USART_CR1寄存器的PCE位置1就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,发送数据帧时会自动添加校验位,接收数据自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会将USART_SR寄存器的PE置1,并可以产生奇偶校验中断。
    使用了奇偶校验控制位后,每个字符帧的格式变成了:起始位+数据帧+校验位+停止位。

    6.中断控制
    这里写图片描述


    五、USART初始化结构体
    初始化结构体的定义在stm32f10x_usart.h文件中,初始化库函数定义在stm32f10x_usart.c中。

    									USART初始化结构体
    									
    typedef struct {
     uint32_t USART_BaudRate; // 波特率
     uint16_t USART_WordLength; // 字长
     uint16_t USART_StopBits; // 停止位
     uint16_t USART_Parity; // 校验位
     uint16_t USART_Mode; // USART 模式
     uint16_t USART_HardwareFlowControl; // 硬件流控制
     } USART_InitTypeDef;
    

    1)USART_BaudRate:波特率设置。标准库函数会根据设定值计算得到USARTDIV值,从而设置USART_BRR的寄存器值。
    2)USART_WordLength:.数据帧字长,它设定USART_CR1寄存器M位的值。如果没有使能奇偶位校验控制,一般使用8数据位。
    3)USART_StopBits停止位设置。
    4)USART_Parity:奇偶校验控制选择。
    5)USART_Mode:USART模式选择有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1寄存器的 RE 位和 TE位。
    6)USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式下才有效,可选有,使能RTS、使能CTS、同时使能RTS和CTS、不使用硬件流。

    当使用同步模式,需配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置,该结构体内容只有在同步模式下才设置。

    				USART时钟初始化结构体
     typedef struct {
     uint16_t USART_Clock; // 时钟使能控制
     uint16_t USART_CPOL; // 时钟极性
     uint16_t USART_CPHA; // 时钟相位
     uint16_t USART_LastBit; // 最尾位时钟脉冲
     } USART_ClockInitTypeDef;
    

    1)USART_Clock:同步模式下SCLK引脚上时钟输出使能控制,可选禁止时钟输出(USART_Clock_Disable)或开启时钟输出(USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。 ݶ
    2)USART_CPOL:同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时SCLK引脚为低电平(USART_CPOL_Low)或高电平(USART_CPOL_High)。它设定USART_CR2寄存器的 CPOL位的值。
    3)USART_CPHA:同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。它设定 USART_CR2寄存器的 CPHA位的值。USART_CPHA与 USART_CPOL配合使用可以获得多种模式时钟关系。
    4)USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在SCLK引脚输出,可以是不输出脉冲(USART_LastBit_Disable) 、 输出脉冲(USART_LastBit_Enable)。它设定 USART_CR2 寄存器的 LBCL位的值。


    六、USART1接发通信实验
    USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
    来编写一个程序实现开发板与电脑通信,在开发板上电时通过USART发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
    1、编程思路

    1. 使能 RX和 TX 引脚 GPIO时钟和 USART时钟;
    2. 初始化 GPIO,并将 GPIO复用到 USART上;
    3. 配置 USART 参数;
    4. 配置中断控制器并使能 USART接收中断;
    5. 使能 USART;
    6. 在 USART接收中断服务函数实现数据接收和发送。
      2、代码分析
      1)GPIO和USART宏定义
     /**
     * 串口宏定义,不同的串口挂载的总线和 IO 不一样,移植时需要修改这几个宏
     */ 
     // 串口 1-USART1
     #define DEBUG_USARTx USART1
     #define DEBUG_USART_CLK RCC_APB2Periph_USART1
     #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
     #define DEBUG_USART_BAUDRATE 115200 
     // USART GPIO 引脚宏定义
     #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
     #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd 
     
     #define DEBUG_USART_TX_GPIO_PORT GPIOA
     #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
     #define DEBUG_USART_RX_GPIO_PORT GPIOA
     #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10 
     
     #define DEBUG_USART_IRQ USART1_IRQn
     #define DEBUG_USART_IRQHandler USART1_IRQHandler
    

    使用宏定义方便程序移植和升级 。开发板中的 CH340G 的收发引脚默认通过跳帽连接到 USART1,如果想使用其他串口,可以把 CH340G 跟 USART1 直接的连接跳帽拔掉,然后再把其他串口的 IO用杜邦线接到 CH340G的收发引脚即可。
    这里我们使用USART1,设定波特率为115200,选定USART的GPIO为PA9和PA10。

    2)嵌套向量中断控制器NVIC配置

     static void NVIC_Configuration(void)
     {
     NVIC_InitTypeDef NVIC_InitStructure; 
     /* 嵌套向量中断控制器组选择 */
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
     
     /* 配置 USART 为中断源 */
     NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
     /* 抢断优先级为 1 */
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
     /* 子优先级为 1 */
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
     /* 使能中断 */
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
     /* 初始化配置 NVIC */
     NVIC_Init(&NVIC_InitStructure);
     }
    

    3)USART初始化配置

     void USART_Config(void)
     {
     GPIO_InitTypeDef GPIO_InitStructure;
     USART_InitTypeDef USART_InitStructure;
     
     // 打开串口 GPIO 的时钟
     DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
     
     // 打开串口外设的时钟
     DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
     
     // 将 USART Tx 的 GPIO 配置为推挽复用模式
     GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
     GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
     
     // 将 USART Rx 的 GPIO 配置为浮空输入模式
     GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
     GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
     
     // 配置串口的工作参数
     // 配置波特率
     USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
     // 配置 针数据字长
     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
     // 配置停止位
     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(DEBUG_USARTx, &USART_InitStructure);
     
     // 串口中断优先级配置
     NVIC_Configuration();
     
     // 使能串口接收中断
     USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
     
     // 使能串口
     USART_Cmd(DEBUG_USARTx, ENABLE);
     }
    

    调用RCC_APB2PeriphClockCmd函数开启GPIO端口时钟,使用GPIO之前必须开启对应的时钟。RCC_APB2PeriphClockCmd函数开启USART时钟。
    使用GPIO之前需要初始化配置它,并且还要添加特殊设置,因为我们使用它作为外设引脚,一般都有特殊功能,模式设置为复用功能,把串口的Tx引脚配置为复用推挽输出,Rx引脚为浮空输入,数据完全由外部输入决定。
    配置USAT1通信参数为:波特率115200,字长8,1个停止位,没有校验位,不使用硬件流控制,收发一体工作模式,然后调用USART初始化函数完成配置。
    USART接收中断,需要配置NVIC,这里调用NVIC_Configuration函数完成配置,然后调用USART_ITConfig函数使能USART接收中断。
    最后 调用USART_Cmd函数使能USART,最终配置的是USART_CR1的UE位,具体作用是开启USART工作时钟,没有时钟那USART这个外设就工作不了。

    4)字符发送

    /***************** 发送一个字符 **********************/
     void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
     {
     /* 发送一个字节数据到 USART */
     USART_SendData(pUSARTx,ch);
     
     /* 等待发送数据寄存器为空 */
     while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
     }
     
     /***************** 发送字符串 **********************/
     void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
     {
     unsigned int k=0;
     do {
     Usart_SendByte( pUSARTx, *(str + k) );
     k++;
     } while (*(str + k)!='\0');
     
     /* 等待发送完成 */
     while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) {
     }
     }
    

    Usart_SendByte用来指定USART发送一个ASCLL码字符,它有2个形参:第一个USART第二个待发送的字符,它通过调用库函数USART_SendData来实现等待,并且增加了等待发送完成功能,它接收两个参数:一个是USART,一个是事件标志。这里循环检测发送数据寄存器这个标志,当跳出while循环时,说明发送数据寄存器为空。
    Usart_SendString函数用来发送一个字符串,实际调用Usar_SendByte函数发送每个字符,直到遇到空字符才停止发送。最后使用循环检测发送完成的事件标志TC,保证数据完成后才退出函数。

    5)USART中断服务函数

    void DEBUG_USART_IRQHandler(void)
     {
     uint8_t ucTemp;
     if (USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET) 
     {
     ucTemp = USART_ReceiveData( DEBUG_USARTx );
     USART_SendData(USARTx,ucTemp);
     }
     
     }
    

    该代码在stm32f10x_it.c文件中的,用来集中存放中断服务函数。当使能了中断并中断发生时,就会执行这里的中断服务函数。
    6)main函数

    int main(void)
     {
     /*初始化 USART 配置模式为 115200 8-N-1,中断接收*/
     USART_Config();
     
     Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\n");
     
     while (1) {
     
     }
     }
    

    USART_Config()函数完成USART初始化配置,包括GPIO USART配置,接收中断使能等。
    接下来调用字符发送函数把数据发给串口调试助手。
    最后什么也不做,等待接收中断产生,并在中断服务函数中回传数据。


    七、USART1指令控制RGB彩灯实验
    1、思路要点
    1)初始化配置RGB彩色灯GPIO
    2)使能RX和TX引脚GPIO时钟和USART时钟
    3)初始化GPIO,并将GPIO复用到USART上
    4)配置USART参数
    5)使能USART
    6)获取指令输入,根据控制RGB彩色灯
    2、代码分析
    1)GPIO和USART宏定义

    1 #define DEBUG_USARTx USART1
    2 #define DEBUG_USART_CLK RCC_APB2Periph_USART1
    3 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd
    4 #define DEBUG_USART_BAUDRATE 115200
    5 
    6 // USART GPIO 引脚宏定义
    7 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)
    8 #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
    9 
    10 #define DEBUG_USART_TX_GPIO_PORT GPIOA
    11 #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9
    12 #define DEBUG_USART_RX_GPIO_PORT GPIOA
    13 #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10
    14 
    15 #define DEBUG_USART_IRQ USART1_IRQn
    16 #define DEBUG_USART_IRQHandler USART1_IRQHandler
    

    2)USART初始化配置

    1 void USART_Config(void)
    2 {
    3 GPIO_InitTypeDef GPIO_InitStructure;
    4 USART_InitTypeDef USART_InitStructure;
    5 
    6 // 打开串口 GPIO 的时钟
    7 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
    8 
    9 // 打开串口外设的时钟
    10 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
    11 
    12 // 将 USART Tx 的 GPIO 配置为推挽复用模式
    13 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    15 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    16 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
    17 
    18 // 将 USART Rx 的 GPIO 配置为浮空输入模式
    19 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    21 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    22 
    23 // 配置串口的工作参数
    24 // 配置波特率
    25 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
    26 // 配置 针数据字长
    27 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    28 // 配置停止位
    29 USART_InitStructure.USART_StopBits = USART_StopBits_1;
    30 // 配置校验位
    31 USART_InitStructure.USART_Parity = USART_Parity_No ;
    32 // 配置硬件流控制
    33 USART_InitStructure.USART_HardwareFlowControl =
    34 USART_HardwareFlowControl_None;
    35 // 配置工作模式,收发一起
    36 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    37 // 完成串口的初始化配置
    38 USART_Init(DEBUG_USARTx, &USART_InitStructure);
    39 
    40 // 使能串口
    41 USART_Cmd(DEBUG_USARTx, ENABLE);
    42 }
    

    与上实验基本一样,唯一不同没使用使能中断
    3)重定向printf和scanf函数

    1 ///重定向 c 库函数 printf 到串口,重定向后可使用 printf 函数
    2 int fputc(int ch, FILE *f)
    3 {
    4 /* 发送一个字节数据到串口 */
    5 USART_SendData(DEBUG_USARTx, (uint8_t) ch);
    6 
    7 /* 等待发送完毕 */
    8 while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
    9 
    10 return (ch);
    11 }
    12 
    13 ///重定向 c 库函数 scanf 到串口,重写向后可使用 scanf、getchar 等函数
    14 int fgetc(FILE *f)
    15 {
    16 /* 等待串口输入数据 */
    17 while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
    18 
    19 return (int)USART_ReceiveData(DEBUG_USARTx);
    20 }
    

    fputc函数是printf函数内部的一个函数,功能是将字符ch写入文件指针f所指向文件的当前写指针位置,我们使用USART函数重新修改fputc函数内容,达到类似写入的功能。
    fgetc实现字符读取功能。
    还有一点需要注意的,使用 fput和 fgetc函数达到重定向 C语言标准库输入输出函数必须在 MDK的工程选项把“Use MicroLIB”勾选上,MicoroLIB 是缺省 C库的备选库,它对标准 C库进行了高度优化使代码更少,占用更少资源。
    为使用 printf、scanf 函数需要在文件中包含 stdio.h头文件。
    4)输出提示信息

    1 static void Show_Message(void)
    2 {
    3 printf("\r\n 这是一个通过串口通信指令控制 RGB 彩灯实验 \n");
    4 printf("使用 USART 参数为:%d 8-N-1 \n",USART_BAUDRATE);
    5 printf("开发板接到指令后控制 RGB 彩灯颜色,指令对应如下:\n");
    6 printf(" 指令 ------ 彩灯颜色 \n");
    7 printf(" 1 ------ 红 \n");
    8 printf(" 2 ------ 绿 \n");
    9 printf(" 3 ------ 蓝 \n");
    10 printf(" 4 ------ 黄 \n");
    11 printf(" 5 ------ 紫 \n");
    12 printf(" 6 ------ 青 \n");
    13 printf(" 7 ------ 白 \n");
    14 printf(" 8 ------ 灭 \n");
    15 }
    

    5)main函数

    1 int main(void)
    2 {
    3 char ch;
    4 
    5 /* 初始化 RGB 彩灯 */
    6 LED_GPIO_Config();
    7 
    8 /* 初始化 USART 配置模式为 115200 8-N-1 */
    9 USART_Config();
    10 
    11 /* 打印指令输入提示信息 */
    12 Show_Message();
    13 while (1)
    14 {
    15 /* 获取字符指令 */
    16 ch=getchar();
    17 printf("接收到字符:%c\n",ch);
    18 
    19 /* 根据字符指令控制 RGB 彩灯颜色 */
    20 switch (ch)
    21 {
    22 case '1':
    23 LED_RED;
    24 break;
    25 case '2':
    26 LED_GREEN;
    27 break;
    28 case '3':
    29 LED_BLUE;
    30 break;
    31 case '4':
    32 LED_YELLOW;
    33 break;
    34 case '5':
    35 LED_PURPLE;
    36 break;
    37 case '6':
    38 LED_CYAN;
    39 break;
    40 case '7':
    41 LED_WHITE;
    42 break;
    43 case '8':
    44 LED_RGBOFF;
    45 break;
    46 default:
    47 /* 如果不是指定指令字符,打印提示信息 */
    48 Show_Message();
    49 break;
    50 }
    51 }
    52 }
    

    首先我们定义一个字符变量来存放接收到的字符。
    接下来调用 LED_GPIO_Config 函数完成 RGB 彩色 GPIO 初始化配置,该函数定义在bsp_led.c 文件内。
    调用 USART_Config 函完成 USART初始化配置。
    Show_Message函数使用 printf 函数打印实验指令说明信息。
    getchar函数用于等待获取一个字符,并返回字符。我们使用 ch变量保持返回的字符,接下来判断 ch内容执行对应的程序。
    我们使用 switch语句判断 ch 变量内容,并执行对应的功能程序。

    本文引用《STM32库开发实战指南》

    展开全文
  • UART和USART有区别

    千次阅读 2019-08-25 19:55:39
    UART  UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信,如汽车音响与外接AP之间的通信,与PC机通信包括与监控调试器和其它...
  • UART与USART区别

    万次阅读 2017-11-26 10:32:48
    USART:通用同步和异步收发器 UART:通用异步收发器 当进行异步通信时,这两者是没有区别的。区别在于USART比UART多了同步通信功能。 这个同步通信功能可以把USART当做SPI来用,比如用USART来驱动SPI设备。 ...
  • STM32的USART讲解

    千次阅读 2017-11-21 21:05:05
    一,串口相关寄存器USART_SR 状态寄存器 USART_DR 数据寄存器 USART_BRR 波特率寄存器 USART_CR1 控制寄存器 USART_SR-状态寄存器:     状态寄存器USART_SR,描述串口寄存器的一些状态:    如位5:读数据...
  • USART

    千次阅读 2017-11-07 16:12:16
    USART初始化结构体typedef struct{ uint32_t USART_BaudRate;//波特率 uint32_t USART_WordLength;//字长 uint32_t USART_StopBits;//停止位 uint32_t USART_Parity;//校验位 uint32_t USART_Mode;//USART模式 ...
  • USART与UART的区别

    万次阅读 2019-03-11 17:29:32
    USART(universal synchronous asynchronous receiver and transmitte): 通用同步异步收发器 USART是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。 UART(universal asynchronous receiver and ...
  • STM32--USART详解

    2020-10-11 21:57:55
    STM32–USART详解 1 串口的基本概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用分数波特...
  • STM32------USART详解

    千次阅读 2018-05-27 22:52:59
    1. 串口的基本概念在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用分数波特率发生器提供宽...
  •  while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); 仿真发现USART1时钟味使能。。。 使能时钟后仿真,USART1寄存器有反应,发送数据是仍死在  while(USART_GetFlagStatus(USART1, USART_FLAG_...
  • STM32USART串口库函数介绍之USART_Init

    万次阅读 2016-10-08 20:05:26
    本文将介绍USART串口的库函数,总的来说有以下函数体: ※ USART_Init函数:初始化所使用的串口外设 ※ USART_Cmd函数:使能或者失能USART外设 ※ USART_ITConfig函数:使能或者失能指定的USART中断 ※ USART_...
  • STM32 USART库函数介绍2

    千次阅读 2016-10-08 20:37:04
    USART_Cmd函数的功能是使能或失能USART串口外设。 例:使能USART1 USART_Cmd(USART1,ENABLE); USART_ITConfig函数的功能是使能或者失能指定的USART串口中断。 USART_IT_PE 奇偶错误中断 USART_IT_TXE 发送中断...
  • USART_FLAG_RXNE与USART_IT_RXNE区别

    万次阅读 2017-07-29 13:06:57
    从固件库资料中可以看出,USART_FLAG_RXNE是接收数据寄存器非空标志位,USART_IT_RXNE为接收中断标志位,也就是说,串口一接收到数据,这两个标志位都会被置位。
  • STM32 串口中的硬件BUG,请注意!

    万次阅读 多人点赞 2012-11-28 13:26:59
    调试串口时,发现串口会出现...1. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);使能了接收中断,那么ORE中断也同时被开启了。  2. ORE中断只能使用USART_GetFlagStatus(USART1, USART_FLAG_ORE) 读到
  • 今天在使用USART模块,遇到了一些问题并解决了,于是发贴共享。 问题描述: 在使用USART做串口通讯时,我只把接收中断打开,并设置抢占优先级为最低一个级别,而接收中断上一个优先级处理事情比较多,可能占用了...
  • STM32串口中断接收和中断发送

    万次阅读 2016-03-19 14:58:08
    STM32串口USART1中断接收和中断发送 先贴出中断函数: void USART1_IRQHandler(void){  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {  USART_ClearITPendingBit(USART1, USART_IT_RX
  • stm32 USART_IT_IDLE中断 一帧数据

    万次阅读 2018-01-25 12:22:28
    USART_IT_IDLE中断,是串口收到一帧数据后,发生的中断。也可以叫做一包数据 USART_IT_IDLE和USART_IT_RXNE区别 当接收到1个字节,会产生USART_IT_RXNE中断 当接收到一帧数据,就会产生USART_IT_IDLE中断 清...
  • STM32固件库中提供了串口收发的标志位函数,包括USART_GetFlagStatus(…,…);和USART_GetITStatus(…,…);,两者容易混淆,重点区别就在于:前者返回值是中断标志位状态(读SR寄存器),后者返回值是中断发生与否的...
1 2 3 4 5 ... 20
收藏数 19,491
精华内容 7,796
关键字:

usart