PIC單片機實例六:基于PROTEUS模擬的精美調光臺燈
一.原理和功能介紹
1.PROTEUS模擬的效果圖如下:
我簡要介紹一下系統(tǒng)的組成及各部分的功能
本系統(tǒng)主要有五部分構成:
1.顯示和鍵盤
顯示采用的是最常用最便宜的1602液晶,內藏HD44780,且內含簡單字庫.指令簡單,容易上手
鍵盤部分由7個按鍵和一個電位器旋鈕組成系統(tǒng)的輸入部分.(如圖)
2.運算處理單元
本系統(tǒng)的所有操作都由PIC16F877A單片機完成(選擇他的理由:端口多,不需擴展)
3.存儲單元
主要由一片24C01的1K內存的EEPROM完成,此芯片與單片機通過I2C總線通信,具有占有端口少的優(yōu)點.
4.輸出控制單元
由一片光耦和一個雙向晶閘管組成.單片機輸出的脈沖通過控制光耦的通斷控制雙向晶閘管的通斷,從而控制電燈的亮度.
5.時鐘單元
這是一個附加單元,對本系統(tǒng)無關鍵作用,只是為了增加附加值.有一片DS1302時鐘芯片和一個32768Hz的晶振組成
功能說明
1.一上電,系統(tǒng)顯示"ADJUST"可調狀態(tài),此時旋鈕調節(jié)光的亮度,即PWM波的占空比,如果此時按下"存儲"鍵,則此時的亮度值被記錄下來并保存,共可以記錄下三個不同的亮度.
2.如你按下"模式"鍵,此時旋鈕被屏蔽,只能調用你存儲的三個亮度值,分別為"MODE 1","MODE 2","MODE 3"方便你的使用.
3.時間設置的兩個按鍵可以實時調整時間.
二.程序(picc 8.05)
1.主程序
/***************************************************************
* 標題:PWM輸出 *
* 作者:Wujieflash *
* 日期:2008年1月25日 *
* 功能:使用PIC的CCP模塊輸出PWM波 *
***************************************************************/
#include
#include "LCD1602_init.h"
#include "ds1302.h"
#include "i2c.h"
static volatile bitGODONE@ ((unsigned)&ADCON0*8)+2;
uch k,kb,kmem;
void PWMinit()
{
TRISC=0xfb;//設置C口狀態(tài)
PORTC=0x00;
PR2=0xff; //設置PWM波的周期
CCP1CON=0x0c;
CCPR1L=0x1c;//設置占空比
T2CON=0x01; //設置1:4預分頻
TMR2ON=1; //開啟定時器2
}
/*-------------------------------------------------------*/
void AD_convert_init() //旋鈕采樣輸出
{
uch i;
ADCON1=0x00;
ADCON0=0x01;
for(i=0;i<100;i++);
GODONE=1;
}
/*-------------------------------------------------------*/
void ADScan()
{
if(GODONE==0)
{
if(ADRESH>0xe3)ADRESH=0xe3; //限制占空比的范圍
if(ADRESH<0x1c)ADRESH=0x1c;
CCPR1L=ADRESH;
GODONE=1;
}
}
/*-------------------------------------------------------*/
void KeyScan() //設置時間子程序
{
int d;
if(RC6==0) //設置鍵按下
{
k++; //選定入口值
k=k%3;
}
while(1)
{
if(RC6==1)break;//等待按鍵松開
}
switch(k)//鍵盤服務入口
{
case 1://設置分
{
d=R1302(0x83);//讀取分
d=d/16*10+d%16;//轉換為16進制
minute=flag; //設置秒的閃爍標志
hour=1; //其余變量不閃爍
if(minute==0) //閃爍
{
WriteCommand_1602(0xce);
WriteData_1602(0x00);
WriteData_1602(0x00);
}
if(RC7==0) //分數值加1
{
d++;
if(d>0x3b)d=0;//大于59就為0
d=d/10*16+d%10;
W1302(0x82,d);//寫入DS1302
while(1)
{
if(RC7==1)break;//等待鍵松開
}
}
break;
}
case 2://設置時
{
d=R1302(0x85);//讀取時
d=d/16*10+d%16;//轉換為16進制
hour=flag; //設置秒的閃爍標志
minute=1; //其余變量不閃爍
if(hour==0) //閃爍
{
WriteCommand_1602(0xcb);
WriteData_1602(0x00);
WriteData_1602(0x00);
}
if(RC7==0) //時數值加1
{
d++;
if(d>0x17)d=0;//大于59就為0
d=d/10*16+d%10;
W1302(0x84,d);//寫入DS1302
while(1)
{
if(RC7==1)break;//等待鍵松開
}
}
break;
}
case 0://設置完畢,不閃爍
{
minute=1;
hour=1;
break;
}
}
}
/*-------------------------------------------------------*/
void KeyScan1() //輸出顯示子程序
{
if(RC0==0) //模式鍵
{
kb++;
kb=kb%2;
}
while(1)
{
if(RC0==1)break;
}
switch(kb)
{
case 0: //可調模式,
{
ADON=1;//旋鈕有效
WriteCommand_1602(0x80);
WriteData_1602('A');
WriteData_1602('D');
WriteData_1602('J');
WriteData_1602('U');
WriteData_1602('S');
WriteData_1602('T');
WriteCommand_1602(0x8a);
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
if(RC5==0) //存儲鍵
{
kmem++;
kmem=kmem%3;
while(1)
{
if(RC5==1)break;
}
switch(kmem)
{
case 1:
{
w_24cl01b(0x00,ADRESH); //寫入第一通道
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('1');
WriteData_1602(0x00);
WriteData_1602('O');
WriteData_1602('K');
break;
}
case 2:
{
w_24cl01b(0x01,ADRESH); //寫入第二通道
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('2');
WriteData_1602(0x00);
WriteData_1602('O');
WriteData_1602('K');
break;
}
case 0:
{
w_24cl01b(0x02,ADRESH); //寫入第三通道
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('3');
WriteData_1602(0x00);
WriteData_1602('O');
WriteData_1602('K');
break;
}
}
}
break;
}
case 1: //"MODE"模式
{
ADON=0; //屏蔽旋鈕
WriteCommand_1602(0x80);
WriteData_1602('M');
WriteData_1602('O');
WriteData_1602('D');
WriteData_1602('E');
WriteData_1602(0x0);
WriteData_1602(0x0);
if(RC1==0) //模式一
{
CCPR1L=r_24cl01b(0x00);
ADRESH=r_24cl01b(0x00);
while(1)
{
if(RC1==1)break;
}
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('1');
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
}
if(RC3==0) //模式二
{
CCPR1L=r_24cl01b(0x01);
ADRESH=r_24cl01b(0x01);
while(1)
{
if(RC3==1)break;
}
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('2');
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
}
if(RC4==0) //模式三
{
CCPR1L=r_24cl01b(0x02);
ADRESH=r_24cl01b(0x02);
while(1)
{
if(RC4==1)break;
}
WriteCommand_1602(0x8a);
WriteData_1602('C');
WriteData_1602('H');
WriteData_1602('3');
WriteData_1602(0x00);
WriteData_1602(0x00);
WriteData_1602(0x00);
}
break;
}
}
}
/*-------------------------------------------------------*/
void DisplaySet(uch val) //顯示占空比子程序
{
int pwm,pwm_shi,pwm_ge;
if(val>0xe3)val=0xe3;
if(val<0x1c)val=0x1c;
pwm=val*100/255;
pwm_shi=0x30+pwm/10;
pwm_ge=0x30+pwm%10;
WriteCommand_1602(0xc4);
WriteData_1602(pwm_shi);
WriteData_1602(pwm_ge);
WriteData_1602('%');
}
/*-------------------------------------------------------*/
void main()
{
init1602();
i2c_init();
AD_convert_init();
TMR1init();
PWMinit();
while(1)
{
ADScan();
DisplaySet(ADRESH);
FlashMaohao();
DisplayTime1602();
KeyScan();
KeyScan1();
}
}
2.I2C子程序
/***************************************************************
* 標題:I2C協(xié)議 *
* 作者:Wujieflash *
* 日期:2008年2月2日 *
* 功能:I2C通訊協(xié)議 *
***************************************************************/
#define nop() asm("nop")
#define SCL RD6
#define SDA RD7
void i2c_init()
{
TRISD6=0;
TRISD7=0;
RD6=0;
RD7=0;
}
/*------------------------------------------------------------------*/
//start
void start_iic()
{
SDA=1;
nop();
nop();
SCL=1;
nop();
nop();
nop();
nop();
SDA=0;
nop();
nop();
SCL=0;//it is ready to send data
nop();
}
/*------------------------------------------------------------------*/
//stop
void stop_iic(void)
{
SDA=0;
nop();
nop();
SCL=1;
nop();
nop();
nop();
nop();
SDA=1;
nop();
}
/*------------------------------------------------------------------*/
//send ack
void ack_iic()
{
SDA=0;
nop();
nop();
SCL=1;
nop();
nop();
nop();
nop();
SCL=0;
nop();
nop();
}
/*------------------------------------------------------------------*/
//send nack
void nack_iic()
{
SDA=1;
nop();
nop();
SCL=1;
nop();
nop();
nop();
nop();
SCL=0;
nop();
nop();
}
/*------------------------------------------------------------------*/
//send 1 byte
void send_iic(char c)
{
uch i;
for(i=0;i<8;i++)
{
SCL=0;
if((c<{
SDA=1;
}
else
{
SDA=0;
}
SCL=1;
nop();
nop();
nop();
nop();
SCL=0;
nop();
}
SDA=1;
TRISD7=1;
SCL=1;
nop();
nop();
nop();
nop();
SCL=0;
nop();
TRISD7=0;
}
/*------------------------------------------------------------------*/
//receive 1 byte
uch receive_iic(void)
{
uch rxbuf=0;
uch i;
//SDA=1;
TRISD7=1;
for(i=0;i<8;i++)
{
SCL=0;
nop();
nop();
nop();
nop();
nop();
SCL=1;
nop();
nop();
rxbuf=rxbuf<<1;
if(SDA==1)
{
rxbuf=rxbuf+1;
}
nop();
nop();
}
SCL=0;
nop();
TRISD7=0;
return(rxbuf);
}
/*------------------------------------------------------------------*/
//write 24cl01b subroutine
void w_24cl01b(unsigned char addr,unsigned char data)
{
int i;
start_iic();
send_iic(0xa0);
send_iic(addr);
send_iic(data);
stop_iic();
for(i=0;i<500;i++);
}
/*------------------------------------------------------------------*/
//read 24cl01b subroutine
char r_24cl01b(unsigned char addr)
{
int i;
uch data;
start_iic();
send_iic(0xa0);
send_iic(addr);
start_iic();
send_iic(0xa1);
data=receive_iic();
nack_iic();
stop_iic();
for(i=0;i<1000;i++);
return(data);
}
3.1602操作子程序
/***************************************************************
* 標題:LCD1602驅動 *
* 作者:Wujieflash *
* 日期:2008年1月29日 *
* 功能:驅動1602液晶的驅動程序 *
***************************************************************/
#include
#define RS RD0
#define RW RD1
#define E RD2
#define uch unsigned char
//延時子程序
void delay_1602(int time)
{
while(time--);
}
/*-----------------------------------------------*/
//寫命令子程序
void WriteCommand_1602(uch command)
{
RS=0;
RW=0;
delay_1602(400);
E=1;
PORTB=command;
E=0;
}
/*-----------------------------------------------*/
//寫數據子程序
void WriteData_1602(uch data)
{
RS=1;
RW=0;
delay_1602(400);
E=1;
PORTB=data;
E=0;
}
/*-----------------------------------------------*/
//初始化LCD1602子程序
void init1602()
{
TRISD0=0;
TRISD1=0;
TRISD2=0;
RD0=0;
RD1=0;
RD2=0;
TRISB=0;
PORTB=0;
WriteCommand_1602(0x01);
WriteCommand_1602(0x38);
WriteCommand_1602(0x0c);
WriteCommand_1602(0x80);
//WriteData_1602('M');
//WriteData_1602('O');
//WriteData_1602('D');
//WriteData_1602('E');
//WriteData_1602(':');
WriteCommand_1602(0xc0);
WriteData_1602('P');
WriteData_1602('W');
WriteData_1602('M');
WriteData_1602(':');
}
4.時鐘子程序
/***************************************************************
* 標題:DS1302數字時鐘 *
* 作者:Wujieflash *
* 日期:2008年1月29日 *
* 功能:顯示日歷與時間的程序 *
***************************************************************/
#define RST RD3
#define SCLK RD4
#define IO RD5
uch flag=0;
uch minute=1,hour=1;
uch clock[]={0};
uch DisCash[]={0x00,0x30,0x09,0x16,0x01,0x03,0x09};
/////往1302寫入1Byte數據////////////////////////
void RTInputByte(uch d)
{
uch i;
TRISD3=0;
TRISD4=0;
TRISD5=0;
for(i=8; i>0; i--)
{
IO = d&0x01; //取最低位
SCLK = 1; //上升沿發(fā)送
SCLK = 0; //恢復
d = d >> 1;
}
}
///////從1302讀取1Byte數據////////////////////////
uch RTOutputByte(void)
{
uch i,val=0;
TRISD5=1; //設置為輸入
for(i=8; i>0; i--)
{
val = val >>1;
if(IO)val=val|0x80;// 從最低位開始接收
SCLK = 1; //下降沿接收
SCLK = 0;
}
TRISD5=0;
return(val);
}
///////先寫地址,后寫命令/數據//////////////////////////
void W1302(uch ucAddr, uch ucDa)
{
RST = 0;
SCLK = 0;
RST = 1; //打開DS1302
RTInputByte(ucAddr); // /* 地址,命令 */
RTInputByte(ucDa); // /* 寫1Byte數據*/
SCLK = 1;
RST = 0; //關閉DS1302
}
///////先寫地址,后讀命令/數據////////////////////////
uch R1302(uch ucAddr)
{
uch ucData;
RST = 0;
SCLK = 0;
RST = 1;
RTInputByte(ucAddr); // /* 地址,命令 */
ucData = RTOutputByte(); // /* 讀1Byte數據 */
SCLK = 1;
RST = 0;
return(ucData);
}
/////////向1302寫入 秒 分 時 日 月 星期 年 */////////////
void Set1302(uch *pClock)
{
uch i;
uch ucAddr = 0x80; //起使地址
W1302(0x8e,0x00); ///* 控制命令,WP=0,允許寫操作*/
for(i =7; i>0; i--)
{
W1302(ucAddr,*pClock); ///* 秒 分 時 日 月 星期 年 */
pClock++;
ucAddr +=2; //寫地址加2
}
W1302(0x8e,0x80); // /* 控制命令,WP=1,寫保護*/
}
////////從1302讀出 秒 分 時 日 月 星期 年 *//////////////////
void v_Get1302(unsigned char ucCurtime[])
{
unsigned char i;
unsigned char ucAddr = 0x81;
for(i=0;i<7;i++)
{
ucCurtime[i] = R1302(ucAddr);///*格式為: 秒 分 時 日 月 星期 年 */
ucAddr += 2;
}
}
//使用LCD1602顯示
void DisplayTime1602()
{
uch hour_shi,hour_ge,minute_shi,minute_ge;
v_Get1302(clock);
hour_shi=0x30+clock[2]/16;
hour_ge=0x30+clock[2]%16;
minute_shi=0x30+clock[1]/16;
minute_ge=0x30+clock[1]%16;
if(hour==1)
{WriteCommand_1602(0x80+0x4b);
WriteData_1602(hour_shi);
WriteData_1602(hour_ge);
}
if(minute==1)
{WriteCommand_1602(0x80+0x4e);
WriteData_1602(minute_shi);
WriteData_1602(minute_ge);
}
}
//TRM1初始化子程序
void TMR1init()
{
//TRM1 INITIAL
T1CON=0X30; //8分頻
TMR1IF=0; //清中斷標志
TMR1IE=1; //使能定時器1中斷
TMR1L=0XDB; //初始值(定時0.5S)
TMR1H=0X0B;
TMR1ON=1; //開定時器1
}
//冒號閃爍子程序
void FlashMaohao()
{
if(TMR1IF==1)
{
TMR1ON=0;
TMR1IF=0;
TMR1L=0XDB; //重新付初值
TMR1H=0X0B;
flag++;
flag=flag%2; //閃爍標志在0-1間翻轉
TMR1ON=1;
}
if(flag==0)
{
WriteCommand_1602(0xcd);
WriteData_1602(':');
}
if(flag==1)
{
WriteCommand_1602(0xcd);
WriteData_1602(0x00);
}
}
三.總結
雖然是完成了,但還有很多遺憾:
1.并沒有完全仿真出來,主要由于軟件需要的數字和模擬器件太多,資源太多,運行不過來.
2.有些實物已經驗證的程序在PROTEUS上卻失敗,如24C01能寫入,卻讀出來總為0,事實卻可以的.
3.程序邏輯性還是差了點,感覺很混亂.