• s3c2440裸机-I2c编程-3.i2c程序框架


    1.iiC设备的功能

    很显然,IIC控制器提供了传输数据的能力,至于数据有什么含义,IIC控制器并不知道,数据的含义有外部i2c从设备,我们需要阅读芯片手册,才知道IIC控制器应该发出怎样的数据。

    下图是AT24cxx的操作方法:

     

    2.I2c程序框架

    显然我们的程序应该分为两层(IIC设备层,IIC控制器层),框架如下图所示:

     

    最上层是i2c_test层,用来对i2c的功能进行测试和验证。

    第2层是i2c设备层,用来对具体某一型号的从设备进行i2c读写

    第3层是通用i2c控制器层,用来提供对具体某一型号的i2c主控进行管理操作

    最底层是i2c控制器具体的型号层

    在通用i2c控制层,我们提供一个统一的接口i2c_transfer,不关使用哪个芯片,他最终都会调用i2c_transfer,来选择某一款I2C控制器,把数据发送出去,或者从I2c设备读到数据。

    对于每一次传输的数据都可以用一个i2c_msg结构体来表示。但是,读某个地址的数据时,就要用两个i2c_msg结构体来描述它,因为一个i2c_msg结构体只能描述一个传输方向(读/写),我们读取ac24ccxx某个地址上的数据时,要先写出要读取的地址,然后来读取设备地址上的数据。

     

    i2c_test.c文件

    该文件的内容如下:

    void i2c_test(void)
    {
            /* 初始化: 选择I2C控制器 */
            /* 提供菜单供测试 */
    }

     这个菜单最终会调用到at24cxx.c里面的函数。

    at24cxx.c文件

    在里面会使用标准的接口i2c_transfer来启动I2C传输。该文件的内容如下:

    #define AT24CXX_ADDR 0x50
    int at24cxx_write(unsigned int addr, unsigned char *data, int len)
    {
            i2c_msg msg;
            int i;
            int err;
            unsigned char buf[2];
    
            for (i = 0; i < len; i++)
            {
                    buf[0] = addr++;
                    buf[1] = data[i];
                    /* 构造i2c_msg */
                    msg.addr  = AT24CXX_ADDR;
                    msg.lags = 0; /* write */
                    msg.len   = 2;
                    msg.buf   = buf;
                    msg.err   = 0;
                    msg.cnt_transferred = -1;
                    /* 调用i2c_transfer */
                    err = i2c_transfer(&msg, 1);
                    if (err)
                            return err;
            }
            return 0;
    }
    
    int at24cxx_read(unsigned int addr, unsigned char *data, int len)
    {
            i2c_msg msg[2];
            int err;
            /* 构造i2c_msg */
            msg[0].addr  = AT24CXX_ADDR;
            msg[0].lags  = 0; /* write */
            msg[0].len   = 1;
            msg[0].buf   = &addr;
            msg[0].err   = 0;
            msg[0].cnt_transferred = -1;
            msg[1].addr  = AT24CXX_ADDR;
            msg[1].lags  = 1; /* read */
            msg[1].len   = len;
            msg[1].buf   = data;
            msg[1].err   = 0;
            msg[1].cnt_transferred = -1;
            /* 调用i2c_transfer */
            err = i2c_transfer(&msg, 2);
            if (err)
                    return err;
            return 0;
    }
    View Code

    i2c_controller.h文件

    typedef struct i2c_msg {
            unsigned int addr;  /* 7bits */
            int flags;  /* 0 - write, 1 - read */
            int len;
            int cnt_transferred;
            unsigned char *buf;
    }i2c_msg, *p_i2c_msg;
    typedef struct i2c_controller {
            int (*int)(void);
            int (*master_xfer)(i2c_msg msgs, int num);
            char *name;
    }i2c_controller, *p_i2c_controller;

    i2c_controller.c文件

    该文件的内容如下:

    #define I2C_CONTROLLER_NUM 10
    /* 有一个i2c_controller数组用来存放各种不同芯片的操作结构体 */
    static p_i2c_controller p_i2c_controllers[I2C_CONTROLLER_NUM];
    static p_i2c_controller p_i2c_con_selected;
    
    void register_i2c_controller(p_i2c_controller *p)
    {
            int i;
            for (i = 0; i < I2C_CONTROLLER_NUM; i++)
            {
                    if (!p_i2c_controllers[i])
                    {
                            p_i2c_controllers[i] = p;
                            return;
                    }
            }
    }
    
    /* 根据名字来选择某款I2C控制器 */
    int select_i2c_controller(char *name)
    {
            int i;
            for (i = 0; i < I2C_CONTROLLER_NUM; i++)
            {
                    if (p_i2c_controllers[i] && !strcmp(name, p_i2c_controllers[i]->name))
                    {
                            p_i2c_con_selected = p_i2c_controllers[i];
                            return 0;
                    }
            }
            return -1;
    }
    
    /* 实现 i2c_transfer 接口函数 */
    int i2c_transfer(i2c_msg msgs, int num)
    {
            return p_i2c_con_selected->master_xfer(msgs, num);
    }
    
    void i2c_init(void)
    {
    /* 注册下面的I2C控制器 */
    s3c2440_i2c_con_add();
    /* 选择某款I2C控制器 */
    select_i2c_controller("s3c2440");
    /* 调用它的init函数 */
    p_i2c_con_selected->init();
    }
    View Code

    有数组一定有注册函数register_i2c_controller会把下面实现的I2C控制器结构体i2c_controller放到i2c_controller数组里面。select_i2c_controller函数根据名字来选择某款I2C控制器后,以后就会使用被选择的I2C控制器来启动传输。

    s3c2440_i2c_controller.c文件

    中断服务函数,当发生中断时,就会调用中断服务函数,代码如下(详细内容见下一节):

    void i2c_interrupt_func(int irq)
    {
            /* 每传输完一个数据将产生一个中断 */
            /* 对于每次传输, 第1个中断是"已经发出了设备地址" */
    }

    s3c2440_i2c_con_init函数,用来初始化I2C,控制器代码如下:

    void s3c2440_i2c_con_init(void)
    {
            /* 配置引脚用于I2C*/
            GPECON &= ~((3<<28) | (3<<30));
            GPECON |= ((2<<28) | (2<<30));
    
            /* 设置时钟 */
            /* [7] : IIC-bus acknowledge enable bit, 1-enable in rx mode
             * [6] : 时钟源, 0: IICCLK = fPCLK /16; 1: IICCLK = fPCLK /512
             * [5] : 1-enable interrupt
             * [4] : 读出为1时表示中断发生了, 写入0来清除并恢复I2C操作
             * [3:0] : Tx clock = IICCLK/(IICCON[3:0]+1).
             * Tx Clock = 100khz = 50Mhz/16/(IICCON[3:0]+1)
             */
            IICCON = (1<<7) | (0<<6) | (1<<5) | (30<<0);
    
            /* 注册中断处理函数 */
            register_irq(27, i2c_interrupt_func);
    }
    View Code

    1).IICCON = (0<<6) | (1<<5) | (30<<0); 设置IICCON控制寄存器。选择发送时钟,使能中断。设置ACK应答使能,bit[7]。

    2).register_irq(27, i2c_interrupt_func):注册中断处理函数,当发生I2C中断的时候就会调用i2c_interrupt_func中断处理函数。

    初始化完成后,就可以调用do_master_tx写I2C从机了,这个函数仅仅启动I2C传输,然后等待,直到数据在中断服务程序中传输完毕后再返回。函数代码如下:

    int do_master_tx(p_i2c_msg msg)
    {
            p_cur_msg = msg;
            msg->cnt_transferred = -1;
            msg->err = 0;
    
            /* 设置寄存器启动传输 */
            /* 1. 配置为 master tx mode */
            IICCON |= (1<<7); /* TX mode, 在ACK周期释放SDA */
            IICSTAT = (1<<4); /*IIC-bus data output enable/disable(1: Enable Rx/Tx)*/
                    
            /* 2. 把从设备地址写入IICDS */
            IICDS = msg->addr<<1;//[slave addr [7:1], addr[0] is trans dir]
    
            /* 3. IICSTAT = 0xf0 (启动传输), slave addr数据即被发送出去,当到达第9个clk,无论是否有ack, 将导致中断产生 */
            IICSTAT = 0xf0;
    
            /* 后续的传输由中断驱动 */
            /* 循环等待中断处理完毕 */
            while (!msg->err && msg->cnt_transferred != msg->len);
            if (msg->err)
                    return -1;
            else
                    return 0;
    }
    View Code

    1).IICDS = msg->addr<<1: 把从机地址(高7位,所以需要向右移一位)写入到IICDS寄存器中。

    2).IICSTAT = 0xf0:设置IICSTAT寄存器,将s3c2440设为主机发送器,并发出S信号后,紧接着就发出从机地址。后续的传输工作将在中断服务程序中完成。

    do_master_rx函数的实现和do_master_tx函数类似,代码如下:

    int do_master_rx(p_i2c_msg msg)
    {
            p_cur_msg = msg;
            msg->cnt_transferred = -1;
            msg->err = 0;
    
            /* 设置寄存器启动传输 */
            /* 1. 配置为 Master Rx mode */
            IICCON |= (1<<7); /* RX mode, 在ACK周期回应ACK */
            IICSTAT = (1<<4);  /*IIC-bus data output enable/disable*/
    
            /* 2. 把从设备地址写入IICDS */
            IICDS = (msg->addr<<1)|(1<<0);
    
            /* 3. IICSTAT = 0xb0 , 从设备地址即被发送出去, 将导致中断产生 */
            IICSTAT = 0xb0;
            /* 后续的传输由中断驱动 */
            /* 循环等待中断处理完毕 */
            while (!msg->err && msg->cnt_transferred != msg->len);
            if (msg->err)
                    return -1;
            else
                    return 0;
    }
    View Code

     1).IICDS = (msg->addr<<1)|(1<<0):把从设备地址写入IICDS,前7位是从机地址,第8位表示传输方向(0表示写操作,1表示读操作)。

    s3c2440传输函数,根据标志位flags,来指明是读/写(1:读 0:写)。代码如下:

    int s3c2440_master_xfer(p_i2c_msg msgs, int num)
    {
            int i;
            int err;
            for (i = 0; i < num; i++)        
            {
                    if (msgs[i].flags == 0)/* write */
                            err = do_master_tx(&msgs[i]);
                    else
                            err = do_master_rx(&msgs[i]);
                    if (err)
                            return err;
            }
            return 0;
    }

    s3c2440_i2c_con_add函数把上面定义的s3c2440_i2c_con结构体注册到上层的i2c_controller数组中.

    void s3c2440_i2c_con_add(void)
    {
            register_i2c_controller(&s3c2440_i2c_con);
    }

    我们定义一个i2c_controller结构体s3c2440_i2c_con。下面的代码对他进行初始化。

    static i2c_controller s3c2440_i2c_con = {
            .name = "s3c2440",
            .init = s3c2440_i2c_con_init,
            .master_xfer = s3c2440_master_xfer,
    };

    框架总结如下:

  • 相关阅读:
    C# Log4.Net日志组件的应用系列(二)
    C# Log4.Net日志组件的应用系列(一)
    使用TFS+GIT实现分布式项目管理
    动软代码生成器使用教程
    SVN使用教程
    windows系统重装流程
    使用纯真IP库获取用户端地理位置信息
    使用扩展方法重写.NET底层架构
    使用单例模式创建模型仓储层的唯一调用
    使用SQL Delta.v5.1.1.98.破解版同步数据结构
  • 原文地址:https://www.cnblogs.com/fuzidage/p/15384943.html
Copyright © 2020-2023  润新知