单片机学习笔记
开发板介绍
用的是普中科技的EM3.V2.2开发板,没什么可说的,做好最小系统板后也可以自己搭建电路使用。
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口的结构
上拉电阻就是将不确定的信号通过一个电阻拉到高电平,同时此电阻起到一个限流的作用,下拉就是下拉到低电平。
作用:
- 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;
}
}
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)
{
while(i--);
}
void main()
{
while(1)
{
led=1;
delay(50000);
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)
{
while(i--);
}
void main()
{
u8 i;
led=0x01;
delay(50000)
while(1)
{
for(i=0;i<8;i++)
{
led=(0x01<<i);
delay(50000);
}
}
}
蜂鸣器
蜂鸣器模块电路
多谐振荡器由晶体管或集成电路构成,当接通电源后(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);
}
}
继电器
继电器电路
继电器程序
#include<reg51.h>
typedef unsigned char u8;
typedef unsigned int u16;
sbit relay=P1^4;
void main()
{
relay=0;
while(1)
{
}
}
静态数码管
静态数码管模块电路
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Xg1JDGqm-1628929040392)(F:\01单片机学习笔记\静态数码管模块.png)]
静态数码管原理
? LED数码管根据LED的不同接法可以分为2类:共阴和共阳。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FA91VRjT-1628929040394)(F:\01单片机学习笔记\静态数码管原理.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)
{
}
}
动态数码管
动态数码管模块电路
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-EisFFO5x-1628929040400)(F:\01单片机学习笔记\独立按键模块.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(); //按键处理函数
}
}
矩阵按键
矩阵按键模块电路
矩阵按键扫描原理方法
-
逐行扫描:我们可以通过高四位轮流输出低电平来对矩阵键盘进行逐行扫描,当低四位接收到的数据不全为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]; //
}
*单片机IO扩展(并转串)–74HC165
74HC165芯片介绍
输入端的扩展
逻辑图和真值表
程序
#include "reg52.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_LED P0
sbit IN_PL = P1^6;
sbit IN_Data = P1^7;
sbit SCK = P3^6;
u8 Read74HC165(void)
{
u8 i;
u8 indata;
IN_PL = 0;
_nop_();
IN_PL = 1;
_nop_();
indata=0;
for(i=0; i<8; i++)
{
indata = indata<<1;
SCK = 0;
_nop_();
indata |= IN_Data;
SCK = 1;
}
return(indata);
}
void main()
{
u8 h165Value;
GPIO_LED = 0xff;
while(1)
{
h165Value = Read74HC165();
if(h165Value != 0xFF)
{
GPIO_LED = h165Value;
}
}
}
*单片机IO扩展(串转并)–74HC595
输出端的扩展
逻辑图和真值表
程序
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
void delay(u16 i)
{
while(i--);
}
void Hc595SendByte(u8 dat)
{
u8 a;
SRCLK = 1;
RCLK = 1;
for(a=0;a<8;a++)
{
SER = dat >> 7;
dat <<= 1;
SRCLK = 0;
_nop_();
_nop_();
SRCLK = 1;
}
RCLK = 0;
_nop_();
_nop_();
RCLK = 1;
}
void main()
{
u8 ledNum;
ledNum = ~0x01;
while(1)
{
Hc595SendByte(ledNum);
ledNum = _crol_(ledNum, 1);
delay(50000);
}
}
LED点阵
LED点阵模块电路
LED点阵左上角第一个点的LED被点亮
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
void Hc595SendByte(u8 dat1,u8 dat2)
{
u8 a;
SRCLK = 1;
RCLK = 1;
for(a=0;a<8;a++)
{
SER = dat1 >> 7;
dat1 <<= 1;
SRCLK = 0;
_nop_();
_nop_();
SRCLK = 1;
}
for(a=0;a<8;a++)
{
SER = dat2 >> 7;
dat2 <<= 1;
SRCLK = 0;
_nop_();
_nop_();
SRCLK = 1;
}
RCLK = 0;
_nop_();
_nop_();
RCLK = 1;
}
void main()
{
while(1)
{
Hc595SendByte(0xfe,0x01);
}
}
LED点阵显示数字0
#include "reg51.h"
#include "intrins.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit SRCLK=P3^6;
sbit RCLK=P3^5;
sbit SER=P3^4;
u8 ledduan[]={0x00,0x00,0x3E,0x41,0x41,0x41,0x3E,0x00};
u8 ledwei[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
void delay(u16 i)
{
while(i--);
}
void Hc595SendByte(u8 dat1,u8 dat2)
{
u8 a;
SRCLK = 1;
RCLK = 1;
for(a=0;a<8;a++)
{
SER = dat1 >> 7;
dat1 <<= 1;
SRCLK = 0;
_nop_();
_nop_();
SRCLK = 1;
}
for(a=0;a<8;a++)
{
SER = dat2 >> 7;
dat2 <<= 1;
SRCLK = 0;
_nop_();
_nop_();
SRCLK = 1;
}
RCLK = 0;
_nop_();
_nop_();
RCLK = 1;
}
void main()
{
u8 i=0;
while(1)
{
for(i=0;i<8;i++)
{
Hc595SendByte(ledwei[i],ledduan[i]);
delay(10);
}
}
}
直流电机
直流电机驱动电路图
程序
五线四相步进电机模块输出-->直流电机
5V-->直流电机两脚任意一个
O1-->直流电机两脚任意一个
**********************************************************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
#include<intrins.h> //因为要用到左右移函数,所以加入这个头文件
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit moto=P1^0;
void delay(u16 i)
{
while(i--);
}
void main()
{
u8 i;
moto=0; //关闭电机
for(i=0;i<100;i++) //循环100次,也就是大约5S
{
moto=1; //开启电机
delay(5000); //大约延时50ms
}
moto=0; //关闭电机
while(1)
{
}
}
步进电机
步进电机驱动电路图
程序
#include "reg52.h"
#include<intrins.h>
typedef unsigned int u16;
typedef unsigned char u8;
sbit MOTOA = P1^0;
sbit MOTOB = P1^1;
sbit MOTOC = P1^2;
sbit MOTOD = P1^3;
#define SPEED 200
void delay(u16 i)
{
while(i--);
}
void main()
{
P1=0X00;
while(1)
{
MOTOA = 1;
MOTOB = 0;
MOTOC = 1;
MOTOD = 1;
delay(SPEED);
MOTOA = 1;
MOTOB = 1;
MOTOC = 1;
MOTOD = 0;
delay(SPEED);
MOTOA = 0;
MOTOB = 1;
MOTOC = 1;
MOTOD = 1;
delay(SPEED);
MOTOA = 1;
MOTOB = 1;
MOTOC = 0;
MOTOD = 1;
delay(SPEED);
}
}
中断系统
中断的概念
CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);
CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);
待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断 。
? 引起CPU中断的根源,称为中断源。中断源向CPU提出的中断请求。CPU暂时中断原来的事务A,转去处理事件B。对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回。实现上述中断功能的部件称为中断系统。
51单片机的中断系统结构
89C51/52的中断系统有5个中断源 ,2个优先级,可实现二级中断嵌套 。
- (P3.2)可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置1,向CPU申请中断。
- (P3.3)可由IT1(TCON.2)选择其为低电平有效还是下降沿有效。当CPU检测到P3.3引脚上出现有效的中断信号时,中断标志IE1(TCON.3)置1,向CPU申请中断。
- TF0(TCON.5),片内定时/计数器T0溢出中断请求标志。当定时/计数器T0发生溢出时,置位TF0,并向CPU申请中断。
- TF1(TCON.7),片内定时/计数器T1溢出中断请求标志。当定时/计数器T1发生溢出时,置位TF1,并向CPU申请中断。
- RI(SCON.0)或TI(SCON.1),串行口中断请求标志。当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断。
中断允许控制
? CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mY7JJSST-1628929040414)(F:\01单片机学习笔记\中断允许控制1.png)]
- EX0(IE.0),外部中断0允许位;
- ET0(IE.1),定时/计数器T0中断允许位;
- EX1(IE.2),外部中断0允许位;
- ET1(IE.3),定时/计数器T1中断允许位;
- ES(IE.4),串行口中断允许位;
- EA(IE.7), CPU中断允许(总允许)位。
中断请求标志
TCON的中断标志
-
IT0(TCON.0),外部中断0触发方式控制位。 ? 当IT0=0时,为低电平触发方式。 ? 当IT0=1时,为边沿触发方式(下降沿有效)。 -
IE0(TCON.1),外部中断0中断请求标志位。 -
IT1(TCON.2),外部中断1触发方式控制位。 -
IE1(TCON.3),外部中断1中断请求标志位。 -
TF0(TCON.5),定时/计数器T0溢出中断请求标志位。 -
TF1(TCON.7),定时/计数器T1溢出中断请求标志位。
? 同一优先级中的中断申请不止一个时,则有中断优先权排队问题。同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,其排列如所示:
中断源
51单片机的中断优先级有三条原则:
- CPU同时接收到几个中断时,首先响应优先级别最高的中断请求。
- 正在进行的中断过程不能被新的同级或低优先级的中断请求所中断。
- 正在进行的低优先级中断服务,能被高优先级中断请求所中断。
为了实现上述后两条原则,中断系统内部设有两个用户不能寻址的优先级状态触发器。其中一个置1,表示正在响应高优先级的中断,它将阻断后来所有的中断请求;另一个置1,表示正在响应低优先级中断,它将阻断后来所有的低优先级中断请求。
中断处理过程
中断响应条件
- 中断源有中断请求;
- 此中断源的中断允许位为1;
- CPU开中断(即EA=1)。
以上三条同时满足时,CPU才有可能响应中断。
使用中断,程序员需要做什么?
- 你想使用的中断是哪个?选择相应的中断号;
- 你所希望的触发条件是什么?(低电平还是下降沿)
- 你希望在中断之后干什么?
例子:
以外部中断0为例,
主程序中需要有以下代码:
EA=1;
EX0=1;
IT0=0/1;
中断服务函数:
void int0 () interrupt 0 using 1
{
do anything that you want
}
例程
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit k3=P3^2;
sbit led=P2^0;
void delay(u16 i)
{
while(i--);
}
void Int0Init()
{
IT0=1;
EX0=1;
EA=1;
}
void main()
{
Int0Init();
while(1);
}
void Int0() interrupt 0
{
delay(1000);
if(k3==0)
{
led=~led;
}
}
定时器和计数器
CPU时序的有关知识
- 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期)
- 状态周期:2个振荡周期为1个状态周期,用S表示。振荡周期又称S周期或时钟周期。
- 机器周期:1个机器周期含6个状态周期,12个振荡周期。
- 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位。
例如:外接晶振为12MHz时,51单片机相关周期的具体值为:振荡周期=1/12us;状态周期=1/6us;机器周期=1us;指令周期=1~4us;
在学习定时器之前需要明白的
- 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器。
- 定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程是自动完成的,不需要CPU的参与。
- 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1。
- 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理。CPU转而处理一些复杂的事情。同时可以实现精确定时作用。
定时/计数器的工作原理
定时/计数器实质上是一个加1计数器。它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。
可见,由溢出时计数器的值减去计数初值才是加1计数器的计数值。
51单片机定时器结构
定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THx和TLx组成。TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制T0、T1的启动和停止及设置溢出标志。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fi3UUOMb-1628929040422)(F:\01单片机学习笔记\51单片机定时器结构.png)]
定时/计数器的控制
51单片机定时/计数器的工作由两个特殊功能寄存器控制。TMOD用于设置其工作方式;TCON用于控制其启动和中断申请。
? 1、工作方式寄存器TMOD
? 工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1。其格式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-88EU1OSG-1628929040424)(F:\01单片机学习笔记\工作方式寄存器TMOD.png)]
GATE是门控位, GATE=0时,用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作。即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件。
C/T :定时/计数模式选择位。C/T =0为定时模式;C/T =1为计数模式。
M1M0:工作方式设置位。定时/计数器有四种工作方式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yB67lZ1o-1628929040426)(F:\01单片机学习笔记\定时器计数器工作方式设置表.png)]
2、控制寄存器TCON
? TCON的低4位用于控制外部中断,已在前面介绍。TCON的高4位用于控制定时/计数器的启动和中断申请。其格式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qp01ErmA-1628929040428)(F:\01单片机学习笔记\控制寄存器TCON.png)]
TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1为1。CPU响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询TF1的状态。所以,TF1可用作查询测试的标志。TF1也可以用软件置1或清0,同硬件置1或清0的效果一样。
TR1(TCON.6):T1运行控制位。TR1置1时,T1开始工作;TR1置0时,T1停止工作。TR1由软件置1或清0。所以,用软件可控制定时/计数器的启动与停止。
TF0(TCON.5):T0溢出中断请求标志位,其功能与TF1类同。
TR0(TCON.4):T0运行控制位,其功能与TR1类同。
定时/计数器的工作方式
1、方式0
? 方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KKdO9eNz-1628929040433)(F:\01单片机学习笔记\定时器工作方式0.png)]
定时器模式时有:
N
=
t
/
T
c
y
N=t/Tcy
N=t/Tcy 计数初值计算的公式为:
X
=
2
13
?
N
X=2^{13}-N
X=213?N 定时器的初值还可以采用计数个数直接取补法获得。计数模式时,计数脉冲是T0引脚上的外部脉冲。
? 门控位GATE具有特殊的作用。当GATE=0时,经反相后使或门输出为1,此时仅由TR0控制与门的开启,与门输出1时,控制开关接通,计数开始;当GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和TR0共同控制。当TR0=1时,外中断引脚信号引脚的高电平启动计数,外中断引脚信号引脚的低电平停止计数。这种方式常用来测量外中断引脚上正脉冲的宽度。
2、方式1
方式1的计数位数是16位,由TL0作为低8位,TH0作为高8位,组成了16位加1计数器 。
计数个数与计数初值的关系为:
X
=
2
16
?
N
X=2^{16}-N
X=216?N
3、方式2
? 方式2为自动重装初值的8位计数方式。
计数个数与计数初值的关系为:
X
=
2
8
?
N
X=2^{8}-N
X=28?N 工作方式2特别适合于用作较精确的脉冲信号发生器。
4、方式3
方式3只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数。
工作方式3将T0分成为两个独立的8位计数器TL0和TH0 。
使用定时器,该做哪些工作
初始化程序应完成如下工作:
- 对TMOD赋值,以确定T0和T1的工作方式。
- 计算初值,并将其写入TH0、TL0或TH1、TL1。
- 中断方式时,则对EA赋值,开放定时器中断。
- 使TR0或TR1置位,启动定时/计数器定时或计数。
计数器初值的计算
- 机器周期也就是CPU完成一个基本操作所需要的时间。
- 机器周期=1/单片机的时钟频率。
- 51单片机内部时钟频率是外部时钟的12分频。也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频。比如说你用的是12MHZ的晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候。机器周期=1/1M=1us。
- 而我们定时1ms的初值是多少呢,1ms/1us=1000。也就是要计数1000个数,初值=65535-1000+1(因为实际上计数器计数到66636才溢出)=64536=FC18H
定时器中断
例程
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit led=P2^0;
void Timer0Init()
{
TMOD|=0X01;
TH0=0XFC;
TL0=0X18;
ET0=1;
EA=1;
TR0=1;
}
void main()
{
Timer0Init();
while(1);
}
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC;
TL0=0X18;
i++;
if(i==1000)
{
i=0;
led=~led;
}
}
交通灯
电路图
程序
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
#define GPIO_DIG P0
#define GPIO_TRAFFIC P1
sbit RED10 = P1^0;
sbit GREEN10 = P1^1;
sbit RED11 = P1^2;
sbit YELLOW11= P1^3;
sbit GREEN11 = P1^4;
sbit RED00 = P3^0;
sbit GREEN00 = P3^1;
sbit RED01 = P1^5;
sbit YELLOW01= P1^6;
sbit GREEN01 = P1^7;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
u8 DisplayData[8];
u8 Second;
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;
}
GPIO_DIG=DisplayData[i];
delay(100);
GPIO_DIG=0x00;
}
}
void Timer0Init()
{
TMOD|=0X01;
TH0=0XFC;
TL0=0X18;
ET0=1;
EA=1;
TR0=1;
}
void main()
{
Second = 1;
Timer0Init();
while(1)
{
if(Second == 70)
{
Second = 1;
}
if(Second < 31)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(30 - Second) % 100 / 10];
DisplayData[3] = smgduan[(30 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
GPIO_TRAFFIC = 0xFF;
RED00 = 1;
GREEN00 = 1;
GREEN11 = 0;
GREEN10 = 0;
RED01 = 0;
RED00 = 0;
}
else if(Second < 36)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(35 - Second) % 100 / 10];
DisplayData[3] = smgduan[(35 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
GPIO_TRAFFIC = 0xFF;
RED00 = 1;
GREEN00 = 1;
YELLOW11 = 0;
RED10 = 0;
YELLOW01 = 0;
RED00 = 0;
}
else if(Second < 66)
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(65 - Second) % 100 / 10];
DisplayData[3] = smgduan[(65 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
GPIO_TRAFFIC = 0xFF;
RED00 = 1;
GREEN00 = 1;
RED11 = 0;
RED10 = 0;
GREEN01 = 0;
GREEN00 = 0;
}
else
{
DisplayData[0] = 0x00;
DisplayData[1] = 0x00;
DisplayData[2] = smgduan[(70 - Second) % 100 / 10];
DisplayData[3] = smgduan[(70 - Second) %10];
DisplayData[4] = 0x00;
DisplayData[5] = 0x00;
DisplayData[6] = DisplayData[2];
DisplayData[7] = DisplayData[3];
DigDisplay();
GPIO_TRAFFIC = 0xFF;
RED00 = 1;
GREEN00 = 1;
YELLOW11 = 0;
RED10 = 0;
YELLOW01 = 0;
RED00 = 0;
}
}
}
void Timer0() interrupt 1
{
static u16 i;
TH0=0XFC;
TL0=0X18;
i++;
if(i==1000)
{
i=0;
Second ++;
}
}
NE555脉冲发生器
原理图
代码
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_DIG P0
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
u8 DisplayData[8];
unsigned long Freq;
unsigned long TimeCount;
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;
}
GPIO_DIG=DisplayData[i];
delay(100);
GPIO_DIG=0x00;
}
}
void Timer_Config()
{
TMOD=0x51;
TH0=0x3C;
TL0=0xB0;
ET0=1;
ET1=1;
EA=1;
TR0=1;
TR1=1;
}
void main()
{
Timer_Config();
while(1)
{
if(TR1 == 0)
{
Freq = Freq + TL1;
Freq = Freq + (TH1 * 256);
DisplayData[0] = smgduan[Freq%1000000/100000];
DisplayData[1] = smgduan[Freq%100000/10000];
DisplayData[2] = smgduan[Freq%10000/1000];
DisplayData[3] = smgduan[Freq%1000/100];
DisplayData[4] = smgduan[Freq%100/10];
DisplayData[5] = smgduan[Freq%10];
Freq = 0;
TH1 = 0;
TL1 = 0;
TR0 = 1;
TR1 = 1;
}
DigDisplay();
}
}
void Timer0() interrupt 1
{
TH0=0x3C;
TL0=0xB0;
TimeCount++;
if(TimeCount==20)
{
TR0=0;
TR1=0;
TimeCount=0;
}
}
void Timer1() interrupt 3
{
Freq=Freq+65536;
}
串口通信
串行通信原理
计算机串行通信基础
- 随着多微机系统的广泛应用和计算机网络技术的普及,计算机的通信功能愈来愈显得重要。计算机通信是指计算机与外部设备或计算机与计算机之间的信息交换。
- 通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式。
- 计算机通信是将计算机技术和通信技术的相结合,完成计算机与外部设备或计算机与计算机之间的信息交换 。可以分为两大类:并行通信与串行通信。
- 并行通信通常是将数据字节的各位用多条数据线同时进行传送 。并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。
- 串行通信是将数据字节分成一位一位的形式在一条传输线上逐个地传送。串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。
串行通信基本概念
1、异步通信
? 异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。
? 异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高。
2、同步通信
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。
串行通信的传输方向
单工是指数据传输仅能沿一个方向,不能实现反向传输。
半双工是指数据传输可以沿两个方向,但需要分时进行。
全双工是指数据可以同时进行双向传输。
串行通信常见的错误校验
1、奇偶校验
? 在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。
2、代码和校验
? 代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。
3、循环冗余校验
? 这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。
传输速率
? 比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240个/秒 =2400 bps
传输距离与传输速率的关系
? 串行接口或终端直接传送串行信息位流的最大距离与传输速率及传输线的电气特性有关。当传输线使用每0.3m(约1英尺)有50PF电容的非平衡屏蔽双绞线时,传输距离随传输速率的增加而减小。当比特率超过1000bps 时,最大传输距离迅速下降,如9600bps 时最大距离下降到只有76m(约250英尺)。
串行接口通信标准
一、RS232C接口
? RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。
1、机械特性
? RS-232C接口规定使用25针连接器,连接器的尺寸及每个插针的排 列位置都有明确的定义。(阳头)
2、功能特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0BChDD7-1628929040451)(F:\01单片机学习笔记\RS232C功能特性.png)]
3、过程特性
? 过程特性规定了信号之间的时序关系,以便正确地接收和发送数据 。
采用RS-232C接口存在的问题
1、传输距离短,传输速率低
? RS-232C总线标准受电容允许值的约束,使用时传输距离一般不要超过15米(线路条件好时也不超过几十米)。最高传送速率为20Kbps。
2、有电平偏移
? RS-232C总线标准要求收发双方共地。通信距离较大时,收发双方的地电位差别较大,在信号地上将有比较大的地电流并产生压降。
3、抗干扰能力差
? RS-232C在电平转换时采用单端输入输出,在传输过程中当干扰和噪声混在正常的信号中。为了提高信噪比,RS-232C总线标准不得不采用比较大的电压摆幅。
二、RS422A接口
? RS-422A输出驱动器为双端平衡驱动器。如果其中一条线为逻辑“1”状态,另一条线就为逻辑“0”,比采用单端不平衡驱动对电压的放大倍数大一倍。差分电路能从地线干扰中拾取有效信号,差分接收器可以分辨200mV以上电位差。若传输过程中混入了干扰和噪声,由于差分放大器的作用,可使干扰和噪声相互抵消。因此可以避免或大大减弱地线干扰和电磁干扰的影响。RS-422A传输速率(90Kbps)时,传输距离可达1200米。
三、RS485接口
RS-485是RS-422A的变型:RS-422A用于全双工,而RS-485则用于半双工。RS-485是一种多发送器标准,在通信线路上最多可以使用32 对差分驱动器/接收器。如果在一个网络中连接的设备超过32个,还可以使用中继器。
RS-485的信号传输采用两线间的电压来表示逻辑1和逻辑0。由于发送方需要两根传输线,接收方也需要两根传输线。传输线采用差动信道,所以它的干扰抑制性极好,又因为它的阻抗低,无接地问题,所以传输距离可达1200米,传输速率可达1Mbps。
RS-485是一点对多点的通信接口,一般采用双绞线的结构。普通的PC机一般不带RS485接口,因此要使用RS-232C/RS-485转换器。对于单片机可以通过芯片MAX485来完成TTL/RS-485的电平转换。在计算机和单片机组成的RS-485通信系统中,下位机由单片机系统组成,上位机为普通的PC机,负责监视下位机的运行状态,并对其状态信息进行集中处理,以图文方式显示下位机的工作状态以及工业现场被控设备的工作状况。系统中各节点(包括上位机)的识别是通过设置不同的站地址来实现的。
串口通信编程
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
void UsartInit()
{
SCON=0X50;
TMOD=0X20;
PCON=0X80;
TH1=0XF3;
TL1=0XF3;
ES=1;
EA=1;
TR1=1;
}
void main()
{
UsartInit();
while(1);
}
void Usart() interrupt 4
{
u8 receiveData;
receiveData=SBUF;
RI = 0;
SBUF=receiveData;
while(!TI);
TI=0;
}
RS485通信
EEPROM(IIC总线)
DS18B20温度传感器
DS1302时钟
红外通信
AD模数转换
DA数模转换
RS485通信
EEPROM(IIC总线)
DS18B20温度传感器
DS1302时钟
红外通信
AD模数转换
DA数模转换
LCD1602液晶
|