4.STM32freeRTOS----队列传递DMA搬运的ADC数据


说明:这个实验是想熟悉一下freeRTOS,创建两个任务,一个任务来接收DMA搬运ADC转换过来的数据,并且把这个数据写入一个队列;另一个任务就是去队列中把这个数据读出来并且打印到串口。


详细:这个实验的思路。
我想熟悉一下任务之间的通信—队列的方式,于是想把ADC转换过来的数据放到一个队列中,然后通过读取这个队列就知道转换数据。

主函数:
首先在主函数中对串口进行初始化,led初始化,adc初始化。然后创建一个开始任务。最后开启任务调度。

开始任务函数:
开启与关闭临界区。
创建一个队列,并且创建两个任务,然后记得删除开始任务。
注意:两个任务函数的优先级最好不要一样,为了避免采集到一半然后被迫停止。

任务1:把队列的数据读出来,放在一个缓冲区中(自定义一个数组),然后打印出来。
任务2 :把ADC的数据放入队列,由于是DMA搬运的数据,所以需要一个缓冲区(自定义的数组)来存放DMA从ADC1->DR数据寄存器搬运过来的数据,然后再把这个缓冲区的数据放到队列中去。

贴代码:

//main.c
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "adc.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#define QueueLength 1
#define DATA_LEN    3

QueueHandle_t Queue_MSG;


//任务句柄
TaskHandle_t 	Start_Handler = NULL;


//任务函数
void start_task(void *parm);
void task1(void *parm);
void task2(void *parm);

int  main(void)
{
	//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	//在串口1配置的时候已经写过这语句,main函数里面可以不要
	
	uart_init(115200);
	LED_Init();
	Adc_Init();
	
	//创建一个开始任务
	xTaskCreate(
		(TaskFunction_t ) start_task,
		(const char *		) "startTask",					//任务名
		(uint16_t 			) 100,							//任务堆栈大小
		(void * 		 		) NULL,						//任务输入参数
		(UBaseType_t		) 1,							//任务优先级
		(TaskHandle_t * ) &Start_Handler );					//任务句柄
	vTaskStartScheduler();          				//开启任务调度
}

void start_task(void *parm)
{
	taskENTER_CRITICAL();//开启临界区
	
	Queue_MSG = xQueueCreate(QueueLength,DATA_LEN*sizeof(u16));// 创建一个队列
	if(Queue_MSG != NULL)
	{
		xTaskCreate(task1,"task1",100,NULL,3,NULL);
		xTaskCreate(task2,"task2",100,NULL,2,NULL);
		printf("队列创建成功\n");
	}	
	else
	{
		printf("队列创建失败\n");
	}
	vTaskDelete(NULL);			//删除开始任务
	taskEXIT_CRITICAL(); //关闭临界区
}
void task1(void *parm)//把队列中的数据读出来
{
	u16 REV_AD_DATA[DATA_LEN] = {0};
	portBASE_TYPE xStatus;
	const portTickType xTickToWait = 100 / portTICK_RATE_MS;//将超时ms转换为超时时钟节拍
	/*
		xTicksToWait 如果调用时队列为空,
		则任务应阻止等待接收项目的最长时间。
		如果 xTicksToWait 为零且队列为空,
		则 xQueueReceive() 将立即返回。 
		时间以滴答时钟周期(时间片)定义,因此如果需要,
		应使用常量portTICK_PERIOD_MS转换为实时。
	*/
	while(1)
	{
		LED1 = !LED1;
		xStatus = xQueueReceive(Queue_MSG,&REV_AD_DATA,xTickToWait);
	/*
		从队列中接收项目。 
		该项目是通过副本接收的,因此必须提供足够大小的缓冲区。 
		复制到缓冲区中的字节数在创建排队时定义。
			
		已成功接收的项目将从队列中删除。	
	*/
		if(xStatus == pdTRUE)
		{
		printf("%5d,%5d,%5d\n",REV_AD_DATA[0],REV_AD_DATA[1],REV_AD_DATA[2]);
		}
		else
		{
			printf("err:recevice from the queue\n");
		}
		vTaskDelay(100);	
	}
}
void task2(void *parm)//将 DMA传输过来的ADC数据添加到队列中
{
	portBASE_TYPE  xStatus;
	const portTickType xTickToWait = 100 / portTICK_RATE_MS;//将超时ms转换为超时时钟节拍
	u16 AD_Val[DATA_LEN] = {0};
	DMA_Config(DMA1_Channel1 ,(u32)&ADC1->DR , (u32)AD_Val , 3);//配置DMA
	while(1)
	{
		if(Queue_MSG != 0)
		{
			 xStatus = xQueueSend(Queue_MSG, &AD_Val, xTickToWait);			//发送消息到队列(默认队尾)
			if(xStatus != pdTRUE)
            {
                printf("Could not send to the queque\r\n");
            }
		}
	}
}

//adc.c
#include "adc.h"

void Adc_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	ADC_InitTypeDef   ADC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO,ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6); 	//72/6=12 最大不超过14MHz
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 ;// | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	ADC_DeInit(ADC1);
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换模式
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//无外部触发转换
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立ADC
	ADC_InitStructure.ADC_NbrOfChannel = 3;//转换数量
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描模式
	ADC_Init(ADC1, &ADC_InitStructure);
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_239Cycles5);

	ADC_Cmd(ADC1, ENABLE);
	ADC_DMACmd(ADC1, ENABLE);
	
	ADC_ResetCalibration(ADC1);//开启复位校准
	while(ADC_GetResetCalibrationStatus(ADC1));//等待复位结束
	
	ADC_StartCalibration(ADC1);						//开启AD校准
    while(ADC_GetCalibrationStatus(ADC1));			//等待校准结束
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//
	
}

void DMA_Config(DMA_Channel_TypeDef* DMAy_Channelx ,u32 cpar, u32 cmar, u16 cndtr)
{
	DMA_InitTypeDef DMA_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	DMA_DeInit(DMAy_Channelx);
	
	DMA_InitStructure.DMA_BufferSize = cndtr;					//DMA缓存
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;				//内存基地址
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;            //外设基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;			//数据传输从外设到内存
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;				//没有内存到内存传输
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//内存地址16位
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						//内存地址递增
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;				//循环工作模式
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设数据16位
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			//外设地址不变
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;			//高优先级
	DMA_Init(DMAy_Channelx, &DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1, ENABLE);//
	
}


串口代码不贴了哈,需要的联系。

//led.c
#include "led.h"


void LED_Init(void)
{
 
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);	 //使能端口时钟
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;				 
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;		 
	GPIO_Init(GPIOC, &GPIO_InitStructure);
						  				 
	GPIO_SetBits(GPIOC,GPIO_Pin_13); 						 
}
//led.h

#ifndef __LED_H
#define __LED_H	 
#include "sys.h"

#define LED1 PCout(13)// PC13
void LED_Init(void);   //初始化	 	
			    
#endif

总结:
其实这节的重点应该是关于队列的这几个函数的用法,可以看开发手册或者运用翻译软件来学习。

  • 1
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值