文章目录
基础
最小系统
复位电路
头文件
如上在KEIL编程中并没有使用到头文件 ,如果没有加"sfr P2 =0x80;"这一句,就需要添加头文件"reg52.h"了
巧用宏定义
#define HC(x,y) {P2=P2&0x1f|(x<<5);P0=y;P2=P2&0x1f;}
时钟
振荡周期:也称时钟周期(频率的倒数),单片机提供时钟信号的振荡源周期,频率一般有11.0592MHz,12MHz等
状态周期:是时钟周期的2倍,
机器周期:是包含6个状态周期,机器周期=1/单片机时钟频率
单片机时钟频率:是外部时钟的12分频,如果是12MHz的晶振,机器周期=1/单片机时钟频率=1/(12MHz/12)=12/12M=1us
定时器
TMOD是定时器、计数器模式控制寄存器,它是一个逐位定义的8位寄存器,但只能使用字节寻址。
GATE:门控制
GATE=0: 仅由TR0,TR1置位分别启动定时器T0、T1
GATE=1:由外部中断引脚INT0、INT1来启动定时器T0、T1
当INT0引脚为高电平时TR0置位,启动定时器T0;
当INT1引脚为高电平时TR1置位,启动定时器T1
C/T:定时器和计数器选择位,0为定时器,1为计数器
定时
这里一个机器周期为1us,若定时时间为1ms,则需要1000个机器周期,计算出初值;如果机器周期为2us,则只需要500个机器周期。
定时器初值计算:初值=(65536-机器周期数量)
数码管
共阳极:
位选为高电平(即1)选中数码管,
各段选为低电平(即0接地时)选中各数码段。
uchar code table[]={
0xc0,//0
0xf9,//1
0xa4,//2
0xb0,//3
0x99,//4
0x92,//5
0x82,//6
0xf8,//7
0x80,//8
0x90,//9
0x88,//A
0x83,//B
0xc6,//C
0xa1,//D
0x86,//E
0x8e, //F
0x8c, //P
0xc1,//U
0x91,//Y
0x7c,//L
0x00,//全亮
0xff //熄灭
};
共阴极:
位选为低电平(即0)选中数码管,
各段选为高电平(即1接+5V时)选中各数码段。
uchar code leddata[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0x76, //"H"
0x38, //"L"
0x37, //"n"
0x3E, //"u"
0x73, //"P"
0x5C, //"o"
0x40, //"-"
0x00 //熄灭
};
使用数码管小技巧
void display()
{
static u8 d1;
HC(7,0xff);
HC(6,1<<d1);
HC(7,led[znx[d1]]);
d1==7?d1=0:d1++;
}
在定时器服务函数中调用该函数,就可以实现八位数码管的循环点亮;
Keil
编程
优先级的问题
if(P1&0x0f !=0x0f) 是错的
改正后 if((P1&0x0f) !=0x0f)
使用问题
项目
串口通信
串口中断要有定时器T1参加,因为C51是用定时器1来产生波特率的。
因此就需要给 定时器T1 设置初值。
例程1
单片机发送数据到电脑
#include "reg52.h"
void UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //串口方式1
TMOD = 0x20; // 定时器使用方式2自动重载
TH1 = 0xFD; //9600波特率对应的预设数,定时器方式2下,TH1=TL1
TL1 = 0xFD;
TR1 = 1;//开启定时器,开始产生波特率
}
void delay(unsigned int n)
{
while (n--);
}
void main(void)
{
UartInit();
P0 =0x00;
while(1)
{
SBUF = 0x30; //把数据放到SBUF中
while (TI == 0);//未发送完毕就等待
TI = 0; //发送完毕后,要把TI重新置0
delay(20000) ;
}
}
例程2
#include "stdio.h"
#include "stdarg.h"
#include "reg52.h"
void UartInit(void) //9600bps@11.0592MHz
{
SCON = 0x50; //串口方式1
TMOD = 0x20; // 定时器使用方式2自动重载
TH1 = 0xFD; //9600波特率对应的预设数,定时器方式2下,TH1=TL1
TL1 = 0xFD;
TR1 = 1;//开启定时器,开始产生波特率
}
void delay(unsigned int n)
{
while (n--);
}
void UsartPrintf(char *fmt,...)
{
unsigned char UsartPrintfBuf[30];
va_list ap;
unsigned char *pStr = UsartPrintfBuf;
va_start(ap, fmt);
vsprintf((char *)UsartPrintfBuf, fmt, ap);
va_end(ap);
while(*pStr != 0) {
SBUF = *pStr++ ;
while(TI == 0);
TI = 0;
}
}
void main(void)
{
int inumber = 30;
char string[4] = "qy";
UartInit();
P0 =0x00;
while(1)
{
UsartPrintf("yy%d %s", inumber, string);
inumber++ ;
delay(20000) ;
delay(20000) ;
delay(20000) ;
delay(20000) ;
}
}
SCON
串口工作方式寄存器SCON
RI:接收中断标志位,数据接收结束时,标志位会自动置1,需要通过程序将其置0
TI:发送中断标志位,数据发送结束时,标志位会自动置1,需要通过程序将其置0
RB8:存放发送数据的第9位
TB8:存放接收数据的第9位
REN:串行接收允许位,0允许串行接收,1禁止串行接收
SM2:多机控制位
SM1,SM0:串行工作方式
其中, fosc为单片机的时钟频率;波特率指串行口每秒钟发送(或接收)的位数。
波特率计算
如果SCON已经确定,波特率取决于晶振频率
当串口工作在工作方式0和2是,波特率固定,方式0时fosc/12;方式2时fosc/32或fosc/64(根据SMOD判断)。
当串口工作在方式1时,定时器1工作在方式2时,波特率=(2^SMOD/32)*(单片机时钟频率/(256-X)),X是初值 。
可以使用波特率计算软件提取码: g8cc
设置定时计数器1的工作方式
TMOD寄存器
高四位为定时计数器1的设置,串口通信波特率设置占用定时计数器1,D7置0,D6置0 。
LCD1602
举个栗子 :比如我们需要在LCD1602的第二排第一个位置显示数字6,在程序中,我们先要初始化, 写指令函数(参数为0xc0),写数据函数(‘数字6的ASCII码’)//注意十六进制,END。
初始化(三条必加指令):写指令0x38(功能指令);写指令0x06(模式设置指令);写指令0x0c(显示开关控制指令);延时大概1ms;
写指令函数:指令控制字给P0口;RS=0;RW=0;(参数设定)EN=0;EN=1;延时1ms;EN=0;(模拟有效时序段)
写数据函数:数据给P0口;RS=1;RW=0;EN=0;EN=1;延时1ms;EN=0;
清屏指令 writecmd(0x01)
写第一行的第一个 writecmd(0x80) 写第二行的第一个 writecmd(0xc0)