程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> i2c - gpio

i2c - gpio

編輯:C++入門知識

處理器只支持3個i2c通道,常常會不夠用,最近寫了一個gpio模擬i2c的driver,把模擬的i2c通道加入了i2c-core中,作為第 4 通道,調用接口與標准i2c一致,代碼如下:
[cpp] 
#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); 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved