流水灯
- 核心功能
实现P2端口连接的8个LED流水灯效果:先左移循环点亮,再右移循环熄灭。
- 代码结构
硬件定义
#define LED_PORT P2 // LED接P2端口,低电平驱动(取反控制)
延时函数
void delay_10us(u16 ten_us) { while(ten_us--); }
// 实际延时 ≈ ten_us × 10μs(12MHz晶振下)
主程序逻辑
初始化LED(点亮第1个LED,LED_PORT=~0x01)。
进入循环,交替执行左移和右移流水灯效果。
- 流水灯实现方法
方法1(注释):直接移位 + 循环
for(i=0;i<8;i++) { LED_PORT=~(0x01<<i); ... }
方法2:循环移位函数
// 左移7次(从0x01→0x02→...→0x80)
LED_PORT=_crol_(LED_PORT,1); // 需包含intrins.h
// 右移7次(从0x80→0x40→...→0x01)
LED_PORT=_cror_(LED_PORT,1);
- 关键点
低电平驱动:~0x01取反使P2.0输出低电平,点亮LED。
延时控制:delay_10us(50000)约500ms(12MHz晶振)。
循环移位函数:
_crol_(val, n):将val循环左移n位。
_cror_(val, n):将val循环右移n位。
- 硬件连接
LED阳极接VCC,阴极接P2.0-P2.7,需串联限流电阻(约220Ω)。
注:若需调整流水灯速度,可修改delay_10us参数或更换定时器实现精准延时。
蜂鸣器
语言: C
简介: 通过脉冲信号控制蜂鸣器发出声音
代码: 蜂鸣器
原理
💡 就像用快慢不同的鼓掌节奏制造不同音高
蜂鸣器 相当于一个迷你喇叭
BEEP=!BEEP 动作相当于快速开关电源(类似反复拔插耳机插头会听到"哒哒"声)
delay_10us(100) 控制鼓掌速度:数字越大间隔越长,声音越低沉
▶ 实际效果:发出类似"滴滴-"的提示音后静音(循环一次后停止)
关键代码结构
void main() {
u16 i=2000; // 定义发声持续时间
while(1) {
while(i--) { // 循环2000次
BEEP=!BEEP; // 电平翻转制造声波
delay_10us(100); // 控制音高
}
i = 0; // 计数器清零防止重新进入发声循环
BEEP=0; // 停止发声
}
}
输入输出
-> 无外部输入
<- P2.5引脚输出方波信号 → 蜂鸣器发声
硬件小知识
无源蜂鸣器 vs 有源蜂鸣器:
本文代码适配无源型(需脉冲驱动,像用快节奏拍皮球)
有源型只需通电就响(像按下音乐贺卡的开关)
静态数码管
语言: C
简介: 控制静态数码管持续显示数字0
原理
字符 共阴极段码(十六进制) 共阳极段码(十六进制) 对应点亮段(逻辑电平)
0 0x3F 0xC0 a, b, c, d, e, f
1 0x06 0xF9 b, c
2 0x5B 0xA4 a, b, g, e, d
3 0x4F 0xB0 a, b, g, c, d
4 0x66 0x99 f, g, b, c
5 0x6D 0x92 a, f, g, c, d
6 0x7D 0x82 a, f, g, c, d, e
7 0x07 0xF8 a, b, c
8 0x7F 0x80 a, b, c, d, e, f, g
9 0x6F 0x90 a, b, c, d, f, g
A 0x77 0x88 a, b, c, e, f, g
B 0x7C 0x83 d, e, f, g, c
C 0x39 0xC6 a, d, e, f
D 0x5E 0xA1 b, c, d, e, g
E 0x79 0x86 a, d, e, f, g
F 0x71 0x8E a, d, e, f
关键说明
💡 像用7根火柴棍拼数字
数码管 内部有7个LED段 + 1个小数点(共8个)
段码数据 相当于开关组合:0x3F(00111111) 表示A~F段全亮,G段不亮
取反操作 ~ 像把说明书倒着看(因部分硬件需反向电流驱动)
▶ 实际效果:通电后数码管固定显示数字"0"
关键代码结构
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; // 预存0-F的亮灯方案 此为共阴极表示共阳需取反操作
void main() {
SMG_A_DP_PORT = ~gsmg_code[0]; // 取出"0"的亮灯方案反向输出 取反成共阳输出
while(1); // 锁定显示状态
}
输入输出
-> 无外部输入
<- P0端口输出固定段码信号 → 数码管显示"0"
硬件连接揭秘
共阴极数码管 vs 共阳极:
本文代码适配共阳极(所有LED负极相连,像共享插座地线)
若显示乱码可能需要去除~(说明你的硬件非共阳无需取反)
注意事项
⚠️ P0口需接2KΩ上拉电阻(如同给水管加压)
⚠️ 只能固定显示0(修改数组索引可切换数字,如gsmg_code[5]显示"5")
⚠️ 长时间静态显示可能造成LED老化(类似手机屏幕烧屏)
动态数码管
语言: C
简介: 实现8位数码管动态扫描显示数字0-7
代码: 动态数码管
原理
74HC138 :像快递分拣员
可以把 74HC138 想象成一个 快递分拣机器人,专门负责把“包裹”(输入信号)精准送到8个不同的“分拣口”(输出信号)。它用最简单的规则帮你省掉一堆复杂的线路,特别适合需要“多选一”的场景。
核心功能
输入:3个数字(A0, A1, A2),用二进制表示 0~7(比如 000 是0,111 是7)。
输出:8个分拣口(Y0~Y7),每次只有一个口“开门”(输出低电平),其他口“关门”(高电平)。
控制权:有3个“总开关”(使能端 E3, E2, E1),必须同时满足条件才会开始分拣。
工作流程:快递分拣系统
开启分拣系统:
按下“总开关”:E3=1(高电平),E2=0 和 E1=0(低电平)。
(如果总开关没开,分拣系统直接罢工,所有分拣口都“关门”)
输入包裹编号:
告诉机器人包裹编号(A0, A1, A2)。比如:
A2=0, A1=0, A0=0 → 包裹编号 0
A2=1, A1=1, A0=1 → 包裹编号 7
自动分拣:
机器人根据编号,打开对应的分拣口(Y0~Y7中的一个输出低电平)。
其他分拣口全部关闭(高电平)。
实际用途
省IO口:用3根线(A0~A2)控制8个设备,不用接8根线到单片机。
(比如用单片机控制8个LED灯,只需3个IO口+1个使能端)
简化逻辑:避免写一堆“如果-否则”代码,硬件直接帮你选通。
扩展场景:级联多个74HC138,可以控制更多设备(比如16选1)。
关键记忆点
3根线选8个口 → 二进制编号的魔力。
总开关必须开 → E3=1,E2=0,E1=0。
低电平有效 → 开门的分拣口是低电平(像“绿灯”,其他口是“红灯”)。
74HC245 芯片详解
一、芯片概述
74HC245 是一款 高速 CMOS 八位双向总线收发器,支持三态输出控制,主要用于增强总线驱动能力、实现数据双向传输及电气隔离13。其核心功能包括:
双向数据传输:通过方向控制引脚切换数据流向(A→B 或 B→A)15。
三态输出:使能控制下可输出高电平、低电平或高阻态,避免总线冲突36。
驱动能力增强:输出电流可达 35~83mA,解决单片机等控制器驱动能力不足的问题46。
二、引脚功能与配置
74HC245 采用 20 引脚封装,关键引脚定义如下:
引脚号 名称 功能说明
1 DIR 方向控制:高电平时数据从 A→B,低电平时从 B→A13。
19 OE (或 CE) 使能控制:低电平时芯片工作,高电平时输出高阻态(禁用)14。
2-9 A0-A7 A 端数据总线:双向传输端口,与 B 端互为输入/输出35。
11-18 B0-B7 B 端数据总线:功能与 A 端对称,方向由 DIR 决定15。
10,20 GND, VCC 电源地(GND)和电源正极(3~5V)35。
三、工作原理
数据流向控制:
DIR=1 时,A 端为输入,B 端为输出(A→B)15。
DIR=0 时,B 端为输入,A 端为输出(B→A)36。
使能逻辑:
OE=0 时,芯片正常工作,数据按 DIR 设定方向传输14。
OE=1 时,A/B 端均呈高阻态,隔离总线35。
驱动增强:
单片机等控制器的 IO 口直接驱动大电流负载(如 LED 显示屏、数码管)时,通过 74HC245 提升输出电流稳定性46。
四、典型应用场景
总线驱动扩展:
在 16x16 LED 点阵屏中,级联多片 74HC245 驱动行列信号,解决单片机 IO 驱动能力不足的问题14。
双向数据隔离:
工业控制系统中,作为主控芯片与传感器/执行器间的缓冲器,防止电气干扰损坏核心电路26。
逻辑电平转换:
在 3.3V 与 5V 系统间实现双向电平匹配(如 SPI 通信),保障信号兼容性68。
多设备共享总线:
通过三态输出特性,允许多个设备分时复用数据总线,避免竞争36。
五、关键设计要点
下拉/上拉电阻:未使用时,A/B 端需接下拉电阻稳定电位,防止悬空干扰4。
限流保护:驱动大电流负载(如 LED)时,B 端串联 100Ω 电阻限制电流4。
级联扩展:多片 74HC245 串联可控制更多信号线(如 32 针控制 16x16 点阵)16。
六、与同类芯片对比
74HC244:仅支持单向数据传输(A→B),驱动能力相同7。
74HC540/541:单向缓冲器,无方向控制功能,适用于单向信号增强7。
通过合理配置 74HC245,可显著提升数字系统的稳定性和扩展性,尤其在多设备、高负载场景中表现突出
💡 像用旋转彩灯营造连续亮灯效果
位选控制 相当于点名:
LSA/LSB/LSC 三根线组合出8种点名方式(类似二进制001=1号,010=2号)
动态扫描 本质是"轮班制":
每次只点亮1位数码管 → 快速切换 → 人眼感觉8位同时亮
类似快速翻动连环画产生动画效果
消影处理 像擦黑板:清空旧数据再写新内容,防止数字拖影
关键代码结构
void smg_display() {
for(i=0; i<8; i++) {
// 位选解码(7-i实现从高位到低位扫描)
switch(7-i) {
case 0: LSC=1;LSB=1;LSA=1; break; // 选中第8位数码管
... // 其他位选择逻辑
}
SMG_A_DP_PORT = gsmg_code[i]; // 显示对应数字 注:此处数组gsmg_code和静态数码管一样故不作展示
delay_10us(100); // 保持显示
SMG_A_DP_PORT = 0x00; // 关闭所有段(消影)
}
}
输入输出
-> 无外部输入
<- P0输出段码 + P2.2-P2.4输出位选 → 8位数码管显示0-7
实现亮点
✅ 逆向扫描设计(7-i实现从右到左显示)
✅ 硬件级消影处理
✅ 3根控制线驱动8位数码管(3-8译码器原理)
硬件工作流程
选中第N位数码管(通过LSA/LSB/LSC组合)
发送数字N的段码(如i=0时显示数字0)
保持显示10us×100=1ms
关闭当前数码管
切到下一个数码管(循环完成8位扫描约需8ms)
注意事项
⚠️ 扫描频率需>50Hz(8位×1ms=8ms → 125Hz无闪烁)
⚠️ 段码数据未取反(说明硬件电路设计为共阳极数码管)
⚠️ 显示内容固定为0-7(修改gsmg_code[i]可自定义显示)
性能优化技巧
// 示例:提升亮度可调整延时比例
SMG_A_DP_PORT = gsmg_code[i];
delay_10us(200); // 增加亮的时间
SMG_A_DP_PORT = 0x00;
delay_10us(50); // 减少暗的时间
现象扩展
若删除消影代码SMG_A_DP_PORT=0x00:
会出现"数字重叠"现象(如显示"8"时可能看到相邻位的残影)
类似用湿毛笔在纸上连续写字产生的渗透效果
独立按键
语言: C
简介: 通过扫描独立按键控制LED状态翻转
代码: 独立按键
原理
💡 像用门铃按钮控制电灯开关
按键检测 本质是监听电线通断:
按键按下 → P0口对应引脚接地(类似把电线接到地面)
程序像保安不断检查各个门(引脚)是否被推开(电平变低)
消抖处理 如同确认敲门声:
首次检测到按下后等待10ms(消除弹簧抖动产生的误信号)
确认依然按下才执行操作
关键代码结构
u8 key_scan(u8 mode) {
static u8 key=1; // 按键状态锁
if(mode) key=1; // 模式切换
if(key==1 && 任意按键按下) {
delay_10us(1000); // 10ms消抖
key=0; // 锁定直到松开
return 对应键值;
}
else if(全部按键松开) {
key=1; // 解除锁定
}
return 无按键;
}
输入输出
-> P0.0-P0.3输入按键状态
<- P2.0输出LED控制信号
实现亮点
✅ 硬件级按键消抖处理
✅ 静态变量实现状态锁定(类似门闩机制)
✅ 双模式设计(单次/连续触发)
工作流程演示
按下K1 → P0.0变低电平
检测到变化 → 延时10ms确认
依然按下 → LED1状态翻转
松开按键 → 状态锁复位
注意事项
⚠️ 消抖时间需根据实际按键调整(类似区分轻触和重按)
⚠️ 按键引脚需接上拉电阻(如同给弹簧增加复位力)
⚠️ 主循环中10ms延时可能影响响应速度(可优化为定时器扫描)
硬件连接要点
按键典型接法:
VCC → 10KΩ上拉电阻 → 按键 → GND
单片机引脚接在电阻与按键之间
升级建议
// 示例:实现长按功能
if(持续检测到按键按下>2秒) {
LED1=0; // 长按关灯
}
故障排查指南
现象 可能原因
按键无反应 上拉电阻未接/接触不良
LED随机闪烁 消抖时间不足(增大delay_10us参数)
同时按下多键异常 未处理组合按键(增加逻辑判断)
矩阵按键
语言: C
简介: 实现4x4矩阵按键扫描并在数码管显示按键编号
代码: 矩阵按键
原理
💡 像在停车场找车位的两种方式:
行列扫描法:
每次检查一列车位(置低一列)
查看哪行亮起红灯(检测行电平)
如:第三列第二行有车 → 对应按键9
线翻转法:
先让所有行亮红灯(置低行)→ 发现第2列有车
再让所有列亮红灯(置低列)→ 发现第3行有车
组合坐标 (2,3) → 对应按键7
方法1(行列扫描法)
u8 key_matrix_ranks_scan() {
KEY_MATRIX_PORT=0xf7; // 激活第一列(类似打开第一排探照灯)
if(KEY_MATRIX_PORT!=0xf7) { // 检测行变化
switch(KEY_MATRIX_PORT) { // 判断哪行被按下
case 0x77: key_value=1; // 第一列第一行
// 其他情况类似...
}
}
while(KEY_MATRIX_PORT!=0xf7); // 等待松手(类似等车门关闭)
// 重复扫描其他列...
}
方法2(线翻转法)
u8 key_matrix_flip_scan(void)
{
static u8 key_value=0;
KEY_MATRIX_PORT=0x0f;//给所有行赋值0,列全为1 // 设置行线为低电平(0),列线为高电平(1)
if(KEY_MATRIX_PORT!=0x0f) // 判断是否有列线被拉低(按键按下时,行线低电平会传导到列线)
{
delay_10us(1000);//消抖
if(KEY_MATRIX_PORT!=0x0f) // 再次检测,确认按键真实按下(非干扰或抖动)
{ //测试列
KEY_MATRIX_PORT=0x0f; // 重新设置行线为低,列线输入高电平
switch(KEY_MATRIX_PORT)//保存行为0,按键按下后的列值 读取列线状态(低4位)
{
case 0x07: key_value=1;break; // 列1被拉低(二进制 00000111)
case 0x0b: key_value=2;break; // 列2被拉低(二进制 00001011)
case 0x0d: key_value=3;break; // 列3被拉低(二进制 00001101)
case 0x0e: key_value=4;break; // 列4被拉低(二进制 00001110)
}
//测试行
KEY_MATRIX_PORT=0xf0; // 设置列线为低电平(0),行线为输入高电平(1)
switch(KEY_MATRIX_PORT)//保存列为0,按键按下后的键值 读取行线状态(高4位)
{
case 0x70: key_value=key_value;break; // 行1被拉低(二进制 01110000)
case 0xb0: key_value=key_value+4;break; // 行2被拉低(二进制 10110000)
case 0xd0: key_value=key_value+8;break; // 行3被拉低(二进制 11010000)
case 0xe0: key_value=key_value+12;break; // 行4被拉低(二进制 11100000)
}
while(KEY_MATRIX_PORT!=0xf0);//等待按键松开(列线恢复高电平)
}
}
else
key_value=0; // 无按键按下时,返回0
return key_value; // 返回按键编码值(1~16或0)
}
输入输出
-> P1口矩阵按键输入(16键布局)
<- P0口数码管显示键值-1(如按键1显示0)
实现亮点
✅ 双扫描算法集成(行列扫描+线翻转)
✅ 硬件级按键消抖(10ms延时去抖)
✅ 松手检测机制(避免重复触发)
硬件接线示意
矩阵按键布局:
列线:P1.0-P1.3 (输出模式)
行线:P1.4-P1.7 (输入模式)
数码管:共阴极接法(需取反段码)
注意事项
⚠️ 实际键值范围1-16,显示值为key-1
⚠️ 线翻转法需保持行列线初始状态为0x0F
⚠️ 若显示镜像数字需检查段码是否反向(如显示"Γ"可能是未取反段码)
性能优化建议
// 示例:改用定时器中断扫描(释放主循环资源)
void Timer0_ISR() interrupt 1 {
key=key_matrix_ranks_scan();
if(key) SMG_A_DP_PORT = ~gsmg_code[key-1];
}
故障排查表
现象 可能原因 解决方法
多键同时按下异常 未处理组合按键 添加防串键逻辑
显示数字跳变 消抖时间不足 增大delay_10us参数
某列全部失灵 列线驱动失效 检查P1.0-P1.3输出电平
扩展应用
实现电子密码锁:
u8 password[4] = {3,7,9,15}; // 预设密码
if(输入序列匹配password) 解锁继电器;
74HC595驱动16x16 LED点阵
语言: C
简介: 通过4片74HC595级联控制16x16点阵LED基础显示
代码: 74HC595驱动16x16 LED点阵
原理💡 像用电子绣花机织布
1. 74HC595 是针脚控制器
功能:它像一台能控制8根绣花针的机器。每根针可以独立控制“抬起”或“下针”(对应输出高/低电平)。
扩展能力:如果用4台这样的机器(级联4片74HC595),就能控制32根针(8针×4片),足够驱动一个16x16的LED点阵(16行+16列)。
2. 串行数据像织布机的纱线
SER引脚(串行输入):
想象你有一根纱线(数据),要从布料的起点(SER引脚)穿进去。每次只能穿一个线头(1个比特,0或1)。
例如:要织出图案“1010”,你需要按顺序塞入1、0、1、0四个线头。
SRCLK(移位时钟):
这是织布机的“脚踏板”。每踩一脚(给一个脉冲),纱线就往前穿一格(数据移一位)。
比如:踩4脚,纱线“1010”就穿进了4根针的孔里。
RCLK(锁存时钟):
这是“织布完成按钮”。只有按下它(给一个脉冲),所有穿好的纱线才会同时织到布料上(数据输出到针脚)。
这样做的意义:避免织布时看到半成品(数据输出不稳定)。
3. 级联原理:四条串联的传送带
工作流程:
假设你有4台机器(4片74HC595)串联,每台控制8根针。
第一步:新来的包裹(数据)总是塞到第一条传送带(第一片74HC595)的入口。
第二步:每踩一脚踏板(SRCLK脉冲),包裹就往前推一格。
第三步:当第一条传送带塞满8个包裹后,第9个包裹会被推到第二条传送带,依此类推。
卸货时机:
当所有传送带都塞满后,按下卸货按钮(RCLK脉冲),所有包裹(数据)同步显示在针脚上。
扫描逻辑 类似十字绣:
先选通第N行(穿横向纱线)
再点亮该行需要亮的列(刺纵向纱线)
快速轮换所有行形成完整图案
关键代码结构
// 数据发送顺序说明
void hc595_write_data(u8 row_low, u8 row_high, u8 col_low, u8 col_high) {
// 发送顺序:列高8位 → 列低8位 → 行高8位 → 行低8位
// 对应硬件:595(D)→595(C)→595(B)→595(A)
for(i=0;i<8;i++) SER=col_high>>7; // 先发送列高字节
...// 依次发送其他三个字节
RCLK=1; // 数据同步输出
}
硬件连接拓扑
|--595A(行低8位)--行驱动电路--行1~8
单片机--|--595B(行高8位)--行驱动电路--行9~16
|--595C(列低8位)--列驱动电路--列1~8
|--595D(列高8位)--列驱动电路--列9~16
实现亮点
✅ 四芯片协同控制(2片管行,2片管列)
✅ 串转并技术节省IO资源(3线控32路)
✅ 高位优先传输保证数据对齐
注意事项
⚠️ 数据发送顺序是dat4→dat3→dat2→dat1(最后发送的数据对应第一片595)
⚠️ 595芯片需VCC接5V,GND接地
⚠️ LED共阳/共阴决定数据有效电平(本例代码适配共阴接法)
性能参数
发送32位数据耗时:4×8×2×10us = 640us
切换间隔:500ms(可通过修改delay_ms参数调整)
点阵工作流程
准备行数据:
如显示第5行:row_low=0x20(00100000)
准备列数据:
如该行第3列要点亮:col_low=0x04(00000100)
发送数据:
hc595_write_data(0x20, 0x00, 0x04, 0x00);
delay_ms(2); // 保持显示
快速轮巡所有行(>60Hz避免闪烁)
当前代码局限
⚠️ 示例仅实现基础流水灯,需改进:
// 伪代码:实现字符显示
u8 char_map[16]={0x01,0x03,...}; // 字形数据
for(row=0;row<16;row++){
hc595_write_data(row_data, char_map[row], 0,0);
delay_ms(1);
}
注意事项
驱动能力:
需加三极管/MOS管放大行驱动电流
消隐处理:
切换行列前应先关闭显示
扫描速度:
16行×2ms=32ms → 约31Hz(需优化至>60Hz)
升级建议
// 示例:增加显示缓冲区
u8 display_buf16; // 存储16行x16列数据
void refresh_led(){
for(int i=0;i<16;i++){
hc595_write_data(1<<i, 0, display_buf[i][0], display_buf[i][1]);
delay_us(100);
hc595_write_data(0,0,0,0); // 消隐
}
}
故障排查表
现象 可能原因 检测点
某行/列全不亮 595芯片供电异常 测量595 VCC电压
显示镜像 数据位序错误 检查SER=dat>>7是否匹配硬件
局部闪烁 消隐缺失 在行切换前插入全关断指令
LED点阵显示数字/汉字
语言: C
简介: 通过4片74HC595驱动16x16 LED点阵显示数字/汉字
代码: LED点阵显示数字/汉字
原理
💡 像用电子画笔逐行绘制数字
行选控制:每次点亮一行(类似画板横格线)
列数据:决定该行哪些LED亮(类似在横线上画黑点)
快速轮巡:以人眼无法察觉的速度逐行刷新(类似翻页动画)
关键数据结构
// 列数据:32字节(16行×2字节)
u8 gled_col[32] = {
0x00,0x00, // 第1行列数据(低8位+高8位)
0xE0,0x03, // 第2行 → 二进制11100000 00000011 → 第1-3列亮
...
};
// 行数据:32字节(前16控制行低8位,后16控制行高8位)
u8 gled_row[32] = {
0x01,0x02, // 第1行 → 00000001(A芯片)| 00000010(B芯片)→ 行1
0x04,0x08, // 第2行 → 00000100 | 00001000 → 行2
...
};
核心代码流程
for(i=0; i<16; i++) {
// 发送数据:行低8位 | 行高8位 | 列低8位取反 | 列高8位取反
hc595_write_data(gled_row[i], gled_row[i+16], ~gled_col[i*2], ~gled_col[i*2+1]);
delay_10us(10);
hc595_write_data(0x00, 0x00, 0x00, 0x00); // 消影
}
硬件连接拓扑
|--595A → 行1-8
单片机--|--595B → 行9-16
|--595C → 列1-8
|--595D → 列9-16
扫描参数分析
单行显示时间:约10us(延时)+ 数据发送时间 ≈ 15us
整屏刷新率:16行×15us = 240us → 约4166Hz(远超无闪烁要求)
显示效果拆解
以数字0的中间部分为例:
行5数据:0x04,0x10 → 列3和列13亮
行6数据:0x04,0x10 → 列3和列13亮
...
形成数字0的左右竖线
注意事项
⚠️ 电平匹配:
列数据取反(~gled_col)说明硬件为共阳接法
若显示反向(亮灭颠倒),需去除取反操作
⚠️ 硬件限制:
每行最大同时点亮LED数受驱动芯片电流限制
长时间静态显示建议降低亮度(增加消影时间)
性能优化建议
// 使用NOP指令优化时序(原代码已实现)
_nop_(); // 产生1个机器周期延时(约1us @12MHz晶振)
// 示例:动态亮度调节
if(i==0) delay_10us(20); // 首行加亮
else delay_10us(10);
扩展显示其他字符
制作字模:(工具里有取字模软件)
// 数字1的列数据示例
{0x00,0x00,0x10,0x04,0x08,0x04,0xFC,0x07,...}
切换显示:
u8 num=0;
if(按键按下) num=(num+1)%10;
memcpy(gled_col, number_font[num], 32);
故障排查表
现象 可能原因 解决方案
显示重影 消影时间不足 增加delay_10us(10)至20
某行不亮 行数据错误 检查gled_row[i]和gled_row[i+16]组合
列镜像显示 高低位顺序错误 交换gled_col[i2]和gled_col[i2+1]
附:数字0点阵示意图
复制
██████
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██ ██
██████
直流电机控制
语言: C
简介: 控制直流电机运行5秒后停止
代码: 直流电机控制
原理
💡 像用定时插座控制电风扇
P1.0引脚 相当于电源开关
DC_Motor=1 → 接通电源(类似按下开关)
DC_Motor=0 → 切断电源(类似拔掉插头)
延时函数 像闹钟倒计时:
5000ms=5秒 → 相当于设置5分钟后自动关风扇
驱动本质:通过晶体管/MOS管放大电流(单片机引脚不能直接驱动电机)
关键代码结构
void main() {
DC_Motor = 1; // 开启电机(类似打开水龙头)
delay_ms(5000); // 维持5秒水流
DC_Motor = 0; // 关闭电机(类似拧紧水龙头)
while(1); // 进入待机状态
}
输入输出
-> 无外部输入
<- P1.0输出PWM信号(本代码实际输出100%占空比方波)
实现亮点
✅ 直接操作寄存器实现快速响应
✅ 宏定义运行时间方便修改
✅ 简洁的启停控制逻辑
硬件连接示意图
单片机P1.0 → 驱动模块信号端
驱动模块电源端 → 5V电源
驱动模块输出端 → 直流电机
(⚠️ 必须通过驱动模块连接,避免烧毁单片机)
工作流程
上电后立即启动电机
持续运行5000ms(5秒)
永久关闭电机
程序进入死循环保持关闭状态
性能参数
驱动电压:5V(需根据电机规格调整)
最大电流:取决于驱动模块(常用L298N模块支持2A)
控制精度:延时误差约±10%(受晶振频率影响)
注意事项
⚠️ 必须遵守:
严禁将电机直接连接单片机IO口
电机负极接O1端子,正极接5V端子(反接会导致反转)
大功率电机需外接独立电源
⚠️ 扩展注意:
添加续流二极管防止电机关闭时反向电动势损坏电路
超过100mA电流需加散热片
升级建议
// 示例:添加PWM调速功能
for(int i=0;i<5000;i++){
DC_Motor = 1;
delay_ms(1); // 占空比调节
DC_Motor = 0;
delay_ms(3);
}
故障排查表
现象 可能原因 解决方案
电机不转 驱动模块未供电 检查模块电源指示灯
电机抖动 电源功率不足 更换更大电流电源
单片机重启 电机干扰电源 增加电源滤波电容
电机小知识
有刷 vs 无刷电机:
本文代码适配有刷电机(价格低、易驱动)
无刷电机需专用驱动器(但效率高、寿命长)
步进电机控制
语言: C
简介: 通过ULN2003驱动28BYJ-48步进电机,实现方向切换/调速控制
代码: 步进电机控制
原理
💡 像用电子节拍器控制舞者步伐
相位控制:
四相八拍模式(A→AB→B→BC→C→CD→D→DA)
类似舞者的8个基础舞步动作
方向控制:
顺/逆时针 = 正序/倒序播放舞步
调速原理:
delay_ms(speed)控制舞步切换间隔 → 间隔越短速度越快
关键代码结构
// 核心驱动函数
void step_motor_send_pulse(u8 step, u8 dir) {
if(dir==0) step=7-step; // 方向取反
switch(step) { // 输出相位组合
case 0: A=1,B=0,C=0,D=0; break;
case 1: A=1,B=1,C=0,D=0; break;
... // 其他相位组合
}
}
// 主控制逻辑
while(1) {
key = 按键扫描();
if(KEY1按下) 方向反转;
if(KEY2按下) 速度加快;
if(KEY3按下) 速度减慢;
发送脉冲(step++, dir);
delay_ms(speed);
}
输入输出
-> P3.0-P3.2输入按键信号
<- P1.0-P1.3输出四相控制信号 → ULN2003 → 步进电机
实现亮点
✅ 双旋转方向支持(顺/逆时针)
✅ 动态速度调节(1~5级可调)
✅ 8拍驱动模式(比4拍更平稳)
✅ 按键消抖处理
性能参数
速度范围:
STEPMOTOR_MAXSPEED=1 → 约100RPM
STEPMOTOR_MINSPEED=5 → 约20RPM
步距角:5.625°/步 → 64步/圈(实际需512步因减速齿轮)
硬件连接图
┌─────ULN2003─────┐
P1.0 IN1 ──┤A 电机├─ +5V
P1.1 IN2 ──┤B ├─ 红
P1.2 IN3 ──┤C ├─ 蓝
P1.3 IN4 ──┤D ├─ 粉
└────────────────┘
ULN2003 芯片详解
一、概述
ULN2003 是一款 高耐压、大电流达林顿晶体管阵列,专为驱动大功率负载设计,广泛应用于继电器、步进电机、LED显示屏等场景13。其核心特点包括:
驱动能力:单路输出电流可达 500mA/50V,多路并联可提升总驱动能力25。
兼容性:输入兼容 TTL/CMOS 逻辑电平(5V),可直接连接单片机等控制器13。
保护设计:内置 续流二极管,可抑制感性负载的反电动势,保护电路安全35。
二、内部结构与引脚功能
内部结构:
集成 7组NPN达林顿管,每组串联 2.7kΩ 基极电阻,简化外部电路设计13。
输入级含 4kΩ 下拉电阻,防止单片机IO口悬空导致误动作37。
关键引脚:
IN1~IN7:输入信号端,连接控制器IO口。
OUT1~OUT7:集电极开路输出端,需外接负载至电源57。
COM:
驱动电阻性负载(如LED)时可悬空;
驱动感性负载(如继电器)时需接电源,为续流二极管提供回路58。
三、工作原理
逻辑控制:
输入高电平时,对应达林顿管导通,输出端(OUT)拉低至低电平,形成灌电流通路57。
输入低电平时,输出端呈高阻态,负载断电57。
续流保护:
感性负载断电时,内部续流二极管导通,释放线圈储能,避免高压击穿芯片35。
四、典型应用场景
继电器驱动:单片机的低电流信号通过ULN2003放大,直接控制继电器线圈15。
步进电机驱动:多路并联驱动四相或五相步进电机(如28BYJ-48)57。
LED驱动:驱动大电流LED阵列或数码管,需外接限流电阻46。
逻辑缓冲器:增强信号带载能力,隔离控制器与负载电路67。
五、关键设计要点
灌电流设计:
负载需接在 电源正极与OUT引脚之间,通过芯片灌入电流形成回路78。
布线优化:
GND引脚走线需加粗,避免大电流导致压降或发热5。
扩展驱动能力:
多路并联时,总驱动电流可达 3.5A(7×500mA)25。
六、与同类芯片对比
ULN2003 vs ULN2803:后者支持8路输出,驱动能力相同,适用于更多通道需求6。
ULN2003 vs 晶体管阵列:集成度高,省去外部续流二极管和基极电阻,简化PCB设计13。
总结
ULN2003 凭借其高可靠性、强驱动能力及内置保护功能,成为工业控制、消费电子等领域中驱动大功率负载的首选方案15。设计时需注意 灌电流连接方式、COM引脚配置及 散热处理,以确保系统稳定运行。
注意事项
⚠️ 必须遵守:
务必通过ULN2003驱动芯片连接电机
电机红线接5V,其他线序需严格对应
长时间运行需注意电机发热
⚠️ 优化建议:
添加限位开关保护
改用定时器中断控制脉冲
增加急停按键功能
扩展应用示例
// 示例:添加圈数计数器
if(step_count >= 512){ // 满1圈
step_count=0;
motor_stop();
}
故障排查表
现象 可能原因 解决方案
电机振动不转 相位顺序错误 检查A/B/C/D接线顺序
只能单向转 方向控制逻辑错误 检查step=7-step实现
加速无反应 速度已达上限 检查STEPMOTOR_MAXSPEED值
步进电机小知识
28BYJ-48命名含义:
28:外径28mm
B:步进式
YJ:永磁减速
48:四相八拍
外部中断控制LED
语言: C
简介: 通过外部中断0实现按键控制LED状态翻转
代码: 外部中断控制LED
原理
💡 像门铃触发机制
INT0(P3.2) 是门铃按钮接口
按键按下产生下降沿(类似按下门铃瞬间)
触发中断服务程序(如同门铃响)
消抖处理 像确认访客身份
延时10ms后再次检测按键状态
防止弹簧抖动造成误触发
中断编号与功能对照表 interrupt
中断号 中断名称 中断源 触发方式 相关寄存器 向量地址 典型应用场景
0 外部中断 0 (INT0) P3.2 引脚电平/边沿变化 - 低电平触发 (IT0=0)- 下降沿触发 (IT0=1) TCON (IT0, IE0)IE (EX0, EA) 0x0003 按键、传感器信号触发
1 定时器 0 溢出 定时器 0 计数器溢出 定时器溢出时自动触发 (TF0=1) TCON (TF0)IE (ET0, EA) 0x000B 定时任务、PWM 生成
2 外部中断 1 (INT1) P3.3 引脚电平/边沿变化 - 低电平触发 (IT1=0)- 下降沿触发 (IT1=1) TCON (IT1, IE1)IE (EX1, EA) 0x0013 第二路外部事件检测
3 定时器 1 溢出 定时器 1 计数器溢出 定时器溢出时自动触发 (TF1=1) TCON (TF1)IE (ET1, EA) 0x001B 高精度定时、串口波特率生成
4 串口中断 (UART) 串口接收/发送完成 - 接收完成 (RI=1)- 发送完成 (TI=1) SCON (RI, TI)IE (ES, EA) 0x0023 串口通信数据处理
5 定时器 2 溢出 (仅8052系列)定时器2溢出 定时器溢出或捕获事件触发 (TF2=1 或 EXF2=1) T2CON (TF2, EXF2)IE (ET2, EA) 0x002B 扩展定时、PWM 或捕获功能
关键配置说明
中断使能:
全局中断使能:EA = 1(必须开启)。
本地中断使能:例如 EX0=1(开启 INT0)、ET0=1(开启定时器 0)。
优先级设置:
通过 IP 寄存器设置优先级(如 PX0=1 设为高优先级)。
关键代码结构
// 中断初始化
void exti0_init() {
IT0 = 1; // 下降沿触发
EX0 = 1; // 开启外部中断0
EA = 1; // 总中断使能
}
// 中断服务函数
void exti0() interrupt 0 {
delay_10us(1000); // 10ms消抖
if(KEY3==0) // 二次确认
LED1 = !LED1; // LED状态翻转
}
硬件连接要求
按键接法:
K3一端 → GND
K3另一端 → P3.2 (INT0引脚)
LED1:P2.0 → LED阳极 → 330Ω电阻 → VCC
工作流程
按键按下:P3.2电平从1→0产生下降沿
触发中断:CPU暂停主程序,执行exti0()
消抖确认:等待10ms后再次检测K3状态
状态翻转:若确认有效按下,LED亮/灭切换
性能参数
中断响应时间:约3-8个机器周期(@12MHz)
消抖延时:10ms(可调范围为5-20ms)
最大触发频率:约50次/秒(受消抖时间限制)
优化建议
// 改进方案:使用定时器消抖(减少中断阻塞时间)
volatile bit key_flag = 0;
void exti0() interrupt 0 {
key_flag = 1; // 仅设置标志位
}
void main() {
while(1) {
if(key_flag) {
delay_ms(10);
if(!KEY3) LED1=!LED1;
key_flag=0;
}
}
}
常见问题排查
现象 可能原因 解决方案
LED无反应 中断未使能 检查EA/EX0是否置1
偶尔误触发 消抖不足 增大delay_10us参数
只能切换一次 按键卡住 检查按键是否正常弹起
硬件设计要点
上拉电阻:P3口内置弱上拉,无需外接
防干扰措施:
按键并联104瓷片电容滤高频噪声
长导线连接时串接100Ω电阻
LED限流:330Ω电阻限制电流约10mA
中断机制详解
触发条件:
IT0=1:下降沿触发(1→0跳变)
IT0=0:低电平触发(需手动清除标志)
执行特点:
自动保护现场(ACC/PSW等入栈)
中断优先级:INT0为最高优先级(默认)
中断标志IE0自动清零(仅边沿触发模式)
实验升级方向
多中断协同:结合定时器实现呼吸灯
中断优先级:添加紧急停止按钮(INT1)
功耗优化:中断唤醒休眠模式
定时器控制LED闪烁
语言: C
简介: 利用定时器0中断实现LED1精确1秒间隔闪烁
代码: 定时器控制LED闪烁
原理
💡 像用秒表定时提醒
定时器0 是电子秒表:
每1ms震动一次(中断) → 震动1000次=1秒
每次震动自动重置秒表(重装初值)
LED控制 像闹钟提醒:
累计1000次震动后 → 翻转LED状态
定时器0 和 定时器1 配置项对比表
配置项 定时器0(T0) 定时器1(T1) 作用 相关寄存器/位 可选值/配置方式
模式设置 TMOD = 0x01
(低4位控制T0) TMOD = 0x10
(高4位控制T1) 设置定时器为指定模式(模式0~3) TMOD - 模式0:13位定时器(已淘汰)- 模式1:16位定时器(常用)- 模式2:8位自动重装(适合波特率生成)- 模式3:双8位定时器(仅T0可用)
初值高位 TH0 = 0xFC TH1 = 0xFC 设置定时器初值高位(与TLx共同决定定时时长) TH0(T0)TH1(T1) 范围:0x00~0xFF需根据晶振频率和目标时间计算(见下方公式)。
初值低位 TL0 = 0x18 TL1 = 0x18 设置定时器初值低位 TL0(T0)TL1(T1) 同上
中断使能 ET0 = 1 ET1 = 1 允许定时器溢出时触发中断 IE寄存器 - ETx=1:开启中断- ETx=0:关闭中断
总中断使能 EA = 1(全局使能,T0和T1共用) EA = 1 总中断开关,需与ETx配合使用 IE寄存器(EA位) - EA=1:允许所有中断- EA=0:禁止所有中断
启动控制 TR0 = 1 TR1 = 1 启动定时器运行 TCON寄存器 - TRx=1:启动定时器- TRx=0:停止定时器
溢出标志 TF0(溢出时硬件置1,需手动清零) TF1(溢出时硬件置1,需手动清零) 标志定时器溢出状态 TCON寄存器 - 在中断服务函数中需手动清零:TFx = 0;
中断号 interrupt 1 interrupt 3 中断服务函数的标识号 无 - T0中断号:1- T1中断号:3
关键参数计算公式
定时初值计算(16位模式):
初
值
目
标
时
间
(
秒
)
晶
振
频
率
(
示例
初
值
自动重装模式初值计算(模式2,8位):
初
值
目
标
时
间
(
秒
)
晶
振
频
率
(
)
关键代码结构
void time0_init() {
TMOD |= 0x01; // 定时器0模式1(16位)
TH0 = 0xFC; // 初值高位
TL0 = 0x18; // 初值低位
ET0 = 1; // 使能定时器0中断
EA = 1; // 总中断使能
TR0 = 1; // 启动定时器
}
void time0() interrupt 1 { //中断服务函数需在中断中重装初值并清除标志位
TH0 = 0xFC; // 重装初值
TL0 = 0x18;
static u16 i = 0;
if(++i >= 1000) {
i = 0;
LED1 = !LED1; // 每秒翻转
}
}
硬件连接
LED1 → P2.0 → 330Ω电阻 → GND
晶振:12MHz(关键参数)
定时器参数详解
晶振频率:12MHz → 机器周期=1μs
初值计算:
目标定时:1ms
计数值 = 65536 - 1000 = 64536 → 0xFC18
误差分析:
实际定时 = (65536 - 64536) × 1μs = 1000μs = 1ms
累计1000次 → 精确1秒
性能优化建议
// 使用自动重载模式(模式2)优化
TMOD |= 0x02; // 模式2(8位自动重载)
TH0 = 0x38; // 200μs中断一次
TL0 = 0x38;
if(++i >= 5000) { // 200μs×5000=1s
// ...
}
常见问题排查
现象 可能原因 解决方案
LED不闪烁 中断未启用 检查EA/ET0/TR0是否置1
闪烁过快 初值错误 重新计算定时初值
间隔不准 晶振频率不符 调整初值或更换晶振
定时器工作流程
初始化设置1ms定时
定时器启动后从0xFC18开始累加
计满65536后触发中断(耗时1ms)
中断服务程序重装初值并计数
1000次中断后(1秒)翻转LED
扩展应用
多任务定时:在中断内设置多个计数器,实现不同时间基准
PWM调光:通过定时器控制LED占空比
实时时钟:结合秒、分、时计数器
// 示例:添加分钟计数器
if(++sec >=60) {
sec=0;
min++;
}
关键学习点
定时器模式选择
中断服务程序编写规范
静态变量在中断中的运用
精确计时算法设计
串口通信
语言: C
简介: 实现单片机与电脑串口助手的数据回传(类似“复读机”功能)
代码: 串口通信
原理
💡 像两个人在打电话
单片机:耳朵(接收数据)和嘴巴(发送数据)同时工作(全双工)
SBUF寄存器:相当于电话听筒,既收声又发声
中断机制:像电话铃声,一有来电(数据)立即接听
通信本质:
双方约定相同的“语速”(波特率),单片机收到信息后像复读机一样原样复述。CH340芯片相当于翻译官,把USB信号和单片机信号互相转换。
串口初始化配置项详解表
配置项 作用 相关寄存器/位 可选值/配置方式 示例及注意事项
TMOD |= 0x20 设置 定时器1 为 模式2(8位自动重装模式),用于生成波特率。 TMOD(高4位控制T1) - TMOD=0x20:定时器1模式2(常用串口波特率生成)- 其他模式:0x00(模式0)、0x10(模式1)、0x30(模式3,无效) 使用 |= 避免覆盖定时器0的配置(低4位)。若需单独配置:TMOD = (TMOD & 0x0F) | 0x20。
SCON = 0x50 设置 串口工作方式1(10位异步收发),并开启接收使能。 SCON - 工作方式:- 0x40:方式1(8位数据,可变波特率)- 0x50:方式1 + 允许接收(REN=1)- 0x60:方式2(9位固定波特率)- 0x70:方式3(9位可变波特率) 必须设置 REN=1 才能接收数据。方式1是8位数据帧,适用于标准通信。
PCON = 0x80 波特率加倍(仅方式1和方式3有效),提高通信速率。 PCON(SMOD位) - SMOD=1(波特率加倍):PCON |= 0x80- SMOD=0(默认波特率):PCON &= ~0x80 若晶振频率较低(如11.0592MHz),加倍可支持更高波特率(如115200)。
TH1 = baud 设置定时器1的 重装初值(决定波特率),需根据晶振频率计算。 TH1(8位自动重装值) 范围:0x00~0xFF初值计算公式:TH1=256−晶振频率12×32×波特率TH1=256−12×32×波特率晶振频率 示例:12MHz晶振,波特率9600:TH1=256−12,000,00012×32×9600=253→0xFDTH1=256−12×32×960012,000,000=253→0xFD
TL1 = baud 定时器1启动时,初值从 TL1 加载,但模式2下 TL1自动重装。 TL1(初始计数值) 同TH1(自动重装模式下,TL1仅在首次启动时使用) 通常与TH1设置相同值,保证首次计数值正确。
ES = 1 使能 串口中断(接收完成或发送完成时触发中断)。 IE寄存器(ES位) - ES=1:开启串口中断- ES=0:关闭串口中断 需与 EA=1(总中断使能)同时开启。中断服务函数中需手动清除 RI 或 TI 标志。
EA = 1 开启 全局中断使能(总开关)。 IE寄存器(EA位) - EA=1:允许所有中断- EA=0:禁止所有中断 若关闭EA,即使单独开启ES,串口中断也无法触发。
TR1 = 1 启动 定时器1 运行,开始生成波特率。 TCON寄存器(TR1位) - TR1=1:启动定时器1- TR1=0:停止定时器1 定时器1必须启动才能生成波特率时钟信号。
串口工作方式对比表
工作方式 SCON设置(SM0, SM1) 数据格式 波特率来源 是否自动重装 典型应用
方式0 SM0=0, SM1=0(SCON=0x00) 同步移位寄存器模式:- 8位数据(无起始/停止位) 固定为 晶振频率/12(不可调) 否 - 扩展I/O(如74HC595驱动LED)- 同步数据传输(需外接芯片配合)
方式1 SM0=0, SM1=1(SCON=0x40) 异步通信模式:- 1起始位- 8位数据- 1停止位 由 定时器1溢出率 控制(常用模式2自动重装): 是(仅定时器1模式2) - 标准UART通信(如PC与单片机通信)- 传感器数据接收(如GPS模块、蓝牙模块)
方式2 SM0=1, SM1=0(SCON=0x80) 异步通信模式:- 1起始位- 9位数据(第9位可作地址/校验位)- 1停止位 固定为: 否 - 多机通信(通过第9位区分地址/数据帧)- 简单校验通信(奇偶校验替代方案)
方式3 SM0=1, SM1=1(SCON=0xC0) 异步通信模式:- 1起始位- 9位数据- 1停止位 同 方式1(由定时器1溢出率控制) 是(仅定时器1模式2) - 高可靠性多机通信(支持可变波特率)- 需要校验位的复杂协议(如自定义纠错协议)
应用场景总结
工作方式 适用场景
方式0 硬件扩展(如LED屏驱动)、同步数据传输
方式1 标准异步通信(PC与单片机、模块间通信)
方式2 固定波特率多机通信(从机地址过滤)
方式3 可变波特率多机通信或高可靠性通信(支持校验位)
关键配置公式与示例
SCON寄存器:
SM0, SM1:选择工作方式(见上表)。
REN:接收使能位(必须置1才能接收数据)。
TB8/RB8:方式2/3中第9位数据的发送/接收位。
波特率计算公式(方式1,定时器1模式2):
波
特
率
晶
振
频
率
示例(11.0592MHz晶振,波特率9600,SMOD=1):
多机通信流程(方式2/3):
主机发送地址帧时,置 TB8=1。
从机接收地址帧后,若匹配自身地址,则置 SM2=0 以接收后续数据帧。
关键代码结构**
void uart_init(u8 baud) //初始化串口
{
TMOD|=0X20; //设置计数器工作方式2
SCON=0X50; //设置为工作方式1
PCON=0X80; //波特率加倍
TH1=baud; //计数器初始值设置
TL1=baud;
ES=1; //打开接收中断
EA=1; //打开总中断
TR1=1; //打开计数器
}
void main() {
uart_init(0XFA); // 初始化串口(设定通话语速为9600字/分钟)
while(1); // 挂机等待来电(实际应用中可在此处理其他任务)
}
// 中断服务函数(电话铃响时的自动接听)
void uart() interrupt 4 {
RI = 0; // 擦掉来电显示(清除接收标志)
SBUF = SBUF; // 原声回放(接收数据直接转发)
while(!TI); // 等对方说完"喂?"(等待发送完成)
TI = 0; // 挂断准备下次通话(清除发送标志)
}
输入输出
→ P3.0(RXD):接收来自电脑的数据(耳朵)
← P3.1(TXD):发送数据给电脑(嘴巴)
实现亮点
✅ 中断驱动:不占用主程序资源(像智能来电提醒)
✅ 自动重装载:定时器1工作方式2保证波特率稳定(类似节拍器)
✅ 零数据丢失:即时响应接收中断
硬件连接示意图
电脑USB口 ↔ CH340芯片 ↔
↗ P3.0(RXD) → 单片机"耳朵"
↘ P3.1(TXD) ← 单片机"嘴巴"
(黄色跳线帽像电话线,连接翻译官和大脑)
工作流程
1️⃣ 初始化设定通信参数(9600bps, 8位数据)
2️⃣ 持续监听串口(类似手机待机)
3️⃣ 收到数据立即触发"电话接听"(中断)
4️⃣ 自动回传相同内容(智能客服)
性能参数
波特率:9600bps(每秒约传输960个字符)
误差容忍度:<3%(需使用11.0592MHz晶振保证精度)
最大传输距离:约1.5米(若加MAX232芯片可延长至15米)
注意事项
⚠️ 保命三原则:
必须使用跳线帽连接CH340与单片机(忘接就像没插电话线)
双方波特率必须完全一致(否则像中国人对英国人讲方言)
避免长导线引入干扰(好比打电话时远离电磁炉)
⚠️ 进阶防护:
添加奇偶校验位检测错误(类似重要信息重复确认)
关键数据增加帧头帧尾(像快递包裹贴标签)
升级建议
// 示例:添加数据校验功能
void uart() interrupt 4 {
static u8 cnt;
RI = 0;
if(SBUF == 0xAA && cnt==0){ // 检测帧头
cnt++;
} else {
SBUF = SBUF ^ 0xFF; // 数据取反校验
while(!TI);
TI = 0;
cnt = 0;
}
}
故障排查表
现象 可能原因 急救措施
收到乱码 波特率不匹配 检查双方是否都是9600
数据截断 未等待TI标志 在while(!TI)前加延时
只能收不能发 TXD线接触不良 用万用表蜂鸣档检测通路
通信冷知识
RS232 vs TTL:
本实验使用TTL电平(0-5V),直接连接CH340
传统串口使用±12V电平,需MAX232转换(像电压翻译官)
I2C通信
语言: C
简介: 实现I2C总线基础通信协议(主设备控制)
代码:IIC代码
原理
💡 像收发摩尔斯电码
SCL(时钟线):相当于节拍器,控制数据传输节奏
SDA(数据线):实际传输数据的通道
起始信号:类似"喂喂,听得到吗?"(SCL高时SDA从高→低)
停止信号**:像说"再见"挂断(SCL高时SDA从低→高)
通信本质:
主设备(单片机)通过精确的时序控制,与从设备(如EEPROM、传感器)进行字节级的对话。每个数据位传输都需时钟线配合,像两人踩着鼓点传纸条。
关键代码结构
🔌 通信启停
void iic_start() {
SDA=1; SCL=1; → 举起旗子
SDA=0; → 突然放下(开始信号)
SCL=0; → 压低节拍器准备传数据
}
void iic_stop() {
SDA=0; SCL=1; → 最后时刻
SDA=1; → 突然举旗(结束信号)
}
📩 数据收发
// 发送字节(像发射8发子弹)
void iic_write_byte(u8 dat) {
for(8次){
SCL=0; → 准备装弹
SDA = (最高位)?1:0; → 设置当前位
SCL=1; → 扣扳机发射
dat <<=1; → 旋转弹夹
}
}
// 接收字节(像听8声敲门)
u8 iic_read_byte() {
for(8次){
SCL=1; → 竖起耳朵
if(SDA高) receive++; → 记录敲门声
SCL=0; → 准备听下一声
}
}
👍 👎 应答机制
void iic_ack() {
SDA=0; → 点头说"收到"
SCL脉冲 → 确认动作
}
void iic_nack() {
SDA=1; → 摇头说"没听懂"
SCL脉冲 → 拒绝动作
}
硬件连接示意图
单片机 从设备
P2.0(SCL) ↔ SCL → 所有从设备的SCL
P2.1(SDA) ↔ SDA → 所有从设备的SDA
(需外接4.7K上拉电阻,像给电线加上弹簧)
工作流程
1️⃣ 主设备发起start信号
2️⃣ 发送从设备地址 + 读写位(7位地址+1位方向)
3️⃣ 等待从设备ACK
4️⃣ 传输数据字节(每次等应答)
5️⃣ 发送stop结束通信
性能参数
通信速率:约100kHz(标准模式)
寻址范围:7位地址支持128个设备
传输距离:通常<1米(高速模式更短)
注意事项
⚠️ 保命三原则:
必须外接上拉电阻(SCL/SDA到VCC接4.7K电阻)
总线空闲时SCL/SDA保持高电平
不同从设备需分配唯一地址
⚠️ 时序关键点:
保持信号建立/保持时间(代码中的delay_10us)
从设备响应超时需重试(代码中的time_temp>100判断)
写操作后需等待EEPROM写入周期(约5ms)
升级建议
// 添加错误重试机制
u8 iic_write_with_retry(u8 addr, u8 data) { // 定义I2C带重试的写函数,参数:设备地址addr,写入数据data
for(int i=0; i<3; i++){ // 最多重试3次
iic_start(); // 发送I2C起始信号(SCL高时SDA从高变低)
if(!iic_write_byte(addr<<1)){ // 尝试写入设备地址(左移1位,最低位0表示写操作),返回0表示收到ACK应答
iic_write_byte(data); // 地址验证成功,继续写入数据字节
iic_stop(); // 发送I2C停止信号(SCL高时SDA从低变高)
return SUCCESS; // 返回成功状态码
}
}
return FAIL; // 3次重试均失败,返回失败状态码
}
故障排查表
现象 可能原因 急救措施
无任何响应 未接上拉电阻 补焊4.7K电阻到VCC
只能读不能写 从设备地址错误 确认7位地址是否包含方向位
随机数据错误 总线受干扰 缩短线长,加磁环
协议冷知识
I2C vs SPI:
I2C省引脚但速度较慢(适合传感器)
SPI需要更多线但速度更快(适合存储器)
I2C有官方协议文档,SPI更像"约定俗成"
AT24C02 EEPROM读写
语言: C
简介: 实现I2C EEPROM芯片AT24C02的字节级读写操作
代码: AT24C02 EEPROM读写
原理
💡 像在笔记本上记小纸条
AT24C02:带256页的电子笔记本(256x8=2KB容量)
0xA0地址:笔记本封面写的收件人名字(设备固定地址)
页地址:相当于页码(00-FF),每页存8位数字(1字节)
操作本质:
通过I2C总线告诉EEPROM:"在第XX页写数字YY" 或 "把第XX页的数字读给我看"。芯片内部有"缓存-存储"机制,写入需要时间(类似墨水晾干)。
关键代码结构
📝 写入数据(存纸条)
void write_data(u8 page, u8 data) {
iic_start(); // 敲开EEPROM的门
iic_write_byte(0xA0); // 喊:"AT24C02同学!"
iic_write_byte(page); // "请翻到第XX页"
iic_write_byte(data); // "在这里写数字YY"
iic_stop(); // 合上笔记本
delay_ms(10); // 等墨水干(必须等待!)
}
📖 读取数据(查笔记)
u8 read_data(u8 page) {
u8 result;
iic_start(); // 再次敲门
iic_write_byte(0xA0); // "AT24C02同学!"
iic_write_byte(page); // "请翻到第XX页"
iic_start(); // 突然说:"现在请读给我听!"
iic_write_byte(0xA1); // 切到听筒模式
result = iic_read_byte(0); // 竖起耳朵听数字
iic_stop(); // 合上笔记本
return result; // 返回听到的数字
}
硬件连接示意图
单片机 AT24C02
P2.0(SCL) → SCL → 第5脚
P2.1(SDA) ↔ SDA → 第6脚
GND → A0/A1/A2 → 设置地址(全接地表示地址0)
VCC(5V) → VCC → 第8脚
(需在SCL/SDA线上接4.7K上拉电阻,像给电线装弹簧)
工作流程
写入操作:
发送起始信号 → 2. 写设备地址(0xA0) → 3. 写页地址 → 4. 写数据 → 5. 停止 → 6. 等待10ms
读取操作:
发送起始信号 → 2. 写设备地址(0xA0) → 3. 写页地址 → 4. 重复起始 → 5. 写读地址(0xA1) → 6. 读数据 → 7. 停止
性能参数
写入时间:5ms(代码延迟10ms保平安)
寿命周期:100万次擦写(像铅笔写了擦)
数据保留:100年(比纸质笔记本更可靠)
注意事项
⚠️ 保命三原则:
必须延迟10ms再写下一页(否则数据丢失)
地址不能超过0xFF(最多256字节)
多设备时通过A0/A1/A2设置不同地址(类似班级座位号)
⚠️ 隐藏陷阱:
跨页写入需分多次(每页最多写8字节)
低温环境延迟需增加(墨水干得慢)
突然断电会导致最后写入数据丢失
升级建议
// 示例:多字节连续写入
void write_page(u8 start_page, u8 *data, u8 len) {
for(int i=0; i<len; i++){
if((start_page+i)%8 == 0) delay_ms(10); // 每8字节等待
at24c02_write_one_byte(start_page+i, data[i]);
}
}
故障排查表
现象 可能原因 急救措施
写后读不到 未延迟直接读取 在write后加足够延时
地址错乱 A0/A1/A2配置错误 检查芯片地址引脚电平
只能读写一次 未正确发送停止信号 用示波器抓取I2C波形
存储冷知识
EEPROM vs FLASH:
EEPROM可单字节擦写(像便签纸)
FLASH需整块擦除(像黑板擦)
AT24C02属于"字节可寻址"存储,适合小数据频繁修改
DS18B20温度传感器
语言: C
简介: 实现单总线温度传感器DS18B20的数据采集
代码: DS18B20温度传感器
原理
💡 像与智能温度计对话
单总线协议:仅用一根数据线完成供电、时钟、数据(需严格时序控制)
温度分辨率:0.0625℃/LSB(像温度计的刻度精确到小数点后四位)
负温度处理:采用二进制补码形式(类似数学中的负数表示法)
通信本质:
通过精确的时序脉冲发送指令,传感器将温度数据编码为16位二进制数返回。由于采用寄生供电,需在数据线维持高电平时充电。
关键代码结构
🔁 总线复位(敲门问候)
void ds18b20_reset() {
DQ=0; delay_10us(75); // 用力敲门750us
DQ=1; delay_10us(2); // 松手等回应(15-60us存在脉冲)
}
🔍 存在检测(确认应答)
u8 ds18b20_check() {
while(DQ高 && 未超时); // 等待传感器拉低总线
if(超时) return 1; // 没人应答
while(DQ低 && 未超时); // 等待传感器释放总线
return (超时)?1:0; // 返回检测结果
}
📖 数据读取(逐位接收)
u8 read_bit() {
DQ=0; _nop_(); // 制造读时隙
DQ=1; _nop_(); // 释放总线
return DQ电平; // 1为高,0为低
}
u8 read_byte() {
u8 dat=0;
for(8次){
dat |= (read_bit() << i); // 低位优先拼接
}
return dat;
}
📝 数据写入(时序控制)
void write_byte(u8 dat) {
for(8次){
if(当前位为1){
DQ=0; _nop_(); // 写1时隙
DQ=1; delay(6); // 维持高电平
} else {
DQ=0; delay(6); // 写0时隙
DQ=1; _nop_();
}
}
}
工作流程
初始化:复位总线 → 检测存在脉冲
启动转换:发送0xCC(SKIP ROM) → 0x44(启动转换)
等待转换:延时750ms(12位精度时)
读取温度:复位 → 发送0xCC → 0xBE(读暂存器) → 读取两个字节
数据处理:合并高低字节 → 判断符号位 → 转换为浮点温度
关键参数
参数 数值 说明
分辨率 0.0625℃/bit 12位模式下的精度
转换时间 750ms(max) 12位精度需等待时间
温度范围 -55℃ ~ +125℃ 超出范围可能损坏传感器
供电方式 寄生/外部供电 需严格时序维持供电
代码优化建议
增加转换等待:在ds18b20_start()后添加延时确保转换完成
void ds18b20_start() {
... // 现有代码
delay_ms(800); // 保证12位转换完成
}
错误重试机制:在读取温度时加入重试逻辑
float read_temp_retry(u8 retries) {
while(retries--){
if(ds18b20_init() == 0){
return ds18b20_read_temperture();
}
}
return -999; // 错误码
}
精确延时校准:根据主频调整nop()数量
// 假设12MHz晶振(1机器周期=1us)
define WRITE_1_DELAY _nop_();_nop_();_nop_();_nop_();_nop_() // 5us
硬件连接示意图
单片机 DS18B20
P3.7(DQ) ↔ DQ → 数据线(需4.7K上拉)
GND → GND → 接地
VCC(可选) → VDD → 非寄生供电时连接
故障排查表
现象 可能原因 解决方案
返回-0.0625℃ 总线未上拉 检查4.7K上拉电阻
温度值跳变 电源干扰 并联104电容在VDD-GND之间
持续85℃ 转换未完成就读取 增加转换等待时间
通讯无响应 时序偏差 用示波器校准延时
应用场景
工业现场温度监控
智能家居温控系统
冷链物流温度记录
农业大棚环境监测
注: 实际开发中建议使用经过验证的延时函数,并配合示波器调试时序,确保满足DS18B20严格的时序要求。
DS1302实时时钟驱动
语言: C
简介: 实现DS1302实时时钟芯片的初始化、时间写入与读取
代码: DS1302实时时钟驱动
原理
💡 与电子钟表的数字对话
三线接口:通过RST(复位)、CLK(时钟)、IO(数据)三根线通信,类似拍电报的节奏控制
BCD编码:时间数据用4位二进制表示十进制(如0x59表示59秒)
写保护位:0x8E寄存器的最高位是"门锁",0表示允许修改时间,1表示锁定
核心操作:
主控通过严格的时序向DS1302发送指令,读写时间寄存器。每个字节传输都需先送地址命令,再传数据,低位优先(LSB First)。
关键代码结构
📝 写单字节(发密码本)
void ds1302_write_byte(u8 addr, u8 dat) {
RST拉高启动 → 发送8位地址(低位先) → 发送8位数据(低位先) → RST拉低结束
}
时序要点:
CLK上升沿锁存数据
地址和数据均从最低位开始发送
📖 读单字节(听回音)
u8 ds1302_read_byte(u8 addr) {
RST拉高启动 → 发送地址 → 读取8位数据 → 后处理确保总线释放
}
数据拼接:
采用value = (temp<<7) | (value>>1)逐位组装,确保LSB First正确解析
最终操作IO口防止总线冲突
初始化流程
解锁写保护:向0x8E写入0x00
写入初始时间:按秒、分、时、日、月、周、年顺序写入BCD码
重新上锁:向0x8E写入0x80
初始时间示例:
u8 gDS1302_TIME = {0x47, 0x51, 0x13, 0x20, 0x04, 0x05, 0x21};
// 含义:2021年5月20日 星期四 13:51:47
硬件连接要点
单片机 DS1302
P3.0(RST) → RST → 第5脚
P3.1(CLK) → CLK → 第6脚
P3.2(IO) ↔ IO → 第7脚(需4.7K上拉电阻)
VCC → VCC → 第1脚
GND → GND → 第4脚
关键问题解析
🔍 读数据位序处理
代码逻辑:
for(i=0; i<8; i++) {
temp = IO引脚状态;
value = (temp<<7) | (value>>1); // 新位放高位,旧数据右移
}
正确性验证:
假设原始数据0x12(00010010),发送顺序:0(LSB)→1→0→0→1→0→0→0
经8次循环处理后,value=0x12,验证无误
⚠️ IO模式风险
隐患:若IO配置为推挽输出,DS1302拉低时可能短路
解决:使用准双向模式或开漏输出+上拉电阻
优化建议
时间格式转换函数
// BCD转十进制
u8 bcd_to_dec(u8 bcd) { return (bcd>>4)*10 + (bcd&0x0F); }
// 十进制转BCD
u8 dec_to_bcd(u8 dec) { return ((dec/10)<<4) | (dec%10); }
12/24小时制处理
// 设置24小时制
void ds1302_set_24h_format() {
u8 hour = ds1302_read_byte(0x85);
if(hour & 0x80) ds1302_write_byte(0x84, hour & 0x7F);
}
读取优化(防冲突)
u8 safe_read(u8 addr) {
u8 retry = 3;
while(retry--) {
u8 data = ds1302_read_byte(addr);
if(data == ds1302_read_byte(addr)) return data; // 双读校验
}
return 0xFF; // 错误码
}
故障排查指南
现象 可能原因 解决方案
时间读取全FF 未解除写保护 检查0x8E寄存器写入流程
分钟数不变化 CH位未清零 确保秒寄存器最高位为0
星期显示错误 寄存器定义误解 确认周寄存器范围(1-7)
温度影响走时 晶振未起振 检查32.768kHz晶振及电容
性能参数
时钟精度:±2ppm(约每月误差5秒)
备用电池:3V纽扣电池可维持十年
工作电压:2.0V-5.5V
协议冷知识
DS1302 vs DS3231:
DS1302:基础功能,三线接口,±2ppm精度
DS3231:I2C接口,集成温补,±2ppm精度
共同点:均需BCD码转换,注意闰年处理
红外遥控解码
语言: C
简介: 通过外部中断解码红外遥控信号
代码: 红外遥控解码
原理
💡 像快递员按门铃收包裹
外部中断0 = 门铃按钮(检测到信号下降沿触发)
引导信号 = 特殊的敲门节奏(9ms低电平 + 4.5ms高电平)
数据位 = 摩尔斯电码(0.56ms低电平 + 不同长度高电平区分0/1)
校验机制 = 核对快递单号与回执码(控制码与反码比对)
关键代码结构
void ired() interrupt 0 {
// 检测9ms引导信号(类似听特定节奏的敲门声)
while((!IRED)&&(time_cnt)) { ... }
// 解析4字节数据(像拆包裹的4层包装)
for(i=0;i<4;i++) {
// 每个字节8位(类似8位密码锁)
if(ired_high_time>=8) // 高电平>0.8ms判为1(长音=1,短音=0)
}
// 校验控制码(类似核对发票代码)
if(gired_data[2]!=~gired_data[3]) { ... }
}
输入输出
-> 红外接收头信号输入(VS1838B等)
<- 解码结果存入gired_data[4]数组
实现亮点
✅ 精准的时序检测(误差<10us)
✅ 自动校验反码防止误码
✅ 超时退出机制防止死锁
硬件连接示意图
红外接收器 → 单片机INT0引脚(P3.2)
接收器GND → 地线
接收器VCC → 5V电源
(⚠️ 接收器需避开强光直射)
工作流程
检测到下降沿触发中断(类似门铃响)
验证引导信号真伪(确认是快递员不是推销)
逐位解析32位数据(拆解4层包裹)
校验数据有效性(检查快递单完整性)
性能参数
载波频率:38kHz(兼容NEC协议)
有效距离:5-8米(受环境光影响)
响应速度:<200ms(从按下按键到解码完成)
注意事项
⚠️ 必须遵守:
接收器需加黑色滤光片防可见光干扰
同一空间避免多个遥控器同时使用
中断服务函数执行时间需<1ms
⚠️ 扩展注意:
增加软件去抖动处理(防静电干扰)
金属外壳设备需开接收窗
升级建议
// 示例:添加按键值映射表
const u8 KEY_MAP[] = {0x12,0x18,0x57,0x5A}; // 对应上下左右键
u8 get_key(){
if((gired_data[2]&0x0F) == KEY_MAP[0]) return 'U';
// 其他键值判断...
}
故障排查表
现象 可能原因 解决方案
接收不灵敏 环境光干扰 加装遮光罩
数据错乱 晶振频率偏差 调整延时参数
只能解码部分键 按键时间过长 增加重复码处理
红外小知识
NEC协议 vs RC5协议:
本文代码适配NEC协议(引导码+32位数据)
RC5协议使用相位编码(需不同解码算法)
空调遥控多用自定义协议(需逻辑分析仪抓包)
XPT2046 ADC驱动
语言: C
简介: 通过SPI总线实现12位精密模数转换
代码: XPT2046 ADC驱动
原理
💡 像智能电子秤的工作流程
CS片选 = 称重开关(拉低开始称重)
CLK时钟 = 称重传感器心跳(每个脉冲同步数据传输)
控制命令 = 选择称重模式(如净重/毛重/去皮)
12位数据 = 精确到1克的重量读数(0-4095对应量程)
关键代码结构
// 发送控制字(设置称重模式)
void xpt2046_wirte_data(u8 dat) {
DIN = dat >> 7; // 先传重要参数(如模式选择)
CLK脉冲序列 // 每个脉冲像称重传感器的确认信号
}
// 读取称重结果(获取精确读数)
u16 xpt2046_read_data() {
for(12次循环){ // 12位精度(类似电子秤显示到小数点后三位)
dat <<=1; // 腾出位置放新数据
dat |= DOUT; // 逐位组合测量结果
}
}
// 完整测量流程
u16 xpt2046_read_adc_value(u8 cmd) {
CS=0; // 启动称重
发送模式指令; // 选择单次/连续测量
等待稳定时间; // 类似等待电子秤数值稳定
读取数字结果; // 获取最终重量数据
CS=1; // 结束称重
}
输入输出
-> 模拟信号输入(支持8通道差分/单端输入)
<- 12位数字量输出(0x000-0xFFF)
实现亮点
✅ 严格遵循SPI协议时序(误差<100ns)
✅ 自动处理转换等待时间(硬件BUSY信号)
✅ 支持低功耗模式(可关闭参考电压)
硬件连接示意图
传感器信号 → XPT2046_AIN0
参考电压 → 2.5V基准源
单片机P1.5 → CS(片选)
单片机P1.6 → CLK(时钟)
单片机P1.7 → DIN(控制命令)
单片机P1.8 ← DOUT(转换结果)
(⚠️ 模拟走线需做包地处理)
工作流程
拉低CS激活芯片(按下称重开关)
发送8位控制字(选择通道/输入模式)
等待6个时钟周期的转换时间(数据稳定过程)
读取12位有效数据(获取精确读数)
拉高CS释放总线(关闭称重显示)
性能参数
转换精度:±1LSB(最大积分非线性误差)
输入范围:0-VREF(建议VREF≤VCC)
通道切换时间:1μs
功耗:0.5mW(@125KHz采样率)
注意事项
⚠️ 必须遵守:
参考电压需稳定(波动<1%)
模拟输入阻抗匹配(建议≤10kΩ)
数字信号需隔离(磁珠/0Ω电阻隔离)
⚠️ 扩展注意:
添加基准电压监控电路
低温环境需温度补偿
升级建议
// 示例:添加软件校准功能
u16 calibrated_read(u8 cmd) {
u16 raw = xpt2046_read_adc_value(cmd);
return (raw * cal_factor) >> 8; // 8位校准系数
}
故障排查表
现象 可能原因 解决方案
读数漂移 参考电压漂移 改用REF5025基准源
信号饱和 输入超量程 增加前置衰减电路
通信失败 时钟相位错误 调整CLK边沿采样点
ADC小知识
采样速率 vs 精度:
本文代码适合中速采样(<100KHz)
高速采样需减少软件延时(启用DMA)
高精度测量需外部基准源(禁用内部参考)
定时器PWM控制
语言: C
简介: 通过定时器中断实现可调占空比的PWM信号
代码: 定时器PWM控制
原理
💡 像调节水龙头水流大小
定时器中断 = 定时查看水表(固定间隔检查时间)
占空比gduty = 开水时间比例(如30%时间全开,70%关闭)
周期gtim_scale = 完整开关周期(如水龙头每2秒循环一次开关动作)
PWM引脚 = 水龙头把手(实际控制水流通断)
关键代码结构
// 初始化像设置闹钟规则
void pwm_init() {
TMOD |= 0x01; // 选择16位定时器(精确到毫秒级)
TH0 = gtim_h; // 设置初始倒计时时间
ET0 = 1; // 允许定时器报时(中断)
}
// 中断服务函数像自动水阀控制器
void pwm() interrupt 1 {
static u16 time = 0; // 水流计时沙漏
time++;
if(time <= gduty) PWM=1; // 打开水龙头
else PWM=0; // 关闭水龙头
}
输入输出
-> 占空比参数(0-gtim_scale)
-> 周期参数(定时器初值×倍数)
<- PWM波形输出(固定频率可变脉宽)
实现亮点
✅ 软件PWM灵活可调(频率/占空比独立设置)
✅ 自动重载定时初值保证精度
✅ 实时占空比调整无需重启
硬件连接示意图
单片机P1.0 → PWM输出引脚
(⚠️ 驱动大电流设备需接MOS管)
(🔧 典型应用:LED调光/电机调速)
工作流程
初始化定时器(设置计时基准)
启动定时器开始循环计时
每次中断更新"时间沙漏"
根据沙漏值切换输出电平
达到周期值时重置沙漏
性能参数
最大频率:约1KHz(12MHz晶振时)
占空比分辨率:1/gtim_scale(如gtim_scale=100时为1%)
最小脉宽:定时器中断间隔时间
注意事项
⚠️ 必须遵守:
占空比不得大于gtim_scale
定时器初值需满足中断执行时间
高频PWM需优化中断函数
⚠️ 扩展注意:
添加硬件死区保护(H桥电路必需)
电磁敏感场合需加RC滤波
升级建议
// 示例:添加频率调节函数
void pwm_set_freq(u32 freq) {
u16 reload = 65536 - (12000000/(12*freq));
gtim_h = reload >> 8;
gtim_l = reload & 0xFF;
TH0 = gtim_h; // 立即生效
TL0 = gtim_l;
}
故障排查表
现象 可能原因 解决方案
无PWM输出 中断未开启 检查EA/ET0使能位
占空比反向 比较逻辑错误 调换if(time<=gduty)条件
波形抖动 中断被抢占 关闭其他中断源
PWM小知识
硬件PWM vs 软件PWM:
本文为软件实现(灵活但占用CPU)
硬件PWM精度更高(如STM32的TIM模块)
电机控制推荐硬件PWM+死区发生器
LCD1602驱动
语言: C
简介: 支持4位/8位模式的字符液晶屏驱动
代码: LCD1602驱动
原理
💡 像操作智能信箱投递信件
RS引脚 = 信件类型选择(0=投递地址/1=投递内容)
RW引脚 = 读写方向(0=投递/1=取件)
E使能 = 投递确认按钮(下降沿触发处理)
数据总线 = 信件内容(4位/8位两种投递方式)
关键代码结构
// 写命令(设置信箱参数)
void lcd1602_write_cmd(u8 cmd) {
LCD1602_RS=0; // 选择设置模式(类似调整信箱投递规则)
LCD1602_E脉冲生成 // 按确认按钮提交设置
}
// 显示字符(投递信件)
void lcd1602_show_string(u8 x,u8 y,u8 *str) {
计算位置:0x80+y*0x40+x // 第1行0x80起,第2行0xC0起
逐字投递:while(*str!='\0') 发送每个字符
}
输入输出
-> ASCII码字符数据
-> 控制命令(显示模式/光标设置)
<- 无直接输出(通过液晶屏显示内容)
实现亮点
✅ 兼容4/8位模式灵活切换
✅ 自动处理显示地址换行
✅ 支持标准HD44780指令集
硬件连接示意图
单片机P0口 → LCD数据线(D0-D7)
单片机P2.0 → RS(寄存器选择)
单片机P2.1 → RW(读写选择)
单片机P2.2 → E(使能信号)
(🔧 VO引脚需接10K电位器调节对比度)
工作流程
初始化设置(设置数据位数/显示模式)
清屏准备显示
定位光标位置(行列坐标计算)
逐个字符写入显示RAM
自动换行处理(超出行宽转下一行)
性能参数
显示容量:16×2字符
字符尺寸:5×8点阵
响应时间:<1ms(指令执行)
工作电压:5V±0.5V
注意事项
⚠️ 必须遵守:
上电后需延时100ms再初始化
禁止带电插拔液晶屏
对比度电压需在0-5V可调
⚠️ 扩展注意:
低温环境需负压驱动(-3V~-5V)
强光环境需加背光
升级建议
// 示例:添加自定义字符
void create_char(u8 addr, u8 *pattern) {
lcd1602_write_cmd(0x40 | (addr<<3)); // CGRAM地址设置
for(int i=0;i<8;i++) lcd1602_write_data(pattern[i]);
}
故障排查表
现象 可能原因 解决方案
显示乱码 初始化顺序错误 严格按数据手册时序
仅显示方块 未正确设置4/8位模式 检查LCD1602_4OR8_DATA_INTERFACE宏定义
字符缺笔画 接触不良 检查排线连接
LCD小知识
字符型 vs 图形型:
本文驱动字符型(固定字库)
图形LCD需点阵控制(如12864)
OLED无需背光(但驱动协议不同)
LCD12864驱动
语言: C
简介: 并口驱动128x64点阵液晶显示模块
代码: LCD12864驱动
原理
💡 像操作智能电子画板
RS引脚 = 画笔模式选择(0=设置画布参数/1=绘制内容)
WR引脚 = 作画动作(0=落笔绘制/1=抬笔停绘)
E使能 = 画板确认键(每次操作需按压确认)
数据总线 = 颜料盒(每次传递8种颜色配置)
PSB引脚 = 通讯方式选择(1=快递寄送整箱颜料/0=分批寄送)
关键代码结构
// 设置画布参数(如纸张尺寸)
void lcd12864_write_cmd(u8 cmd) {
LCD12864_RS=0; // 选择参数设置模式
LCD12864_WR=0; // 进入写入状态
LCD12864_E脉冲生成 // 按压确认键提交参数
}
// 绘制具体图案
void lcd12864_show_string(u8 x,u8 y,u8 *str) {
switch(y) { // 四行画布定位:
case 0: x|=0x80; break; // 第一行画布起笔点
case 1: x|=0x90; break; // 第二行画布起笔点
case 2: x|=0x88; break; // 第三行画布起笔点
case 3: x|=0x98; break; // 第四行画布起笔点
}
while(*str) lcd12864_write_data(*str++); // 逐笔绘制
}
输入输出
-> 8位控制命令/显示数据
<- 无直接输出(通过液晶像素点呈现)
实现亮点
✅ 四行地址自动映射算法
✅ 严格遵循ST7920时序规范
✅ 支持中英文混合显示
硬件连接示意图
单片机P1口 → DB0-DB7(数据总线)
单片机P3.0 → RS(寄存器选择)
单片机P3.1 → WR(读写控制)
单片机P3.2 → E(使能信号)
单片机P3.3 → PSB(并/串选择)
(⚠️ V0引脚需接10K电位器调节对比度)
工作流程
设置并口模式(PSB=1)
初始化显示参数(开显示/关光标)
计算目标坐标地址(四行映射算法)
逐字节写入显示RAM
自动换行(超32字符自动下一行)
性能参数
分辨率:128×64像素(4行×16汉字)
字符尺寸:16×16点阵(中文)/8×16(英文)
响应速度:<300ns(E脉冲宽度)
工作电压:3.3V-5V(需与控制器电压匹配)
注意事项
⚠️ 必须遵守:
上电后等待≥40ms再初始化
禁止频繁快速清屏(间隔≥2ms)
背光电流需≤120mA(串联限流电阻)
⚠️ 扩展注意:
添加温度补偿(-20℃以下需加热膜)
强电磁环境需加磁珠滤波
升级建议
// 示例:添加图形显示功能
void draw_image(u8 *img) {
lcd12864_write_cmd(0x34); // 进入扩展指令集
for(int y=0; y<64; y++){
lcd12864_write_cmd(0x80 | y); // 行地址
lcd12864_write_cmd(0x80); // 列地址
for(int x=0;x<16;x++) lcd12864_write_data(img[y*16+x]);
}
lcd12864_write_cmd(0x36); // 开启图形显示
}
故障排查表
现象 可能原因 解决方案
半边显示 初始化未切扩展模式 补发0x36指令
显示反白 对比度过高 调节VO电压至0.5-1V
字符错位 地址计算错误 检查case语句偏移量
显示屏小知识
字符型 vs 图形型:
本文代码默认字符模式(需指令切换图形模式)
内置GB2312字库(约8K汉字)
灰度显示需PWM控制背光
74HC165并行转串行
语言: C
简介: 通过74HC165扩展IO读取8个独立按键状态
代码: 74HC165并行转串行
原理
💡 像工厂流水线安检扫描
SHLD引脚 = 行李传送带开关(0=放新行李到扫描区/1=停止上新)
CLK时钟 = X光机扫描节奏(每个脉冲扫描一件行李)
QH输出 = 安检结果输出(逐件传送扫描结果)
8位数据 = 8个独立包裹的安检状态(0=危险品/1=安全)
关键代码结构
// 安检流程控制
u8 hc165_read_data() {
HC165_SHLD=0; // 打开传送带放新行李
delay(安检准备时间);
HC165_SHLD=1; // 关闭传送带开始扫描
for(8次扫描){ // 逐个检查包裹
CLK=0; // 准备扫描仪
value |= 当前包裹状态; // 记录安检结果
CLK=1; // 完成当前扫描
}
return 危险品清单;
}
输入输出
-> 8路独立按键状态(K1-K8)
<- 8位LED状态(D1-D8)
实现亮点
✅ 硬件级时序精确控制
✅ 实时状态同步更新
✅ 最小化IO占用(仅用3个引脚读取8个按键)
硬件连接示意图
K1-K8 → 74HC165并行输入
74HC165_QH → P1.7(串行输出)
74HC165_CLK → P1.6(扫描时钟)
74HC165_SHLD → P1.5(装载控制)
P2口 → LED模块
(⚠️ 按键需接10K上拉电阻)
工作流程
拉低SHLD装载按键状态
拉高SHLD锁定当前状态
8个时钟脉冲逐位移出数据
实时更新LED显示状态
性能参数
扫描速度:约50μs/次(12MHz晶振)
最大延时:<1ms(按键响应时间)
抗干扰能力:需硬件滤波
注意事项
⚠️ 必须遵守:
按键未按下时保持高电平
CLK上升沿需保持稳定
避免长线传输(<30cm)
⚠️ 扩展注意:
添加硬件消抖电路(RC滤波)
多片级联时需串联QH输出
升级建议
// 示例:添加按键消抖
u8 stable_read() {
u8 val1 = hc165_read_data();
delay_ms(10);
u8 val2 = hc165_read_data();
return (val1 == val2) ? val1 : 0xFF;
}
故障排查表
现象 可能原因 解决方案
LED全亮 按键未接上拉 检查10K上拉电阻
响应延迟 CLK频率过低 减小delay_10us参数
状态错位 接线顺序错误 核对K1-D1对应关系
芯片小知识
74HC165 vs CD4021:
本文使用74HC165(高速CMOS版本)
CD4021适合更高电压场景(3-15V)
多片级联时时钟需同步
四线步进电机控制
语言: C
简介: 通过8拍节拍控制双极性步进电机运动
代码: 四线步进电机控制
原理
💡 像操作机械手臂爬楼梯
8拍节拍 = 8级台阶(每级对应不同线圈通电组合)
方向控制 = 上下楼选择(dir=1上行/dir=0下行)
速度调节 = 台阶停留时间(speed值=每级台阶停留时长)
TC1508S驱动 = 电梯动力系统(放大控制信号驱动电机)
关键代码结构
// 输出相位组合(类似电梯楼层按钮)
void step_motor_send_pulse(u8 step, u8 dir) {
if(dir==0) step=7-step; // 反向时倒序输出节拍
switch(step) { // 8种线圈通电组合:
case 0: A+相导通; break; // 台阶1
case 1: A+B+导通; break; // 台阶2
// ...其他相位组合
}
}
// 速度控制核心逻辑
void main() {
delay_ms(speed); // 延时决定转速
if(speed>1) speed-=1; // 加速(缩短台阶停留时间)
if(speed<5) speed+=1; // 减速(延长台阶停留时间)
}
输入输出
-> 3路按键输入(方向/加速/减速)
<- 4路相位驱动信号(IN_A~IN_D)
实现亮点
✅ 8拍模式平滑运行(半步效果)
✅ 实时方向切换
✅ 线性速度调节
硬件连接示意图
P1.0-P1.3 → TC1508S驱动芯片
TC1508S输出 → 步进电机两相绕组
P3.0-P3.3 → 独立按键
(⚠️ 驱动芯片必须接散热片)
工作流程
检测按键状态(每循环扫描一次)
方向键按下时翻转dir标志位
加速/减速键调整speed参数
按8拍顺序输出相位组合
根据speed参数延时控制转速
性能参数
步距角:0.9°(8拍模式,两相电机)
最大转速:约300RPM(speed=1时)
最小转速:约60RPM(speed=5时)
驱动电流:≥1A(需外接驱动芯片)
注意事项
⚠️ 必须遵守:
驱动电压需匹配电机额定电压
避免电机堵转(需加装机械限位)
连续运行需监测驱动芯片温度
⚠️ 扩展注意:
添加电流衰减模式(降低发热)
配置堵转检测保护电路
升级建议
// 示例:添加微步进控制
void micro_step(u8 step) {
// 使用PWM实现16细分微步
IN_A_PWM = sin_table[step];
IN_B_PWM = cos_table[step];
}
故障排查表
现象 可能原因 解决方案
电机振动不转 相位顺序错误 检查ABCD接线顺序
只能单向转 方向控制线故障 测量dir信号电平
驱动芯片发烫 电流过大 调整VREF电位器降低电流
步进电机小知识
两相 vs 五相步进电机:
本文驱动两相电机(结构简单/成本低)
五相电机扭矩更大(但驱动复杂)
闭环步进电机自带编码器反馈(精度更高)
RS485通信
语言: C
简介: 实现半双工RS485串口数据回传
代码: RS485通信
原理
💡 像使用对讲机对话
RS485_DIR引脚 = 对讲机PTT按键(1=说话模式/0=收听模式)
串口中断 = 对讲机提示灯(收到信息自动提醒)
半双工 = 同一时间只能单向通信(不能边说边听)
波特率9600 = 约定好的语速(每秒9600位)
关键代码结构
// 对讲机模式切换
void uart() interrupt 4 {
RS485_DIR=1; // 按下PTT准备说话
SBUF=接收的数据; // 复述刚听到的内容
while(!TI); // 等待说完最后一个字
RS485_DIR=0; // 松开PTT恢复收听
}
输入输出
-> PC串口发送的数据
<- 原样返回的数据
⚡ 方向控制:P1.0(收发切换)
实现亮点
✅ 自动收发切换机制
✅ 波特率加倍(PCON=0x80)提升通信距离
✅ 简洁的中断处理逻辑
硬件连接示意图
单片机P3.0 → MAX485 RO(接收输出)
单片机P3.1 → MAX485 DI(发送输入)
单片机P1.0 → MAX485 DE/RE(收发使能)
MAX485 A/B → 差分总线(需接120Ω终端电阻)
(⚠️ 总线需远离强电线路)
工作流程
初始化串口(设置9600bps)
默认设为接收模式(DIR=0)
收到数据触发中断
切换发送模式回传数据
恢复接收模式继续监听
性能参数
通信距离:≤1200米(@100Kbps)
节点容量:32设备(标准驱动芯片)
抗干扰度:±15kV静电保护(典型)
注意事项
⚠️ 必须遵守:
总线首尾端接120Ω匹配电阻
A/B线需双绞且不接地
逻辑地需单点连接
⚠️ 扩展注意:
添加TVS管防浪涌
隔离电源设计(防地环路)
升级建议
// 示例:添加MODBUS协议
void modbus_response() {
if(rec_data[0]==设备地址){
校验数据;
组织应答帧;
发送数据;
}
}
故障排查表
现象 可能原因 解决方案
数据乱码 波特率不匹配 检查双方波特率设置
只能收不能发 DIR控制线故障 用示波器检测P1.0信号
通信距离短 未接终端电阻 在总线末端补120Ω电阻
通信协议小知识
RS485 vs RS232:
本文采用RS485(差分信号抗干扰强)
RS232为全双工但传输距离短(<15m)
CAN总线更可靠(但成本更高)
评论 (0)