OpenVINOTM,给你看得见的未来!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » 菜鸟学习单片机

共7条 1/1 1 跳转至

菜鸟学习单片机

助工
2013-09-19 14:15:11    评分

1.点灯程序

#include <reg52.h>

sbit D1=P1^0;

void main (){

while(1){

D1=0;

}

}

分析:

⑴#include <reg52.h>是包含的头文件

⑵sbit是位定义,D1就相当于P1^0接口

注意:有很多人问我P1^0后面的0能不能用 变量代替,然后对变量+1或者-1来选择 不同引脚,想法很好,但是不允许这样定义。

⑶while(1){……}是一个大循环,一直在执行里面的代码,因为单片机主程序一般都是有 一个while死循环,这是单片机程序特有的特性。以保证单片机程序运行时不跳出程序, 或者说不让程序结束。以便做一些重复性自动控制。你向单片机烧录完程序以后做成 成品不可能仅仅让其执行一次或者你一直等着他执行完手动复位,这样就失去了意义。

⑷D1=0;就是对D1赋值0,也就是让P1^0输出低电平,点亮二极管。

注意:输出低电平点亮二极管还是输出高电平点亮二极管要取决于你电路板的二极管的 接法。

总结:

单片机实际就是对其引脚的控制

写程序的一般步骤:

1.先看原理图,弄明白元器件是怎么连接的,如何赋值。

2.选择你所需要的引脚,P0口(P0^0……P0^7

  P1口(P1^0……P1^7

  P2口(P2^0……P2^7

  P3口(P3^0……P3^7

如果用八位则不需要位定义,P0就代表P0^0……P0^7

3.用你的排线连接好单片机与元器件

4.根据硬件连接来编写程序。

2.静态数码管显示

#include<reg52.h>

#define uint unsigned int

#define uchar unsigned char

uchar code table[]={

0xc0,0xf9,0xa4,0xb0,

0x99,0x92,0x82,0xf8,

0x80,0x90,0x88,0x83,

0xc6,0xa1,0x86,0x8e};

void delay(uint z){

uint x,j;

for (x=z;x>0;x--)

for (j=110;j>0;j--);

}

void main(){

uint i;

for (i=0;i<=15;i++)

{

P1=table[i];

delay(200);

}

}

分析:

⑴uchar code table[]={

  0xc0,0xf9,0xa4,0xb0,

  0x99,0x92,0x82,0xf8,

  0x80,0x90,0x88,0x83,

  0xc6,0xa1,0x86,0x8e};

  是一个数组,数组中的元素是共阳极(给低电平点亮)的数码管的段码,通过对数组table的下标改变来选取0~F的段码,通过P1口送到静态显示的八个接入端。

⑵void delay(){……}函数是一个延时函数。

⑶P1=table[i];因为控制数码管需要八根线,所以选择P1口(P1^0~P1^7),每一根线输出八位二进制数用来决定数码管的哪一段点亮,如图

例如上面程序段码,共阳极是给低电平点亮,当P1=table[0] 时候,即P1=0xc0,即P1^0~P1^7分别等于1100 0000,就是GH段不点亮,其余的都点亮,就显示0.

⑷共阴极数码管段码表

uchar code table[]={

0x3f,0x06,0x5b,0x4f,

0x66,0x6d,0x7d,0x07,

0x7f,0x6f,0x77,0x7c,

0x39,0x5e,0x79,0x71};

 

 

 

 

 

3.按键

#include<reg52.h>

#define uchar unsigned char

#define uint unsigned int 

uchar temp;

delay(uint);

void main ()

{

while(1)

{

temp=P1;

delay(100);//去抖动

if (P1==0xff);

else

{

switch(temp)

{

case 0x7f:P2=0xf9;break;

case 0xbf:P2=0xa4;break;

case 0xdf:P2=0xb0;break;

case 0xef:P2=0x99;break;

case 0xf7:P2=0x92;break;

case 0xfb:P2=0x82;break;

case 0xfd:P2=0xf8;break;

case 0xfe:P2=0x80;break;

}

}

}  

}

delay(uint z)

{uint x,y;

for (y=z;y>0;y--)

for (x=100;x>0;x--);

}

分析:

temp=P1;用P1作为输入口,接收按键传来的八位二进制数,不同的按键按下P1口八个引脚接收的键值是不同的(按下是低电平),即temp值不同,通过不同的键值来选择执行case后面的对应的段码,并将段码送到P2口,并让静态数码管显示。

 

 

 

 

 

4.动态数码管显示(秒表0~999)

#include<reg52.h>

#define uint unsigned int

#define uchar unsigned char

uchar num,a,b,s,g; 

uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

void delay(uint z)

{

uint x,y;

for (x=z;x>0;x--)

for (y=100;y>0;y--);

}

void display(uint b,uint s,uint g)

{

P2=0xfe;

P1=table[g];

delay(1);

 

P2=0xfd;

P1=table[s];

delay(1);

 

P2=0xfb;

P1=table[b];

delay(1);

}

void init ()

{

EA=1;//开总中断

ET0=1;//启动定时器0中断

TR0=1;//启动定时器

TMOD=0x01;

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;

num=0;

a=0;

}

void timar() interrupt 1

{

TH0=(65536-50000)/256;

TL0=(65536-50000)%256;

a++;

if (a==20)

{

a=0;

num++;

}

 

}

void main ()

{

init();

while(1)

{ b=num/100;

s=num%100/10;

g=num%10;

display(b,s,g);


}


}

分析:

⑴动态显示主要就是利用人眼的视觉感来设计的,一般来说如果显示的频率过慢,则会有断断续续的显示;如果显示的频率加快,则人眼就分辨不出这种视觉残余。例如display函数中延时函数delay的数值是给的很小的,就是为了让人眼就分辨不出,产生视觉残余现象。

所谓的位选就是选择哪一个数码管工作,段选就是让位选中的工作的数码管显示什么数字。例如上述程序display中

P2=0xfe

因为0xfe二进制为 11111110 即选择最右边的数码管亮,这既是位选

P1=table[g];

通过查表的方式通过g的值来选择数组 table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};中第几个元素,g的值是在主函数中g=num%10得来的,例如g=0 则选择table[0],则这个时候P1=0x3f,即最右边的数码管显示0,这既是段选。  

因为我给P2口赋值时候(P2=0xfe;P2=0xfd;P2=0xfb;) 的延时程序非常短(delay(1);)所以给人的视觉效果是三个数码管一起亮的,而P1口的作用是让其显示190P2口的作用是让右边三个数码管亮,效果如图:

⑵中断

①定义:就是cpu在执行一个程序时,发生了一件事情,要求cpu停止现在的程序去处理这件事情,处理完成后返回原来停止的地方继续执行

 

如图

  80C51的中断系统有5个中断源,2个优先级,可实现二级中断嵌套 。

 

中断函数void timar() interrupt 1interrupt后面的标号对应着不同的中断源是不同的,上面的源程序用的是定时/计数器0中断,所以标号为1

中断程序执行是在void init ()初始化函数执行后和主函数一起执行,相互不会影响,当达到中断的条件时,即设置的初值减到0的时候进入中断(上述代码是50ms,也就是每当50ms减到0时候进入中断,每进入中断一次执行一次a++,直到a=20,即20*50ms=1s的时候,num++,完成每间隔一秒+1),处理中断函数里面的代码。

定时/计数器初始化的过程:

TMOD赋值,以确定T0T1的工作方式。

计算初值,并将其写入TH0TL0TH1TL1

中断方式时,则对IE赋值,开放中断。

使TR0TR1置位,启动定时/计数器定时或计数。

例如

EA=1;//开总中断

ET0=1;//启动定时器0中断

TR0=1;//启动定时器

TMOD=0x01;

 

 

 

 

 

 

 

 

 

5.DS18B20

#include "stdio.h"

#include "reg51.h"

#include "intrins.h"                            

#define  uchar unsigned char

#define  uint  unsigned int

sbit     DQ=P3^7;  //温度输入口

sbit     cold=P1^0; //制冷

sbit     hot=P1^1; //制热 

sbit beep = P1^5;  //蜂鸣器

char  sss;

bit  bbb;                         

uint  temp=0; //初始化temp0

uchar data temp_data[]={0x00,0x00};          //读出温度暂放

//*************初始化函数******//

void init()

{

SCON=0x50;    //设置串口为模式1且允许接收 

TMOD=0X21;    //设置定时器1为模式2和模式1

TH1=0xfd;                         //设置波特率为9600

TL1=0xfd;

TH0 = (65535 - 50000) / 256;   //设置初值

TL0 = (65535 - 50000) % 256;

PCON&=0x00; //不设置电源控制寄存器

TR1=1;   //定时器1开启

TI=1;

ET0 = 1;    //定时器0中断允许

EA = 1;   //开中断

 

}

/***********11微秒延时函数**********/

//

void delay(uint t)

{

for(;t>0;t--);

}  

/*蜂鸣器延时*/

void sleep(int ms)    

{

    int i,j;

    for(j=ms;j > 0; j--)  

for(i = 0; i < 80; i++);

}

/***********18B20复位函数**********/

void ow_reset(void)

{

char presence=1;

while(presence)

{

while(presence)

{

DQ=1;

_nop_();

_nop_();

DQ=0;    

delay(50);  // 550us

DQ=1;     

delay(6);  // 66us

presence=DQ;  // presence=0继续下一步

   }

delay(45);     //延时500us

presence = ~DQ;

}

DQ=1;

/**********18B20写命令函数*********/

/*向 1-WIRE 总线上写一个字节*/

void write_byte(uchar val)

{

uchar i;

for (i=8; i>0; i--) 

{

DQ=1;

_nop_();

_nop_(); 

DQ = 0;

_nop_(); //5us

_nop_();

_nop_();

_nop_();

_nop_();

DQ = val&0x01;       //最低位移出

delay(6);            //66us

val=val/2;           //右移一位

}

DQ = 1;

delay(1); 

}

//

/*********18B201个字节函数********/

/*从总线上读取一个字节*/

uchar read_byte(void)

{

uchar i;

uchar value = 0;

for (i=8;i>0;i--)

{

DQ=1;

_nop_();

_nop_();

value>>=1;

DQ = 0;             

_nop_(); //延时4us

_nop_();

_nop_();

_nop_();   

DQ = 1;  

_nop_();   //延时4us

_nop_();

_nop_();

_nop_();    

if(DQ)value|=0x80;

delay(6);            //66us

}

DQ=1;

return(value);

}

//

/***********读出温度函数**********/

//

void read_temp()

{

ow_reset();      //总线复位

write_byte(0xCC);   // Skip ROM命令

write_byte(0xBE);  // 发读命令

temp_data[0]=read_byte();  //温度低8

temp_data[1]=read_byte();  //温度高8

ow_reset();

write_byte(0xCC);  // Skip ROM

write_byte(0x44);  // 发转换命令

delay(40000);

}

/***********温度数据处理函数**********/

void work_temp()

{

 

temp=(temp_data[0]>>4)|(temp_data[1]<<4);

/*判断温度上限,下限*/

if(temp>=19)

{

cold=1;  

bbb=1;

TR0 = 1;

}

else

 cold=0;

if(temp<=14)

{

hot=1;  

bbb=1;

TR0 = 1;

}

else

hot=0;

printf("%d\n",temp); 

while(!TI);

}

/**************主函数****************/

main()

{

char  i;

hot=0;

cold=0;

init();

ow_reset();        // 开机先转换一次

write_byte(0xCC);  // Skip ROM

write_byte(0x44);  // 发转换命令

delay(10000);

while(1)

{

read_temp();                               //读出18B20温度数据

work_temp();                               //处理温度数据

if (bbb)                         //蜂鸣器发出声.

        bbb = 0;

        for (i = 0; i < 80; i++) {

          beep = ~beep;

          sleep(1);

        }

      }

}

}

/*中断函数*/

void TIMER0() interrupt 1

{

    TH0 = (65535 - 50000) / 256;  

    TL0 = (65535 - 50000) % 256;

    sss++;

    if (sss == 10)                        //蜂鸣器发声的频率

 {

      sss = 0;

    }

}

分析:上述程序中有关于18b20的一些操作是需要根据技术手册来编写程序的,类似的还有很多,比如红外遥控模块,1602显示模块……需要根据技术手册来初始化,这里就不多说了。

 

 

 

希望这篇文档对大家学习单片机有帮助!!!




关键词: 菜鸟     学习     单片机     程序     执行     数码     显示     tab    

高工
2013-09-19 14:51:19    评分
2楼
楼主可以把工程打包上传,供大家参考用,多谢分享,期待更多的精彩内容

助工
2013-09-19 15:27:04    评分
3楼
好的,以后打包了

高工
2013-09-19 17:16:22    评分
4楼
多谢楼主分享,多多交流哦

高工
2013-09-19 22:44:14    评分
5楼

楼主这个帖子内容好多。建议楼主可以这样:文字部分可以直接在帖子里写出,代码部分直接打包共享工程。楼主这个帖子里有中断部分,我感觉这部分是51里面的重点,可以再发一贴详细讲解一下。


助工
2013-09-20 09:32:36    评分
6楼
等过几天吧,我这几天有点忙。

助工
2013-09-28 12:07:30    评分
7楼

这个必顶学习支持


共7条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]