基于51单片机的四路光强采集系统(附protues仿真)

 

 

概要

资料网盘下载连接:https://pan.baidu.com/s/1eFOjBE39L_eT65kbix6D-g 
提取码:9e6v

基于51单片机的四路光强采集系统的课程设计。主要采用了PCF8591和89C52单片机进行数据采集和显示。整体结构如图所示:

86a2590cb56f4d2eb0ac3e4bad14557a.png

 

整体架构流程

采用四个光敏电阻进行分压从而通过电压的变化间接测得光强的变化。

按下通道切换按键进行各个通道的测量,如图所示

82276fb6085043c781ce1c7591f53a61.png

PCF8591采集电压后首先经过A-D转换,然后通过iic协议传输测量值

f1c3cf1dde9d47dbb9ebca6a6602136a.png

 

最后单片机通过定时器扫描按键和数码管,进行通道的选择和显示

65a2fa3994dd4e6bb6692b9ef7c296e4.png

 

技术名词解释

PCF8591中文资料链接:https://pan.baidu.com/s/1cqw3QRkb0iR2T2bL24PHig 
提取码:yo9j

技术细节

注:protues使用的是8.15版本,如果打不开可查看自己的版本是否过低!

使用下列代码时建议包装到头文件中使用,如下图,详情可下载资料包查看:

6a522a01bb5844f79007e8da08ab4e07.png

 

下面是详细代码:

main主程序

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"
#include "Key.h"
#include "Nexie.h"
#include "PCF8591.h"

unsigned char value;
unsigned char KeyNum=0,AIN_flag=0;

void Select_AINx_Show(void)	//通道选择及显示函数
{
	KeyNum=Key();
	if(KeyNum==1)
	{
		AIN_flag++;
		if(AIN_flag>3)AIN_flag=0;
	}
	
	Nixie_SetBuf(2,AIN_flag);
	value=PCF8591_ADC(AIN_flag);
	Nixie_SetBuf(5,value/100);
	Nixie_SetBuf(6,value/10%10);
	Nixie_SetBuf(7,value%10);
}


void main()
{
	Timer0_Init();//定时器初始化
	
	Nixie_SetBuf(1,11);//美化作用
	Nixie_SetBuf(3,11);
	
	while(1)
	{
		Select_AINx_Show();
		Delay(300);			//采样延时
	}
}



void Timer0_Routine() interrupt 1
{
		static unsigned int T0Count1,T0Count2;
		TL0 = 0x18 ;		//设置定时初始值
		TH0 = 0xFC ;		//设置定时初始值
		T0Count1++;T0Count2++;//每1ms进行一次++;
		if(T0Count1>=20)//20ms调用一次Key_Loop()函数
		{
			T0Count1=0;
			Key_Loop();
		}
			if(T0Count2>=2)//2ms调用一次Nixie_Loop()函数
		{
			T0Count2=0;
			Nixie_Loop();
		}
	
		
}

PCF8591程序:

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


#define PCF8591_Address 0x90

/**
  * @brief PCF8591_ADC
  * @param Chanl 选择想读取的通道 
  * @retval Data要读出的数据
  */
unsigned char PCF8591_ADC(unsigned char Chanl)
{
	unsigned char Data;
	I2C_Start();
	I2C_SendByte(PCF8591_Address);
	I2C_ReceiveAck();
	I2C_SendByte(Chanl);
	I2C_ReceiveAck();
	I2C_Stop();
	I2C_Start();
	I2C_SendByte(PCF8591_Address|0x01);
	I2C_ReceiveAck();
	I2C_RceiveByte();
	I2C_SendAck(0);	//主机主动应答,接收这次转换值
	Data=I2C_RceiveByte();
	I2C_ReceiveAck();
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}

iic协议程序:

#include <REGX52.H>

#include "intrins.h"

sbit I2C_SCL=P2^0;
sbit I2C_SDA=P2^1;

#define delay_time 40

void IIC_Delay(unsigned char i)
{
    do{_nop_();}
    while(i--);        
}

/**
  * @brief  I2C开始
  * @param 无
  * @retval无
  */
void I2C_Start(void)
{
	I2C_SDA=1;
	I2C_SCL=1;
	IIC_Delay(delay_time);
	I2C_SDA=0;
	IIC_Delay(delay_time);
	I2C_SCL=0;
}
/**
  * @brief  I2C停止
  * @param 	无
  * @retval	无
  */
void I2C_Stop(void)
{
	I2C_SDA=0;
	I2C_SCL=1;
	IIC_Delay(delay_time);
	I2C_SDA=1;
	IIC_Delay(delay_time);
}
/**
* @brief  I2C发送一个字节
  * @param 无
  * @retval无
  */
void I2C_SendByte(unsigned char Byte)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		I2C_SDA=Byte&(0x80>>i);
		IIC_Delay(delay_time);
		I2C_SCL=1;
		IIC_Delay(delay_time);
		I2C_SCL=0;
	}
}
/**
  * @brief  I2C接收一个字节
  * @param 无
  * @retval Byte
  */
unsigned char I2C_RceiveByte(void)
{
	unsigned char i,Byte;
	I2C_SDA=1;
	for(i=0,Byte=0x00;i<8;i++)
	{
		I2C_SCL=1;
		IIC_Delay(delay_time);
		if(I2C_SDA){Byte=Byte|(0x80>>i);}
		I2C_SCL=0;
		IIC_Delay(delay_time);
	}
	return Byte;
}
/**
* @brief  I2C发送应答
  * @param AckBit 应答位0应答1非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
	I2C_SDA=AckBit;
	IIC_Delay(delay_time);
	I2C_SCL=1;
	IIC_Delay(delay_time);
	I2C_SCL=0;
}
/**
  * @brief  I2C接收应答
  * @param 无
  * @retval AckBit接收到的应答0应答1非应答
  */
unsigned char I2C_ReceiveAck(void)
{
	unsigned char AckBit;
	I2C_SDA=1;
	I2C_SCL=1;
	IIC_Delay(delay_time);
	AckBit=I2C_SDA;
	I2C_SCL=0;
	IIC_Delay(delay_time);
	return AckBit;
}

按键扫描程序:

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

unsigned char Key_KeyNumber;

/**
  * @brief  外部调用主函数
  * @param 无
  * @retval Temp 传出在loop函数中得到的Key_KeyNumber值;
  */
unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Key_KeyNumber;
	Key_KeyNumber=0;//对全局变量清0,防止下次中断进来
									//没按键按下仍传出上次按键值
	return Temp;
}

/**
  * @brief  定时器按周期进来一次则检测按键是否有按下的
  * @param 无
  * @retval keyNumber得到键值在loop中调用并返回值给NowState
  */
unsigned char Key_GetState()
{
unsigned char keyNumber=0;
	if(P3_0==0)keyNumber=1;
return keyNumber;
}
/**
  * @brief  定时器周期扫描函数
  * @param 无
  * @retval 无
  */
void Key_Loop(void)
{
	static unsigned char NowState,LastState;//静态变量防止下次进来销毁
	LastState=NowState; 
	NowState=Key_GetState(); //调用按键按下函数,并把按下的值赋给NowState
	if(LastState==1 && NowState==0)//如果上次为1按下状态,这次为松开状态0则可以确定1键按下了
	{
	 Key_KeyNumber=1; //把得到的键值从中断中传出,放到全局变量Key_KeyNumber中
	}

}

数码管扫描程序:

#include <REGX52.H>
#include "Delay.h"
unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};//0是为了照应loop中i=1
unsigned char NixieNumb[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
												//	0			1    2   3 		4 		5		6   7			8			9		0		横杠(g)
/**
  * @brief  外部调用对数组Nixie_Buf[9]赋值
  * @brief  使得loop函数循环对scan函数赋值时读到相应数据
  * @brief  未赋值的数组位即为10;对应NixieNumb[]中的0;
  * @param Table,Number在Nixie_Buf数组的下标 赋的值(该值即对应NixieNumb[]中的值)
  * @retval 无
  */
void Nixie_SetBuf(unsigned char Table,Number)
{
	Nixie_Buf[Table]=Number;
}

/**
  * @brief  数码管显示
  * @param  Table,Number显示的位置 显示的数字
	* @param 两参数由定时器按周期扫描时给定
  * @retval 无
  */
void Nixie_Scan(unsigned char Table,Number)
	{
		P0=0x00;//消隐
		switch(Table)
		{//     P2_4=C-4,P2_3=B-2;P2_2=A-1
			case 1:P2_4=1;P2_3=1;P2_2=1;break;
			case 2:P2_4=1;P2_3=1;P2_2=0;break;
			case 3:P2_4=1;P2_3=0;P2_2=1;break;
			case 4:P2_4=1;P2_3=0;P2_2=0;break;
			case 5:P2_4=0;P2_3=1;P2_2=1;break;
			case 6:P2_4=0;P2_3=1;P2_2=0;break;
			case 7:P2_4=0;P2_3=0;P2_2=1;break;
			case 8:P2_4=0;P2_3=0;P2_2=0;break;
		}
		P0=NixieNumb[Number];//不用delay再消隐,因为定时器进来有时间间隔
		
	}
/**
  * @brief  定时器调用的函数
  * @param  无
  * @retval 无
  */
	void Nixie_Loop(void)//定时器每进一次就调用一次scan函数
	{										//使得八个数码管依次快速显示
		static unsigned char i=1;
		Nixie_Scan(i,Nixie_Buf[i]);
		i++;
		if(i>=9)i=1;
	}

定时器初始化程序:

#include <REGX52.H>
/**
  * @brief  定时器初始化
  * @param 无
  * @retval 无
  */
	void Timer0_Init(void)//1毫秒@11.0592MHz
	{
		TMOD&=0xF0; //设置定时器模式
		TMOD|=0x01; //设置定时器模式
		TL0 = 0x18;		//设置定时初始值
	  TH0 = 0xFC;		//设置定时初始值
		TF0=0;			//清除TF0标志
		TR0=1;			//定时器0开始计时
		ET0=1;
		EA=1;
		PT0=0;
	}
	
	

延时程序:

void Delay(unsigned int xms)
	{
		unsigned char i,j;
		while(xms)
		{
			i=2;
			j=239;
			do
			{
				while(--j);
			}
		while(--i);
			xms--;
		}
	}
	

 

小结

通过51单片机简单的几句c语言程序就可以做到四路光照采集,当然这只是一种实现的思路。欢迎大家交流讨论更好的方法,也希望能给你带来一些帮助!

 

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SFR-小曾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值