stm32f103运行FreeRTOS使用DMA实现USART1不定长接收

使用DMA是实现USART1的不定长接收和发送数据。接收采用FIFO缓存和队列的方式向应用层抛数据。首先定义接收和发送的缓存区,缓存区的大小根据收发数据量确定。

#define USAER1_RX_MAX        	128    //串口1最大接收数据量
#define USAER1_TX_MAX         	128 
#define USART1_RX_FIFO_MAX		(1024)

static uint8_t USART1_TX_Buff[USAER1_TX_MAX];
static uint8_t USART1_RX_Buff[USAER1_RX_MAX];

然后初始化串口和DMA

/**************************************************************************
函 数 名:Usart1_Init
功能描述:串口1初始化。
输入参数:
    bound:波特率
输出参数:None
返 回 值:None。
其他说明:
        使用串口1前,必须调用该函数初始化。
**************************************************************************/
void Usart1_Init(const uint32_t bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;         //GPIO配置
    USART_InitTypeDef USART_InitStructure;       //串口配置
    NVIC_InitTypeDef NVIC_InitStructure;         //中断配置

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	    //使能USART1,GPIOA时钟
	
   /* 配置串口使用的GPIO */
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;                                     //PA9(TX)
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;	                              //复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure);                                          //初始化GPIOA9
	
    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;                                    //PA10(RX)
    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;                                  //上拉输入(RX引脚可以设置为非模拟功能任何模式,甚至不设置)
    GPIO_Init(GPIOA, &GPIO_InitStructure);                                          //初始化GPIOA10  
	
   /* 配置串口 */
    USART_InitStructure.USART_BaudRate   = bound;                                   //串口波特率
    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
		
   /* 配置中断优先级 */
    NVIC_InitStructure.NVIC_IRQChannel                   = USART1_IRQn;             //设置串口1的优先级
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8 ;                      //主优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0 ;		                  //次优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;			            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);	          //根据指定的参数初始化VIC寄存器	

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);                                  //开启串口接收中断
    
	Usart1_Send_DMA_Init();
	Usart1_Receive_DMA_Init();
	USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
	
	uartRecQueue = xQueueCreate(10, sizeof(USART_Read_t));     //申请串口接收队列,用于存放分帧后的数据指针
    
	USART_Cmd(USART1, ENABLE);                                                      //使能串口1 
}
/**************************************************************************
函 数 名:Usart1_Send_DMA_Init
功能描述:串口1的DMA通道初始化
输入参数:None
输出参数:None
返 回 值:None。
其他说明:
**************************************************************************/
static void Usart1_Send_DMA_Init(void)
{
    DMA_InitTypeDef  DMA_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel4);
   
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	
    DMA_InitStructure.DMA_BufferSize = USAER1_TX_MAX;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_TX_Buff;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
	
	DMA_Init(DMA1_Channel4, &DMA_InitStructure);
	
	/* 发送一次数据,产生DMA1_FLAG_TC4标记 */
	DMA_SetCurrDataCounter(DMA1_Channel4, 1);
	DMA_ClearFlag(DMA1_FLAG_TC4);
	DMA_Cmd(DMA1_Channel4, ENABLE);
}
/**************************************************************************
函 数 名:Usart1_Receive_DMA_Init
功能描述:串口1的DMA通道初始化
输入参数:None
输出参数:None
返 回 值:None。
其他说明:
**************************************************************************/
static void Usart1_Receive_DMA_Init(void)
{
    DMA_InitTypeDef  DMA_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel5);
   
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	
    DMA_InitStructure.DMA_BufferSize = USAER1_RX_MAX;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART1_RX_Buff;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel5, &DMA_InitStructure);
	
	NVIC_InitTypeDef NVIC_InitStructure;         //中断配置
	/* 配置中断优先级 */
    NVIC_InitStructure.NVIC_IRQChannel                   = DMA1_Channel5_IRQn;             //设置串口1的优先级
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 7 ;                      //主优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0 ;		                  //次优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;			            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);	          //根据指定的参数初始化VIC寄存器	
	
	
	DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE, ENABLE);
	
	DMA_Cmd(DMA1_Channel5,ENABLE);
}

DMA接收开启转移完成和转移一般中断。在中断中从缓存向FIFO转移数据。

/**************************************************************************
函 数 名:Data_TO_FIFO
功能描述:
输入参数:
@srcData:数据源
@srcLen:数据源长度
输出参数:
@destFIFO:目标FIFO
返 回 值:None。
其他说明:
**************************************************************************/
static void Data_TO_FIFO(const uint8_t *srcData, const uint32_t srcLen, USART1_RX_FIFO_t *destFIFO)
{
	if( srcData == NULL || destFIFO == NULL )
		return;
	
	if( (destFIFO->w_p + srcLen) < USART1_RX_FIFO_MAX ){
		memcpy(destFIFO->buff + destFIFO->w_p, srcData, srcLen );
		destFIFO->w_p += srcLen;
	}else{
		uint32_t temp = USART1_RX_FIFO_MAX - destFIFO->w_p;
		memcpy(destFIFO->buff + destFIFO->w_p, srcData, temp );
		memcpy(destFIFO->buff, srcData + temp, srcLen - temp );
		destFIFO->w_p = srcLen - temp;
		if( destFIFO->w_p >= destFIFO->r_p )
			destFIFO->coverFlag = 1;	//标记覆写了未读取的数据
	}
}

/**************************************************************************
函 数 名:DMA1_Channel5_IRQHandler
功能描述:
输入参数:None
输出参数:None
返 回 值:None。
其他说明:
**************************************************************************/
void DMA1_Channel5_IRQHandler(void)
{
	if( DMA_GetITStatus(DMA1_IT_TC5) == SET){			//转移结束
		Data_TO_FIFO(&USART1_RX_Buff[USAER1_RX_MAX/2], USAER1_RX_MAX/2, &usart1Fifo);
//		printf("DMA1_IT_TC5\r\n");
		DMA_ClearITPendingBit(DMA1_IT_TC5);
	}else if( DMA_GetITStatus(DMA1_IT_HT5) == SET){		
		Data_TO_FIFO(USART1_RX_Buff, USAER1_RX_MAX/2, &usart1Fifo);
//		printf("DMA1_IT_HT5\r\n");
		DMA_ClearITPendingBit(DMA1_IT_HT5);
	}
	else if( DMA_GetITStatus(DMA1_IT_TE5) == SET){		//错误
		DMA_ClearITPendingBit(DMA1_IT_TE5);
	}
}

串口开启空闲中断,实现接收数据的分帧。分帧后的数据节点通过写入到队列。

/**************************************************************************
函 数 名:USART1_IRQHandler
功能描述:串口1中断服务函数。
输入参数:None
输出参数:None
返 回 值:None
其他说明:
        接收到\r\n或者接收数据达到最大值,即接收数据结束。
**************************************************************************/
void USART1_IRQHandler(void)
{
	BaseType_t xHigherPrioritTaskWoken;
	USART_Read_t usartRead;
	
    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET){    //中断类型是接收数据
		DMA_Cmd(DMA1_Channel5, DISABLE);
		uint32_t recvLen = USAER1_RX_MAX - DMA_GetCurrDataCounter(DMA1_Channel5);
		
		if( recvLen < USAER1_RX_MAX/2 )
			Data_TO_FIFO(USART1_RX_Buff, recvLen, &usart1Fifo);
		else
			Data_TO_FIFO(&USART1_RX_Buff[USAER1_RX_MAX/2], recvLen - USAER1_RX_MAX/2, &usart1Fifo);
		
		usartRead.sr_p = usart1Fifo.r_p;
		usartRead.er_p = (usart1Fifo.w_p == 0) ? (USART1_RX_FIFO_MAX - 1) : (usart1Fifo.w_p - 1); 

		usart1Fifo.r_p = usartRead.er_p + 1;
		if( uartRecQueue )
			xQueueSendFromISR( uartRecQueue, &usartRead, &xHigherPrioritTaskWoken );
		
		DMA_SetCurrDataCounter(DMA1_Channel5,USAER1_RX_MAX);
		DMA_Cmd(DMA1_Channel5, ENABLE);
		/* 清理空闲中断 */
		USART1->SR;//先读SR
        USART1->DR;//再读DR
	}			
}

串口的发送数据接口函数

/**************************************************************************
函 数 名:Usart1_SendData
功能描述:串口1发送数据。
输入参数:
    pData:发送的数据缓存
    dataLen:发送的数据长度
输出参数:None
返 回 值:None
其他说明:
支持发送超过发送缓存的数据,但是为了执行效率,尽量不要发送超过发送缓存的数据,或者把发送缓存开大一点。
**************************************************************************/
void Usart1_SendData(const uint8_t *pData, const uint16_t dataLen)
{
#if 0
    uint16_t i =0; 
	
    for( i = 0; i < dataLen; i++ )
    {
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);    //等待发送寄存器空       
        USART_SendData(USART1, pData[i]);   			
    }
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);    //等待发送寄存器空   
#endif
	uint32_t circ = dataLen / USAER1_TX_MAX;
	uint32_t res = dataLen % USAER1_TX_MAX;
	uint32_t i = 0;

	while(DMA_GetFlagStatus(DMA1_FLAG_TC4) != SET);	//等待发送完成
	
	for( i = 0; i < circ; i++ ){
		DMA_Cmd(DMA1_Channel4, DISABLE);
		memcpy(USART1_TX_Buff, pData + i * USAER1_TX_MAX, USAER1_TX_MAX);
		DMA_SetCurrDataCounter(DMA1_Channel4, USAER1_TX_MAX);
		DMA_ClearFlag(DMA1_FLAG_TC4);
		DMA_Cmd(DMA1_Channel4, ENABLE);
		while(DMA_GetFlagStatus(DMA1_FLAG_TC4) != SET);	//等待发送完成
	}
	
	if( res ){
		DMA_Cmd(DMA1_Channel4, DISABLE);
		memcpy(USART1_TX_Buff, pData + i * USAER1_TX_MAX , res);
		DMA_SetCurrDataCounter(DMA1_Channel4, res);
		DMA_ClearFlag(DMA1_FLAG_TC4);
		DMA_Cmd(DMA1_Channel4, ENABLE);
	}
	 
}

发送数据缓存的大小根据发送数据量调整。

接收函数

/**
name:USART1_Read
function:串口1读数据
input:None
output:
@rData:读取的数据指针,实际地址空间在该函数内部申请,所以在外部使用后必须要释放
@rDataLen:读取的数据长度指针
@timeout:等待超时时间
return:错误
other:None
*/
int USART1_Read(uint8_t **rData, uint32_t *rDataLen, uint32_t timeout)
{
    USART_Read_t usartRead;
    uint8_t *p = NULL;
    uint32_t tempLen = 0;

    if( rData == NULL || rDataLen == NULL)
        return -1;

    if(xQueueReceive(uartRecQueue, &usartRead, timeout) != pdTRUE)  //读取串口队列消息,没有数据就阻塞等待
        return -1;

    if( usart1Fifo.coverFlag ){
        usart1Fifo.coverFlag = 0;
		printf("usart1Receive.fifo.coverFlag\r\n");
        return -1;
    }

    if( usartRead.er_p >= usartRead.sr_p ){ //结束地址大于开始地址
		*rDataLen = usartRead.er_p - usartRead.sr_p + 1;
		p = pvPortMalloc(*rDataLen + 1);
		if( p != NULL ){
			memset(p, 0, *rDataLen + 1);
			memcpy(p, &usart1Fifo.buff[usartRead.sr_p], *rDataLen);
		}
	}else{    //结束地址小于开始地址
		tempLen = USART1_RX_FIFO_MAX - usartRead.sr_p;
		*rDataLen = tempLen + usartRead.er_p +1;

    p = pvPortMalloc(*rDataLen + 1);
    if( p != NULL ){
      memset(p, 0, *rDataLen + 1);
      memcpy(p, &usart1Fifo.buff[usartRead.sr_p], tempLen);
      memcpy(p + tempLen, usart1Fifo.buff, usartRead.er_p + 1);
    }
  }

  *rData = p;

  return 0;
}

最后调用收发

/**************************************************************************
函 数 名:MainTaskFunc
功能描述:主任务。
输入参数:
@pvParameters:系统传参
输出参数:None
返 回 值:None
其他说明:
**************************************************************************/
static void MainTaskFunc( void *pvParameters )
{
	uint8_t *tmp = NULL;
	uint32_t len = 0;
	
	while(1)
	{
		if( 0 == USART1_Read(&tmp, &len, portMAX_DELAY) ){
			Usart1_SendData(tmp, len);
			vPortFree(tmp);
		}
	}
}

完整工程下载链接:stm32f103+freertos+USART(DMA)不定长接收-单片机文档类资源-CSDN下载

### 回答1STM32F103是一款基于ARM Cortex-M3内核的单片机,它具有丰富的外设和性能优势。FreeRTOS是一款流行的实时操作系统,可用于在STM32F103实现多任务和调度。USARTSTM32F103系列的串行通信接口,用于与外部设备进行通信。DMA(直接内存访问)是一种数据传输方式,可实现高效的数据传输,提高系统性能。CubeMX是一款图形化开发工具,可用于配置和生成STM32F103的初始化代码。 在STM32F103使用FreeRTOS框架,可以实现多任务和调度功能。通过配置CubeMX,可以方便地设置USARTDMA外设。首先,使用CubeMX配置USART外设的工作模式、波特率等参数,并使用DMA传输数据。然后,通过编程将USART设置为DMA模式,使数据在接收和发送时通过DMA传输,提高效率和性能。在FreeRTOS任务中,可以编写代码实现与外设的通信,通过USART发送和接收数据。使用FreeRTOS的任务和调度功能,可以同时处理多个任务,提高系统的并发性和响应能力。 总之,通过结合STM32F103FreeRTOSUSARTDMA,以及使用CubeMX配置工具,可以方便地实现多任务调度和串口通信。这样的架构可以提高系统性能,实现更复杂的应用。 ### 回答2: STM32F103是一款由意法半导体(STMicroelectronics)推出的32单片机,它使用了Cortex-M3内核。在STM32F103中,我们可以使用FreeRTOS(Real-Time Operating System)来实现多任务处理和实时性。 USART是通用异步收发传输器,它可以用于串行通信。在STM32F103中,我们可以使用USART来进行与外部设备的通信。 DMA(Direct Memory Access)是一种数据传输方法,它可以在系统的CPU不直接参与的情况下进行数据传输。在STM32F103中,通过配置USARTDMA,可以实现高效的数据传输。DMA还可以在多个外设之间进行数据传输,提高系统的效率。 CubeMX是一个图形化的配置工具,它可以帮助我们快速地配置和初始化STM32的硬件资源,生成相应的代码框架。 通过结合使用FreeRTOSUSARTDMA和CubeMX,我们可以实现STM32F103上进行多任务处理、串行通信和高效数据传输的需求。首先,使用CubeMX快速配置USARTDMA相关的硬件资源,并生成代码框架。然后,根据需要,使用FreeRTOS进行任务的创建、调度和管理,实现多任务处理。在任务中,通过调用USART的相应接口来进行串行通信,并通过配置DMA实现高效的数据传输。 总的来说,使用STM32F103FreeRTOSUSARTDMA和CubeMX的组合,可以帮助我们充分发挥STM32的功能,实现复杂的应用需求,并提升系统的性能和效率。 ### 回答3: stm32f103STMicroelectronics(ST)公司推出的一款32单片机,它的系列名称是STM32。该系列的芯片具有高性能和低功耗的特点,广泛应用于嵌入式系统中。 FreeRTOS是一款实时操作系统(RTOS),广泛应用于嵌入式系统开发中。它提供了丰富的功能,包括任务管理、时间管理、内存管理和通信等。使用FreeRTOS能够更好地组织和管理任务,提高系统的实时性和稳定性。 USART是通用同步/异步收发器(Universal Synchronous/Asynchronous Receiver Transmitter)的缩写,是一种常用的串口通信接口。它可以实现串行数据的发送和接收,常见的应用场景包括与外部设备进行数据通信和调试信息的输出。 DMA是直接内存访问(Direct Memory Access)的缩写,它是一种不需要CPU干预的数据传输方式。在数据量较大或者需要高速传输的场景下,使用DMA能够提高数据传输的效率,减轻CPU负担。 CubeMX是一个集成开发环境(IDE),用于简化STM32芯片的配置和代码生成。通过CubeMX可以轻松地配置芯片的外设、时钟和中断等,方便开发者快速搭建项目框架。它还可以生成基于CMSIS和HAL库的初始化代码,简化开发流程。 结合以上信息,stm32f103 freertos usart dma cubemx可以理解为在使用stm32f103芯片开发嵌入式系统时,使用FreeRTOS实现任务管理和时间管理,通过USART进行与外部设备的通信,利用DMA实现高效的数据传输,同时使用CubeMX进行芯片配置和代码生成的开发流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值