PIC单片机实例六:基于PROTEUS模拟的精美调光台灯
扫描二维码
随时随地手机看文章
一.原理和功能介绍
1.PROTEUS模拟的效果图如下:
我简要介绍一下系统的组成及各部分的功能
本系统主要有五部分构成:
1.显示和键盘
显示采用的是最常用最便宜的1602液晶,内藏HD44780,且内含简单字库.指令简单,容易上手
键盘部分由7个按键和一个电位器旋钮组成系统的输入部分.(如图)
2.运算处理单元
本系统的所有操作都由PIC16F877A单片机完成(选择他的理由:端口多,不需扩展)
3.存储单元
主要由一片24C01的1K内存的EEPROM完成,此芯片与单片机通过I2C总线通信,具有占有端口少的优点.
4.输出控制单元
由一片光耦和一个双向晶闸管组成.单片机输出的脉冲通过控制光耦的通断控制双向晶闸管的通断,从而控制电灯的亮度.
5.时钟单元
这是一个附加单元,对本系统无关键作用,只是为了增加附加值.有一片DS1302时钟芯片和一个32768Hz的晶振组成
功能说明
1.一上电,系统显示"ADJUST"可调状态,此时旋钮调节光的亮度,即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口状态
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协议 *
* 作者:Wujieflash *
* 日期:2008年2月2日 *
* 功能:I2C通讯协议 *
***************************************************************/
#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; //上升沿发送
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.程序逻辑性还是差了点,感觉很混乱.