stm32 串口+DMA+环形FIFO缓存收发数据

μcos环境例程
freertos环境例程
重要几点
1.配置DMA,串口及环形buff之间的关系;
2.USART_IT_IDLE空闲中断接收完一帧数据,处理环形buff入口指针,通知用户程序接收完一次数据;
3.发送数据无需利用环形buff,直接将待传数据作为DMA的源地址,再使能相应DMA通道,根据串口TC中断判断发送完成;
4.用户程序中读取FIF0;

环形FIFO buff

FIFO实现为一个维护入口下标和出口下标的数组,循环覆写,与DMA的循环模式类似

typedef struct {
	uint8_t* buffer;	//FIFO数据
	uint16_t in;			//入口下标
	uint16_t out;			//出口下标
	uint16_t size;		//FIFO大小
}FIFO_Type;

几个FIFO功能函数

void Fifo_Init(FIFO_Type* fifo, uint8_t* buffer, uint16_t size)
{
	fifo->buffer = buffer;
	fifo->in = 0;
	fifo->out = 0;
	fifo->size = size;
}

uint16_t Fifo_Get(FIFO_Type* fifo, uint8_t* buffer, uint16_t len)
{
	uint16_t lenght;
	uint16_t in = fifo->in;	
	uint16_t i;
	lenght = (in + fifo->size - fifo->out)%fifo->size;
	if(lenght > len)
		lenght = len;
	for(i = 0; i < lenght; i++)
	{
		buffer[i] = fifo->buffer[(fifo->out + i)%fifo->size];
	}
	fifo->out = (fifo->out + lenght)%fifo->size;
	return lenght;
}

uint16_t Fifo_Status(FIFO_Type* fifo)
{
	uint16_t lenght;
	lenght = (fifo->in + fifo->size - fifo->out)%fifo->size;
	return lenght;
}

USART初始化

注意使能串口TC中断IDLE中断串口DMA收发,分别标志传输完成和接受完成,其他配置为常规配置,根据自己需求配置即可,串口1为例

  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
 	 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

	USART_InitStructure.USART_BaudRate = 115200;//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	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(USART1, &USART_InitStructure); //初始化串口1
 USART_ITConfig(USART1, USART_IT_IDLE , ENABLE);//空闲中断
 USART_ITConfig(USART1, USART_IT_TC , ENABLE);//传输完成中断
 USART_DMACmd(USART1, USART_DMAReq_Tx|USART_DMAReq_Rx, ENABLE);//使能串口DMA收发
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

DMA初始化

使能相应的DMA通道,通道表如下在这里插入图片描述
在这里插入图片描述
如上表得知,串口1的DMA收发通道为DMA1的通道4通道5,初始化如下

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);		

	DMA_DeInit(DMA1_Channel5);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->DR);
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Uart1_Rx_Buffer;  //FIFO的buffer指针指向
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = BSP_UART1_RX_SIZE;  //FIF0的buffer大小
	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_InitStructure.DMA_Mode = DMA_Mode_Circular;  //DMA循环覆盖写入
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	/* Enable USART1 DMA TX request */

	DMA_Cmd (DMA1_Channel5,ENABLE); 

	DMA_DeInit(DMA1_Channel4);
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0;  //传输再修改地址,并使能该通道
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_BufferSize = 1;
	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_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);

	//接收FIFO初始化
	Fifo_Init(&Uart1_Rx_Fifo,Uart1_Rx_Buffer,BSP_UART1_RX_SIZE);


	//同步量,RTOS相关
	OSMutexCreate (&Uart1TxSem, "uart1 tx mutex", &err); 
	OSSemCreate (&Uart1TxWaitSem, "uart1 tx wait sem", 0,&err); 

串口中断服务函数

处理串口TC中断,IDLE中断

void Bsp_IntHandler(CPU_FNCT_VOID isr)//  RTOS相关
{
    CPU_SR_ALLOC();

    CPU_CRITICAL_ENTER();                                       /* Tell the OS that we are starting an ISR            */

    OSIntEnter();

    CPU_CRITICAL_EXIT();

    if (isr != (CPU_FNCT_VOID)0) {
        isr();
    }
    OSIntExit();  	
}
void USART1_IRQHandler(void)  //中断
{
	Bsp_IntHandler(USART1_IntHandler);
}
void USART1_IntHandler(void)   //   中断服务函数
{
	uint32_t temp = 0;
	OS_ERR err;	
	if(USART_GetITStatus(USART1,USART_IT_IDLE)!= RESET)//空闲中断
	{
		Uart1_Rx_Fifo.in = BSP_UART1_RX_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
		OSTaskSemPost(&MyTask_TCB, OS_OPT_POST_NONE, &err);//通知用户任务接收到一次数据帧
		temp = USART1->SR; //软件序列清除IDLE位
		temp = USART1->DR; //先读USART_SR,然后读USART_DR
		USART_ClearITPendingBit(USART1,USART_IT_IDLE);
	}
	else if(USART_GetITStatus(USART1,USART_IT_TC)!= RESET)//发送完成中断
	{
		USART_ClearITPendingBit(USART1,USART_IT_TC);
		DMA_Cmd(DMA1_Channel4, DISABLE);
		OSSemPost (&Uart1TxWaitSem, OS_OPT_POST_NONE, &err);
 		if(err != OS_ERR_NONE)
		{
			temp = err;
		}			
	}	
}

发送函数接收函数

发送,串口资源互斥访问,信号量通知串口发送成功

void Uart_Send(const uint8_t* buf, uint16_t len)
{
	OS_ERR err;
	if(len > BSP_UART1_TX_SIZE || len == 0)
	{
		return;
	}

	OSMutexPend(&Uart1TxSem, 500, OS_OPT_PEND_BLOCKING, (CPU_TS *) 0, &err);
	OSSemSet(&Uart1TxWaitSem, 0, &err);
	if(err != OS_ERR_NONE)
	{
		return err;
	}
	DMA_Cmd(DMA1_Channel4, DISABLE);
	DMA1_Channel4->CNDTR = len;//发送数据大小
	DMA1_Channel4->CMAR = (uint32_t)buf;//发送数据地址
	/* Enable USART1 DMA TX request */
	DMA_Cmd (DMA1_Channel4,ENABLE);
	OSSemPend(&Uart1TxWaitSem, 500, OS_OPT_PEND_BLOCKING, (CPU_TS *) 0, &err);
	 OSMutexPost (&Uart1TxSem, OS_OPT_POST_NONE, &err);
}


uint16_t Uart_1_Get(uint8_t *buffer, uint16_t len)
{
	return Fifo_Get(&Uart1_Rx_Fifo,buffer, len);  //读出长度len的fifo中的数据存在buffer参数中,fifo数据不足则有多少回多少
}
  • 15
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
引用提供了一个关于FIFO(First-In-First-Out)的功能函数的示例代码。这段代码实现了FIFO的初始化、数据读取和状态查询等功能。FIFO是一种常用的数据缓存机制,用于在数据的输入和输出之间进行临时存储。 在引用中,有一段代码展示了如何从FIFO中读取数据进行发送。代码中使用FIFO_ReadNotCopy函数从FIFO中读取数据,并将读取到的数据发送出去。发送完成后,使用FIFO_ReduceOne函数清除已读取过的数据。 而在引用中,展示了一个示例函数CopyTempDataToFIFO,用于将实时数据拷贝到发送缓冲区中。该函数将实时数据按照一定的格式写入到FIFO中,以供后续发送使用。 综上所述,stm32fifo数据缓存可以通过使用FIFO机制来实现。可以根据具体需求,使用FIFO的初始化、读取和写入函数,将数据按照先进先出的原则进行缓存。这样可以确保数据在发送过程中的顺序和完整性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [stm32 串口+DMA+环形FIFO缓存收发数据](https://blog.csdn.net/weixin_43862847/article/details/87194718)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [STM32上使用的环形FIFO队列,用于缓存待发送数据](https://blog.csdn.net/cp1300/article/details/79822476)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值