串口IAP升级DAY1:串口发送接收知识

该代码示例展示了如何在STM32上使用串口进行数据实时处理,通过定时器配合串口中断判断接收完成,并使用普通、DMA和中断三种方式发送数据。关键在于根据波特率计算接收字节时间,通过定时器检测数据帧结束。代码中包含了GPIO配置、串口初始化、中断设置、DMA配置以及定时器初始化等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

串口数据实时处理:定时器+串口 判断串口数据接收完成
串口三种发送(Tx)方式:
普通、DMA、中断
核心思想
根据波特率来计算接收一个字节所需要的时间,当超过这个时间没有收到数据,则表明这一帧数据已经接受完毕

实现方法
串口中断函数接收第一个字节之后,开启定时器计数。接受下一个字节的时候清空定时器计数。如此,当没有数据接收后,计时器无法清零,当计时器计数超过设定的数值之后,触发定时器溢出中断,此时数据即接收完毕
在这里插入图片描述
在这里插入图片描述
下面是代码:

1、为了方便,定义宏和发送接收数组,主要内容如下所示:

	#define USE_USART1
	#ifdef USE_USART1
	#define IAP_USART						USART1
	#define	RCC_IAP_UART_CLK_ENABLE()		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)
	#define IAP_BAUDRATE					(115200)
	#define IAP_USART_IRQn					USART1_IRQn
	#define IAP_USART_IRQHandler			USART1_IRQHandler
	
	#define IAP_USART_IRQ_PREP				(1)
	#define IAP_USART_IRQ_SUBP				(3)
			
	#define IAP_RX_TIM						TIM2
	#define	RCC_IAP_RX_TIM_CLK_ENABLE()		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE)
	#define IAP_RX_TIM_IRQn					TIM2_IRQn
	#define IAP_RX_TIM_IRQHandler			TIM2_IRQHandler

	#define IAP_RX_TIM_IRQ_PREP				(1)
	#define IAP_RX_TIM_IRQ_SUBP				(3)
		
	/* 对应0.5ms 
	** 算法(BACK_RX_TIMER_PRES+1)*(BACK_RX_TIMER_PERI+1)/84000 (ms)
	*/
	#define IAP_RX_TIMER_PRES				(83)
	#define IAP_RX_TIMER_PERI				(499)
	#define IAP_RX_TIMER_DIV				TIM_CKD_DIV1

	/* DMA参数宏定义 */
	#define IAP_DMA_CLK_ENABLE()			RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE)
	#define IAP_TX_DMA_STREAM				DMA2_Stream7
	
	#define IAP_TX_DMA_FLAG_TCIF			DMA_FLAG_TCIF7
	#define IAP_TX_DMA_FLAG_FEIF			DMA_FLAG_FEIF7
	#define IAP_TX_DMA_FLAG_DMEIF			DMA_FLAG_DMEIF7
	#define IAP_TX_DMA_FLAG_TEIF			DMA_FLAG_TEIF7
	#define IAP_TX_DMA_FLAG_HTIF			DMA_FLAG_HTIF7
			
	#define IAP_TX_DMA_IT_TCIF				DMA_IT_TCIF7	
	#define IAP_TX_DMA_IT_TEIF				DMA_IT_TEIF7
	
	#define IAP_TX_DMA_CHANNEL				DMA_Channel_4
	#define IAP_DMA_TX_IRQn					DMA2_Stream7_IRQn
	#define IAP_DMA_TX_IRQHandler			DMA2_Stream7_IRQHandler
	
	#define IAP_UARTDMA_IRQ_PREP			(1)
	#define IAP_UARTDMA_IRQ_SUBP			(3)
	
#endif

/* 外部定义引用 */
extern struct rt_semaphore IapRx_OPSem;

uint8_t IAP_TX_BUF[IAP_BUFF_LEN] = {0};
__IO uint8_t IAP_RX_BUF[IAP_BUFF_LEN] = {0};     /* 接收缓冲,最大IAP_BUFF_LEN个字节*/
__IO uint16_t IAP_Rxoffset =0;

2、串口GPIO口、配置初始化、接收中断、发送DMA、定时器接收的配置:
GPIO口配置

/**
* @brief UART MSP Initialization
* This function configures the hardware resources used in this example
* @param None
* @retval None
*/
void RT_UART_MspInit(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	/* Peripheral clock enable */
	RCC_AHB1PeriphClockCmd(Debug_U1_TX_CLK, ENABLE);	

	/**USART1 GPIO Configuration    
	PA9     ------> USART1_TX
	PA10    ------> USART1_RX 
	*/
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_PuPd	= GPIO_PuPd_NOPULL;
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_100MHz;
	GPIO_InitStruct.GPIO_Pin	= Debug_U1_TX_Pin | Debug_U1_RX_Pin;
	GPIO_Init(Debug_U1_TX_GPIO_Port, &GPIO_InitStruct);
		
	GPIO_PinAFConfig(Debug_U1_TX_GPIO_Port, Debug_U1_TX_PinSource, GPIO_AF_USART1);
	GPIO_PinAFConfig(Debug_U1_RX_GPIO_Port, Debug_U1_RX_PinSource, GPIO_AF_USART1);
}

配置初始化、接收中断、发送DMA、定时器接收的配置:

/**
* @brief BSP层 IAP串口配置初始化
* @param None
* @retval None
*/
static void BSP_IapUartCFG_Init(void)
{
	USART_InitTypeDef USART_InitStruct = {0};
	/* RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE) */
	RCC_IAP_UART_CLK_ENABLE();
	
	USART_InitStruct.USART_BaudRate				= IAP_BAUDRATE;
	USART_InitStruct.USART_WordLength			= USART_WordLength_8b;	
	USART_InitStruct.USART_StopBits				= USART_StopBits_1;
	USART_InitStruct.USART_Parity				= USART_Parity_No;
	USART_InitStruct.USART_HardwareFlowControl	= USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode					= USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(IAP_USART, &USART_InitStruct);
	
	USART_Cmd(IAP_USART,ENABLE);				/* 该函数会使能串口 */
}

/**
* @brief BSP层 IAP串口中断的初始化
* @param None
* @retval None
*/
static void BSP_IapUartNVIC_Init(void)
{
	NVIC_InitTypeDef NVIC_InitStruct = {0};
	
	
	USART_ITConfig(IAP_USART,USART_IT_RXNE,ENABLE);	/* 开启接收中断	*/
	
	/* 使能串口中断 */
	NVIC_InitStruct.NVIC_IRQChannel						= IAP_USART_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= IAP_USART_IRQ_PREP;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= IAP_USART_IRQ_SUBP;
	
	NVIC_Init(&NVIC_InitStruct); /* 定义串口中断优先级 */
}

/**
* @brief BSP层 IAP串口DMA的初始化
* @param None
* @retval None
*/
static void BSP_IapUartDMA_Init(void)
{
	DMA_InitTypeDef DMA_InitStruct = {0};
	NVIC_InitTypeDef NVIC_InitStruct = {0};
	
	/* 使能DMA时钟 */
	IAP_DMA_CLK_ENABLE(); 
	DMA_DeInit(IAP_TX_DMA_STREAM);
	
	DMA_InitStruct.DMA_PeripheralBaseAddr	= (uint32_t)(&(IAP_USART->DR));	/* 外设基地址 */
	DMA_InitStruct.DMA_Memory0BaseAddr		= (uint32_t)(IAP_TX_BUF);		/* 内存基地址 */
	DMA_InitStruct.DMA_PeripheralInc		= DMA_PeripheralInc_Disable;	/* 外设非增量模式 */
	DMA_InitStruct.DMA_MemoryInc			= DMA_MemoryInc_Enable;			/* 存储器增量模式 */
	DMA_InitStruct.DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Byte;	/* 外设数据长度:8位 */
	DMA_InitStruct.DMA_MemoryDataSize		= DMA_MemoryDataSize_Byte;		/* 存储器数据长度:8位 */
	DMA_InitStruct.DMA_Mode					= DMA_Mode_Normal;				/* 外设流控模式 */
	DMA_InitStruct.DMA_Priority				= DMA_Priority_VeryHigh;		/* 优先级 */
	DMA_InitStruct.DMA_FIFOMode				= DMA_FIFOMode_Disable;
	DMA_InitStruct.DMA_FIFOThreshold		= DMA_FIFOThreshold_Full;
	DMA_InitStruct.DMA_MemoryBurst			= DMA_MemoryBurst_Single;		/* 存储器突发单次传输 */
	DMA_InitStruct.DMA_PeripheralBurst		= DMA_PeripheralBurst_Single;	/* 外设突发单次传输 */
	
	DMA_InitStruct.DMA_Channel				= IAP_TX_DMA_CHANNEL;			/* 通道选择 */
	DMA_InitStruct.DMA_DIR					= DMA_DIR_MemoryToPeripheral;	/* 存储器到外设 */
	DMA_InitStruct.DMA_BufferSize			= IAP_BUFF_LEN;

	/* 初始化DMA */
	DMA_Init(IAP_TX_DMA_STREAM, &DMA_InitStruct);	
	/* 清除DMA2_Steam7传输完成标志 */
	DMA_ClearFlag(IAP_TX_DMA_STREAM,IAP_TX_DMA_FLAG_TCIF);	
	DMA_ITConfig(IAP_TX_DMA_STREAM, DMA_IT_TC, ENABLE);

	/* 配置DMA发送中断 */
	NVIC_InitStruct.NVIC_IRQChannel						= IAP_DMA_TX_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= IAP_UARTDMA_IRQ_PREP;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= IAP_UARTDMA_IRQ_SUBP;
	
	NVIC_Init(&NVIC_InitStruct);
}

/**
* @brief BSP层 IAP串口定时器的初始化
* @param None
* @retval None
*/
static void BSP_IapUartTIM_Init(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};
	NVIC_InitTypeDef NVIC_InitStruct = {0};

	/* 接收用的定时器 */
	RCC_IAP_RX_TIM_CLK_ENABLE();
	TIM_DeInit( IAP_RX_TIM );/* 复位RX定时器 */
	
	TIM_TimeBaseInitStruct.TIM_Period				= IAP_RX_TIMER_PERI;
	TIM_TimeBaseInitStruct.TIM_Prescaler			= IAP_RX_TIMER_PRES;
	TIM_TimeBaseInitStruct.TIM_ClockDivision		= IAP_RX_TIMER_DIV;
	TIM_TimeBaseInitStruct.TIM_CounterMode			= TIM_CounterMode_Up;	
	TIM_TimeBaseInit(IAP_RX_TIM, &TIM_TimeBaseInitStruct);	
	TIM_ITConfig(IAP_RX_TIM, TIM_IT_Update, ENABLE);

	/* 配置定时器中断 */
	NVIC_InitStruct.NVIC_IRQChannel						= IAP_RX_TIM_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= IAP_RX_TIM_IRQ_PREP;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= IAP_RX_TIM_IRQ_SUBP;
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_ClearFlag(IAP_RX_TIM, TIM_FLAG_Update);
	TIM_Cmd(IAP_RX_TIM,DISABLE);	
}


/**
* @brief BSP层 IAP串口的初始化
* 包括其接收中断、配置、发送DMA、接收定时器的初始化
* @param None
* @retval None
*/
void BSP_IAP_Uart_Init(void)
{
	/* 配置初始化 */
	BSP_IapUartCFG_Init();
	/* 中断初始化 */
	BSP_IapUartNVIC_Init();
	
	/* DMA初始化 */	
	BSP_IapUartDMA_Init();
	/* 定时器初始化 */
	BSP_IapUartTIM_Init();		
}

3、串口发送DMA、串口接收中断处理函数、串口接收定时器的中断处理函数、串口DMA发送的中断处理函数

/**
* @brief IAP串口发送DMA
* @param None
* @retval None
*/
void BSP_IAP_USART_Transmit_DMA(uint16_t Size)
{
	if ((Size > IAP_BUFF_LEN) || (Size == 0))
	{ return; }

	/* Disable the USART TX DMA request */
	USART_DMACmd(IAP_USART, USART_DMAReq_Tx, DISABLE);
	/* Disable the DMA  TX Streams */
	DMA_Cmd(IAP_TX_DMA_STREAM, DISABLE);
	/* Clear all falgs of DMA TX Streams */
	DMA_ClearFlag(IAP_TX_DMA_STREAM,
		           (IAP_TX_DMA_FLAG_FEIF | IAP_TX_DMA_FLAG_DMEIF | IAP_TX_DMA_FLAG_TEIF |  
		            IAP_TX_DMA_FLAG_HTIF | IAP_TX_DMA_FLAG_TCIF )
		           );
	/* Set the DMA TX Stream lenth */
	IAP_TX_DMA_STREAM->NDTR = Size;
	
	/* Enable the DMA  TX Streams */
	DMA_Cmd(IAP_TX_DMA_STREAM, ENABLE);
	/* Enable the USART TX DMA request */
	USART_DMACmd(IAP_USART, USART_DMAReq_Tx, ENABLE);
}

/**
* @brief IAP串口接收中断处理函数
* @param None
* @retval None
*/
void IAP_USART_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
	
	if(USART_GetITStatus(IAP_USART,USART_IT_RXNE) == SET)
	{
		/* 清除中断标志 */
		USART_ClearITPendingBit(IAP_USART,USART_IT_RXNE);	
		/* 新方案 待校验*/
		IAP_RX_BUF[IAP_Rxoffset%IAP_BUFF_LEN] = (u8)(USART_ReceiveData(IAP_USART) & 0xFF);
		IAP_Rxoffset++;
		
		if (IAP_Rxoffset >= IAP_BUFF_LEN)	IAP_Rxoffset = 0;
		
		/* 清零计数器, 并启动定时器 */
		TIM_SetCounter(IAP_RX_TIM, 0);
		TIM_Cmd(IAP_RX_TIM,ENABLE);
	}
	
	if (USART_GetITStatus(IAP_USART, USART_IT_ORE_RX) == SET)
	{
		USART_ClearITPendingBit(IAP_USART,USART_IT_ORE_RX);	
		USART_ReceiveData(IAP_USART); 
	}
	
    /* leave interrupt */
    rt_interrupt_leave();
}

/**
* @brief IAP串口接收定时器的中断处理函数
* @param None
* @retval None
*/
void IAP_RX_TIM_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    if (TIM_GetITStatus(IAP_RX_TIM, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(IAP_RX_TIM, TIM_IT_Update);
		
        TIM_SetCounter(IAP_RX_TIM, 0);
        /* TIM  enable counter */
        TIM_Cmd(IAP_RX_TIM, DISABLE);
			
		rt_sem_release(&IapRx_OPSem);
    }	
	
    /* leave interrupt */
    rt_interrupt_leave();	
}


/**
* @brief IAP串口DMA发送的中断处理函数
* @param None
* @retval None
*/
void IAP_DMA_TX_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
	
	if (DMA_GetITStatus(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TEIF))
	{
		DMA_ClearITPendingBit(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TEIF);

		DMA_Cmd(IAP_TX_DMA_STREAM,DISABLE);	/* 传输完成以后关闭串口DMA */
	}

	if (DMA_GetITStatus(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TCIF))
	{
		DMA_ClearITPendingBit(IAP_TX_DMA_STREAM, IAP_TX_DMA_IT_TCIF);
		/* TODO 待完善发送完成需要关闭该关闭的东西 */
		DMA_Cmd(IAP_TX_DMA_STREAM,DISABLE);	/* 传输完成以后关闭串口DMA */
	}
	
    /* leave interrupt */
    rt_interrupt_leave();	
}

4、实现:
线程:

/**
* @brief IAP升级串口接收处理
* @param void *paramemter
* @retval None
*/
static void Bsp_iaprx_task(void *paramemter)
{
	for (;;)
	{
		rt_sem_take(&IapRx_OPSem, RT_WAITING_FOREVER);
		DRV_IAP_Receive_Task();
	}
}

/**
* @brief IAP升级串口发送处理
* @param void *paramemter
* @retval None
*/
static void Bsp_iaptx_task(void *paramemter)
{
	for (;;)
	{		
		rt_sem_take(&IapTx_OPSem, RT_WAITING_FOREVER);
		APP_IAP_Master_Task();
	}
}

线程调用接收处理部分 :

/* 背板串口APP应用回调函数 */
extern void IAP_RxPFrame_DataCallback(void);
extern void IAP_TxPFrame_DataCallback(void);

extern uint8_t IAP_TX_BUF[IAP_BUFF_LEN];
extern __IO uint8_t IAP_RX_BUF[IAP_BUFF_LEN];
extern __IO uint8_t IAP_Rxoffset;

struct rt_semaphore IapRx_OPSem;
struct rt_semaphore IapTx_OPSem;

/* 线程调用接收处理部分 */
void DRV_IAP_Receive_Task(void)
{
	rt_memcpy((void*)IAP_TX_BUF, (void*)IAP_RX_BUF, IAP_Rxoffset);
	rt_sem_release(&IapTx_OPSem);
}

通讯发送主任务:

extern __IO uint8_t IAP_Rxoffset;

/* IAP串口发送主任务 */
void APP_IAP_Master_Task(void)
{		
	BSP_IAP_USART_Transmit_DMA(IAP_Rxoffset);
}

此代码发一次回一次,接收不清零。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值