stm32usart串口通信(标准库)

通信

按照数据传输方式

  • 串行通信:数据位一个接一个地按顺序发送。

  • 异步串行:如RS-232、UART。
  • 同步串行:如SPI、I2C、CAN。
  • 并行通信:多个数据位同时在多个通道上传输,通常速度较快,但受限于距离和干扰。

  • 并行总线:如PCI、IDE。
  •  按照通信模式

  • 单工(Simplex):数据只能单向传输。例如,传统的广播电视。

  • 半双工(Half-Duplex):数据可以双向传输,但不能同时进行。例如,对讲机。

  • 全双工(Full-Duplex):数据可以同时双向传输。例如,电话通信。

  • ce5ea4f68d5d4cfead2e8346f7f91ed8.png

    通信速率

    在数字通信系统中,通信速率(传输速率)指数据在信道中传输的速度,它分为两种:传信率和传码率。

    传信率:每秒钟传输的信息量,即每秒钟传输的二进制位数,单位为 bit/s(即比特每秒),因而又称为比特率。

    传码率:每秒钟传输的码元个数,单位为 Baud(即波特每秒),因而又称为波特率。

    波特率被传输的是码元,码元是信号被调制后的概念,每个码元都可以表示一定 bit 的数据信息量。举个例子,在 TTL 电平标准的通信中,用 0V 表示逻辑 0,5V 表示逻辑 1,这时候这个码元就可以表示两种状态。如果电平信号 0V、2V、4V 和 6V 分别表示二进制数 00、01、10、11,这时候每一个码元就可以表示四种状态。

    比特率和波特率的关系可以用以下式子表示:

    比特率 = 波特率 * log2M

    其中 M 表示码元承载的信息量。我们也可以理解 M 为码元的进制数。采用二进制的时候,波特率和比特率数值上相等。

    数据传输方式:同步、异步

    同步:共用同一时钟信号,在总线上保持统一的时序和周期完成信息传输。

    优点:可以实现高速率、大容量的数据传输,以及点对多点传输。

    468173e47ba044aab43ba18425a04d28.png

    异步通信不需要时钟信号,而是在数据信号中加入开始位和停止位等一些同步信号,以便

    使接收端能够正确地将每一个字符接收下来,某些通信中还需要双方约定传输速率。452e07e45f8a408ba16e416a83796080.png

    UART(通用异步收发传输器)是一个全双工通用异步串行收/发模块,主要用于打印程序调试信息、上位机和下位机的通信以及ISP程序下载等场合。

    UART至少需要两根数据线用于通信双方进行数据双向同时传输,最简单的UART接口由TxD、RxD、GND共3根线组成。其中,TxD用于发送数据,RxD用于接收数据,GND为信号地线,通过交叉连接实现两个芯片间的串口通信。

    492ba7c9143043538d73c31950322377.png

    串口通讯

    在电路加USB转串口芯片,实现USB通讯协议和UART串行通讯协议的转换.

    串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的 数据包格式要约定一致才能正常收发数据.

    a9dfc6cdc25c41dea72409d79d3a4afa.png

    1.波特率设置

    异步通信设置相同波特率

    2.数据帧格式

  • 起始位、结束位
  • 一个数据帧是从起始位开始,直到停止位。数据帧中的起始位是由一个逻辑 0

    的数据位表示,而数据帧的停止位可以是 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。

  • 数据位
  • 有效数据位通常会被约定为 5、6、7 或者 8 个位长。有效数据位是低位(LSB)在前,高位(MSB)在后。

  • 校验位
  • 校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,检验

    方法有:奇检验、偶检验、0 检验、1 检验以及无检验。

    奇校验是指有效数据为和校验位中“1”的个数为奇数,比如一个 8 位长的有效数据为:10101001,总共有 4 个“1”,为达到奇校验效果,校验位设置为“1”,最后传输的数据是 8 位的有效数据加上 1 位的校验位总共 9 位。

    偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为“0”。0 校验是指不管有效数据中的内容是什么,校验位总为“0”,1 校验是校验位总为“1”。无校验是指数据帧中不包含校验位

     

    USART

    USART发送和接收原理

    发送过程

    1. 初始化USART:配置波特率、数据位、停止位等参数。
    2. 使能发送:允许USART进行数据发送操作。
    3. 写入数据:将要发送的数据写入数据寄存器(DR)。
    4. 等待发送完成:USART会通过状态寄存器(SR)中的标志位指示发送是否完成。

    接收过程

    1. 配置接收:设置USART以接收数据。
    2. 读取数据:从数据寄存器(DR)读取接收到的数据。
    3. 检查接收状态:通过状态寄存器(SR)的标志位确定新数据是否可用。

    USART寄存器概述

    USART的主要寄存器包括:

  • BRR(波特率寄存器)
  • CR1(控制寄存器1)
  • CR2(控制寄存器2)
  • CR3(控制寄存器3)
  • SR(状态寄存器)
  • DR(数据寄存器)
  • 波特率寄存器(BRR)

  • 功能:配置USART的波特率。
  • 计算公式BRR = F_CLK / (16 * BaudRate)
  • USART1->BRR = SystemCoreClock / 115200; // 设置波特率为115200

     

    控制寄存器1(CR1)

  • 功能:配置USART的基本功能。

  • 重要位解析:

  • UE(USART使能位):设置为1使能USART模块。
  • TE(发送使能位):设置为1以启用发送功能。
  • RE(接收使能位):设置为1以启用接收功能。
  • RXNEIE(接收中断使能位):当接收到数据时使能接收中断。
  • TXEIE(发送中断使能位):当发送缓冲区为空时使能发送中断。
  •  控制寄存器2(CR2)

  • 功能:配置停止位数等。

  • 重要位解析:

  • STOP[13:12]:配置停止位的数量,常用的选项有0(1个停止位)、1(0.5个停止位)、2(2个停止位)和3(1.5个停止位)。
  • 控制寄存器3(CR3)

  • 功能:配置一些高级功能,如流控等。

  • 重要位解析:

  • CTSE(CTS使能位):使能CTS流控制。
  • RTSE(RTS使能位):使能RTS流控制。
  • 状态寄存器(SR)

  • 功能:反映USART的当前状态。

  • 重要位解析:

  • TXE(发送数据寄存器空标志):如果为1,则可以写入数据到DR,表示发送缓冲区为空。
  • RXNE(接收数据寄存器非空标志):如果为1,则可从DR读取数据,表示接收到数据。
  • TC(传输完成标志):如果为1,则所有数据已经成功发送。
  • 4. 数据寄存器(DR)

  • 功能:用于发送和接收数据。

  • 当要发送数据时,将数据写入此寄存器;接收到数据时,从此寄存器读取数据。

  •  

    USART的3种方式

    1.轮询方式

    CPU不断地查询I/O设备是否准备就绪,如果准备就绪就发送,否则提示超时错误;会占用CPU的大量时间,效率低。

    发送数据

    在使用轮询方式发送数据时,主要依赖于TXE标志位来判断数据寄存器是否为空。当数据寄存器为空时,可以向其写入数据进行发送。

  • 检查USART_SR寄存器中的TXE标志位。
  • TXE为1,则写入数据到DR寄存器。
  • 接收数据

    在接收数据时,我们通过检查RXNE标志位来判断是否有新数据可读。当接收寄存器有数据时,该标志位被置位

  • 检查USART_SR寄存器中的RXNE标志位。
  • RXNE为1,则读取DR寄存器中的数据。
  • #include "usart.h"
    
    
    void USART_Init_Config(void)
    {
        GPIO_InitTypeDef GPIO_InitStructure;
    	USART_InitTypeDef USART_InitStructure;
        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);  
    	USART_DeInit(USART1);      //重置指定的USART外设到其初始状态                                                 
        
        //USART1_TX   
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         //选择要使用的I/O引脚,此处选择PA9引脚
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的输出速度为50MHz
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //设置引脚输出模式为复用推挽输出模式
        GPIO_Init(GPIOA, &GPIO_InitStructure); 
       
        //USART1_RX	  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;            //选择要使用的I/O引脚,此处选择PA10引脚
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚输入模式为浮空输入模式
        GPIO_Init(GPIOA, &GPIO_InitStructure); 
    
        //USART1 配置
    	USART_InitStructure.USART_BaudRate = 115200;                                   //设置波特率为115200
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //数据位占8位
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //设置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
        USART_Cmd(USART1, ENABLE);                                                     //使能串口1
    }
    
    
    //重定向printf函数
    int fputc(int ch, FILE *f)
    {
    		USART_SendData(USART1, (uint8_t) ch);
    		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
    	
    		return (ch);
    }
    
    //重定向scanf函数
    int fgetc(FILE *f)
    {
    		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    
    		return (int)USART_ReceiveData(USART1);
    }
    
    

    2.中断方式

    中断发送

    在USART中断发送过程中,我们通常利用TXE标志位来触发发送中断。当数据寄存器(DR)为空时,该标志位会被置位,表示可以发送下一个字节的数据。

  • 启用USART发送中断(TXEIE)。
  • 当用户通过程序写入数据到USART的数据寄存器时,若数据寄存器空,TXE标志位被置位。
  • 触发USART发送中断,进入中断服务例程(ISR)。
  • 在ISR中,可以继续发送下一个字节并清除TXE标志位。
  • 中断接收

    在USART中断接收过程中,我们利用RXNE标志位来触发接收中断。当接收数据寄存器有新数据时,该标志位被置位,表示可以读取数据。

  • 启用USART接收中断(RXNEIE)。
  • 当USART接收到新的数据时,RXNE标志位被置位。
  • 触发USART接收中断,进入中断服务例程(ISR)。
  • 在ISR中,读取数据寄存器的内容并清除RXNE标志位。
  •  usart.c

    #include "usart.h"
    
    
    void NVIC_Usart_Config(void)
    {
       NVIC_InitTypeDef NVIC_InitStructure;
      
      /* 嵌套向量中断控制器组选择 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      
      /* 配置USART为中断源 */
      NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
      /* 抢断优先级*/
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      /* 子优先级 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      /* 使能中断 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      /* 初始化配置NVIC */
      NVIC_Init(&NVIC_InitStructure);
    
    }
    
    
    void USART_Init_Config(void){
    
         GPIO_InitTypeDef GPIO_InitStructure;   //定义一个GPIO_InitTypeDef类型的结构体变量,用于配置GPIo引脚
    	USART_InitTypeDef USART_InitStructure; //定义一个USART_InitTypeDer类型的结构体变量,用于配置串口
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //开始USART1和GPIOA的时钟
    	USART_DeInit(USART1);                                                       //复位USART1
        
        //USART1_TX  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;         
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置引脚的输出速度为50MHz
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	  //设置引脚输出模式为复用推挽输出模式
        GPIO_Init(GPIOA, &GPIO_InitStructure); 
       
        //USART1_RX	  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;           
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //设置引脚输入模式为浮空输入模式
        GPIO_Init(GPIOA, &GPIO_InitStructure); 
    
        //USART1 配置
    	USART_InitStructure.USART_BaudRate = 115200;                                   //设置波特率为115200
    	USART_InitStructure.USART_WordLength = USART_WordLength_8b;                    //数据位占8位
    	USART_InitStructure.USART_StopBits = USART_StopBits_1;                         //设置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_ITConfig(USART1,USART_IT_RXNE,ENABLE); //打开串口1的接收中断
        USART_ITConfig(USART1,USART_IT_TC,ENABLE);   //开始串口1的发送中断
    	USART_Init(USART1, &USART_InitStructure);    //初始化串口1
        USART_Cmd(USART1, ENABLE);                   //使能串口1
        
        NVIC_Usart_Config();
    }
    
    //重定向printf函数
    int fputc(int ch, FILE *f)
    {
    		USART_SendData(USART1, (uint8_t) ch);
    		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);		
    	
    		return (ch);
    }
    
    //重定向scanf函数
    int fgetc(FILE *f)
    {
    		while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    
    		return (int)USART_ReceiveData(USART1);
    }
    
    
    //中断处理函数
    void USART1_IRQHandler(void)
    {
        uint8_t Rx_Data;
    
        if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {
            Rx_Data = (uint8_t)USART_ReceiveData(USART1);
            
            // 发送固定字符串 "hello zhi_bei_you"
           Usart_SendStr(USART1, (uint8_t *)"hello zhi_bei_you\n");
    
            // 判断输入的 Rx_Data 并相应处理
            if (Rx_Data == '0') // 注意这里比较的是字符'0'
            {
                GPIO_SetBits(GPIOB, GPIO_Pin_5); // 熄灭LED灯
                printf("熄灭LED灯\n");
            }
            else if (Rx_Data == '1') // 注意这里比较的是字符'1'
            {
                GPIO_ResetBits(GPIOB, GPIO_Pin_5); // 点亮LED灯
                printf("点亮LED灯\n");
            }
        }    
    }
    
    /* 发送字符串 */
    void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
    {
        while (*str) // 直到遇到字符串结束符 '\0'
        {
            while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); // 等待发送缓冲区空
            USART_SendData(pUSARTx, *str++); // 发送当前字符,并移动到下一个
        }
        while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET); // 等待发送完成
    }
    
    

     main

    #include "stm32f10x.h"
    #include "systick.h"
    #include "LED.h"
    #include "usart.h"
    
    int main()
    {
    		LED_ConfigInit();
    		USART_Init_Config();
    //		SystemClock_Config();
    
    		while(1)
    		{
    			
    //			printf('123\n');
    //			
    		}
    //	//
    //	LED_ConfigInit();
    //	GPIO_ResetBits(GPIOB,GPIO_Pin_5);
    //	uint8_t a[10]={100,2,3,4,5,6,7,8,9,10};
    //	while(1){
    //		//GPIO_SetBits(GPIOB,GPIO_Pin_5);
    //		//SysTick_Delay_Ms(1000);
    //		Usart_SendArray(DEBUG_USARTx, a,10);
    //		//GPIO_ResetBits(GPIOB,GPIO_Pin_5);
    //		//SysTick_Delay_Ms(1000);
    //	}
    //	
    }

    9ed67b9c5e7a400686fb685512601fa4.png

    运行结果如图,但是hello_zhi_you被打印了两次,我不知道怎么回事,如果有知道的大佬愿意解惑,非常感谢!

    3、DMA方式

    直接存储器传送,使用 DMA(Direct Memory Access)配置 USART 进行数据接收或发送可以显著提高系统的性能,尤其是在需要处理大量数据的情况下。DMA 允许外设与内存之间的数据传输,而不占用 CPU 的时间,从而实现更高效的数据处理。

     

    ###持续更QwQ,如果有错,欢迎指出,送上作为一个浅陋学习者真诚的感谢!

     

    作者:zhi_beiyou

    物联沃分享整理
    物联沃-IOTWORD物联网 » stm32usart串口通信(标准库)

    发表回复