当前位置: 首页 > news >正文

GD32自学笔记:5.定时器中断

        定时器中断功能主要是两点:

        1.怎么配置的定时器中断时间间隔;

        2.中断里长什么样

一、定时器中断配置函数

        直接在bsp_basic_timer.c里找到下面函数:

void basic_timer_config(uint16_t pre,uint16_t per)
{/* Ò»¸öÖÜÆÚµÄʱ¼äT = 1/f, ¶¨Ê±Ê±¼ätime = T * ÖÜÆÚÉèÔ¤·ÖƵֵλpre,ÖÜÆÚλpertime = (pre + 1) * (per + 1) / psc_clk*/timer_parameter_struct timere_initpara; 							// ¶¨Ò嶨ʱÆ÷½á¹¹Ìå/* ¿ªÆôʱÖÓ */rcu_periph_clock_enable(BSP_TIMER_RCU); 							// ¿ªÆô¶¨Ê±Æ÷ʱÖÓ/* CK_TIMERx = 4 x CK_APB1  = 4x50M = 200MHZ */rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // ÅäÖö¨Ê±Æ÷ʱÖÓtimer_deinit(BSP_TIMER);														  // ¸´Î»¶¨Ê±Æ÷/* ÅäÖö¨Ê±Æ÷²ÎÊý */timere_initpara.prescaler = pre-1;                    //  ʱÖÓÔ¤·ÖƵֵ 0-65535   psc_clk = CK_TIMER / pretimere_initpara.alignedmode = TIMER_COUNTER_EDGE;     // ±ßÔµ¶ÔÆë                  timere_initpara.counterdirection = TIMER_COUNTER_UP;  // ÏòÉϼÆÊý    timere_initpara.period = per-1;                       // ÖÜÆÚ  /* ÔÚÊäÈë²¶»ñµÄʱºòʹÓà  Êý×ÖÂ˲¨Æ÷ʹÓõIJÉÑùƵÂÊÖ®¼äµÄ·ÖƵ±ÈÀý */timere_initpara.clockdivision = TIMER_CKDIV_DIV1;     // ·ÖƵÒò×Ó         /* Ö»Óи߼¶¶¨Ê±Æ÷²ÅÓÐ ÅäÖÃΪx£¬¾ÍÖØ¸´x+1´Î½øÈëÖÐ¶Ï */    timere_initpara.repetitioncounter = 0;							  // ÖØ¸´¼ÆÊýÆ÷ 0-255  timer_init(BSP_TIMER,&timere_initpara);								// ³õʼ»¯¶¨Ê±Æ÷/* ÅäÖÃÖжÏÓÅÏȼ¶ */nvic_irq_enable(BSP_TIMER_IRQ,3,2); 									// ÉèÖÃÖжÏÓÅÏȼ¶Îª 3,2/* ʹÄÜÖÐ¶Ï */timer_interrupt_enable(BSP_TIMER,TIMER_INT_UP);       // ʹÄܸüÐÂʼþÖÐ¶Ï /* ʹÄܶ¨Ê±Æ÷ */timer_enable(BSP_TIMER);
}

        定时器中断初始化流程:

  1. 启用定时器时钟

    • 代码rcu_periph_clock_enable(BSP_TIMER_RCU);
    • 功能:开启定时器模块的时钟(BSP_TIMER_RCU 定义了具体定时器外设,如 TIMER0)。
    • 说明:通过 RCU(Reset and Clock Unit)模块为定时器提供时钟信号。
  2. 配置定时器时钟分频

    • 代码rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);
    • 功能:设置定时器时钟为 APB1 时钟的 4 倍(4 × 50MHz = 200MHz)。
    • 说明:定时器时钟频率(CK_TIMER)决定计数速度,影响定时精度。
  3. 复位定时器

    • 代码timer_deinit(BSP_TIMER);
    • 功能:将定时器寄存器复位到默认状态,确保无残留配置。
    • 说明:清除之前的配置,为新设置做准备。
  4. 配置定时器参数

    • 代码
      timer_parameter_struct timere_initpara; timere_initpara.prescaler = pre-1; // 预分频值                                                                                                        timere_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边沿对齐 timere_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数 timere_initpara.period = per-1; // 周期                    timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // 时钟分频因子 timere_initpara.repetitioncounter = 0; // 重复计数器            timer_init(BSP_TIMER, &timere_initpara);
    • 功能
      • 定义并初始化定时器参数结构体 timer_parameter_struct
      • 设置预分频值 (pre-1),定时器计数频率 = CK_TIMER / (pre)。
      • 配置为边沿对齐模式(TIMER_COUNTER_EDGE)和向上计数(TIMER_COUNTER_UP)。
      • 设置周期值 (per-1),决定定时器溢出周期,定时时间 = (pre × per) / CK_TIMER。
      • 时钟分频因子为 1(TIMER_CKDIV_DIV1),不额外分频。
      • 重复计数器为 0(repetitioncounter = 0),无重复计数,适用于基本定时器。
      • 调用 timer_init 初始化定时器寄存器。
    • 说明定时时间计算公式为 time = (pre × per) / CK_TIMER,例如,若 pre=20000per=10000, CK_TIMER=200MHz,则 time = (20000 × 10000) / 200,000,000 = 1秒
  5. 配置中断优先级

    • 代码nvic_irq_enable(BSP_TIMER_IRQ, 3, 2);
    • 功能:启用定时器中断(BSP_TIMER_IRQ),设置 NVIC 中断优先级为抢占优先级 3,子优先级 2。
    • 说明:确保定时器中断能够被 CPU 响应,优先级决定中断处理顺序。
  6. 使能定时器更新中断

    • 代码timer_interrupt_enable(BSP_TIMER, TIMER_INT_UP);
    • 功能:启用定时器更新事件中断(TIMER_INT_UP),当计数器溢出时触发中断。
    • 说明:更新事件发生在计数器达到 per-1 并溢出时,触发中断处理程序。
  7. 启动定时器

    • 代码timer_enable(BSP_TIMER);
    • 功能:使能定时器,开始计数。
    • 说明:定时器按照配置的频率和周期运行,定期触发中断。

二、中断函数

        同样在bsp_basic_timer.c中的中断函数如下:

void BSP_TIMER_IRQHANDLER(void)
{/* ÕâÀïÊǶ¨Ê±Æ÷ÖÐ¶Ï */if(timer_interrupt_flag_get(BSP_TIMER,TIMER_INT_FLAG_UP) == SET){timer_interrupt_flag_clear(BSP_TIMER,TIMER_INT_FLAG_UP);  // Çå³ýÖжϱê־λ /* Ö´Ðй¦ÄÜ */gpio_bit_toggle(PORT_LED2,PIN_LED2);                      // ·­×ªledprintf("BSP_TIMER_IRQHANDLER!\r\n");          						// ´®¿Ú´òÓ¡BSP_TIMER_IRQHANDLER!}
}

        定时器中断函数在执行功能前主要进行了两个操作:

  1. 检查中断标志

    • 代码if(timer_interrupt_flag_get(BSP_TIMER, TIMER_INT_FLAG_UP) == SET)
    • 功能:检查定时器 BSP_TIMER 的更新中断标志位(TIMER_INT_FLAG_UP)是否置位(SET)。
    • 说明
      • 更新中断标志在定时器计数器溢出时自动置位,表示一次定时周期完成。
      • 条件判断确保只处理更新中断,避免误处理其他中断类型。
      • BSP_TIMER 是预定义的定时器,此处对应的是TIMER5,由历史会话中的 basic_timer_config 配置。

  1. 清除中断标志

    • 代码timer_interrupt_flag_clear(BSP_TIMER, TIMER_INT_FLAG_UP);
    • 功能:清除定时器的更新中断标志位。
    • 说明
      • 清除标志位以确保下一次中断能够正确触发。
      • 如果不清除,中断可能持续触发,导致程序异常或死锁。
      • 这是中断服务函数的标准操作,防止重复进入中断。

三、如何调用

        在main.c里面只需要配置一下 basic_timer_config函数就行,调用代码如下:

basic_timer_config(20000,10000);  // ¶¨Ê±Æ÷³õʼ»¯
  • 定时时间公式:time = (pre × per) / CK_TIMER
  • 代入参数
    pre = 20000 (实际预分频系数 19999 + 1)
  • per = 10000 (实际周期 9999 + 1)
  • CK_TIMER = 200,000,000 Hz
  • time = (20000 × 10000) / 200,000,000 = 200,000,000 / 200,000,000 = 1 秒

        运行后可以看到串口一直在输出:

        并且开发板上对应的LED2在闪烁

四、思考

为什么常常使用ADC时不直接放在 while(1) 主循环中使用 delay 控制采样间隔?

        不是不行,也可以,但是:

  1. 延时不精确,受主循环影响

    • 在主循环中使用 delay_ms(500) 等软件延时函数时,延时基于 CPU 空闲循环(如空指令循环)实现,但实际时间会因编译优化、系统时钟偏差或中断干扰而抖动(例如,串口中断或 GPIO 操作可能打断延时,导致采样间隔不均匀)。
    • 问题:采样间隔(如每 500ms 采样一次)无法严格控制,可能导致数据采集不均匀,影响信号处理精度(如历史会话中提到的 ADC 采样)。
    • 示例:若主循环中插入其他任务(如 LED 控制),延时函数会阻塞整个 CPU,造成采样时机漂移。
  2. 阻塞主循环,降低系统响应性

    • delay 是“忙等待”(busy-waiting),CPU 在延时期间完全闲置,无法处理其他任务(如用户输入、通信或外设事件)。
    • 问题:在多任务环境中(如实时控制系统),主循环被阻塞会导致系统响应迟钝,甚至错过关键事件。例如,定时器中断每 1 秒触发 LED 翻转,若也需要ADC 采样用 delay 阻塞 500ms,主循环就无法及时响应其他中断。
    • 效率低:CPU 利用率低下,浪费资源,尤其在低功耗应用中不友好。
  3. 不适合实时性和周期性要求

    • ADC 采样往往需要固定间隔(如传感器数据采集),主循环的软件延时无法保证硬件级精度,受温度、电压等因素影响。
    • 问题:在高速采样场景(如音频或电机控制),间隔抖动可能导致数据失真或系统不稳定。若 ADC 采样直接用 delay,与定时器中断(1 秒间隔)冲突,可能干扰整体流程。

为什么ADC采样常放到定时器中断中?

  1. 精确的硬件定时

    • 定时器使用硬件计数器(如 basic_timer_config(20000, 10000) 配置 1 秒间隔),基于系统时钟(CK_TIMER = 200MHz)精确控制中断触发时机,间隔误差极小(纳秒级)。
    • 优势:不受主循环影响,确保 ADC 采样严格周期性(如每 500ms 触发一次),提高数据一致性和精度。定时器可自动重载,无需软件干预。
  2. 非阻塞执行,提高系统效率

    • 中断服务函数(如 BSP_TIMER_IRQHANDLER)在定时器溢出时自动调用,执行 ADC 采样后快速返回,主循环继续运行其他任务。
    • 优势:避免阻塞,支持多任务并行。例如,历史会话中定时器中断可用于 ADC 触发采样,同时主循环处理串口输出或 LED 控制,CPU 利用率高。结合 DMA(直接内存访问)可进一步自动化数据传输,减少 CPU 负担。
  3. 增强实时性和可扩展性

    • 中断优先级机制(如 NVIC 设置抢占优先级 3)确保 ADC 采样及时响应,适用于实时系统(如工业控制)。
    • 优势:易于调整间隔(修改 pre 和 per 参数),并支持多通道采样(如定时器触发 ADC 多通道)。1 秒中断可扩展为 500ms ADC 采样,结合 timer_interrupt_enable(BSP_TIMER, TIMER_INT_UP) 实现周期触发。

文章转载自:

http://rkbWIXYp.zztkt.cn
http://j7UvvcEK.zztkt.cn
http://dbJh49Yu.zztkt.cn
http://Omz9fFHO.zztkt.cn
http://UAthUfHj.zztkt.cn
http://LzxjPHVX.zztkt.cn
http://3nLNy5Ts.zztkt.cn
http://VZTEj3uM.zztkt.cn
http://tJfLWsXP.zztkt.cn
http://eDR7lwiA.zztkt.cn
http://FvLpMOln.zztkt.cn
http://xk4aLND7.zztkt.cn
http://zB36O3aQ.zztkt.cn
http://dToI9y01.zztkt.cn
http://g5ox2jA3.zztkt.cn
http://UL30QYnB.zztkt.cn
http://LzBSZWO4.zztkt.cn
http://fiWvUnzH.zztkt.cn
http://UsVbeZG7.zztkt.cn
http://bBfQWi4i.zztkt.cn
http://qg9J8IrF.zztkt.cn
http://dwYUeyEw.zztkt.cn
http://ZMy4TA1S.zztkt.cn
http://NBS69Pz2.zztkt.cn
http://KQ54fvqd.zztkt.cn
http://VuXXBRiK.zztkt.cn
http://toxdohLb.zztkt.cn
http://KI07sSH3.zztkt.cn
http://exVXUijy.zztkt.cn
http://FJ5gznda.zztkt.cn
http://www.dtcms.com/a/371734.html

相关文章:

  • 前端三件套简单学习:HTML篇1
  • Android --- SystemUI 导入Android Studio及debug
  • 服务器为什么会选择暴雨?
  • Spring Boot + Apache Tika 从文件或文件流中提取文本内容
  • day26|学习前端之算法学习
  • 数据结构之二叉树(2)
  • Mac设置中的安全性缺少“任何来源”
  • 样式化你的 Next.js 应用:CSS 模块、Tailwind CSS 和全局样式
  • Qwen2.5-VL技术详解
  • Claude code 使用笔记
  • FPGA学习笔记——SDR SDRAM的读写(不调用IP核版)
  • C++ 常见面试题汇总
  • cifar10分类对比:使用PyTorch卷积神经网络和SVM
  • 2025算法八股——机器学习——SVM损失函数
  • kafka特性和原理
  • webpack和vite优化方案都有哪些
  • Unity UI 中最干净的点击区域实现:RaycastZone 完整实战讲解
  • Java开发环境配置入门指南
  • lua中table键类型及lua中table的初始化有几种方式
  • 【CMake】缓存变量
  • Flink NetworkBufferPool核心原理解析
  • python数据可视化之Matplotlib(8)-Matplotlib样式系统深度解析:从入门到企业级应用
  • Recharts:React图表库,组件化设计助力高效数据可视化开发
  • Linux知识清单
  • SpringMVC 入门详解: MVC 思想(附核心流程)
  • CMake简易使用教程
  • daily notes[13]
  • Solana 核心概念:计算单元与交易成本解析
  • 【系统分析师】第11章-关键技术:软件需求工程(核心总结)
  • 如何通过日志先行原则保障数据持久化:Redis AOF 和 MySQL redo log 的对比