基于51单片机的智能数码管百叶窗

设计要求:本设计以MCS-51系列单片机为核心,采用常用电子器件设计,一个电源开关,用一台直流电机控制百叶窗叶片的旋转(正转/反转),用一个光敏电阻传感器测量室内光强度,并用两位数码管显示测量结果,设置三个按键:手动/自动、手动正转和手动反转用一个发光二极管显示手动/自动状态,自动状态时二极管亮。设置两个极限位置保护行程开关,用于保护百叶窗叶片:当正转到极限位置压下行程开关时,电机停止正转,但还可以反转;当反转到极限位置压下行程开关时,电机停止反转,但还可以正转。按键输入采用中断方式,按键中断请求信号接INT0。单片机根据设定光强S1和S2和实测光强P来控制电机M的动作,分别使电机正转、反转或者停转来达到控制光强弱的功能。

使用的设备:光敏电阻传感器,直流电机,两位数码管

实现功能:自动模式和手动模式

实现方式:采用中断方式

 系统框图:

 程序流程图:

目录

I2C总线的设定

代码如下:

ADC数据转换:通过PCF8591实现

寻址

控制字节

proteus仿真中的接线法 

电机:通过L298N实现

引脚

 proteus仿真中的接线法 

两位数码管

 代码

proteus仿真中的接线法

 proteus仿真中的定时器外部中断

 定时器和外部中断

代码

主函数


I2C总线的设定

代码如下:

#include <REGX52.H>
#include "I2C.h"

void I2CStart()  //I2C开始
{
	I2C_SCL=1;
	I2C_SDA=1;
	I2C_SDA=0; 
	I2C_SCL=0;
}

void I2CStop()  //I2C结束
{
	I2C_SCL=0;
	I2C_SDA=0;
	I2C_SCL=1;
	I2C_SDA=1;
}

bit I2CWrite(unsigned char dat) //I2C写操作,dat-代写数值,ack-返回应答值
{
	bit ack;  //用来暂存应答值
	unsigned char mask;  //用来暂存数据
	for(mask=0x80;mask!=0;mask>>=1)
	{
		 if((mask&dat))
			 I2C_SDA=1;
		 else
			 I2C_SDA=0;
		 
		 I2C_SCL=1;    //拉高SCL
		 I2C_SCL=0;    //再拉低SCL,完成一个周期
	}
	   I2C_SDA=1;    //主机释放SDA
		 I2C_SCL=1;
		 ack=I2C_SDA;  //读取SDA值,即为应答值
		 I2C_SCL=0;
	   return (~ack); //因为原本的I2C是0表示应答,1表示非应答,所以这里取反
}

unsigned char I2CReadNAK()  //I2C总线读操作,发送非应答信号并继续读下去,返回值-读到的字节
{
	unsigned char mask; 
	unsigned char dat;  //暂存数据
	I2C_SDA=1;  //确保主机释放SDA
	for(mask=0x80;mask!=0;mask>>=1)
	{
		I2C_SCL=1;
		if(I2C_SDA)
			dat|=mask;
		else
			dat&=~mask;
		I2C_SCL=0;
	}
	I2C_SDA=1;  //拉高SDA,发送非应答信号
	I2C_SCL=1;  //拉高SCL
	I2C_SCL=0;  //再拉低SCL完成非应答
	
	return dat; //返回数据
}

unsigned char I2CReadACK()  //I2C总线读操作,发送应答信号并不再读下去,返回值-读到的字节
{
	unsigned char mask;
	unsigned dat; //暂存数据
	I2C_SDA=1;    //确保主机释放SDA
	for(mask=0x80;mask!=0;mask>>=1)
	{
		I2C_SCL=1;
		if(I2C_SDA)
			dat|=mask;
		else
			dat&=~mask;
		I2C_SCL=0;
	}
	I2C_SDA=0;  //拉高SDA,发送应答信号
	I2C_SCL=1;  //拉高SCL
	I2C_SCL=0;  //再拉低SCL完成应答
	
	return dat; //返回数据
}

ADC数据转换:通过PCF8591实现

 

寻址

i2c总线系统中的每个PCF8591设备通过发送一个有效的地址来寻址。
地址由固定部分可编程部分组成。
可编程部分必须根据地址引脚AO、A1和A2进行设置。
地址总是必须作为I2c总线协议中的开始条件之后的第一个字节被发送。
地址字节的最后一位是读写位,它设定了接下来数据传输的方向。

控制字节

发送到PCF8591设备的第二个字节将存储在其控制寄存器中,并需要控制设备的功能。

1、最高位和第三位默认为0
2、第六位:AD使能位,为1开启,为0关闭
3、第四位和第五位:单端、差分选择位
4、第二位:自动增量位(一般位0)
5、读取选择位:比如读取AIN0,或者读取AIN2

(注意:最低位为第0位,最高位为第七位)

proteus仿真中的接线法 

1、PCF8591 是一个单片集成、单独供电、8-bit CMOS数据获取器件。
2、AIN0、AIN1、AIN2、AIN3为模拟输入端
3、AOUT为模拟输出端
4、EXT为低电平时使用内部时钟,为高电平时使用外部时钟
5、A0、A1、A2为地址引脚
6、OCS振荡器、VREF基准电压、AGND接地

 

#include <REGX52.H>
#include "I2C.h"

//通过I2C读取AD值
unsigned char GetADCValue(unsigned char chn)
{
	unsigned char val;
	I2CStart();
	if(!I2CWrite(0x48<<1))     //判断最后一位是读或写
	{
		I2CStop();             
		return 0;
	}
	I2CWrite(0x40|chn);        //AD使能位,为1开启,为0关闭  
	I2CStart();
	I2CWrite(0x48<<1 | 0x01);  //读写位置1,即为读出数据
	I2CReadACK();
	val=I2CReadNAK();
	I2CStop();
	
	return val;
}

电机:通过L298N实现

通过将引脚接为高电平或低电平控制接通还是关断,ENA使能左侧电机,ENB使能右侧电机。

 IN1IN2为一组,对应OutA(输出A)

 IN3IN4为一组,对应OutB(输出B)

引脚

 proteus仿真中的接线法 

两位数码管

 SMG1控制第一位数码管,SMG2控制第二位数码管

RP与段码表配合,可以显示想要输出的数字

 代码

#include <REGX52.H>
#include "Display.h"
//数码管的动态扫描
unsigned char  Number[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳数码管显示当前值0-9
void Display(unsigned char num)
{
	static unsigned char t;
	SMG1=0;  //控制第一位数码管暗灭
	SMG2=0;  //控制第二位数码管暗灭
	if(t==0)
	{
		t=1;
		PR=Number[num/10];  //显示十位
		SMG1=1;  //点亮第一位数码管
		SMG2=0;
	}
	else
	{
		t=0;
		PR=Number[num%10];  //显示个位
		SMG1=0;
		SMG2=1;  //点亮第二位数码管
	}
}

proteus仿真中的接线法

 注:由于I0口的输出有效,要加“上拉电阻”拉高电平才能驱动。

 proteus仿真中的定时器外部中断

XTAL1:反向振荡放大器的输入。

XTAL2:来自反向振荡器的输出。

RST:接复位按钮。

PSEN:外部程序存储器的选通信号。

EA/VPP:当/EA保持低电平时,则在此期间读取外部程序存储器,不管是否有内部程序存储器。当/EA端保持高电平时,此期间读取内部程序存储器。

ALE/PROG:在编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,

 定时器和外部中断

代码

#include <REGX52.H>

//定时器初始化
void Time1_init()
{
  TMOD |= 0x10;			 //使用模式1,16位定时器
  TH1   = (65536-10000)/256;	//定时器装入初始值10ms
  TL1   = (65536-10000)%256;
  EA    = 1;  //总中断
  ET1   = 1;//定时器1初始化
  TR1   = 1;
  
}

//外部中断初始化
void EX_Init()
{
  EA=1;
  IT0=1;
  EX0=1;
}

主函数

#include <REGX52.H>
#include "I2C.h"
#include "Init.h"
#include "ADC.h"
#include "Display.h"
bit flag=0;//模式选择
sbit LED=P1^7;	 //LED指示灯引脚
sbit IN1=P1^0;	   //电机
sbit IN2=P1^1; 
sbit K2=P3^3;			//按键控制正反转
sbit K3=P3^4;
sbit xianwei1=P3^6;	   //限位开关
sbit xianwei2=P3^7;
unsigned char Light_val; //光照(电阻)强度

void main()
{
	Time1_init();  //定时器初始化
	EX_Init();     //中断初始化
	while(1)
	{
		Light_val=(char)(GetADCValue(0)/3);  //获取ADC数值
		if(flag==0)  //自动模式
		{
			LED=0;           //灯亮
      xianwei1=1;
			xianwei2=1;
			if((xianwei1==1)&&(Light_val<30))       //如果光照强度小于30并且限位开关1没有关闭
			{IN1=1;IN2=0;}   //正转
			else if((xianwei2==1)&&(Light_val>50))  //如果光照强度大于50并且限位开关2没有关闭
			{IN1=0;IN2=1;}   //反转
			else
			{IN1=1;IN2=1;}   //刹车
		}
		if(flag==1)  //手动模式
		{
			K2=1;
			K3=1;
			xianwei1=1;
			xianwei2=1; 
			if((K2==0)&&(xianwei1==1))  //如果摁下K2并且限位开关1没有关闭
			{IN1=1;IN2=0;while(!K2);}   //正转,并且摁下K2不松手时一直循环
			if((K3==0)&&(xianwei2==1))  //如果摁下K3并且限位开关2没有关闭
			{IN1=0;IN2=1;while(!K3);}   //反转,并且摁下K3不松手时一直循环
			else
			{IN1=1;IN2=1;}  //刹车
		}
	}
}


//中断函数
void Int0() interrupt 0
{
  flag=~flag;  //模式变换
}

//定时器中断服务函数为计时器使用
void time1(void)interrupt 3
{
  
  TH1=(65536-10000)/256;
  TL1=(65536-10000)%256;
  Display(Light_val);	//数码管显示数值
  
}

仿真

物联沃分享整理
物联沃-IOTWORD物联网 » 基于51单片机的智能数码管百叶窗

发表评论