【笔记】C51程序学习笔记
前言
Windows上标准51单片机的C51学习笔记
注意:本文所有代码均按照sdcc编译器代码规范编写,如果使用的Keli编译器,需要自行修改代码
存储空间的访问
单片机有两个存储器,分别是:只读存储器ROM、随机存储器RAM
- ROM:通常用于存程序(code)
- RAM:通常用于存数据(data)
51单片机存储器的空间
- 标准51单片机
- ROM空间为4KB
- RAM空间为128B
- STC12C5A60S2单片机
- ROM空间(4KB)+外扩内存(56KB)总共为60KB
- RAM空间为1280B
- 标准51单片机
C语言中的变量值将会默认存入RAM
1 | int a = 0; |
- 因为RAM容量较小,所以如果有大量数据需要存储(例如数组),可以通过
code
关键字强制将变量的值存入ROM
1 | __code int a = 0; |
- 如果使用的是增强型51单片机(例如STC12C5A60S2单片机),可以通过
xdata
关键字强制将变量的值存入外扩内存
1 | __xdata int a = 0; |
操作2进制位
- 在操作2进制位时,如果需要改变2进制位,通常不使用算术运算符,通常使用按位运算符
指定位为0
- 任意位与0相与,结果不变
- 任意位与1相与,结果为0
举例:将末位置0
1 | num = num & 0b00000001; |
指定位为1
- 任意位与0相或,结果不变
- 任意位与1相或,结果为1
举例:将末位置1
1 | num = num | 0b00000001; |
指定位取反
- 任意位与0异或,结果不变
- 任意位与1异或,结果取反
举例:将末位置取反
1 | num = num ^ 0b00000001; |
固定格式
1 |
|
引入头文件
1 |
为引脚赋值
- I/O端口都是可以位寻址的,所以既可以按字节赋值,也可以按位赋值
按字节赋值
- 将P0的所有引脚都通电(
00000000
)
1 | P0 = 0x00; |
- 将P0的所有引脚都断电(
11111111
)
1 | P0 = 0xFF; |
按位赋值
- 将P0的第1个引脚通电(
11111110
)
1 | P0_0 = 0; |
- 将P0的第1个引脚断电(
11111111
)
1 | P0_0 = 1; |
循环语句提升效率
- 循环要以递减的方式迭代。在编译成汇编代码后,递减的方式迭代要比递增的方式迭代执行效率更高
1 | int i = 100; |
1 | for (int i = 100; i > 0; i--) |
发光二极管
- 发光二极管是一个输出外设
拉电流和灌电流
拉电流
- 拉电流时
- 输入为1时,发光二极管亮灯
- 输入为0时,发光二极管不亮灯
- 拉电流为200微安
1 | 单片机引脚 <- 发光二极管正级 |
灌电流
- 灌电流时
- 输入为0时,发光二极管亮灯
- 输入为1时,发光二极管不亮灯
- 灌电流为20毫安,所以通常使用灌电流,才能让发光二极管达到正常亮度
1 | 单片机引脚 <- 限流电阻 <- 发光二极管负级 |
软件延迟函数
- 通过600~700次的空循环,可以实现延迟1ms左右
1 | void delay(unsigned int ms) |
通过延迟实现闪烁
- 频率越高闪烁越快
- 每1s内,实现1次亮和1次灭,这样的频率为1Hz
- 因为人眼有余晖时间,所以人眼可识别的最大闪烁频率为50Hz
1 |
|
通过调整占空比改变亮度
- 亮度的决定因素:发光二极管闪烁时,亮的时间越大,灭的时间越小,亮度越亮
- 占空比:灭的时间 / 亮的时间 + 灭的时间
- 当亮和灭的时间间隔发生改变时,占空比就会改变,发光二极管亮度就会改变
- 50Hz在1秒内的的两次间隔之和为20ms,所以能产生19级亮度
- 当亮1ms,灭19ms,亮度最暗
- 当亮19ms,灭1ms,亮度最亮
1 |
|
STC12C5A60S2的特殊功能寄存器
引入头文件
1 |
切换端口工作模式
- 以修改P0端口的第0个位置为例
工作模式
工作模式 | M1寄存器的值 | M0寄存器的值 | 备注 |
---|---|---|---|
标准双向口模式(缺省值) | 0 | 0 | 灌电流为20毫安,拉电流为200微安 |
强推挽输出模式(强推模式) | 0 | 1 | 灌电流为20毫安,拉电流提升为20毫安 |
开漏模式 | 1 | 0 | - |
高阻模式 | 1 | 1 | - |
设置方法
- 每个位可以单独设置工作模式,由2个寄存器对应位的值决定
1 | P0M1 = 0x00; |
按键
按键是一个输入外设
按键按下时,输入为0
按键没有按下时,输入为1
软件消抖
- 按键按下和抬起时,会有抖动现象产生,抖动现象通常会在按下和抬起的10ms~20ms之间发生
- 软件消抖通过利用软件延迟函数实现
- 软件消抖的效果
- 按下消抖效果:按下时进入循环,此时通过延迟10ms,解决10ms之内按下瞬间出现反复按的情况。
- 抬起消抖效果:如果按住并且一直没抬起,此时会进入无限循环,也就是不会做处理。抬起时通过延迟10ms,解决10ms之内按下瞬间出现反复按的情况
按下后执行业务
P0_0
:按键的连接点
1 |
|
抬起后执行业务
P0_0
:按键的连接点
1 |
|
按键检测函数
- 为每一个按键按下赋予键值,在主函数中通过判断减值,实现指定业务
1 | #include<8052.h> |
七段数码管
数码管的引脚一般都在上下,共阴和共阳是由内部决定的
共阳数码管(灌电流)点亮:位信号(com端)接高电平,段信号(a、b、c、d、e、f、g、dp端)接低电平
- 本案例接线方式
- 位信号1接P0.3、…、位信号4接P0.0
- 段信号a接P1.0、…、段信号g接P1.6、段信号dep接P1.7
- 本案例接线方式
共阴数码管(拉电流)点亮:位信号(com端)接低电平,段信号(a、b、c、d、e、f、g、dp端)接高电平
不应该将位信号同时点亮,如果需要全部点亮每个位,可以采用动态刷新技术
不同位数的七段数码管
- 1位七段数码管一共有10个引脚
- 2个com端(这两个引脚内部是想通的)
- a(上)、b(右上)、c(右下)、d(下)、e(左下)、f(左上)、g(中)、dp(小数点)端
- 2位七段数码管一共有10个引脚
- com1端、com2端
- a(上)、b(右上)、c(右下)、d(下)、e(左下)、f(左上)、g(中)、dp(小数点)端
- N位七段数码管一共有
(8+N)
个引脚- com1端、…、comN端
- a(上)、b(右上)、c(右下)、d(下)、e(左下)、f(左上)、g(中)、dp(小数点)端
- 1位七段数码管一共有10个引脚
全部点亮
- 通过动态扫描(刷新),实现全部点亮检查坏点:通过快速切换不同位上的数字,实现现实多个位上的数字扫描
- 因为人眼能识别的最大闪烁频率为50Hz,所以为了骗过人眼,需要将所有数字在20ms内完成闪烁
- 七段码:每个数字位用于显示数字的16进制值,16进制数的每一位决定哪一个笔划亮灯
- 从com端到a~dep端如果是灌电流,则com端输出0时,数码管指定位亮灯
- 选位:选位阶段需要指定哪个位亮
- 送段:送出段信号阶段需要指定亮哪个数字
- 延迟:如果是4位数码管,每个位的延迟应设置为5ms
- 灭段:为了解决拖影(拖尾)现象,将亮灯的数字熄灭
1 |
|
封装为函数
1 | void display(unsigned char *arr) |
点阵屏
8(列)*8(行)的点阵屏的操作逻辑与8位数码管理论上相同
分辨率:8(列)*8(行)
像素:64
点阵屏的引脚是由方向决定的,共阴和共阳也是由方向决定的
共阳(灌电流)接线方式(引脚在左右)点亮:列信号接高电平(高选列),行信号接低电平(低送行)
共阴(拉电流)接线方式(引脚在上下)点亮:列信号接低电平(低选列),行信号接高电平(高送行)
- 本案例接线方式(单屏)
- 列信号1接P0.0、…、位信号8接P0.7
- 行信号1接P1.0、…、段信号8接P1.7
- 本案例接线方式(多屏)
- 列信号1接P0.0、…、位信号8接P0.7
- 列信号9接P2.0、…、位信号16接P2.7
- 行信号1接P1.0、…、段信号8接P1.7
- 本案例接线方式(单屏)
不应该将列信号同时点亮,如果需要全部点亮每个列,可以采用动态刷新技术
取模:将文字转化成点阵
- 汉字的字模需要最少12*12的分辨率才能正常显示
切屏:每次刷新8列
滚屏:每次刷新1列
单屏
全部点亮
- 通过动态扫描(刷新)全部点亮,检查是否坏点
1 |
|
双屏
全部点亮
1 |
|
蜂鸣器(Buzz)
有源蜂鸣器:内置了震荡源芯片的蜂鸣器,接上电点就能响,但是不能改变振动频率所以只能发出一种音调
无源蜂鸣器:没有内置震荡源芯片的蜂鸣器,接上电后,需要给脉冲信号才能响,可以通过改变震动频发出不同的音调
拉电流和灌电流都可以使蜂鸣器发声
- 本案例接线方式
- 灌电流。蜂鸣器一端接P1.0
- 本案例接线方式
因为人耳识别声音的范围为5Hz~20kHz,所以延迟最大为100ms
软件延迟实现蜂鸣器5Hz声音
1 |
|
定时/计数器实现蜂鸣器500Hz声音
1 |
|
定时/计数器实现蜂鸣器指定频率声音
<frequency>
:频率
1 |
|
定时/计数器实现蜂鸣器音阶
- 全音符的音长大约在1600ms
- 2分音符的音长大约在800ms
- 4分音符的音长大约在400ms(~600ms)
- 8分音符的音长大约在200ms
- 16分音符的音长大约在100ms
- 32分音符的音长大约在50ms
1 |
|
C大调音符频率对照表
- 中音的频率约定于2倍的低音
- 高音的频率约定于2倍的中音
- 以此类推
序号 | 音符 | 频率 |
---|---|---|
1 | 低音1 | 262 |
2 | 低音2 | 294 |
3 | 低音3 | 330 |
4 | 低音4 | 349 |
5 | 低音5 | 392 |
6 | 低音6 | 440 |
7 | 低音7 | 494 |
8 | 中音1 | 523 |
9 | 中音2 | 587 |
10 | 中音3 | 659 |
11 | 中音4 | 698 |
12 | 中音5 | 784 |
13 | 中音6 | 880 |
14 | 中音7 | 988 |
15 | 高音1 | 1046 |
16 | 高音2 | 1175 |
17 | 高音3 | 1318 |
18 | 高音4 | 1397 |
19 | 高音5 | 1568 |
20 | 高音6 | 1760 |
21 | 高音7 | 1976 |