GD32F407标准库IAR环境 DMA+ADC+中断

近期由于公司要求更改使用GD32做项目,究其原因也就不多说了。自己之前一直使用KEIL+cubeMX做STM32做开发,STM32的标准库基本都没有怎么用过,突然要改到使用GD的标准库来开发,所以在开发过程中遇到了不少的问题。作为记录供参考吧。

首先为自己做个辩解:项目进展是其一,再加上自己也想快速 的完成项目,所以都是看网上的例程或者他的经验来写自己的驱动程序部分。但是有些弯路并不是所有的人都走过......
使用GD的标准库版本:GD32F4xx_Firmware_Library_V1.4
标准库说明文档:GigaDevice Semiconductor Inc.GD32F4xxARM® Cortex™-M4 32-bit MCU 《固件库使用指南1.0 版本(2018年 12月 )》
芯片使用说明文档:《GigaDevice Semiconductor Inc.GD32F4xxARM® Cortex ™ M4 32 bit MCU适用GD32F405xx 、 GD32F407 xx 、 GD32 F450xx 系列用户手册2.2 版 本(2020 年 3 月)》

弯路1: 不知道标准库的中断入口函数是怎么写的?(例程中也没有明显的标识); 根据自己的经验,一般都是在《gd32f4xx_it.c》这个代码中有写道,但是没有! 答案: 找到GD32对应芯片的启动文件 在 ASM代码中写明了各个中断入口函数的名称 xxx_ccc_IRQHandler 结尾的,xxx标识外设名称,ccc表示通道名称;例如DMA1_Channel4_IRQHandler 表示DMA1 通道4的中断入口函数名称。拷贝到 _it.c文件中即可使用。

弯路2: 针对DMA+ADC+中断+多通道采集 不知道如何配置DMA和ADC? 答案:网上有说到ADC+DMA配置的程序但是有些版本的函数和我现在使用的函数不一样,有些一样,但是也不一定能用,所以一定要仔细区分自己的库函数版本。
话不多说贴代码:验证可以使用的。功能:

  1. tim4 CH0触发启动DMA,当DMA采集完成所设定的通道数量后,产生中断,设置标志位,关闭TIM4,由外部程序重新开启TIM4后才可以再次开始采样,这样做只是为了调试使用,正常使用过程中,TIM4不需要关闭;
  2. TIM4对应的A0后为PWM 输出口,在开启定时器后有PWM输出,可以使用示波器查看波形输出,确定定时器是否在触发ADC工作。后期程序中只需要关闭PWM端口输出即可正常使用。
  3. 通过标志位读取ADC的值。ADC设定为7个通道,规则采样,一次DMA采集一个规则。
//---------------------------------------------------------
void adc_config(void)
{
    rcu_periph_clock_enable(RCU_ADC0);
    /* config ADC clock */
    adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    adc_deinit();
    /* ADC channel length config */
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, ADC_CHS);

    /* ADC discontinuous mode */
    //adc_discontinuous_mode_config(ADC0,ADC_REGULAR_CHANNEL, 4); //使能断续模式时无法使用DMA连续采样

    /* ADC regular channel config */
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_11, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_8, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_3, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_12, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_14, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 5, ADC_CHANNEL_15, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 6, ADC_CHANNEL_2, ADC_SAMPLETIME_144);

        /* ADC external trigger source config */
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_FALLING); //  EXTERNAL_TRIGGER_FALLING
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_T4_CH0); //TIM4 CH0 

    /* ADC data alignment config */
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
    adc_resolution_config(ADC0, ADC_RESOLUTION_12B);

    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE); //ENABLE  将ADC将自动重复启动测试
        /* ADC contineous function enable */
    adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); // ADC SCAN MODE ENABLE ADC将自动完成一个规则的所有通道数据的转化,并参数一个中断
    /* ADC DMA function enable */
    adc_dma_mode_enable(ADC0);
    adc_dma_request_after_last_enable(ADC0);//
    /* enable ADC interface */
    adc_enable(ADC0);
    /* ADC calibration and reset calibration */
    adc_calibration_enable(ADC0);

    /* ADC software trigger enable */
    //adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);

    //adc_end_of_conversion_config

}


void dma_config(void)
{
    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA1);

    /* ADC_DMA_channel configuration */
    dma_deinit(DMA1,DMA_CH0);
    dma_periph_address_config(DMA1,DMA_CH0,(uint32_t )(&ADC_RDATA(ADC0)));
    dma_peripheral_address_generation_config(DMA1, DMA_CH0, DMA_PERIPH_INCREASE_DISABLE);
    dma_periph_width_config(DMA1, DMA_CH0, DMA_PERIPH_WIDTH_16BIT); //很重要的设置

    dma_memory_address_config(DMA1,DMA_CH0, DMA_MEMORY_0, (uint32_t)&adcData);  //
    dma_memory_address_generation_config(DMA1, DMA_CH0, DMA_MEMORY_INCREASE_ENABLE);
    dma_memory_width_config(DMA1,DMA_CH0, DMA_MEMORY_WIDTH_16BIT); //很重要的设置,注意观察区别

    dma_priority_config(DMA1,DMA_CH0, DMA_PRIORITY_HIGH);
    dma_transfer_direction_config(DMA1, DMA_CH0, DMA_PERIPH_TO_MEMORY);
    dma_transfer_number_config(DMA1,DMA_CH0, 7); //和设定的ADC输入采样个数有关系
            
    dma_circulation_enable(DMA1,DMA_CH0);  //没有去测试具体功能

    dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0);   //没有去测试具体功能
    dma_channel_enable(DMA1,DMA_CH0); 
    //----------------------------------------------------
    //设置dma中断优先级
    nvic_irq_enable(DMA1_Channel0_IRQn, 0, 0);
    /* enable DMA transfer complete interrupt */
    dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INTC_FTFIFC);
    dma_interrupt_enable(DMA1,DMA_CH0, DMA_CHXCTL_FTFIE);
     
}
//---------------------------- ADC 端口配置---------------------------
void init_adcgpio(void)
{

    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOB);
    rcu_periph_clock_enable(RCU_GPIOC);

    /* enable ADC clock */
    //rcu_periph_clock_enable(RCU_ADC0);
    /* enable DMA clock */
    //rcu_periph_clock_enable(RCU_DMA1);
    /* config ADC clock */
    //adc_clock_config(ADC_ADCCK_PCLK2_DIV4);

    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3 | GPIO_PIN_2);
    gpio_mode_set(GPIOB, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
    gpio_mode_set(GPIOC, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5);
}

//-------------------初始化入口函数----------------------
void init_adc_dam(void)
{
    init_adcgpio();

    adc_config();
    dma_config();
}

//当传输完成足够的数据后,就关闭定时器,设置标志位,进行处理
void DMA1_Channel0_IRQHandler(void)  
{
    if (dma_interrupt_flag_get(DMA1, DMA_CH0, DMA_INTF_FTFIF))
    {
        //设置标志位 DMA完成了 
        //清除全部标志位
        timer_disable(TIMER4);
        test_adc_v();
        dma_interrupt_flag_clear(DMA1, DMA_CH0, DMA_INTC_FTFIFC);
    }
}

库函数中有几个坑要注意:
坑1: DMA 配置中配置外设宽度函数, 只能使用DMA_PERIPH_WIDTH_XXX的声明,千万不能使用DMA_MEMORY_WIDTH_xxx MEMORY的声明。同理,在配置MEMORY地址宽度时也不能选错。切记,写错后编译器是不会报错的。但是采集的数据会让.......

  dma_periph_width_config(DMA1, DMA_CH0, DMA_PERIPH_WIDTH_16BIT);
 dma_memory_width_config(DMA1,DMA_CH0, DMA_MEMORY_WIDTH_16BIT);

坑2:标准库说明文档中,提到关于DMA中断标志清零API函数的标志清零选项中写的不对;下图红色标记是正确的。


DMA中断清零函数标志说明正确项.png

坑3:先引用一篇文章,链接:GD32F330 | ADC实例 基于DMA方式 - Tuple - 博客园 (cnblogs.com) 一定要看准库版本信息,文章中的函数和我现在是使用的库不同,我的库,都需要增加一个设备的入口。
坑4: 在坑3提到的文章中,最后博主给出了很好的总结,但是我要再加一条: 仔细看自己的使用的单片机是否有AVCC或者VREF引脚,看你的开发板上是否将这些引脚准确连接? 硬件是软件的基础!!!

弯路3: 切记要多看文档!!! 我之前按照网上的一些例程,直接拿来用,并不能工作,也无法进入DMA中断,除了有入口函数的问题,还有一个重要问题就是DMA是否支持你所使用的设备;例如上面的博主中使用的是DMA0,但是CPU是F3,而我使用的是F4,DMA0是无效的,通过看文档,才发现只有DMA1才支持ADC0和ADC1功能。如下图


DMA1配置端口说明.png

而DMA0 支持的如下图:


DMA0端口说明png

先记下这些内容,以备今后参考。

你可能感兴趣的:(GD32F407标准库IAR环境 DMA+ADC+中断)