打印

ADC使用DMA,利用ADC的中断也无法跟踪DMA的行为

[复制链接]
648|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
eagle1983|  楼主 | 2019-12-9 16:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
想看一下DMA是怎么传输数据的。于是想利用ADC转换完成中断去跟踪DMA的行为。今天倒腾了一天,看来不行。记录一下调试过程:

启用规则组将八路ADC配置为扫描+连续模式。转换结束后使用DMA传输数据。
疑点:
1.        DMA什么时候读取数据?
手册里这么说:
The ADC generates a DMA request at the end of conversion of a regular channel. When this request is received, the DMA will transfer the converted data from the ADC_RDATA register to the destination which is specified by the user
由于ADC模块只有一个转换结果寄存器:ADC_RDATA。所以可以肯定必须在切换通道之后,在下一个通道的转换结束之前,把上次转换的结果读出来。所以无论规则组定义的通道是几个,每个通道转换完成后DMA必须立刻读取ADC的转换数据。
2.        是否可以通过ADC转换完成中断来看DMA的行为?
手册中不是说ADC转换完成后会请求DMA把数据拿走嘛!那就可以开启ADC转换完成中断,把断点设置在中断里就可以看到DMA的行为了。结果是这样的:
进入中断后,在Keil里看ADC的状态寄存器ADC_SATA只有STRC(Start flag of regular channel group)被置位(很可能这个位是0,原因后面说明),而DMA也早就把ADC_RDATA中的数据拿走了。我想是因为DMA在内核进入中断所需的时间(12个时钟周期)内已经访问过ADC_SATA了,EOC也早已被清零了(这个结论在下面第四小节验证)。但为何还能进入中断,是以为这个中断被挂起了现在才响应。(在Keil里查看SCB-ICSR寄存器可知0x1C这个向量号被挂起,正好是ADC的中断向量号)
3.        既然不能通过ADC的中断跟踪DMA的行为,那是否可以用查询ADC转换完成的方法去看呢?
情况也上述第二小节相同
4.        既然是DMA提前访问了ADC_SATA,那么可以关掉DMA,验证第二小节的结论。
关掉DMA,打开ADC中断会立刻进入中断。若在中断中不访问ADC_SATA,也不清除中断标志,则程序一直在中断服务代码中,导致其他代码无法执行。如果在中断代码中读取ADC_SATA或者清除ADC_STAT寄存器(虽然看到ADC_STAT寄存器的那些位没有变化),则不会出现程序一直在中断服务代码中的情况。所以可以验证上述第二小节的结论是正确的。同时,在第二小节中看到的STRC标志也不一定是1,也许在ADC转换完成后被清掉了。

使用特权

评论回复
沙发
eagle1983|  楼主 | 2019-12-9 16:18 | 只看该作者
本帖最后由 eagle1983 于 2019-12-9 16:19 编辑

贴上代码:
void ADC_Init(void)
{        
        rcu_periph_clock_enable(RCU_ADC);
        rcu_periph_clock_enable(RCU_GPIOA);
        rcu_periph_clock_enable(RCU_GPIOC);
        rcu_adc_clock_config(RCU_ADCCK_APB2_DIV8);        // 10MHz
        
        gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
        gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1);
        gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2);
        gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3);
        gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_5);
        gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
        gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1);
        gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_2);
        gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3);
        
        // 规则排序可以改变每个通道采回来的值,对应DMA数组的下标
        adc_channel_length_config(ADC_REGULAR_CHANNEL, 9);          // 规则组
        
        adc_regular_channel_config(0, ADC_CHANNEL_10, ADC_SAMPLETIME_239POINT5);        // 通道10: PC0
        adc_regular_channel_config(1, ADC_CHANNEL_11, ADC_SAMPLETIME_239POINT5);        // 通道11: PC1
        adc_regular_channel_config(2, ADC_CHANNEL_12, ADC_SAMPLETIME_239POINT5);        // 通道12: PC2
        adc_regular_channel_config(3, ADC_CHANNEL_13, ADC_SAMPLETIME_239POINT5);        // 通道13: PC3 3.3v
        adc_regular_channel_config(4, ADC_CHANNEL_0,  ADC_SAMPLETIME_239POINT5);        // 通道00: PA0
        adc_regular_channel_config(5, ADC_CHANNEL_1,  ADC_SAMPLETIME_239POINT5);        // 通道01: PA1
        adc_regular_channel_config(6, ADC_CHANNEL_2,  ADC_SAMPLETIME_239POINT5);        // 通道02: PA2
        adc_regular_channel_config(7, ADC_CHANNEL_3,  ADC_SAMPLETIME_239POINT5);        // 通道04: PA3
        adc_regular_channel_config(8, ADC_CHANNEL_5,  ADC_SAMPLETIME_239POINT5);        // 通道05: PA5 3.3V

        //12位ADC的转换时间为(239.5+12.5)/10 = 25.2us added by eagle
        adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);        // 规则组触发使能
        adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
        adc_data_alignment_config(ADC_DATAALIGN_RIGHT);         // 数据右对齐
        
        adc_enable();
        //delay_1ms(1);        
        adc_calibration_enable();
        
        adc_special_function_config(ADC_SCAN_MODE, ENABLE);                // 扫描模式使能
        adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);    // 连续转换模式使能
        adc_dma_mode_enable();                                                               // DMA请求
        
        ADC_DMAInit();
        dma_channel_enable(DMA_CH0);

        adc_interrupt_flag_clear(ADC_INT_FLAG_EOC);        
               
        nvic_priority_group_set(NVIC_PRIGROUP);
        nvic_irq_enable(ADC_CMP_IRQn, PRI_ADC);
        
        adc_interrupt_enable(ADC_INT_EOC);        // added by eagle

        adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
        
}

void ADC_DMAInit(void)
{
        dma_parameter_struct  dma_init_struct;
        
        
        rcu_periph_clock_enable(RCU_DMA);
        
        dma_deinit(DMA_CH0);
        dma_init_struct.direction    = DMA_PERIPHERAL_TO_MEMORY;        // 外设到内存
        dma_init_struct.memory_addr  = (uint32)Adc_HwValue;                // 内存地址
        dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;        // 内存地址增量
        dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;                // 内存地址长度
        dma_init_struct.number       = FILTER_SAMPLES*ADC_CHN_SIZE;        // 传输数据总长度
        dma_init_struct.periph_addr  = (uint32)&ADC_RDATA;                        // 外设地址
        dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;        // 外设地址增量
        dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;        // 外设地址长度
        dma_init_struct.priority     = DMA_PRIORITY_MEDIUM;                        // 优先级
        dma_init(DMA_CH0, &dma_init_struct);
        
        dma_circulation_enable(DMA_CH0);
        dma_memory_to_memory_disable(DMA_CH0);
}

使用特权

评论回复
板凳
CharryW| | 2019-12-18 11:36 | 只看该作者
中断请求在EOC置位时就已经产生了,即使很快被DMA访问清除,但请求已经产生了中断会执行的,不过猜测即使在ADC中不做任何操作不清除标志位,ADC中断也只会产生一次。  至于DMA的行为一定不是靠EOC发出搬运请求的,因为EOC只在ADC一组通道转换完置位,而DMA需要在每个通道转换完时搬走RDATA的数据,使用的应该是ADC的内部信号。

使用特权

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

本版积分规则

4

主题

9

帖子

0

粉丝