• STM32F103VET6基于STM32CubeMX创建串口中断+ DMA 不定长数据接收


    STM32F103VET6基于STM32CubeMX创建串口中断+ DMA 不定长数据接收


    📓DMA参数介绍

    • 🎉DMA传输方式
      在这里插入图片描述
    • 🌿传输方向; 外设到内存;内存到外设;内存到内存; 外设到外设
    • 🌿DMA_Mode_Normal,正常模式,

    🍁当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次

    • 🌿DMA_Mode_Circular ,循环传输模式

    🍁当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式.

    • 🌿Data Width :DMA内存地址自增,每次增加一个Byte(字节)

    ⛳注意事项

    • 🚩本例程的串口1中断服务函数不是由STM32CubeMX自动配置生成的,而是定义在main.c文件中,默认是勾选上的,如果没有取消勾选,生成代码后,会与stm32f1xx_it.c中生成相关的中断服务函数会与main.c中的USART1_IRQHandler函数重复冲突,导致编译报错。
      在这里插入图片描述

    🌼主程序main.c代码完善

    • 📜在main.c中添加下面的代码
    #define UART_RX_LEN 1024           // 一次最大接收的数据量
    uint8_t UART_RX_BUF[UART_RX_LEN];  // DMA数据接收缓存
    __IO uint16_t UART_RX_STA = 0;     // 第15bit表示一帧数据接收完成,第14~0位表示接收到的数据量
    
    void USART1_IRQHandler(void)
    {//串口中断函数
    	if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET)  // 空闲中断标记被置位
    	{
    	    __HAL_UART_CLEAR_IDLEFLAG(&huart1);  // 清除中断标记
    	    HAL_UART_DMAStop(&huart1);           // 停止DMA接收
    	    UART_RX_STA = UART_RX_LEN - __HAL_DMA_GET_COUNTER(huart1.hdmarx);  // 总数据量减去未接收到的数据量为已经接收到的数据量
    	    UART_RX_BUF[UART_RX_STA] = 0;  // 添加结束符
    	    UART_RX_STA |= 0X8000;         // 标记接收结束
    	    HAL_UART_Receive_DMA(&huart1, UART_RX_BUF, UART_RX_LEN);  // 重新启动DMA接收
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 🔖在while(1)中添加下面的代码:
    		if(UART_RX_STA & 0X8000)
    {
    	HAL_UART_Transmit(&huart1, UART_RX_BUF, UART_RX_STA & 0X7FFF, 100);    // 将接收到的数据发送回去
    	UART_RX_STA = 0;  // 清除标记
    }
     printf("STM32F103CET6");
    	HAL_Delay(1000);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    📝printf重定向相关代码

    • 📑在usart.c文件中添加下面的代码,并在target设置中勾选Use Micro Lib选项,,并在调用printf函数的地方包含stdio.h头文件。
    #include 
    int fputc(int ch,FILE *f)
    {
        uint32_t temp = ch;
     
        HAL_UART_Transmit(&huart1,(uint8_t *)&temp,1,0xFFFF);        //huart1是串口的句柄
        HAL_Delay(2);
     
        return ch;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    📒其他打印封装函数参考:

    #include 
    #include "usart.h"
    #include 
    #include 
    //使用printf()发送数据,需要对printf函数进行重定向,且只能使用USART1。
    // 重定向fputc函数,使用printf()发送数据
    int fputc(int ch, FILE *f)
    {
    	// 参数1:串口句柄,参数2:要发送的数据;参数3:要发生数据的长度;参数4:超时等待时间
    	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
    	return ch;
    }
    //DMA发送:本项目中使用的方法
    // DMA模式
    void log_DMA(const char *format, ...)
    {
    	va_list args;			// 定义参数列表变量
    	va_start(args, format); // 从format位置开始接收参数表,放在arg里面
    
    	char strBuf[256];				// 定义输出的字符串
    	vsprintf(strBuf, format, args); // 使用vsprintf将格式化的数据写入缓冲区
    	va_end(args);					// 结束可变参数的使用
    
    	// 等待上次的数据发送完成,避免新的数据覆盖正在传输的数据,导致混乱
    	while (HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX)
    	{
    		// Wait for DMA transfer to complete
    	}
    
    	HAL_UART_Transmit_DMA(&huart1, (uint8_t *)strBuf, strlen(strBuf));
    }
    
    
    //中断式发送:
    // 中断模式
    void log_IT(const char *format, ...)
    {
    	va_list args;			// 定义参数列表变量
    	va_start(args, format); // 从format位置开始接收参数表,放在arg里面
    
    	char strBuf[256];				// 定义输出的字符串
    	vsprintf(strBuf, format, args); // 使用vsprintf将格式化的数据写入缓冲区
    	va_end(args);					// 结束可变参数的使用
    
    	// 等待上次的数据发送完成,避免新的数据覆盖正在传输的数据,导致混乱
    	while (HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX)
    	{
    		// Wait for transfer to complete
    	}
    
    	HAL_UART_Transmit_IT(&huart1, (uint8_t *)strBuf, strlen(strBuf));
    }
    
    //使用sprintf()函数,阻塞式发送:
    // 堵塞模式
    void log(const char *format, ...)
    {
    	va_list args;			// 定义参数列表变量
    	va_start(args, format); // 从format位置开始接收参数表,放在arg里面
    
    	char strBuf[256];				// 定义输出的字符串
    	vsprintf(strBuf, format, args); // 使用vsprintf将格式化的数据写入缓冲区
    	va_end(args);					// 结束可变参数的使用
    	HAL_UART_Transmit(&huart1, (uint8_t *)strBuf, strlen(strBuf), HAL_MAX_DELAY);
    }
    
    
    • 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

    📚工程源码

    链接:https://pan.baidu.com/s/1O1CnA0eMttLLXWZPpUWJEQ 
    提取码:ud7l
    
    • 1
    • 2
    • 🌿DMA接收+空闲中断例程
    链接:https://pan.baidu.com/s/1qOtfw21j0pQmAhJoVUe-cw?pwd=pb23 
    提取码:pb23
    
    • 1
    • 2
    • 🌿DMA接收+空闲中断+DMA发送例程
    链接:https://pan.baidu.com/s/1TaeL2FCV-uqDv6xbXrnvhw?pwd=vtrk 
    提取码:vtrk
    
    • 1
    • 2
  • 相关阅读:
    CMT2380F32模块开发17-ADC例程
    详细解析张雪峰老师对计算机专业的评价“进可攻,退可守”--【职场篇】
    [RoarCTF 2019]Easy Calc
    【JUC系列】同步工具类之ThreadLocal
    Schrodinger Shape Screen 工具使用方法
    「容器管理系统」 1. 开篇:框架选型和环境搭建
    实战教程:从下载到成功安装,VMware与CentOS 7的完美结合
    嵌入式IDE之修改MDK主题(暗黑主题)
    密码学基础——加密与哈希,凯撒密码,对称加密与非对称加密以及Diffie-Hellman密码交换协议
    Python 标准库之pathlib,路径操作
  • 原文地址:https://blog.csdn.net/weixin_42880082/article/details/127672670