打印
[技术问题解答]

KL25 ADC+DMA配合

[复制链接]
楼主: ianhom
手机看帖
扫描二维码
随时随地手机跟帖
41
ianhom 发表于 2015-1-26 21:09
感谢回复!问题在于DMA的BCR计数减为零后,如何在没有代码干预,也没有中断的操作下重新给BCR赋值,让DMA ...

哥们,现在我也在纠结这个问题,如果没有外界触发,是不是使能DMA后,就只能转移一次,不能像STM32那样,使能后,无限往目的地址传数据呢,一定要再次触发DMA吗?请问楼主的这个问题现在解决了么,求分享经验

使用特权

评论回复
42
ianhom|  楼主 | 2015-7-3 13:46 | 只看该作者
lhaven 发表于 2015-7-3 11:58
哥们,现在我也在纠结这个问题,如果没有外界触发,是不是使能DMA后,就只能转移一次,不能像STM32那样, ...

到目前为止DMA的中断还是要进入的,重装BCR值。不过能省去每次采样完的ADC中断,中断次数能大大减少

使用特权

评论回复
43
lhaven| | 2015-7-3 14:00 | 只看该作者
ianhom 发表于 2015-7-3 13:46
到目前为止DMA的中断还是要进入的,重装BCR值。不过能省去每次采样完的ADC中断,中断次数能大大减少 ...

感谢回复,那我要用ADC的新的数据之前,是不是需要 在下面那个函数中 重新查询一次COCO的标志,或者更新一下  目标地址,然后再从目标地址取出数据,这样就是新的数据了,是吗?
if((DMA_DSR_BCR(n) & DMA_DSR_BCR_DONE_MASK)== DMA_DSR_BCR_DONE_MASK)
          {
               DMA_DSR_BCR(n) |= DMA_DSR_BCR_DONE_MASK;                  
               
               DMA_DAR(n) = D_ADDR;   //这里更新一下目标地址
               DMA_DSR_BCR(n) = DMA_DSR_BCR_BCR(SIZE);  //重载BCR
          }

使用特权

评论回复
44
ianhom|  楼主 | 2015-7-3 14:17 | 只看该作者
有点没明白,我实现的代码是定时器周期触发ADC采样,采样完成后自动触发DMA0转移数据,然后自动触发DMA1修改ADC采样通道,等待下次定时器触发重复上述过程。上述过程不会进任何中断,数据会按顺序放到内存中。唯一的中断是在BCR归零(上述过程已经发生N多次)后才会进入一次DMA中断。关于代码的说明我放在main函数的printf的一个链接,可以看一下

使用特权

评论回复
45
lhaven| | 2015-7-3 17:41 | 只看该作者
ianhom 发表于 2015-7-3 14:17
有点没明白,我实现的代码是定时器周期触发ADC采样,采样完成后自动触发DMA0转移数据,然后自动触发DMA1修 ...

非常感谢楼主热心解答,问题已解决,虽然是要重新触发一下DMA才能转移数据,但是比直接读取ADC,真的大大缩减了时间,只不过我发现,在给BCR重载之前,还要重新使能一下DMA,不知道这个是不是问题

使用特权

评论回复
46
ianhom|  楼主 | 2015-7-3 17:52 | 只看该作者
貌似没有使能,直接请标志,重装BCR
void DMA1_Irq(void)
{
    DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK; /* DMA0 通道DONE标志清零 */
    DMA_DSR_BCR0 |= DMA_DSR_BCR_BCR(sg_dwDmaCnt); /* DMA0 通道计数重载 */
    DMA_DSR_BCR1 |= DMA_DSR_BCR_DONE_MASK; /* DMA1 通道DONE标志清零 */
    DMA_DSR_BCR1 |= DMA_DSR_BCR_BCR(sg_dwDmaCnt/2); /* DMA1 通道计数重载 */
    printf ("When you see this, DMA is reloaded!\n");
}

使用特权

评论回复
47
尤彼卡| | 2015-7-3 19:30 | 只看该作者
通常是在DMA的结束中断中重新更新目的地址实现再一次的DMA loop

使用特权

评论回复
48
ianhom|  楼主 | 2015-7-4 08:57 | 只看该作者
尤彼卡 发表于 2015-7-3 19:30
通常是在DMA的结束中断中重新更新目的地址实现再一次的DMA loop

启用了循环buf还需要更新目的地址吗

使用特权

评论回复
49
小狗爱吃骨头| | 2015-7-5 20:42 | 只看该作者
哈哈,谢谢楼主的无私分享

使用特权

评论回复
50
lhaven| | 2015-7-6 10:24 | 只看该作者
本帖最后由 lhaven 于 2015-7-6 10:39 编辑
ianhom 发表于 2015-7-3 17:52
貌似没有使能,直接请标志,重装BCR
void DMA1_Irq(void)
{

楼主,我现在还有个疑惑,应该是我对DMA还不了解吧,拿你分享的例子来说,
源地址数据size :DMA_DCR_SSIZE(SIZE_16_BIT) ,
目地址数据size :DMA_DCR_DSIZE(SIZE_16_BIT)   ,这两个size设置的是2个字节的,
然后DMA_DSR_BCR0 = DMA_DSR_BCR_BCR(dwDmaCnt); 这个设置的是传输的总字节数,假如传输的ADC数据也是2个字节,16位的,所以一次传输的数据个数共有 dwDmaCnt/2个,这样理解对吗?

如果对,那这是ADC采集一次的数据量,还是说ADC共采集了 dwDmaCnt/2 次 呢?我就是想要连续采集10次的数据,把这10次的数据存起来,看看这10次的数据是什么,该怎么实现呢,现在感觉有点乱,不知道咋回事,早复为盼

使用特权

评论回复
51
ianhom|  楼主 | 2015-7-6 11:00 | 只看该作者
dwDmaCnt是转移的总字节数,ADC采样的数据是16位2字节,所以总的ADC结果就是dwDmaCnt/2

比如我想保存10个ADC数据,这10个数据都来自同一个ADC通道,这十个数据放依次放到某个数组中,那这个dwDmaCnt就是20

要实现上述效果注意配置目的地址自动增加

使用特权

评论回复
52
lhaven| | 2015-7-6 11:06 | 只看该作者
本帖最后由 lhaven 于 2015-7-6 11:10 编辑
ianhom 发表于 2015-7-6 11:00
dwDmaCnt是转移的总字节数,ADC采样的数据是16位2字节,所以总的ADC结果就是dwDmaCnt/2

比如我想保存10个 ...

对的,现在的现象就是如你所说,但是这个是指,ADC一下子采集了10次?我测了一下dwDmaCnt=256,即采集128个数据,存在buff[128]中,时间才20多us,感觉不太对,

还有就是,
static uint16 *pwAdData_Original = NULL;   /* 动态申请的用于AD数据缓冲区指针,用于free */
static uint16 *pwAdData_Align    = NULL;   /* 动态申请的用于AD数据缓冲区指针,已对齐   */
static uint8  *pucAdChl_Original = NULL;   /* 动态申请的用于AD通道缓冲区指针,用于free */
static uint8  *pucAdChl_Align    = NULL;   /* 动态申请的用于AD通道缓冲区指针,已对齐   */
这四句的定义我不懂,这个缓冲区是多大的呢,我在我的程序里是用数组的,假如想要10个数据,我就定义数据为buff[20],是这样吗

使用特权

评论回复
53
ianhom|  楼主 | 2015-7-6 11:36 | 只看该作者
我的程序里是用TPM定时触发ADC采样的,ADC采样完成后自动触发DMA0转移2个字节的数据,数据转移完以后,自动触发DMA1对ADC进行通道切换,然后一直等下一次TPM定时触发ADC........

如果说你一下转移了256个字节但是时间很短,你看看这128个数据是不是完全一样的,有可能是你的adc转换完了,但是你的DMA一直被其他触发源(不是ADC)触发着,所以短时间内转移了ADC的相同数据128次,以上只是猜测

那四句定义是我为了申请一个地址对齐的缓冲区用的,通用性不好,建议用下面这个方法
#pragma data_alignment = 1024                             /* 下一行变量将定义在1024字节对齐的地址上 */
static unsigned char sg_aucAdData[1024]  = {0};   /* sg_aucAdData为DMA循环buffer的地址         */

之所以要这么做,是因为我的数据会循环存放到我的缓冲中,数据存满后自动重头覆盖,这个功能可以使用KL25的DMA中循环buff实现,不过有个限制条件,就是缓冲区的首地址必须是对齐的,比如我的循环缓冲有1024个字节,那缓冲区(数组)的首地址必须是1024字节对齐,即地址为0xXXXXX000,0xXXXXX400,0xXXXXX800,0xXXXXXC00

使用特权

评论回复
54
lhaven| | 2015-7-6 12:13 | 只看该作者
ianhom 发表于 2015-7-6 11:36
我的程序里是用TPM定时触发ADC采样的,ADC采样完成后自动触发DMA0转移2个字节的数据,数据转移完以后,自动 ...

非常感谢你的解答,我的采集函数也是在TPM中断里面进行的,为什么你上面说“ADC采样完成后自动触发DMA0转移2个字节的数据”,怎么才转移两个字节呢,你在这里设置的是0xFFFC0啊,不应该是转移了0xfffC0个字节吗?还是我哪里理解错了呢
DMA_Init((void*)(&ADC0_RB),          /* AD数据源地址                          */
             (void*)(pwAdData_Align),    /* AD数据存放目的地址                    */
             (void*)(pucAdChl_Align),    /* AD通道列表源地址                      */
             (void*)(&ADC0_SC1B),        /* AD通道选择目的地址                    */
             0xFFFC0);                    /* 最大转移数量,减少DMA中断次数         */

因为我测的电压是不变的,我上面说的,一下子转移了128个数据,这128个数据,是相近而不同的,数据也是对的,所以就是不明白,这样子就算采集了128次?还是只采集了一次?如果不用DMA的话,貌似采集一次需要2~3us,而我现在采集128个数据的时间次20us,ADC也是使能了硬件滤波的,感觉好奇怪,一下子省了这么多时间,有点怀疑,主要就是不理解,转移了256个字节,是不是就指采集了128次?

使用特权

评论回复
55
ianhom|  楼主 | 2015-7-6 13:35 | 只看该作者
DMA转移了128次不代表ADC采样了128次,DMA只是转移了数据。不知道你要的效果是不是ADC采样结束后才触发DMA转移一次数据,然后等下一次ADC采样完成后再次触发DMA转移

为什么是2个字节?我的程序里应该是TPM周期触发ADC转换,转换完成后有一个有效的ADC结果,这个时候ADC才会触发DMA转移一次ADC结果,所以是2个字节,随后DMA不再转移,一直等到TPM定时周期结束,再次触发ADC,ADC再次完成后才会再次触发DMA转移2个字节,上述的过程转移了4个字节,在上述过程中不会进入任何一个中断,都是由硬件完成,当所上述的过程不断重复,总的转移字节数达到0xFFFC0后,TPM+ADC+DMA的过程也会重复0xFFFC0/2次,就是说转移2个字节的过程会重复0xFFFCO/2次,然后才会进入一次DMA中断,重装如这个0xFFFC0这个数字。

不知道你有没有看过我博客里的说明,可以看一下,有的地方可能没有写清楚,欢迎提问

使用特权

评论回复
56
芙蓉洞| | 2015-7-6 14:33 | 只看该作者
谢谢楼主的分享,非常棒的资料

使用特权

评论回复
57
lhaven| | 2015-7-6 16:58 | 只看该作者
ianhom 发表于 2015-7-6 13:35
DMA转移了128次不代表ADC采样了128次,DMA只是转移了数据。不知道你要的效果是不是ADC采样结束后才触发DMA ...

哈哈,楼主厉害,非常感谢楼主耐心的解答,另博客写的very good!
我是在中断里ADC采集的,转换完后就触发DMA传输一次,循环64次,也就是采集64个数据,DMA也转移64次,因为缓冲区是128的,也没用DMA中断,所以在此刻重载下BCR就可以了,现象终于对了,有一点不明白,为什么ADC0_SC3不能使能,即硬件滤波不能用,是因为硬件滤波会导致ADC持续转换的原因吗?

使用特权

评论回复
58
ianhom|  楼主 | 2015-7-6 20:17 | 只看该作者
”ADC0_SC3使能“和”硬件滤波“是指开启硬件求平均功能吗,我这边使用起来貌似没有问题,ADC转换完成标志COCO要在N次采样完成并求完平均后才会被置上。关于更多ADC的配置,论坛里有很多大神的帖子有详细说明。

使用特权

评论回复
59
lhaven| | 2015-7-7 10:49 | 只看该作者
ianhom 发表于 2015-7-6 20:17
”ADC0_SC3使能“和”硬件滤波“是指开启硬件求平均功能吗,我这边使用起来貌似没有问题,ADC转换完成标志C ...

感谢回复,是的,开启硬件平均滤波的时候是不是只要使能这两位就行了呢,ADC_SC3_AVGE_MASK | ADC_SC3_AVGS,只打开这两句没有问题,但如果我把持续转换的这个寄存器也打开的话ADC_SC3_ADCO_MASK,就不行了

另外我怎么没看到你的程序有类似这样的判断呢while(!(ADC0_SC1A & ADC_SC1_COCO_MASK));我发现加了这个COCO的判断后,所需的时间大大增加了,按理说判断了COCO置1后,才表明转换完成啊

使用特权

评论回复
60
ianhom|  楼主 | 2015-7-7 16:46 | 只看该作者
ADC的这个问题我还没有研究过,请各位大神帮忙解惑。

我没有开启TPM和ADC中断,我设置DMA的硬件触发源是ADC,所以在ADC转换完成的时候,DMA会自动把ADC结果放到指定的内存中。所以我的代码里没有对COCO的判断。我代码的本意就是希望ADC完全后台处理,不需要任何中断或软件干预,自动切换ADC通道并转移数据。可惜BCR不能自动重载,必须要DMA中断一次重载BCR。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则