说明:这个实验是想熟悉一下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
总结:
其实这节的重点应该是关于队列的这几个函数的用法,可以看开发手册或者运用翻译软件来学习。