1、软件安装:
2、单片机(Micro Controller Unit)MCU:
内部集成了cpu,RAM,ROM,定时器,中断系统,通讯接口,
用于信息采集处理硬件设备控制;
8051内核所以带了这个内核的单片机都叫51单片机(是所有兼容8031指令系统的单片机统称);
本次学习使用的单片机信息:
3、单片机命名规则:
4、单片机89c52结构
5、LED
TTL电平:高电平5V,低电平0V
寄存器(p2)以8个为单位分组,对应八个IO口,cpu通过配置寄存器的01来控制IO口的高低电平;
编程中用16进制表示,
点亮LED:单片机的晶振不会停,P2会一直工作;
增加循环,让程序停止在循环里,P2不会重复工作;
LED闪烁:
LED流水灯:
独立按键控制LED:
按键开关的抖动:
按键消抖:可以用触发器过滤,也可以延迟消抖;
独立按键控制LED表示二进制:
独立按键控制LED移位
数码管:
共阴极和共阳极数码管原理图,本次学习使用共阴极数码管;
双向缓冲器(把单片机的驱动信号改成控制型号,利用缓冲器去放大高电平达到更好的驱动效果):
LE是跳线帽:把LE接到高电平VCC,LE接到高电平,就保证P00(A0)输入什么B0就接收什么;
(在单片机中高电平的驱动能力有限,其输出的最大电流不能太大,低电平驱动能力强一些,所以采用低电平点亮LED。)
电容正常为皮法拉(pf):
采用电容来保证电路的稳定(电源滤波);
一般情况下数码管由于IO输入端的接口为相同值,所以一般显示相同的值,要实现动态数码管显示可以采用快速切换数字的方法,由于晶振频率的原因,程序执行一遍人眼看不出来,所以可以通过不断执行程序语句来实现动态数码管显示;
74LS138译码器:输入为CBA,输出为Y0-Y7;
静态数码管代码实现:
数码管消影(在段选后把段选清零):
位选,段选,...()
清零方法在段选后延迟1ms,然后把P0清零;
数码管的驱动方式:
代码实现:
#include <REGX52.H>
#include <../headFile/at89c52.h>
unsigned char NixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7c,0x39,0x5E,0x79,0x71,0x00};
void Nixie(unsigned char Location,Number){
switch(Location)
{
case 1: //
P2_4 = 1;
P2_3 = 1;
P2_2 = 1;
break;
case 2: //LED7
P2_4 = 1;
P2_3 = 1;
P2_2 = 0;
break;
case 3: //LED6
P2_4 = 1;
P2_3 = 0;
P2_2 = 1;
break;
case 4: //LED5
P2_4 = 1;
P2_3 = 0;
P2_2 = 0;
break;
case 5: //LED4
P2_4 = 0;
P2_3 = 1;
P2_2 = 1;
break;
case 6: //LED3
P2_4 = 0;
P2_3 = 1;
P2_2 = 0;
break;
case 7: //LED2
P2_4 = 0;
P2_3 = 0;
P2_2 = 1;
break;
case 8: //LED1
P2_4 = 0;
P2_3 = 0;
P2_2 = 0;
break;
}
P0 = NixieTable[Number];
Delay(1);
P0 = 0x00;
}
void main()
{
while(1){
Nixie(1,0);
Nixie(2,1);
Nixie(3,2);
Nixie(4,3);
Nixie(5,4);
Nixie(6,5);
Nixie(7,6);
Nixie(8,7);
}
}
模块化编程(提高代码可阅读性,可维护性,可移植性):
LCD1602调试工具:
使用LCD后会和数码管和LED引脚冲突:
#include <REGX52.H>
#include "LCD1602.h"
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
c语言预编译:
矩阵键盘(为了减少IO口的占用):
矩阵按键每一行类似于独立按键;
如果逐行扫描在51开发板中矩阵键盘的IO口会和其他外设产生引脚冲突,例如矩阵键盘的P15口就与蜂鸣器的BZ口冲突,逐行扫描后会导致蜂鸣器一直响;
矩阵键盘密码锁:
IO口的模式(模式可以定制):
准双向口输入配置:
弱上拉,强下拉(单片机高电平驱动能力弱)单片机的IO口可以双向输出输入,假如一个IO口给1在独路给0,也不会短路,
高电平的驱动能力弱于低电平驱动能力;
开漏输出:
扫描:
数码管扫描(输出扫描)
原理:显示第一位——显示第二位——显示第三位——。。。快速循环这个过程,最终实现数码管同时显示的效果;
矩阵键盘(输入扫描)
原理:读取第一行(列)——读取第二行列——读取第三行列。。。快速循环这个过程,最终实现所有按键同时检测的效果;
(视频显示像素点如果不采用矩阵扫描会导致需要大量的IO口,采用矩阵扫描可以极大节约IO口数量)
以上扫描的共性:节省IO口;
定时器:
51单片机定时器属于单片机内部资源,电路连接和运转均在单片机内部完成;
用于计时系统,软件计时,使得程序固定时间完成一项操作;
替代长时间Delay,提高cpu运行效率和处理速度;
多任务执行:把程序切换成时间片穿插在一条时间线上;
STC89C52有(T0,T1,T2)三个定时器,T0与T1与传统的51兼容,T2时此型号单片机增加的资源;
定时器在单片机内部根据时钟的输出信号,每隔固定时间段,计数器单元的数值就增加1,计数器单元值增加的设定的提醒时间时,计数单元就会向中断系统发出中断申请,产生提醒,使程序跳转到中断服务函数中执行;
时钟(提供计数单元的时钟脉冲) ----------计数单元(时钟计数)---------中断系统(产生中断,执行定时任务)
定时器的工作模式:
STC89C52的T0和T1都有的四种工作模式:
模式0:13位定时器/计数器
模式1:16位定时器/计数器(常用)
模式2:8位自动重装模式
模式3:两个8位计数器
高字节TH,低字节TL;这个计数器只能数到(0---65535);unsigned int
溢出时计数器会清零,并申请中断;只有两个字节存定时器计数情况;
定时器时钟:
该时钟可以由内部引脚提供(该计数器就是时钟),也可以从外部引脚提供(该计数器就充当计数器的功能)
SYSclk:系统时钟,即晶振周期,本开发板12MHZ
在振荡器中采用一个特殊的元件——石英晶体,可以产生高度稳定的信号,这种采用石英晶体的振荡器称为晶体振荡器。
定时器中断系统:
高优先级中断可以打断低优先级中断;
中断相当于可以同时执行多个任务;
STC89C52中断资源:
单片机通过配置寄存器来控制内部线路的连接,通过内部线路的不同连接方式来实现不同的功能;
按键控制LED控制流水灯模式:
定时器开启,12M的晶振1微秒加一,加到最大值才产生中断;总共定时65535微秒;
64535离计数器溢出差值1000,所以计时时间为1ms,(给计数器赋值,多次产生中断,直到达到目标时间);
1、配置TMOD(寄存器)工作模式
配置定时器0的低四位;
TMOD是不可寻址寄存器,如果只给低四位赋值时,只用一个定时器的情况下没有影响,如果要用两个定时器,只赋值低四位会导致赋值刷新另一个定时器的参数IO,导致定时器功能无法正常使用;(可以用与或赋值,避免上述情况)
TMOD = TMOD & 0xF0;//把TMOD的低四位清零,高四位保持不变
TMOD = TMOD | 0x01;//把TMOD的低四位置一,高四位不变;
让定时器在模式1工作所以在M1给0,M0给1,C/T给0,GATE给0,
M1和M0共同决定定时器的工作模式,门控端GATE给0就是int0单独控制;
可位寻址寄存器可以对每一位单独赋值,不可位寻址的寄存器不能单独赋值,只能单独赋值;
TF0 = 0;把TF0清零防止配置好就产生中断;
TR0 = 1;定时器是否开启,TR0给1允许T0开始计数,TR0=0时禁止T0计数;(GATE = 0);
IE0 ;控制外部中断引脚,
IT0 ;控制外部中断引脚,
配置中断:
ET0 = 1 ;
EA =1;
PT0 =0;
配置好中断后写入中断任务子函数(中断子程序):