找回密码
 成为LPLD会员
查看: 9473|回复: 76

[固件库编程] [OSKinetis例程]K60实现ADC四通道自动连续采集 无需CPU干预

[复制链接]
回帖奖励 69 金钱 回复本帖可获得 1 金钱奖励! 每人限 2 次(中奖概率 50%)

4

主题

218

帖子

1720

积分

超级版主

Rank: 8Rank: 8

积分
1720
发表于 2013-10-30 21:24:06 | 显示全部楼层 |阅读模式
本帖最后由 洋葱圈 于 2013-10-31 15:05 编辑

之前有许多朋友询问如何使用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[A]和R[B]结果寄存器的值。
Step 3.配置PDB模块,使用软件触发或其他外设触发均可,例程中位软件触发。关键步骤是要配置PDB的ADC预触发模式,使能通道0和通道1的预触发,并配置为Back to Back模式,这样才能让ADC的COCO转换完成标志自动触发下一个AD转换!
Step 4.触发PDB工作,坐享其成!

主要代码如下:
[C] 纯文本查看 复制代码
  //**********************************************
  //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[0]);       //源地址: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[0]);       
  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[0]);
    printf("  ADC0_RB=%d\r\n", Result[1]);
    printf("    ADC1_RA=%d\r\n", Result[2]);
    printf("      ADC1_RB=%d\r\n", Result[3]);
  } 

运行结果:




例程下载(回复可见):
游客,如果您要查看本帖隐藏内容请回复

务必注意:本例程只是一个用户代码,它必须基于OSKinetis固件库来运行,因此必须放置在固件库目录下的/project目录才能正确编译,固件库地址 http://www.lpld.cn/?p=97

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?成为LPLD会员

x
回复

使用道具 举报

0

主题

4

帖子

29

积分

新手上路

Rank: 1

积分
29
发表于 2014-4-29 17:20:57 | 显示全部楼层

回帖奖励 +1 金钱

qingyun 发表于 2014-4-27 21:38
兄弟,可不可以说一下在哪找的,谢谢啦!

今天使用PIT+pdb+adc+DMA实现了单个ADC,即ADC0分时采集3个通道,无需CPU干涉的功能,我想应该能满足你的需求,现将main文件共享给与,使用的是LPLD的(DMA PDB ADC)LPLD_DmaPdbAnalogSample这个例程改的,直接把C文件换一下就行了!


/**
* --------------基于"拉普兰德K60底层库V3"的工程(LPLD_DmaPdbAnalogSample)-----------------
* @file LPLD_DmaPdbAnalogSample.c
* @version 0.1
* @date 2013-9-29
* @brief 利用DMA+PDB+ADC,实现非CPU干预下的ADC采集。
*
* 版权所有:北京拉普兰德电子技术有限公司
* http://www.lpld.cn
* mail:support@lpld.cn
* 硬件平台:  LPLD K60 Card / LPLD K60 Nano
*
* 本工程基于"拉普兰德K60底层库V3"开发,
* 所有开源代码均在"lib"文件夹下,用户不必更改该目录下代码,
* 所有用户工程需保存在"project"文件夹下,以工程名定义文件夹名,
* 底层库使用方法见相关文档。
*
*/
#include "common.h"
/****************************************
说明:
   *使用RUSH Kinetis开发板上的电位器R7
    作为模拟量调节器。
   *将MiniUSB线插入RUSH Kinetis开发板的USB
    插座,并连接至电脑USB接口。
   *使用串口调试助手波特率设置为115200
   *使用串口调试助手查看运行结果。
   *通过调节电位器的旋钮查看运行结果。
****************************************/
//模块初始化结构体定义
PDB_InitTypeDef pdb_init_struct;
ADC_InitTypeDef adc0_init_struct;
PIT_InitTypeDef pit0_init_struct;
DMA_InitTypeDef dma_init_struct;
DMA_InitTypeDef dma_init_struct1;

uint16 Result[30];
uint8 Source1[10]={1,2,3,4,5,6,7,8,9,10};
uint8 Result1;
volatile uint8  uc_adc_mux[6]= {DAD0,DAD1,AD14};   
//函数声明
void adc0_isr();
void dma0_isttest(void);
void main (void)
{
  GPIO_InitTypeDef gpio_init_struct;
  
  gpio_init_struct.GPIO_PTx = PTE;
  gpio_init_struct.GPIO_Pins = GPIO_Pin2;
  gpio_init_struct.GPIO_Dir = DIR_OUTPUT;
  gpio_init_struct.GPIO_Output = OUTPUT_H;
  gpio_init_struct.GPIO_PinControl = IRQC_DIS;
  LPLD_GPIO_Init(gpio_init_struct);  
  
  
  //1.配置ADC采样参数
  //初始化ADC0模块的A组通道
  adc0_init_struct.ADC_Adcx = ADC0;     //选择ADC0
  adc0_init_struct.ADC_BitMode = SE_12BIT;      //配置转换精度
  adc0_init_struct.ADC_CalEnable = TRUE;        //使能初始化自动校准
  adc0_init_struct.ADC_HwTrgCfg = HW_TRGA;      //配置A组为硬件触发,即由PDB触发开始转换
  adc0_init_struct.ADC_DmaEnable = TRUE;        //使能DMA
  //初始化ADC0
  LPLD_ADC_Init(adc0_init_struct);   
  //使能ADC0的DAD1输入引脚复用功能
  LPLD_ADC_Chn_Enable(ADC0, AD14);
  LPLD_ADC_Chn_Enable(ADC0, DAD0);
  LPLD_ADC_Chn_Enable(ADC0, DAD1);
  
  //2.配置DMA
  //初始化PDB工作参数
  dma_init_struct.DMA_CHx = DMA_CH0;   //使用Ch0通道
  dma_init_struct.DMA_Req = ADC0_DMAREQ;        //DMA请求源为ADC0
  dma_init_struct.DMA_MajorLoopCnt = 30;        //主循环计数1000次
  dma_init_struct.DMA_MinorByteCnt = 2; //次循环传输字节计数(由于ADC采样为12位,因此传输2字节)
  dma_init_struct.DMA_SourceAddr = (uint32)&(ADC0->R[0]);       //源地址:ADC0结果寄存器A地址
  dma_init_struct.DMA_SourceDataSize = DMA_SRC_16BIT;   //源地址传输数据宽度16位
  dma_init_struct.DMA_DestAddr = (uint32)&Result;       //目的地址
  dma_init_struct.DMA_DestDataSize = DMA_DST_16BIT;     //目的地址传输数据宽度16位
  dma_init_struct.DMA_DestAddrOffset = 2;
  dma_init_struct.DMA_LastDestAddrAdj = -60;
  dma_init_struct.DMA_MajorCompleteIntEnable = TRUE;//使能DMA传输完成中断
  dma_init_struct.DMA_Isr = dma0_isttest;           //DMA中断回调函数
  dma_init_struct.DMA_AutoDisableReq = FALSE;   //禁用自动禁用请求,即不受主循环计数计数限制
  //初始化DMA
  LPLD_DMA_Init(dma_init_struct);
  //使能DMA中断
  LPLD_DMA_EnableIrq(dma_init_struct);
  //使能DMA请求
  LPLD_DMA_EnableReq(DMA_CH0);
  
  dma_init_struct1.DMA_CHx = DMA_CH1;   //使用Ch0通道
  dma_init_struct1.DMA_Req = PDB_DMAREQ;        //DMA请求源为ADC0
  dma_init_struct1.DMA_MajorLoopCnt = 3;        //主循环计数1000次
  dma_init_struct1.DMA_MinorByteCnt = 1; //次循环传输字节计数(由于ADC采样为12位,因此传输2字节)
  dma_init_struct1.DMA_SourceAddr = (uint32)&uc_adc_mux[0];         //源地址:ADC0结果寄存器A地址
  dma_init_struct1.DMA_SourceDataSize = DMA_SRC_8BIT;   //源地址传输数据宽度16位
  dma_init_struct1.DMA_SourceAddrOffset =1;
  dma_init_struct1.DMA_LastSourceAddrAdj = -3;
  dma_init_struct1.DMA_DestAddr = (uint32)&(ADC0->SC1[0]);//&Result1;       //目的地址  &ADC0_SC1A
  dma_init_struct1.DMA_DestDataSize = DMA_DST_8BIT;     //目的地址传输数据宽度16位
  dma_init_struct1.DMA_DestAddrOffset = 0;
  dma_init_struct1.DMA_LastDestAddrAdj = 0;
  dma_init_struct1.DMA_AutoDisableReq = FALSE;   //禁用自动禁用请求,即不受主循环计数计数限制
  //初始化DMA
  LPLD_DMA_Init(dma_init_struct1);
  //使能DMA请求
  LPLD_DMA_EnableReq(DMA_CH1);
  
  //3.配置PDB触发参数
  //初始化PDB工作参数
  pdb_init_struct.PDB_CounterPeriodS = 6;   //PDB计数器周期设置
  pdb_init_struct.PDB_LoadModeSel = LOADMODE_0; //加载模式设置
  pdb_init_struct.PDB_ContinuousModeEnable = FALSE;    //使能连续工作模式,即只需要开始触发一次,以后PDB就会连续工作
  pdb_init_struct.PDB_TriggerInputSourceSel = TRIGGER_PIT0;     //配置触发源为PIT0
  pdb_init_struct.PDB_DelayMs = 1;//延时1ms
  pdb_init_struct.PDB_DmaEnable = TRUE;//pdb的DMA使能
  //初始化PDB
  LPLD_PDB_Init(pdb_init_struct);
  //配置PDB触发ADC参数:触发ADC0模块、使能A组通道预触发、触发延时0
  LPLD_PDB_AdcTriggerCfg(ADC0, PRETRIG_EN_A, 0);
  
  //4.配置PIT触发周期
  //初始化PIT参数
  pit0_init_struct.PIT_Pitx = PIT0;     //选择PIT0
  pit0_init_struct.PIT_PeriodMs = 10;  //PIT0计数周期30us
  //初始化PIT0
  LPLD_PIT_Init(pit0_init_struct);  
//  PIT_Start(PIT0);
  while(1)
  {
    printf("Reslut=%d\r\n", Result);
  }
}
void dma0_isttest(void)
{
  LPLD_GPIO_Toggle_b(PTE, 2);
}

回复 支持 1 反对 0

使用道具 举报

1

主题

14

帖子

91

积分

注册会员

Rank: 2

积分
91
发表于 2014-5-13 16:09:23 | 显示全部楼层
学习学习,赞一个。
回复 支持 1 反对 0

使用道具 举报

4

主题

8

帖子

37

积分

新手上路

Rank: 1

积分
37
发表于 2013-10-30 22:14:54 | 显示全部楼层
有一个问题请求解释: ADC是最多4通道同时采集嘛?如果我的传感器模拟量输入超过4个是不是必须要先后采样转换了么...?

4

主题

218

帖子

1720

积分

超级版主

Rank: 8Rank: 8

积分
1720
 楼主| 发表于 2013-10-30 22:29:20 | 显示全部楼层
RT_holmes 发表于 2013-10-30 22:14
有一个问题请求解释: ADC是最多4通道同时采集嘛?如果我的传感器模拟量输入超过4个是不是必须要先后采样转 ...

是最多4个通道自动连续采集,不是只有4个通道。

4

主题

218

帖子

1720

积分

超级版主

Rank: 8Rank: 8

积分
1720
 楼主| 发表于 2013-10-30 22:29:21 | 显示全部楼层
RT_holmes 发表于 2013-10-30 22:14
有一个问题请求解释: ADC是最多4通道同时采集嘛?如果我的传感器模拟量输入超过4个是不是必须要先后采样转 ...

是最多4个通道自动连续采集,不是只有4个通道。

0

主题

3

帖子

38

积分

新手上路

Rank: 1

积分
38
发表于 2013-10-31 10:45:45 | 显示全部楼层

回帖奖励 +1 金钱

很好 新手用的容易

2

主题

6

帖子

28

积分

新手上路

Rank: 1

积分
28
发表于 2013-11-1 11:39:40 | 显示全部楼层

回帖奖励 +1 金钱

支持LPLD版主
回复 支持 反对

使用道具 举报

0

主题

1

帖子

19

积分

新手上路

Rank: 1

积分
19
QQ
发表于 2013-11-3 20:09:48 | 显示全部楼层

回帖奖励 +1 金钱

顶一个,不错。
回复 支持 反对

使用道具 举报

4

主题

12

帖子

79

积分

注册会员

Rank: 2

积分
79
发表于 2013-11-4 12:50:59 | 显示全部楼层
PDB触发ADC会不会影响1msPIT中断?
回复 支持 反对

使用道具 举报

4

主题

218

帖子

1720

积分

超级版主

Rank: 8Rank: 8

积分
1720
 楼主| 发表于 2013-11-4 14:47:25 | 显示全部楼层
jayxing26 发表于 2013-11-4 12:50
PDB触发ADC会不会影响1msPIT中断?

例程中没有用到PIT模块。
回复 支持 反对

使用道具 举报

4

主题

12

帖子

79

积分

注册会员

Rank: 2

积分
79
发表于 2013-11-4 20:44:14 | 显示全部楼层

回帖奖励 +1 金钱

洋葱圈 发表于 2013-11-4 14:47
例程中没有用到PIT模块。

就是问下如果我PIT设置了1ms中断,然后使用PDB触发ADC,这样是不是会有中断冲突?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 成为LPLD会员

本版积分规则

    Archiver|手机版|小黑屋|

GMT+8, 2017-6-26 08:09 , Processed in 0.080138 second(s), 24 queries .

© 2001-2011 Powered by Discuz! X3.1. Theme By Yeei!

快速回复 返回顶部 返回列表