51单片机学习笔记
单片机
【所使用单片机为STC89C52RC单片机】
本人单片机图片:
单片机介绍
单片机,英文Micro Controller Unit,简称MCU。
内部集成了CPU,RAM,ROM,定时器,中断系统,通讯接口等一系列电脑的常用硬件功能。
单片机的任务是信息采集(依靠传感器),处理(依靠CPU)和硬件设备(例如电机,LED等)的控制。
所属系列为8051,即51单片机。
8051最开始指在80年代生产的8051内核的单片机。后延伸为只要是8051内核的单片机,都统称为51单片机。
单片机命名规则:
单片机内部结构
结构图:
管脚图:
原理图:
VCC是正极(高电平),GND是负极(低电平)
【原理图很重要,对学习新外设很有帮助】
软件
1、Keil:可以创建项目编写程序
2、stc-isp:烧录(下载)程序的软件
一、LED
点亮LED
LED:发光二极管
从上面的原理图可以查到LED原理图
电阻的作用:限流,防止电流过大烧毁LED
从图中可以看到,LED左端连的VCC(高电平,即1),右端是P2口,为了有电流通过从而点亮LED,因此P2口应为低电平,即0。(感觉P2类似地址)
则根据原理图,要使第一个LED灯亮就需要P2_0为0,其他为1,因此可以写出下面代码:
#include <REGX52.H>
void main(){
P2 = 0xFE; //1111 1110
while(1); //死循环,以免程序反复运行
}
【<REGX52.H>是一个头文件,里面定义了P2,所以必须要有,可以右键快速添加】
LED周期闪烁
周期,就要有延时函数。
可以借助stc-isp的软件延时功能生成延时函数。
因此,就可以写出使LED灯周期闪烁的代码:
#include <REGX52.H>
#include <INTRINS.H> //_nop_函数的头文件
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main(){
while(1){
P2 = 0xFE;//1111 1110
Delay500ms();
P2 = 0xFD;//1111 1101
Delay500ms();
}
}
LED流水灯
流水灯就是使八个LED灯依次亮起。
经过上面两个,运用学c的知识,自己就可以写出一个流水灯代码了。
#include <REGX52.H>
#include <INTRINS.H>
void Delay1ms(unsigned int xms) //@11.0592MHz
{
while(xms--)
{
unsigned char i, j;
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
while(1)
{
P2 = 0xFE;//1111 1110
Delay1ms(200);
P2 = 0xFD;//1111 1101
Delay1ms(200);
P2 = 0xFB;//1111 1011
Delay1ms(200);
P2 = 0xF7;//1111 0111
Delay1ms(200);
P2 = 0xEF;//1110 1111
Delay1ms(200);
P2 = 0xDF;//1101 1111
Delay1ms(200);
P2 = 0xBF;//1011 1111
Delay1ms(200);
P2 = 0x7F;//0111 1111
Delay1ms(200);
}
}
或者使用位运算符,将主函数简化:
void main()
{
P2 = 0xFE;
while(1)
{
Delay1ms(200);
P2 = (P2<<1) + 1;
if(P2 == 0xFF){
P2 = 0xFE;
}
}
}
二、独立按键
1、先看一下独立按键的原理图
从图中可以知道,独立按键是P3口控制的。
再者就是,右上方都接了GND,低电平,即0,那么如果按键按下的话,对应的P3接口就是0。
因此,可以通过判断P3接口是不是0,从而判断出按键有没有被按下,哪个按键被按下了。
注意:第一个独立按键是P3_1,而第二个是P3_0。
2、按键消抖:
对于机械开关,当机械触点断开、闭合时,由于机械出点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动,如图所示
因此我们需要用延时函数消磨掉这段时间,从而消除这种抖动。
运用综上所述的两点,我们就可以写出以下程序
实操一:独立按键控制LED闪灭
#include <REGX52.H>
void Delay(unsigned int x) //@11.0592MHz
{
while(x--)
{
unsigned char i, j;
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void main()
{
while(1)
{
if(P3_1==0){
Delay(20);
while(P3_1==0);
Delay(20); //消抖
P2_0 = ~P2_0; //~各位取反
}
}
}
实操二:独立按键控制LED显示二进制加减,清零
#include <REGX52.H>
void Delay(unsigned int x) //@11.0592MHz
{
unsigned char i, j;
while(x--)
{
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
}
void main()
{
P2 = 0xFF;
while(1){
if(P3_1 == 0)
{
Delay(20);
while(P3_1 == 0);
Delay(20);
P2--;
}
if(P3_0 == 0){
Delay(20);
while(P3_0 == 0);
Delay(20);
P2++;
}
if(P3_2 == 0){
Delay(20);
while(P3_2 == 0);
Delay(20);
P2 = 0xFF;
}
}
}
实操三:独立按键控制LED灯移位
#include <REGX52.H>
void Delay(unsigned int x) //@11.0592MHz
{
while(x--)
{
unsigned char i, j;
i = 11;
j = 191;
do
{
while (--j);
} while (--i);
}
}
void main()
{
P2 = 0xFE;//1111 1110
while(1)
{
if(P3_1 == 0)
{
Delay(20);
while(P3_1 == 0);
Delay(20);
P2 = (P2<<1)+1;
if(P2 == 0xFF)
{
P2_0 = 0;
}
}
if(P3_0 == 0)
{
Delay(20);
while(P3_0 == 0);
Delay(20);
P2 = (P2>>1) + 0x80;
if(P2 == 0xFF)
{
P2_7 = 0;
}
}
}
}
三、数码管
数码管介绍
LED数码管:数码管是由多个发光二极管封装在一起组成“8”字型的器件。
单个数码管引脚定义
数码管的接法,有共阳和共阴之分。共阴时,拉高电压即可点亮。共阳时,拉低电平点亮。
【右侧的上图是共阴极连接,下图是共阳极连接。】
以共阴极连接为例(所用单片机就是共阴极),共用的3,8一端就叫做位选(位:四位一体的数码管的位数),另一端叫做段选。
如果要显示“6”,则A,C,D,E,F,G要亮,即段选端要为1,而B,DP要为0。
因此,段码就为1011 1110
四位一体数码管引脚定义
举一反三:如果是共阴极连接(右上图),想要第三位显示“1”的话,那么就要位码为1101,段码为0110 0000。
数码管显示原理图
74HC138译码器:
控制哪一位的数码管亮起,且将原本需要8个I/O输入口减少成了三个P2口,且需为低电平。
C为高位,A为低位,且有效端口为低电平。
例:若P2_4=0, P2_3=0, P2_2=0, 则有效端为Y0,且只有Y0为低电平,即0.
74HC245(位于下图):
双向数据缓冲器。因为单片机输出能力有限,需要该芯片提高输入能力。
从图中可以看到,数码管的段码(若是共阴极,段码也称阳码)连的是P0输入端口。
因此,显示数码管步骤应为:
①赋值P2_3,P2_3,P2_4控制位数;
②赋值P0,控制显示;
注意:P0_7为高位,高位对高位,注意二进制数字顺序
静态数码管显示
由上面所学知识,我们就可以编写代码显示数码管了
#include <REGX52.H>
void main(){
P2_4 = 1;
P2_3 = 1;
P2_2 = 1;
P0 = 0x7D;
while(1){
}
}
每次显示数码管的时候都要敲这么多代码,因为可以写一个显示数码管的函数,如下:
#include <REGX52.H>
unsigned nixietable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
void nixie(unsigned char location, num){
switch(location){
case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;
case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;
case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;
case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;
case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;
case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;
case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;
case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;
}
P0 = nixietable[num];
}
void main(){
nixie(3,6);
while(1){
}
}
动态数码管显示
即,动态清零,数字消影,来实现动态数码管显示。这是单片机不断扫描来成像的,硬件设备简单,但也因此将耗费大量CPU时间,资源。
例子:
#include <REGX52.H>
#include "Delay.h"
unsigned nixietable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};
void nixie(unsigned char location, num){
switch(location){
case 1: P2_4 = 1; P2_3 = 1; P2_2 = 1; break;
case 2: P2_4 = 1; P2_3 = 1; P2_2 = 0; break;
case 3: P2_4 = 1; P2_3 = 0; P2_2 = 1; break;
case 4: P2_4 = 1; P2_3 = 0; P2_2 = 0; break;
case 5: P2_4 = 0; P2_3 = 1; P2_2 = 1; break;
case 6: P2_4 = 0; P2_3 = 1; P2_2 = 0; break;
case 7: P2_4 = 0; P2_3 = 0; P2_2 = 1; break;
case 8: P2_4 = 0; P2_3 = 0; P2_2 = 0; break;
}
P0 = nixietable[num];
Delay(1); //数字消影,若不清除数字,数字会产生错乱,出现”影子“
P0 = 0x00; //动态清零,若不清零,则段选和位选容易串位
}
void main(){
while(1){
nixie(1,0);
nixie(2,3);
nixie(3,0);
nixie(4,1);
}
}
数码管驱动方式
1、数码管直接扫描,如上;
2、专用驱动芯片:内部自带显存、扫描电路,单片机只需告诉他显示什么即可。(得另买,了解就得了)
任务
任务代码:
#include <REGX52.H>
#include "Delay.h"
#include "Nixie.h"
unsigned char n=0,j=0,k=0,tem=0,mode,time;
void Display(unsigned char mode)
{
switch(mode)
{
case 1:
nixie(1,10);nixie(2,1);nixie(7,0);nixie(8,1);
break;
case 2:
if(time<250){
time++;
nixie(1,10);nixie(2,2);
if(n < 10)
{
nixie(7,0);nixie(8,n);
}
else
{
nixie(7,1);nixie(8,0);
}
Delay(1);
}
else{
time = 0;
if(n < 10)
{
n++;
}
else
{
n = 0;
}
}
break;
case 3:
time++;
nixie(1,10);nixie(2,3);nixie(7,0);nixie(8,3);
if(j == 0)
{
nixie(5,11);nixie(6,11);
}
if(time>125)
{
Delay(1);
j=~j;
time = 0;
}
break;
case 4:
nixie(1,10);nixie(2,4);nixie(7,k);nixie(8,k);
break;
}
}
void main()
{
while(1){
if(P3_1==0){Delay(20);if(P3_1==0)tem=1;}
if(P3_0==0){Delay(20);if(P3_0==0)tem=2;}
if(P3_2==0){Delay(20);if(P3_2==0)tem=3;}
if(P3_3==0){Delay(20);
while(P3_3==0)Display(4);
Delay(20);
{
if(tem!=4)k=0;
else
{
if(k<9)k++;
else k = 0;
}
tem=4;
}
}
if(tem)
mode = tem;
Display(mode);
}
}