欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32笔记(九)---串口通信

程序员文章站 2022-04-01 23:37:54
...

串口通信(USART)

一、 串口通信协议简介

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

简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。

1-2 常用标准
①.RS232标准

RS232标准串口通讯结构图

STM32笔记(九)---串口通信
  • RS232标准串口主要用于工业设备直接通信

  • 电平转换芯片一般有MAX3232,SP3232

    DB9 标准的公头及母头接法

STM32笔记(九)---串口通信

RS-232 与 TTL 电平区别(232趋向高容错)

STM32笔记(九)---串口通信
②.USB转串口(USB2TTL)

USB转串口通讯结构图

STM32笔记(九)---串口通信
  • USB转串口主要用于设备跟电脑通信
  • 电平转换芯片一般有CH340、PL2303、CP2102、FT232
  • 使用的时候电脑端需要安装电平转换芯片的驱动
③.原生的串口到串口 (TTL2TTL)

原生的串口到串口结构图

STM32笔记(九)---串口通信
  • 原生的串口通信主要是控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信
  • GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块
1-3 串口数据包的基本组成

STM32笔记(九)---串口通信

  • 起始位:由1个逻辑 0 的数据位表示

  • 结束位:由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示

  • 有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 、9位长

  • 校验位:可选,为的是数据的抗干扰性。

    校验方法分为:

    1-奇校验(odd)、 2-偶校验(even)

    3-0 校验(space)、 4-校验(mark)

    5-无校验(noparity)

    • 奇校验 (odd) :有效数据和校验位中“ 1”的个数为奇数

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

    • 偶校验 (even) :有效数据和校验位中“ 1”的个数为偶数

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

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

    • 1 校验: 校验位总为“ 1”。

    • 无校验: 数据包中不包含校验位。

二、 STM32串口功能框图讲解

STM32笔记(九)---串口通信
2-1 引脚
STM32笔记(九)---串口通信

TX:数据发送

RX:数据接收

SCLK :时钟,仅同步通信时使用

nRTS :请求发送(Request To Send)

nCTS :允许发送(Clear To Send)

2-2 数据寄存器
STM32笔记(九)---串口通信
  • 数据寄存器 —USART_DR :9位有效,包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。一个地址对应了两个物理内存。

    STM32笔记(九)---串口通信
  • 字长配置:USART_CR1:M,0:8bit,1:9bit

    STM32笔记(九)---串口通信
  • 停止位配置:USART_CR2:STOP

    STM32笔记(九)---串口通信
  • 奇偶验证位:USART_CR1:PCE(使能校验控制)、PS(奇偶校验选择)、PEIE(PE中断使能)

    STM32笔记(九)---串口通信
  • USART_SR :PE(奇偶校验反映位)

    STM32笔记(九)---串口通信
2-2-1 如何通过串口实现发送接收?

​ 1、 USART_CR1:UE、TE、RE均配置为开启

STM32笔记(九)---串口通信STM32笔记(九)---串口通信

​ 2、 以发送为例,发送过程涉及2个寄存器的值改变(发送数据寄存器----TDR)
STM32笔记(九)---串口通信

2、 TDR发送数据后状态寄存器(USART_SR)中的TXE(empty)位置1,若TXEIE=1则产生中断,不代表数据发送成功,此时TC位置1才表示发送成功,若此时TCIE=1则产生中断(相应的读取时UE、RE使能情况下,读取数据进入接收移位寄存器后传到RDR,RXNE置1,用户可从DR寄存器中读数据,若RXNEIE=1,则产生中断)、

  • TXEIE/TCIE/RXNEIE均位于CR1寄存器

STM32笔记(九)---串口通信STM32笔记(九)---串口通信

​ USART_SR:TXE,Transmit data register empty

2-3 控制器

STM32笔记(九)---串口通信

STM32笔记(九)---串口通信STM32笔记(九)---串口通信
2-4 波特率
  • 每秒钟要发送多少数据(多少个二进制位)

  • 配置单位:USART_BRR:波特率寄存器

    STM32笔记(九)---串口通信
  • 计算公式:

    STM32笔记(九)---串口通信

    例:**USART:**USART1,时钟为72M **波特率:**115200

    STM32笔记(九)---串口通信

    最小精度:1/16(2^4)

三、 代码

3-1 固件库相关配置结构体
  • USART初始化结构体:USART_InitTypeDef

    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
      // 硬件流选择 CR3_CTSE、CR3_RTSE
      uint16_t USART_HardwareFlowControl;
    } USART_InitTypeDef;
    
  • USART同步时钟初始化结构体: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;
    
3-2 固件库相关配置函数
  • 串口初始化函数

    void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
    
  • 中断配置函数:

    void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
    
  • 串口使能函数:

    void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
    
  • 数据发送函数

    void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
    
  • 数据接收函数:

    uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
    
  • 中断状态位获取函数:

    ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
    
3-3 固件库编程
编程要点

1-初始化串口需要用到的GPIO

2-初始化串口,USART_InitTypeDef

3-中断配置(接收中断,中断优先级)

4-使能串口

5-编写发送和接收函数

6-编写中断服务函数

实验1:中断接收和发送
  • 需求:单片机给电脑发送数据,电脑上位机把数据打印出来;电脑上位机给单片机发数据,单片机接收到数据之后立马发回给电脑,并打印出来。

    • ‘bsp-usart.c’
    #include "bsp-usart.h"
    
    static void NVIC_Configuration(void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
      
      /* 嵌套向量中断控制器组选择 */
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      
      /* 配置USART为中断源 */
      NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
      /* 抢断优先级*/
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      /* 子优先级 */
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      /* 使能中断 */
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      /* 初始化配置NVIC */
      NVIC_Init(&NVIC_InitStructure);
    }
    
    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);	    
    }
    
    /* 发送一个字节 */
    void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
    {
    	USART_SendData(pUSARTx, data);
    	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
    }
    
    /* 发送两个字节的数据 */
    void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)
    {
    	uint8_t temp_h,temp_l;
    	
    	temp_h = (data&0xff00) >> 8 ;
    	temp_l = data&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 );
    }
    
    /* 发送8位数据的数组 */
    void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num)
    {
    	uint8_t i;
    	for( i=0; i<num; i++ )
      {
    		Usart_SendByte(pUSARTx, array[i]);
    	}
    	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
    }
    
    /* 发送字符串 */
    void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
    {
    	uint8_t i=0;
    	do
      {
    		Usart_SendByte(pUSARTx, *(str+i));
    		i++;
    	}while(*(str+i) != '\0');
    	while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == 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 __BSP_USART_H
    #define __BSP_USART_H
    
    #include "stm32f10x.h"
    #include <stdio.h>
    
    #define DEBUG_USART1     1
    #define DEBUG_USART2     0
    #define DEBUG_USART3     0
    #define DEBUG_USART4     0
    #define DEBUG_USART5     0
    
    #if DEBUG_USART1
    // 串口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
    
    #elif DEBUG_USART2
    //串口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
    
    #elif DEBUG_USART3
    //串口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
    
    #elif DEBUG_USART4
    //串口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
    
    #elif DEBUG_USART5
    //串口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
    
    #endif
    
    void USART_Config(void);
    void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
    void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data);
    void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num);
    void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str);
    #endif  /* __BSP_USART_H */
    
    
    • ‘stm32f10x_it.c’
    void DEBUG_USART_IRQHandler(void)
    {
    	uint8_t ucTemp;
    	if(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TC) != RESET)//再次判断是否发送完毕
    	{
    		ucTemp = USART_ReceiveData(DEBUG_USARTx);				 //接收数据
    		USART_SendData(DEBUG_USARTx, ucTemp);					 //将接收数据发送至单片机显示
    	}
    }
    
    
    • ‘main.c’
    #include "stm32f10x.h"
    #include "bsp-usart.h"
    
    int main(void)
    {
    	USART_Config();
    	printf("test_printf~~\n");
    	Usart_SendStr(DEBUG_USARTx,"test_SendStr~~\n");
    	while(1)
    	{
    		//
    	}
    }
    
    

    串口调试助手根据‘配置串口的工作参数’选择相应参数调试

    串口调试助手现象:

    STM32笔记(九)---串口通信
实验2:串口控制RGB灯亮灭
  • **需求:**电脑给单片机发命令,用于控制开发板上的RGB灯。

    (与实验1相比改动部分)

    • ‘stm32f10x_it.c’
    void DEBUG_USART_IRQHandler(void)
    {
    	if(USART_GetFlagStatus(DEBUG_USARTx,USART_FLAG_TC) != RESET)
    	{
    		ch = USART_ReceiveData(DEBUG_USARTx);//ch为外部引用全局变量
    		printf("ch = %c\n", ch);
    	}
    }
    
    • ‘main.c’
    #include "stm32f10x.h"
    #include "bsp-led.h"
    #include "bsp-usart.h"
    
    uint16_t ch = 0;//全局变量ch,用于接收串口信息
    
    int main(void)
    {
    	USART_Config();				//串口初始化
    	LED_GPIO_Config();			//LED初始化
    	while(1)
    	{
    		//USART判断ch的值进行RGB的状态切换
    		LED_USART_SWITCH();		//ch--switch函数
    /***************************************/
    //		void LED_USART_SWITCH(void)
    //		{
    //			switch(ch)
    //			{
    //				case 0x01: LEDON_G;break;		//绿灯
    //				case 0x02: LEDON_R;break;		//红灯
    //				case 0x03: LEDON_B;break;		//蓝灯
    //				default:   LED_ALL(ON);break;	//白灯
    //			}
    //		}
    /***************************************/
    	}
    }
    
相关标签: 嵌入式 stm32