This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
如题,最近在调试msp430f5438a的dma,今天发现一个bug???
如果把下面的语句屏蔽掉,则dma工作正常,如果不屏蔽,则dma就死了,永远不中断了。
//uint8_t j=0;
//for(j=0;j<128;j++)
//{
// while(!(ADC12IFG & BIT0))//等待采样结束
// {
// ;
// }
// DMA_DST_AD0[j] = ADC12MEM0;// Move A1 results, IFG is cleared
//}
具体见程序:
__no_init uint16_t DMA_DST_AD0[2048]; int main(void) { WDTCTL = WDTPW+WDTHOLD; // Stop WDT //ADC设置 P6SEL |=BIT0; // Enable VeREF+ & A/D channel A0 ADC12CTL0 = ADC12ON // 打开ADC内核 +ADC12MSC // 连续采样 +ADC12SHT0_2; // 保持时间为16个cycles ADC12CTL1 = ADC12SSEL_2 // 时钟选择为:MCLK +ADC12DIV_4 // 时钟分频为:4+1=5,即25M/5=5MHz +ADC12SHP // 采样信号源自采样定时器 +ADC12CONSEQ_2; // 单通道多次采样 ADC12MCTL0 = ADC12INCH_0+ADC12EOS; // ref+=AVcc, channel = A0 ADC12CTL0 |= ADC12ENC; // Enable conversions ADC12CTL0 |= ADC12SC; // Start conversion - software trigger ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //如果把下面的这些语句屏蔽掉,dma读取ad值正常,但是放开这些语句,即先 //直接读一次ad值后再用dma读,此时dma就永远不触发了,这是怎么回事? ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //uint8_t j=0; //for(j=0;j<128;j++) //{ // while(!(ADC12IFG & BIT0))//等待采样结束 // { // ; // } // DMA_DST_AD0[j] = ADC12MEM0;// Move A1 results, IFG is cleared //} //DMA0设置 //触发源选择 DMACTL0 = DMA0TSEL_24; // 通道0 ADC12IFGx triggered //DMA0通道参数设置 DMA0CTL = 0; // 寄存器清零 DMA0CTL = DMADT_4 // 单次重复传输模式 +DMAEN // 使能DMA +DMADSTINCR_3 // 目标地址自增,源地址不变 +DMAIE; // 使能DMA传输完成中断 DMA0SZ = 2048; // DMA0传输大小 __data16_write_addr((uint16_t) &DMA0SA,(uint32_t) &ADC12MEM0); //源地址:ADC12MEM0 __data16_write_addr((uint16_t) &DMA0DA,(uint32_t) &DMA_DST_AD0);//目的地址:DMA_DST[] __bis_SR_register(GIE);//enable interrupts while(1); } //------------------------------------------------------------------------------ // DMA Interrupt Service Routine //------------------------------------------------------------------------------ #pragma vector=DMA_VECTOR __interrupt void DMA_ISR(void) { switch(__even_in_range(DMAIV,16)) { case 0: break; case 2: // DMA0IFG = DMA Channel 0 __no_operation(); break; case 4: break; // DMA1IFG = DMA Channel 1 case 6: break; // DMA2IFG = DMA Channel 2 case 8: break; // DMA3IFG = DMA Channel 3 case 10: break; // DMA4IFG = DMA Channel 4 case 12: break; // DMA5IFG = DMA Channel 5 case 14: break; // DMA6IFG = DMA Channel 6 case 16: break; // DMA7IFG = DMA Channel 7 default: break; } }
你好,
你的问题我已经收藏记录下来,因为手上暂时没有MSP430F5438A demo板,我先尝试在MSP430F5529下跑一下你的程序。
有结果通知你。
楼主,
我对你的程序有点疑问,你既然采用的是DMA硬件触发ADC12MEM0数据的传输,为什么还需要手动去进行一次读取的操作呢。你这样一读取的话,ADC12MEM0里面的数据就被清楚了,当然触发不了DMA了。硬件触发DMA的原理就是ADC12MEM0里面有了转换完成的结果就会自动触发DMA将数据从ADC12MEM0搬到DMA_DST_AD0数组里面。其中间过程不需要手动操作或是CPU的干预。
谢谢
1、首先感谢回复啊,这帖子好多天了都没有TI员工来回复,今天终于等到一个。
2、5438a我用了ad0,ad1,ad2分别采集3个模拟量,前两个要采集传感器,我想用dma去读数据。
后面一个通道采集电池电量,当系统待机时才采集,我想直接读mem寄存器数据后求平均处理。
上面的程序是为了演示dma手动读一次后dma再去读会读不到这个现象。
我实际测试时,先把ad初始化,开启3个通道,序列通道重复采样模式。再用ad2读了一次电池电量,
后才初始化dma,使ad0 ad1的数据让dma0 dma1去读,这样dma就死活不触发了,dma有关联性?
3个dma通道不独立???我手动读了一次ad2的数据,和ad0 ad1的数据有什么关系???
3、按照我发的程序来讨论:我们先不谈为什么要用dma了,还要手动去读一次数据。首先我把ad初始化了,
手动去读一次数据,然后再初始化dma,让dma去读ad的数据。在这个过程中,我手动读过一次ad数据
后,ifg标志置位了,我也清零了。这个总该不影响dma吧。再者你说mem中的数据读没了,dma就不触
发了?????????
1)从仿真来看,虽然dma不触发了,但是ad的数据是变化的,即说明ad工作正常,新的数据也放到了mem
寄存器,ifg标志也置位了,你dma为什么还不触发,再等什么???
2)即使我在dma初始化前再把ad初始化一次也不管用,dma照样不触发。
3)以上两点说明dma没有触发和我先读一次ad数据没有关系
4、用户的需求是千奇百怪的,总不能我要用dma了,那这个ad的数据寄存器我都不能读了吧???
有可能我一段时间用dma去读ad数据,过段时间我又得读寄存器去判断ad值。。。。什么需求都
有可能发生的。
5、我要知道为什么我读了一次ad值后dma就不触发了呢?
谢谢回复
我先读一次ad数据,在dma初始化前再初始化一次ad,dma还是不触发
P6SEL |=BIT0; // Enable VeREF+ & A/D channel A0 ADC12CTL0 = ADC12ON // 打开ADC内核 +ADC12MSC // 连续采样 +ADC12SHT0_2; // 保持时间为16个cycles ADC12CTL1 = ADC12SSEL_2 // 时钟选择为:MCLK +ADC12DIV_4 // 时钟分频为:4+1=5,即25M/5=5MHz +ADC12SHP // 采样信号源自采样定时器 +ADC12CONSEQ_2; // 单通道多次采样 ADC12MCTL0 = ADC12INCH_0+ADC12EOS; // ref+=AVcc, channel = A0 ADC12CTL0 |= ADC12ENC; // Enable conversions ADC12CTL0 |= ADC12SC; // Start conversion - software trigger //DMA0设置 //触发源选择 DMACTL0 = DMA0TSEL_24; // 通道0 ADC12IFGx triggered //DMA0通道参数设置 DMA0CTL = 0; // 寄存器清零 DMA0CTL = DMADT_4 // 单次重复传输模式 +DMAEN // 使能DMA +DMADSTINCR_3 // 目标地址自增,源地址不变 +DMAIE; // 使能DMA传输完成中断 DMA0SZ = 2048; // DMA0传输大小 __data16_write_addr((uint16_t) &DMA0SA,(uint32_t) &ADC12MEM0); //源地址:ADC12MEM0 __data16_write_addr((uint16_t) &DMA0DA,(uint32_t) &DMA_DST_AD0);//目的地址:DMA_DST[] __bis_SR_register(GIE);//enable interrupts
在dma初始化前我这样再初始化ad一次,dma就可以正常触发了。
//ADC设置 ADC12CTL0=0; ADC12CTL1=0; ADC12MCTL0=0; P6SEL |=BIT0; // Enable VeREF+ & A/D channel A0 ADC12CTL0 = ADC12ON // 打开ADC内核 +ADC12MSC // 连续采样 +ADC12SHT0_2; // 保持时间为16个cycles ADC12CTL1 = ADC12SSEL_2 // 时钟选择为:MCLK +ADC12DIV_4 // 时钟分频为:4+1=5,即25M/5=5MHz +ADC12SHP // 采样信号源自采样定时器 +ADC12CONSEQ_2; // 单通道多次采样 ADC12MCTL0 = ADC12INCH_0+ADC12EOS; // ref+=AVcc, channel = A0 ADC12CTL0 |= ADC12ENC; // Enable conversions ADC12CTL0 |= ADC12SC; // Start conversion - software trigger //DMA0设置 //触发源选择 DMACTL0 = DMA0TSEL_24; // 通道0 ADC12IFGx triggered //DMA0通道参数设置 DMA0CTL = 0; // 寄存器清零 DMA0CTL = DMADT_4 // 单次重复传输模式 +DMAEN // 使能DMA +DMADSTINCR_3 // 目标地址自增,源地址不变 +DMAIE; // 使能DMA传输完成中断 DMA0SZ = 2048; // DMA0传输大小 __data16_write_addr((uint16_t) &DMA0SA,(uint32_t) &ADC12MEM0); //源地址:ADC12MEM0 __data16_write_addr((uint16_t) &DMA0DA,(uint32_t) &DMA_DST_AD0);//目的地址:DMA_DST[] __bis_SR_register(GIE);//enable interrupts
楼主,
首先在430里面DMA硬件触发ADC的结果转换,它的trigger resource是DMACTL0 = DMA0TSEL_24;//ADC12IFGx也就是说DMA是在ADC转换结束以后置位的标识符来触发DMA。如果说你手动去读的话,清掉了这个标志位,那DMA就不能硬件触发.
考虑到你的程序里里面用了两个DMA通道,而且ADC采用的是sequence的方式,在这种情况下ADC12IFGx的置位是在sequence complete的情况下才去置位的。换句话说,只有在这种情况下你的DMA操作才能被触发。你去读数据可以根据你的需求,什么时候读都可以,单前提是如果你需要DMA按你的需求工作的话,你的保证有ADC12IFGx的标志位去触发它。不然DMA 的硬件触发还真没法工作。
还有一种解决办法,就是选择触发源为DMA,采用类似于软件触发的操作。通过在程序中去设置DMAREQ这个位来触发DMA的操作。这样就和ADC12IFGx的标志位没任何关系了。什么时候你想用DMA都行,但有一个小小的问题,就是要等ADC12MEMx寄存器里面有数据。
最后总结一下,客户的需求确实多变的,关键是要看程序怎么设计以及相应的硬件有哪些功能可以支持。
谢谢
楼主,
关于ADC初始化方式的问题,你可以通过在线调试的方式,比较一下这两种操作过后,ADC相应的寄存器里面的值是否是真的都处于reset状态了。这样比较直观的看出你的初始化操作是否有效。
谢谢
楼主,
你两次初始化的ADC操作,前面一次应该是没有成功,因为你在ADC12EN=1的情况下是修改不了ADC12CTL0这个寄存器的,只有在将ADC12EN清零以后,针对ADC12CTL0的操作才是有效的。
谢谢
1、楼主位的程序可以很好说明我的问题。现在拿他来说明问题。到目前为止大家好像还没明白我的问题根源之所在。
我只用了ad0,是单通道重复采样,即ad0转换完毕后ADC12IFG0位置会被置位。虽然我在dma初始化前读了一次
ADC12MEM0寄存器,当然ADC12IFG0标志也会被自动清零;但是我用的是单通道重复采样,当dma初始化完毕
后,我仿真发现ADC12MEM0寄存器的内容是新的,即ad0在采集数据,ADC12IFG0标志也被置位了,但是此时
dma还是没有触发。ADC12MEM0寄存器有新的内容了,ADC12IFG0标志也置位了,你为什么还不触发。
2、ls可以把我的楼主位的程序放到你的开发板上仿真试一试就知道我要说明什么问题了。你先屏蔽我那段,仿真看看能否进
中断,即dma是否在工作。再把屏蔽的那段放开,看看dma还工作吗?
谢谢
xtaens,
刚测试了一下,前面确实如你所说,不加那段程序,DMA是工作正常的,但是加了那段程序以后,DMA就不在触发了。
但是现在我发现一个有趣的现象,如果我们LPM0这个去掉的话,即使加上那段程序,DMA还是能一直触发的。参考程序如下
//******************************************************************************
#include <msp430.h>
unsigned int DMA_DST;
unsigned int DMA_DST_AD0;
unsigned char flag=0;
int main(void)
{
WDTCTL = WDTPW+WDTHOLD; // Hold WDT
P1OUT &= ~BIT0; // P1.0 clear
P1DIR |= BIT0; // P1.0 output
// P4DIR |= BIT1; // P4.1 output
// P4SEL |= BIT1; // P4.1 select
// P6SEL |= BIT7; //AD7
//Setup Timer B0
// TBCCR0 = 0xFFFE;
// TBCCR1 = 0x8000;
// TBCCTL1 = OUTMOD_3; // CCR1 set/reset mode
// TBCTL = TBSSEL_2+MC_1+TBCLR; // SMCLK, Up-Mode
// Setup ADC12
P6SEL |=BIT0; // Enable VeREF+ & A/D channel A0
ADC12CTL0 = ADC12ON+ADC12SHT0_8+ADC12MSC; // Turn on ADC12, set sampling time
// set multiple sample conversion
ADC12CTL1 = ADC12SHP+ADC12CONSEQ_2; // Use sampling timer, set mode
// ADC12IE = 0x01; // Enable ADC12IFG.0
ADC12MCTL0 = ADC12SREF_0+ADC12INCH_0; // V+=AVcc V-=AVss, A0 channel
ADC12CTL0 |= ADC12ENC; // Enable conversions
ADC12CTL0 |= ADC12SC;
//////////////////////////////////////////////////////////////////////////////////
// unsigned char j=0;
// for(j=0;j<128;j++)
// {
while(!(ADC12IFG0 & BIT0))//等待采样结束
{
}
DMA_DST_AD0 = ADC12MEM0;// Move A1 results, IFG is cleared
// flag = ADC12IFG0;
// }
// Setup DMA0
DMACTL0 = DMA0TSEL_24; // ADC12IFGx triggered
DMACTL4 = DMARMWDIS; // Read-modify-write disable
DMA0CTL &= ~DMAIFG;
DMA0CTL = DMADT_4+DMAEN+DMADSTINCR_3+DMAIE; // Rpt single tranfer, inc dst, Int
DMA0SZ = 1; // DMA0 size = 1
__data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
// ... from ADC12MEM0
__data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &DMA_DST);
// ... to destination in RAM
__bis_SR_register(GIE); // LPM0 w/ interrupts
// __no_operation(); // used for debugging
while(1);
}
//------------------------------------------------------------------------------
// DMA Interrupt Service Routine
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(DMA_VECTOR))) DMA_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(DMAIV,16))
{
case 0: break;
case 2: // DMA0IFG = DMA Channel 0
P1OUT ^= BIT0; // Toggle P1.0
break;
case 4: break; // DMA1IFG = DMA Channel 1
case 6: break; // DMA2IFG = DMA Channel 2
case 8: break; // DMA3IFG = DMA Channel 3
case 10: break; // DMA4IFG = DMA Channel 4
case 12: break; // DMA5IFG = DMA Channel 5
case 14: break; // DMA6IFG = DMA Channel 6
case 16: break; // DMA7IFG = DMA Channel 7
default: break;
}
}
你可以测试一下,我正在找原因。我们也可以讨论一下。
谢谢