概要
资料网盘下载连接:https://pan.baidu.com/s/1eFOjBE39L_eT65kbix6D-g
提取码:9e6v
基于51单片机的四路光强采集系统的课程设计。主要采用了PCF8591和89C52单片机进行数据采集和显示。整体结构如图所示:
整体架构流程
采用四个光敏电阻进行分压从而通过电压的变化间接测得光强的变化。
按下通道切换按键进行各个通道的测量,如图所示
PCF8591采集电压后首先经过A-D转换,然后通过iic协议传输测量值
最后单片机通过定时器扫描按键和数码管,进行通道的选择和显示
技术名词解释
PCF8591中文资料链接:https://pan.baidu.com/s/1cqw3QRkb0iR2T2bL24PHig
提取码:yo9j
技术细节
注:protues使用的是8.15版本,如果打不开可查看自己的版本是否过低!
使用下列代码时建议包装到头文件中使用,如下图,详情可下载资料包查看:
下面是详细代码:
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语言程序就可以做到四路光照采集,当然这只是一种实现的思路。欢迎大家交流讨论更好的方法,也希望能给你带来一些帮助!