前言
比赛时会提供驱动文件,包含IIC初始化函数,IIC写入函数,IIC读取函数,发送应答信号函数,接收应答信号函数·。对于at24c02需要写一个读取函数和写入函数,PCF8591需要adc转换函数和一个dac转换函数。
代码
iic.c
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "intrins.h"
#include "iic.h"
#define DELAY_TIME 5
/* 引脚定义,需要自己补充的部分 */
sbit sda = P2^1; /* 数据线 */
sbit scl = P2^0; /* 时钟线 */
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
/********************* at24c02 **************************/
/*** 读取函数,参数为要读取的地址 ***/
unsigned char iic_eeprom_read(unsigned char add)
{
unsigned char dat;
I2CStart();//初始化iic总线
I2CSendByte(0xa0);//发送从机 (写) 地址
I2CWaitAck();//等待从机答应信号
I2CSendByte(add);//写入要访问的地址
I2CWaitAck(); //等待从机答应信号
I2CStop();//结束本轮通讯
I2CStart();//重新初始化iic总线
I2CSendByte(0xa1);//发送从机 (读) 地址
I2CWaitAck();//等待从机答应信号
dat = I2CReceiveByte();//读取地址中的数据
I2CSendAck(1);//发送答应信号
I2CStop();//结束通讯
return dat;
}
/*** 写入函数,参数1为要写入的地址,参数1要写入的数据 ***/
/*** 注意连续写入时要加10ms延时确保等到上一字节数据写入在写入下一字节 ***/
void iic_eeprom_write(unsigned char add,unsigned char dat)
{
I2CStart();//初始化iic总线
I2CSendByte(0xa0);//发送从机 (写) 地址
I2CWaitAck();//等待从机答应信号
I2CSendByte(add);//写入要访问的地址
I2CWaitAck();//等待从机答应信号
I2CSendByte(dat);//向访问地址写入数据
I2CWaitAck(); //等待从机答应信号
I2CStop();//结束通讯
}
/********************* adc\dac **************************/
/* PCF8591AD转换函数,channel为模拟输入的通道 3光敏、1电位器、0 A/D */
unsigned char PCF8591_AD_Conversion(unsigned char channel )
{
unsigned char dat = 0;
I2CStart();//初始化iic总线
I2CSendByte(0x90);//写入从机PCF8591的(写)地址,
I2CWaitAck(); //等待从机应答信号
I2CSendByte(0x00 | channel); //写入控制指令,单端输入,AD转换,选择通道channel
I2CWaitAck(); //这一步不能少,否则读出来一直是255
I2CStop(); //结束本次通信,改变收发方向
I2CStart();//初始化iic总线
I2CSendByte(0x91);//写入从机PCF8591的 (读) 地址,
I2CWaitAck();
dat = I2CReceiveByte();//读取AD转换出的数字量
I2CSendAck(1);//不应答
I2CStop();//结束通信
return dat;
}
/* PCF8591DA转换函数,dat为输入的数字量 */
void PCF8591_DA_Conversion(unsigned char dat)
{
I2CStart(); //iic通信开始
I2CSendByte(0x90); //写入PCF8591的写地址
I2CWaitAck();
I2CSendByte(0x40); //写入控制指令,DA转换
I2CWaitAck(); //等待应答
I2CSendByte(dat); //写入数字量
I2CWaitAck(); //等待应答
I2CStop(); //结束通信
}
iic.h
#ifndef _IIC_H
#define _IIC_H
#include <STC15F2K60S2.H>
void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void IIC_SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);
/* 自己写的程序 */
unsigned char iic_eeprom_read(unsigned char add);
void iic_eeprom_write(unsigned char add,unsigned char dat);
unsigned char PCF8591_AD_Conversion(unsigned char channel );
void PCF8591_DA_Conversion(unsigned char dat);
#endif
main.c
#include <hc138_hs.H>
#include "iic.h"
/* 应用 */
void main(void)
{
unsigned int adc1,adc2,dac1 = 255;
char at24c02_data = 0;
//iic_eeprom_write(0x01,0);//将这个地址的数据清零(初始化的时候调用一次)
at24c02_data = iic_eeprom_read(0x01);//读取at24c02地址 0x01 中的数据
iic_eeprom_write(0x01,++at24c02_data);//将数值加一再写回去,记录单片机的开机次数
while(1)
{
//使用数码管1、2位显示开机次数
smg_SetOne(1,at24c02_data/10);
smg_SetOne(2,at24c02_data%10);
//adc、dac
adc1 = PCF8591_AD_Conversion(1);//读取光敏电阻分压的电压的数字量
adc1 = adc1*50/255; //电压计算,放大10倍 ( 5v -> 50 )
adc2 = PCF8591_AD_Conversion(3);//读取电位器上分压的电压的数字量
PCF8591_DA_Conversion(adc2);//将读取到的电压值再用dac输出
adc2 = adc2*50/255;//电压计算,放大10倍 ( 5v -> 50 )
//数码管显示敏电阻分压的电压
smg_SetOne(4,adc1/10 +10);//加10,为带点的段码
smg_SetOne(5,adc1%10);
//数码管显示电位器上分压的电压
smg_SetOne(7,adc2/10 +10);//加10,为带点的段码
smg_SetOne(8,adc2%10);
}
}
效果