STM32串口通信

  • 学习计划
  • 一、串口通信知识点
  • 二、硬件部分
  • 1.所需硬件
  • 2.部分硬件连接
  • 三、阻塞式
  • 0、串口阻塞式发送和接收概念
  • 1、STM32CUBEMX配置
  • 2、编写阻塞式串口发送与接收代码
  • 3、学习小技巧
  • 4、程序编译与下载
  • 5、程序验证
  • 6、重定向printf和scanf
  • 三、中断式
  • 0、串口中断式发送和接收概念
  • 1、STM32CUBEMX配置更新
  • 2、编写中断式串口发送与接收代码
  • 3、程序验证
  • 四、DMA
  • 0、DMA概念
  • 1、STM32CUBEMX配置更新
  • 2、编写DMA式不定长串口发送与接收代码
  • 3、程序验证
  • 参考文章

  • 学习计划

  • 每天更新STM32学习笔记

  • 一、串口通信知识点

  • 串口通信的数据按顺序传输,其数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口,故串口通信至少需要两根线(GND和一根信号线)来实现单工通信。若要实现全双工通信,则要三根线(GND和两根信号线)。
  • 串口通信的数据包由起始位、数据位(5~8位)、校验位(奇\偶\无校验位)以及停止位组成,通信双方的数据包格式要约定一致才能正常收发数据。
  • 串口通信速率:波特率,单位Bits/s。
  • UART只用于异步通信;USART用于同步/异步通信。若为同步通信,则要加一根时钟线。
  • 嵌入式中的通讯协议——UART、I2C、SPI、DMA

    二、硬件部分

    1.所需硬件

    STM32F407ZGT6相关开发板、USB转TTL、STLINK下载器

    2.部分硬件连接

    USB转TTL———–STM32
    RXD——————-PA9
    TXD——————-PA10

    STLINK————–STM32
    SWCLK————–PA14
    SWDIO————–PA13

    三、阻塞式

    0、串口阻塞式发送和接收概念

    串口阻塞发送:发送一段数据,在没有发送完所有数据之前,一直停留在此发送函数(可设定阻塞时间),这个过程中会阻塞别的程序运行;

    1、STM32CUBEMX配置

    在STM32CUBEMX中配置时钟树、选择系统调试方式后,STM32CUBEMX入门
    配置串口参数:异步,115200,8,NONE,1。

    2、编写阻塞式串口发送与接收代码

    生成代码后,在main.c文件-while死循环前添加代码:

    //阻塞式发送
    HAL_UART_Transmit(&huart1, (uint8_t *)"HELLO", 5, 20);
    //写在while死循环前只发送一次HELLO
    

    在main.c文件-while死循环中添加代码:

    //阻塞式接收
    uint8_t rev[5];
    HAL_UART_Receive(&huart1 ,rev, 3, 0xFFFF);//接收:串口1句柄、接收区首地址、接收的字节数、超时时间ms	
    HAL_UART_Transmit(&huart1, rev, 3, 0xFFFF);//接收到的数据,发送回去,验证是否发送成功
    		
    

    3、学习小技巧

    编写阻塞式串口发送代码在学习的过程中可以通过左侧Functions栏,找到所需函数。
    如需要串口发送函数:

    4、程序编译与下载

    编译成功后,连接好STLINK下载器,点击下载。

    5、程序验证

    连接USB转TTL模块,打开XCOM软件。

    程序验证成功!

    6、重定向printf和scanf

    为了简写,需要重定向printf和scanf。但是printf()这个函数是格式化输出到屏幕的,scanf()函数又是格式化输入(从屏幕),并不能输出到串口助手中,所以需要进行重定向。

  • 6.1、首先我们需要在keil集成开发环境中勾选STM32官方的微库(MicroLib是针对以C语言编写的基于ARM嵌入式应用程序的高度优化的库)。

  • 6.2、引用标准库的头文件

  • #include <stdio.h>//引用标准库的头文件,重定义printf,scanf
    
  • 6.3、添加重定向代码
  • //因为printf内部就是调用fputc进行单个字符输出,
    //所以重定向printf就将单个字符以串口的形式发送出去。
    int fputc(int c,FILE *f)//重定向printf
    {
       uint8_t ch[1]={c};
       HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
       return c;
    }
    int fgetc(FILE *stream)//重定向scanf
    {
       uint8_t ch[1];
       HAL_UART_Receive(&huart1, ch, 1, 0xFFFF);
       return ch[0];
    }
    

  • 6.4、使用printf和scanf函数实现阻塞式串口发送与接收
  • printf("ABC%d",123);//写在while死循环前,只发送一次ABC123
    
    int i=0;
    scanf("%d",&i);//用scanf接收数据,scanf通过回车判断结束的,所以接收的字节数要+2 \r\n 
    printf("%d\r\n",i);//接收到的数据,用printf发送回去,验证是否发送成功
    

  • 6.5、程序验证
  • 三、中断式

    0、串口中断式发送和接收概念

    串口中断发送:送数据的过程在中断中进行,这个中断实际上是发送数据寄存器空的中断。

    1、STM32CUBEMX配置更新

    在阻塞式STM32CUBEMX配置上进行更新,再重新生成代码,自己写的阻塞式代码删除,学习中断式:

    2、编写中断式串口发送与接收代码

        //main函数中,在while循环前
      	
    	//中断式发送
    	HAL_UART_Transmit_IT(&huart1, (uint8_t *)"HELLO", 5);//调用次函数,每发完一个字节就会进入一次中断,	在中断中,HAL库会自动触发下一个中断,直到发送完数据
    
    	//中断式接收
      //uint8_t rev[5];//不能定义局部变量,要定义全局变量,因有可能在接收到数据时,改局部变量已经被销毁了。
    	HAL_UART_Receive_IT(&huart1 ,rev, 3);//当接收完数据时,会直接进入接收回调函数,要在接收回调函数中写发送代码,实现与阻塞式一样的效果	
    
    //接收回调函数,写在main.c文件中
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	HAL_UART_Transmit_IT(&huart1, rev, 3);//接收到的数据,发送回去,验证是否发送成功
    	HAL_UART_Receive_IT(&huart1 ,rev, 3);//开启下一次接收中断
    }
    
    uint8_t rev[5];//全局变量
    


    3、程序验证

    四、DMA

    0、DMA概念

    DMA传输是将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

    1、STM32CUBEMX配置更新

    在中断式STM32CUBEMX配置上进行更新:

    USART1-DMA Setting-选择发送和接收,再重新生成代码:

    2、编写DMA式不定长串口发送与接收代码

    //在main.h中创建串口接收结构体
    #define RX_BUF_MAX_LEN 512
    typedef struct
    {
    	uint8_t rx_buff[RX_BUF_MAX_LEN];//缓存区
    	uint16_t len;		//接收长度
    }USRAT_RX;
    
    //在stm32xxxx_it.c中定义结构体变量
    /* USER CODE BEGIN PV */
    USRAT_RX usart1_rx;//首先定义一个结构体变量
    /* USER CODE END PV */
    
    //在stm32xxxx_it.c中找到串口1中断服务函数,编写不定长串口收发程序
    void USART1_IRQHandler(void)//串口1中断服务函数,当串口1产生任何中断都会进入此函数
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
     if(__HAL_UART_CLEAR_FLAG(&huart1,UART_FLAG_IDLE) != RESET)//如果空闲中断不为0 RESET,表示此时触发了空闲中断,收到一帧数据
     {
    		__HAL_UART_CLEAR_IDLEFLAG(&huart1); //清除空闲中断		
    		HAL_UART_DMAStop(&huart1);//停止串口1的DMA传输	 
    		usart1_rx.len = RX_BUF_MAX_LEN - __HAL_DMA_GET_COUNTER(huart1.hdmarx);	//计算接收到的数据长度:要接收到的字节数总数-剩余数=实际接收数
    				
    	  HAL_UART_Transmit_DMA(&huart1,usart1_rx.rx_buff,usart1_rx.len);
    	  HAL_UART_Receive_DMA(&huart1,usart1_rx.rx_buff,RX_BUF_MAX_LEN);	
    		
     }
      /* USER CODE END USART1_IRQn 0 */
      HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
      /* USER CODE END USART1_IRQn 1 */
    }
    
    //在main.c中声明结构体变量
    extern USRAT_RX usart1_rx;
    
    //在main.c中添加string.h头文件,编写串口结构体初始化函数
    #include <string.h>//使用memset函数,需引用此头文件
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    void Clear_Usart(USRAT_RX *usart_rx)		
    //初始化(清空)串口结构体
    {
    	memset((uint8_t*)usart_rx,0,sizeof(USRAT_RX)); 
    }
    /* USER CODE END 0 */
    
    //在main.c中while循环前
      /* USER CODE BEGIN 2 */
    	Clear_Usart(&usart1_rx); //初始化串口结构体
      __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);	//使能串口1的空闲中断,由于HAL库没有提供空闲函数的回调函数,所以要在串口中断服务函数中添加代码
      HAL_UART_Receive_DMA(&huart1,usart1_rx.rx_buff,RX_BUF_MAX_LEN);	//打开DMA传输模式
    
      /* USER CODE END 2 */
    

    3、程序验证


    参考文章

    嵌入式中的通讯协议——UART、I2C、SPI、DMA
    STM32F4_HAL库_串口阻塞/中断/DMA三种方式发送数据的配置
    【STM32】 DMA原理,步骤超细详解,一文看懂DMA
    STM32串口DMA模式发送接收数据并控制LED灯
    【STM32F4+CubeMX零基础快速入门】串口收发全攻略

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32串口通信实战教程

    发表回复