51知识点笔记总结

liuxiaobai
2025-04-10 / 0 评论 / 1 阅读 / 正在检测是否收录...

流水灯

  1. 核心功能

实现P2端口连接的8个LED流水灯效果:先左移循环点亮,再右移循环熄灭。

  1. 代码结构

硬件定义

#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. 流水灯实现方法

方法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);

  1. 关键点

低电平驱动:~0x01取反使P2.0输出低电平,点亮LED。

延时控制:delay_10us(50000)约500ms(12MHz晶振)。

循环移位函数:

_crol_(val, n):将val循环左移n位。

_cror_(val, n):将val循环右移n位。

  1. 硬件连接

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→A‌13。
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

评论 (0)

取消