
STC8H8K64U单片机-点灯
学前准备
下载keil5开发工具:https://software.share888.top/note/devtool/detail/keil.html
下载stc-isp 烧录工具:https://software.share888.top/note/devtool/detail/stc-isp.html
我用的板子是打狗棒的板子,淘宝20元左右。教程如下:https://www.stcai.com/hxgnsyb
创建第一个点灯工程
打开keil软件,新建一个keil项目,选择合适的目录


点击下拉框中,如果出现STC MCU Database,说明环境导入成功

可通过搜索框搜索到stc相关芯片信息

在源码目录,右键打开操作面板,选择Add New Item to Group ...

新建main.c文件。根据面板提示,选择C File,确定好文件名称,当前的文件名称为main。

Add完成后,在源码目录中会多一个 main.c文件
在 main.c中编写代码,实现main函数. 将下面的代码烧录到单片机就行了。正常灯就会亮
c
#include <STC8H.H> // 1. 包含头文件,告诉编译器这是 STC8H 系列芯片
void main()
{
// 1. 先配置模式 (强推挽)
P2M0 = 0xFF; P2M1 = 0x00;
// 3. 控制灯的状态
// 大多数开发板是“低电平点亮” (0 = 亮, 1 = 灭)
// 如果这句代码灯没亮,试着改成 LED = 1;
P20 = 0 ;
// 4. 死循环,让程序停在这里,保持灯一直亮着
while(1)
{
// 什么都不做,就让它亮着
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
让灯闪烁
很简单,直接上代码
c
#include <STC8H.H>
#include <intrins.h> // 用于 _nop_()
/**
* @brief 精准延时 1 毫秒 (基于 24MHz 1T 架构)
* 适用于 STC8H, STC8Y, STC8A 等系列
*/
void Delay1ms() //@24.000MHz
{
unsigned char i, j;
_nop_(); // 微调,对齐周期
i = 24; // 这些系数是专为 24MHz 计算的
j = 169;
do
{
while (--j);
} while (--i);
}
/**
* @brief 通用毫秒延时
* @param ms 延时毫秒数
*/
void Delay_ms(unsigned int ms)
{
unsigned int k;
for(k = 0; k < ms; k++)
{
Delay1ms();
}
}
void main()
{
// 配置强推挽
P2M0 = 0xFF;
P2M1 = 0x00;
while(1)
{
P2 = 0xFE; // P2.0 亮
Delay_ms(1000); // 延时 1 秒
P2 = 0xFF; // P2.0 灭
Delay_ms(1000); // 延时 1 秒
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
流水灯效果
c
#include <STC8H.H>
#include <intrins.h> // 用于 _nop_()
// ==========================================
// 精准延时函数 (适配 24MHz)
// ==========================================
void Delay1ms() //@24.000MHz
{
unsigned char i, j;
_nop_();
i = 24;
j = 169;
do
{
while (--j);
} while (--i);
}
void Delay_ms(unsigned int ms)
{
unsigned int k;
for(k = 0; k < ms; k++)
{
Delay1ms();
}
}
// ==========================================
// 主函数
// ==========================================
void main()
{
// 1. 配置 P2 口为 强推挽输出 (让灯更亮,驱动能力更强)
// 0xFF = 1111 1111 (所有引脚都设为强推挽)
P2M0 = 0xFF; // 1111 1111
P2M1 = 0x00; // 0000 0000
while(1)
{
// --- 第1步:点亮第1个灯 (最右边/最低位) ---
// 二进制: 1111 1110 (只有最后一位是0,灯亮)
// 十六进制: 0xFE
P2 = 0xFE; // 1111 1110
Delay_ms(500); // 延时 0.5 秒
// --- 第2步:点亮第2个灯 ---
// 二进制: 1111 1101 (倒数第二位是0,灯亮)
// 十六进制: 0xFD
P2 = 0xFD; // 1111 1101
Delay_ms(500);
// --- 第3步:点亮第3个灯 ---
// 二进制: 1111 1011
// 十六进制: 0xFB
P2 = 0xFB; // 1111 1011
Delay_ms(500);
// --- 第4步:点亮第4个灯 ---
// 二进制: 1111 0111
// 十六进制: 0xF7
P2 = 0xF7; // 1111 0111
Delay_ms(500);
// --- 第5步:点亮第5个灯 ---
// 二进制: 1110 1111
// 十六进制: 0xEF
P2 = 0xEF; // 1110 1111
Delay_ms(500);
// --- 第6步:点亮第6个灯 ---
// 二进制: 1101 1111
// 十六进制: 0xDF
P2 = 0xDF; // 1101 1111
Delay_ms(500);
// --- 第7步:点亮第7个灯 ---
// 二进制: 1011 1111
// 十六进制: 0xBF
P2 = 0xBF; // 1011 1111
Delay_ms(500);
// --- 第8步:点亮第8个灯 (最左边/最高位) ---
// 二进制: 0111 1111
// 十六进制: 0x7F
P2 = 0x7F; // 0111 1111
Delay_ms(500);
// (可选) 全灭停顿一下,效果更清晰
P2 = 0xFF; // 1111 1111 (全灭)
Delay_ms(200);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
按键点灯
也很简单,重点是知道那个是按键的地址
c
#include <STC8H.H> // 这个文件里包含了所有寄存器的绝对地址
// 定义按键的“位地址”
// 语法:sbit 变量名 = 寄存器名 ^ 位数;
sbit KEY_S4 = P3^3;
void main()
{
// 1. 配置 P1 口为 强推挽输出 (让灯更亮,驱动能力更强)
// 0xFF = 1111 1111 (所有引脚都设为强推挽)
P1M0 = 0xFF; // 1111 1111
P1M1 = 0x00; // 0000 0000
while(1)
{
// 现在你可以像使用变量一样使用它
if (KEY_S4 == 0)
{
// 按键按下了
P10 = 0;
}else{
P10 = 1;
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
问题
sbit和sfr 的作用
在 STC8(以及所有基于 8051 内核的单片机,如 STC89, STC12, STC15 等)的 C 语言开发环境(通常是 Keil C51)中,sfr 和 sbit 确实是独有的扩展关键字
简单来说,它们是用来给硬件寄存器起别名的,让你能用人类可读的名字去控制硬件,而不是去记复杂的十六进制地址
sfr (Special Function Register)
- 含义:特殊功能寄存器。
- 作用:用来定义一个**8 位(1 字节)**的硬件寄存器。
- 场景:当你需要操作整个端口(如 P1 口所有引脚)、配置定时器模式、设置串口波特率时,你操作的是一个完整的 8 位寄存器。
- 例子:c
// 告诉编译器:名字叫 P1 的变量,对应的是地址为 0x90 的硬件寄存器 sfr P1 = 0x90; // 现在你可以像操作普通变量一样操作它 P1 = 0xFF; // 把 P1 口 8 个引脚全部设为高电平1
2
3
4
5
sbit (Special Bit)
- 含义:特殊功能寄存器的位。
- 作用:用来定义某个 sfr 寄存器中的某一位(1 个 bit)。
- 场景:当你只需要控制某一个具体的引脚(如 P1.0 接的 LED),或者只关心某个状态标志位(如溢出标志 OV)时。
- 例子:c
// 方法一:基于已定义的 sfr 来定义位 (推荐) // 意思是:LED 这个名字,代表 P1 寄存器的第 0 位 sbit LED = P1^0; // 方法二:直接指定绝对地址 (较少用,需查手册) // P1.0 的位地址通常是 0x90 sbit LED = 0x90; // 使用 LED = 0; // 只把 P1.0 拉低,其他引脚不变1
2
3
4
5
6
7
8
9
10
sfr 和 sbit 的核心区别
| 特性 | sfr | sbit |
|---|---|---|
| 操作对象 | 整个字节 (8 位) | 单个位 (1 位) |
| 数据宽度 | 8 bit (0 ~ 255) | 1 bit (0 或 1) |
| 典型用途 | 一次性控制整个端口、设置寄存器配置值 | 控制单个 GPIO 引脚、读取单个状态标志 |
| 赋值范围 | 0x00 到 0xFF | 0 或 1 (真/假) |
| 定义依赖 | 独立定义,直接对应物理地址 | 通常依赖于 sfr 定义,或者直接指定位地址 |
| 代码示例 | P1 = 0x0F; (改变 8 个引脚) | LED = 1; (只改变 1 个引脚) |
| 底层原理 | 映射到 SFR 区的一个字节地址 | 映射到 SFR 区的一个位地址 (51 单片机特有的位寻址功能) |

