目录
1.单片机简介
51单片机是一个集成电路芯片,就是一个小黑块的那个就是单片机
其包括:中央处理器CPU、随机存储器RAM、只读存储器ROM、
多种IO口和中断系统、定时器、计数器(或者还有显示驱动电路、脉宽调制电路、模拟多路转换器、AD转换器电路)等集中在一个硅片上构成的一个小而完整地微型计算机系统。
2.开发环境和程序烧录
开发环境:程序的编写工具 这里在kile4中用c语言编写文件编译成HEX文件。
project->new uVision project->选择第一步的工程文件夹位置->输入
工程名字->选择芯片类型AT89c52->会提示是否拷贝STARTUP.A51,选择是->生成了工程目录-
>在工程目录Source Group1上右键鼠标->add Exiting file to Source Group1...->选择代码文
件main.c
USB串口通讯:H340 需要安装
程序烧录:将编写的HEX文件下载到单片机中 -这里用STC-ISP,1.选择单片机型号2.选择串口3.打开程序文件4.程序下载
sfr指令:描述一组IO口中的数据 sfr P0=0x80
sbit指令:一个IO地址中的数据 sbit led2=P3^6
3.点亮LED
#include "reg52.h"
sbit led1 = P3^7;//根据原理图(电路图),设备变量led1指向P3组IO口的第7口
sbit led2 = P3^6;//根据原理图(电路图),设备变量led2指向P3组IO口的第6口
void main()
{
led1 = 0;//根据电路图,低电平点亮led
led2 = 0;
while(1);
}
4.按键控制LED
#include "reg52.h"
sbit led2 = P3^6;//根据原理图(电路图),设置变量led2指向P3组IO口的第6口
sbit sw1 = P2^1;
void main()
{
led2 = 0;
while(1){
if(sw1==0){
led2=~led2;
}
}
}
当程序复杂时用IO口状态位控制IO口——不直接对IO口进行操作
#include "reg52.h"
#define on 0 //将on 定义为0
#define off 1 //off 定义为1
sbit led2 = P3^6;//根据原理图(电路图),设置变量led2指向P3组IO口的第6口
sbit sw1 = P2^1;
sbit sw2 = P2^0;
void Delay500ms();
void main()
{
int ledmark=0;
while(1){
if(sw1==0){ //sw1为0是按下了
Delay500ms(); //延时500ms后还是按下的话就翻转
if(sw1==0){
ledmark=on;
}
}
if(sw2==0){ //sw1为0是按下了
Delay500ms(); //延时500ms后还是按下的话就翻转
if(sw2==0){
ledmark=off;
}
}
led2=ledmark;
}
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
5.振动传感器控制LED灯
#include "reg52.h"
#define on 0 //将on 定义为0
#define off 1 //off 定义为1
sbit led2 = P3^6;//根据原理图(电路图),设置变量led2指向P3组IO口的第6口
sbit vibrate=P3^3; //振动模块的数字信号接在3.3口
void Delay500ms();
void main()
{
while(1){
if(vibrate==0){
led2=on;
Delay500ms();
led2=off;
}else{
led2=off;
}
}
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
6.振动传感器控制继电器
#include "reg52.h"
#define on 0 //将on 定义为0
#define off 1 //off 定义为1
sbit vibrate=P3^3; //振动数字信号接在P3.3
sbit jidianqi=P3^2; //继电器的控制信号3.2口
void Delay500ms();
void main()
{
while(1){
if(vibrate==0){
jidianqi=on;
Delay500ms();
jidianqi=off;
}else{
jidianqi=off;
}
}
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
7.无线传感器控制蜂鸣器
#include "reg52.h"
sbit D0_ON = P1^2;
sbit D1_OFF = P1^3;
sbit switcher = P1^1;
void main()
{
//查询方式哪个按键被按下
while(1)
{
if(D0_ON == 1)//收到遥控信号A,D0表现为高电平
{
//A被按下,我们导通继电器,给继电器IN一个低电平
switcher = 0;
}
if(D1_OFF == 1)//收到遥控信号C,D1表现为高电平
{
//B被按下,我们不导通继电器,给继电器IN一个高电平
switcher = 1;
}
}
}
8.电动车振动报警装置
#include "reg52.h"
sbit D0_ON = P1^2;
sbit D1_OFF = P1^3;
sbit switcher = P1^1;
sbit vibrate=P1^4;
void Delay500ms();
void main()
{
int mark; //报警标志位
//查询方式哪个按键被按下
while(1)
{
if(D0_ON == 1){//收到遥控信号A,D0表现为高电平,//A被按下关闭报警模式,短响一下
mark=0;
switcher = 0; //给继电器一个短响
Delay500ms();
switcher = 1;
}
if(D1_OFF == 1)//收到遥控信号B,D1表现为高电平 //B被按下 开启报警模式,长响一下
{
mark=1;
//B被按下,我们不导通继电器,给继电器IN一个高电平
switcher = 0; //给继电器一个长响
Delay500ms();
Delay500ms();
switcher = 1;
}
if(mark==1 && vibrate==0){ //报警模式下 还震动了
switcher = 0; //开启继电器响1秒然后关闭
Delay500ms();
Delay500ms();
switcher = 1; //关闭
}
}
}
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
9.定时器控制LED
设置:TCON(计数器和中断的控制设置-触发方式、请求源)和TOMD(开启哪个计数器)寄存器
TCON中每个位可以单独操作
TMOD每个位需要一起操作,具体内容见——10进制转换
STC-ISP这个软件定时器所有的生成代码的部分应该是基于15系列,不能用自动重装,删去AUXR
#include "reg52.h" //定时器控制LED
sbit led1 = P3^7;
void main()
{
int num; //计次
//配置寄存器
TMOD=0x01; //选择模式01的寄存器 16位
//TCON中的配置
TR0=1; //开始计次
TF0=0; //计数溢出标志位
TL0=0x00; //开始计数的低8位
TH0=0xDC; //开始计数的高8位
while(1){
if(TF0==1){ //如果溢出标志位变化 完成一次计数 计次+1
num++; //计次一次后配置寄存器
//TR0=1; //开始计数 可要可不要,赋值后不会变化
TF0=0; //计数溢出标志位
TL0=0x00; //开始计数的低8位
TH0=0xDC; //开始计数的高8?
if(num==100){
num=0; //如果计次到100次10ms则LED翻转 num重置
led1=~led1;
}
}
}
}
10.进制转换16进制转换2进制
16进制为什么经常使用?
4个2进制数就是16进制数的一个位,1000 1000就是0x88;
如何快速完成进制转换?
8 4 2 1 法 如1000就是8+0+0+0=8;
void Timer0Init(void) //10微秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xF7; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
这种定时器配置TMOD用到了 &= 和 |= 就是为了在配置定时器0的时间不影响定时器1,
清0时:不改变的数&1,要清0的位置&0
改变时:不改变的数|0,要改变的位置|1
11.中断系统
设置寄存器IE、IP和TCON
设置IE(开启哪个中断)、IP(中断优先级设置)寄存器和TCON(中断触发方式0或1到0)寄存器
中断使用后需要手动复位 INT0=1;
#include "reg52.h" //定时器控制LED1一直闪动 定时器控制LED2亮1秒且LED1不变化
sbit led1 = P3^7;
sbit led2 = P3^6;
int num; //计次
void tcon_and_tmod(void);
//void Timer_rountinr(void) interrupt 1 中断不要预编译
void Delay2000ms();
void main()
{
tcon_and_tmod(); //初始化定时器
while(1){ //每2秒另外一个灯就亮2秒
//int1=1;
Delay2000ms();
INT1=0;
}
}
void tcon_and_tmod(void) //定时器0初始化 和中断优先级设置
{
//配置定时器寄存器 //可以用软件生成代码
TMOD=0x01; //选择模式01的寄存器 16位
//TCON中的配置
TR0=1; //开始计次
TF0=0; //计数溢出标志位和中断请求标志位 进入中断会自动清零
IT1=0; //外部中断1低电平触发
TL0=0x00; //开始计数的低8位
TH0=0xDC; //开始计数的高8位
//配置IE
EA=1; //打开总中断
ET0=1; //定时器0的中断打开
EX1=1; //外部中断1打开 优先级应该低于定时器0的中断
//配置IP(中断优先级设置)
PX1=1; //外部中断1 优先级设置到高位 不同的设置时不一样的状态。
PT0=1; //定时器0中断1 优先级设置到高位
}
void Timer_rountinr(void) interrupt 1 //每10ms进入一次中断1 每0.5sLED反转一次
{
num++; //计次一次后配置寄存器
//TR0=1; //开始计数 可要可不要,赋值后不会变化
//TF0=0; //计数溢出标志位 ,进入中断会自动清零
TL0=0x00; //开始计数的低8位
TH0=0xDC; //开始计数的高8
if(num==50){
num=0; //如果计次到100次10ms则LED翻转 num重置
led1=~led1;
}
}
void Int1_Routine(void) interrupt 2 //IT1=0时促发
{
led2=0;
Delay2000ms(); //这是维持亮灯的状态
led2=1;
INT1=1; //因为不是开关不会自动复位,需要手动复位
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
12.IO口模拟PWM开发SG90
注意:STC-ICP定时器设置用于stc15用于STC89时不能用自动重装
//在延时的这个2s一直输出plus_width为3的脉冲波,才能控制舵机固定在一个角度
#include "reg52.h" //定时器控制LED1一直闪动 定时器控制LED2亮1秒且LED1不变化
sbit sim_PMW = P1^1;
int num; //计次
int plus_width;
void Timer0_Init(void); //0.5ms的定时器
void Delay2000ms();
void main()
{
Delay2000ms();
EA=1; //打开总中断
ET0=1; //打开定时器0的中断
Timer0_Init();
while(1){
plus_width=3;
num=0;
Delay2000ms(); //在延时的这个2s一直输出plus_width为3的脉冲波
plus_width=1;
num=0;
Delay2000ms(); //在延时的这个2s一直输出plus_width为1的脉冲波
}
}
void Timer0_Init(void) //500微秒@11.0592MHz 的定时器
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Timer_rountinr(void) interrupt 1 //每0.5ms进入一次中断1
{
num++;
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
if(num<=plus_width){ // 小于脉冲宽度时设置为高电平
sim_PMW=1;
}else{ //大于脉冲宽度时设置为低电平
sim_PMW=0;
}
if(num==40){ //满足一个总脉冲宽度重置
num=0;
}
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
13.超声波测距
注意:C语言中的数,2进制、8进制、16进制只是表示方法不同,但是其实表示一个数。
超声波测距仪:在超过某一特定时间(过长测量距离)会自动拉低输出回响电平,结束本次测量
测量得周期: 最好大于60ms不然发出波和接受波容易干扰
#include "reg52.h" //超声波控制LED
double time,distance;
sbit led1 = P3^6;
sbit led2 = P3^7;
sbit trig=P1^5;
sbit eoch=P1^6;
void Delay10us(); //10us的定时器
void Timer0_Init(void); //给一个定时器@11.0592MHz
void main()
{
//1,给trig一个触发信号
//2.当eoch变为1的时候开始计时
//3,当eoch变为0的时候结束计时
//4. 计算距离=时间X速度
Timer0_Init(); //配置定时器
while(1){
trig=0; //1,给trig一个触发信号
trig=1;
Delay10us();
trig=0;
while(eoch==0); //2.当eoch变为1的时候开始计时
TR0=1;
while(eoch==1); //3,当eoch变为0的时候结束计时
TR0=0;
TF0 =0; //清除TF0标志
time=(TH0*256+TL0)*1.085; //单位是s
distance=time*0.017; //单位是cm
if(distance<10){
led1=1;
led2=0;
}else{
led1=0;
led2=1;
}
TH0=0;
TL0=0;
}
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Timer0_Init(void) //100微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0; //设置定时初始值
TH0 = 0; //设置定时初始值
//TF0 = 0; //清除TF0标志
}
14.自动开盖垃圾桶
对于高灵敏度反应元件:如振动传感器(检测之后就复位了),需要用中断来检测。
现在仍然问题:无法做到在外部中断1中用定时器0的中断模拟PWM信号,代码如下:
#include "reg52.h" //超声波控制LED
double time,distance;
//int num; //计数
int plus_width;
int num=0;
sbit led1 = P3^6; //垃圾桶状态灯
sbit led2 = P3^7;
sbit trig=P1^5; //超声波控制信号
sbit eoch=P1^6;
sbit sim_pmw=P1^1; //舵机PWM
sbit beer=P2^0; //蜂鸣器
sbit vibrator=P2^2; //振动传感器
sbit lid_state=P3^3; //外部中断0 //盖子状态0是开,1是关
void Delay10us(); //延时10us
void Delay60ms(); //延时60ms
void Delay2000ms(); //延时2s
void Timer1_Init(void); //给一个定时器1@11.0592MHz
void Timer0_Init(void);
void distance_measure(void); //用超声波检测是否开盖
void vibration_measure(void); //用振动检测是否开盖
void main()
{
//1,给trig一个触发信号
//2.当eoch变为1的时候开始计时
//3,当eoch变为0的时候结束计时
//4. 计算距离=时间X速度
Timer1_Init(); //配置定时器1
Timer0_Init(); //配置定时器0
EA=1; //打开总中断
ET0=0; //定时器0中断
EX1=1; //打开外部中断0
IT1=0; // 外部中断0 低电平触发,需要手动复位
TR0=1;
while(1){
//ET0=0; //定时器0中断
distance_measure(); //用超声波检测是否进入外部中断0
vibration_measure(); //用振动检测 是否进入外部中断0
}
}
void distance_measure(void)
{
trig=0; //1,给trig一个触发信号
trig=1;
Delay10us();
trig=0;
while(eoch==0); //2.当eoch变为1的时候开始计时
TR1=1;
while(eoch==1); //3,当eoch变为0的时候结束计时
TR1=0;
TF1 =0; //清除TF0标志
time=(TH1*256+TL1)*1.085; //单位是s
distance=time*0.017; //单位是cm
if(distance<10){ //距离小于10cm 进入外部中断0
lid_state=0;
}else{ //距离大于10cm 可以什么都不做
//led1=0;
//led2=1;
}
TH1=0;
TL1=0;
Delay60ms(); //延迟60ms防止声波冲突
}
void vibration_measure(void)
{
if(vibrator==0){ //发生振动输出低电平 进入外部中断0
lid_state=0;
}
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Delay60ms() //@11.0592MHz
{
unsigned char i, j;
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Timer1_Init(void) //@11.0592MHz 计算距离
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0; //设置定时初始值
TH1 = 0; //设置定时初始值
//TF1 = 0; //清除TF0标志
}
void Timer0_Init(void) //0.5ms@11.0592MHz
{
//定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
TF0 = 0; //清除TF0标志
//TR0 = 1; //定时器0开始计时
}
void Timer0_rountinr(void) interrupt 1 //每0.5ms进入一次定时器0的中断
{
//led1=0;
//led2=0;
num++;
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
if(num<=plus_width){ // 小于脉冲宽度时设置为高电平 plus_width就是角度1是0度,4是135度
sim_pmw=1;
}else{ //大于脉冲宽度时设置为低电平
sim_pmw=0;
}
if(num==40){ //满足一个总脉冲宽度重置
num=0;
}
}
void Int1_Routine(void) interrupt 2 //进入外部中断1之后
//PWM输出转为135度(进入定时器0) 2s 后又回到0度保持
// plus_width就是角度1是0度,4是135度
{
//TR0 = 1; //定时器0开始计时 开始模拟PWM
plus_width=4; //开始转到135度保持 就是开盖
led1=0;
led2=1;
ET0=1; //打开定时器0中断 就可以进入产生脉冲
Delay2000ms(); //延迟2s后
ET0=0; //关闭定时器0中断 进行设置
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
plus_width=1; //开始转到0度保持 就是关盖
led1=1;
led2=0; //管盖之后灯也关掉
ET0=1; //打开定时器0中断 就可以进入产生脉冲
Delay2000ms(); //延迟2s后
ET0=0; //打开定时器0中断 就可以进入产生脉冲
//TR0 = 0;
INT1=1; //进入中断后手动复位
}
换一种写法如下:
#include "reg52.h" //超声波控制LED
double time,distance;
//int num; //计数
int plus_width,vibration;
int num=0;
sbit led1 = P3^6; //垃圾桶状态灯
sbit led2 = P3^7;
sbit trig=P1^5; //超声波控制信号
sbit eoch=P1^6;
sbit sim_pmw=P1^1; //舵机PWM
sbit beer=P2^0; //蜂鸣器
sbit vibrator=P3^2; //振动传感器
sbit wireless_a=P2^1; //无限遥控a,按下的时候是高电平,会自动复位
void Delay10us(); //延时10us
void Delay60ms(); //延时60ms
void Delay2000ms(); //延时2s
void Timer1_Init(void); //给一个定时器1@11.0592MHz
void Timer0_Init(void);
void distance_measure(void); //用超声波检测是否开盖
void vibration_measure(void); //用振动检测是否开盖
void main()
{
//1,给trig一个触发信号
//2.当eoch变为1的时候开始计时
//3,当eoch变为0的时候结束计时
//4. 计算距离=时间X速度
Timer1_Init(); //配置定时器1
Timer0_Init(); //配置定时器0
EA=1; //打开总中断
ET0=1; //定时器0中断
EX0=1; //打开外部中断0 //用振动检测 是否产生了振动 进入外部中断 vibration=1
IT0=0; // 外部中断0 低电平触发,需要手动复位
while(1){
//ET0=0; //定时器0中断
distance_measure(); //用超声波检测是否到达某个距离 distance <10
//vibration_measure();
if(distance<10 || vibration==1 || wireless_a==1){ //距离小于10cm vibration=1 开盖
beer=0;Delay60ms();Delay60ms();beer=1; //喇嘛鸣响
plus_width=4;led1=0;led2=1; //开盖
Delay2000ms();
plus_width=1;led1=1;led2=0; //关盖
}else{ //距离大于10cm 可以什么都不做
//led1=0;
//led2=1;
}
//超声波复位
TH1=0;
TL1=0;
Delay60ms(); //延迟60ms防止声波冲突
//振动复位
INT0=1;
vibration=0;
}
}
void distance_measure(void)
{
trig=0; //1,给trig一个触发信号
trig=1;
Delay10us();
trig=0;
while(eoch==0); //2.当eoch变为1的时候开始计时
TR1=1;
while(eoch==1); //3,当eoch变为0的时候结束计时
TR1=0;
TF1 =0; //清除TF0标志
time=(TH1*256+TL1)*1.085; //单位是s
distance=time*0.017; //单位是cm
}
void Delay10us() //@11.0592MHz
{
unsigned char i;
i = 2;
while (--i);
}
void Delay60ms() //@11.0592MHz
{
unsigned char i, j;
i = 108;
j = 145;
do
{
while (--j);
} while (--i);
}
void Delay2000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 15;
j = 2;
k = 235;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Timer1_Init(void) //@11.0592MHz 计算距离
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0; //设置定时初始值
TH1 = 0; //设置定时初始值
//TF1 = 0; //清除TF0标志
}
void Timer0_Init(void) //0.5ms@11.0592MHz
{
//定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void Timer0_rountinr(void) interrupt 1 //每0.5ms进入一次定时器0的中断
{
num++;
TL0 = 0x33; //设置定时初始值
TH0 = 0xFE; //设置定时初始值
if(num<=plus_width){ // 小于脉冲宽度时设置为高电平 plus_width就是角度1是0度,4是135度
sim_pmw=1;
}else{ //大于脉冲宽度时设置为低电平
sim_pmw=0;
}
if(num==40){ //满足一个总脉冲宽度重置
num=0;
}
}
void Int0_Routine(void) interrupt 0
{
vibration=1;
}