前言
本次学习单片机的型号为“STC89C52RC”
开发软件为“Keil5”和“stc-isp”
一.单片机介绍(大部分废话,可以跳到下个阶段)
简介:
单片机英文名Micro Controller Unit,简称MCU,其内部集成了CPU、RAM、ROM、定时器、中断系统、通讯接口等一系列电脑常用的硬件功能,其任务是信息采集、处理和硬件设备的控制,相当于一个袖珍版计算机。学习单片机是了解计算机原理和结构的最佳选择
为什么叫51单片机
上世纪80年代intel开发的8051单片机,因为其中含有“51”,故叫51单片机
单片机配置:
位数:8位
RAM:512字节
ROM:8k(flash)
工作频率:12MHz
其中RAM相当于运存,ROM相当于内存
单片机命名规则
STC代表的是公司
89代表的是型号,这指的是51单片机
C代表的是工作电压,3.8V~3.8V
52代表的是程序空间及RAM空间大小,52是8k字节的大小和512字节的RAM空间大小
单片机内部结构
单片机管脚图:
单片机最小系统:
LED:
简介:中文名发光二极管,全称Light Emitting Diode,会发光的玩意儿
区分正负极:正极引脚较长,负极引脚较短,正极内电极较小,负极内电极较大。
LED模块原理图:
利用高低电频控制接正接负,高电频为5V为正,低电频为0V为负,通过输出高低电频控制LED灯亮或者灭
辣么怎么控制输出高低电频
一切尽在图中
轻触按键:
相当于是一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开
LED数码管:
数码管是一种简单、廉价的显示器,是由多个发光二极管封装在一起组成“8”字型的器件,原型参考计算器和红绿灯
数码管断码表(看不懂):
扫描的概念:
数码管扫描(输出扫描)
原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
矩阵键盘扫描(输入扫描)
原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
以上两种扫描方式的共性:节省I/O口
后话:
单片机介绍内容巨多,接下来还有定时器,寄存器,中断系统,串口等,在此不再概述,否则篇幅会很长,直接进入学习
二.单片机程序开发
模块化编程:
传统写c语言代码,都是在一个文件中定义函数使用函数,但是可以把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,称为模块化编程,就像从头文件<stdio.h>中调用printf一样。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。
.c文件:函数、变量的定义
.h文件:可被外部调用的函数、变量的声明
C语言预编译:
C语言的预编译以#开头,作用是在真正的编译开始之前,对代码做一些处理(预编译)
此外还有#ifdef,#if,#else,#elif,#undef等
LCD1602调试工具:
使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能,可实时观察单片机内部数据的变换情况,便于调试和演示。
三.点亮一个LED
1.创建文件:
首先在project里面创建一个新project文件,接着选择单片机的型号,在文件夹里添加新的c文件
这三个从左到右分别是编译,运行,重新运行
接着就像写C语言一样编程就可以了
2.程序源码
原理:
1代表高电频,0代表低电频,如果想让第一个灯亮,则应该为1111 1110
但是代码中不支持二进制,需要转换为16进制
进制转换:
在c语言里边,十六进制前需要一个0x作为前缀表示这是一个十六进制的数
<REGX52.H>
这里面放置了很多寄存器,如果不引用这个玩意儿,编译过不去,因为不认识P2
源码:
#include <REGX52.H>
void main()
{
P2 = 0xFE;
}
(so easy)
可以控制P2的十六进制数来控制8个LED的亮灭
3.烧录
用到stc-isp软件
步骤:
1.首先选择型号,本次学习型号是“STC89C52RC”
2.串口选择CH340那个
3.在程序中选择自动生成hex文件
4.再次点击编译
5.选择程序文件并下载
6.按单片机的开关进行冷启动
至此,D1被点亮了
总结:
这节课教怎么写代码并将代码烧录到单片机中,并以此来控制LED灯
四.LED闪烁
原理:用while死循环
源码:
#include <REGX52.H>
void main(){
while(1){
P2 = 0xFF;
P2 = 0xFE;
}
}
显然,人眼看出是常亮的。因为单片机的频率是12MHz,人眼捕捉不到它的闪烁,但是其实它是在闪烁的,所以需要控制它的延时,stc-isp中有延时计算器可以提供帮助,配置改成应该的配置,89C52的8051指令集应该是STC-Y1,然后复制代码到编译器中
注:代码中的_nop_()是没有被定义的,需要引用头文件<INTRINS.H>
最终的代码为:
#include <REGX52.H>
#include <INTRINS.H>
void Delay500ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main(){
while(1){
P2 = 0xFF;
Delay500ms();
P2 = 0xFE;
Delay500ms();
}
}
在学习流水灯之前,自制流水灯程序:
#include <REGX52.H>
#include <INTRINS.H>
void Delay100ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 195;
j = 138;
do
{
while (--j);
} while (--i);
}
void Delay500ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main(){
while(1){
P2 = 0xFE;
Delay100ms();
P2 = 0xFD;
Delay100ms();
P2 = 0xFB;
Delay100ms();
P2 = 0xF7;
Delay100ms();
P2 = 0xEF;
Delay100ms();
P2 = 0xDF;
Delay100ms();
P2 = 0xBF;
Delay100ms();
P2 = 0x7F;
Delay500ms();
}
}
四.流水灯
源码如上
延时函数:
void Delayxms(unsigned int ms) //@12.000MHz
{
unsigned char data i, j;
while(ms){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
ms--;
}
}
五.独立按键控制LED亮灭
1.独立按键
原理图:
分别接到单片机这四个口
注:K1应该是P3_1,K2应该是P3_0,不按照顺序
原理:
利用if语句,如果按下就是0,松开就是1,控制LED亮灭
代码:
#include <REGX52.H>
void main(){
while(1){
if(P3_1 == 0)
P2_0 = 0;
else
P2_0 = 1;
}
}
按位运算:
按位与&:都是1才是1
按位或|:都是0才是0
按位异或^:不一样才是1
按位取反~:每位取反
基本语句:
1 if
2 while
3 for
4 switch
语法和c语言一致
六.独立按键控制LED状态
按键的抖动:
按键的消抖:
硬件处理:
在按键处添加一些电路,通过一些电路的操作消抖
软件处理:
通过设计代码消除抖动的影响,如通过延时函数
死循环的作用:
死循环可以让程序困在那一步而不往下运行,可以人为操作死循环,例如while(P3_1 == 0)表示不松开按键就是死循环
软件处理消抖示例核心部分:
if(P3_1 == 0){
Delayxms(20);
while(P3_1 == 0);
Delayxms(20);
}
七.独立按键控制LED显示二进制
代码:
#include <REGX52.H>
void Delayxms(unsigned int ms) //@12.000MHz
{
unsigned char data i, j;
while(ms--){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void main(){
unsigned char LEDnum = 0;
while(1){
if(P3_1==0){
Delayxms(20);
while(P3_1==0);
Delayxms(20);
LEDnum++;
P2 = ~LEDnum;
}
}
}
注:这里没有对P2直接进行++,因为无效
这里的数据在单片机实际上也就是二进制的数据,会自动转化为二进制
八.独立按键控制LED移位
原理:
用到移位符“<<”,“>>”,通过按键对其进行移位运算
代码(两个按键控制左移右移):
#include <REGX52.H>
void Delayxms(unsigned int ms) //@12.000MHz
{
unsigned char data i, j;
while(ms){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
ms--;
}
}
void main(){
unsigned char LEDnum=0;
P2 = ~0x01;
while(1){
if(P3_1==0){
Delayxms(20);
while(P3_1==0);
Delayxms(20);
LEDnum++;
if(LEDnum>=8)
LEDnum=0;
P2 = ~(0x01<<LEDnum);
}
if(P3_0==0){
Delayxms(20);
while(P3_0==0);
Delayxms(20);
if(LEDnum==0)
LEDnum=7;
else
LEDnum--;
P2 = ~(0x01<<LEDnum);
}
}
}
九.静态数码管显示
1.数码管展示
原理图:
其中包括一个138译码器和74HC25,用于控制数码管
引脚:
共阴极和共阳极连接:
数码管段数表示:
引脚序号:
2.数码管点亮原理
数码管里面其实就是8个LED(7条和一点),想显示什么图形,就点亮对应的LED就好了,如想显示6,就点亮A、C、D、E、F、G即可。
根据共阳极还是共阴极来确定给0还是1,根据段码控制想点亮的段
负极接地给低电频,正极接VCC给高电频
位选:
通过选择不同位置的数码管控制整个数码管的亮灭
3.四位一体数码管
51单片机用的是四位一体数码管,其原理图如下
4.138译码器和74HC25
138译码器:
3口入8口出,所谓138译码器
三口指CBA(从高位到低位)三个口,8口指Y0~Y7八个口
以二进制数来看,三位正好可以表示八个
74HC25:
即是双向数据缓冲器,用于缓冲数据,提高驱动能力
DIR的功能是控制缓冲数据的方向,接高电频是从左向右,低电频是从右向左
5.控制数码管显示:
原理:
通过P2口位选,通过P0口选择亮的LED组合成要显示的形状,八个LED组合成的二进制数转换为16进制
代码(控制第三个数码管显示6):
#include <REGX52.H>
void main(){
P2_4 = 1;
P2_3 = 0;
P2_2 = 1;
P0 = 0x7D;
while(1){
}
}
因为引脚冲突,其他地方的LED也会亮起来
控制位置和数字显示:
void miu(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;
}
switch(num){
case 1:P0=0x3F;break;
case 2:P0=0x06;break;
case 3:P0=0x47;break;
case 4:P0=0x66;break;
case 5:P0=0x6D;break;
case 6:P0=0x7D;break;
case 7:P0=0x07;break;
case 8:P0=0x7F;break;
case 9:P0=0x6F;break;
case 0:P0=0x3F;break;
}
}
第二个switch-case可以用数组简化
十.动态数码管显示
原理:
实质上就是用循环不断扫描的过程,如果没有延时函数,则人眼不能捕捉到其变化,所以看起来是常量的
如想让第1 2 3位显示1 2 3,代码如下:
#include <REGX52.H>
unsigned char miu_arr[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
void Delayxms(unsigned int ms) //@12.000MHz
{
unsigned char data i, j;
while(ms--){
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void miu(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=miu_arr[num];
}
void main(){
while(1){
miu(1,1);
Delayxms(5);
miu(2,2);
Delayxms(5);
miu(3,3);
Delayxms(5);
}
}
天下武功,唯快不破,如果间隔太短,则会出现残影,所以需要消影
消影:
则是在段选之后加个清零,对下一位没有影响
十一.单片机驱动方式
1.单片机直接扫描
硬件设备简单,但是会耗费大量的单片机CPU时间
2.专用驱动芯片
内部自带显存、扫描电路,单片机只需告诉它显示什么即可