【单片机】CH32V307 控制LED01每隔1s闪烁一次

背景知识

主频main frequency" 或 "clock frequency.
指微控制器或其他数字电路的时钟振荡器的频率。在 CH32V307 或其他微控制器中,主频起到以下作用:
指令执行速度的决定因素:主频确定了微控制器每秒执行多少指令周期,从而影响整体性能和反应速度。
定时器和外设的时基:许多外设和定时器依赖于主频来确定其工作频率。例如,PWM 输出、ADC 转换速率等。
功耗管理:主频越高,微控制器的功耗通常也越高。因此,在对性能要求较低的应用中,可能会降低主频以节省能源。
系统同步:主频为整个系统提供了一个共同的时钟基准,有助于各个模块和外设之间的同步协作。

晶振oscillator。
external oscillator就是外部晶振。
晶振使用机械振动晶体,产生稳定频率震荡。晶振可以提供精确的时钟信号。
external crystal oscillator 是外晶振,精确度高,成本高,稳定性好
internal crystal oscillator 是内晶振,精确度稍低,成本低,易受温度,和电源电压影响

HSI_VALUE
外部晶振启动的timeout,通常需要稍微加大

GPIO Init

因为要点亮LED01,所以同样需要初始化GPIO

/********************************************************************
* 函 数 名       : GPIO_INIT
* 函数功能       : 初始化 GPIO
* 输    入          : 无
* 输    出          : 无
********************************************************************/
void GPIO_INIT(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure={0}; //GPIO的结构体定义
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE); // 在STM32对于外设的使用里面,每使用到一个外设,都要打开其对应的外设时钟
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;//    规定输出在 pin 11(LED01)
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//    输出模式:推挽输出(简单说时一种可以输出高低电平的模式)
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz; // 引脚的输出频率影响着引脚对于数据传输速度的快慢,以及影响芯片的功耗
    GPIO_Init(GPIOE, &GPIO_InitStructure);
}

GPIO_InitTypeDef 是一个GPIO结构体,用于存储 GPIO(通用输入/输出)初始化的相关设置。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); 开启高速APB时钟,每使用一个外设,都要打开其对应的外设时钟

Interrupt Init
/********************************************************************
* 函 数 名       : 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);
}

首先定义了一个空的定时器中断结构体
使用TIM6中断源,定义抢占优先级,子优先级,启用中断

TIM6 定时器 Init

这是这个任务的核心

/********************************************************************
* 函 数 名        : 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 );
}

这个init方法接收两个参数:定时器溢出值arr(类似while<100的100),预分频值psc(降低定时器频率,不然按照定时器自身的频率,会非常快就完成一次溢出。预分频器会将核心时钟速度除以(预分频值 + 1)来得到定时器的计数速度。例如,如果核心时钟是72MHz,预分频值是71(注意+1的存在),那么定时器的计数速度就会是 1MHz。) t i m e f r e q u e n c y = c l o c k f r e q u e n c y p s c + 1 time frequency = \frac{clock frequency}{psc + 1} timefrequency=psc+1clockfrequency

RCC_APB1PeriphClockCmd enable 对应时钟

定义arr,psc

设置clock division,设置向下计数模式

使用上面的设置初始化

enable 中断,当溢出时产生中断

enable 自动重新加载,溢出后立刻刷新arr

enable tim6,开始计数

终端服务程序函数
/********************************************************************
* 函 数 名     : 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) 状态
}

首先定义函数TIM6_IRQHandler并生命这是个快速中断
定义LED_Status 变量初始值为0

函数体中的功能如注释所示

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);        // 死循环
}

初始化,并一直循环
注意 TIM6_Init( 5000-1, 14400-1 ),用上面的计时器频率公式计算下。

在system_ch32v30x.c我们可以看到定义的sysclk频率是 #define SYSCLK_FREQ_72MHz 72000000

arr是5000-1,psc是14400-1

那我们自定定时器的freq = 72000000 / (14400 -1 +1) = 5000

也就是5000次/s,和arr值匹配

程序调用逻辑

main()

|-- GPIO_INIT()

|-- TIM6_Init()

|-- Interrupt_Init()

|-- while(1);

|-- [Interrupt Occurs]  

    |-- TIM6_IRQHandler()  
  1. 程序启动,进入main()函数
  2. GPIO初始化 (GPIO_INIT())
    1. 在这一步,GPIOE的Pin 11和Pin 12被配置为输出模式。这两个引脚通常用于连接LED。
  3. 定时器TIM6初始化 (TIM6_Init())
    1. 初始化TIM6定时器,设置其计数模式、预分频和溢出值(自动重装值)。预分频和溢出值的设定依赖于您想要的定时周期。
  4. 中断初始化 (Interrupt_Init())
    1. 配置NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)以允许TIM6中断,并设置其优先级。
  5. 进入无限循环 (while(1);)
    1. 主程序进入一个无限循环,等待中断事件的发生。
  6. 中断服务例程 (TIM6_IRQHandler())
    1. 当TIM6定时器达到预设的计数值并产生一个"更新"或"溢出"事件时,这个中断服务例程(ISR)会被调用。
    2. ISR会清除TIM6的更新标志,以便下一个中断。
    3. ISR也会改变LED(连接在GPIOE的Pin 11)的状态。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用C语言编写的示例代码,实现了一个基于PIC单片机的按键控制LED闪烁的功能: ```c #include <pic.h> // 定义常量 #define LED_PIN RB0 #define KEY_PIN RB1 // 延时函数,单位为毫秒 void delay(unsigned int ms) { unsigned int i, j; for(i = 0; i < ms; i++) for(j = 0; j < 120; j++); } // LED闪烁函数,每1秒闪烁一次 void blink() { LED_PIN = 1; // LED亮 delay(1000); // 延时1秒 LED_PIN = 0; // LED灭 delay(1000); // 延时1秒 } void main() { TRISB1 = 1; // 配置KEY_PIN为输入 TRISB0 = 0; // 配置LED_PIN为输出 while(1) { if(KEY_PIN == 0) // 检测按键是否按下 { blink(); // 执行LED闪烁函数 while(KEY_PIN == 0); // 等待按键松开 } } } ``` 在这个示例代码中,我们定义了两个常量:LED_PIN表示LED灯的控制引脚,KEY_PIN表示按键的输入引脚。我们通过配置TRISB1为1,将KEY_PIN设置为输入,配置TRISB0为0,将LED_PIN设置为输出。 在主函数中,我们使用一个while循环,不停地检测按键是否按下。当KEY_PIN等于0时,说明按键被按下,我们就调用blink函数来执行LED闪烁的操作。在blink函数中,我们先让LED_PIN变为1,使LED灯亮起来,然后延时1秒钟,再让LED_PIN变为0,使LED灯熄灭。最后再延时1秒钟,等待下一次闪烁。这样就能实现按键按下后,LED灯每1秒钟闪烁一次的功能。 需要注意的是,在检测按键是否按下时,我们使用了一个while循环,等待按键松开。这是为了避免按键的抖动干扰,确保只有当按键真正被按下时才执行闪烁操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值