此项目通过Proteus软件仿真,证实程序可行性,顺带将上一次做的步进电机部分知识加入进去,继续进行练习。
实际项目也可通过购买相关电路模块,然后通过杜邦线连接来实现其功能。
注*:查找相关类似的即可。
1.单片机最小系统(本次项目用的是C52单片机)
2.LED1602(显示)
3.PFC8591(AD/DA转换,用于将光敏信号采集和传送)
4.L293(电机驱动芯片)
5.DS18B20(温度采集)
原理图所示:
具体功能说明:手动/自动控制电机完成百叶窗动作,手动方式为按键,自动方式为光敏电阻检测后的反馈,来驱动电机。
主程序:
#include<reg52.h>
#include<intrins.h>
#include"I2C.h"
typedef unsigned int u16;
typedef unsigned char u8; //对系统默认数据类型进行重定义
#define LCD_PORT P0
unsigned char seg_buf[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳数码管显示当前值0-83
unsigned char Light_val=0; //光强度
int real_tempure=0; //实际温度
unsigned char storage_temperature[10]; //存储温度
sbit select = P3^2; //手/自动切换
sbit LED = P1^7;
sbit IN1 = P1^5;
sbit IN2 = P1^6;
sbit K2 = P3^3; //按键控制正反转
sbit K3 = P3^4;
sbit S1 = P3^6; //限位开关
sbit S2 = P3^7;
sbit DQ = P1^0; //测温
sbit RS = P2^0; //1602引脚定义
sbit RW = P2^1;
sbit E = P2^2;
void delay (u16 x) //延迟函数,大约延时1ms
{
u16 y,z;
for (y = x; y>0; y--)
for (z = 110; z>0; z--);
}
void write_com(u8 com) //1602写
{
RS = 0;
RW = 0;
E = 0;
delay(2);
LCD_PORT = com;
delay(2);
E = 1;
delay(2);
E = 0;
}
void write_date(u8 date)
{
RS = 1;
RW = 0;
E = 0;
delay(2);
LCD_PORT = date;
E = 1;
delay(2);
E = 0;
}
void Cursor_XY (u8 x, u8 y) //1602的光标位置定义
{
if(y==0)
write_com(0x80|x);
if(y==1)
write_com(0x80|(x-0x40));
}
void Print(u8 *str) //1602显示字符串
{
while(*str!='\0')
{
write_date(*str);
str++;
}
}
void LCD_Print(u8 x, u8 y, u8 *str) //1602在指定位置显示字符串
{
Cursor_XY (x,y);
Print(str);
}
void LCD_Print_num(u8 x, u8 y, u16 num) //1602在指定位置显示数字
{
u8 digital_position [6] = {0};
digital_position [0] = num/100+'0';
digital_position [1] = num%100/10+'0';
digital_position [2] = num%10+'0';
Cursor_XY (x,y);
Print( digital_position );
}
void init_lcd1602() //初始化1602
{
E = 0;
write_com(0x38);
write_com(0x0c);
write_com(0x06);
write_com(0x01);
write_com(0x80);
}
void transform(u8 *pt) //将采集到的温蒂显示出来
{
const u8 code dotcode[4]={0,25,50,75};
u8 x = 0x00,y = 0x00;
u8 t[2];
t[0] = *pt;
pt++;
t[1] = *pt;
if (t[1]>0x07) //判断正负温度
{
storage_temperature[0]=0x2d; //0x2d为"-"的ASCII码
t[1]=~t[1];
t[0]=~t[0];
x = t[0]+1;
t[0] = x;
if(x>255)
t[1]++;
}
else storage_temperature[0] = 0x2b; //0xfe为变"+"的ASCII码
t[1]<<=4;
t[1]=t[1]&0x70;
x=t[0];
x>>=4;
x=x&0x0f;
t[1]=t[1]|x;
storage_temperature[1]=t[1]/100+0x30;
if( storage_temperature[1]==0x30) storage_temperature[1]=' '; //消影
storage_temperature[2]=(t[1]%100)/10+0x30;
storage_temperature[3]=(t[1]%100)%10+0x30;
t[0]=t[0]&0x0c;
t[0]>>=2;
x=t[0];
y=dotcode[x];
storage_temperature[5]=y/10+0x30;
storage_temperature[6]=y%10+0x30;
}
void delay_DS18B20(u16 i)
{
while(i--);
}
void Init_DS18B20(void) //DS18B20初始化
{
u8 x=0;
DQ = 1; //复位
delay_DS18B20(8);
DQ = 0;
delay_DS18B20(80);
DQ = 1;
delay_DS18B20(14);
x=DQ;
delay_DS18B20(20);
}
u8 DS18B20_read(void) //DS18B20读字节
{
u8 i=0;
u8 dat = 0;
for (i=8;i>0;i--)
{ DQ = 1;delay_DS18B20(1);
DQ = 0;
dat>>=1;
DQ = 1;
if(DQ)
dat|=0x80;
delay_DS18B20(4);
}
return(dat);
}
void DS18B20_write(u8 dat) //DS18B20写字节
{
u8 i=0;
for (i=8; i>0; i--)
{ DQ = 1;delay_DS18B20(1);
DQ = 0;
DQ = dat&0x01;
delay_DS18B20(5);
DQ = 1;
dat>>=1;
}
}
u8 *ReadTemperature() //DS18B20读取当前温度
{ u8 tt[2];
Init_DS18B20();
DS18B20_write(0xCC);
DS18B20_write(0x4E);
DS18B20_write(120);
DS18B20_write(-20);
DS18B20_write(0x3f);
delay_DS18B20(80);
Init_DS18B20();
DS18B20_write(0xCC);
DS18B20_write(0x44);
delay_DS18B20(80);
Init_DS18B20();
DS18B20_write(0xCC);
DS18B20_write(0xBE);
delay_DS18B20(80);
tt[0] = DS18B20_read();
tt[1] = DS18B20_read();
return(tt);
}
void EX_Init() //外部中断初始化
{
EA = 1;
IT0 = 1;
EX0 = 1;
}
u8 GetADCValue(u8 chn) //通过IIC读取AD数值
{
u8 val;
I2CStart();
if(!I2CWrite(0x48<<1))
{
I2CStop();
return 0;
}
I2CWrite(0x40 | chn);
I2CStart();
I2CWrite(0x48<<1 | 0x01);
I2CReadACK();
val = I2CReadNAK();
I2CStop();
return val;
}
/lcd指定位置显示数字
void LCD_Print_tempure(u8 x, u8 y, u16 num)
{
u8 digital_position[6]={0};
if(num<0) //负温度
{
digital_position[0] = '-';
digital_position[1] = num%1000/100+'0';
digital_position[2] = num%100/10+'0';
digital_position[3] ='.';
digital_position[4] = num%10+'0';
}
else //正温度
{
digital_position[0]= num/1000+'0';
digital_position[1]= num%1000/100+'0';
digital_position[2]= num%100/10+'0';
digital_position[3]='.';
digital_position[4]= num%10+'0';
}
Cursor_XY (x,y);
Print(digital_position);
}
void Control_motor() //根据温度和光照控制电机
{
if(flag==0) //自动模式
{
S1 = 1;
S2 = 1;
LED = 0;
_nop_();
if((Light_val<30)&&(real_tempure<200)&&(S1==1))
{IN1 = 1;IN2 = 0;}
else if(((Light_val>50)||(real_tempure>300))&&(S2==1))
{IN1 = 0;IN2 = 1;}
else
{IN1 = 1;IN2 = 1;}
}
if(flag==1)
{
K3 = 1;
K2 = 1;
S1 = 1;
S2 = 1;
LED = 1;
if((K2==0)&&(S1==1))
{IN1 = 0;IN2 = 1;while(!K2);}
if((K3==0)&&(S2==1))
{IN1 = 1;IN2 = 0;while(!K3);}
else
{IN1 = 1;IN2 = 1;}
}
}
u16 Get_Temperature() //读取温度
{
u8 *pt;
u16 real_tempure;
pt=ReadTemperature();
transform(pt);
real_tempure = ((storage_temperature[2]-0x30)*10)+storage_temperature[3]-0x30;
real_tempure =(real_tempure*10) + storage_temperature[5]-0x30;
if(storage_temperature[1]==0x31)
{
real_tempure+=1000;
}
if(storage_temperature[0]==0x2d)
{
real_tempure*=-1;
}
return real_tempure;
}
void Int0() interrupt 0 //中断函数
{
select=~select; //模式变换
}
void main() //主函数
{
EX_Init();
init_lcd1602();
Init_DS18B20();
LCD_Print(0,0,"Light:");
LCD_Print(0,1,"Temapure:");
while(1)
{
Light_val=(char)(GetADCValue(0)/3);
real_tempure=Get_Temperature();
LCD_Print_tempure(9,1,real_tempure);
LCD_Print_num(6,0,Light_val);
Control_motor();
}
}
通讯程序:
#include <reg52.h>
#include <intrins.h>
#include "I2C.h"
#define I2CDelay() {_nop_();_nop_();_nop_();_nop_();}
void I2CStart() //产生总线起始信号
{
I2C_SDA = 1;
I2C_SCL = 1;
I2CDelay();
I2C_SDA = 0;
I2CDelay();
I2C_SCL = 0;
}
void I2CStop() //产生总线停止信号
{
I2C_SCL = 0;
I2C_SDA = 0;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
I2C_SDA = 1;
I2CDelay();
}
bit I2CWrite(unsigned char dat) //I2C总线写操作
{
bit ack;
unsigned char mask;
for (mask=0x80; mask!=0; mask>>=1)
{
if ((mask&dat) == 0)
I2C_SDA = 0;
else
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
I2C_SCL = 0;
}
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1;
ack = I2C_SDA;
I2CDelay();
I2C_SCL = 0;
return (~ack);
}
unsigned char I2CReadNAK() //I2C总线读操作
{
unsigned char mask;
unsigned char dat;
I2C_SDA = 1;
for (mask=0x80; mask!=0; mask>>=1)
{
I2CDelay();
I2C_SCL = 1;
if(I2C_SDA == 0)
dat &= ~mask;
else
dat |= mask;
I2CDelay();
I2C_SCL = 0;
}
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
I2C_SCL = 0;
return dat;
}
unsigned char I2CReadACK() //I2C总线读操作
{
unsigned char mask;
unsigned char dat;
I2C_SDA = 1;
for (mask=0x80; mask!=0; mask>>=1)
{
I2CDelay();
I2C_SCL = 1;
if(I2C_SDA == 0)
dat &= ~mask;
else
dat |= mask;
I2CDelay();
I2C_SCL = 0;
}
I2C_SDA = 0;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
I2C_SCL = 0;
return dat;
}