• 【软件STM32cubeIDE下H73xx配置串口uart1+中断接收/DMA收发+HAL库+简单数据解析-基础样例】


    1、前言

    最近有段时间没有调试STM32了,想着H743调试串口1,也不能老师吃灰,也遇到了问题,即使最简单的功能,一些细节也是非常重要的,保持初学者之心,是非常重要的,同时也算做记录,对于初学者会有帮助,另外因为之前做过很多相关实验,遇到问题基本轻车熟路了,体现了记录的好处。

    2、实验器件

    软件环境:STM32cubeIDE 1.8.0
    硬件环境:STM32H743xx(正点原子阿波罗开发板)
    下载模块:ST-link下载器 (下载器)
    串口模块:串口转换器 (可用开发板上232模块代替)

    3-1、普通收发+中断接收实验

    第一步:代码调试-基本配置

    (1)基本配置

    新建stm32项目,先配置(下载口)和(时钟口),如下图,在这里插入图片描述在这里插入图片描述

    ###q (2)uart1以及DMA相关配置
    uart1配置稍微复杂点,但是配置串口多了,熟悉了,其实也就知道了,分为几个步骤。
    (0)先锁定自己需要引脚,记得F4默认是FA9,FA10,但是H7不是,我们想使用特定引脚的话,需要选择引脚,然后再选择uart1
    在这里插入图片描述

    (1)配置异步通信与开启中断使能,这个时候uart1锁定的引脚就会变成绿色,没选好之前,是黄色的。
    在这里插入图片描述
    这里需要注意的是,在自己实际操作生成代码时,发现并没有生成中断函数,所以最好在中断上再点下。
    在这里插入图片描述

    (2)配置DMA,这部分直接添加就好,都是默认的也不需要改什么。
    在这里插入图片描述

    (3)配置串口的波特率相关设置,改成你需要的波特率,一般也只改波特率,其它不用动。

    在这里插入图片描述

    (3)时钟配置

    也就是外部时钟配置,之前F4用的是168M,F1是72M,H7一般是480M,配置如下。
    在这里插入图片描述

    (4)保存后,就可以生成代码

    建议先保存,然后生成代码,否则直接生成代码,可能导致软件崩掉,之前遇到过。
    在这里插入图片描述在这里插入图片描述

    第二步:加入prinf打印

    (1)不重复造轮子:打印相关连接

    具体打印方式,之前就写过,所以不要重复造轮子,之前看文章,将代码复制过来。
    文章连接:# 关于软件stm32cubeIDE下配置printf重定向无法输出问题-解决方式之一

    (2)加入代码片段,注意第四步要放到main里运行

    另外注意自己测试发现,不能使用如下定义的方式。

    //>>第三步:定义输出函数printf
    #define printf(...)  HAL_UART_Transmit_DMA((UART_HandleTypeDef * )&huart3, (uint8_t *)u_buf,\
    											sprintf((char *)u_buf,__VA_ARGS__));
    
    • 1
    • 2
    • 3

    需要使用以下方式,并且需要加入换行符号 \n

    printf(“\r\n star \r\n\r\n”); //起来之后输出一条语句
    printf(“\r\n star \r\n\r\n”); //起来之后输出一条语句
    printf(“\r\n star \r\n\r\n”); //起来之后输出一条语句

    
    //>>第一步:添加库
    #include "string.h"
    #include "stdint.h"
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    
    /* USER CODE END Includes */
    //>>第二步:定义数组
    uint8_t u_buf[64];
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    //>>第三步:定义输出函数printf
    #ifdef __GNUC__
    #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
    #else
    #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
    #endif
    //STM32cubeIDE下
    PUTCHAR_PROTOTYPE
    {
        HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1,0x200);
        return ch;
    }
    
    //keil下 //没在keil下尝试过 一直使用软件stm32cubeIDE没有取keil验证
    //UART_HandleTypeDef huart1; //UART句柄
    int fputc(int ch,FILE *f)
    {
    	HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,0xffff);
    	return ch;
    }
    
    
    /* USER CODE END PTD */
    
    //>>第四步:打印输出
    	  	   printf("\r\n star \r\n\r\n");  //起来之后输出一条语句
    		  HAL_Delay(1000);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    (3)代码测试

    随便输出些什么,进行打印测试,在网上找个串口上位机工具,然后连接后串口转USB工具,对应引脚,就可以进行测了。
    第四步代码放在主函数,这里没有截图上位机串口数据,打印可以放置的位置指示下,下图已经做完实验的一个截图,开始中断时还没有加入printf,这点请熟知,有疑问可以看代码或评论。
    在这里插入图片描述

    第三步:加入接收代码

    (1)加入初始化配置

    具体代码见下边连接,这里放置图片展示代码内容。
    在这里插入图片描述

    (2)加入回调函数部分

    触发发送与接收,都会回调函数,我们这次就简单些,收到什么就发送什么。

    
    uint8_t aRxBuffer[1];
    void UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
      static uint32_t rxIndex = 0;
    
      if(huart->Instance == USART1)
          {
    
    	  HAL_UART_Transmit(&huart1, (uint8_t *)aRxBuffer, 1,0xFFFF);
    	  HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, 1);
    
          }
    
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    (3)加入到中断内

    这点还是挺重要,在中断函数内加入回调函数,不加入相应功能无法实现。
    在这里插入图片描述

    第四步:实验验证

    (1)发送
    在这里插入图片描述
    (2)中断接收

    在这里插入图片描述

    3-2、DMA收发实验

    因为上述步骤,相关配置已经完成了,我们不需要,再重新配置,直接使用即可。

    第一步:加入DMA初始化

    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//开启空闲中断
      /*.虽然我们使用的CubeMx来配置DMA,
       * 但只是配置DMA模式为串口到内存,
       * 所以还需要在程序中进一步指定:
       * DMA具体搬运到内存的哪一个位置中,
       * 我们建立一个数组用以存放DMA搬运的串口数据,
       * 并使用HAL_UART_Receive_DMA()函数来配置*/
      HAL_UART_Receive_DMA(&huart1, (uint8_t*)UART1_ReceBuf, UART1_Buf_LEN);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在这里插入图片描述

    第二步:加入DMA相关数组

    /* USER CODE BEGIN PTD */
    //>>第二步:定义数组
    uint8_t u_buf[64];
    #define UART1_Buf_LEN 100
     uint8_t UART1_SendBuf[UART1_Buf_LEN];
     uint8_t UART1_ReceBuf[UART1_Buf_LEN];
    /* USER CODE END PTD */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    第三步:加入回调函数

    /* USER CODE BEGIN PFP */
    
    uint8_t aRxBuffer[1];
    //void UART_RxCpltCallback(UART_HandleTypeDef *huart)
    //{
    //  static uint32_t rxIndex = 0;
    //
    //  if(huart->Instance == USART1)
    //      {
    //
    //	  HAL_UART_Transmit(&huart1, (uint8_t *)aRxBuffer, 1,0xFFFF);
    //	  HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, 1);
    //
    //      }
    //
    //
    //
    //}
    void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)//发送完成回调函数
    {
    
    }
    
    //采用DMA空闲串口中断模式  貌似不会回调到这里
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//接收完成回调函数
    {
    
    }
    /* USER CODE END 0 */
    
    uint8_t UART1_ReceBuf_deal_with_data[5];
    void USAR_UART_IDLECallback(UART_HandleTypeDef *huart)
    {
    
    //(1)
     HAL_UART_DMAStop(&huart1);                    //停止本次DMA传输 需要使用DMA发送则需要关掉  如果使用的阻塞方式HAL_UART_Transmit发送请使用本行
    HAL_UART_Transmit(&huart1,(uint8_t*)UART1_ReceBuf,3,0x200);//阻塞方式发送 使用时需要关闭DMA模式 阻塞发送没有问题,收啥 发啥
    //数据头正确了再赋值
    if(UART1_ReceBuf[0]==0x31 &&  UART1_ReceBuf[1]==0x32)
    {
    	//		目标							源地址	    	数据量
    	memcpy(&UART1_ReceBuf_deal_with_data,&UART1_ReceBuf,3);
    
    
    }
    
    memset(UART1_ReceBuf,0,UART1_Buf_LEN);                                            //清零接收缓冲区
      //  data_length = 0;
    HAL_UART_Receive_DMA(&huart1, (uint8_t*)UART1_ReceBuf, UART1_Buf_LEN);                    //重启开始DMA传输 每次255字节数据
    
    }
    
    
    void USER_UART_IRQHandler(UART_HandleTypeDef *huart)
    {
        if(  huart->Instance == USART1)
        {
    
    
        	  if(RESET != __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE))   //判断是否是空闲中断
        	         {
        	             __HAL_UART_CLEAR_IDLEFLAG(&huart1);                     //清楚空闲中断标志(否则会一直不断进入中断)
        	             USAR_UART_IDLECallback(huart);                          //调用中断处理函数
        	         }
        }
    }
    
    /* USER CODE END PFP */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    第四步:加入到中断函数内

    void USART1_IRQHandler(void)
    {
      /* USER CODE BEGIN USART1_IRQn 0 */
    
      /* USER CODE END USART1_IRQn 0 */
      HAL_UART_IRQHandler(&huart1);
      /* USER CODE BEGIN USART1_IRQn 1 */
      USER_UART_IRQHandler(&huart1);
      /* USER CODE END USART1_IRQn 1 */
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    第四步:简单数据解析功能

    (1)加入接收部分代码

    我们收到什么数据,就发出什么数据,因为在回调函数里,不能再用DMA发送了,但可以堵塞发出。

    在这里插入图片描述

    (2)加入处理部分代码

    我们将数据收到后,如果数据对了,我们只是将数据复制一份,处理不在回调内做,以免拖慢回调。
    在这里插入图片描述

    (3)测试代码

    这里这里发放出成功接收并解析代码,处理后发送出来,就是如果接收到数据“123”,那么返回数据“ABC”。
    在这里插入图片描述

    4、代码连接

    (1)中断接收

    代码链接:https://download.csdn.net/download/qq_22146161/88500425

    (2)DMA收发

    代码链接:https://download.csdn.net/download/qq_22146161/88500428

    5、注意细节

    (1)总是多个字符,或者多个其它字符。

    实际测试中发现,总会多个0x0D数字,不知道哪里来的,多次检查后,发现其实之前有发送,因为进入空闲中断后,停止了造成这种奇特现象。
    在这里插入图片描述
    原因点如下:发现有奇怪数据时候,一定看看有没其它地方输出。
    在这里插入图片描述

    (2)复制函数的使用。

    调试解析时候,我甚至将收到数据打印出来了,但是就是无法触发发送,才发现复制函数数组赋值那块弄错了,还是需要注意的。
    在这里插入图片描述

    (3)逻辑调试与测试。

    在我们不清楚的情况,或者要测试逻辑时候,可以让其将数据打印出来了,帮助我们定位错的原因。如下图,我们不知道收到什么是数据时候,可以让其直接发回来,帮助我们判断,甚至,复制完数据后,成没成功,也可以打印出来。

    在这里插入图片描述

    (4)十进制的1与十六进制0x31。

    我这里代码有时候说“123”,有时候又说0x31,0x32,0x33,其实这是他们进制表示不同,一个是十进制,一个是十六进制,比如,十进制的1等同于十六进制0x31,跟ascii码对照表有关系。

    (5)不同串口工具,有些信息不显示。

    不同上位机串口工具,在使用的时候有差异的,如果不切换到16进制进行显示,我也不会发现十六进制的0x0D,后来发现这个其实是换行符‘\n’。
    在这里插入图片描述

    (6)数据解析注意

    开始自己看之前的文章的时候,没主要,就发了数据“12”,发现不回,检查后,才知道,数据为12的时候,只复制,不在while进行发送,这点给忘了。
    在这里插入图片描述
    在这里插入图片描述
    如下错误,为只输入了“12”
    在这里插入图片描述

    (7)初始化顺序不对,不运行

    需要将DMA初始化,放在uart前在这里插入图片描述

    (8)回调函数写的有问题,只返回一个字符

    在这里插入图片描述

    (9)在网上找到样例,进行测试,没有成功

    在这里插入图片描述

    总结

    再次调试相同东西,再简单东西,也有细节值得记录吧~~~。

  • 相关阅读:
    单列模式的深度刨析以及反射破坏单列机制
    typeScript--[类的实例方法与静态方法]
    Redis - Python 客户端基本使用指南
    手写SVG图片
    1.Type类型详解
    基于Android的仓库管理系统APP设计与实现
    RT-Thread 组件学习
    TCP通讯CS模式之C#设计笔记(十八)
    投稿玄学之SCI给了大修,还会拒稿吗?
    Python数据攻略-Pandas常用数据操作与数据清洗
  • 原文地址:https://blog.csdn.net/qq_22146161/article/details/134210091