CH32V307系統(tǒng)提供了多個類型的定時器,具體可查看手冊了解:
這次的分享,我們使用到的定時器為系統(tǒng)時基定時器和基本定時器TIM6。
閃爍使用的LED,為LED1,使用連接線將LED1和PA0連接即可;實際運行時,間隔1秒閃爍一次。
有的同學(xué)可能會說,使用Delay_Ms或Delay_Us做演示,也能實現(xiàn)LED閃爍呀!
但在Delay的時候,你的程序,需要在這個地方,等待Delay時間后,才會繼續(xù)運行。
而使用中斷,我們的程序,能夠繼續(xù)運行做其他的事情,等到定時中斷到來的時候,才處理LED的閃爍。這樣程序的處理效率將會更高。
一、系統(tǒng)時基定時器SysTick
現(xiàn)在,我們了解一下系統(tǒng)時基定時器SysTick。
系統(tǒng)時基定時器:這是內(nèi)核控制器自帶的一個 64 位可選遞增或遞減的計數(shù)器,用于產(chǎn)生 SYSTICK 異常(異常號:15),可專用于實時操作系統(tǒng),為系統(tǒng)提供“心跳”節(jié)律,也可當(dāng)成一個標(biāo)準(zhǔn)的 64 位計數(shù)器。具有自動重加 載功能及可編程的時鐘源。
通過查看提供的實例和資料,了解到如下的關(guān)鍵信息:
1、系統(tǒng)的運行頻率可以工作在72MHz,也可以工作在144MHz,通過system_ch32v30x.h/system_ch32v30x.c來修改:
// #define SYSCLK_FREQ_72MHz 72000000
(左右移動查看全部內(nèi)容)
在我的實例中,設(shè)置工作在144MHz。
2、在設(shè)置SysTick的時候,通過SysTick->CMP來設(shè)置定時中斷周期:SysTick->CMP = SystemCoreClock / 1000 * 1000; //后面的1000代表1000HZ(那就是1ms進(jìn)一次中斷),*1000 表示 1000ms進(jìn)入一次
3、有人會遇到,中斷只進(jìn)入一次,經(jīng)過請教沁恒的陶工,了解到如下處理方法:
// 中斷只進(jìn)入一次的問題解決方法:
// 1. 如果使用沁恒提供的工具鏈,則使用如下的聲明:
// void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
// 2. 如果使用通用的risc-v工具鏈,如我在macOS下使用賽昉提供的工具鏈,則使用如下的聲明:
void SysTick_Handler(void) __attribute__((interrupt()));
(左右移動查看全部內(nèi)容)
最終,具體的代碼如下:
/*
使用VTF IRQ中斷控制LED閃爍
*/
// 中斷只進(jìn)入一次的問題解決方法:
// 1. 如果使用沁恒提供的工具鏈,則使用如下的聲明:
// void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
// 2. 如果使用通用的risc-v工具鏈,如我在macOS下使用賽昉提供的工具鏈,則使用如下的聲明:
void SysTick_Handler(void) __attribute__((interrupt()));
// LED狀態(tài)
volatile uint16_t LED_Status = 0; // 中斷里使用的變量加 volatile 可當(dāng)成全局變量
// 初始化 GPIO
void GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// 初始化 SysTick 定時器
void SysTick_init(void)
{
/*配置中斷優(yōu)先級*/
NVIC_InitTypeDef NVIC_InitStructure = {0};
NVIC_InitStructure.NVIC_IRQChannel = SysTicK_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//搶占式優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//響應(yīng)式優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_Init(&NVIC_InitStructure);
/*配置定時器*/
SysTick->CTLR= 0;
SysTick->SR = 0;
SysTick->CNT = 0;
SysTick->CMP = SystemCoreClock / 1000 * 1000; //后面的1000代表1000HZ(那就是1ms進(jìn)一次中斷),*1000 表示 1000ms進(jìn)入一次
SysTick->CTLR= 0xf;
}
int main(void)
{
/* Initialize board components. */
BOARD_SystemClock_Config();
BOARD_IOMUX_Init();
BOARD_Peripheral_Init();
GPIO_INIT(); // 初始化 GPIO
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置中斷控制器的優(yōu)先級分組為 占優(yōu)先級 2位 ,優(yōu)先級2位。
USART_Printf_Init(115200);
printf("SystemClk:%drn", SystemCoreClock);
printf("Interrupt SysTick Testrn");
SysTick_init();
while (1)
{
}
}
// 中斷服務(wù)處理
void SysTick_Handler(void)
{
SysTick->SR = 0;
LED_Status = !LED_Status; // 將 LED 狀態(tài)值取反
GPIO_WriteBit(GPIOA, GPIO_Pin_0, LED_Status); // 配置 PA0 (即 LED1) 狀態(tài)
printf("Toggle LED by SysTick: %drn", LED_Status);
}
(左右移動查看全部內(nèi)容)
在上述代碼中,關(guān)鍵部分如下:
GPIO_INIT:初始化GPIO,使用PA0;記得預(yù)先連接LED1和PA0
SysTick_init:SysTick初始化,主要設(shè)置中斷周期
SysTick_Handler:中斷服務(wù)處理,其內(nèi)部的操作需要狠準(zhǔn)快
除了板載的LED1(前提是連好了線)會間隔1秒閃爍一次,通過串口工具,我們也可以看到輸出的調(diào)試信息:
二、基本定時器TIM6
然后,我們再來了解一下基本定時器。
基本定時器:基本定時器是一個 16 位自動裝載計數(shù)器,支持 16 位可編程預(yù)分頻器??梢晕粩?shù)模轉(zhuǎn)換(DAC)提供時鐘,觸發(fā) DAC 的同步電路?;径〞r器之間是互相獨立的,互不共享任何資源。
CH32V307提供了兩個基本定時器TIM6和TIM7,用法是一樣的,下面的實例中,使用TIM6。
通過官方提供的實例和資料了解到,基本定時器使用的過程中,有兩個設(shè)置是最重要的:
TIM_Prescaler:定時器預(yù)分頻器設(shè)置,時鐘源經(jīng)該預(yù)分頻器才是定時器時鐘。
TIM_Period:定時器周期,實際就是設(shè)定自動重載寄存器的值。
這兩個參數(shù)結(jié)合就能實現(xiàn)實際所需要的定時。
根據(jù)定時器時鐘的頻率,比如時鐘的頻率是144MHz,可以理解為1秒鐘MCU會自己數(shù)144M次,預(yù)分頻系數(shù)就是將頻率分割,比如分頻系數(shù)是144,則該時鐘的頻率會變成144MHZ/144=1MHz,但是在設(shè)置的時候要注意,數(shù)值應(yīng)該是144-1。
為了讓 LED1間隔 1 秒閃爍一次,我們需要讓定時器 1 秒溢出,要計數(shù) 144M * 1 = 144M 個時鐘周期,而定時器只有16位,最大65535,所以這是不夠的。因此,需要用到預(yù)分頻器,設(shè)分頻系數(shù)為 14400,可以得到 10KHz 的定時器時鐘,這樣設(shè)置計數(shù)值 10000 就可以做到 1s 定時。
最終具體的代碼如下:
/*
使用基本定時器TIM6控制LED閃爍
*/
// 中斷只進(jìn)入一次的問題解決方法:
// 1. 如果使用沁恒提供的工具鏈,則使用如下的聲明:
// void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
// 2. 如果使用通用的risc-v工具鏈,如我在macOS下使用賽昉提供的工具鏈,則使用如下的聲明:
void TIM6_IRQHandler(void) __attribute__((interrupt()));
// LED狀態(tài)
volatile uint16_t LED_Status = 0; // 中斷里使用的變量加 volatile 可當(dāng)成全局變量
// 初始化 GPIO
void GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
// 初始化定時器 TIM6
void TIM6_Init(u16 arr, u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
TIM_ARRPreloadConfig(TIM6, ENABLE);
TIM_Cmd(TIM6, ENABLE);
}
// 初始化定時器中斷
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure = {0};
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
/* Initialize board components. */
BOARD_SystemClock_Config();
BOARD_IOMUX_Init();
BOARD_Peripheral_Init();
GPIO_INIT(); // 初始化 GPIO
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設(shè)置中斷控制器的優(yōu)先級分組為 占優(yōu)先級 2位 ,優(yōu)先級2位。
USART_Printf_Init(115200);
TIM6_Init(10000 - 1, 14400 - 1); // 初始化定時器,讓 LED 1 秒閃爍一次,我們需要讓定時器 1 秒溢出,要計數(shù) `144M * 1 = 144M` 個時鐘周期,而定時器只有16位,這是不夠的。需要用到預(yù)分頻器,設(shè)分頻系數(shù)為 14400,可以得到 10KHz 的定時器時鐘,這樣設(shè)置計數(shù)值 10000 就可以做到 1s 定時。
Interrupt_Init(); //初始化定時器中斷
printf("SystemClk:%drn", SystemCoreClock);
printf("Interrupt TIM6 Testrn");
while (1)
{
}
}
// 中斷服務(wù)處理
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update); //清除標(biāo)志位
LED_Status = !LED_Status; // 將 LED 狀態(tài)值取反
GPIO_WriteBit(GPIOA, GPIO_Pin_0, LED_Status); // 配置 PE11 (即 LED1) 狀態(tài)
printf("Toggle LED by TIM6: %drn", LED_Status);
}
(左右移動查看全部內(nèi)容)
在上述代碼中,關(guān)鍵部分如下:
GPIO_INIT:初始化GPIO,使用PA0;記得預(yù)先連接LED1和PA0
TIM6_init:TIM6初始化,主要設(shè)置分頻和時間周期
Interrupt_Init:終端初始化
TIM6_IRQHandler中斷服務(wù)處理,其內(nèi)部的操作同樣需要狠準(zhǔn)快
除了板載的LED1(前提是連好了線)會間隔1秒閃爍一次,通過串口工具,我們也可以看到輸出的調(diào)試信息:
更多熱點文章閱讀
原文標(biāo)題:【試用報告】沁恒CH32V307評估板體驗:定時器使用基礎(chǔ)
文章出處:【微信公眾號:電子發(fā)燒友論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
-
電子技術(shù)
+關(guān)注
關(guān)注
18文章
853瀏覽量
55510 -
電子發(fā)燒友論壇
+關(guān)注
關(guān)注
4文章
197瀏覽量
990 -
ch32
+關(guān)注
關(guān)注
0文章
73瀏覽量
589
原文標(biāo)題:【試用報告】沁恒CH32V307評估板體驗:定時器使用基礎(chǔ)
文章出處:【微信號:gh_9b9470648b3c,微信公眾號:電子發(fā)燒友論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論