新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > I2C總線驅動程序

        I2C總線驅動程序

        作者: 時間:2016-11-21 來源:網絡 收藏
        #include "linux/kernel.h"
        #include "linux/module.h"
        #include "linux/i2c.h"
        #include "linux/init.h"
        #include "linux/time.h"
        #include "linux/interrupt.h"
        #include "linux/delay.h"
        #include "linux/errno.h"
        #include "linux/err.h"
        #include "linux/platform_device.h"
        #include "linux/pm_runtime.h"
        #include "linux/clk.h"
        #include "linux/cpufreq.h"
        #include "linux/slab.h"
        #include "linux/io.h"
        #include "linux/of_i2c.h"
        #include "linux/of_gpio.h"
        #include "plat/gpio-cfg.h"
        #include "mach/regs-gpio.h"
        #include "asm/irq.h"
        #include "plat/regs-iic.h"
        #include "plat/iic.h"
        //#define PRINTK printk
        #define PRINTK(...)
        enum s3c24xx_i2c_state {
        STATE_IDLE,
        STATE_START,
        STATE_READ,
        STATE_WRITE,
        STATE_STOP
        };
        struct s3c2440_i2c_regs {
        unsigned int iiccon;
        unsigned int iicstat;
        unsigned int iicadd;
        unsigned int iicds;
        unsigned int iiclc;
        };
        struct s3c2440_i2c_xfer_data {
        struct i2c_msg *msgs;
        int msn_num;
        int cur_msg;
        int cur_ptr;
        int state;
        int err;
        wait_queue_head_t wait;
        };
        static struct s3c2440_i2c_xfer_data s3c2440_i2c_xfer_data;
        static struct s3c2440_i2c_regs *s3c2440_i2c_regs;
        static void s3c2440_i2c_start(void)
        {
        s3c2440_i2c_xfer_data.state = STATE_START;
        if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) // 讀 //
        {
        s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << 1;
        s3c2440_i2c_regs->iicstat = 0xb0; // 主機接收,啟動
        }
        else // 寫 //
        {
        s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->addr << 1;
        s3c2440_i2c_regs->iicstat = 0xf0; // 主機發送,啟動
        }
        }
        static void s3c2440_i2c_stop(int err)
        {
        s3c2440_i2c_xfer_data.state = STATE_STOP;
        s3c2440_i2c_xfer_data.err = err;
        PRINTK("STATE_STOP, err = %dn", err);
        if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) // 讀 //
        {
        // 下面兩行恢復I2C操作,發出P信號
        s3c2440_i2c_regs->iicstat = 0x90;
        s3c2440_i2c_regs->iiccon = 0xaf;
        ndelay(50); // 等待一段時間以便P信號已經發出
        }
        else // 寫 //
        {
        // 下面兩行用來恢復I2C操作,發出P信號
        s3c2440_i2c_regs->iicstat = 0xd0;
        s3c2440_i2c_regs->iiccon = 0xaf;
        ndelay(50); // 等待一段時間以便P信號已經發出
        }
        // 喚醒 //
        wake_up(&s3c2440_i2c_xfer_data.wait);
        }
        static int s3c2440_i2c_xfer(struct i2c_adapter *adap,
        struct i2c_msg *msgs, int num)
        {
        unsigned long timeout;
        // 把num個msg的I2C數據發送出去/讀進來 //
        s3c2440_i2c_xfer_data.msgs = msgs;
        s3c2440_i2c_xfer_data.msn_num = num;
        s3c2440_i2c_xfer_data.cur_msg = 0;
        s3c2440_i2c_xfer_data.cur_ptr = 0;
        s3c2440_i2c_xfer_data.err = -ENODEV;
        s3c2440_i2c_start();
        // 休眠 //
        timeout = wait_event_timeout(s3c2440_i2c_xfer_data.wait, (s3c2440_i2c_xfer_data.state == STATE_STOP), HZ * 5);
        if (0 == timeout)
        {
        printk("s3c2440_i2c_xfer time outn");
        return -ETIMEDOUT;
        }
        else
        {
        return s3c2440_i2c_xfer_data.err;
        }
        }
        static u32 s3c2440_i2c_func(struct i2c_adapter *adap)
        {
        return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
        }
        static const struct i2c_algorithm s3c2440_i2c_algo = {
        // .smbus_xfer = ,
        .master_xfer = s3c2440_i2c_xfer,
        .functionality = s3c2440_i2c_func,
        };
        // 1. 分配/設置i2c_adapter
        //
        static struct i2c_adapter s3c2440_i2c_adapter = {
        .name = "s3c2440_100ask",
        .algo = &s3c2440_i2c_algo,
        .owner = THIS_MODULE,
        };
        static int isLastMsg(void)
        {
        return (s3c2440_i2c_xfer_data.cur_msg == s3c2440_i2c_xfer_data.msn_num - 1);
        }
        static int isEndData(void)
        {
        return (s3c2440_i2c_xfer_data.cur_ptr >= s3c2440_i2c_xfer_data.msgs->len);
        }
        static int isLastData(void)
        {
        return (s3c2440_i2c_xfer_data.cur_ptr == s3c2440_i2c_xfer_data.msgs->len - 1);
        }
        static irqreturn_t s3c2440_i2c_xfer_irq(int irq, void *dev_id)
        {
        unsigned int iicSt;
        iicSt = s3c2440_i2c_regs->iicstat;
        if(iicSt & 0x8){ printk("Bus arbitration failednr"); }
        switch (s3c2440_i2c_xfer_data.state)
        {
        case STATE_START : // 發出S和設備地址后,產生中斷 //
        {
        PRINTK("Startn");
        // 如果沒有ACK, 返回錯誤 //
        if (iicSt & S3C2410_IICSTAT_LASTBIT)
        {
        s3c2440_i2c_stop(-ENODEV);
        break;
        }
        if (isLastMsg() && isEndData())
        {
        s3c2440_i2c_stop(0);
        break;
        }
        // 進入下一個狀態 //
        if (s3c2440_i2c_xfer_data.msgs->flags & I2C_M_RD) // 讀 //
        {
        s3c2440_i2c_xfer_data.state = STATE_READ;
        goto next_read;
        }
        else
        {
        s3c2440_i2c_xfer_data.state = STATE_WRITE;
        }
        }
        case STATE_WRITE:
        {
        PRINTK("STATE_WRITEn");
        // 如果沒有ACK, 返回錯誤 //
        if (iicSt & S3C2410_IICSTAT_LASTBIT)
        {
        s3c2440_i2c_stop(-ENODEV);
        break;
        }
        if (!isEndData()) // 如果當前msg還有數據要發送 //
        {
        s3c2440_i2c_regs->iicds = s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr];
        s3c2440_i2c_xfer_data.cur_ptr++;
        // 將數據寫入IICDS后,需要一段時間才能出現在SDA線上
        ndelay(50);
        s3c2440_i2c_regs->iiccon = 0xaf; // 恢復I2C傳輸
        break;
        }
        else if (!isLastMsg())
        {
        // 開始處理下一個消息 //
        s3c2440_i2c_xfer_data.msgs++;
        s3c2440_i2c_xfer_data.cur_msg++;
        s3c2440_i2c_xfer_data.cur_ptr = 0;
        s3c2440_i2c_xfer_data.state = STATE_START;
        // 發出START信號和發出設備地址 //
        s3c2440_i2c_start();
        break;
        }
        else
        {
        // 是最后一個消息的最后一個數據 //
        s3c2440_i2c_stop(0);
        break;
        }
        break;
        }
        case STATE_READ:
        {
        PRINTK("STATE_READn");
        // 讀出數據 //
        s3c2440_i2c_xfer_data.msgs->buf[s3c2440_i2c_xfer_data.cur_ptr] = s3c2440_i2c_regs->iicds;
        s3c2440_i2c_xfer_data.cur_ptr++;
        next_read:
        if (!isEndData()) // 如果數據沒讀完, 繼續發起讀操作 //
        {
        if (isLastData()) // 如果即將讀的數據是最后一個, 不發ack //
        {
        s3c2440_i2c_regs->iiccon = 0x2f; // 恢復I2C傳輸,接收到下一數據時無ACK
        }
        else
        {
        s3c2440_i2c_regs->iiccon = 0xaf; // 恢復I2C傳輸,接收到下一數據時發出ACK
        }
        break;
        }
        else if (!isLastMsg())
        {
        // 開始處理下一個消息 //
        s3c2440_i2c_xfer_data.msgs++;
        s3c2440_i2c_xfer_data.cur_msg++;
        s3c2440_i2c_xfer_data.cur_ptr = 0;
        s3c2440_i2c_xfer_data.state = STATE_START;
        // 發出START信號和發出設備地址 //
        s3c2440_i2c_start();
        break;
        }
        else
        {
        // 是最后一個消息的最后一個數據 //
        s3c2440_i2c_stop(0);
        break;
        }
        break;
        }
        default: break;
        }
        // 清中斷 //
        s3c2440_i2c_regs->iiccon &= ~(S3C2410_IICCON_IRQPEND);
        return IRQ_HANDLED;
        }
        //
        * I2C初始化
        //
        static void s3c2440_i2c_init(void)
        {
        struct clk *clk;
        clk = clk_get(NULL, "i2c");
        clk_enable(clk);
        // 選擇引腳功能:GPE15:IICSDA, GPE14:IICSCL
        s3c_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL);
        s3c_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);
        // bit[7] = 1, 使能ACK
        * bit[6] = 0, IICCLK = PCLK/16
        * bit[5] = 1, 使能中斷
        * bit[3:0] = 0xf, Tx clock = IICCLK/16
        * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
        //
        s3c2440_i2c_regs->iiccon = (1<<7) | (0<<6) | (1<<5) | (0xf); // 0xaf
        s3c2440_i2c_regs->iicadd = 0x10; // S3C24xx slave address = [7:1]
        s3c2440_i2c_regs->iicstat = 0x10; // I2C串行輸出使能(Rx/Tx)
        }
        static int i2c_bus_s3c2440_init(void)
        {
        // 2. 硬件相關的設置 //
        s3c2440_i2c_regs = ioremap(0x54000000, sizeof(struct s3c2440_i2c_regs));
        s3c2440_i2c_init();
        request_irq(IRQ_IIC, s3c2440_i2c_xfer_irq, 0, "s3c2440-i2c", NULL);
        init_waitqueue_head(&s3c2440_i2c_xfer_data.wait);
        // 3. 注冊i2c_adapter //
        i2c_add_adapter(&s3c2440_i2c_adapter);
        return 0;
        }
        static void i2c_bus_s3c2440_exit(void)
        {
        i2c_del_adapter(&s3c2440_i2c_adapter);
        free_irq(IRQ_IIC, NULL);
        iounmap(s3c2440_i2c_regs);
        }
        module_init(i2c_bus_s3c2440_init);
        module_exit(i2c_bus_s3c2440_exit);
        MODULE_LICENSE("GPL");
        ==============================================================
        解析:
        編寫"總線(適配器adapter)"驅動
        Device Drivers
        I2C support
        I2C Hardware Bus support
        < > S3C2410 I2C Driver
        nfs 30000000 192.168.1.123:/work/nfs_root/uImage_noi2cbus; bootm 30000000
        應用程序發送消息出去時,會調用適配器adapter里面的算法函數algo里面的master_xfer函數,先把消息記錄下來之后調用start函數,start函數里發送start啟動信號并且把設備地址發送出去,然后休眠。發送完之后產生一個中斷,在中斷函數里面讀出狀態,如果沒有ack的話認為發生錯誤發出停止信號喚醒應用程序;如果有ack信號而且是最后一個消息,最后一個數據則發出停止信號,否則進入下一個狀態讀/寫,如果是寫判斷是否是最后一個數據,若還有數據要發送把數據發送出去,若是最后一個數據但是不是最后一個消息的話開始處理下一個消息,發出start信號和設備地址,若是最后一個消息的最后一個數據則發出停止信號;如果是讀判斷數據是否讀完,如果沒有讀完但是接收到的不是最后一個數據則發送ack信號,若接收到最后一個數據否則無ack,但若數據已經讀完則處理下一個消息發送start信號。



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 金坛市| 凤阳县| 韶关市| 安塞县| 布尔津县| 伽师县| 德昌县| 嘉祥县| 沂水县| 祁门县| 平定县| 榆中县| 乌拉特前旗| 香港| 巩留县| 莱芜市| 县级市| 榆树市| 建湖县| 阆中市| 岐山县| 江达县| 西充县| 古蔺县| 勐海县| 大新县| 定边县| 金阳县| 惠水县| 博白县| 安庆市| 穆棱市| 武川县| 富源县| 通榆县| 丹凤县| 闽侯县| 芦山县| 江城| 阜宁县| 开平市|