【经验分享】[转贴] K60实现ADC四通道自动连续采集 无需CPU...
本帖最后由 zndz410 于 2014-5-7 11:50 编辑K60实现ADC四通道自动连续采集 无需CPU干预
之前有许多朋友询问如何使用K60的ADC实现多个通道的连续自动采集功能。其实就K60而言,他的ADC有ADC0和ADC1,每个ADCx又有A和B两组控制通道,因此最多可以实现4组ADC通道的连续自动采集,而且配合DMA和PDB,无需CPU对采集过程进行干预,我们可以直接读取内存中的转换结果!
本例程同样是基于LPLD的OSKinetis固件库开发的,代码中全部使用库函数进行开发,开发过程无需涉及任何寄存器操作。每步我们都写了详细注释,相信不了解原理的童鞋也能看个大概!
下面我还是简单叙述下实现原理:
Step 1.配置ADC模块,配置A组和B组控制通道均为硬件触发,使能DMA请求。并使能相关输入通道。
Step 2.配置DMA模块,分别用2个DMA通道来控制ADC0和ADC1的DMA传输,并配置DMA源地址为ADC结果寄存器,配置主循环计数2次,因为要分别传输R和R结果寄存器的值。
Step 3.配置PDB模块,使用软件触发或其他外设触发均可,例程中位软件触发。关键步骤是要配置PDB的ADC预触发模式,使能通道0和通道1的预触发,并配置为Back to Back模式,这样才能让ADC的COCO转换完成标志自动触发下一个AD转换!
Step 4.触发PDB工作,坐享其成!
主要代码如下:
//**********************************************
//Step 1.配置ADC0和ADC1的参数
//ADC0-A
adc0_init_struct.ADC_Adcx = ADC0;
adc0_init_struct.ADC_BitMode = SE_12BIT; //12位精度
adc0_init_struct.ADC_CalEnable = TRUE; //使能初始化校验
adc0_init_struct.ADC_HwAvgSel = HW_4AVG; //使能4次硬件校准
adc0_init_struct.ADC_HwTrgCfg = HW_TRGA; //配置A组为硬件触发
adc0_init_struct.ADC_DmaEnable = TRUE; //使能DMA请求
LPLD_ADC_Init(adc0_init_struct); //初始化ADC0-A组通道
//ADC0-B,只需配置不同参数的成员变量
adc0_init_struct.ADC_HwTrgCfg = HW_TRGB; //配置B组为硬件触发
LPLD_ADC_Init(adc0_init_struct); //初始化ADC0-B组通道
//使能ADC0的通道DAD1、AD14的引脚复用功能
LPLD_ADC_Chn_Enable(ADC0, DAD1);
LPLD_ADC_Chn_Enable(ADC0, AD14);
//使能ADC0的相关转换通道
LPLD_ADC_EnableConversion(ADC0, DAD1, 0, FALSE);
LPLD_ADC_EnableConversion(ADC0, AD14, 1, FALSE);
//ADC1A
adc1_init_struct.ADC_Adcx = ADC1;
adc1_init_struct.ADC_BitMode = SE_12BIT;
adc1_init_struct.ADC_CalEnable = TRUE;
adc1_init_struct.ADC_HwAvgSel = HW_4AVG;
adc1_init_struct.ADC_HwTrgCfg = HW_TRGA;
adc1_init_struct.ADC_DmaEnable = TRUE;
LPLD_ADC_Init(adc1_init_struct);
//ADC1B
adc1_init_struct.ADC_HwTrgCfg = HW_TRGB;
LPLD_ADC_Init(adc1_init_struct);
LPLD_ADC_Chn_Enable(ADC1, AD10);
LPLD_ADC_Chn_Enable(ADC1, AD11);
LPLD_ADC_EnableConversion(ADC1, AD10, 0, FALSE);
LPLD_ADC_EnableConversion(ADC1, AD11, 1, FALSE);
//**********************************************
//Step 2.配置DMA CH0和DMA CH1,分别处理ADC0和ADC1的DMA请求
//DMA CH0
dma0_init_struct.DMA_CHx = DMA_CH0; //使用Ch0通道
dma0_init_struct.DMA_Req = ADC0_DMAREQ; //DMA请求源为ADC0
dma0_init_struct.DMA_MajorLoopCnt = 2; //主循环计数2次,因为要循环ADC0的AB两组通道
dma0_init_struct.DMA_MinorByteCnt = 2; //次循环传输字节计数(由于ADC采样为12位,因此传输2字节)
dma0_init_struct.DMA_SourceAddr = (uint32)&(ADC0->R); //源地址:ADC0结果寄存器A地址
dma0_init_struct.DMA_SourceDataSize = DMA_SRC_16BIT; //源地址传输数据宽度16位
dma0_init_struct.DMA_SourceAddrOffset = 4; //源地址偏移为4个字节,因为ADC0->R寄存为32位宽,A组传输完成后移动到B组
dma0_init_struct.DMA_LastSourceAddrAdj = -8;//主循环最后调节地址为-8个字节,因为主循环为2次计数,因此地址偏移了8个字节
dma0_init_struct.DMA_DestAddr = (uint32)&Result; //目的地址,即定义的结果保存数组头地址
dma0_init_struct.DMA_DestDataSize = DMA_DST_16BIT; //目的地址传输数据宽度16位
dma0_init_struct.DMA_DestAddrOffset = 2; //目的地址偏移为2个字节,因为Result为16位变量
dma0_init_struct.DMA_LastDestAddrAdj = -4; //目的地址最后调节地址为-4个字节
dma0_init_struct.DMA_AutoDisableReq = FALSE; //禁用自动禁用请求,即不受主循环计数计数限制
//初始化DMA
LPLD_DMA_Init(dma0_init_struct);
//使能DMA请求
LPLD_DMA_EnableReq(DMA_CH0);
//DMA CH1,配置基本相同
dma1_init_struct.DMA_CHx = DMA_CH1;
dma1_init_struct.DMA_Req = ADC1_DMAREQ;
dma1_init_struct.DMA_MajorLoopCnt = 2;
dma1_init_struct.DMA_MinorByteCnt = 2;
dma1_init_struct.DMA_SourceAddr = (uint32)&(ADC1->R);
dma1_init_struct.DMA_SourceDataSize = DMA_SRC_16BIT;
dma1_init_struct.DMA_SourceAddrOffset = 4;
dma1_init_struct.DMA_LastSourceAddrAdj = -8;
dma1_init_struct.DMA_DestAddr = (uint32)&Result+4; //目的地址,由于数组中头2个元素存ADC0的结果,因此要偏移4个字节取第3个元素的地址
dma1_init_struct.DMA_DestDataSize = DMA_DST_16BIT;
dma1_init_struct.DMA_DestAddrOffset = 2;
dma1_init_struct.DMA_LastDestAddrAdj = -4;
dma1_init_struct.DMA_AutoDisableReq = FALSE;
//初始化DMA
LPLD_DMA_Init(dma1_init_struct);
//使能DMA请求
LPLD_DMA_EnableReq(DMA_CH1);
//**********************************************
//Step 3.配置PDB,用于触发ADC
pdb_init_struct.PDB_CounterPeriodMs = 100; //PDB计数器周期,这个决定了4个通道每采集一次的间隔
pdb_init_struct.PDB_LoadModeSel = LOADMODE_0;
pdb_init_struct.PDB_ContinuousModeEnable = TRUE; //使能连续工作模式,即只需要开始触发一次,以后PDB就会连续工作
pdb_init_struct.PDB_TriggerInputSourceSel = TRIGGER_SOFTWARE; //软件触发模式,即不需要用其他模块触发PDB工作
//初始化PDB
LPLD_PDB_Init(pdb_init_struct);
//配置PDB预触发功能
//使能ADC0-A组的预触发功能
LPLD_PDB_AdcTriggerCfg(ADC0, PRETRIG_EN_A|PRETRIG_DLY_A, 0);
//使能ADC0-B组的预触发功能,并使用Back to Back模式
LPLD_PDB_AdcTriggerCfg(ADC0, PRETRIG_BB_B|PRETRIG_EN_B|PRETRIG_DLY_B, 0);
//使能ADC1-A组的预触发功能,并使用Back to Back模式
LPLD_PDB_AdcTriggerCfg(ADC1, PRETRIG_BB_A|PRETRIG_EN_A|PRETRIG_DLY_A, 0);
//使能ADC1-B组的预触发功能,并使用Back to Back模式
LPLD_PDB_AdcTriggerCfg(ADC1, PRETRIG_BB_B|PRETRIG_EN_B|PRETRIG_DLY_B, 0);
//软件触发PDB开始工作
LPLD_PDB_SoftwareTrigger();
while(1)
{
delay();
printf("ADC0_RA=%d\r\n", Result);
printf("ADC0_RB=%d\r\n", Result);
printf(" ADC1_RA=%d\r\n", Result);
printf(" ADC1_RB=%d\r\n", Result);
}
运行结果:
例程下载:
务必注意:本例程只是一个用户代码,它必须基于OSKinetis固件库来运行,因此必须放置在固件库目录下的/project目录才能正确编译,固件库地址www.eefocus.com/bbs/article_891_561852.html
楼主的经验分享很丰富,赞一个!
页:
[1]