51单片机基础C程序架构作者:u010785142keil基本步骤:(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 codedofly_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 codedofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};// 显示段码值01234567unsigned 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,若是时钟频率为12MHzTL0 = 0x00; //则8192μs中断一次;注:2的13次方是8192ET0=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 0110TL0=0x06; //计数值为250,若为12MHz,相当于250us;EA=1; //总中断打开ET0=1; //定时器中断打开TR0=1; //定时器开关打开}void Timer0_isr(void) interrupt 1 using 1{n=n++; //没中断一次,n+1,每两次中间间隔250usif(n=40)//n=40,就是中断40次,相当于250*4=10ms{n=0;m++; //每10ms,m+1if(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. 定时器2voidTIM2Inital(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;elseLED=1;}一个led状态转换:while (1) //主循环{if(!KEY) //如果检测到低电平,说明按键按下{DelayMs(10); //延时去抖,一般10-20msif(!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计数到255EA=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计数到255EA=1; //总中断打开ET1=1; //定时器中断打开TR1=1; //定时器开关打开}R. 看门狗溢出测试sfr WDTRST = 0xA6;voidRst_Watchdog( void ) //喂狗{WDTRST = 0x1E; //先赋值1E 然后赋值E1WDTRST = 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_B1DelayMs(Speed);Coil_C1DelayMs(Speed);Coil_D1DelayMs(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,技术论坛: 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,技术论坛:请在发送区输入任意信息"); 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,技术论坛: thank you!");DelayMs(240);//延时循环发送DelayMs(240);}}。