CH32V307教程 [第三集] [时钟]
目录
基础知识
CH32V307 有三种“通用的”定时器:ADTM、GPTM、BCTM
ADTM (Advanced Timer, 高级定时器) 功能最多
GPTM (General-Purpose Timer, 通用定时器) 有基本的捕获比较、PWM 等功能
BCTM (Basic Timer, 基本定时器) 只有定时和触发 DAC 的功能
更改主频
在项目 User 组中打开 system_ch32v30x.c
,可以看到:
这里是设置芯片主频的地方,外部晶振的时钟经过锁相环(PLL)可以产生代码中预设的这些频率作为主频。默认 72MHz ,使用其它频率只需修改注释即可。
比如本例程的代码使用的是最高主频 144 MHZ,只需要修改成如下即可。
从 main.c
里可以看到包含的头文件,我们打开 debug.h
,再打开 ch32v30x.h
可以看到 ch32v30x.h
里边有:
#define HSE_VALUE ((uint32_t)8000000) /* Value of the External oscillator in Hz */
/* In the following line adjust the External High Speed oscillator (HSE) Startup Timeout value */
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x500) /* Time out for HSE start up */
#define HSI_VALUE ((uint32_t)8000000) /* Value of the Internal oscillator in Hz */
HSE_VALUE
是外部晶振的频率,一般是 8 MHz,赤菟开发板的外部晶振频率也是 8MHz。
HSE_STARTUP_TIMEOUT
是晶振启动超时阈值,默认值为 0x500,需修改为 0x4000。因为我们要使用外部晶振,如果不修改可能会因外部晶振启动时间较长而导致系统认为外部晶振启动失败。
修改后的代码为:
#define HSE_VALUE ((uint32_t)8000000) /* Value of the External oscillator in Hz */
/* In the following line adjust the External High Speed oscillator (HSE) Startup Timeout value */
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x4000) /* Time out for HSE start up */
#define HSI_VALUE ((uint32_t)8000000) /* Value of the Internal oscillator in Hz */
定时器中断
任务目标
控制 LED1 每隔 1 秒闪烁一次。
基础知识
从上文可知,基本定时器 (BCTM) 即可支持普通的定时器中断。
从芯片手册可知,基本定时器模块包含两个 16 位可自动重装的定时器(TIM6 和 TIM7),此处我们使用 TIM6 作为我们定时器中断的定时器。
查看 CH32V307 的时钟树:
查寄存器表可以得知,复位后,TIM6 的输入时钟就是系统时钟 System Clock (主频现在是 144 MHz)。
为了让 LED1 每隔 1 秒闪烁一次,我们可以让定时器每隔 0.5 秒溢出一次,即要计数 144M * 0.5 = 72M
个时钟周期。但是,定时器只有 16 位,是不够直接计到 72M 的,所以需要用到预分频器,将其分频系数设置为 14400,即可得到 10kHz 的频率,这样设置计数值为 5000 就可以做到每 0.5 秒溢出一次。
实现过程
新建工程,使用上文所述方法,修改晶振启动超时阈值并将芯片主频设置为 144 MHz。
由于第二篇教程外部中断代码与定时器代码的相似度极高,我们可以直接将GPIO教程代码 main.c 文件内容中的代码复制到本工程的 main.c。
GPIO_INIT()
函数可以直接继续用。
此时需要一个初始化定时器的代码,可以直接在官方例程 CH32V307EVT 的 EXAM -> TIM
中借鉴相似的例程,最后在 Output_Compare_Mode
中找到 void TIM1_OutCompare_Init( u16 arr, u16 psc, u16 ccp )
。
将其修改为初始化 TIM6 的代码。
/********************************************************************
* 函 数 名 : TIM6_Init
* 函数功能 : 初始化 定时器 TIM6
* 输 入 : arr:自动重装值,psc 预分频系数
* 输 出 : 无
********************************************************************/
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 );
}
修改初始化外部中断的函数 EXTI_INT_INIT()
改为初始化定时器中断的函数。
/********************************************************************
* 函 数 名 : EXTI_INT_INIT
* 函数功能 : 初始化外部中断
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void EXTI_INT_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure={0};
EXTI_InitTypeDef EXTI_InitStructure={0};
NVIC_InitTypeDef NVIC_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* GPIOA ----> EXTI_Line0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
EXTI_InitStructure.EXTI_Line=EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //按下为高电平,用上升沿
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
删除 EXTI_Init()
和 GPIO_Init()
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure={0};
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
右键点击 EXTI0_IRQn
,选择打开声明,得知定时器中断 6 的IRQn 的名字为 TIM6_IRQn
最终初始化定时器中断的函数为:
/********************************************************************
* 函 数 名 : Interrupt_Init
* 函数功能 : 初始化定时器中断
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure={0};
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
按照以下所需功能完成 main()
函数。
调用 GPIO_INIT()
初始化 GPIO。
初始化定时器,由上文可知,要把预分配系数设置为 14400-1 ,计数值设置为 5000-1(由于寄存器是从 0 开始计数 的所以要 -1)。
初始化中断。
进入死循环等待中断。
因此,最终的 main()
函数为:
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
GPIO_INIT(); // 初始化 GPIO
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 LED 1 秒闪烁一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
Interrupt_Init();//初始化定时器中断
while(1); // 死循环
}
借鉴外部中断的服务程序,再根据任务目标 控制 LED1 每隔 1 秒闪烁一次
完成定时器中断的中断服务程序。
/********************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : 中断服务程序的函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t LED_Status = 0; // 中断里使用的变量加 volatile 可当成全局变量
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update);//清除标志位
LED_Status = !LED_Status ; // 将 LED 状态值取反
GPIO_WriteBit(GPIOE, GPIO_Pin_11, LED_Status); // 配置 PE11 (即 LED1) 状态
}
中断服务程序里的操作为三步:
- 清除标志位
- 将 LED 状态值取反
- 配置 PE11 引脚状态
实验代码
/**
******************************************************************
* @file main.c
* @author xy,Benue
* @version V1.0
* @date 2022-1-15
* @brief 控制 LED1 1秒闪烁一次。
******************************************************************
* @attention
* VeriMake 用于CH32V307例程
******************************************************************
*/
#include "debug.h"// 包含 CH32V307 的头文件,C 标准单元库和delay()函数
/********************************************************************
* 函 数 名 : GPIO_INIT
* 函数功能 : 初始化 GPIO
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure={0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
}
/********************************************************************
* 函 数 名 : TIM6_Init
* 函数功能 : 初始化 定时器 TIM6
* 输 入 : arr:计数值,psc 预分频系数
* 输 出 : 无
********************************************************************/
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 );
}
/********************************************************************
* 函 数 名 : Interrupt_Init
* 函数功能 : 初始化定时器中断
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure={0};
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
GPIO_INIT(); // 初始化 GPIO
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断控制器的优先级分组为 占优先级 2位 ,优先级2位。
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 LED 1 秒闪烁一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
Interrupt_Init();//初始化定时器中断
while(1); // 死循环
}
/********************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : 中断服务程序的函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t LED_Status = 0; // 中断里使用的变量加 volatile 可当成全局变量
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update);//清除标志位
LED_Status = !LED_Status ; // 将 LED 状态值取反
GPIO_WriteBit(GPIOE, GPIO_Pin_11, LED_Status); // 配置 PE11 (即 LED1) 状态
}
实验现象
LED1 每隔 1s 闪烁一次。
PWM
任务目标
配置 PWM 改变 LCD 背光亮度
基础知识
在 资料 -> openCH 赤菟开发板开发资源v1.0.0
中的引脚表可以看到,控制 LCD 屏幕背光的引脚是 PB14,是 TIM1 通道 2 的互补输出。
实现过程
新建工程,将芯片主频设置为 144Mhz,由于需要定时调整 LCD 背光亮度,直接复制上文定时中断 main.c
中的代码即可。
因为不需要用到 LED1,所以可以删除函数 GPIO_INIT()
。
此时还需要一个初始化定时器互补输出的代码,可以直接去借鉴官方例程 CH32V307EVT 的 EXAM -> TIM -> ComplementaryOutput_DeadTime
中的 TIM1_Dead_Time_Init()
函数。
/*********************************************************************
* @fn TIM1_Dead_Time_Init
*
* @brief Initializes TIM1 complementary output and dead time.
*
* @param arr - the period value.
* psc - the prescaler value.
* ccp - the pulse value.
*
* @return none
*/
void TIM1_Dead_Time_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure={0};
TIM_OCInitTypeDef TIM_OCInitStructure={0};
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure={0};
TIM_BDTRInitTypeDef TIM_BDTRInitStructure={0};
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_TIM1, ENABLE );
/* TIM1_CH1 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );
/* TIM1_CH1N */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStructure );
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = ccp;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
TIM_OC1Init( TIM1, &TIM_OCInitStructure );
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Disable;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Disable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_OFF;
TIM_BDTRInitStructure.TIM_DeadTime = 0xFF;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig( TIM1, &TIM_BDTRInitStructure );
TIM_CtrlPWMOutputs(TIM1, ENABLE );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_ARRPreloadConfig( TIM1, ENABLE );
TIM_Cmd( TIM1, ENABLE );
}
修改为 PB14 TIM1 通道 2 的互补输出。
/*******************************************************************************
* 函 数 名 : TIM1_Init
* 函数功能 : Initializes TIM1
* 输 入 : arr: 周期值
* psc: 预分频值
* ccp: 脉冲宽度
* 输 出 : None
*******************************************************************************/
void TIM1_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_TIM1, ENABLE );
/* TIM1_CH2N */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStructure );
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = ccp;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; //高电平有效
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //停止时为低电平
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_CtrlPWMOutputs(TIM1, ENABLE );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_ARRPreloadConfig( TIM1, ENABLE );
TIM_Cmd( TIM1, ENABLE );
}
在 main() 函数中添加函数初始化定时器互补输出。
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断控制器的优先级分组为 占优先级 2位 ,优先级2位。
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 LED 1 秒闪烁一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
TIM1_Init(5000-1,14400-1,2500);//周期值5000-1,预分频系数14400-1,脉冲宽度2500
Interrupt_Init();//初始化定时器中断
while(1); // 死循环
}
若此时尝试编译下载,会发现屏幕在以肉眼可见的频率闪烁,这是因为 PWM 周期太长(0.5s),我们可以将预分配系数修改为 0 来加快 PWM 频率。
最终的 main() 函数为:
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断控制器的优先级分组为 占优先级 2位 ,优先级2位。
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 LED 1 秒闪烁一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
TIM1_Init(5000-1,0,2500);//周期值5000-1,预分频系数0,脉冲宽度2500
Interrupt_Init();//初始化定时器中断
while(1); // 死循环
}
修改定时中断服务函数让背光亮度定时更改。
/********************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : 中断服务程序的函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t brightness = 0; // 中断里使用的变量加 volatile 可当成全局变量
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update); //清标志位
brightness += 500;//每0.5s亮度增加
brightness %= 5000;//亮度达到5000以上时,将亮度重置为1
TIM_SetCompare2(TIM1, brightness);
}
实验代码
/**
******************************************************************
* @file main.c
* @author xy,Benue
* @version V1.0
* @date 2022-1-15
* @brief lcd 背光亮度变化。
******************************************************************
* @attention
* VeriMake 用于CH32V307例程
******************************************************************
*/
#include "debug.h"// 包含 CH32V307 的头文件,C 标准单元库和delay()函数
/********************************************************************
* 函 数 名 : TIM6_Init
* 函数功能 : 初始化 定时器 TIM6
* 输 入 : arr:计数值,psc 预分频系数
* 输 出 : 无
********************************************************************/
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 );
}
/*******************************************************************************
* 函 数 名 : TIM1_Init
* 函数功能 : Initializes TIM1
* 输 入 : arr: 周期值
* psc: 预分频值
* ccp: 脉冲宽度
* 输 出 : None
*******************************************************************************/
void TIM1_Init( u16 arr, u16 psc, u16 ccp )
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB | RCC_APB2Periph_TIM1, ENABLE );
/* TIM1_CH2N */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOB, &GPIO_InitStructure );
TIM_TimeBaseInitStructure.TIM_Period = arr;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit( TIM1, &TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = ccp;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; //高电平有效
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //停止时为低电平
TIM_OC2Init( TIM1, &TIM_OCInitStructure );
TIM_CtrlPWMOutputs(TIM1, ENABLE );
TIM_OC1PreloadConfig( TIM1, TIM_OCPreload_Disable );
TIM_ARRPreloadConfig( TIM1, ENABLE );
TIM_Cmd( TIM1, ENABLE );
}
/********************************************************************
* 函 数 名 : Interrupt_Init
* 函数功能 : 初始化定时器中断
* 输 入 : 无
* 输 出 : 无
********************************************************************/
void Interrupt_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure={0};
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/********************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断控制器的优先级分组为 占优先级 2位 ,优先级2位。
TIM6_Init( 5000-1, 14400-1 ); // 初始化定时器,让 LED 1 秒闪烁一次,我们需要让定时器 0.5 秒溢出,要计数 `144M * 0.5 = 72M` 个时钟周期,而定时器只有16位,这是不够的。需要用到预分频器,设分频系数为 14400,可以得到 10KHz 的定时器时钟,这样设置计数值 5000 就可以做到 0.5 ms 定时。
TIM1_Init(5000-1,0,2500);//周期值5000-1,预分频系数0,脉冲宽度2500
Interrupt_Init();//初始化定时器中断
while(1); // 死循环
}
/********************************************************************
* 函 数 名 : TIM6_IRQHandler
* 函数功能 : 中断服务程序的函数
* 输 入 : 无
* 输 出 : 无
*********************************************************************/
void TIM6_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
volatile uint16_t brightness = 0; // 中断里使用的变量加 volatile 可当成全局变量
void TIM6_IRQHandler(void)
{
TIM_ClearFlag(TIM6, TIM_FLAG_Update); //清标志位
brightness += 500;//每0.5s亮度增加
brightness %= 5000;//亮度达到5000以上时,将亮度重置为1
TIM_SetCompare2(TIM1, brightness);
}
实验现象
屏幕背光亮度会每隔 0.5s 提高一些,达到峰值后重置为最暗,再继续慢慢提高,依此往复。