近期由于公司要求更改使用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配置的程序但是有些版本的函數和我現在使用的函數不一樣,有些一樣,但是也不一定能用,所以一定要仔細區分自己的庫函數版本。
話不多說貼代碼:驗證可以使用的。功能:
- tim4 CH0觸發啟動DMA,當DMA采集完成所設定的通道數量后,產生中斷,設置標志位,關閉TIM4,由外部程序重新開啟TIM4后才可以再次開始采樣,這樣做只是為了調試使用,正常使用過程中,TIM4不需要關閉;
- TIM4對應的A0后為PWM 輸出口,在開啟定時器后有PWM輸出,可以使用示波器查看波形輸出,確定定時器是否在觸發ADC工作。后期程序中只需要關閉PWM端口輸出即可正常使用。
- 通過標志位讀取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函數的標志清零選項中寫的不對;下圖紅色標記是正確的。
坑3:先引用一篇文章,鏈接:GD32F330 | ADC實例 基于DMA方式 - Tuple - 博客園 (cnblogs.com) 一定要看準庫版本信息,文章中的函數和我現在是使用的庫不同,我的庫,都需要增加一個設備的入口。
坑4: 在坑3提到的文章中,最后博主給出了很好的總結,但是我要再加一條: 仔細看自己的使用的單片機是否有AVCC或者VREF引腳,看你的開發板上是否將這些引腳準確連接? 硬件是軟件的基礎!!!
彎路3: 切記要多看文檔!!! 我之前按照網上的一些例程,直接拿來用,并不能工作,也無法進入DMA中斷,除了有入口函數的問題,還有一個重要問題就是DMA是否支持你所使用的設備;例如上面的博主中使用的是DMA0,但是CPU是F3,而我使用的是F4,DMA0是無效的,通過看文檔,才發現只有DMA1才支持ADC0和ADC1功能。如下圖
而DMA0 支持的如下圖:
先記下這些內容,以備今后參考。