本例記錄使用GD32307C開發板,實現以內部Timer1 CH1為觸發源,觸發ADC0的兩個通道,進行并行非連續采樣,病通過DMA傳輸采樣結果。同時輸出PWM,用來檢測Timer1 CH1的觸發周期。
下面介紹具體實現過程:
1. gpio初始化
本例需要用到以下三根IO:
PA1 -- TIMER1_1,PWM
PC3 -- ADC0_CH13
PC5 -- ADC0_CH15
查詢芯片手冊需要將PC3/PC5設定為模擬輸入AIN(ADC功能),將PA1設定為備份功能輸出腳AF_PP(PWM輸出)。
以PA1為例,設定為Alternate功能后,如果有啟動Timer1,則該腳位的功能為TIMER1_CH1。
- 使能GPIO Group時鐘。
- 使能Alternate Function時鐘。
- GPIO 功能config 。
代碼如下:
/*!\brief configure the GPIO peripheral\param[in] none\param[out] none\retval none
*/
//PA1 -- TIMER1_1,PWM
//PC3 -- ADC0_CH13
//PC5 -- ADC0_CH15
static void gpio_config(void)
{/* enable GPIOC clock */rcu_periph_clock_enable(RCU_GPIOC);rcu_periph_clock_enable(RCU_GPIOA); /* 開啟復用功能時鐘 */rcu_periph_clock_enable(RCU_AF); /* config the GPIO as analog mode */ gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_MAX, GPIO_PIN_3|GPIO_PIN_5);/*configure PB3(TIMER1 CH1) as alternate function*/gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
}
2.Timer初始化
這里配置Timer1的定時周期為1ms,附上Timer時間公式:
定時時間:Time= (1+prescaler)/ systemcoreclock *(1+period)
我這邊systemcoreclock為120MHZ。
設定CH1位PWM0模式輸出,用來驗證ADC采樣間隔(ADC采樣以內部Timer1_CH1為觸發源)。
在下面代碼里面,我們設定為邊沿對齊方式(EAPWM),向上計數:
EAPWM的周期由TIMERx_CAR寄存器值決定,占空比由TIMERx_CHxCV寄存器值決定。
這里介紹一下PWM的工作模式(PWM0/PWM1)
PWM的工作模式:
- PWM 模式0。在向上計數時,一旦計數器值小于TIMERx_CH0CV時,
O0CPRE為有效電平,否則為無效電平。在向下計數時,一旦計數器的值大
于TIMERx_CH0CV時,O0CPRE 為無效電平,否則為有效電平。- PWM 模式1。在向上計數時,一旦計數器值小于TIMERx_CH0CV時,
O0CPRE為無效電平,否則為有效電平。在向下計數時,一旦計數器的值大
于TIMERx_CH0CV時,O0CPRE為有效電平,否則為無效電平。
占空比計算公式:
Duty = A/(N+1)? ?
N = Timer裝載值
A =?TIMER_CHxCV,即timer_channel_output_pulse_value_config的最后一個參數
EAPWM的時序示意圖如下:
代碼如下:
/*!\brief configure the timer peripheral\param[in] none\param[out] none\retval none
*/
static void timer1_config(void) // 1 ms
{timer_oc_parameter_struct timer_ocintpara;timer_parameter_struct timer_initpara;//使能定時器時鐘rcu_periph_clock_enable(RCU_TIMER1);timer_deinit(TIMER1);/* TIMER1 基本配置 */timer_initpara.prescaler = 119;timer_initpara.alignedmode = TIMER_COUNTER_EDGE;timer_initpara.counterdirection = TIMER_COUNTER_UP;timer_initpara.period = 999;timer_initpara.clockdivision = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER1,&timer_initpara);/* CH1 configuration in PWM mode1 */timer_ocintpara.outputstate = TIMER_CCX_ENABLE; /* 通道使能 */ timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE; /* 通道互補輸出使能(定時器2無效) */ timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; /* 通道極性 */ timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;/* 互補通道極性(定時器2無效)*/ timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;/* 通道空閑狀態輸出(定時器2無效)*/ timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;/*互補通道空閑狀態輸出(定時器2無效) */timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);/* duty 占空比設定*/timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 500);timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);timer_auto_reload_shadow_enable(TIMER1);
}
使用示波器量測PA1腳位,輸出為1KHZ的方波。
3. dma初始化
這里我們選擇DMA0_CH0,這里我們設定為16bit寬度數據源,與16bit外設數據寬度。DMA傳輸數量設定為100.
所以我們定義一個100 uint16_t的數組作為DMA存放數據的內存。
開啟半滿與全滿中斷。
代碼如下:
/*!\brief configure the DMA peripheral\param[in] none\param[out] none\retval none
*/
uint16_t adc_value[100];
uint32_t dmaHalfIntCnt; static void dma_config(void)
{/* ADC_DMA_channel configuration */dma_parameter_struct dma_data_parameter;/* enable DMA clock */rcu_periph_clock_enable(RCU_DMA0);/* 復位dma 通道0 */dma_deinit(DMA0, DMA_CH0);/* initialize DMA data mode */dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));//配置外設寄存器為adc的規則數據寄存器dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;//外設寄存器地址不增加dma_data_parameter.memory_addr = (uint32_t)(&adc_value);//存放數據的內存地址dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;//存放數據的內存地址自增dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;//外設數據寬度32位 adc1[16-31]adc0[0-15]dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT; //內存數據寬度32位dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;//數據傳輸方式是外設到內存dma_data_parameter.number = 100;//dma傳輸數量dma_data_parameter.priority = DMA_PRIORITY_HIGH;//dma優先級高dma_init(DMA0, DMA_CH0, &dma_data_parameter);//循環模式開啟dma_circulation_enable(DMA0, DMA_CH0);/* dam 轉換結束產生中斷*/dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_FTF); dma_interrupt_enable(DMA0, DMA_CH0, DMA_INT_HTF); /* 使能dma通道0*/dma_channel_enable(DMA0, DMA_CH0);//中斷管理器開啟通道4中斷nvic_irq_enable(DMA0_Channel0_IRQn, 0, 0);
}
DMA中斷程序如下:(函數名與startup中的中斷向量表函數名一致)
void DMA1_Channel1_IRQHandler(void)
{if(dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_FTF)){ //清除dma中斷標記dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_INT_FLAG_FTF);gd_eval_led_toggle(LED2);//dma_finish = SET;} else if(dma_interrupt_flag_get(DMA0, DMA_CH0, DMA_INT_FLAG_HTF)){//清除dma中斷標記dma_interrupt_flag_clear(DMA0, DMA_CH0, DMA_INT_FLAG_HTF);dmaHalfIntCnt ++;}
}
截取部分Startup_xx.s向量表
g_pfnVectors:
? .
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.
4. ADC初始化
我們使用ADC0的CH13/CH15兩個通道,這里設置ADC為規則并行模式。
開啟掃描模式,ADC會自動采樣轉換開啟的通道。
不要開啟連續掃描,采用間斷掃描方式。
設定TIM1_CH1為外部觸發源,病使能ADC的DMA mode,具體代碼如下:
/*!\brief configure the ADC peripheral\param[in] none\param[out] none\retval none
*/
static void adc_config(void)
{/* enable ADC0 clock */rcu_periph_clock_enable(RCU_ADC0);/* reset ADC */adc_deinit(ADC0);/* ADC mode config 工作在規則并行模式 *///adc_mode_config(ADC_MODE_FREE); adc_mode_config(ADC_DAUL_REGULAL_PARALLEL); // 連續轉換模式使能//adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);/* ADC continous function enable *///adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE); adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); /* 右對齊*/adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);/* 配置規則通道數量 *///adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 2);/* ADC規則模式配置;2個adc并行模式下,不可以2個adc同時采集一個通道,adc1數據放在高16位,adc0數據放在低16位*/adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_13, ADC_SAMPLETIME_55POINT5);adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_15, ADC_SAMPLETIME_55POINT5);/* adc 使用TIM1 的CH1 的上升沿觸發*/adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_EXTTRIG_REGULAR_T1_CH1);/* adc觸發使能*/adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);/* 使能ADC*/adc_enable(ADC0);delay_1ms(1U);/* 標定復位ADC */adc_calibration_enable(ADC0);/* ADC DMA 使能*/adc_dma_mode_enable(ADC0);
}
5. 測試代碼
這里創建一個thread來測試,測試代碼如下:
void LED1_Task(void* parameter)
{gpio_config();timer1_config();dma_config();adc_config();timer_enable(TIMER1);while(1){//gd_eval_led_toggle(LED3);printf("%s tick:%d \n",__FUNCTION__,xTaskGetTickCount()); //Ben 221024#1printf("dmaHalfIntCnt:%d \n",dmaHalfIntCnt);printf("%d %d %d %d\n",adc_value[0],adc_value[1],adc_value[2],adc_value[3]); vTaskDelay(500); }
}
在500ms間隔下的測試結果為:
dmaHalfIntCnt:2410?
1663 0 1663 1
LED1_Task tick:120965?
dmaHalfIntCnt:2420?
1663 1 1663 0
LED1_Task tick:121469?
dmaHalfIntCnt:2430?
1663 0 1661 1
LED1_Task tick:121973?
dmaHalfIntCnt:2440?
1663 0 1663 0
LED1_Task tick:122477?
dmaHalfIntCnt:2450?