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 - 极客工坊

标签: Arduino教程