STM32H7 串口 空闲中断 硬件FIFO 任意长接收 Hal库 IDLE

STM32H7 串口 空闲中断 硬件FIFO 任意长接收 Hal库 IDLE

由于工作原因好久不接触ST的芯片了,所以断更ST的东西了,不过偶尔玩玩也挺好的。

接着上篇继续说串口的事儿,这次是FIFO,STM32H7的串口都是带硬件FIFO,大小是发送和接收各16个字节,其他新出的系列,例如H5的串口带8字节的硬件FIFO,这种情况下DMA咱就可以省略了,直接用FIFO就行。

使用之前呢,先要说明本次使用的环境以及库版本,编译环境是MDK5.27 ,hal库版本是1.11版本。这个很重要。

前两篇文章里边用的hal库版本比较老,缺失了很多。例如新版本已经增加了对idle的支持。并且修复了一些错误,例如硬件FIFO深度宏定义。

好,进入正题。

插个题外话,未经允许不得转载哦。

我们的目标呢,依旧是接收任意长数据。

我们拆解开来看,里边是有两个点需要注意,一个是IDLE,一个是硬件FIFO。

首先是IDLE,这个中断在hal库中定义为事件。我们在这里称为空闲中断。目前看这个中断是ST的芯片独有的,其他的芯片不一定有,可能有其他方式实现。空闲中断嘛,顾名思义就是串口在闲的时候发出中断,有闲就有忙,那这里的忙是什么?按照hal库里的实现,可以理解为,只要串口在收发数据的过程中就是忙,反之所谓的空闲就是没有在收发数据中。这个IDLE空闲中断特指的是串口在没有接收数据,注意啊,不关发送的事儿,因为串口本身就是异步全双工的,可以理解为发送是一个硬件,接收是一个硬件,两个是互相不干扰的。

再说说IDLE空闲中断发生的时机。我记得之前在103的手册中看到过,说是串口接收到最后一个字节后,在超过一个字节的时间里没有收到数据,会发生这个中断。当然H743里边也有单独实现超时机制(指定超时时间,硬件实现哦,题外话:soc中常用这个,soc的FIFO一般是64字节),不在本次实现验证说明中。这段主要就是想说IDLE发生在接收到最后一字节之后的一个字节时间之后且一次传输只发生一次。

然后是串口的硬件FIFO,F7 我没有看手册,就当作没有实现哈,就当作H7是ST公司首次在STM32系列MCU中加入硬件FIFO的功能。在不考虑DMA的情况下,我们通过串口接收接收数据,每收到一个数据就要发生一次中断。在cortexM3手册中是明确说明的,从触发中断到响应中断的第一条指令开始,是需要12个cpu周期的,cortexM7,我就懒得翻了,不知道有没有这个说明,STM32F103系列在72MHz主频下,中断频率最高是200Khz。说这些东西呢,是想让各位读者知道,中断对单片很重要,但不能滥用,要好好的规划才行,尤其是你的系统很复杂的时候, 这里用FIFO和IDLE的目的就是减少中断,还不影响实时性。

相关的知识点就先说这么多。

接下来就说怎么实现了,这次就不分析hal库了,大概前两篇的差不多。剩下的我就直接贴代码了。

这是C文件里的涉及到的宏定义以及全局变量。

#define BuffSize	128ul

UART_HandleTypeDef hUart1 = {0};

uint8_t RxData[2][BuffSize];

这段是uart1的初始化。

void Uart1_Init(void)
{
	hUart1.Instance = USART1;
	hUart1.Init.BaudRate = 115200;
	hUart1.Init.ClockPrescaler = UART_PRESCALER_DIV2;
	hUart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	hUart1.Init.Mode = UART_MODE_TX_RX;
	hUart1.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED;
	hUart1.Init.OverSampling = UART_OVERSAMPLING_8;
	hUart1.Init.Parity = UART_PARITY_NONE;
	hUart1.Init.StopBits = UART_STOPBITS_1;
	hUart1.Init.WordLength = UART_WORDLENGTH_8B;
	
	HAL_UART_Init(&hUart1);
	
	HAL_UARTEx_EnableFifoMode(&hUart1);
	HAL_UARTEx_SetTxFifoThreshold(&hUart1,UART_TXFIFO_THRESHOLD_8_8);
	HAL_UARTEx_SetRxFifoThreshold(&hUart1,UART_RXFIFO_THRESHOLD_8_8);
	
	__HAL_UART_ENABLE_IT(&hUart1,UART_IT_IDLE);
	HAL_NVIC_EnableIRQ(USART1_IRQn);
	HAL_NVIC_SetPriority(USART1_IRQn,14,0);
	HAL_UARTEx_ReceiveToIdle_IT(&hUart1,RxData[0],BuffSize);
}

这段是uart1引脚配置。

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	if(huart == &hUart1)//串口1
	{
		GPIO_InitTypeDef GPIO_InitStruct;
		
		GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull = GPIO_PULLUP;
		GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM;
		GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
		GPIO_InitStruct.Pin = GPIO_PIN_10 | GPIO_PIN_9;
		
		__HAL_RCC_GPIOA_CLK_ENABLE();
		__HAL_RCC_USART1_CLK_ENABLE();
		
		HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
	}
}

这是中断相关处理。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_UARTEx_RxEventCallback(huart,BuffSize);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart == &hUart1)
	{
		if(huart->pRxBuffPtr <= RxData[1])
		{
			HAL_UARTEx_ReceiveToIdle_IT(huart,RxData[1],BuffSize);
			Uart1_RxData_NotifityISR(RxData[0],Size);
		}
		else
		{
			HAL_UARTEx_ReceiveToIdle_IT(huart,RxData[0],BuffSize);
			Uart1_RxData_NotifityISR(RxData[1],Size);
		}
	}
}

void USART1_IRQHandler(void)
{
	HAL_UART_IRQHandler(&hUart1);
}

这是对外接口部分,也就收发数据接口。

__weak void Uart1_RxData_NotifityISR(uint8_t *pRxData,uint32_t Count)
{
}

uint32_t Uart1_TxData(uint8_t *pTxData,uint32_t Count)
{
	if(HAL_UART_GetState(&hUart1) & 0x01)return 1;
	HAL_UART_Transmit_IT(&hUart1,pTxData,Count);
	return 0;
}

c文件呢一共就这么多。

下边这段是h文件,是不是非常简洁。

#ifndef __Uart_H_
#define __Uart_H_

#include "stdint.h"

void Uart1_Init(void);
void Uart1_RxData_NotifityISR(uint8_t *pRxData,uint32_t Count);
uint32_t Uart1_TxData(uint8_t *pTxData,uint32_t Count);

#endif

下边这段是测试代码,功能是回显。

static uint8_t *pvRxData;
static volatile uint32_t RxCount = 0;

void Uart1Step(void)//Rate:10ms
{
	if(RxCount > 0)
	{
		if(Uart1_TxData(pvRxData,RxCount) == 0)RxCount = 0;
	}
}

void Uart1_RxData_NotifityISR(uint8_t *pRxData,uint32_t Count)
{
	pvRxData = pRxData;
	RxCount = Count;
}

测试结果,如图所示
在这里插入图片描述

好了,今天就这么多,有问题可以联系我,共同探讨,单片机疑难杂症就可以找我探讨哦。

在使用STM32HAL库时,可以通过以下步骤实现串口DMA空闲中断: 1. 配置串口DMA传输,使其使用循环模式和使用空闲中断。 ```c /* Configure the DMA handler for Transmission process */ hdma_tx.Instance = USARTx_TX_DMA_STREAM; hdma_tx.Init.Channel = USARTx_TX_DMA_CHANNEL; hdma_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; hdma_tx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_tx.Init.MemInc = DMA_MINC_ENABLE; hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_tx.Init.Mode = DMA_NORMAL; hdma_tx.Init.Priority = DMA_PRIORITY_LOW; hdma_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_tx.Init.MemBurst = DMA_MBURST_INC4; hdma_tx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_tx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmatx, hdma_tx); /* Configure the DMA handler for reception process */ hdma_rx.Instance = USARTx_RX_DMA_STREAM; hdma_rx.Init.Channel = USARTx_RX_DMA_CHANNEL; hdma_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_rx.Init.Mode = DMA_CIRCULAR; hdma_rx.Init.Priority = DMA_PRIORITY_HIGH; hdma_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; hdma_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdma_rx.Init.MemBurst = DMA_MBURST_INC4; hdma_rx.Init.PeriphBurst = DMA_PBURST_INC4; /* Initialize DMA handle */ if(HAL_DMA_Init(&hdma_rx) != HAL_OK) { Error_Handler(); } /* Associate the initialized DMA handle to the the UART handle */ __HAL_LINKDMA(huart, hdmarx, hdma_rx); /* Configure the DMA handler callbacks */ HAL_DMA_RegisterCallback(&hdma_tx, HAL_DMA_XFER_CPLT_CB_ID, DMA_TxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_CPLT_CB_ID, DMA_RxCpltCallback); HAL_DMA_RegisterCallback(&hdma_rx, HAL_DMA_XFER_ERROR_CB_ID, DMA_RxErrorCallback); /* Configure DMA transfer interrupts */ __HAL_DMA_ENABLE_IT(&hdma_rx, DMA_IT_TC); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_HT); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_FE); __HAL_DMA_DISABLE_IT(&hdma_rx, DMA_IT_TE); ``` 2. 实现DMA传输完成回调函数。 ```c void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { /* Transmission complete callback */ } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { /* Reception complete callback */ } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { /* UART error callback */ } ``` 3. 实现DMA空闲中断回调函数。 ```c void HAL_UART_IDLE_IRQHandler(UART_HandleTypeDef *huart) { /* Disable DMA transfer on reception */ hdma_rx.Instance->CR &= ~DMA_SxCR_EN; /* Clear IDLE flag */ __HAL_UART_CLEAR_IDLEFLAG(huart); /* Call user callback */ HAL_UART_RxCpltCallback(huart); /* Enable DMA transfer on reception */ hdma_rx.Instance->CR |= DMA_SxCR_EN; } ``` 4. 配置空闲中断使能。 ```c /* Enable the UART Idle Interrupt */ __HAL_UART_ENABLE_IT(&huart, UART_IT_IDLE); ``` 通过以上步骤,就可以实现串口DMA空闲中断
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值