ATtiny下纯软件的I2C程序
编了很长时间硬件的I2C程序,发现在ATtiny下还不如纯软件来得方便,虽然速度不如硬件I2C延时稳定性不如硬件I2C,但是I2C是主机SCL的时钟,倒也无所谓了。//本程序详细说明2线制I2C主从模块之间联系的过程
//MCU:ATtiny x4、x5
//时间:2013-1-3
//本程序详细说明2线制I2C主从模块之间联系的过程
//MCU:ATtiny x4、x5
//时间:2013-1-3
#include <util/delay.h>
/**************************************************/
//软串口定义部分
/**************************************************/
#include <SoftwareSerial.h> //软串口
SoftwareSerial Serial(3, 4); //软串口 RX, TX 针脚位
// SoftwareSerial Serial(0, 1); //软串口 RX, TX 针脚位
/**************************************************/
//测试用BH1751FVI光照度模块定义部分
/**************************************************/
#define I2C_Address_BH1751FVI 0x46 //BH1751FVI芯片I2C写地址
unsigned char BH1751FVI_Pattern = 0x21; //测量模式
/*
0x10 连续高分辨率模式
在1lx 分辨率下开始测量,测量时间一般为120ms,最大180ms
0x11 连续高分辨率模式2
在0.5lx 分辨率下开始测量,测量时间一般为120ms
0x13 连续低分辨率模式
在41lx 分辨率下开始测量,测量时间一般为16ms,最大24ms
0x20 一次高分辨率模式
在1lx 分辨率下开始测量,测量时间一般为120ms,测量后自动设置为断电模式
0x21 一次高分辨率模式2
在0.5lx 分辨率下开始测量,测量时间一般为120ms,测量后自动设置为断电模式
0x23 一次低分辨率模式
在41lx 分辨率下开始测量,测量时间一般为16ms,测量后自动设置为断电模式
*/
unsigned int BH1751FVI_Result = 0; //测量结果
/**************************************************/
//设置SDA、SCL引脚,此处为ATtiny84V的设置
/**************************************************/
#if defined(__AVR_ATtiny84__) | defined(__AVR_ATtiny44__)
#define PORT_SDA PORTA //数据寄存器
#define PORT_SCL PORTA
#define DDR_SDA DDRA //方向寄存器
#define DDR_SCL DDRA
#define PIN_SDA PINA //输入引脚寄存器
#define PIN_SCL PINA
#define PIN_USI_SDA 6 //端口号
#define PIN_USI_SCL 4
#endif
#if defined (__AVR_ATtiny25__) | defined(__AVR_ATtiny45__) | defined(__AVR_ATtiny85__) | \
defined(__AVR_AT90Tiny26__) | defined(__AVR_ATtiny26__)
#define PORT_SDA PORTB //数据寄存器
#define PORT_SCL PORTB
#define DDR_SDA DDRB //方向寄存器
#define DDR_SCL DDRB
#define PIN_SDA PINB //输入引脚寄存器
#define PIN_SCL PINB
#define PIN_USI_SDA 0 //端口号
#define PIN_USI_SCL 2
#endif
//SDA、SCL端口定义
#define SDA_OUTPUT (DDR_SDA|=(1<<PIN_USI_SDA)) //SDA设为输出
#define SDA_INPUT (DDR_SDA&=~(1<<PIN_USI_SDA)) //SDA设为输入
#define SCL_OUTPUT (DDR_SCL|=(1<<PIN_USI_SCL)) //SCL设为输出
#define SCL_INPUT (DDR_SCL&=~(1<<PIN_USI_SCL)) //SCL设为输入
/***************************************************************/
#define SDA_HIGH (PORT_SDA|=(1<<PIN_USI_SDA)) //SDA设为高电平
#define SDA_LOW (PORT_SDA&=~(1<<PIN_USI_SDA)) //SDA设为低电平
#define SCL_HIGH (PORT_SCL|=(1<<PIN_USI_SCL)) //SCL设为高电平
#define SCL_LOW (PORT_SCL&=~(1<<PIN_USI_SCL)) //SCL设为低电平
/***************************************************************/
#define SDA_Release() {SDA_LOW; SDA_INPUT;} //释放SDA为高阻态
#define SCL_Release() {SCL_LOW; SCL_INPUT;} //释放SCL为高阻态
//延时时长
#define SCL_Time_LOW 5 //SCL低电平维持时间,最小5us
#define SCL_Time_HIGH 5 //SCL高电平维持时间,最小5us
unsigned char I2C_datmun = 0; //需要从I2C从器件读取的字节数
void setup()
{
Serial.begin(9600);
I2C_begin();
//以下为单步测试过程
// unsigned int val;
// I2C_Start();
// if (I2C_beginTransmission(I2C_Address_BH1751FVI));
// if (I2C_SendDat(0x21));
// I2C_Stop();
// delay(200);
// I2C_Start();
// if (I2C_SendRdDAdr(I2C_Address_BH1751FVI));
// if (val = I2C_RcvAckDat());
// if (val = (val<<8) + I2C_RcvNAckDat()) Serial.println(val, DEC);
// I2C_Stop();
}
void loop()
{
delay(2000);
Serial.println(BH1751FVI_Read());
}
/**************************************************/
//I2C基础程序部分
/**************************************************/
// void delay_us(unsigned int us) //延时函数(有几个微秒的误差,不影响本程序),此设置针对8MHz(最多8100us)
// {
// unsigned int _count = (us * 8) - 7;
// while(_count) _count--;
// }
bool I2CWaitAck() //等待ACK信号
{
bool _SDA_Level = 0;
SDA_Release(); //释放SDA为高阻态
//读ACK信号
SCL_LOW; //拉低SCL
_delay_us(SCL_Time_LOW); //延时低电平
SCL_HIGH; //拉高SCL
if (PIN_SDA&(1<<PIN_USI_SDA)) _SDA_Level = 1; //如果SDA为高电平,表明收到ASK
_delay_us(SCL_Time_HIGH); //延时高电平
SCL_LOW; //拉低SCL
return _SDA_Level;
}
void I2CSendAck() //发送ACK信号
{
SDA_OUTPUT; //SDA设为输出
//发送1位ACK信号
SCL_LOW; //拉低SCL
SDA_LOW; //拉低SDA
_delay_us(SCL_Time_LOW); //延时低电平
SCL_HIGH; //拉高SCL
_delay_us(SCL_Time_HIGH); //延时高电平
SCL_LOW; //拉低SCL
SDA_Release(); //释放SDA为高阻态
}
//关闭I2C
void I2C_Stop()
{
SDA_OUTPUT; //SDA设为输出
//发停止信号
SCL_HIGH; //拉高SCL
_delay_us(SCL_Time_LOW); //延时
SDA_HIGH; //拉高SDA
_delay_us(SCL_Time_LOW); //延时
}
//启动I2C
void I2C_Start()
{
SDA_OUTPUT; //SDA设为输出
//发送起始信号
SDA_LOW; //拉低SDA
_delay_us(SCL_Time_LOW); //延时
SCL_LOW; //拉低SCL
_delay_us(SCL_Time_LOW); //延时
}
//发送7位器件 写地址:XXXX XXX0
bool I2C_SendWrDAdr(unsigned char wrDAdr)
{
//发送写地址信息包
if (I2C_SendDat(wrDAdr)) return 1;
else return 0;
}
//发送7位器件 读地址:XXXX XXX1
bool I2C_SendRdDAdr(unsigned char rdDAdr)
{
//发送写地址信息包
if (I2C_SendDat(rdDAdr+1)) return 1;
else return 0;
}
//发送一个字节
bool I2C_SendDat(unsigned char configDat)
{
SDA_OUTPUT; //SDA设为输出
unsigned char _i = 8; //要发送的bit数
//发送一个字节
do{
_i--;
SCL_LOW; //拉低SCL电平
if (bitRead(configDat,_i)) SDA_HIGH; //如果此位为1则拉高SDA电平
else SDA_LOW; //否则拉低SDA电平
_delay_us(SCL_Time_LOW); //延时低电平
SCL_HIGH; //拉高SCL电平
_delay_us(SCL_Time_HIGH); //延时高电平
}while(_i); //_i为0时结束
SCL_LOW; //拉低SCL
//等待ACK信号
if (I2CWaitAck()) return 1;
return 0;
}
//接收一个字节(不产生应答)
unsigned char I2C_RcvNAckDat()
{
unsigned char val = 0;
SDA_Release(); //释放SDA为高阻态
unsigned char _i = 8; //要发送的bit数
//接收一个字节
do{
_i--;
SCL_LOW; //拉低SCL电平
_delay_us(SCL_Time_LOW); //延时低电平
SCL_HIGH; //拉高SCL电平
if (PIN_SDA&(1<<PIN_USI_SDA)) bitSet(val,_i); //如果读到SDA高电平,此结果位置1
_delay_us(SCL_Time_HIGH); //延时高电平
}while(_i); //_i为0时结束
SCL_LOW; //拉低SCL
return val;
}
//接收一个字节(产生应答)
unsigned char I2C_RcvAckDat()
{
unsigned char val = 0;
SDA_Release(); //释放SDA为高阻态
unsigned char _i = 8; //要发送的bit数
//接收一个字节
do{
_i--;
SCL_LOW; //拉低SCL电平
_delay_us(SCL_Time_LOW); //延时低电平
SCL_HIGH; //拉高SCL电平
if (PIN_SDA&(1<<PIN_USI_SDA)) bitSet(val,_i); //如果读到SDA高电平,此结果位置1
_delay_us(SCL_Time_HIGH); //延时高电平
}while(_i); //_i为0时结束
SCL_LOW; //拉低SCL
//发送ACK信号
I2CSendAck();
return val;
}
/**************************************************/
//I2C应用程序部分
/**************************************************/
//设置I2C
void I2C_begin()
{
SDA_OUTPUT; //SDA设为输出
SCL_OUTPUT; //SDA设为输出
SDA_HIGH; //SDA设为高电平
SCL_HIGH; //SDA设为高电平
_delay_us(100);
}
//开启I2C
bool I2C_beginTransmission(unsigned char wrDAdr)
{
I2C_Start(); //启动I2C
if (I2C_SendWrDAdr(wrDAdr)) return 1; //发送7位器件 写地址
return 0;
}
//发送一个字节地址或命令
// 发送一个字节
void I2C_write(unsigned char dat)
{
if (I2C_SendDat(dat));
}
//准备接收数据
// 重启I2C - 发送器件读地址
void I2C_requestFrom(unsigned char rdDAdr, unsigned char num)
{
I2C_Start();
if (I2C_SendRdDAdr(rdDAdr));
I2C_datmun = num;
}
//接收一个字节
// 接收一个字节
unsigned char I2C_read()
{
if ((I2C_datmun--) <= 0) return 0; //如果已经读完,直接退出
if (I2C_datmun) return I2C_RcvAckDat(); //未读完时产生应答
else return I2C_RcvNAckDat(); //读最后一个字节时不产生应答
}
//关闭I2C
// 停止I2C
void I2C_endTransmission()
{
I2C_Stop();
}
/**************************************************/
//测试用BH1751FVI光照度模块I2C读取
// 开启I2C - 发送一个字节命令 - 延时等待I2C从器件把数据准备好 - 准备接收数据 - 接收一个字节 - 再接收一个字节 - 关闭I2C
// I2C全时序:启动I2C - 发送器件写地址 - 发送一个字节 - 延时 - 重启I2C - 发送器件读地址 - 接收N个字节 - 关闭I2C
/**************************************************/
unsigned int BH1751FVI_Read()
{
//发送测量命令
I2C_beginTransmission(I2C_Address_BH1751FVI); //启动I2C
{ //自动选择测量量程
if (BH1751FVI_Result < 200) BH1751FVI_Pattern = 0x21; //一次高分辨率模式2
else if (BH1751FVI_Result < 1000) BH1751FVI_Pattern = 0x20; //一次高分辨率模式
else BH1751FVI_Pattern = 0x23; //一次低分辨率模式
}
I2C_write(BH1751FVI_Pattern); //命令-测量模式
I2C_endTransmission(); //关闭I2C
//取得测量结果
if (BH1751FVI_Pattern == 0x13 || BH1751FVI_Pattern == 0x23) delay(30); //低分辨率下测量等待时间
else delay(200); //高分辨率下测量等待时间
I2C_requestFrom(I2C_Address_BH1751FVI, 2); //要求回传测量结果
BH1751FVI_Result = I2C_read();
BH1751FVI_Result = (BH1751FVI_Result<<8) | I2C_read();
I2C_endTransmission(); //关闭I2C
//计算最终值
unsigned int tmp = 0;
for (unsigned char i = 0; i < 16; i++)
{
tmp += pow(2*bitRead(BH1751FVI_Result, i), i);
}
return BH1751FVI_Result = tmp/1.2; //返回测量结果值
}
via - 极客工坊