单片机学习笔记(上)
开发板介绍
51单片机介绍
单片机
在一片集成电路芯片上集成微处理器、存储器、I/O接口电路,从而构成了单芯片微型计算机,就是单片机。 Intel公司推出了MCS-51系列单片机:集成8位CPU、4K字节ROM、128字节RAM、4个8位并口、1个全双工串行口、2个16位定时/计数器。寻址范围64K,并有控制功能较强的布尔处理器。
电平特性
数字电路中只有两种电平:高电平和低电平
高电平:5V或者3.3V,取决单片机电源。
低电平:0V
RS232电平:计算机串口的电平
高电平:-12V
低电平:+12V
所以当我们用单片机跟电脑通信的时候,我们要通过各种元器件将单片机的电平转换为计算机可识别的电平才能跟电脑进行通信。
二进制与十六进制
基数是2,逢二进一。
基数是16,逢十六进一。
二进制逻辑运算
“与”运算
有0得0
1&1=1 ; 1&0=0 ; 0&0=0 ;
“或”运算
有1得1
1|1=1 ; 1|0=1 ; 0|0=0;
“非”运算
1的非得0,0的非得1。
~1=0; ~0=1;
“异或”运算
必须不同,否则没有(0)
11=0;10=1;0^0=0;
引脚封装图
P3口第二功能各引脚功能定义:
P3.0:RXD串行口输入
P3.1:TXD串行口输出
P3.2:INT0外部中断0输入
P3.3:INT1外部中断1输入
P3.4:T0定时器0外部输入
P3.5:T1定时器1外部输入
P3.6:WR外部写控制
P3.7:RD外部读控制
8051内部结构
总线(BUS)是计算机各部件之间传送信息的公共通道。微机中有内部总线和外部总线两类。内部总线是CPU内部之间的连线。外部总线是指CPU与其它部件之间的连线。
外部总线有三种:
数据总线DB(Data Bus), 地址总线 AB(Address Bus)和控制总线 CBControl Bus)。
CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器;
RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据;
ROM:用以存放程序、一些原始数据和表格;
I/O口:四个8位并行I/O口,既可用作输入,也可用作输出;
T/C:两个定时/记数器,既可以工作在定时模式,也可以工作在记数模式;
五个中断源的中断控制系统;
一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;
片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最高振荡频率取决于单片机型号及性能。
单片机工作基本时序
机器周期和指令周期
(1)振荡周期:也称时钟周期,是指为单片机提供时钟脉冲信号的振荡源的周期,我们开发板上为12MHZ。
(2)状态周期:每个状态周期为时钟周期的2倍,是振荡周期经二分频后得到的。
(3)机器周期:一个机器周期包含6个状态周期S1~S6,也就是 12 个时钟周期。在一个机器周期内,CPU可以完成一个独立的操作。
(4)指令周期:它是指CPU完成一条操作所需的全部时间。每条指令执行时间都是有一个或几个机器周期组成。MCS - 51 系统中,有单周期指令、双周期指令和四周期指令。
单片机IO口的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-argmVerP-1614944449890)(C:\Users\dell\AppData\Local\Temp\1614942637242.png)]
上拉电阻就是将不确定的信号通过一个电阻拉到高电平,同时此电阻起到一个限流的作用,下拉就是下拉到低电平。
作用:
- OC门要输出高电平,外部必须加上拉电阻。
- 加大普通IO口的驱动能力。
- 起到限流的作用。
- 抵抗电磁干扰。
选取原则:
- 从降低功耗方面考虑应该足够大,因为电阻越大,电流越小。
- 从确保足够的引脚驱动能力考虑应该足够小,电阻越小,电流才能越大。
- 开漏输出时,过大的上拉电阻会导致信号上升沿变缓。
C语言基础
C语言基础
一个简单的单片机C程序要有什么
#include<reg51.h>
void main()
{
while(1)
{
}
}
C语言中常用语句略,if,while,do…while,for,switch…case
函数略
C-51的数据类型扩充定义
sfr:特殊功能寄存器声明
sfr 变量名=地址值;
*特殊功能寄存器在reg51.H这个头文件里面都帮我们定义好了,所以平时我们就不要自己去定义寄存器的名字。
sbit:特殊功能位声明
sbit 变量名=地址值;
*在给某个引脚取名的时候经常会用到。
bit:位变量声明
*用来定义位数据变量
例:sfr SCON= 0X98;
sbit LED= P0^2;
单片机最小系统
1.电源电路
2.复位电路
3.时钟电路
4.下载电路
开发软件安装与工程建立
KEIL安装
见百度,非常简单,需要破解
创建一个基本工程
project–>new project–>新建一个文件夹,保存在那里面–>选择单片机—>File—>new—>文件名main.c—>右击Source Group,Add File。
#include<reg51.h>
void main()
{
while(1)
{
}
}
点那个彩虹锤,output勾上create hex file
编译,运行
点亮第一个LED
C语言知识点:
bit和sbit都是C51扩展的变量类型。
sbit用法:
sbit 变量名=地址值;
在给某个引脚取名的时候经常会用到。
#include<reg51.h>
sbit led=P0^0;
void main()
{
while(1)
{
led=1;//给p0^0管脚一个高电平
}
}
LED闪烁
typedef使用
typedef unsigned char u8;
typedef unsigned int u16;//后面要加分号
重新定义一些常用的关键词,可以增强程序的可移植性,因为在不同的编译软件上面,C语言的数据类型的关键词的位宽是不一样的。
while循环函数
while(i<10)
{
i=i+1;
}
while语句的语义是:计算表达式的值,当值为真(非0)时,执行循环体语句。
延时函数
void delay(u16 i)//大约延时10us
{
while(i--);
}
LED闪烁程序
#include<reg51.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit led=P0^0;
void delay(u16 i)//大约延时10us
{
while(i--);
}
void main()
{
while(1)
{
led=1;//给p0^0管脚一个高电平
delay(50000);//500ms
led=0;
delay(50000);
}
}
LED流水灯
#define使用
#define A P0(注意后面不用加分号)
循环左移右移函数
crol(a,b);循环左移函数,a是左移的值,b是左移的位数。包含在instrins.h库函数里面。
cror(a,b);循环右移函数,a是右移的值,b是右移的位数。包含在instrins.h库函数里面。
LED流水灯程序
#include<reg51.h>
#include<instrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define led P0
void delay(u16 i)//大约延时10us
{
while(i--);
}
void main()
{
u8 i;
led=0x01;//16进制 化成二进制是0000 0001
delay(50000)
while(1)
{
for(i=0;i<8;i++)
{
led=(0x01<<i);//0x01 0000 0001 0000 0010移位
delay(50000);
}
}
}
蜂鸣器
蜂鸣器模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wL4Ycpqw-1614944449897)(C:\Users\dell\AppData\Local\Temp\1614942269888.png)]
多谐振荡器由晶体管或集成电路构成,当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
改变单片机引脚输出波形的频率,就可以调整控制蜂鸣器音调,产生各种不同音色、音调的声音。
改变输出电平的高低电平占空比,则可以控制蜂鸣器的声音大小。
蜂鸣器程序
#include<reg51.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit beep=P1^5;
void delay(u16 i)//大约延时10us
{
while(i--);
}
void main()
{
while(1)
{
beep=~beep;//取反
delay(10);
}
}
继电器
继电器电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fhhncgcA-1614944449899)(C:\Users\dell\AppData\Local\Temp\1614942307804.png)]
继电器程序
#include<reg51.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit relay=P1^4;
void main()
{
relay=0;
while(1)
{
}
}
静态数码管
静态数码管模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2rfDHAGM-1614944449902)(C:\Users\dell\AppData\Local\Temp\1614942755145.png)]
静态数码管原理
LED数码管根据LED的不同接法可以分为2类:共阴和共阳。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J8DQ66sx-1614944449904)(C:\Users\dell\AppData\Local\Temp\1614933129637.png)]
共阳码表:
0xC0 | 0xF9 | 0xA4 | 0xB0 | 0x99 | 0x92 | 0x82 | 0xF8 | 0x80 | 0x90 | 0x88 | 0x83 | 0xC6 | 0xA1 | 0x86 | 0x8E | 0xFF |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 无显示 |
共阴码表:
0x3f | 0x06 | 0x5b | 0x4f | 0x66 | 0x6d | 0x7d | 0x07 | 0x7f | 0x6f | 0x77 | 0x7c | 0x39 | 0x79 | 0x79 | 0x71 | 0x00 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 无显示 |
74HC245
数码管静态显示原理
LED显示器工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个8位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法的优点是占用CPU时间少,显示便于监测和控制。缺点是硬件电路比较复杂,成本较高
代码
#include<reg51.h>
#include<intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
u8 code smgduan[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF};//共阳
void main()
{
P0=smgduan[0];
while(1)
{
}
}
动态数码管
动态数码管模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iopuqmGN-1614944449905)(C:\Users\dell\AppData\Local\Temp\1614942907550.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T8IWxhZd-1614944449906)(C:\Users\dell\AppData\Local\Temp\1614942931043.png)]74HC138,J10接J1
74HC245与74HC138
动态数码管原理
动态显示的特点是将所有数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。动态显示的亮度比静态显示要差一些,所以在选择限流电阻时应略小于静态显示电路中的。
代码
#include<reg51.h>
#include<intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSB=P2^4;
u8 code smgduan[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xFF};//共阳
void delay(u16 i){
while(i--);
}
void DigDisplay(){
u8 i;
for(i=0;i<8;i++){
switch(i)
{
case 0:
LSA=0;LSB=0;LSC=0;break;
case 1:
LSA=1;LSB=0;LSC=0;break;
case 2:
LSA=0;LSB=1;LSC=0;break;
case 3:
LSA=1;LSB=1;LSC=0;break;
case 4:
LSA=0;LSB=0;LSC=1;break;
case 5:
LSA=1;LSB=0;LSC=1;break;
case 6:
LSA=0;LSB=1;LSC=1;break;
case 7:
LSA=1;LSB=1;LSC=1;break;
}
P0=smgduan[i];
delay(100);
P0=0x00;//P0口数据清零,如果不清零会有重影
}
}
void main()
{
P0=smgduan[0];
while(1)
{
}
}
独立按键
独立按键模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D5h2UwfP-1614944449907)(C:\Users\dell\AppData\Local\Temp\1614943067407.png)]
独立按键程序
/*************************************************************
实验现象:下载程序后按下K1按键可以对D1小灯状态取反
*************************************************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit k1=P1^0; //定义P10口是k1
sbit led=P0^0; //定义P00口是led
void delay(u16 i)
{
while(i--);
}
//按键处理函数
void keypros()
{
if(k1==0) //检测按键K1是否按下
{
delay(1000); //消除抖动 一般大约10ms
if(k1==0) //再次判断按键是否按下
{
led=~led; //led状态取反
}
while(!k1); //检测按键是否松开
}
}
void main()
{
led=1;
while(1)
{
keypros(); //按键处理函数
}
}
矩阵按键
矩阵按键模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nNSIDEpA-1614944449908)(C:\Users\dell\AppData\Local\Temp\1614943473595.png)]
矩阵按键扫描原理方法
-
逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为1的时候,说明有按键按下,然后通过接收到的数据是哪一位为0来判断是哪一个按键被按下。
-
行列扫描:我们可以通过高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下,然后再反过来,高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是那一行有按键按下,这样就能够确定是哪一个按键按下了。
矩阵按键变独立按键
JP3中的5678脚接GND
矩阵按键程序(行列扫描)
//矩阵按键实验 *
//实现现象:下载程序后动态数码管的第一个显示0,按下矩阵按键上的按键显示对应的数字
//注意事项:如果不想让点阵模块显示,可以将74HC595模块上的JP595短接片拔掉。
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
#define GPIO_DIG P0
#define GPIO_KEY P1
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 KeyValue; //用来存放读取到的键值
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0~F的值
void delay(u16 i)
{
while(i--);
}
//函数功能: 检测有按键按下并读取键值
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
void main()
{
LSA=0; //给一个数码管提供位选
LSB=0;
LSC=0;
while(1)
{
KeyDown(); //按键判断函数
GPIO_DIG=smgduan[KeyValue]; //
}
e;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
void main()
{
LSA=0; //给一个数码管提供位选
LSB=0;
LSC=0;
while(1)
{
KeyDown(); //按键判断函数
GPIO_DIG=smgduan[KeyValue]; //
}