GD32F103系列學習

一、基本流程

systick_config();                                                   // 系統(tǒng)時鐘配置

 rcu_periph_clock_enable(RCU_GPIOC);                                 // 使能LED對應(yīng)的GPIO時鐘
 gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); // 配置LED燈對應(yīng)的端口和引腳
 gpio_bit_reset(GPIOC, GPIO_PIN_13);                                 // 重置引腳

 while(1){
 gpio_bit_set(GPIOC, GPIO_PIN_13);                                 // PC13引腳拉到高電平,打開燈
 delay_1ms(500);                                                   // 延時500ms
 gpio_bit_reset(GPIOC, GPIO_PIN_13);                               // PC13引腳拉到低電平,關(guān)閉燈
 delay_1ms(500);                                                   // 延時500ms
 }

拿LED閃爍這個例子來說,基本流程是先進行系統(tǒng)時鐘初始化systick_config()。然后根據(jù)我們業(yè)務(wù)需要,去使能和配置對應(yīng)的外設(shè)即可。

像這個例子中就是先打開GPIOC口。因為我們要使用PC13這個GPIO口。然后初始化PC13的引腳,配置為輸出推挽模式,頻率設(shè)置為50MHz。最后復位PC13引腳。最后在死循環(huán)中讓PC13每隔500ms電平翻轉(zhuǎn)一次即可。

二、時鐘

GD32F10X系列的系統(tǒng)時鐘(CK_SYS)有三個可選的時鐘源。一個內(nèi)部8MRC振蕩器時鐘(IRC8M)、一個外部高速晶體振蕩器(HXTAL)、一個鎖相環(huán)(PLL)。

系統(tǒng)時鐘的最大運行頻率可以達到108MHz。

image-20230412142545797.png

系統(tǒng)總線分為三條。分別是高速總線AHB,兩條低速總線APB1和APB2。APB2最高可以達到108MHz,APB1最高可以達到54MHz。外設(shè)上的時鐘是根據(jù)所在總線的頻率倍頻或者分頻得到。

目前有這么幾個時鐘。三個時鐘源CK_PLL,CK_IRC8M,CK_HXTAL,系統(tǒng)時鐘CK_SYS,高速總線時鐘CK_AHB,低速總線時鐘1(CK_APB1),低速總線時鐘2(CK_APB2)。剩下的就是外設(shè)時鐘,包括ADC時鐘(CK_ADCx),定時器時鐘(CK_TIMERx)等。

這幾個時鐘的轉(zhuǎn)換關(guān)系可以通過觀察對應(yīng)寄存器的值來算出。

2.1 時鐘源

首先三個時鐘源對應(yīng)的頻率是。

2.1.1 CK_IRC8M

這個就是8MHz。

2.1.2 CK_PLL

這個通過觀察時鐘樹,由CK_IRC8M先進行二分頻,然后經(jīng)過PLLMF倍頻得到。只要知道倍頻是多少,即可算出。這兒的倍頻是需要看時鐘配置寄存器 0(RCU_CFG0)的PLLMF[3:0]位來得到。

image-20230412143927252.png

我查看了板子的寄存器值是11010。查詢手冊可知這是PLL源時鐘 x 27得到。所以

CK_PLL = 8MHz / 2 x 27 = 108MHz

image-20230412144136664.png
2.1.3 CK_HXTAL

4到16M的外部高速晶體振蕩器時鐘,這個由外界輸入決定。外部的輸入時鐘是多少就是多少。

HXTAL晶體振蕩器可以通過設(shè)置控制寄存器RCU_CTL的HXTALEN位來啟動或關(guān)閉,在控制寄存器RCU_CTL中的HXTALSTB位用來指示外部高速振蕩器是否已

穩(wěn)定。在啟動時,直到這一位被硬件置‘1’,時鐘才被釋放出來。這個特定的延遲時間被稱為振蕩器的啟動時間。當HXTAL時鐘穩(wěn)定后,如果在中斷寄存器RCU_INT

中的相應(yīng)中斷使能位HXTALSTBIE位被置‘1’,將會產(chǎn)生相應(yīng)中斷。此時,HXTAL時鐘可以被直接用作系統(tǒng)時鐘源或者PLL輸入時鐘。

將控制寄存器RCU_CTL的HXTALBPS和HXTAKEN位置‘1’來使能外部旁路模式。旁路輸入時,信號接至OSCIN,OSCOUT保持懸空狀態(tài),如下圖所示。此時,

CK_HXTAL等于驅(qū)動OSCIN管腳的外部時鐘。

image-20230412145428630.png

2.2 系統(tǒng)時鐘CK_SYS

系統(tǒng)時鐘CK_SYS可選擇的有上面2.1的三個時鐘源。系統(tǒng)復位后,IRC8M時鐘默認做為CK_SYS的時鐘源,改變配置寄存器0(RCU_CFG0)中的系統(tǒng)時鐘變換

位SCS可以切換系統(tǒng)時鐘源為HXTAL或CK_PLL。當SCS的值被改變,系統(tǒng)時鐘將使用原來的時鐘源繼續(xù)運行直到轉(zhuǎn)換的目標時鐘源穩(wěn)定。當一個時鐘源被直接或通

過PLL間接作為系統(tǒng)時鐘時,它將不能被停止。

image-20230412150005094.png

查看寄存器得到SCS的值10。所以系統(tǒng)時鐘選擇的是CK_PLL。也就是108M。

image-20230412150124736.png

此時已經(jīng)確定系統(tǒng)時鐘是108MHz。

2.3 總線時鐘

通過觀察時鐘樹我們知道。高速總線AHB上的時鐘由系統(tǒng)時鐘CK_SYS分頻得到,低速總線APB1和APB2上的時鐘由CK_AHB分頻得到。

2.3.1 CK_AHB

CK_AHB由CK_SYS分頻得到,預分頻因子查看時鐘配置寄存器0(RCU_CFG0)的AHBPSC[3:0]位可獲取。最大分頻為512。

image-20230412151454903.png

查看對應(yīng)寄存器結(jié)果是0x0,此時代表不分頻。所以我自己板子的

CK_AHB=CK_SYS=108MHz

image-20230412151540960.png
2.3.2 CK_APB1

CK_APB1由CK_AHB分頻得到。預分頻因子查看時鐘配置寄存器0(RCU_CFG0)的APB1PSC[2:0]位可獲取。最大分頻為16。

image-20230412152442607.png

查看自己寄存器的結(jié)果是0x4,對應(yīng)100,代表選擇CK_AHB時鐘2分頻。所以

CK_APB1 = CK_AHB / 2 = 108MHz / 2 = 54MHz

image-20230412152545699.png
2.3.3 CK_APB2

CK_APB1由CK_AHB分頻得到。同CK_APB1。預分頻因子查看時鐘配置寄存器0(RCU_CFG0)的APB2PSC[2:0]位可獲取。最大分頻為16。

image-20230412152820793.png

查看自己板子的寄存器的值為0x0,所以是選擇CK_AHB時鐘不分頻。

CK_APB2 = CK_AHB = 108MHz

image-20230412152915961.png

2.4 定時器時鐘CK_TIMERx

定時器外設(shè)都是掛靠在低速總線上的,多個定時器掛靠的低速總線不一樣。觀察中密度GD32F10X系列結(jié)構(gòu)??梢钥闯鯰IMER0定時器掛靠在APB2上,TIMER1~3掛在APB1上。

image-20230412153318827.png

針對TIMER2來分析。

image-20230412153509281.png

CK_TIMER2時鐘是根據(jù)CK_APB1來得到的。判斷APB1的預分頻因子,如果是1,那么CK_TIMERx = CK_APB1 x 1;如果不是1,那么CK_TIMERx = CK_APB1 x 2。

我們從上面知道APB1的預分頻因子是2。所以CK_TIMERx的時鐘就是2倍的CK_APB1。

CK_TIMERx = CK_APB1 x 2 = 54MHz x 2 = 108MHz

所以TIMER2定時器的時鐘就是108MHz。

三、例程

3.1、燈運行

main.c

#include "gd32f10x.h"
#include "gd32f103b_start.h"
#include "systick.h"

/**
* LED2每隔1s閃爍一次
* PC13引腳是LED2的關(guān)聯(lián)引腳
*/
int main(void)
{
 systick_config();                                                   // 系統(tǒng)時鐘配置

 rcu_periph_clock_enable(RCU_GPIOC);                                 // 使能LED對應(yīng)的GPIO時鐘
 gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13); // 配置LED燈對應(yīng)的端口和引腳
 gpio_bit_reset(GPIOC, GPIO_PIN_13);                                 // 重置引腳

 while(1){
 gpio_bit_set(GPIOC, GPIO_PIN_13);                                 // PC13引腳拉到高電平,打開燈
 delay_1ms(500);                                                   // 延時500ms
 gpio_bit_reset(GPIOC, GPIO_PIN_13);                               // PC13引腳拉到低電平,關(guān)閉燈
 delay_1ms(500);                                                   // 延時500ms
 }
}

上面main函數(shù)中使用的函數(shù)都是調(diào)用的系統(tǒng)函數(shù)或者外設(shè)函數(shù)。

重點:系統(tǒng)時鐘初始化的本質(zhì)

void systick_config(void)
{
 /* setup systick timer for 1000Hz interrupts */
 if(SysTick_Config(SystemCoreClock / 1000U)){
 /* capture error */
 while(1){
 }
 }
 /* configure the systick handler priority */
 NVIC_SetPriority(SysTick_IRQn, 0x00U);
}
image-20230412160749582.png

系統(tǒng)時鐘初始化其實就是將我們選定的時鐘108M分到我們自己需要的系統(tǒng)定時器,這兒我們需要1KHz。所以需要在這個函數(shù)中除以1000即可。系統(tǒng)時鐘SystemCoreClock = 108M。

然后我們查看中斷源文件gd32f10x_it.c

/*!
 \brief      this function handles SysTick exception
 \param[in]  none
 \param[out] none
 \retval     none
*/
void SysTick_Handler(void)
{
 delay_decrement();
}

delay_decrement()

void delay_decrement(void)
{
 if(0U != delay){
 delay--;
 }
}

系統(tǒng)定時中斷,這兒的中斷程序執(zhí)行按頻率衰減。一個周期減一,這樣就能實現(xiàn)延時函數(shù)delay_1ms()。

3.2、PMW模式輸出方波

main.c

#include "gd32f10x.h"
#include <stdio.h>
#include "gd32f10x_eval.h"

void gpio_config(void);
void timer_config(void);

/**
* 利用定時器2的通道2(TIMER2_CH2)輸出方波。波形頻率為1KHz。 
*/
int main(void)
{
 gpio_config();     // GPIO配置
 timer_config();    // 定時器TIMER配置
 while (1);
}

/**
* GPIO配置,并且初始化GPIO的復用功能。
*/
void gpio_config(void)
{
 rcu_periph_clock_enable(RCU_GPIOB);                              // 配置GPIOB的時鐘
 rcu_periph_clock_enable(RCU_AF);                                 // 配置GPIO的AF復用功能,因為這個引腳要作為定時器的通道輸出。
 gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);// 初始化PB0的引腳,AF復用功能的推挽模式
}

/**
* 定時器的配置
*/
void timer_config(void)
{
 /* -----------------------------------------------------------------------
 TIMER2 配置: 以50的占空比生成一個pwm信號:
 CK_TIMER2時鐘 = CK_APB1 / 預分頻因子  = 108MHz / 108 = 1MHz
 TIMER2 通道2的占空比 = (500/ 1000)* 100  = 50%
 ----------------------------------------------------------------------- */
 timer_oc_parameter_struct timer_ocintpara;
 timer_parameter_struct timer_initpara;

 rcu_periph_clock_enable(RCU_TIMER2);    // 使能TIMER2定時器的時鐘

 timer_deinit(TIMER2);                   // 復位TIMER2

 /* TIMER2 配置 */
 timer_initpara.prescaler         = 108-1;                 // 預分頻因子。0~65535
 timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;    // 邊沿計數(shù)對齊
 timer_initpara.counterdirection  = TIMER_COUNTER_UP;      // 上升沿觸發(fā)
 timer_initpara.period            = 1000-1;                // 周期是100
 timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;      // 時鐘分頻值
 timer_initpara.repetitioncounter = 0;                     // 重復計數(shù)為0
 timer_init(TIMER2,&timer_initpara);                       // 初始化TIMER2

 /* PWM模式下的通道2配置 */
 timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;     // 通道輸出極性高
 timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;           // 通道輸出狀態(tài)使能
 timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;    // 通道互補輸出極性高
 timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;         // 通道互補輸出狀態(tài)不使能
 timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;    // 通道輸出空閑狀態(tài)低
 timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;   // 通道互補輸出空閑狀態(tài)低
 timer_channel_output_config(TIMER2,TIMER_CH_2,&timer_ocintpara);              // 定時器通道輸出配置

 timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,499);               // 輸出脈沖值為500
 timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);       // 通道輸出模式為PWM0
 timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);// 通道輸出影子不使能

 timer_auto_reload_shadow_enable(TIMER2);                       // 啟用自動重新加載陰影功能
 timer_primary_output_config(TIMER2,ENABLE);                    // 啟用TIMER主輸出功能
 timer_enable(TIMER2);                                          // 使能TIMER2
}

注意點

  1. GPIO使能時的模式

    使能PB0作為TIMER2的CH2通道時,要注意同時使能GPIO的AF復用功能。并且選擇引腳為AF(復用)的PP(推挽模式)

    rcu_periph_clock_enable(RCU_AF);                                 // 配置GPIO的AF復用功能,因為這個引腳要作為定時器的通道輸出。
    gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);// 初始化PB0的引腳,AF復用功能的推挽模式
  1. 頻率的計算

    計算頻率是這個例子的關(guān)鍵。上面我們已經(jīng)計算得到了當前定時器的時鐘是108MHz。我們最終需要的結(jié)果是1KHz。所以需要108M / 1K = 108000。

    這兒需要認識一個新的時鐘。計數(shù)器時鐘(PSC_CLK)。預分頻器可以將定時器的時鐘(TIMER_CK)頻率按1到65536之間的任意值分頻,分頻后的時鐘

    PSC_CLK驅(qū)動計數(shù)器計數(shù)。分頻系數(shù)受預分頻寄存器TIMERx_PSC控制,這個控制寄存器帶有緩沖器,它能夠在運行時被改變。新的預分頻器的參數(shù)在下一次

    更新事件到來時被采用。

image-20230412170103350.png

在代碼中我們配置的prescaler = 108-1 = 107。我們檢查一下寄存器的值是0x006B。換算得到的十進制就是107。

image-20230412170333738.png

所以在代碼中我們得到的計數(shù)器時鐘:

PSC_CLK = CLK_TIMER2 / (prescaler + 1) = 108MHz / 108 = 1MHz

然后使用計數(shù)器時鐘除以(周期+1)即可得到我們最后的方波頻率。我們設(shè)置的周期period是1000-1 = 999。

最終計算結(jié)果為

PSC_CLK / (period + 1) = 1MHz / 1000 = 1KHz

3.3、pwm輸出呼吸燈

main.c

#include "gd32f10x.h"
#include "core_cm3.h"

FlagStatus flag = SET;

void gpio_config(void);
void timer_config(void);
void systick_config(void);
void delay_1ms(uint32_t count);

/**
 * 呼吸燈的實現(xiàn)(使用pwm脈沖)
 */
 int main(void)
{
 uint16_t i = 0;
 systick_config();
 gpio_config();
 timer_config();

 while(1)
 {
 delay_1ms(10);
 if (SET == flag)
 {
 i++;
 } else {
 i--;
 }
 if (i > 250)
 {
 flag = RESET;
 }
 if (i <= 0 )
 {
 flag = SET;
 }
 /** 修改TIMER2的CH2通道的CH2CVL的寄存器值,也就是 **/
 /** timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,i);的i值 **/
 TIMER_CH2CV(TIMER2) = (uint32_t)i;
 }
}

/**
 * GPIO配置,配置PC13引腳(LED2的引腳)配置PB0作為PWM輸出的引腳
 */
void gpio_config(void)
{
 /** PB0作為timer2通道2的pwm輸出 **/
 rcu_periph_clock_enable(RCU_GPIOB);                              // 配置GPIOB的時鐘
 rcu_periph_clock_enable(RCU_AF);                                 // 配置GPIO的AF復用功能,因為這個引腳要作為定時器的通道輸出。
 gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);// 初始化PB0的引腳,AF復用功能的推挽模式

 rcu_periph_clock_enable(RCU_GPIOC);                              // PC13作為LED2的輸出引腳
 gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);// 初始化PB0的引腳,AF復用功能的推挽模式
 gpio_bit_reset(GPIOC,GPIO_PIN_13);
}

/**
 * 定時器的配置。配置TIMER2的CH2通道。
 */
void timer_config()
{
 timer_oc_parameter_struct timer_ocintpara;
 timer_parameter_struct timer_initpara;

 rcu_periph_clock_enable(RCU_TIMER2);    // 使能TIMER2定時器的時鐘

 timer_deinit(TIMER2);                   // 復位TIMER2

 /* TIMER2 配置 */
 timer_initpara.prescaler         = 108-1;                 // 預分頻因子。0~65535
 timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;    // 邊沿計數(shù)對齊
 timer_initpara.counterdirection  = TIMER_COUNTER_UP;      // 上升沿觸發(fā)
 timer_initpara.period            = 1000-1;                // 周期是100
 timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;      // 時鐘分頻值
 timer_initpara.repetitioncounter = 0;                     // 重復計數(shù)為0
 timer_init(TIMER2,&timer_initpara);                       // 初始化TIMER2

 /* PWM模式下的通道2配置 */
 timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;     // 通道輸出極性高
 timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;           // 通道輸出狀態(tài)使能
 timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;    // 通道互補輸出極性高
 timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;         // 通道互補輸出狀態(tài)不使能
 timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;    // 通道輸出空閑狀態(tài)低
 timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;   // 通道互補輸出空閑狀態(tài)低
 timer_channel_output_config(TIMER2,TIMER_CH_2,&timer_ocintpara);              // 定時器通道輸出配置

 timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,0);               // 輸出脈沖值為500
 timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);       // 通道輸出模式為PWM0
 timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);// 通道輸出影子不使能

 timer_auto_reload_shadow_enable(TIMER2);                       // 啟用自動重新加載陰影功能
 timer_primary_output_config(TIMER2,ENABLE);                    // 啟用TIMER主輸出功能
 timer_enable(TIMER2);                                          // 使能TIMER2
}

首先是系統(tǒng)時鐘的初始化,然后依靠系統(tǒng)時鐘形成延時函數(shù)

systick_config();

然后是gpio的初始化

/**
 * GPIO配置,配置PC13引腳(LED2的引腳)配置PB0作為PWM輸出的引腳
 */
void gpio_config(void)
{
 /** PB0作為timer2通道2的pwm輸出 **/
 rcu_periph_clock_enable(RCU_GPIOB);                              // 配置GPIOB的時鐘
 rcu_periph_clock_enable(RCU_AF);                                 // 配置GPIO的AF復用功能,因為這個引腳要作為定時器的通道輸出。
 gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);// 初始化PB0的引腳,AF復用功能的推挽模式

 rcu_periph_clock_enable(RCU_GPIOC);                              // PC13作為LED2的輸出引腳
 gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);// 初始化PB0的引腳,AF復用功能的推挽模式
 gpio_bit_reset(GPIOC,GPIO_PIN_13);
}

PB0是TIMER2的CH2復用引腳。配置是AF(復用)的PP(推挽)模式。

PC13是LED2的引腳,配置為推挽輸出模式(OUT_PP)

然后是定時器配置

/**
 * 定時器的配置。配置TIMER2的CH2通道。
 */
void timer_config()
{
 timer_oc_parameter_struct timer_ocintpara;
 timer_parameter_struct timer_initpara;

 rcu_periph_clock_enable(RCU_TIMER2);    // 使能TIMER2定時器的時鐘

 timer_deinit(TIMER2);                   // 復位TIMER2

 /* TIMER2 配置 */
 timer_initpara.prescaler         = 108-1;                 // 預分頻因子。0~65535
 timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;    // 邊沿計數(shù)對齊
 timer_initpara.counterdirection  = TIMER_COUNTER_UP;      // 上升沿觸發(fā)
 timer_initpara.period            = 1000-1;                // 周期是100
 timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;      // 時鐘分頻值
 timer_initpara.repetitioncounter = 0;                     // 重復計數(shù)為0
 timer_init(TIMER2,&timer_initpara);                       // 初始化TIMER2

 /* PWM模式下的通道2配置 */
 timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;     // 通道輸出極性高
 timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;           // 通道輸出狀態(tài)使能
 timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;    // 通道互補輸出極性高
 timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;         // 通道互補輸出狀態(tài)不使能
 timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;    // 通道輸出空閑狀態(tài)低
 timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;   // 通道互補輸出空閑狀態(tài)低
 timer_channel_output_config(TIMER2,TIMER_CH_2,&timer_ocintpara);              // 定時器通道輸出配置

 timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,0);               // 輸出脈沖值為500
 timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);       // 通道輸出模式為PWM0
 timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);// 通道輸出影子不使能

 timer_auto_reload_shadow_enable(TIMER2);                       // 啟用自動重新加載陰影功能
 timer_primary_output_config(TIMER2,ENABLE);                    // 啟用TIMER主輸出功能
 timer_enable(TIMER2);                                          // 使能TIMER2
}

這兒主要考慮的是timer_channel_output_config(TIMER2,TIMER_CH_2,&timer_ocintpara);這句。因為我們要形成呼吸的效果,那這兒的輸出的脈沖值就得發(fā)生變化。其他配置和輸出方波一樣。

然后在主函數(shù)中使用延時,隨著系統(tǒng)時鐘中斷不斷進入,i的值不停增加。當系統(tǒng)時鐘中斷進入到50次時再逐漸將i變小。在這個過程中將i的值不停賦值給CH2CV。這樣占空比就一直在變化。

驗證時,需要將PB0的輸出接到PC13上。LED2燈會有呼吸燈效果。

while(1)
 {
 delay_1ms(10);
 if (SET == flag)
 {
 i++;
 } else {
 i--;
 }
 if (i > 250)
 {
 flag = RESET;
 }
 if (i <= 0 )
 {
 flag = SET;
 }
 /** 修改TIMER2的CH2通道的CH2CVL的寄存器值,也就是 **/
 /** timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,i);的i值 **/
 TIMER_CH2CV(TIMER2) = (uint32_t)i;
 }

3.4、定時器中斷輸出呼吸燈

利用TIMER2定時器的中斷來生成呼吸燈。

main.c

#include "gd32f10x.h"
#include "core_cm3.h"

void gpio_config(void);
void timer_config(void);

/**
 * 呼吸燈的實現(xiàn)(使用TIMER2定時實現(xiàn))
 */
 int main(void)
{
 gpio_config();
 timer_config();

 while(1){}
}

void gpio_config(void)
{
 /** PB0作為timer2通道2的pwm輸出 **/
 rcu_periph_clock_enable(RCU_GPIOB);                              // 配置GPIOB的時鐘
 rcu_periph_clock_enable(RCU_AF);                                 // 配置GPIO的AF復用功能,因為這個引腳要作為定時器的通道輸出。
 gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);// 初始化PB0的引腳,AF復用功能的推挽模式

 rcu_periph_clock_enable(RCU_GPIOC);                              // PC13作為LED2的輸出引腳
 gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);// 初始化PB0的引腳,AF復用功能的推挽模式
 gpio_bit_reset(GPIOC,GPIO_PIN_13);
}

void timer_config()
{
 timer_oc_parameter_struct timer_ocintpara;
 timer_parameter_struct timer_initpara;

 rcu_periph_clock_enable(RCU_TIMER2);    // 使能TIMER2定時器的時鐘

 timer_deinit(TIMER2);                   // 復位TIMER2

 /* TIMER2 配置 */
 timer_initpara.prescaler         = 3600-1;                // 預分頻因子。0~65535
 timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;    // 邊沿計數(shù)對齊
 timer_initpara.counterdirection  = TIMER_COUNTER_UP;      // 上升沿觸發(fā)
 timer_initpara.period            = 250-1;                 // 周期是250
 timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;      // 時鐘分頻值
 timer_initpara.repetitioncounter = 0;                     // 重復計數(shù)為0
 timer_init(TIMER2,&timer_initpara);                       // 初始化TIMER2

 /* PWM模式下的通道2配置 */
 timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_HIGH;     // 通道輸出極性高
 timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;           // 通道輸出狀態(tài)使能
 timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;    // 通道互補輸出極性高
 timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE;         // 通道互補輸出狀態(tài)不使能
 timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_LOW;    // 通道輸出空閑狀態(tài)低
 timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;   // 通道互補輸出空閑狀態(tài)低
 timer_channel_output_config(TIMER2,TIMER_CH_2,&timer_ocintpara);              // 定時器通道輸出配置

 timer_channel_output_pulse_value_config(TIMER2,TIMER_CH_2,0);               // 輸出脈沖值為動態(tài)值
 timer_channel_output_mode_config(TIMER2,TIMER_CH_2,TIMER_OC_MODE_PWM0);       // 通道輸出模式為PWM0
 timer_channel_output_shadow_config(TIMER2,TIMER_CH_2,TIMER_OC_SHADOW_DISABLE);// 通道輸出影子不使能

 timer_auto_reload_shadow_enable(TIMER2);                       // 啟用自動重新加載陰影功能
 timer_primary_output_config(TIMER2,ENABLE);                    // 啟用TIMER主輸出功能

 nvic_irq_enable(TIMER2_IRQn, 0, 2);                            // TIMER2中斷設(shè)置, 搶占優(yōu)先級為0, 次優(yōu)先級為2
 timer_interrupt_enable(TIMER2, TIMER_INT_UP);                  // 使能TIMER2更新中斷 
 timer_enable(TIMER2);                                          // 使能TIMER2
}

主函數(shù)中,剛開始不用聲明系統(tǒng)時鐘,因為我們不需要。GPIO設(shè)置一樣。還是PB0和PC13。

在定時器設(shè)置中需要開啟中斷

nvic_irq_enable(TIMER2_IRQn, 0, 2);                            // TIMER2中斷設(shè)置, 搶占優(yōu)先級為0, 次優(yōu)先級為2
timer_interrupt_enable(TIMER2, TIMER_INT_UP);                  // 使能TIMER2更新中斷 

然后在中斷函數(shù)文件中gd32f10x_it.c編寫中斷程序

#include "gd32f10x_it.h"
#include "gd32f10x_eval.h"

uint8_t indexWave[] = {1,1,2,2,3,4,6,8,10,14,19,25,33,44,59,80,
107,143,191,250,250,191,143,107,80,59,44,33,25,19,14,10,8,6,4,3,2,2,1,1};

/**
 * TIMER2中斷程序
 */
void TIMER2_IRQHandler(void)
{
 static uint8_t pwm_index = 0;      //用于PWM查表
 static uint8_t period_cnt = 0;  //用于計算周期數(shù)

 if(timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP))
 {
 /* 清除TIMER2 中斷標志位 */
 timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);

 period_cnt++;
 if(period_cnt >= 10)  //10次定時器中斷更改一次比較寄存器的值,輸出下一種脈沖寬的PWM波
 {
 //根據(jù)PWM表修改定時器的比較寄存器值
 TIMER_CH2CV(TIMER2) = indexWave[pwm_index];
 pwm_index++;  //標志PWM表的下一個元素
 //若PWM脈沖表已經(jīng)輸出完成一遍,重置PWM查表標志
 if( pwm_index >=  40) 
 {
 pwm_index=0; 
 }
 period_cnt=0;  //重置周期計數(shù)標志
 }
 } 
}

indexWave數(shù)組是占空比呈指數(shù)曲線變化的 PWM 信號。共40個數(shù)字。最大是250。把LED的亮度分成了0~250個等級。

本中斷服務(wù)函數(shù)在每次定時器更新事件發(fā)生時執(zhí)行一次。函數(shù)中使用了靜態(tài)變量 pwm_index 和 period_cnt,它們分別用來查找 PWM 表元素和記錄同樣占空

比的脈沖輸出了多少次。

本代碼的目的是每 10 次定時器中斷更新一次 PWM 表中的數(shù)據(jù)到比較寄存器中,當遍歷完 PWM 表的 40 個元素時,再重頭開始遍歷 PWM 表,周而復始,

重復 LED 的呼吸過程。

整個呼吸過程的時間計算方法如下:

因為定時器的 prescaler 設(shè)置為 3600 -1;

所以定時器的時鐘頻率:CK_TIMER2 = 108M / (prescaler +1) = 108000000 / 3600 = 30000HZ

即定時器的時鐘周期為:T = 1 / CK_TIMER2 = 1/30000 s

定時器的周期period為 250-1。

定時器的中斷周期就是 t = T * ( period + 1 ) = 1/30000 * 250 = 1/120s

pwm表有pwm_index=40個占空比數(shù)據(jù),同一個占空比輸出是10次中斷。

所以一個呼吸周期就是 Tf = t * 40 * 10 = 3.33s

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容