keil基本步骤:
(1)新建一个工程:Project——New uVision Project
(2)选择型号:AT89C52
(3)新建.c文件:File——new——a.c
保存为:.c后缀
(4)添加.c文件:
(5)编写程序
以下是基本程序架构:很重要的笔记
A. 基本程序框架:(点亮小灯)
#include<reg52.h>
sbit LED=P1^0;
void main (void)
{
LED=0; // P1 = 0xfe;
while (1)
{
//空循环
}
}
B. for循环语句:(小灯循环点亮)
unsigned char i;
for(i=0;i<10;i++)
{;}
执行顺序:i=0; —— i<10; —— {}中的内容—— i++ ——。。。
C. 左移/右移(小灯逐个往左/右移动亮)
P1=0xfe; // P1=0x7f;
for(i=0;i<8;i++) //加入 for循环,表明for循环大括号中的程序循环执行8次
{
Delay(50000);
P1<<=1; //P1>>=1;
}
循环左移/右移:
for(i=0;i<8;i++) //加入 for循环,表明for循环大括号中的程序循环执行8次
{
Delay(50000);
P1<<=1; //P1>>=1;
P1=P1|0x01; //P1=P1|0x80;
//左移后,最右端自动赋值0,所以需要该语句赋值1
}
P1=0xfe; //P1=0x7f; //重新赋初始值
D. 数组的使用:
unsigned char code table[]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe, 0xff,0xff,0x00,0x00, 0x55,0x55,0xaa,0xaa};
for(i=0;i<16;i++)
{
P1=table[i];
Delay(3000);
}
E. PWM调光
sbit LED0=P1^0;
void main (void)
{
unsigned int CYCLE=600,PWM_LOW=0;
while(1)
{
LED0=1;
Delay(6000);
for(PWM_LOW=1;PWM_LOW<CYCLE;PWM_LOW++)
{
LED0=0;
Delay(PWM_LOW);
LED0=1;
Delay(CYCLE-PWM_LOW);
}
LED0=0;
for(PWM_LOW=CYCLE-1;PWM_LOW>0;PWM_LOW--)
{
LED0=0;
Delay(PWM_LOW);
LED0=1;
Delay(CYCLE-PWM_LOW);
}
}
F. 共阳数码管显示(循环显示数字)
unsignedchar code dofly_table[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,};
P1=dofly_table[i];
{
P1=dofly_table[i];
Delay(60000);
}
G. 独立按键(按键控制数码管显示数字) switch选择语句
switch(P3) //P3口作为独立按键输入端
{
case0xfe:P1=dofly_table[1];break;
case 0xfd:P1=dofly_table[2];break;
default:break;
}
H. 数码管静态显示:位锁存与段锁存(8位数码管显示其中之一/二)
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
sbit LATCH1=P2^2;//定义锁存使能端口 段锁存
sbit LATCH2=P2^3;// 位锁存
main()
{
while(1)
{
DataPort=0xfe; //取位码 第一位数码管选通,即二进制1111 1110 之一
// DataPort=0x7e; //取位码 第一位数码管选通,即二进制0111 1110 之二
LATCH2=1; //位锁存
LATCH2=0;
DataPort=0x4F; //取显示数据,段码 “3”共阴字符码
LATCH1=1; //段锁存
LATCH1=0;
}
}
I. 数码管动态显示:位锁存与段锁存
#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换
sbit LATCH1=P2^2;//定义锁存使能端口 段锁存
sbit LATCH2=P2^3;// 位锁存
unsignedchar code dofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
// 显示段码值01234567
unsigned char codedofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
//分别对应相应的数码管点亮,即位码
main()
{
unsigned char i=0;
while(1)
{
DataPort=dofly_WeiMa[i]; //取位码
LATCH2=1; //位锁存
LATCH2=0;
DataPort=dofly_DuanMa[i]; //取显示数据,段码 //DataPort=dofly_DuanMa[num+i];
LATCH1=1; //段锁存
LATCH1=0;
Delay(200); //扫描间隙延时,时间太长会闪烁,太短会造成重影
i++;
if(8==i) //检测8位扫描完全结束?如扫描完成则从第一个开始再次扫描8位
i=0;
}
}
J. 定时器0
(1)模式0(13位计数器)
void Init_Timer0(void) //初始化
{
TMOD &= 0xF0; //定时器0运行在模式0,13位计数器
// GATE0=0;C/T0#=0; M1=0; M0=0;
TH0 = 0x00; //设置初值0x00,所以计数值为8192,若是时钟频率为12MHz
TL0 = 0x00; //则8192μs中断一次 ;注:2的13次方是8192
ET0=1; //允许定时器0中断
EA=1; //允许总中断
TR0=1; //启动定时器0
}
voidTimer0_isr(void) interrupt 1
{ }
(2)模式1(16位定时器) 重要
void Init_Timer0(void) //初始化
{
TMOD |= 0x01; //模式1,16位定时器,使用"|"符号在使用多个定时器时不受影响
TH0=0x00; //给定初值,这里用定时器最大值从0开始计数一直到65535溢出
TL0=0x00; //一次最多可定时65.536ms
// TH0=(65535-50000)/256=0x3c=60(10); //50ms
// TL0=(65535-50000)%256=0xb0=176(10);
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
void Timer0_isr(void) interrupt 1 using 1
{
TH0=0x00; //重新赋值
TL0=0x00;
LED=~LED; //定时到了:执行的内容
}
注:时间计算
51单片机1个机器周期=12个时钟周期,频率为12MHZ,则一个机器周期为1US,具体到定时器程序就是,假如你想定1MS,那么单片机每次加一个一,就要过1US,那么1MS就要加1000次,所以用65535-1000=64535;再把64535换成16进制为FC17,把FC付给TH0,17给TLO,即可定时1MS,因为65535他就溢出进入中断。
(3)模式2(8位重装模式)
void Init_Timer0(void) //初始化
{
TMOD &= 0xF0; //模式2,8位重装模式
TMOD|=0x0A; //GATE0=1;C/T0#=0; M1=1; M0=0;
TH0=0x06; //设定初值;0x06= 0000 0110
TL0=0x06; //计数值为250,若为12MHz,相当于250us;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
void Timer0_isr(void) interrupt 1 using 1
{
n=n++; //没中断一次,n+1,每两次中间间隔250us
if(n=40) //n=40,就是中断40次,相当于250*4=10ms
{
n=0;
m++; //每10ms,m+1
if(m==100) //m=100,相当于10ms*100=1s
{m=0;
}
}
(4)模式3
定时器0工作于方式3 时,占用了定时器1的TR1和TF0。
K. 定时器1(功能与定时器0一样,定时器1还可以用做串口的波特率发生器)
voidInit_Timer1(void) //初始化
{
TMOD |= 0x10; //模式1,16位定时器,使用"|"符号在使用多个定时器时不受影响
TH1=0x00; //给定初值,这里用定时器最大值从0开始计数一直到65535溢出
TL1=0x00;
// TH0=(65535-50000)/256=0x3c=60(10); //50ms
// TL0=(65535-50000)%256=0xb0=176(10);
EA=1; //总中断打开
ET1=1; //定时器中断打开
TR1=1; //定时器开关打开
}
void Timer1_isr(void) interrupt 3 using 1
{
TH1=0x00; //重新赋值
TL1=0x00;
LED=~LED; //LED闪烁
}
L. 定时器2
voidTIM2Inital(void) //初始化
{
RCAP2H = (65536-60000)/256; //晶振12M 60ms 16bit 自动重载
RCAP2L = (65536-60000)%256;
ET2=1; //打开定时器中断
EA=1; //打开总中断
TR2=1; //打开定时器开关
}
void TIM2(void) interrupt 5 using 1//定时器2中断
{
TF2=0;
LED=~LED; //执行函数
}
M. 产生方波(定时器0模式1)
(1)产生1ms方波
sbitOUT=P1^2;
voidInit_Timer0(void)
{
TMOD |= 0x01;//使用模式1,16位定时器
//TH0=0x00; //给定初值,这里使用定时器最大值从0开始计数一直到65535溢出
//TL0=0x00;
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
main()
{
Init_Timer0();
while(1);
}
void Timer0_isr(void) interrupt 1 using 1
{
TH0=(65536-500)/256; //重新赋值 12M晶振计算,指令周期1uS,
TL0=(65536-500)%256; //1mS方波半个周期500uS,即定时500次
//溢出然后输出端取反
OUT=~OUT; //用示波器可看到方波输出
}
(2)产生200ms方波
void Timer0_isr(void)interrupt 1 using 1
{
staticunsigned char i;
TH0=(65536-10000)/256; //重新赋值 12M晶振计算,指令周期1uS,
TL0=(65536-10000)%256; //直接定时器不够用,定时10ms,然后循环10次
i++;
if(i==11)
{
i=0;
OUT=~OUT; //用示波器可看到方波输出
}
}
N. 独立按键(控制一个led)
KEY=1; //按键输入端口电平置高
while (1) //主循环
{
if(!KEY) //如果检测到低电平,说明按键按下
LED=0;
else
LED=1;
}
一个led状态转换:
while (1) //主循环
{
if(!KEY) //如果检测到低电平,说明按键按下
{
DelayMs(10); //延时去抖,一般10-20ms
if(!KEY) //再次确认按键是否按下,没有按下则退出
{
while(!KEY);//如果确认按下按键等待按键释放,没有释放则一直等待
{
LED=!LED;//释放则执行需要的程序
}}}}
O. 外部中断0(P3^2)
main()
{
P1=0x55; //P1口初始值
EA=1; //全局中断开
EX0=1; //外部中断0开
IT0=0; //电平触发
//IT0=1; //边沿触发
while(1)
{
//在此添加其他程序
}
}
voidISR_Key(void) interrupt 0 using 1
{
P1=~P1; //进入中断程序执行程序,
//此时可以通过 EA=0指令暂时关掉中断
}
P. 外部中断1(P3^3)
main()
{
LED=0; //LED灯点亮
EA=1; //全局中断开
EX1=1; //外部中断0开
IT1=0; //T1=0表示电平触发
// IT1=1; //IT1=1表示边沿触发
while(1)
{
//在此添加其他程序
}
}
void ISR_INT1(void) interrupt 2
{
if(!INT1)
{
DelayMs(10);//在此处可以添加去抖动程序,防止按键抖动造成错误
if(!INT1)
while(!INT1);//等待按键释放
{
LED=!LED;
}
}
}
Q. T0/T1外部计数输入
voidInit_Timer0(void)
{
TMOD |= 0x01 | 0x04; //模式1,16位计数器,用"|"符号在使用多个定时器时不受影响
TH0=0xFF; //给定初值
TL0=245; //从245计数到255
EA=1; //总中断打开
ET0=1; //定时器中断打开
TR0=1; //定时器开关打开
}
main()
{
Init_Timer0();
while(1);
}
void Timer0_isr(void) interrupt 1 using 1
{
TH0=0xFF; //重新给定初值
TL0=245;
LED=~LED; //指示灯反相,可以看到闪烁
}
void Init_Timer1(void)
{
TMOD |= 0x10 | 0x40; //模式1,16位计数器,用"|"符号用多个定时器时不受影响
TH1=0xFF; //给定初值
TL1=245; //从245计数到255
EA=1; //总中断打开
ET1=1; //定时器中断打开
TR1=1; //定时器开关打开
}
R. 看门狗溢出测试
sfr WDTRST = 0xA6;
voidRst_Watchdog( void ) //喂狗
{
WDTRST = 0x1E; //先赋值1E 然后赋值E1
WDTRST = 0xE1;
}
void main( void)
{
int i; // 设置看门狗时间为1个时钟循环后
Rst_Watchdog(); //关看门狗一个时钟循环
for( i = 0; i < 500; i++)
{
Rst_Watchdog();
}
P1=0x00;
while(!key) //按下按键不松开,表示程序一直在按键处循环,
//并用LED显示0x55
{
P1=0x55; //模拟出错 正常情况应该一直显示LED,
//但是加看门狗之后不间断复位,倒是LED闪烁
}
}
S. 步进电机转动原理
sbit A1=P1^0; //定义步进电机连接端口
sbit B1=P1^1;
sbit C1=P1^2;
sbit D1=P1^3;
#define Coil_A1 {A1=1;B1=0;C1=0;D1=0;}//A相通电,其他相断电
#define Coil_B1 {A1=0;B1=1;C1=0;D1=0;}//B相通电,其他相断电
#define Coil_C1 {A1=0;B1=0;C1=1;D1=0;}//C相通电,其他相断电
#define Coil_D1 {A1=0;B1=0;C1=0;D1=1;}//D相通电,其他相断电
#define Coil_OFF {A1=0;B1=0;C1=0;D1=0;}//全部断电
unsigned char Speed;
main()
{
//unsigned int i=64*16; //转2周停止
Speed=5; //调整速度
while(1)
{
Coil_A1 //遇到Coil_A1 用{A1=1;B1=0;C1=0;D1=0;}代替
DelayMs(Speed); //改变这个参数可以调整电机转速 ,
//数字越小,转速越大,力矩越小
Coil_B1
DelayMs(Speed);
Coil_C1
DelayMs(Speed);
Coil_D1
DelayMs(Speed);
}
}
T. 串口通讯
voidInitUART (void)
{
SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收
TMOD |= 0x20; //TMOD: timer 1, mode 2, 8-bit 重装
TH1 = 0xFD; // TH1: 重装值 9600 波特率 晶振 11.0592MHz
TR1 = 1; // TR1: timer 1 打开
EA = 1; //打开总中断
//ES = 1; //打开串口中断
}
void SendByte(unsigned char dat) //发送一个字节
{
SBUF = dat;
while(!TI);
TI = 0;
}
void SendStr(unsigned char *s) //发送一个字符串
{
while(*s!='\0')// \0 表示字符串结束标志,
//通过检测是否字符串末尾
{
SendByte(*s);
s++;
}
}
void main (void)
{
InitUART();
while (1)
{
SendStr("UART test,技术论坛:www.doflye.net thankyou!");
DelayMs(240);//延时循环发送
DelayMs(240);
}
}
U. 串口通讯中断
voidInitUART (void)
{
SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收
TMOD |= 0x20; //TMOD: timer 1, mode 2, 8-bit 重装
TH1 = 0xFD; // TH1: 重装值 9600 波特率 晶振 11.0592MHz
TR1 = 1; // TR1: timer 1 打开
EA = 1; //打开总中断
// ES = 1; //打开串口中断
}
void main (void)
{
InitUART();
SendStr("UART test,技术论坛:www.doflye.net 请在发送区输入任意信息");
ES = 1; //打开串口中断
while (1)
{ }
}
void UART_SER (void) interrupt 4 //串行中断服务程序
{
unsigned char Temp; //定义临时变量
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
Temp=SBUF; //读入缓冲区的值
P1=Temp; //把值输出到P1口,用于观察
SBUF=Temp; //把接收到的值再发回电脑端
}
if(TI) //如果是发送标志位,清零
TI=0;
}
V. RS485通讯原理(与串口通讯类似)
voidmain (void)
{
InitUART();
Ctrl_EN=1; //发送模式(多了这条设定)
while (1)
{
SendStr("UART test,技术论坛:www.doflye.net thank you!");
DelayMs(240);//延时循环发送
DelayMs(240);
}
}