十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

51单片机中断与定时器:从轮询到事件驱动的编程思维升级

51单片机中断与定时器:从轮询到事件驱动的编程思维升级 1. 项目概述从“轮询”到“中断”的思维跃迁搞单片机开发的朋友尤其是从51单片机入门的一定绕不开“中断”和“定时/计数”这两个核心概念。我记得自己刚开始学的时候满脑子都是while(1)死循环用delay_ms()函数来做延时然后不停地用if语句去查询按键有没有被按下。这种“轮询”的方式代码写起来直观但效率极低单片机宝贵的CPU时间几乎都浪费在无意义的等待和查询上了。整个系统显得笨拙、响应慢而且很难处理多个需要及时响应的任务。直到我真正理解了中断和定时器才感觉单片机编程的世界被打开了另一扇门。这次我们就来深入聊聊51单片机的中断与定时/计数实验这不仅仅是完成一个实验更是一次编程思维从“被动轮询”到“主动响应”的关键升级。简单来说中断就是让CPU能够“放下手头不太急的活儿先去处理更紧急的事情”。比如你正在看书主程序这时门铃响了中断请求你标记下看到的页码保护现场跑去开门执行中断服务程序处理完后再回来接着看书恢复现场继续执行主程序。而定时/计数器则是单片机内部一个非常精准的“闹钟”或“计数器”它不需要CPU一直盯着自己就能独立计时或计数时间到了或数够了就产生一个中断信号通知CPU。将这两者结合我们能轻松实现精准的延时、频率测量、脉冲计数、产生PWM波等复杂功能这是构建任何实时性系统的基石。无论你是正在学习单片机的大学生还是希望夯实底层硬件知识的嵌入式爱好者掌握中断和定时器就意味着你开始真正“驾驭”单片机而不仅仅是“使用”它。接下来我会以一个典型的“利用定时器中断实现LED精准闪烁”和“外部中断按键控制”的综合实验为线索带你从原理到寄存器从代码到调试彻底吃透这块内容。2. 51单片机中断系统深度解析2.1 中断是什么为什么必须掌握它让我们先彻底抛弃软件延时函数delay()。想象一个场景你的单片机需要同时控制一个LED以1秒的间隔闪烁并且随时响应一个按键来改变闪烁模式。如果用轮询你的代码框架大概是这样的void main() { while(1) { if(按键按下) { 改变模式(); // 问题1如果按键按下时正在执行LED延时会立即响应吗不会必须等这次延时结束。 } LED 亮; delay_ms(500); // 问题2在这500ms内CPU除了空跑什么也做不了按键检测被“阻塞”。 LED 灭; delay_ms(500); // 同样的问题又一个500ms的“盲区”。 } }这种模式的弊端显而易见响应延迟不可控、CPU利用率极低、多任务难以协调。中断机制就是为了解决这些问题而生的。当中断发生时CPU会自动将当前程序计数器PC等关键信息压入堆栈保护现场然后跳转到预先设定好的中断服务函数ISR去执行。执行完毕后再自动从堆栈恢复信息跳回原来被打断的地方继续执行恢复现场。这个过程由硬件完成速度极快保证了响应的实时性。在51单片机中中断源是有限的这也是其经典架构的一部分。通常包括外部中断0 (INT0)和外部中断1 (INT1)由芯片的特定引脚P3.2和P3.3上的电平或边沿变化触发。定时器/计数器0 (TF0)和定时器/计数器1 (TF1)溢出中断当定时器计数值从最大值如65535回到0时触发。串行口中断 (RI/TI)当串行口接收或发送完一帧数据时触发。在一些增强型51中还有定时器2中断等。2.2 中断相关的关键寄存器剖析要配置和使用中断我们必须和几个特殊功能寄存器SFR打交道。很多初学者觉得寄存器配置繁琐但一旦理解你会发现它提供了极高的灵活性。1. 中断允许寄存器 (IE - Interrupt Enable)这个寄存器像个总开关和分路开关的组合。地址是A8H可以位寻址。位符号地址功能说明7EAAFH全局中断允许位。1 打开总开关允许所有中断0 关闭总开关禁止所有中断。任何中断要想被响应EA必须为1。6--保留5ET2ADH定时器/计数器2中断允许位在52子系列中。4ESACH串行口中断允许位。1 允许串口中断。3ET1ABH定时器/计数器1中断允许位。1 允许T1中断。2EX1AAH外部中断1允许位。1 允许INT1中断。1ET0A9H定时器/计数器0中断允许位。1 允许T0中断。0EX0A8H外部中断0允许位。1 允许INT0中断。实操心得初始化中断时我习惯采用“先开分路再开总闸”的顺序。即先设置好EX0、ET0等具体中断允许位最后再将EA置1。这样能避免在配置过程中被意外中断打断导致初始化不完整。2. 定时器/计数器控制寄存器 (TCON)这个寄存器低4位用于控制外部中断高4位用于控制定时器。地址88H可位寻址。位符号地址功能说明7TF18FH定时器1溢出标志。硬件置1进入中断服务程序后硬件自动清零或软件查询清零。6TR18EH定时器1运行控制位。1 启动定时器10 停止定时器1。5TF08DH定时器0溢出标志。4TR08CH定时器0运行控制位。3IE18BH外部中断1请求标志。当检测到INT1引脚上的有效中断信号时由硬件置1。进入中断后根据触发方式硬件或软件清零。2IT18AH外部中断1触发方式控制位。1 下降沿触发0 低电平触发。1IE089H外部中断0请求标志。0IT088H外部中断0触发方式控制位。重要提示对于边沿触发方式ITx1CPU响应中断后硬件会自动清除IEx标志位。而对于低电平触发方式ITx0IEx标志位会随外部引脚电平变化中断服务程序必须确保在电平变高前退出否则会引发重复中断。因此在绝大多数需要消抖的按键应用中强烈推荐使用下降沿触发。3. 中断优先级寄存器 (IP)51单片机有两个中断优先级高优先级和低优先级。通过IP寄存器设置。高优先级中断可以打断正在执行的低优先级中断反之则不能。同级中断按自然优先级顺序响应INT0 TF0 INT1 TF1 RI/TI。对于初学者如果不需要复杂的嵌套中断可以暂时不配置IP使用默认的自然优先级即可。2.3 中断服务函数的编写规范在C语言中以Keil C51为例中断服务函数有固定的语法格式void 函数名() interrupt 中断编号 using 工作寄存器组interrupt是关键字。中断编号这是重中之重必须与中断源对应。0: 外部中断01: 定时器/计数器02: 外部中断13: 定时器/计数器14: 串行口中断using指定该中断服务函数使用哪一组工作寄存器R0-R7共4组0-3。这是一个高级技巧主要用于快速现场保护。如果中断函数和主程序使用了不同的寄存器组进入中断时就不需要手动压栈保护R0-R7可以节省时间。但对于简单应用和不熟悉内存管理的初学者可以省略using编译器会自动处理。一个标准的外部中断0服务函数示例如下void EX0_ISR() interrupt 0 // 处理外部中断0 { // 1. 首先清除可能的中断标志对于边沿触发硬件已清但有时为安全也可软件操作 // EX0 0; // 实际硬件已清此句可省略 // 2. 执行你的中断任务例如按键处理 if(按键消抖处理有效) { // 执行动作如翻转LED、改变状态等 } // 3. 中断服务函数应尽可能短小精悍避免长时间占用CPU。 }踩坑记录早期我曾在一个中断服务函数里做了复杂的运算和延时导致主程序和其他中断响应变得极其缓慢系统仿佛“卡住”了。中断服务函数的设计黄金法则快进快出。只做最必要的标志位设置、数据读取或简单运算把耗时的处理放到主循环中根据标志位去执行。3. 定时器/计数器核心原理与模式详解3.1 定时与计数本质都是“数数”51单片机的定时器/计数器其核心是一个加1计数器。它既可以计数内部时钟脉冲定时模式也可以计数来自外部引脚T0/P3.4, T1/P3.5的脉冲计数模式。定时器模式计数脉冲来源于内部系统时钟的12分频。假设晶振是12MHz机器周期 12 / 12MHz 1μs。计数器每1μs加1。通过计算需要计数的次数就能实现精准的定时。这是我们最常用的模式。计数器模式计数脉冲来源于外部引脚。当检测到引脚上从高到低的跳变下降沿时计数器加1。常用于测量外部脉冲频率、计数产品数量等。3.2 与控制相关的两个关键寄存器1. 定时器/计数器工作方式寄存器 (TMOD)TMOD用于设置定时器0和定时器1的工作方式和模式。地址89H不可位寻址必须字节操作。高4位控制T1低4位控制T0。位序位名称作用7GATET1门控位。与INT1引脚配合用于测量脉冲宽度一般置0。6C/TT1模式选择。0 定时器模式1 计数器模式。5M1T1方式选择位。4M0T1方式选择位。3GATET0门控位。2C/TT0模式选择位。1M1T0方式选择位。0M0T0方式选择位。M1和M0共同决定工作方式M1 M0方式功能说明0 0方式013位定时/计数器由TLx低5位和THx 8位组成。兼容早期产品现已少用。0 1方式116位定时/计数器由TLx和THx全16位组成。计数范围0-65535。最常用。1 0方式28位自动重装定时/计数器。TLx计数THx存放重装初值。溢出后TLx自动从THx重装。适用于产生精确的波特率。1 1方式3仅T0有此方式。将T0拆成两个独立的8位计数器。T1在此方式下无中断通常用作串口波特率发生器。2. 定时器/计数器控制寄存器 (TCON)如前所述其高4位TF1, TR1, TF0, TR0用于控制定时器的启动/停止和溢出标志。3.3 定时器初值的计算核心技能这是定时器应用的数学基础。我们以最常用的方式116位模式和定时模式为例。核心公式定时时间 (最大计数值 - 初值) × 机器周期其中方式1下最大计数值 2^16 65536。机器周期 12 / 晶振频率。推导出初值计算公式初值 65536 - (定时时间 / 机器周期)举例使用12MHz晶振要定时50ms0.05秒。机器周期 12 / 12MHz 1μs 0.000001s。需要计数的次数 定时时间 / 机器周期 0.05 / 0.000001 50000次。初值 65536 - 50000 15536。将15536转换为十六进制0x3CB0。所以TH0 0x3C;TL0 0xB0;计算技巧为了简化可以记住一个常用初值。对于12MHz晶振定时50ms的初值就是0x3CB0。如果需要定时1ms则计数次数为1000初值为65536-100064536即0xFC18。你可以根据这个比例去推算其他时间。自动重装模式方式2初值计算 方式2是8位计数器最大计数值256。初值计算公式为初值 256 - (定时时间 / 机器周期)。溢出后TLx会自动用THx的值重装非常适合做高精度的重复定时比如串口波特率发生器。4. 综合实验定时器中断LED闪烁与外部中断按键控制现在我们将理论付诸实践完成一个经典的综合实验用定时器0中断实现一个LED以1秒周期精准闪烁同时用外部中断0下降沿触发响应一个按键每按一次按键LED的闪烁频率加倍周期减半。4.1 硬件电路设计与连接思路这个实验的硬件非常简单单片机一片AT89C51/52或STC89C52等任何51内核芯片。LED一个发光二极管阳极通过一个220Ω-1kΩ的限流电阻接VCC5V阴极接单片机的一个I/O口例如P1.0。注意这是共阳极接法端口输出低电平0时LED亮。你也可以用共阴极接法。按键一个轻触开关。一端接地GND另一端接单片机的外部中断0引脚P3.2。同时必须在P3.2引脚与VCC之间连接一个上拉电阻通常10kΩ以确保按键未按下时引脚处于稳定的高电平状态按下时引脚被拉低到GND产生一个下降沿。晶振12MHz晶振连接在XTAL1和XTAL2之间配合两个20-30pF的电容接地。复位电路经典的RC复位10uF电容10k电阻或手动复位按钮。硬件避坑指南上拉电阻必不可少对于机械按键如果不加上拉电阻引脚在未按下时处于“悬空”状态电平不确定极易引入干扰导致误触发中断。10kΩ是经典值。按键消抖主要靠软件硬件上可以在按键两端并联一个0.1uF的电容来滤除部分毛刺但最有效的消抖还是在软件中断服务函数中实现。LED限流电阻必须接直接连接IO口到LED当IO输出低电平时相当于对地短路电流可能超过IO口最大承受能力通常10-20mA长期会损坏单片机引脚。220Ω电阻在5V下提供约(5-1.7)/220≈15mA电流对于普通LED足够安全明亮。4.2 软件代码实现与逐行解析以下是完整的C语言代码基于Keil开发环境#include reg52.h // 包含51单片机寄存器定义头文件 sbit LED P1^0; // 定义LED连接到P1.0口 sbit KEY P3^2; // 定义按键连接到P3.2 (INT0) unsigned int g_time_cnt 0; // 全局变量用于累计定时中断次数 unsigned int g_flash_period 1000; // 全局变量LED闪烁周期单位ms初始1000ms /* 定时器0初始化函数 */ void Timer0_Init(void) { // 1. 设置定时器0为工作方式116位定时器 // TMOD 0x01; // 低4位0001即T0方式1定时模式GATE0。 // 更安全的写法只操作T0相关的位不影响T1假设T1未被使用 TMOD 0xF0; // 将TMOD低4位清0高4位保持不变1111 0000 TMOD | 0x01; // 将TMOD最低位置1设置为方式1|0000 0001 // 2. 计算并装入定时初值目标定时1ms 12MHz // 1ms需要计数次数 0.001 / (12/12e6) 1000 // 初值 65536 - 1000 64536 0xFC18 TH0 0xFC; // 装入高8位初值 TL0 0x18; // 装入低8位初值 // 3. 打开定时器0中断允许 ET0 1; // 4. 打开全局中断允许 EA 1; // 5. 启动定时器0 TR0 1; } /* 外部中断0初始化函数 */ void EX0_Init(void) { // 1. 设置外部中断0为下降沿触发方式 IT0 1; // IT01下降沿触发 // 2. 打开外部中断0允许 EX0 1; // 3. 全局中断已在Timer0_Init中打开这里无需重复打开EA } /* 主函数 */ void main(void) { Timer0_Init(); // 初始化定时器0 EX0_Init(); // 初始化外部中断0 LED 1; // 初始化LED为熄灭状态共阳极高电平灭 while(1) { // 主循环里什么都不用做所有事情都由中断服务程序处理。 // 这种架构是中断系统的精髓主程序可以休眠或处理低优先级任务。 // 这里为了演示主循环可以空跑或者添加其他低优先级任务。 } } /* 定时器0中断服务函数 */ void Timer0_ISR() interrupt 1 { // 1. 重装定时初值方式1不是自动重装必须手动重装 TH0 0xFC; TL0 0x18; // 2. 定时中断次数累计 g_time_cnt; // 3. 判断是否达到LED翻转的时间点 if(g_time_cnt (g_flash_period / 2)) // 周期为g_flash_period每半周期翻转一次 { g_time_cnt 0; // 计数清零 LED ~LED; // LED状态取反实现闪烁 } } /* 外部中断0服务函数 */ void EX0_ISR() interrupt 0 { unsigned int i; // 简单延时消抖约10ms。注意在中断中延时是坏习惯这里仅用于教学演示。 // 更好的做法是用定时器标记或状态机消抖后续会讲。 for(i0; i1000; i); // 再次检测按键是否仍为低电平确认是有效按下非抖动 if(KEY 0) { // 改变闪烁周期1000ms - 500ms - 250ms - 125ms - 回到1000ms if(g_flash_period 125) { g_flash_period / 2; } else { g_flash_period 1000; } // 可以在这里添加蜂鸣器提示音或其他反馈 } // 等待按键释放避免一次按下多次触发 while(KEY 0); // 释放后稍作延时避开释放抖动 for(i0; i1000; i); }代码关键点解析全局变量g_time_cnt和g_flash_period用于在中断函数和主程序虽然本例主程序未用间传递信息。它们被多个执行流主循环、T0中断、INT0中断访问但在本简单例子中不存在同时改写冲突。在更复杂系统中需考虑使用volatile关键字防止编译器优化或进行关中断保护。定时器重装方式1下每次进入中断必须手动重装初值TH0和TL0。如果忘记下一次定时将从0开始计数导致定时时间错误。中断中的延时在EX0_ISR中使用了for循环延时进行消抖和等待释放这是非常不推荐的做法因为它会长时间阻塞CPU可能导致定时器中断无法及时响应影响LED闪烁的准确性。这只是为了直观演示。下面我们会讨论正确的做法。4.3 更优的按键处理状态机与非阻塞消抖在中断服务函数中进行延时是禁忌。正确的做法是将消抖逻辑放到主循环中中断只负责设置一个“按键事件发生”的标志位。改进版代码思路#include reg52.h sbit LED P1^0; sbit KEY P3^2; unsigned int g_time_cnt 0; unsigned int g_flash_period 1000; bit g_key_event 0; // 新增按键事件标志位1表示有按键事件待处理 unsigned char g_key_debounce_cnt 0; // 新增消抖计数器 void Timer0_Init(void) { /* 同上 */ } void EX0_Init(void) { /* 同上 */ } void main(void) { Timer0_Init(); EX0_Init(); LED 1; while(1) { // 主循环负责处理按键消抖和状态改变 if(g_key_event) // 如果中断告诉我们按键可能按下了 { // 简单的延时消抖也可以用定时器实现更精准的消抖 // 这里假设主循环很快用循环计数模拟延时 if(g_key_debounce_cnt 100) // 假设计数到100代表消抖时间到 { g_key_debounce_cnt 0; if(KEY 0) // 再次确认按键仍处于按下状态 { // 执行按键动作 if(g_flash_period 125) g_flash_period / 2; else g_flash_period 1000; // 等待按键释放非阻塞方式 while(KEY 0); // 这里虽然还是阻塞但已在主循环不影响其他中断 } g_key_event 0; // 处理完毕清除标志 } } // 这里可以轻松添加其他任务如数码管扫描、串口数据处理等 } } /* 定时器0中断服务函数 */ void Timer0_ISR() interrupt 1 { TH0 0xFC; TL0 0x18; g_time_cnt; if(g_time_cnt (g_flash_period / 2)) { g_time_cnt 0; LED ~LED; } } /* 外部中断0服务函数 - 简化版 */ void EX0_ISR() interrupt 0 { // 只做一件事设置标志位。越快越好 g_key_event 1; g_key_debounce_cnt 0; // 重置消抖计数器 }这种“中断设标志主循环处理”的模式是嵌入式系统处理外部事件的经典架构保证了系统的实时性和效率。5. 进阶应用与深度优化技巧掌握了基础实验后我们可以探索更复杂和实用的应用场景。5.1 利用定时器测量脉冲宽度或频率定时器的门控位GATE提供了一个有趣的功能。当GATE1且TRx1时定时器的启动不仅受TRx控制还受外部中断引脚INTx的控制。只有INTx引脚为高电平时定时器才开始计数。这可以用来测量一个正脉冲的宽度。思路设置定时器为方式1GATE1TRx1C/T0定时模式。将待测脉冲信号接到INTx引脚同时也是定时器的门控引脚。脉冲上升沿到来时INTx变高定时器开始计数。脉冲下降沿到来时INTx变低定时器停止计数。读取此时定时器THx和TLx的值根据机器周期即可计算出脉冲高电平的持续时间。5.2 实现多任务时间片调度在一个复杂的系统中可能有多个任务需要以不同的周期执行。例如任务A每10ms执行一次如按键扫描任务B每100ms执行一次如更新显示任务C每500ms执行一次如读取传感器。我们可以利用一个基准定时器中断比如1ms中断来实现一个简单的时间片调度器。unsigned int g_sys_tick 0; // 系统时基每1ms加1 void Timer0_ISR() interrupt 1 // 1ms中断 { TH0 0xFC; TL0 0x18; g_sys_tick; } void main(void) { // ... 初始化 while(1) { // 任务A每10ms执行 if(g_sys_tick % 10 0) { Task_A(); } // 任务B每100ms执行 if(g_sys_tick % 100 0) { Task_B(); } // 任务C每500ms执行 if(g_sys_tick % 500 0) { Task_C(); } // 注意g_sys_tick在中断中自增主循环中判断需声明为volatile // 且任务函数执行时间必须远小于其执行周期否则会错过后续触发。 } }这是一种协作式调度任务函数必须主动“让出”CPU。更高级的可以使用状态机将长任务拆分成多个步骤在每次调度中执行一部分避免阻塞。5.3 中断嵌套与优先级管理当系统中有多个中断源且它们可能同时或几乎同时发生时就需要考虑优先级。51单片机通过IP寄存器可以设置两个优先级。配置示例让串口中断为高优先级定时器0中断为低优先级。IP 0x10; // 0001 0000 PS1串口中断为高优先级 // 或使用位操作 PS 1; // 串口高优先级 PT0 0; // 定时器0低优先级默认就是0这样当CPU正在处理低优先级的定时器0中断时如果发生高优先级的串口中断CPU会暂停当前的T0中断服务程序转去执行串口中断服务程序执行完毕后再回来继续执行T0中断服务程序。注意事项中断嵌套会增加系统的复杂性并需要更大的堆栈空间来保存多级现场。在资源紧张的51系统上除非必要应谨慎使用。确保高优先级中断服务函数非常短小避免长时间阻塞低优先级中断和主程序。6. 调试技巧与常见问题排查实录理论懂了代码写了但实验现象不对以下是新手最常见的坑和排查方法。6.1 现象LED完全不闪烁检查1硬件连接。用万用表测量LED两端电压在程序运行时是否变化检查限流电阻是否接好单片机电源和地是否正常检查2定时器初始化。TMOD寄存器设置对吗TR0启动位打开了吗EA和ET0中断允许位打开了吗建议在初始化后将TMOD、IE、TCON的值通过串口打印出来如果有仿真器则直接查看确认与预期一致。检查3中断服务函数格式。函数名后面的interrupt 1写对了吗是interrupt 0还是1函数体内部重装初值了吗对于方式1检查4编译优化。检查Keil的优化等级。有时高优化等级可能会优化掉它认为“未使用”的全局变量如g_time_cnt。尝试将优化等级调到Level 0或者将全局变量声明为volatile。volatile unsigned int g_time_cnt;6.2 现象LED闪烁但时间不准忽快忽慢检查1初值计算。晶振频率你用的是多少代码里计算初值的假设和实际硬件一致吗用示波器或逻辑分析仪测量LED引脚波形计算实际周期与理论值对比。检查2中断服务函数执行时间。你的中断服务函数里是不是做了太多事情尤其是用了for或while循环延时这会严重影响定时精度。确保中断服务函数尽可能短。检查3其他中断干扰。如果开启了其他中断如串口并且其服务函数执行时间很长会打断当前的定时器中断造成定时累积误差。评估系统所有中断的负载。检查4堆栈溢出。如果中断嵌套很深或函数调用层次多可能导致堆栈溢出破坏内存造成程序跑飞表现出各种怪异现象。可以尝试增大编译器中设置的堆栈大小。6.3 现象按键不响应或连按多次检查1外部中断配置。IT0设置了吗下降沿触发设为1。EX0和EA打开了吗检查2硬件上拉电阻。这是最常见的问题P3.2引脚必须通过一个10kΩ电阻上拉到VCC否则引脚悬空电平不定。检查3按键消抖。你是否做了消抖在中断服务函数里直接处理按键动作必然会因为机械抖动导致多次触发。必须消抖。检查4中断服务函数逻辑。在边沿触发模式下中断标志IE0是硬件自动清除的。你的代码里有没有不小心用软件清除了它或者错误地配置成了电平触发IT00却没有及时处理6.4 使用仿真器进行调试如果有条件使用硬件仿真器如STC-ISP的硬件仿真功能或J-Link配合某些开发板是最高效的调试手段。单步执行在初始化代码后暂停查看TMOD、IE、TCON等寄存器的值。断点在中断服务函数入口设置断点观察是否能进入。逻辑分析仪/示波器这是观察时序的终极工具。用它测量LED引脚波形可以直观看到周期、占空比是否准确测量按键引脚波形可以看到抖动情况和中断触发边沿。6.5 软件模拟调试没有硬件时可以使用Proteus等仿真软件搭建电路进行纯软件仿真。它可以模拟单片机运行、外设响应是学习原理、验证代码逻辑的利器。在Proteus中你可以放置示波器、电压探针动态观察任何引脚的电平变化。最后分享一个我个人的深刻体会单片机的中断和定时器是理解整个嵌入式系统“实时性”概念的敲门砖。从最初死循环的“懵懂”到学会使用中断的“惊喜”再到后来面对多个中断和任务时对优先级、响应时间、资源竞争的“纠结”这个过程是每个嵌入式工程师的必经之路。把51单片机的中断和定时器玩透了再去看STM32等更高级芯片的中断控制器NVIC和高级定时器你会发现很多概念是一脉相承的只是变得更强大、更灵活。所以不要小看这个经典的实验它里面蕴含的“事件驱动”、“异步处理”、“时间管理”的思想会贯穿你的整个嵌入式开发生涯。多写代码多调试多思考“如果……会怎样”这些经验远比死记硬背寄存器配置要宝贵得多。
返回列表