天天爱玛丽 发表于 2024-3-20 14:38:27

一个arduino解析I2C协议的例子,自己备忘


arduino的库只能使用bOOt-L0ader中编号的IO,使用了非编号IO,库就不能用了,分享个解决模拟I2C主机协议的示例,
就是一个头文件sim_iic.h, 内容如下:
#ifndef __SIM_IIC_H__
#define __SIM_IIC_H__

#include <avr/io.h>

//Please include this file into your project. e.g. #include "sim_iic.h"

//Do not modify the following values,
//This address corresponds to the chip end.
#define SLAW0x5A
#define SLAR0x5B

typedef enum { I2C1=1, I2C2, I2C3 }DEV;

//Defined IO number of Simulated IIC
#define Pin_SDA1    7 //F7
#define Pin_SDA2    6 //F6
#define Pin_SDA3    5 //F5
#define Pin_SCL   2 //E2

//Defined Port of Simulated IIC
#define PORT_SDAPORTF
#define PIN_SDA   PINF
#define DDR_SDA   DDRF
#define PORT_SCLPORTE
#define DDR_SCL   DDRE
#define PIN_SCL   PINE

#define SDA1_L   do {bitClear(PORT_SDA, Pin_SDA1); bitSet(DDR_SDA,Pin_SDA1); nops();} while(0)
#define SDA1_H   do {bitClear(DDR_SDA,Pin_SDA1); bitSet(PORT_SDA, Pin_SDA1); nops();} while(0)
#define SDA1_GET   ((uint8_t)(PIN_SDA & bit(Pin_SDA1)) ? 1 : 0)

#define SDA2_L   do {bitClear(PORT_SDA, Pin_SDA2); bitSet(DDR_SDA,Pin_SDA2); nops();} while(0)
#define SDA2_H   do {bitClear(DDR_SDA,Pin_SDA2); bitSet(PORT_SDA, Pin_SDA2); nops();} while(0)
#define SDA2_GET   ((uint8_t)(PIN_SDA & bit(Pin_SDA2)) ? 1 : 0)

#define SDA3_L   do {bitClear(PORT_SDA, Pin_SDA3); bitSet(DDR_SDA,Pin_SDA3); nops();} while(0)
#define SDA3_H   do {bitClear(DDR_SDA,Pin_SDA3); bitSet(PORT_SDA, Pin_SDA3); nops();} while(0)
#define SDA3_GET   ((uint8_t)(PIN_SDA & bit(Pin_SDA3)) ? 1 : 0)

#define SDA_L(dev)\
do { \
    switch(dev) { \
      case I2C1: SDA1_L; break; \
      case I2C2: SDA2_L; break; \
      case I2C3: SDA3_L; break; \
    } \
} while(0)

#define SDA_H(dev)\
do { \
    switch(dev) { \
      case I2C1: SDA1_H; break; \
      case I2C2: SDA2_H; break; \
      case I2C3: SDA3_H; break; \
    } \
} while(0)

inline uint8_t SDA_GET(DEV dev)
{
switch(dev)
{
    case I2C1: return SDA1_GET;
    case I2C2: return SDA2_GET;
    case I2C3: return SDA3_GET;
}
}

#define SCL_L   do { bitClear(PORT_SCL, Pin_SCL); bitSet(DDR_SCL,Pin_SCL); nops();} while(0)
#define SCL_H   do { bitClear(DDR_SCL,Pin_SCL); bitSet(PORT_SCL, Pin_SCL); nops();} while(0)
#define SCL_PIN   ((uint8_t)(PIN_SCL & bit(Pin_SCL)) ? 1 : 0) //unused

#define ACK   1
#define NOACK   0

inline void nops(void)
{
    uint8_t dly = 10;
    while (--dly){ _NOP();}
}

inline void delayus(uint8_t n)
{
    while (--n) { nops(); }
}

inline void i2c_start(DEV dev)
{
    SDA_H(dev);
    SCL_H;
    delayus(10);
    SDA_L(dev);
    SCL_L;
}

inline void i2c_stop(DEV dev)
{
    SCL_L;
    SDA_L(dev);
    SCL_H;
    SDA_H(dev);
    delayus(10);
}

//Send ACK (LOW)
inline void i2c_ack(DEV dev, uint8_t ack)
{
    if (ack) {
      SDA_H(dev);
    } else {
      SDA_L(dev);
    }
    SCL_H;
    SCL_L;
}

//write a byte, return ack
inline uint8_t i2c_write_byte(DEV dev, uint8_t dat)
{
    uint8_t i, ack=0;

    for (i=0; i<8; i++)
    {
      if(dat & 0x80) {
            SDA_H(dev);
      } else {
            SDA_L(dev);
      }
      SCL_H;
      SCL_L;
      dat <<= 1;
    }
    SCL_H;
    ack = SDA_GET(dev); //从机应答
    SCL_L;
   
    return ack;
}

//read a byte, ack=1:ack, ack=0:no ack
inline uint8_t i2c_read_byte(DEV dev)
{
    uint8_t i, chr=0;
   
    SDA_H(dev);
    for (i=0; i<8; i++)
    {
      SCL_H;
      chr <<= 1;
      if (SDA_GET(dev)) chr += 1; //从机返回的数据
      SCL_L;
    }

    return chr;
}

//call this function to write iic device. do not use it now!!
inline void i2c_write(DEV dev, uint8_t addr, uint8_t *p, uint8_t len)
{
    uint8_t i;
   
    i2c_start(dev);
    i2c_write_byte(dev, SLAW);
    i2c_write_byte(dev, addr);
   
    for (i=0; i<len; i++)
    {
      i2c_write_byte(dev, *p);
      p++;
    }

    i2c_stop(dev);
}

//call this function to read iic device.
inline void i2c_read(DEV dev, uint8_t addr, uint8_t *p, uint8_t len)
{
    uint8_t i;

    i2c_start(dev);
    i2c_write_byte(dev, SLAW); //write
    i2c_write_byte(dev, addr);
   
    i2c_start(dev);
    i2c_write_byte(dev, SLAR); //read
   
    for(i=0; i<len; i++)
    {
      *p++ = i2c_read_byte(dev);
      i2c_ack(dev, (i==(len-1)));
    }
    i2c_stop(dev);
}

#endif


使用也很简单,例如读取iic从设备地址0开始的4个字节:
i2c_read(dev, 0x00, recvbuf, 4);

天天爱玛丽 发表于 2024-3-20 14:40:01

我自己写的,没版权问题,发这里就是为了备忘,
任何代码带来的问题,我不负责的.

radio988931 发表于 2024-3-20 15:05:06

感谢分享!

iffi123 发表于 2024-3-20 15:38:02

连续读的时序有问题,很多人都忽略这个细节

天天爱玛丽 发表于 2024-3-20 15:44:54

iffi123 发表于 2024-3-20 15:38
连续读的时序有问题,很多人都忽略这个细节

是个行家
我知道,一般能适应,没遇到有问题的.
页: [1]
查看完整版本: 一个arduino解析I2C协议的例子,自己备忘