STM32串口+freeRTOS+DMA+先进先出队列+动态内存申请

动态内存申请:就是我需要用的时候,再开辟一段内存。比如在程序中需要把数据uint8_t a[1,2,3], 复制给另一个数组,那就要创建创建另一个数组,需要3个字节内存, 那就临时申请3个字节,使用完后又把这3个字节释放。

**1、节点:**队列里的一个数据块,类似FreeRTOS的任务控制块。数据结构如下:
在这里插入图片描述
2、队列:N个节点连接起来
在这里插入图片描述
如果我们要往串口发送数据。
1、printf(“this is a FreeRTOS system”)
2、printf(“now start ADC”)
3、printf(“start to display on LCD”)
常规的发送完1之后,即使我们用DMA发送,MCU也必须等1发送完以后再执行2这句,等待2发送完以后再执行3这句。但是我们用队列就不一样了,MCU把1写到节点1,把2写到节点2,把3写到节点3,执行这这步很快。实际数据呢还没有发送出去,只是把它放到队列里等待发送了。我们可以定时比如1ms检查一次串口是否空闲了,则把一个节点通过DMA的方式给串口发送。

3、用实际程序说明:
1)、定义数据结构,串口DMA就从这个数据 与 串口数据寄存器间 搬运数据

struct COM_DATA_ST
{
	uint8_t SendBuff[200];
	uint8_t ReceBuff[200];
	uint8_t Send_Complete_Flag;
	uint8_t Bus_Idle_Count;
};
 struct node
{
	uint8_t Length;						//有效数据
	uint8_t *Data;						//数据指针
	struct node *pNext;					//节点指针
};

定义一个串口数据和三个队列头节点。如果只发送,只要一个头节点就可以了。

//定义三个链表头结点,头结点不存数据
struct node Rece_Quene;			
struct node Send_Quene;		
struct node Send_Ack_Quene;	

2)、串口初始化和DMA初始化
用STM32CubeMX生成串口,DMA程序。但初始化里作一下修改,生成的MDA程序没有初始化DMA发送的数据长度、数据地址,所以我们增加这部分,然后使能DMA。

/* USART1_TX Init */
hdma_usart1_tx.Instance = DMA1_Channel4;
hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_tx.Init.Mode = DMA_NORMAL;
hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
{
  _Error_Handler(__FILE__, __LINE__);
}
/*******************增加以下部分***********************************/
hdma_usart1_tx.Instance->CNDTR=0;
hdma_usart1_tx.Instance->CPAR = USART1_DR_Base;
hdma_usart1_tx.Instance->CMAR = (uint32_t)Com_Data.SendBuff;
SET_BIT(huart->Instance->CR3, USART_CR3_DMAT);//使能DMA发送
/******************************************************/

接收部分,我们让它接收固定长度200个字节,然后开串口空闲中断,当串口接收了N个字节后,发现没有收到数据了,会产生空闲中断。中断我们就可以把数据取出来了。

/******************************************************/
hdma_usart1_rx.Instance->CNDTR=RECEBUFF_SIZE;
hdma_usart1_rx.Instance->CPAR = USART1_DR_Base;
hdma_usart1_rx.Instance->CMAR = (uint32_t)Com_Data.ReceBuff;
__HAL_DMA_ENABLE(&hdma_usart1_rx);//enable DMA
SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);//使能DMA接收
/******************************************************/

最后还要设置一下串口空闲中断,和打开串口 总中断

__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);//设置串口空闲中断
HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);	
HAL_NVIC_EnableIRQ(USART1_IRQn);

在空闲中断中,我们调用函数把接收到的数据入到 接收队列,然后开始新的200个数据接收

void USART1_IRQHandler(void)
{
      usart1_rev_idle_callback();
      HAL_UART_IRQHandler(&huart1);
}
void usart1_rev_idle_callback(void)
{
	uint8_t data_length;
	__HAL_DMA_DISABLE(&hdma_usart1_rx);//disable DMA
	data_length=RECEBUFF_SIZE - (uint8_t)__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//取得接收长度,RECEBUFF_SIZE被定义为200,启动DMA接收的时候指定接收200字节
	                                                      //当发生空闲中断时,表示接收完成,200减未接收的个数,等到已接收个数
	InsertNode(&Rece_Quene,Com_Data.ReceBuff,data_length);//把DMA接收到的数据从Com_Data.ReceBuff复制到Rece_Quene连表中
	hdma_usart1_rx.Instance->CNDTR = RECEBUFF_SIZE;//重新设置DMA接收200字节
	__HAL_DMA_ENABLE(&hdma_usart1_rx);//enable DMA
}

并且HAL_UART_IRQHandler(&huart1);中增加清除中断标志

	//如果是空闲中断
  isrflags = __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE);
  cr1its= __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE);
  if((isrflags != RESET) && (cr1its != RESET))
  {
		__HAL_UART_CLEAR_IDLEFLAG(huart);//清除标志
		isrflags=huart->Instance->SR;//读SR可以实现清除状态寄存器
		isrflags=huart->Instance->DR; 
   }

然后接收就完成了,其它函数可以去接收队列里拿数据来处理。

3)、发送数据。
需要发送数据的地方,调用uart_send(&a[0],sizeof(a));把数据入到发送队列里。

void uart_send(uint8_t *ptr,uint8_t length)
{
	InsertNode(&Send_Quene,ptr,length);
}

然后定时检查发送队列里如果有数据,就取数据发送。

Print调用
uart_send调用
start_task
1秒调用1次Print打印信息
uart_send 把信息入队
InsertNode把信息插入Send_Quene队尾
send_data_task
5ms调用一次check_send_quene检查一次串口是否空闲20ms以上
Send_Quene队列不为空则取一个节点复制到发送缓冲启动DMA
send_data
data2quene_task
1ms调用usart1_send_interval_deal
如果DMA发送完成开始计时

写完这笔记发现还有优化的地方,未完待续…

为什么要用FreeRTOS,因为用main…while(1) 发现申请内存p1 =(struct node*)malloc(sizeof(struct node)); 失败,还不知道为什么

经过测试,接收串口有时候会漏接数据。

参考:https://www.amobbs.com/thread-4516795-1-1.html

猜你喜欢

转载自blog.csdn.net/liangbin414/article/details/88633379