代码搬运自其他博主,自己写的注释,持续更新中。。。。
原博主乐者zmh_算法编程题解析,web自动化,opencv-优快云博客
实验内容1️⃣:用AD转换显示电压值,DA转换实现呼吸灯
其中电压值的读取用到了PCF8591芯片
main.c
#include"dac.h"//用到的头文件声明
#include"i2c.h"
#include"timer0.h"
unsigned char dat=0;
void main(){
timer0_init();
while(1){
dat=PCF8591_Read(); //模数AD转换,是将模拟量转换为数字量,方便后续write写入,read和write函数的声明在i2c.c中
PCF8591_Write(255); //参考dac.h里的公式,给定最高电压为5V,单位为FACTOR=5/256,后面temp=FACTOR*dat就是实际电压值,由于是8位,8位2进制最大对应255,此时对应最大电压,实际电压=5/256*255近似5V即灯最亮
delay(10000); //延时,它的定义在dac.h中给出
dat=PCF8591_Read();//继续执行读操作AD转换
PCF8591_Write(200);//改变电压,呈现出一种灯逐渐变暗的现象
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(150);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(100);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(50);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(0);//灯灭
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(50);//灯又开始重新慢慢亮起来
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(100);
delay(10000);
PCF8591_Write(150);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(200);
delay(10000);
dat=PCF8591_Read();
PCF8591_Write(255);
delay(10000);
}
}
void timer0() interrupt 1{ //这里设置了timer0中断目的是延时,这也是除了delay()函数外另一种延时方法
TH0=0xdc;
TL0=0x00;//设置定时器延时10ms,THTL确定方式:12*x/f=设置延时时间,f一般为12Mhz,然后因为是16位重载方式THTL=(65536-x)后化为16进制的值
display(dat); //display函数是控制数码管显示的函数,在Dac.c中声明,这里就是在数码管上显示dat即电压的值
}
timer0.c
#include"timer0.h"//头文件
void timer0_init(){//因为上面用了timer0来延时,所以要初始化timer0
EA=1;//打开中断总开关
ET0=1;//打开timer0这个分开关,允许T0中断
TR0=1;//一旦用到了定时器的定时功能都要设置TR0=1以启动定时器
TMOD=0x01;//设置定时器工作模式为1
TH0=0xdc;//设置延时时间为10ms
TL0=0x00;
}
timer0.h
#ifndef __TIMER0_H__//固定格式,但注意要把中间改成和头文件名一样的大写形式
#define __TIMER0_H__//固定格式,但注意要把中间改成和头文件名一样的大写形式
#include <reg52.h> //固定格式
void timer0_init(); //声明timer0.c文件里用到的函数
#endif//固定格式
dac.c
#include"dac.h"//头文件声明
unsigned char code smgduan[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};//共阳数码管对应的0-9这10个数字(数码管引脚图见下图)
unsigned char i;
unsigned char voltage[2];//这是定义voltage数组表示有两位,即【0】和【1】
void delay(unsigned int i){//定义delay()延时函数
while(i--);//计算机做减法会占用比较多的时间,因此可以用来延时
}
void display(unsigned char dat){ //display()函数的声明
float temp = FACTOR * dat;//电压值
if(dat == 255){//这里和下面是用两位数的voltage来量化电压voltage[1]voltage[0],目的是为了把电压值与数码管上显示的对应起来,以便数组voltage[1]voltage[0]能对应数码管里的0-9这10个数
voltage[1]= 5;//5v
voltage[0] = 0;
}
else{ //非5v
voltage[1]= (unsigned char)temp;//获得电压值的整数部分
voltage[0] = (unsigned char)((temp - voltage[1]) * 10);//获得电压值的小数部分
}
for(i=0;i<2;i++)
{//数码管有两位用来动态显示电压,所以是两种情况(数码管的动态显示的原理是以人眼看不到的极快速度快速切换)
switch(i){//下面的P0是为了控制数码管内引脚,而P1是为了控制哪个数码管工作(一共是4个数码管)
case(0):
LSA=0;LSB=0;LSC=1;LSD=0; break; //数码管的引脚分配显示第一位,在Dac.h中可见LSA=P1^3,LSB=P1^2,LSC=P1^1,LSD=P1^0,这里是P1^1
case(1):
LSA=0;LSB=1;LSC=0;LSD=0; break; //数码管的引脚分配显示第二位,这里是P1^2
}
if(i==1)//P1.2亮
P0=smgduan[voltage[i]]&0x7f;//这里与的目的是让数码管引脚上的dp始终起作用,即小数部分
else //P1^1亮
P0=smgduan[voltage[i]];//显示整数部分
delay(100); //短暂延时
P0=0xFF;//这里是数码管的消隐,直接关闭数码管所有的位,其目的是避免数码管的鬼影,让下一次显示更清晰
}
}
数码管引脚图:对应的二进制顺序为dp gfe dcba(dp是控制小数点)

dac.h
#ifndef __DAC_H__
#define __DAC_H__
#define FACTOR 0.01953125 // 这里的FACTOR实际上=5 / 256,它是一个量化单位(用一个字节来量化表示5V的电压值,共为256个量化等级 )
#include <reg52.h>
void display(unsigned char dat);//声明dac.c中用到的diplay()函数
void delay(unsigned int i);//声明dac.c中用到的delay()函数
sbit LSA=P1^3; //声明P1^0-P1^3的引脚分配
sbit LSB=P1^2;
sbit LSC=P1^1;
sbit LSD=P1^0;
#endif
i2c.c
#include"i2c.h"
void delay5us(void) //这里是定义延时函数,延时5us
{
unsigned char a;
for(a=1;a>0;a--);
}
void I2cStart() //这是I2c的启动函数
{
SDA=1;//根据起始信号图,起始时SDA=1,SCL=1
SCL=1;
delay5us();//然后延时接近5us
SDA=0;//然后变为SDA=0,SCL=1
delay5us();//然后又延时接近5us
SCL=0;//保持SCL=0(SCL始终要保持为低电平)
delay5us();//延时5us保持接收
}
void I2cStop()//这是I2c的终止函数
{
SDA=0;//根据终止信号图,起始时SDA=0,SCL=1
SCL=1;
delay5us();//然后延时接近5us
SDA=1;//之后SDA=1
delay5us();//延时5us保持接收
}
unsigned char I2cReadByte()//这是I2C读取字节的函数
{
unsigned char a=0,dat=0;
SDA=1;
delay5us();//延时接近5us,是为了保证数据的完全传输(根据数据手册数据传输需要4us、4.7us)
for(a=0;a<8;a++)//这里是接收8个字节
{
SCL=1; //SCL由0变1后发送数据
delay5us();
dat&l