2021/05/01
踩坑
一开始安装驱动那里就出问题,驱动明明已经安装,但是识别不了单片机的接口,问客服,感觉客服也很迷,最后还是解决了。目测要么是USB线接触不良,要么是安装的WIN10驱动没起作用,因为后面还安装了一个XP驱动。
单片机相关概念
单片机是单芯片微型计算机的简称,常用英文缩写_MCU_(Microcontroller Unit)代指单片机。
我所使用的单片机为DIP封装,虽然体积较大,但是方便拆卸,从而损坏后更换芯片更便捷。
其他两种封装(PLCC,LQFP),虽然体积小,但是更换比较麻烦,。厂商为_STC_的,这家厂商单片机烧录程序便捷些。
厂商虽然不同,但是内核都是使用的Intel公司的80C51
系列内核,_STC_的单片机命名规则如下:
一些零散知识点(C语言)
由于C语言已经学过不少了,就选择性地记了些笔记:
常用存储单位关系:
中文名称 | 英文名称 | 换算关系 |
---|---|---|
比特(字位) | bit | 1B = 8bit |
字节 | byte | 1B = 8bit |
千字节 | kibibyte | 1KB = 1024B |
兆字节 | Mebibyte | 1MB = 1024KB |
吉字节 | Gigabyte | 1GB = 1024MB |
常说的100M
宽带,并非是100MB
而是100Mbit
,所以换算成下载速度需要除以8
,换算成字节单位,也就是12.5MB
。
数据类型 | 所占位数 | 范围 |
---|---|---|
bit | 1 | 0~1 |
unsigned char | 8 | 0~255 |
char | 8 | -128~127 |
unsigned int | 16 | 0~65535 |
int | 16 | -32768~32767 |
unsigned long | 32 | 0~429467295 |
float | 32 | 3.4e-38~3.4e38 |
double | 64 | 1.7e-308~1.7e308 |
其中,有符号位的最高位是符号位,0
表示正数,1
表示负数。
一些零散知识点(电子电路基础)
单片机是一种数字集成芯片,数字电路中只有两种电平:高电平和低电平。
高电平为5V
,低电平为0V
。
TTL电平信号被利用的较多,因为数字电路中通常数据表示采用二进制,5V
等价于逻辑1
,0V
等价于逻辑0
。TTL电平规定高电平输出电压>2.4V
,低电平输出电压<0.4V
,这是因为实际发送信号时,可能并不一定达到高电平和低电平的精确值,所以只要在这个波动范围内都是会被认定为高电平和低电平的。
而计算机串口使用的是RS232电平。
高电平为-12V
,低电平为+12V
,单片机与计算机串口通信时需要使用电平转换芯片,把RS232电平转换为TTL电平后,单片机才能识别。但是电路USB接口的供电口是5V
,充电宝也是,所以可以用充电宝和电脑USB接口来给单片机供电。
I/O口是基本输入Input/输出Output接口,单片机对外围设备的控制都是通过I/O口来进行的(输出高低电平)。接收外部控制也是通过I/O口来读取外部电压信号。
选电容时,一般选耐压值比应用系统高2-3倍的电容。
有极性的电容,长脚为正,短脚为负。
电容上颜色面积小的部分所对应的为负极,反之为正极(对于直插和贴片电解电容)。而对于钽电容则相反。
单片机需要运行起来最基本的条件为:
- 电源
- 单片机芯片
- 晶振电路
- 复位电路
单片机工作的基本时序:
振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期。
机器周期:一个机器包含12
个时钟周期。在一个机器周期内,CPU可以完成一个独立的操作。
电路原理图中:
电阻上的471
字样代表电阻阻值为47*10^1 Ω
电容上的105
字样代表电容大小为10*10^5 pF
网络标号相同的两点表示这两点实际电气相连(有导线连接)。
点亮LED灯
LED,即发光二极管。其优点是,功耗低,高亮度,色彩艳丽,寿命长。
同样的,LED灯也是长正短负。它的封装方式和电容电阻类似,开发板中用的就是最小的0603封装。贴片式的LED,有小绿点的一端为负极,另一端为正极。
普通发光二极管工作压降为1.6V~2.1V
。工作电流为1~20mA
。普通发光二极管导通压降通常为0.7V
。由于其工作电流较小,通常会再串一个限流电阻。
原理
由原理图可知,要想点亮LED灯,二极管需要处于导通状态,由于电源输入到阳极的信号恒为高电平,那么阴极为低电平才能导通,那么给阴极输入0
即可。
实践
踩坑:在建项目编译文件时,记得在output
里勾选输出hex
文件。
暴力点灯
直接分别对需要亮灯的位置赋值0。
#include <reg52.h>
sbit LED2 = P1^1;
sbit LED3 = P1^2;
sbit LED5 = P1^4;
void main()
{
LED2 = 0;
LED3 = 0;
LED5 = 0;
}
多口操作点灯
对P1
的8个口一起操作,由于操作时是按位操作,所以赋值的数是二进制数,二进制数位上的0和1就分别代表了低电平和高电平,但是写程序时一般会赋值16进制的数。
二进制高位到低位就对应了P1
的8个口从大到小的序号
#include <reg52.h>
void main()
{
P1 = 0xE9; //1110 1001
}
2021/05/02
实现LED灯闪烁
原理
由于程序中,main
函数是会自动循环的,所以如果不加延时,直接让LED灯点亮然后再熄灭的话,对于人来说是看不到闪烁的。所以,针对于人眼的视觉暂留效应,需要添加延时,使得LED灯亮和灭的时间间隔在人眼可见范围内。
而延时分为软件延时(占用CPU资源)和定时延时(使用寄存器,不浪费CPU资源),这里由于初学,采用的是实现较为简单的软件延时。
实践
实现一般闪烁
#include <reg52.h>
#define uint unsigned int
void delay() //简易延时函数
{
uint i = 65535;
while(i--);
}
void main() //main函数会自动循环
{
P1 = 0; //等价于十六进制的0x00,把P1的每一个口赋值成0,点亮8个LED灯
delay();
P1 = 0xFF; //等价于二进制1111 1111,把P1的每一个口赋值成1,熄灭8个LED灯
delay();
}
实现可控延时闪烁
#include <reg52.h>
#define uint unsigned int
void delay_ms(uint z) //毫秒级延时函数
{
uint x, y;
for(x = z; x > 0; x--)
for(y = 114; y > 0; y--) ; //114可以用单片机精灵算,为的是实现毫秒级延时
}
void main() //main函数会自动循环
{
P1 = 0; //等价于十六进制的0x00,把P1的每一个口赋值成0,点亮8个LED灯
delay_ms(100);
P1 = 0xFF; //等价于二进制1111 1111,把P1的每一个口赋值成1,熄灭8个LED灯
delay_ms(100);
}
实现流水灯闪烁
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
void delay_ms(uint z)
{
uint x, y;
for(x = z; x > 0; x--)
for(y = 114; y > 0; y--) ; //114可以用单片机精灵算,为的是实现毫秒级延时
}
uchar temp;
void main()
{
temp = 0xFE; //只让第一个LED点亮
P1 = temp;
delay_ms(500);
while(1)
{
temp = _crol_(temp, 1); //循环左移
P1 = temp;
delay_ms(500);
}
}
最终,尝试写了一个炫酷一点点的亮灯:
花式亮灯
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar temp;
uint i;
void delay_ms(uint z)
{
uint x, y;
for(x = z; x > 0; x--)
for(y = 114; y > 0; y--) ; //114可以用单片机精灵算出,为的是实现毫秒级延时
}
/*下面的点亮函数中,延时函数的参数设置为不同时长,看起来更有层次感*/
void left() //向左依次点亮一个灯
{
temp = 0xFE; //只让第1个LED点亮
P1 = temp;
delay_ms(100);
for(i = 0; i < 7; i++)
{
temp = _crol_(temp, 1); //循环左移
P1 = temp;
delay_ms(100);
}
P1 = 0xFF;
delay_ms(150);
}
void right() //向右依次点亮一个灯
{
temp = 0x7F; //只让第8个LED点亮
P1 = temp;
delay_ms(100);
for(i = 0; i < 7; i++)
{
temp = _cror_(temp, 1); //循环右移
P1 = temp;
delay_ms(100);
}
P1 = 0xFF;
delay_ms(150);
}
void side_to_mid() //从两边向中间依次点亮一个灯
{
P1 = 0x7E;
delay_ms(200);
P1 = 0xBD;
delay_ms(150);
P1 = 0xDB;
delay_ms(100);
P1 = 0XE7;
delay_ms(100);
P1 = 0xFF;
delay_ms(150);
}
void mid_to_side() //从中间向两边依次点亮一个灯
{
P1 = 0XE7;
delay_ms(200);
P1 = 0xDB;
delay_ms(150);
P1 = 0xBD;
delay_ms(100);
P1 = 0x7E;
delay_ms(100);
P1 = 0xFF;
delay_ms(150);
}
void all() //从两边向中间全部点亮
{
P1 = 0x7E;
delay_ms(200);
P1 = 0x3C;
delay_ms(150);
P1 = 0x18;
delay_ms(100);
P1 = 0X00;
delay_ms(150);
}
void main()
{
left();
right();
side_to_mid();
mid_to_side();
all();
while(1); //保持全亮状态
}
Keil中的调试工具
工具栏中一个像放大镜一样的debug功能,可以看IO口在执行完语句的变化。
在debug前记得先点击魔法棒图标,设置晶振频率,我所使用的开发板频率为11.0592MHz
。
先编译再进入debug模式,进入之后可以选择IO口监控状态。同时左边的窗口中有程序运行的时间等等信息,右下角窗口可以选择具体的变量进行监控。左上角可以选择调试模式。
蜂鸣器
原理
单片机是用于控制的,不适合驱动功率器件,所以并不是直接将IO口接到蜂鸣器上面,因为它的输出电流很小,而是用三极管作开关管,再加一个限流电阻,来控制蜂蜜器是否发声的。
直流电机不能直接接到开发板电源上,因为在其结束转动时有较高的反电动势,可能会损坏开发板。
实践
直接在流水灯的代码上稍加修改即可,蜂鸣器在P23口,如果直接赋值给beep一个逻辑0的话,就会使其发出连续不间断的蜂鸣声。
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar temp;
sbit beep = P2^3;
void delay(uint z)
{
uint x, y;
for(x = z; x > 0; x--)
for(y = 114; y > 0; y--);
}
void main()
{
temp = 0xF0;
P1 = temp;
delay(100);
while(1)
{
temp = _crol_(temp, 1);
P1 = temp;
beep = ~beep; //不断取反,使得其每间隔100ms发声
delay(100);
}
}
数码管
原理
数码管内部由8颗LED组成,想要显示什么样的字符,就控制想要显示的部分亮起,其他部分熄灭即可。一般分为两种,共阴极和共阳极。两者只在公共脚处不同,公共脚为VCC
的是共阳极,为GND
的是共阴极。可以用万用表连接公共脚和其他任意脚即可测出数码管类型。
数码管的公共脚叫做位选,其他脚叫做段选。位选用来选择数码管哪一位亮起,段选用来选择亮起数码管的哪些LED灯亮。
我所使用的开发板中,数码管为共阴极型的,由于它们的阴极连接的公共脚是接地,为低电平,那么给数码管输入高电平即可电亮LED管。
数码管显示分为静态显示和动态显示。这里先详细学习静态显示。
静态显示比较占IO口,因为每个数码管的段选都必须接一个8位数据线来保持显示的字形码,显示的字形可以一直保持,直到送入新的字形。
锁存器
可以把数据输入端与输出端进行隔离或连接。
以74HC573
为例:
输出口Q要想输出高低电平,OE脚必须接GND。
LE脚为高时,输出端Q随输入端D的数据而变化。LE脚为低时,输出端Q数据保持不变,输入端D数据变化不会改变Q的数据。
上拉电阻
将不确定的信号通过一个限流电阻(一般为10k-4.7k
欧姆的电阻)钳位在高电平,下拉同理,钳位在低电平。准双向IO
中有上拉电阻,那么它就既能够输出高电平又能够输出低电平,而漏极开路输出电路,由于没有上拉电阻,就只能输出低电平,输入高电平会让它处于开路状态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。