3

C51单片机学习日志

 3 years ago
source link: https://segmentfault.com/a/1190000039944104
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2021/05/01

一开始安装驱动那里就出问题,驱动明明已经安装,但是识别不了单片机的接口,问客服,感觉客服也很迷,最后还是解决了。目测要么是USB线接触不良,要么是安装的WIN10驱动没起作用,因为后面还安装了一个XP驱动。

单片机相关概念

单片机是单芯片微型计算机的简称,常用英文缩写_MCU_(Microcontroller Unit)代指单片机。
我所使用的单片机为DIP封装,虽然体积较大,但是方便拆卸,从而损坏后更换芯片更便捷。
其他两种封装(PLCC,LQFP),虽然体积小,但是更换比较麻烦,。厂商为_STC_的,这家厂商单片机烧录程序便捷些。
厂商虽然不同,但是内核都是使用的Intel公司的80C51系列内核,_STC_的单片机命名规则如下:

一些零散知识点(C语言)

由于C语言已经学过不少了,就选择性地记了些笔记:
常用存储单位关系:

中文名称英文名称换算关系比特(字位)bit1B = 8bit字节byte1B = 8bit千字节kibibyte1KB = 1024B兆字节Mebibyte1MB = 1024KB吉字节Gigabyte1GB = 1024MB

常说的100M宽带,并非是100MB而是100Mbit,所以换算成下载速度需要除以8,换算成字节单位,也就是12.5MB

数据类型所占位数范围bit10~1unsigned char80~255char8-128~127unsigned int160~65535int16-32768~32767unsigned long320~429467295float323.4e-38~3.4e38double641.7e-308~1.7e308

其中,有符号位的最高位是符号位,0表示正数,1表示负数。

一些零散知识点(电子电路基础)

单片机是一种数字集成芯片,数字电路中只有两种电平:高电平和低电平。
高电平为5V,低电平为0V
TTL电平信号被利用的较多,因为数字电路中通常数据表示采用二进制,5V等价于逻辑10V等价于逻辑0。TTL电平规定高电平输出电压>2.4V,低电平输出电压<0.4V,这是因为实际发送信号时,可能并不一定达到高电平和低电平的精确值,所以只要在这个波动范围内都是会被认定为高电平和低电平的。
而计算机串口使用的是RS232电平。
高电平为-12V,低电平为+12V,单片机与计算机串口通信时需要使用电平转换芯片,把RS232电平转换为TTL电平后,单片机才能识别。但是电路USB接口的供电口是5V,充电宝也是,所以可以用充电宝和电脑USB接口来给单片机供电。
I/O口是基本输入Input/输出Output接口,单片机对外围设备的控制都是通过I/O口来进行的(输出高低电平)。接收外部控制也是通过I/O口来读取外部电压信号。
选电容时,一般选耐压值比应用系统高2-3倍的电容。
有极性的电容,长脚为正,短脚为负。
电容上颜色面积小的部分所对应的为负极,反之为正极(对于直插和贴片电解电容)。而对于钽电容则相反。
单片机需要运行起来最基本的条件为:

  1. 单片机芯片
  2. 复位电路
    单片机工作的基本时序:
    振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期。
    机器周期:一个机器包含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中有上拉电阻,那么它就既能够输出高电平又能够输出低电平,而漏极开路输出电路,由于没有上拉电阻,就只能输出低电平,输入高电平会让它处于开路状态。

2021/05/03


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK