DA转换(IIC总线)

项目描述
向PCF8591芯片的AN2通道输入一个电压模拟量,经过AD转换后,单片机通过IIC总线读取转换后的数据,并将其量化后实时显示在LCD上。然后将AD转换后的数据写入PCF8591中进行DA转换,并用LED灯的亮度来显示模拟量的大小。
仿真原理图如下:
DA转换(IIC总线)_第1张图片
C语言代码如下:

/*------------------------
FileName: DAC.h
Function: 头文件
Author: Zhang Kaizhou
Date: 2019-6-13 17:12:49
------------------------*/
#include 
#include 
#include 
#define uchar unsigned char
#define uint unsigned int
#define FACTOR 0.01953125 // 量化单位(用一个字节来量化表示5V的电压值,共为256个量化等级; FACTOR = 5 / 256)
	
/*IIC总线端口定义*/
sbit SDA = P2^0;
sbit SCL = P2^1;

/*LCD1602显示模块端口定义*/
sbit lcdrs = P1^0;
sbit lcdrw = P1^1;
sbit lcden = P1^2;

/*IIC总线模块函数声明*/
void initIIC();
void startIIC();
void responseIIC();
void stopIIC();
void writeDataIIC(uchar dat);
void readDataIIC(uchar * dat);

/*DAC模块函数声明*/
void writeData8591(uchar dat);
void readData8591(uchar channel, uchar * dat);

/*LCD1602显示模块函数声明*/
void writeCommand(uchar command);
void writeDataLCD(uchar dat);
void delay(uint xms);
void LCDInit();
void display(uchar dat);
/*-----------------------------------------------------
FileName: main.c
Function: ADC IIC
Description: 向PCF8591芯片的AN2通道输入一个电压模拟量,
经过AD转换后,单片机通过IIC总线读取转换后的数据,
并将其量化后实时显示在LCD上。
然后将AD转换后的数据写入PCF8591中进行DA转换,
并用LED灯的亮度来显示模拟量的大小。
Author: Zhang Kaizhou
Date: 2019-6-13 17:12:40
------------------------------------------------------*/
#include "DAC.h"

void main(){
	uchar dat = 0;
	initIIC();
	LCDInit();
	while(1){
		/*AD转换*/
		readData8591(2, &dat); // 读取通道2模拟输入量化后的数据
		display(dat);	
		delay(10);
		
		/*DA转换*/
		writeData8591(dat);
		delay(10);
	}
}

/*向PCF85991写数据*/
void writeData8591(uchar dat){
	/*向PCF8591写入地址字节和控制字节*/
	startIIC();
	writeDataIIC(0x90); // 向IIC总线发送寻址信号(PCF8591器件地址位90H),并声明要进行写操作
	responseIIC();
	writeDataIIC(0x40); // 向PCF8591写入控制字节(DA允许; AD为4路单端输入; 禁止自动增益)
	responseIIC();
	
	/*向PCF8591中写入要进行DA转换的数据*/
	startIIC();
	responseIIC();
	writeDataIIC(0x90);
	responseIIC();
	writeDataIIC(dat);
	responseIIC();
	stopIIC();
}

/*从PCF8591的channel模拟输入所对应的ADC数据寄存器中读出数据到变量dat*/
void readData8591(uchar channel, uchar * dat){
	/*向PCF8591写入地址字节和控制字节*/
	startIIC();
	writeDataIIC(0x90); // 向IIC总线发送寻址信号(PCF8591器件地址位90H),并声明要进行写操作
	responseIIC();
	writeDataIIC(0x40 | channel); // 向PCF8591写入控制字节(DA允许; AD为4路单端输入; 禁止自动增益; 选择通道channel)
	responseIIC();
	
	/*从PCF8591读取数据*/
	startIIC();
	writeDataIIC(0x91); // 向IIC总线发送寻址信号,并声明要进行读操作
	responseIIC();
	readDataIIC(dat);
	stopIIC(); // 注意此处无响应信号,直接关闭IIC总线即可(否则会导致读取的数据不稳定)
}
/*----------------------------
FileName: IIC.c
Function: 实现IIC总线基本操作
Author: Zhang Kaizhou
Date: 2019-6-13 17:13:07
----------------------------*/
#include "DAC.h"

/*IIC总线初始化*/
void initIIC(){
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
}

/*IIC总线启动信号*/
void startIIC(){ // 在SCL高电平期间,SDA产生一个下降沿表示启动
	SDA = 1;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 0;
	_nop_();
}

/*IIC总线应答信号*/
void responseIIC(){ // 从机在SCL高电平期间将SDA拉低表示应答
	SDA = 0;
	_nop_();
	SCL = 1;
	_nop_();
	SCL = 0;
	_nop_();
}

/*IIC总线停止信号*/
void stopIIC(){ // 在SCL高电平期间,SDA产生一个上升沿表示停止
	SDA = 0;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 1;
	_nop_();
}

/*向IIC总线写一个字节数据*/
void writeDataIIC(uchar dat){
	uchar i, temp;
	temp = dat;
	for(i = 0; i < 8; i++){
		temp = temp << 1;
		SCL = 0;
		_nop_();
		SDA = CY;
		_nop_();
		SCL = 1;
		_nop_();
	}
	SCL = 0;
	_nop_();
	SDA = 1;
	_nop_();
}

/*从IIC总线读一个字节数据*/
void readDataIIC(uchar * dat){
	uchar i;
	SCL = 0;
	_nop_();
	SDA = 1;
	_nop_();
	for(i = 0; i < 8; i++){
		SCL = 1;
		_nop_();
		* dat = ((* dat) << 1) | SDA;
		SCL = 0;
		_nop_();
	}
}
/*-----------------------------
FileName:display.c
Function: LCD1602显示函数
Author: Zhang Kaizhou
Date: 2019-6-13 17:12:59
------------------------------*/
#include "DAC.h"

uchar code table0[] = {"ADC"};
uchar code table1[] = {"Voltage:"};
uchar code table2[] = {"0123456789"};
uchar num = 0;

/*初始化LCD1602的设置*/
void LCDInit(){
	uchar i;
	lcden = 0; // 拉低使能端,准备产生使能高脉冲信号
	writeCommand(0x38); // 显示模式设置(16x2, 5x7点阵,8位数据接口)
	writeCommand(0x0c); // 开显示,不显示光标
	writeCommand(0x06); // 写一个字符后地址指针自动加1
	writeCommand(0x01); // 显示清零,数据指针清零
	
	/*LCD上电界面*/
	writeCommand(0x80); // 将数据指针定位到第一行首
	for(i = 0; i < strlen(table0); i++){
		writeDataLCD(table0[i]);
		delay(5);
	}
	
	writeCommand(0x80 + 0x40); // 将数据指针定位到第二行首
	for(i = 0; i < strlen(table1); i++){
		writeDataLCD(table1[i]);
		delay(5);
	}
}

/*LCD显示函数*/
void display(uchar dat){
	/*数据的量化*/
	float temp = FACTOR * dat;
	uchar i, j;
	if(dat == 255){
		i = 5;
		j = 0;
	}else{ // 分离整数和小数部分
		i = (uchar)temp;
		j = (temp - i) * 10;
	}
	
	/*数据显示*/
	writeCommand(0x80 + 0x40 + strlen(table1));
	writeDataLCD(table2[i]);
	delay(5);
	writeCommand(0x80 + 0x40 + strlen(table1) + 1);
	writeDataLCD('.');
	delay(5);
	writeCommand(0x80 + 0x40 + strlen(table1) + 2);
	writeDataLCD(table2[j]);
	delay(5);
}

/*写指令函数*/
void writeCommand(uchar command){
	lcdrs = 0; // 命令选择
	lcdrw = 0;
	P0 = command;
	delay(5);
	
	lcden = 1; // 产生一个正脉冲使能信号
	delay(5);
	lcden = 0;
}

/*写数据函数*/
void writeDataLCD(uchar dat){
	lcdrs = 1; // 数据选择
	lcdrw = 0;
	P0 = dat;
	delay(5);
	
	lcden = 1;
	delay(5);
	lcden = 0;
}

/*延时xms函数*/
void delay(uint xms){
	uint i, j;
	for(i = xms; i > 0; i--)
		for(j = 110; j > 0; j--);
}

你可能感兴趣的:(C51)