• 回复
  • 收藏
  • 点赞
  • 分享
  • 发新帖

花一个星期搞定C语言到51,PIC单片机基础程序,分享下自己写的1602LCD驱动以及18B20的驱动程序

花了1周时间从零基础搞定C语言基础,再通过51单片机程序仿真--PIC开发板实物操作,以下是本人学习过程中编写的一个源程序

以下源程序是基于MPLAB IDE 8.73开发平台,编译器是PICC。

其它编译器大同小异,

这是一个1602LCD的驱动显示程序和DS18B20的驱动,此源程序实用性不大,只是给初学者一个参考,

温度显示范围从-55度-+125度,精确到0.1度。单片机为PIC16F877A,晶振16MHZ

对于初学者来说,自己学会编写一个驱动程序,也是一件快乐的事情。

********************************************************************************************************

#include               //调用PIC16F87XA单片机的头文件

__CONFIG(HS&WDTDIS&LVPDIS);


//////////////////////////////////////////////////////////////

/****************LCD1602液晶显示相关函数*********************/

//////////////////////////////////////////////////////////////

//1602液晶相关I/O设置

#define E RB3                   //1602液晶的E脚接在RB3口上

#define RW RB4                //1602液晶的RW脚接在RB4口上

#define RS RB5                 //1602液晶的RS脚接在RB5口上

//////////////////////////////////////////////////////////////

/****************DS18B20温度检测相关函数*********************/

//////////////////////////////////////////////////////////////

unsigned char temp1=0;         //正负温度判断变量,temp1为1时表示负温,为0时标识正温

float temp=125.0;                 //温度变量,默认125度

/***********************短延时函数***************************/

/*16MHZ晶振下,FOSE/4分频,单个周期是0.25US*/

/*延时时间为:(10个固定周期2.5US)+(次数*【1.5US(5+1个周期)】=总延时时间)*/

void Delay(unsigned char t)            //跳入这个循环需要3个指令周期

{                                                //跳出这个循环需要3个指令周期

     while(t>0)                              //while第一次判断语句要消耗3个指令周期,之后需要5个

         t--;                                     //t=t-1,消耗1个指令周期

}                                                //也就是说t=0时需要10个指令周期

/***********************长延时函数***************************/

/*a=6,c=72时是1MS延时*/

void Time(unsigned int a)              //跳入这个循环需要3个指令周期

{                                               //跳出这个循环需要3个指令周期

     unsigned int b,c;                    //定义int型变量需要2个周期,这里有2个所以需要4个周期

     asm("NOP");

     asm("NOP");

     for(b=0;a>b;b++)                  //第一次需要10个指令周期,之后需要11个指令周期

                                                //定义int型变量需要2个指令周期,判断需要9个指令周期,++操作需要1个指令周期

     for(c=72;c>0;c--) ;                //定义int需要2个指令周期,判断需要7个指令周期,--操作需要1个指令周期(C=85,a=1是200US)

}                                              //--循环体内部需要2+8*N+N个指令周期,也就是0.5US+(2US*N)+(0.25US*N)+NPLL(0.25US)

//////////////////////////////////////////////////////////////

/****************LCD1602液晶显示相关函数*********************/

//////////////////////////////////////////////////////////////

/*******************LCD1602忙检测函数************************/

void LCD1602_busy(void)

{

     TRISD7=1;                                     //将RD7口设置为输入口,为读做准备

     RS=0;                                            //RS=0、RW=1、E=1时,忙信号输出到DB7,由RD7读入

     RW=1;                                           //RS=0、RW=1、E=1时,忙信号输出到DB7,由RD7读入

     E=1;                                              //RS=0、RW=1、E=1时,忙信号输出到DB7,由RD7读入

     while(RD7==1);                              //由RD7读入1,表示1602液晶忙,需要等待

     E=0;                                             //读完以后,恢复E的电平

     TRISD7=0;                                    //将RD7口设置为输出口

}

/************LCD1602写指令函数(不带忙检测)*****************/

void LCD1602_Write_com(unsigned char combuf)

{

    RS=0;                                               //选择指令寄存器

    RW=0;                                              //选择写状态

    PORTD=combuf;                                //将命令字通过RD口送至DB

    E=1;                                                //E高电平将命令字写入1602液晶

    Delay(0);                                          //添加一个空操作,使高电平至少维持1us

    E=0;                                                //写完以后,恢复E的电平

}

/*************LCD1602写指令函数(带忙检测)******************/

void LCD1602_Write_com_busy(unsigned char combuf)

{

    LCD1602_busy();                             //调用忙检测函数

    LCD1602_Write_com(combuf);          //调用忙检测函数

}

/*************LCD1602写数据函数(带忙检测)******************/

void LCD1602_Write_data_busy(unsigned char databuf)

{      

    LCD1602_busy();                           //调用忙检测函数     

    RS=1;                                          //选择数据寄存器     

    RW=0;                                        //选择写状态     

    PORTD=databuf;                          //将命令字通过RD口送至DB     

    E=1;                                          //E高电平将命令字写入1602液晶     

    Delay(0);                                    //添加一个空操作,使高电平至少维持1us     

    E=0;                                          //写完以后,恢复E的电平

/*****************LCD1602操作指定地址函数********************/

void LCD1602_Write_address(unsigned char x,unsigned char y)

     x&=0x0f;                                                             //列地址限制在0-15间

     y&=0x01;                                                            //行地址限制在0-1间

     if(y==0)                                                              //如果是第一行

         LCD1602_Write_com_busy(x|0x80);                   //将列地址写入

     else                                                                    //如果是第二行

         LCD1602_Write_com_busy((x+0x40)|0x80);        //将列地址写入

}

 /**********************LCD1602初始化*************************/

void LCD1602_init(void)

{

      Time(6);                                              //延时1MS

      LCD1602_Write_com(0x38);                   //8位数据总线,两行显示模式,5*7点阵显示

     Time(1);                                               //延时约167US

     LCD1602_Write_com(0x38);                    //8位数据总线,两行显示模式,5*7点阵显示

     Time(1);                                               //延时约167US

     LCD1602_Write_com(0x38);                   //8位数据总线,两行显示模式,5*7点阵显示

     LCD1602_Write_com_busy(0x38);           //8位数据总线,两行显示模式,5*7点阵显示

     LCD1602_Write_com_busy(0x08);           //显示功能关,无光标 

     LCD1602_Write_com_busy(0x01);           //清屏

     LCD1602_Write_com_busy(0x06);           //写入新的数据后,光标右移,显示屏不移动

     LCD1602_Write_com_busy(0x0C);           //显示功能开,无光标

}

 /***************LCD1602显示指定地址内容函数******************/

void LCD1602_Disp(unsigned char x,unsigned char y,unsigned char buf)

{

      LCD1602_Write_address(x,y);               //先将地址信息写入

      LCD1602_Write_data_busy(buf);           //再写入要显示的数据

}

/*********************LCD固定内容显示函数********************/

void Fixed_display(void)

{

    LCD1602_Disp(2,0,'D');

    LCD1602_Disp(3,0,'S');

    LCD1602_Disp(4,0,'1');

    LCD1602_Disp(5,0,'8');

    LCD1602_Disp(6,0,'B');

    LCD1602_Disp(7,0,'2');

    LCD1602_Disp(8,0,'0');

    LCD1602_Disp(2,1,'T');

    LCD1602_Disp(3,1,'e');

    LCD1602_Disp(4,1,'m');

    LCD1602_Disp(5,1,'p');

    LCD1602_Disp(6,1,':');

    LCD1602_Disp(11,1,'.');

    LCD1602_Disp(13,1,0xdf);

    LCD1602_Disp(14,1,'C');              //0x43

}

/*********************LCD动态内容显示函数********************/

void Dynamic_Display(void)

{

     unsigned char a=0,b=0,c=0,d=0;

     if(temp1==1)                                     //限制负温温度不低于零下55度

     {

           if(temp>55.0) temp=55.0;

     }

     else if(temp>125.0) temp=125.0;        //限制正温温度不高于125度

     d=(unsigned char)temp/100;               //提取温度百位数值

     c=(unsigned char)temp%100/10;         //提取温度十位数值

     b=(unsigned char)temp%10;               //提取温度个位数值

     a=(unsigned char)(temp*10)%10;       //提取温度小数数值

     if(d==0)                                           //判断百位显示是否为0

     {

           if(temp1==1)                             //如果温度为零下摄氏度时

           {

                 if(c==0)                              //又如果温度不低于零下十度时

                 {

                     c='-'-0x30;                      //十位显示“-”负号

                     d='_';                             //百位显示“_”空格

                 }

                else d='-'-0x30;                     //百位显示“-”负号

           }

           else                                         //否则温度为正常摄氏度

           {

                d='_';                                 //百位显示“_”空格

                if(c==0)  c='_';                    //又如果十位为零时,十位显示“_”空格

           }

     }

     if(a>9) a=9;

     LCD1602_Disp(8,1,d+0x30);               //显示温度百位数值

     LCD1602_Disp(9,1,c+0x30);               //显示温度十位数值

     LCD1602_Disp(10,1,b+0x30);             //显示温度个位数值

     LCD1602_Disp(12,1,a+0x30);             //显示温度小数数值

}

//////////////////////////////////////////////////////////////

/****************DS18B20温度检测相关函数*********************/

//////////////////////////////////////////////////////////////

/******************DS18B20上电复位函数***********************/

unsigned char Reset(void)

{

   unsigned char i;

   TRISA4=0;                                      //设置RA4位输出口

   RA4=0;                                           //拉低DQ总线开始复位

   Delay(200);                                     //设定602.5US,DS18B20规定RESET复位时间不低于480US,不大于960US

                                                        //定时180-240之间

   TRISA4=1;                                      //设置RA4为输入口,以释放总线等电阻拉高总线

   Delay(20);                                       //设定62.5US,DS18B20规定等待间隔时间为60-240US

                                                        //定时20-60之间

   if(RA4==0)                                     //读出数据并保存

   i=0;

   else i=1;

   Delay(200);                                    //维持约449.5US,符合总读时隙不低于480US的要求

   return i;                                         //k=0为复位成功,k=1为复位失败

}

/*******************DS18B20写字节函数************************/

void WriteByte(unsigned char dat)

{

    unsigned char i;

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

    {

        TRISA4=0;                                //设置RA4位输出口

        if(dat&0x01)                              //如果写1

        {

            RA4=0;                                  //拉低总线电平

            Delay(1);                                //维持约4US,符合大于1US小于15US的规范

            TRISA4=1;                             //设置RA4为输入口,以释放总线等电阻拉高总线

            Delay(39);                              //维持约61US,符合不低于60US的要求

        }

        else                                          //如果写0

        {

             TRISA4=0;                           //设置RA4位输出口

              RA4=0;                               //拉低

              Delay(39);                           //维持约61US,符合不低于60US的要求

              TRISA4=1;                          //设置RA4为输入口,以释放总线等电阻拉高总线

              Delay(0);                            //维持约2.5US,符合大于1US规范

         }

        dat=dat>>1;                            //写入字节右移1位

    }

}

/*******************DS18B20读字节函数************************/

unsigned char ReadByte(void)

{

    unsigned char i,buf=0;

    for(i=1;i<=8;i++)                          //接收8次还原一个字节数据

    {

        buf=buf>>1;                           //接收前,先将接收缓冲区右移

        TRISA4=0;                              //设置RA4位输出口

        RA4=0;                                   //拉低

        Delay(2);                                 //维持7US,符合大于1US小于15US的规范

        TRISA4=1;                              //设置RA4为输入口,以释放总线等电阻拉高总线 准备读

        Delay(2);                                 //维持约7US,符合大于1US小于15US的规范

        if(RA4==1) buf|=0x80;             //读出1位数据保存于buf中最高位

        Delay(33);                               //维持约52US,符合总读时隙不低于60US的要求

    }

    return buf;                                  //退出的同时将接收缓冲区参数返回

}

/******************DS18B20启动转换函数***********************/

unsigned char Convert(void)

 {

     if(Reset()==0)                             //如果复位成功

     {  Time(3); //延时0.5MS

         WriteByte(0xcc);                     // 跳过多器件识别

         WriteByte(0x44);                     // 启动温度转换

         return 1;

     }

     else

     {

        return 0;

     }

}

 /******************DS18B20读温度函数*************************/

void ReadFlash(void) 

{

    unsigned int i;

    unsigned char Lsb,Msb;

    if(Reset()==0)

     { 

         Time(3);                                  //延时0.5MS

         WriteByte(0xcc);                       // 跳过多器件识别

         WriteByte(0xbe);                      // 读暂存器

         Lsb=ReadByte();                       // 低字节

         Msb=ReadByte();                      // 高字节

         i=Msb;

         i<<=8;

         i=i|Lsb;

         if(Msb>0x07)                            //如果判断温度为零下摄氏度时

         {

             temp1=1;                            //把零下标识符temp1置1

             i=(~i)+1;                             //读取温度取反加1

             temp=(float)i*0.0625;           //转换为真实温度并把结果转换成浮点数

         }

         else

        {

            temp1=0;

            temp=(float)i*0.0625;           //转换为真实温度并把结果转换成浮点数

        }

    }

    else                                            //那么没有采集到温度就把初始温度设置为125度

    {

        temp=125.0;

    }

}

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

void main(void)

{

    TRISA=0xff;                                 //初始化RA5-RA0的输入输出方向

    PORTA=0x00;                              //初始化RA5-RA0数值

    TRISB=0xc7;                                //初始化对RB口的初始方向进行设置 设置为输出

    TRISD=0x00;                               //初始化RD口的初始方向进行设置,设置为输出

    PORTB=0x00;                              //初始化RB口的数值

    PORTD=0x00;                              //初始化RD口的数值

    Time(6);                                     //延时1MS秒,做内容切换显示

    LCD1602_init();                            //初始化

    LCD1602Fixed_display();                //LCD固定内容显示

/**************************死循环****************************/

    while(1)                                       //死循环,单片机初始化后,将一直运行这个死循环

    {

        if(Convert()==1)                      //启动转换

        {

              ReadFlash();                      //读取温度

              Dynamic_Display();             //读取内容显示

        }

    }

}

********************************************************************************************************





全部回复(8)
正序查看
倒序查看
2014-04-14 13:56

好帖子,应该分享给更多人!

0
回复
2014-04-14 14:19
@电源网-娜娜姐
好帖子,应该分享给更多人!
其实可以在详细点,这样有助于新人学习!
0
回复
shenx123
LV.10
4
2014-04-24 14:07

羡慕那些搞出实验的人啊

给点意见 吧    http://www.dianyuan.com/bbs/1484642.html

0
回复
eason
LV.4
5
2014-04-24 14:53
@yueyunno1
其实可以在详细点,这样有助于新人学习!
同意,希望楼主能继续,最近学习的帖子比较多,感谢这些大咔们。
0
回复
qinzhuai
LV.3
6
2014-04-25 13:31
请教下,怎样能很快的看懂程序?
0
回复
345295362
LV.6
7
2014-04-25 19:05
@qinzhuai
请教下,怎样能很快的看懂程序?
看懂C语言,再去了解PIC单片机指令。
0
回复
2014-04-27 00:09
@345295362
看懂C语言,再去了解PIC单片机指令。
1周时间从零基础搞定C语言基础,再通过51单片机程序仿真--PIC开发板实物操作。。速度算是很快了
0
回复
2014-04-27 20:33
@山东大汉
1周时间从零基础搞定C语言基础,再通过51单片机程序仿真--PIC开发板实物操作。。速度算是很快了

走马看花估计可以,细节的地方得好好看看!

0
回复