STM32串口不定长度传输策略

2023-05-16

目录

一、接收中断+空闲中断

二、接收中断+外加定时器

三、接收中断+自定义结束符

四、串口DMA接收不定长数据


一、接收中断+空闲中断

        RXNE中断和IDLE中断的区别?当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。

(1)中断使能:

/* 使能接收中断*/
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* 使能空闲中断*/
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
USART_Cmd(USART1, ENABLE);

(2)串口中断服务:

uint8_t ch[20];
char i = 0;
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//接收到一个字节
    { 	
        //ch = USART1->DR;
    		ch[i++] = USART_ReceiveData(USART1);
    
    } 
    else if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)//接收到一帧数据
    {
    	USART1->SR;//先读SR
    	USART1->DR;//再读DR
    	i = 0;
    	printf("%s",ch);//只是测试,所以直接在中断里调用printf
    }
}

二、接收中断+外加定时器

        串口接收数据时启动定时器,每接收一帧数据要复位定时值以保证定时器不会溢出。

        定时器的超时值设置为1.5倍接收一个数据的时间

        超时的值设置要根据波特率调整,例如9600的波特率下,接收数据速率为9600bit/s = 1200B/s 也就是 0.83ms/B 那么我们的超时值可以设置为0.84*1.5 = 1.26ms 取个整1.3ms。

三、接收中断+自定义结束符

        这种也是很高效的,具体的做法是,在一帧数据尾部添加一个小尾巴 ~ ~

        这种方式缺点也是很明显的,即只适用于自定义的通讯格式,或者尾部结束符唯一确定的情况。

四、串口DMA接收不定长数据

        思考:我们可以使用DMA去接收数据,不过DMA需要定长才能产生接收中断,如何接收不定长的数据呢?

(1)DMA+串口空闲中断

        STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有两种方式解决:

        在重新开启接收DMA通道之前,将Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。

        建立双缓冲,重新配置DMA_MemoryBaseAddr的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。

串口配置:

/**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
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();
	
#if USE_USART_DMA_RX 
	// 开启 串口空闲IDEL 中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_IDLE, ENABLE);  
  // 开启串口DMA接收
	USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Rx, ENABLE); 
	/* 使能串口DMA */
	USARTx_DMA_Rx_Config();
#else
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
#endif

#if USE_USART_DMA_TX 
	// 开启串口DMA发送
//	USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE); 
	USARTx_DMA_Tx_Config();
#endif

	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

DMA配置:

#if USE_USART_DMA_RX 

static void USARTx_DMA_Rx_Config(void)
{
	DMA_InitTypeDef DMA_InitStructure;

	// 开启DMA时钟
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	// 设置DMA源地址:串口数据寄存器地址*/
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
	// 内存地址(要传输的变量的指针)
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Usart_Rx_Buf;
	// 方向:从内存到外设	
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	// 传输大小	
	DMA_InitStructure.DMA_BufferSize = USART_RX_BUFF_SIZE;
	// 外设地址不增	    
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	// 内存地址自增
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	// 外设数据单位	
	DMA_InitStructure.DMA_PeripheralDataSize = 
	DMA_PeripheralDataSize_Byte;
	// 内存数据单位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	 
	// DMA模式,一次或者循环模式
	//DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;	
	// 优先级:中	
	DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
	// 禁止内存到内存的传输
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	// 配置DMA通道		   
	DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);		
	// 清除DMA所有标志
	DMA_ClearFlag(DMA1_FLAG_TC5);
	DMA_ITConfig(USART_RX_DMA_CHANNEL, DMA_IT_TE, ENABLE);
	// 使能DMA
	DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE);
}
#endif

接收完成处理:

/*串口中断服务函数
*/ 
void DEBUG_USART_IRQHandler(void)
{
#if USE_USART_DMA_RX
	/* 使用串口DMA */
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE)!=RESET)
	{		
		/* 接收数据 */
		Receive_DataPack();
		// 清除空闲中断标志位
		USART_ReceiveData( DEBUG_USARTx );
	}	
#else
  /* 接收中断 */
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{		
        Receive_DataPack();
	}
#endif
}

(2)DMA接收+空闲中断(多字节接收)

中断配置:

__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能空闲中断
HAL_UART_Receive_DMA(&huart1, RxDMABuf_1, RXBUF_1_SIZE); //启动DMA接收

中断处理:

if(huart->Instance == USART1)
{
    //计算DMA接收的长度,也就是接收完一帧的长度
    DMA_Usart1_RxSize = RXBUF_1_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); 
    if(DMA_Usart1_RxSize > 0 )
    {
        memcpy(RxBuf_1, RxDMABuf_1, DMA_Usart1_RxSize);
        RxBufSize_1 = DMA_Usart1_RxSize;
        memset(RxDMABuf_1, 0, sizeof(RxDMABuf_1));
    }
    HAL_UART_Receive_DMA(&huart1, RxDMABuf_1, RXBUF_1_SIZE); //启动下一次接收
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32串口不定长度传输策略 的相关文章

  • 初始化 ST-Link 设备时出错 - 无法连接到设备

    我目前正在使用 ST Link 调试器对我的 STM32F3 Discovery 板进行编程 我使用的IDE是Atollic TrueStudio 5 5 2 现在我面临一个非常奇怪的问题 那就是我不断收到消息 初始化 ST Link 设备
  • 当数据大小较小时,内存到内存 DMA 传输是否需要权衡?

    我正在学习 STM32 F4 微控制器 我正在尝试找出使用 DMA 的限制 根据我的理解和研究 我知道如果数据量较小 即设备使用DMA生成或消耗少量数据 则开销会增加 因为DMA传输需要DMA控制器执行操作 从而不必要地增加系统成本 我做了
  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • 137-基于stm32单片机智能保温杯控制装置Proteus仿真+源程序

    资料编号 137 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DS18B20传感器 电机 制作一个基于stm32单片机智能保温杯控制装置Proteus仿真 2 通过DS18b20传感器检测当前保温杯水的温度 并且
  • HAL库学习

    CMSIS简介 CMSIS Cortex Microcontroller Software Interface Standard 微控制器软件接口标准 由ARM和其合作的芯片厂商 ST NXP 软件工具厂商 KEIL IAR 共同制定的标准
  • 毕业设计 江科大STM32的智能温室控制蓝牙声光报警APP系统设计

    基于STM32的智能温室控制蓝牙声光报警APP系统设计 1 项目简介 1 1 系统构成 1 2 系统功能 2 部分电路设计 2 1 stm32f103c8t6单片机最小系统电路设计 2 2 LCD1602液晶显示电路设计 2 2 风
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • 最终启动顺序错误 - STM32L476 的 Eclipse System Workbench 调试

    我正在尝试调试和运行 STM32L476 的简单汇编代码 我已经设置了 Eclipse Oxygen 在 Eclipse 中安装了最新版本的 System Workbench 插件并安装了 ST Link 驱动程序 IDE 成功构建了程序
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • STM32 暂停调试器时冻结外设

    当到达断点或用户暂停代码执行时 调试器可以停止 Cortex 中代码的执行 但是 当皮质停止在暂停状态下执行代码时 调试器是否会冻结其他外设 例如 DMA UART 和定时器 您只能保留时间 r 取决于外围设备 我在进入主函数时调用以下代码
  • 无法使用 OpenOCD 找到脚本文件

    我正在尝试按照本教程将 OpenOCD 与我的 ST 发现板一起使用 https japaric github io discovery README html https japaric github io discovery READM
  • 毕设开题分享 单片机智能教室系统(智能照明+人数统计)

    1 简介 Hi 大家好 今天向大家介绍一个学长做的单片机项目 单片机智能教室系统 智能照明 人数统计 大家可用于 课程设计 或 毕业设计 项目分享 https gitee com feifei1122 simulation project
  • CMSIS & STM32,如何开始? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 STM32 上使用 CMSIS 启动项目 网上一搜 没找到具体的教程 有些使用 SPL 开始项
  • STM32F4XX的12位ADC采集数值超过4096&右对齐模式设置失败

    文章目录 一 前言 二 问题1 数值超过4096 三 问题1的排错过程 四 问题2 右对齐模式设置失败 五 问题2的解决方法 5 1 将ADC ExternalTrigConv设置为0 5 2 使用ADC StructInit 函数 一 前
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • STM32 上的 ADC 单次转换

    我正在研究 STM32 F103x 上的 ADC 编程 并从最简单的情况 单次转换开始 测量内部温度传感器 连接到 ADC1 的值 并使用 USART 将其发送到 COM 端口 目标似乎很明确 但是当我尝试将源代码下载到闪存时 它不会向 C
  • PWM DMA 到整个 GPIO

    我有一个 STM32F4 我想对一个已与掩码进行 或 运算的 GPIO 端口进行 PWM 处理 所以 也许我们想要 PWM0b00100010一段时间为 200khz 但随后 10khz 后 我们现在想要 PWM0b00010001 然后
  • 在 Contiki 程序中使用 malloc

    考虑以下 Contiki 程序 include
  • STM32F0、ST-link v2、OpenOCD 0.9.0:打开失败

    我在用着发射台 http www ti com ww en launchpad about htmlgcc arm none eabi 4 9 2015q2 为 STM32F0 进行编译 现在我想使用该集合中的 arm none eabi
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015

随机推荐