本系列博客以STC98552RC芯片为示例学习。
一.基本概念
1.单片机基础


2.CPU与内存交互


3.位运算


| 示例(操作 P2.1) | |
|---|---|
| 把某位置 1 | P2 |= (1 << 1); |
| 把某位清 0 | P2 &= ~(1 << 1); |
| 翻转某位 | P2 ^= (1 << 1); |
| 判断某位是否为1 | if (P2 & (1 << 1)) |
| 判断某位是否为0 | if (!(P2 & (1 << 1))) |
| 同时操作多位 |
同时置位 P2.0 和 P2.1:
同时清零 P2.0 和 P2.1:
|
| 定时器高低位赋值 | TL0 = num; TH0 = num >> 8; |
4.逻辑分析仪
-
共地(最重要)
逻辑分析仪的 GND 必须和单片机的 GND 连在一起,否则电平没有参考点,抓出来的波形全是乱码或噪声。 -
电压匹配
51 单片机一般是 5V 电平,大多数廉价逻辑分析仪支持 5V 输入(但有些只支持 3.3V)。接之前最好确认一下你的逻辑分析仪输入电压范围,避免烧坏。 -
不要接错电源
逻辑分析仪的通道输入是高阻抗的,只测信号,不会给单片机供电。千万别把 VCC 接到逻辑分析仪的通道上,只接信号线和 GND。
二.硬件详解

1.led
共阳极二极管,低电平导通

void led_init(void)
{
P2 = 0xFF;
}
void led_all_on(void)
{
P2 = 0;
}
void led_all_off(void)
{
P2 = 0xFF;
}
void led_nor(void)
{
P2 ^= 0xFF;
}
void led_on(unsigned int n)
{
P2 = ~n;
}
2.数码管,三极管
四个NPN共阴极三极管控制位选,P0寄存器引脚控制段选



// 共阴极段码表
unsigned char seg_table[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
//0-9
// 位选
void Select_Bit(unsigned int n)
{
// P1寄存器低四位清零
P1 &= ~(0x0F << 0);
// 选中第n位数码管
P1 |= (1 << n);
}
// 段选
void Select_Seg(unsigned int n)
{
P0 = seg_table[n];
}
// 数码管动态显示
void Digiter_show(unsigned int n) // 1234
{
int m = 0;
int t = 0;
while (n != 0)
{
m = n % 10;
P0 = 0;
Select_Bit(t++);
Select_Seg(m);
delay(100);
n /= 10;
}
}
3.按键

初始化:
P1 |= (0x0F << 4); // 把 P1.4、P1.5、P1.6、P1.7 置 1
P3 |= (1 << 5); // 把 P3.5 置 1
//硬件本应实现下拉电阻,将按键初始为高电平状态,但本芯片硬件无实现,故通过软件置1.
按键的“初始化”只是把引脚设为输入状态,之后引脚电平变化是硬件自动完成的,不需要再调用初始化函数。
| 状态 | 引脚电平 | 原因 |
|---|---|---|
| 按键松开 | 高电平(1) | 引脚内部有弱上拉(或外接上拉电阻),把电平拉到 VCC |
| 按键按下 | 低电平(0) | 按键把引脚直接短接到 GND,强行拉低 |
检测按键:
if ((P1 & (1 << 4)) == 0) // 检测到第一个按键被按下
{
ret = 1;
}
else if ((P1 & (1 <<5)) == 0)
{
ret = 2;
}
else if ((P1 & (1 << 6)) == 0)
{
ret = 3;
}
else if ((P1 & (1 << 7)) == 0)
{
ret = 4;
}
else if ((P3 & (1 << 5)) == 0)
{
ret = 5;
}
4.中断






以外部中断0为例:
// 外部中断0 中断服务函数
void int0_handler(void) interrupt 0
{
g_i++; //由于硬件存在一定误差,会出现中断触发不稳定,不会按照理想情况一次次触发
}
// 外部中断0初始化
void int0_init(void)
{
// 1. int0:P3_2引脚拉高
P3 |= (1 << 2);
// 2. 外部中断0子开关 + CPU中断开关
IE |= (1 << 0) | (1 << 7);
// 3. 外部中断0引脚中断触发方式:下降沿触发
TCON |= (1 << 0);
}
5.定时器





// 定时器0中断服务函数
void timer0_handle(void) interrupt 1
{
TL0 = g_x;
TH0 = g_x >> 8;
P2 ^= (1 << 1);
}
// 定时器0初始化
void timer0_init(void)
{
// 1. 配置定时器0模式:16位定时器
TMOD &= ~(0x0F << 0);
TMOD |= (1 << 0);
// 2. 设置定时器初值 11.0592MHZ:64626 12MHZ:64535
TL0 = g_x;
TH0 = g_x >> 8;
// 3. 开启定时器0,开始计数
TCON |= (1 << 4);
// 4. 开启定时器0中断 + CPU总中断
IE |= (1 << 1) | (1 <<7);
}
6.蜂鸣器

单片机引脚 P21(P2.1)通过电阻 R22 (1kΩ) 连到了 NPN 三极管 Q5 的基极(B)。
三极管 Q5 的发射极(E)直接接了 GND(地)。
跳线帽的作用,就是把 P21 和这个三极管基极“连在一起”。
为什么连在一起,P21 就是低电平了?(核心原理)
在 NPN 三极管内部,基极(B)和发射极(E)之间,相当于一个二极管(PN结)。
当单片机程序想把 P21 输出高电平(1),用来关掉 D2 灯时,电流会顺着 P21 → R22 → 三极管基极。
由于三极管的发射极是接地的,这个电流会直接通过基极-发射极这个“二极管”流向 GND。结果:这个 PN结会把 P21 的电压死死“钳位”在约 0.7V,而不是希望的 5V。
在数字电路里,0.7V 被判定为逻辑低电平(0)。所以,跳线帽插着的时候,P21 永远无法维持高电平,D2 灯(低电平点亮)就永远亮着。这个电路设计其实是“高电平驱动”的 NPN 蜂鸣器。也就是给 P21 高电平,蜂鸣器才响。但之前 D2 灯是“低电平亮”。这就导致两个功能完全冲突:灯亮意味着低电平,蜂鸣器不响;蜂鸣器响意味着高电平,灯灭。这就是硬件复用的坑,所以只能用跳线帽物理隔离。

蜂鸣器发声原理:根据发声频率与单片机工作频率计算定时器初值,发声频率越高,单次发声间隔越短,定时器初值越高(越接近溢出值),中断执行越频繁,蜂鸣器发声频率也就变高,实现了不同频率发声。
无源蜂鸣器一个震荡发声一次:
每次定时器中断 → 引脚电平翻转一次
两次中断 → 电平翻转两次 (一个完整周期)。
每秒发生多少个完整周期 → 就是发声频率(Hz)。
#define HZ_200 63035
#define HZ_400 64285
#define HZ_600 64702
#define HZ_800 64910
#define HZ_1000 65035
| 蜂鸣器类型 | P2.1 持续高电平的结果 | 原因 |
|---|---|---|
| 有源蜂鸣器 | ✅ 一直响 | 内部自带振荡电路,只要通电就发声 |
| 无源蜂鸣器 | ❌ 不响(或只“嗒”一声) | 内部没有振荡电路,必须靠外部不断翻转电平(方波)驱动 |
// 定时器0中断服务函数
void timer0_handle(void) interrupt 1
{
TL0 = g_x;
TH0 = g_x >> 8;
P2 ^= (1 << 1);
}//P2.1引脚连接蜂鸣器与led的D2灯,通过不断对P2电平翻转实现蜂鸣器工作

1718

被折叠的 条评论
为什么被折叠?



