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

发布时间:2023-07-21  

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;

}


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();

}


有数组一定有注册函数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);

}


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;

}


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;

}


 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)

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>