GPIO模拟I2C / I2C注意事项

处理器只支持3个i2c通道,常常会不够用,最近写了一个gpio模拟i2c的driver,把模拟的i2c通道加入了i2c-core中,作为第 4 通道,调用接口与标准i2c一致,代码如下:
define DELAY     2 
#define SCL_GPIO  GPIO_I2C_SCL 
#define SDA_GPIO  GPIO_I2C_SDA 
 
static inline void i2c_delay(uint16_t delay) 
{ 
    udelay(delay); 
} 
 
static inline void set_scl_low(void) 
{ 
    gpio_direction_output(SCL_GPIO, 0); 
} 
 
static inline void set_scl_high(void) 
{ 
    gpio_direction_output(SCL_GPIO, 1); 
} 
 
static inline void set_sda_low(void)
{ 
    gpio_direction_output(SDA_GPIO, 0); 
} 
 
static inline void set_sda_high(void) 
{ 
    gpio_direction_output(SDA_GPIO, 1); 
} 
 
static inline void set_sda_in(void) 
{ 
    gpio_direction_input(SDA_GPIO); 
} 
 
static inline uint8_t get_sda_bit(void) 
{ 
    return __gpio_get_value(SDA_GPIO); 
} 
 
int i2c_gpio_init(void) 
{ 
    int err; 
    err = gpio_request(SCL_GPIO, NULL); 
    if (err != 0) 
        return err; 
    err = gpio_request(SDA_GPIO, NULL); 
 
    set_sda_high(); 
    set_scl_high(); 
 
    return err; 
} 
 
void i2c_gpio_free(void) 
{ 
    gpio_free(SDA_GPIO); 
    gpio_free(SCL_GPIO); 
} 
 
static inline void i2c_start(void) 
{ 
    set_sda_high(); 
    i2c_delay(DELAY); 
    set_scl_high(); 
    i2c_delay(DELAY); 
 
    set_sda_low(); 
    i2c_delay(DELAY); 
    set_scl_low(); 
    i2c_delay(DELAY); 
} 
 
static inline void i2c_stop(void) 
{ 
    set_sda_low(); 
    i2c_delay(DELAY); 
    set_scl_high(); 
    i2c_delay(4*DELAY); 
    set_sda_high(); 
    i2c_delay(4*DELAY); 
} 
 
/*
 * return value:
 *      0 ---  收到ACK
 *      1 ---  没收到ACK
 */ 
uint8_t i2c_send_byte(uint8_t send_byte) 
{ 
    uint8_t rc = 0; 
    uint8_t out_mask = 0x80; 
    uint8_t count = 8; 
    uint8_t value; 
 
    while(count > 0) { 
        set_scl_low(); 
        i2c_delay(DELAY); 
        value = ((send_byte & out_mask) ? 1 : 0); 
        if(value == 1) { 
            set_sda_high(); 
        } else { 
            set_sda_low(); 
        } 
        send_byte <<= 1; 
        i2c_delay(DELAY); 
 
        set_scl_high(); 
        i2c_delay(DELAY); 
 
        count--; 
    } 
    set_scl_low(); 
    set_sda_in(); 
    i2c_delay(4*DELAY); 
    set_scl_high(); 
    i2c_delay(DELAY); 
    rc = get_sda_bit(); 
    i2c_delay(DELAY); 
    set_scl_low(); 
 
    return rc; 
} 
 
/*
 * ack = 0 发送ACK
 * ack = 1 发送非ACK停止读取
 */ 
void i2c_read_byte(uint8_t *buffer, uint8_t ack) 
{ 
    uint8_t count = 0x08; 
    uint8_t data = 0x00; 
    uint8_t temp = 0; 
 
    while(count > 0) { 
        set_scl_low(); 
        i2c_delay(2*DELAY); 
        if(count == 8) 
            set_sda_in(); 
        i2c_delay(DELAY); 
        set_scl_high(); 
        i2c_delay(2*DELAY); 
        temp = get_sda_bit(); 
        data <<= 1; 
        if(temp) 
            data |= 0x01; 
 
        i2c_delay(DELAY); 
        count--; 
    } 
 
    set_scl_low(); 
    i2c_delay(2*DELAY); 
    if(ack) { 
        set_sda_high(); 
    } else { 
        set_sda_low(); 
    } 
    i2c_delay(DELAY); 
    set_scl_high(); 
    i2c_delay(2*DELAY); 
 
    *buffer = data; 
    set_scl_low(); 
} 
 
struct atxx_i2c_gpio { 
    struct i2c_adapter adap; 
    struct device *dev; 
    struct clk *clk; 
    struct i2c_msg *msg; 
    spinlock_t lock; 
}; 
 
static int send_i2c(struct atxx_i2c_gpio *i2c) 
{ 
    int i; 
    uint8_t ack; 
 
    spin_lock_irq(&i2c->lock); 
    i2c_start(); 
 
    ack = i2c_send_byte((i2c->msg->addr << 1) | 0x00); 
    if(ack){ 
        goto out; 
    } 
 
    for(i = 0; i < i2c->msg->len; i++) { 
        ack = i2c_send_byte(i2c->msg->buf[i]); 
        if(ack){ 
            goto out; 
        } 
    } 
 
out: 
    i2c_stop(); 
    spin_unlock_irq(&i2c->lock); 
    return ack; 
} 
 
static int recv_i2c(struct atxx_i2c_gpio *i2c) 
{ 
    int i; 
    uint8_t ack; 
 
    spin_lock_irq(&i2c->lock); 
    i2c_start(); 
 
    ack = i2c_send_byte((i2c->msg->addr << 1) | 0x01); 
    if(ack){ 
        goto out; 
    } 
 
    for(i = 0; i < i2c->msg->len - 1; i++) { 
        i2c_read_byte(&i2c->msg->buf[i], 0); 
    } 
 
    i2c_read_byte(&i2c->msg->buf[i2c->msg->len - 1], 1); 
out: 
    i2c_stop(); 
    spin_unlock_irq(&i2c->lock); 
    return ack; 
} 
 
static int i2c_atxx_gpio_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 
{ 
    int i, ret; 
    struct atxx_i2c_gpio *i2c = i2c_get_adapdata(adap); 
 
    for (i = 0; i < num; i++) { 
        i2c->msg        = &msgs[i]; 
        i2c->msg->flags = msgs[i].flags; 
 
        if (i2c->msg->flags & I2C_M_RD) { 
            ret = recv_i2c(i2c); 
            if (ret) { 
                printk("recv_i2c failed. dev_name(%s).addr = 0x%02x\n", 
                     dev_name(i2c->dev), i2c->msg[0].addr); 
                return -EAGAIN; 
            } 
        } else { 
            ret = send_i2c(i2c); 
            if (ret) { 
                printk("send_i2c failed. dev_name(%s).addr = 0x%02x\n", 
                     dev_name(i2c->dev), i2c->msg[0].addr); 
                return -EAGAIN; 
            } 
        } 
    } 
 
    return num; 
} 
 
static uint32_t i2c_atxx_gpio_func(struct i2c_adapter *adap) 
{ 
    return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 
} 
 
static struct i2c_algorithm i2c_atxx_gpio_algo = { 
    .master_xfer   = i2c_atxx_gpio_xfer, 
    .functionality = i2c_atxx_gpio_func, 
}; 
 
static int i2c_atxx_gpio_probe(struct platform_device *pdev) 
{ 
    int ret; 
    struct atxx_i2c_gpio *i2c; 
 
    ret = i2c_gpio_init(); 
    if(ret) { 
        dev_err(&pdev->dev, "couldn't request gpio\n"); 
        return ret; 
    } 
 
    i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); 
    if (!i2c) { 
        dev_err(&pdev->dev, "couldn't allocate memory\n");; 
        ret = -ENOMEM; 
        goto err_mem; 
    } 
 
    spin_lock_init(&i2c->lock); 
 
    i2c->dev = &pdev->dev; 
    i2c->adap.owner = THIS_MODULE; 
    i2c->adap.algo  = &i2c_atxx_gpio_algo; 
    i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; 
    i2c->adap.timeout = I2C_ATXX_TIMEOUT; 
    i2c->adap.retries = I2C_ATXX_RETRIES; 
    i2c->adap.algo_data  = i2c; 
    i2c->adap.dev.parent = &pdev->dev; 
    i2c->adap.nr = pdev->id != -1 ? pdev->id : 0; 
 
    sprintf(i2c->adap.name, "ATXX i2c gpio adapter"); 
    platform_set_drvdata(pdev, i2c); 
    i2c_set_adapdata(&i2c->adap, i2c); 
 
    ret = i2c_add_numbered_adapter(&i2c->adap); 
    if (ret) { 
        dev_err(i2c->dev, "failure adding adapter\n"); 
        goto err_adp; 
    } 
 
    return 0; 
 
err_adp: 
    kfree(i2c); 
err_mem: 
    i2c_gpio_free(); 
    return ret; 
} 
 
static int i2c_atxx_gpio_remove(struct platform_device *pdev) 
{ 
    struct atxx_i2c_gpio *i2c = platform_get_drvdata(pdev); 
 
    platform_set_drvdata(pdev, NULL); 
    i2c_del_adapter(&i2c->adap); 
    kfree(i2c); 
    i2c_gpio_free(); 
 
    return 0; 
} 
 
static int i2c_atxx_gpio_suspend(struct device *dev) 
{ 
    return 0; 
} 
 
static int i2c_atxx_gpio_resume(struct device *dev) 
{ 
    return 0; 
} 
 
static void i2c_atxx_gpio_shutdown(struct platform_device *pdev) 
{ 
    /* TODO: */ 
} 
 
const struct dev_pm_ops i2c_atxx_gpio_pm_ops = { 
    .suspend = i2c_atxx_gpio_suspend, 
    .resume  = i2c_atxx_gpio_resume, 
}; 
 
static struct platform_driver i2c_atxx_gpio_driver = { 
    .driver = { 
        .name  = "i2c-gpio", 
        .owner = THIS_MODULE, 
        .pm    = &i2c_atxx_gpio_pm_ops, 
    }, 
    .probe      = i2c_atxx_gpio_probe, 
    .remove     = i2c_atxx_gpio_remove, 
    .shutdown   = i2c_atxx_gpio_shutdown, 
}; 
 
static int __init i2c_adap_atxx_gpio_init(void) 
{ 
    return platform_driver_register(&i2c_atxx_gpio_driver); 
} 
static void __exit i2c_adap_atxx_gpio_exit(void) 
{ 
    platform_driver_unregister(&i2c_atxx_gpio_driver); 
} 
 
arch_initcall(i2c_adap_atxx_gpio_init); 

2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。

I2C数据格式如下:

无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。

当数据为单字节传送时,格式为:

开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:

开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。

需要注意的是:

1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。

在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,不发送ACK,有时可以起到减少系统开销的效果。开发的过程当中,开发板上的I2C总线有限,如果I2C设备太多的话,就需要用GPIO模拟I2C来解决了。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值