51单片机学习笔记(1)
认识单片机
单片机又称单片微控制器,它不是完成某一个逻辑功能的芯片,而是把一个计算机系统集成到一个芯片上。相当于一个微型的计算机,和计算机相比,单片机只缺少了I/O设备。概括的讲:一块芯片就成了一台计算机。它的体积小、质量轻、价格便宜、为学习、应用和开发提供了便利条件。
单片机的使用领域已十分广泛,如智能仪表、实时工控、通讯设备、导航系统、家用电器等。
单片机也被称为单片微控器,属于一种集成式电路芯片。在单片机中主要包含CPU、只读存储器ROM和随机存储器RAM等,多样化数据采集与控制系统能够让单片机完成各项复杂的运算,无论是对运算符号进行控制,还是对系统下达运算指令都能通过单片机完成。 由此可见,单片机凭借着强大的数据处理技术和计算功能可以在智能电子设备中充分应用。简单地说,单片机就是一块芯片,这块芯片组成了一个系统,通过集成电路技术的应用,将数据运算与处理能力集成到芯片中,实现对数据的高速化处理。
工具
51单片机,keil5(编写程序),stc-isp(下载程序),单片机驱动程序。
基本程序框架与逻辑运算
#include <REGX52.H>//此处引入的头文件是对应单片机外部设备的,没有这个头文件,编写的程序就没法编译。
void main()
{
while(1)//一般需要单片机一直执行代码,所以用while(1)使得循环一直进行下去。
{
}
}
然后是一些逻辑运算符
+ //加
- //减
* //乘
/ //除
% //取余
= //赋值
> //大于
< //小于
== //等于
<= //小于等于
>= //大于等于
!= //不等于
&& //与
|| //或
! //非
/*---------------------------*/
按位右移
0011 0100 >>1 (按位右移一位) -> 0001 1010
按位左移
0011 0100 <<2 (按位左移两位)-> 1101 0000
按位与
0011 0100 & 0010 1010
(两个数的每一位分别比较,都是1,该位结果为1,否则为0)
0011 0100
0010 1010 -> 0010 0000
按位或
0011 0100 | 0010 1010
(两个数的每一位分别比较,都是0,该位结果为0,否则为1)
0011 0100
0010 1010 -> 0011 1110
按位异或
0011 0100 ^ 0010 1010
(两个数的每一位分别比较,如果相同,该位结果为0,否则为1)
0011 0100
0010 1010 -> 0001 1110
按位取反
~0010 1010
(原来为1,取反为0,原来为0,取反为1)
~0010 1010 -> 1101 0101
延时函数
延时函数就是让单片机在设定的某段时间内歇着,什么也不做,等时间段结束再开始执行下一步指令。
原理即是根据单片机的运算速度,设定一定次数的无意义运算来占用时间。在stc-isp中有相应的小工具帮助我们进行延时设置。
#include <REGX52.H>
#include<INTRINS.H>//由于小工具提供的延时函数中用到了nop函数,所以我们需要在这里添加intrinsic.h的头文件
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void main()
{
while(1)
{
}
}
以上是一个延时500ms的函数,但我们可以对这个函数稍微做些改动,使它适用于更多的场景。
#include <REGX52.H>
//在实际操作中根据对时间精度的需求,我们可以考虑删去nop函数,因此也不需要intrins.h头文件了
void Delay1ms(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms)
{
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
xms--;
}
//以1ms为单位的延时函数,只需要在后续调用函数时给一个值即可实现一函数多用
}
外部设备
单片机上有独立的一列LED,一共八个,根据开发板原理图,此列LED对应着的是P2寄存器。
在keil中打开REGX52.H头文件,我们可以找到其中各部分对应的位置。
REGX52.H头文件内容
/*--------------------------------------------------------------------------
AT89X52.H
Header file for the low voltage Flash Atmel AT89C52 and AT89LV52.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __AT89X52_H__
#define __AT89X52_H__
/*------------------------------------------------
Byte Registers
------------------------------------------------*/
sfr P0 = 0x80;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr P1 = 0x90;
sfr SCON = 0x98;
sfr SBUF = 0x99;
sfr P2 = 0xA0;
sfr IE = 0xA8;
sfr P3 = 0xB0;
sfr IP = 0xB8;
sfr T2CON = 0xC8;
sfr T2MOD = 0xC9;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
/*------------------------------------------------
P0 Bit Registers
------------------------------------------------*/
sbit P0_0 = 0x80;
sbit P0_1 = 0x81;
sbit P0_2 = 0x82;
sbit P0_3 = 0x83;
sbit P0_4 = 0x84;
sbit P0_5 = 0x85;
sbit P0_6 = 0x86;
sbit P0_7 = 0x87;
/*------------------------------------------------
PCON Bit Values
------------------------------------------------*/
#define IDL_ 0x01
#define STOP_ 0x02
#define PD_ 0x02 /* Alternate definition */
#define GF0_ 0x04
#define GF1_ 0x08
#define SMOD_ 0x80
/*------------------------------------------------
TCON Bit Registers
------------------------------------------------*/
sbit IT0 = 0x88;
sbit IE0 = 0x89;
sbit IT1 = 0x8A;
sbit IE1 = 0x8B;
sbit TR0 = 0x8C;
sbit TF0 = 0x8D;
sbit TR1 = 0x8E;
sbit TF1 = 0x8F;
/*------------------------------------------------
TMOD Bit Values
------------------------------------------------*/
#define T0_M0_ 0x01
#define T0_M1_ 0x02
#define T0_CT_ 0x04
#define T0_GATE_ 0x08
#define T1_M0_ 0x10
#define T1_M1_ 0x20
#define T1_CT_ 0x40
#define T1_GATE_ 0x80
#define T1_MASK_ 0xF0
#define T0_MASK_ 0x0F
/*------------------------------------------------
P1 Bit Registers
------------------------------------------------*/
sbit P1_0 = 0x90;
sbit P1_1 = 0x91;
sbit P1_2 = 0x92;
sbit P1_3 = 0x93;
sbit P1_4 = 0x94;
sbit P1_5 = 0x95;
sbit P1_6 = 0x96;
sbit P1_7 = 0x97;
sbit T2 = 0x90; /* External input to Timer/Counter 2, clock out */
sbit T2EX = 0x91; /* Timer/Counter 2 capture/reload trigger & dir ctl */
/*------------------------------------------------
SCON Bit Registers
------------------------------------------------*/
sbit RI = 0x98;
sbit TI = 0x99;
sbit RB8 = 0x9A;
sbit TB8 = 0x9B;
sbit REN = 0x9C;
sbit SM2 = 0x9D;
sbit SM1 = 0x9E;
sbit SM0 = 0x9F;
/*------------------------------------------------
P2 Bit Registers
------------------------------------------------*/
sbit P2_0 = 0xA0;
sbit P2_1 = 0xA1;
sbit P2_2 = 0xA2;
sbit P2_3 = 0xA3;
sbit P2_4 = 0xA4;
sbit P2_5 = 0xA5;
sbit P2_6 = 0xA6;
sbit P2_7 = 0xA7;
/*------------------------------------------------
IE Bit Registers
------------------------------------------------*/
sbit EX0 = 0xA8; /* 1=Enable External interrupt 0 */
sbit ET0 = 0xA9; /* 1=Enable Timer 0 interrupt */
sbit EX1 = 0xAA; /* 1=Enable External interrupt 1 */
sbit ET1 = 0xAB; /* 1=Enable Timer 1 interrupt */
sbit ES = 0xAC; /* 1=Enable Serial port interrupt */
sbit ET2 = 0xAD; /* 1=Enable Timer 2 interrupt */
sbit EA = 0xAF; /* 0=Disable all interrupts */
/*------------------------------------------------
P3 Bit Registers (Mnemonics & Ports)
------------------------------------------------*/
sbit P3_0 = 0xB0;
sbit P3_1 = 0xB1;
sbit P3_2 = 0xB2;
sbit P3_3 = 0xB3;
sbit P3_4 = 0xB4;
sbit P3_5 = 0xB5;
sbit P3_6 = 0xB6;
sbit P3_7 = 0xB7;
sbit RXD = 0xB0; /* Serial data input */
sbit TXD = 0xB1; /* Serial data output */
sbit INT0 = 0xB2; /* External interrupt 0 */
sbit INT1 = 0xB3; /* External interrupt 1 */
sbit T0 = 0xB4; /* Timer 0 external input */
sbit T1 = 0xB5; /* Timer 1 external input */
sbit WR = 0xB6; /* External data memory write strobe */
sbit RD = 0xB7; /* External data memory read strobe */
/*------------------------------------------------
IP Bit Registers
------------------------------------------------*/
sbit PX0 = 0xB8;
sbit PT0 = 0xB9;
sbit PX1 = 0xBA;
sbit PT1 = 0xBB;
sbit PS = 0xBC;
sbit PT2 = 0xBD;
/*------------------------------------------------
T2CON Bit Registers
------------------------------------------------*/
sbit CP_RL2= 0xC8; /* 0=Reload, 1=Capture select */
sbit C_T2 = 0xC9; /* 0=Timer, 1=Counter */
sbit TR2 = 0xCA; /* 0=Stop timer, 1=Start timer */
sbit EXEN2= 0xCB; /* Timer 2 external enable */
sbit TCLK = 0xCC; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */
sbit RCLK = 0xCD; /* 0=Serial clock uses Timer 1 overflow, 1=Timer 2 */
sbit EXF2 = 0xCE; /* Timer 2 external flag */
sbit TF2 = 0xCF; /* Timer 2 overflow flag */
/*------------------------------------------------
T2MOD Bit Values
------------------------------------------------*/
#define DCEN_ 0x01 /* 1=Timer 2 can be configured as up/down counter */
#define T2OE_ 0x02 /* Timer 2 output enable */
/*------------------------------------------------
PSW Bit Registers
------------------------------------------------*/
sbit P = 0xD0;
sbit F1 = 0xD1;
sbit OV = 0xD2;
sbit RS0 = 0xD3;
sbit RS1 = 0xD4;
sbit F0 = 0xD5;
sbit AC = 0xD6;
sbit CY = 0xD7;
/*------------------------------------------------
Interrupt Vectors:
Interrupt Address = (Number * 8) + 3
------------------------------------------------*/
#define IE0_VECTOR 0 /* 0x03 External Interrupt 0 */
#define TF0_VECTOR 1 /* 0x0B Timer 0 */
#define IE1_VECTOR 2 /* 0x13 External Interrupt 1 */
#define TF1_VECTOR 3 /* 0x1B Timer 1 */
#define SIO_VECTOR 4 /* 0x23 Serial port */
#define TF2_VECTOR 5 /* 0x2B Timer 2 */
#define EX2_VECTOR 5 /* 0x2B External Interrupt 2 */
#endif
LED模块
由于51单片机的寄存器是八位的,所以P2寄存器中的八位二进制分别对应着P2_0,P2_1,P2_2,···P2_7,每一位要么是零,要么是一,对应着LED的两个状态,亮和灭。如果给的是0就是亮,给1就是灭。
为了方便与八位的二进制对应,一般采用十六进制来赋值,需要在十六进制数前加上0x来表示十六进制。
注意第一个LED对应的是P2_0,第八个对应的是P2_7。
单独LED的亮灭
因此我们可以通过改变P2寄存器的内容来控制LED的亮灭:
P2=0xFE; //我们使P2为1111 1110,也就是第一位为0,其他为1。所以这时第一个LED会亮起,其他保持熄灭状态。
P2_0 = 0x00; //单独给P2_0赋值为0,就能单独控制第1个LED灯亮起来,其他LED状态不变
LED闪烁
LED闪烁实际上就是LED的亮灭和延时函数结合起来即可,让LED亮起来,歇一会,再亮。
while(1){
P2=0XFE; //1111 1110
Delay(100ms);
P2=0XFD; //1111 1101
Delay(100ms);
P2=0XFB; //1111 1011
Delay(100ms);
P2=0XF7; //1111 0111
Delay(100ms);
}
独立按键模块
单片机左下角有一排四个的独立按键,通过这四个按键可以做一些通过按键来触发的程序。
根据开发板原理图,独立按键所对应的是P3寄存器的前四位。(可参照前文中的REGX52.H头函数内容)
通过按键来做事显然要判断按键按下与否,即使用if语句。但由于51单片机上的4个独立按键是松开就会立刻弹起的,所以我们应该用两层判断来确定按键的一次按下。
不过如果用if的级联,则会导致下达的指令只被执行一次,而51单片机的运算速度相对还是较快的,一闪而过的结果显然不那么合理,所以我们可以再放一个while的空循环,而while本身自己会带有一层判断。
if(P3_1==0){
while (P3_1==1)
{
//再在这里面放指令即可
}
}
按键消抖
对于机械开关,当机械触点断开闭合的时候,由于机械触点的弹性作用,一个开关在闭合的时候不会马上稳定地接通,在断开时也不会一下子断开,即在开关闭合和断开的瞬间会伴随一连串的抖动,这一连串的抖动会对我们的操作带来一定的影响,所以我们可以通过一些延时函数来避免这样的干扰。
if(P3_1==0){
Delay(20);//消除按下按键的抖动
while(P3_1==0);
Delay(20); //消除松开按键的抖动
while (P3_1==1)
{
}
}
LED数码管模块
LED数码管区别于LED点列的是它可以显示数字,而不单是点,利用LED数码管,可以做一些数字的输出。
相比LED点列,LED数码管也更加复杂。
LED数码管组件共有八个数码管,从LED1~LED8,这八个数码管与74HC138编译器中的P22、P23、P24相对应;每个数码管里面还有八个LED,分别是a、b、c、d、e、f、g、dp,这八个LED与74HC245中的P0寄存器相对应。
如果想要对数码管进行操作,则需要是先选中我们要用的数码管,然后亮起LED。这意味着需要我们同时控制着选择数码管的P22、P23、P24和控制LED的P0。
根据74HC138编译器的原理图,发现它用三位的二进制来控制着八个数码管。三位的二进制数一共是八个,每一个对应一个数码管:
P2_4=1; P2_3=1; P2_2=1; //LED8
P2_4=1; P2_3=1; P2_2=0; //LED7
P2_4=1; P2_3=0; P2_2=1; //LED6
P2_4=1; P2_3=0; P2_2=0; //LED5
P2_4=0; P2_3=1; P2_2=1; //LED4
P2_4=0; P2_3=1; P2_2=0; //LED3
P2_4=0; P2_3=0; P2_2=1; //LED2
P2_4=0; P2_3=0; P2_2=0; //LED1
//选择对应数码管,只需要改变P22、P23、P24的值即可。
亮起LED(静态)
根据LED原理图发现八位的寄存器P0对应着这八个LED。和之前的LED模块不同,数码管LED对应寄存器值为1是亮起,0是熄灭。
以下是a、b、c、d、e、f、g、dp与八位寄存器对应关系:
while(1){
P2_4=0;
P2_3=0;
P2_2=0;
P0=0100 1111;
{
动态数码管显示
当需要在不同数码管上面显示不同的数字时要用到动态数码管显示。
由于人类有视觉暂留现象,最高分别能力是二十四分之一秒,而单片机速度很快,可以让单片机以较短的频率在不同数码管中分别显示数字,这样就可以达到在不同数码管中显示数字的目的了。
不过在做动态数码管显示时也要像前面一样通过延时函数(短)“消抖”,不然数字会出现重影。
unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x3E,0x40};//0,1,2,3,4,5,6,7,8,9,U,-;
void Nixie(unsigned char Location,Number)
{
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[Number];
Delay(1);
P0=0x00;
//在调用该Nixie函数的结尾实现了“消抖”
//同时该展示出来的函数具备简化在主函数中控制数码管的操作,只需调用函数即可实现大部分数值显示需求
}