矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 409|回复: 4

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

[复制链接]
     
发表于 2024-3-20 14:38:27 | 显示全部楼层 |阅读模式

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 SLAW  0x5A
#define SLAR  0x5B

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_SDA  PORTF
#define PIN_SDA   PINF
#define DDR_SDA   DDRF
#define PORT_SCL  PORTE
#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 | 显示全部楼层
我自己写的,没版权问题,发这里就是为了备忘,
任何代码带来的问题,我不负责的.
回复 支持 反对

使用道具 举报

     
发表于 2024-3-20 15:05:06 | 显示全部楼层
感谢分享!
回复 支持 反对

使用道具 举报

     
发表于 2024-3-20 15:38:02 | 显示全部楼层
连续读的时序有问题,很多人都忽略这个细节

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

     
 楼主| 发表于 2024-3-20 15:44:54 | 显示全部楼层
iffi123 发表于 2024-3-20 15:38
连续读的时序有问题,很多人都忽略这个细节

是个行家
我知道,一般能适应,没遇到有问题的.
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-5-5 12:52

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表