
1. 应广单片机端口复用基础入门第一次接触应广单片机时我就被它强大的端口复用能力惊艳到了。这种8位OTP单片机在小家电领域应用广泛最大的特点就是用最少的资源实现最多的功能。端口复用可以说是应广单片机的看家本领掌握了这个技巧你就能用几毛钱的单片机做出别人用几块钱单片机才能实现的功能。端口复用听起来高大上其实原理很简单。就像我们家里的插座通过插线板可以同时给多个电器供电。单片机端口也是类似通过合理的电路设计和程序控制可以让一个IO口在不同时间干不同的事。应广单片机的端口通常有三种状态高电平、低电平和高阻态。这三种状态的灵活切换就是端口复用的基础。举个最简单的例子我用一个IO口同时控制两个LED灯。当IO输出高电平时LED1亮输出低电平时LED2亮高阻态时两个灯都不亮。这看起来只能控制两个灯但配合分时复用技术实际可以控制更多。就像电影院放胶片虽然每次只放一张但切换够快就能形成连续画面。LED控制也是这个道理快速轮流点亮多个LED人眼就会觉得它们同时在亮。2. LED控制中的端口复用实战2.1 基础双LED控制电路先来看最基础的双LED控制方案。电路设计很简单两个LED反向并联共用一个IO口。当IO输出高电平时电流从IO流向GND点亮LED1输出低电平时电流从VCC流向IO点亮LED2高阻态时没有电流两个灯都不亮。// 示例代码双LED控制 void main() { while(1) { IO1 1; // LED1亮 delay_ms(10); IO1 0; // LED2亮 delay_ms(10); IO1 Z; // 都灭 delay_ms(10); } }这里有个关键点要注意两个LED的导通电压之和必须大于电源电压。比如用5V供电每个LED的导通电压要在2.5V以上否则会出现两个灯同时亮的bug。我在项目中就踩过这个坑当时用的LED导通电压太低怎么调程序都没用最后换了LED才解决。2.2 多LED分时复用方案想要控制更多LED就要用分时复用技术。以4个LED为例我们可以用两个IO口实现控制。硬件连接上LED1和LED2接IO1LED3和LED4接IO2。软件上采用快速扫描的方式// 4LED扫描示例 void scan_leds() { // 阶段1点亮LED1 IO1 1; IO2 Z; delay_ms(2); // 阶段2点亮LED2 IO1 0; IO2 Z; delay_ms(2); // 阶段3点亮LED3 IO1 Z; IO2 1; delay_ms(2); // 阶段4点亮LED4 IO1 Z; IO2 0; delay_ms(2); }扫描频率建议在100Hz以上这样人眼就看不到闪烁。实际项目中我通常用定时器中断来实现扫描确保时序精确。要注意的是LED点亮时间不宜过长否则会出现亮度不均的问题。2.3 数码管的查理复用技术数码管控制是端口复用的经典应用。传统方法每个段都需要独立IO口而用查理复用技术6个IO口就能控制24个LED如888数码管。原理是利用IO口之间的电压差来点亮特定LED。比如用IO1和IO2控制一个LED当IO11、IO20时电流从IO1流向IO2点亮LED反过来IO10、IO21时电流反向点亮另一个LED。通过这种组合n个IO口可以控制n×(n-1)个LED。// 数码管查理复用示例 void charlieplexing() { // 点亮LED1(IO1→IO2) IO1 1; IO2 0; IO3 Z; delay_ms(1); // 点亮LED2(IO2→IO1) IO1 0; IO2 1; IO3 Z; delay_ms(1); // 其他组合... }查理复用最大的挑战是防止鬼影——不该亮的LED微微发光。这是因为LED在极小电流下就能发光。解决方法是在不用的IO口设置正确的状态高阻或与LED两端同电位彻底切断漏电路径。3. 按键检测的复用技巧3.1 单IO检测双按键端口复用不仅能控制输出还能扩展输入。一个IO口可以检测两个按键电路设计很巧妙按键1按下时IO被拉到VCC按键2按下时IO被拉到GND都没按时IO通过大电阻保持中间电平。// 双按键检测 uint8_t read_key() { IO1 Z; // 先设为高阻 if(IO1 1) return KEY1; else if(IO1 0) return KEY2; else return NO_KEY; }实际应用中要特别注意按键防抖和同时按下的处理。我通常会在硬件上加一个小电阻防止短路软件上加去抖延时和状态机处理。3.2 矩阵按键扫描需要更多按键时可以采用矩阵扫描。4个IO口可以组成4×4矩阵检测16个按键。原理是分时给行线输出高低电平同时检测列线输入// 4×4矩阵按键扫描 uint8_t key_scan() { uint8_t row, col; for(row0; row4; row) { // 设置当前行为低其他为高 set_rows(row); // 读取列状态 col read_cols(); if(col ! 0xFF) { return (row4) | col; } } return NO_KEY; }矩阵扫描要注意鬼键问题即多个按键同时按下时可能出现的误判。解决方法包括二极管隔离、软件状态机等。我在一个项目中就遇到过这个问题最后通过增加二极管和优化扫描算法解决了。4. 综合应用LED与按键复用4.1 共享IO方案设计最考验功力的是让LED和按键共享同一个IO口。这需要精心设计电路和程序时序。基本思路是分时复用在LED显示阶段关闭按键检测电路在按键检测阶段关闭LED驱动。电路设计上可以用三极管做开关当需要显示LED时打开三极管通路检测按键时关闭三极管防止LED影响按键电平。程序上要合理分配显示和检测的时间比例确保两者互不干扰。// LED与按键分时处理 void main() { while(1) { // LED显示阶段 enable_led(); show_led_pattern(); disable_led(); // 按键检测阶段 check_keys(); } }4.2 实际项目经验分享在一个温控器项目中我用5个IO口实现了4个LED和4个按键的控制。硬件上采用PNP三极管做LED开关软件上每20ms为一个周期前15ms用于LED显示后5ms用于按键扫描。这样既保证了LED显示不闪烁又确保了按键响应速度。调试时遇到一个棘手问题LED亮度会随按键操作变化。后来发现是按键扫描时没有完全隔离LED电路导致部分电流被分流。通过在LED回路串联二极管解决了这个问题。5. 高级状态检测技巧5.1 三态检测原理应广单片机的IO口可以检测三种状态高、低、高阻。利用这个特性两个IO口可以表示9种状态3×3组合。这在需要多种配置的系统中特别有用比如通过跳线帽选择工作模式。检测原理是先设置内部上拉读取电平再设置内部下拉再次读取。通过两次读取结果的组合判断外部状态// 三态检测函数 uint8_t read_state() { uint8_t state 0; // 第一次检测内部上拉 set_pullup(IO1); if(read_io(IO1) 0) state | 0x01; // 外部下拉 // 第二次检测内部下拉 set_pulldown(IO1); if(read_io(IO1) 1) state | 0x02; // 外部上拉 return state; // 00高阻,01下拉,10上拉,11错误 }5.2 实际应用案例在一个多模式小风扇项目中我用两个IO口检测四种工作模式通过不同的电阻组合实现。相比传统的一个IO一种模式节省了宝贵的IO资源。电路设计上要注意外部电阻值要远小于内部上下拉电阻通常小10倍以上确保检测可靠。调试中发现环境湿度会影响检测结果特别是在高阻态判断时。最后通过软件滤波多次检测取结果和硬件防护涂三防漆解决了这个问题。6. 完整系统设计实例6.1 温度控制器设计现在来看一个完整的案例用8pin应广单片机实现带温度显示、NTC测温、触摸控制的系统。硬件设计上3个IO口用于数码管显示查理复用1个IO口用于NTC测温1个IO口用于触摸检测剩下3个IO口用于功能按键和状态指示软件架构采用时间片轮询void main() { init_all(); while(1) { if(timer1_expired()) { // 每1ms scan_display(); // 数码管扫描 } if(timer2_expired()) { // 每10ms read_ntc(); // 温度采样 check_touch(); // 触摸检测 check_keys(); // 按键扫描 } // 其他任务... } }6.2 调试经验与技巧在资源如此紧张的系统里调试需要特别技巧。我总结了几点经验用LED闪烁频率指示程序状态比如正常运行时1Hz闪烁出错时快速闪烁利用空闲IO口输出调试信号用示波器观察时序在程序关键点插入短脉冲用逻辑分析仪捕捉执行流程当系统异常时逐步关闭功能模块定位问题源记得有一次系统偶尔会死机最后发现是NTC采样时没有关闭数码管显示导致ADC受到干扰。通过合理安排各任务执行时序解决了这个问题。