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下载

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值