1.概述

本文档以STM32F767平台为例,详细介绍SylixOS上GPIO模仿I2C总线的驱动开发流程。

2.初始化

GPIO模仿的I2C总线的初始化,实际上是I2C总线的SDA和SCL的GPIO管脚初始化。初始化流程如图 2.1所示。

SylixOS 基于STM32平台的GPIO模仿I2C总线的驱动开发流程
图 2.1 I2C初始化流程图

代码实现,如程序清单 2.1所示。I2C总线的SDA和SCL两个GPIO管脚的GPIO速度要设置成快速模式,输出模式需要设置成推挽输出模式。

程序清单 2.1 I2C初始化代码

    /*
     *  申请 I2C 1 通道的 SCL 的 GPIO
     */
    if (ERROR_NONE != API_GpioRequest(I2C1_CHANNEL_SCL, I2C1_SCL_GPIO_NAME)) {
        return  (PX_ERROR);
    }

    /*
     *  设置上拉
     */
    if (ERROR_NONE != API_GpioSetPull(I2C1_CHANNEL_SCL, GPIO_PUPD_PU)) {
        return  (PX_ERROR);
    }

    /*
     *  设置为推挽输出模式,且 GPIO 速度为快速
     */
    if (ERROR_NONE != API_GpioDirectionOutput(I2C1_CHANNEL_SCL,
                                                       (GPIO_SPEED_SET  |
                                                        GPIO_OTYPE_SET  |
                                                    LW_GPIOF_INIT_HIGH))) {
        return  (PX_ERROR);
    }

    /*
     *  申请 I2C 1 通道的 SDA 的 GPIO
     */
    if (ERROR_NONE !=  API_GpioRequest(I2C1_CHANNEL_SDA, I2C1_SDA_GPIO_NAME)) {
        return  (PX_ERROR);
    }
    if (ERROR_NONE != API_GpioSetPull(I2C1_CHANNEL_SDA, GPIO_PUPD_PU)) {
        return  (PX_ERROR);
    }
    if (ERROR_NONE != API_GpioDirectionOutput(I2C1_CHANNEL_SDA,
                                                       (GPIO_SPEED_SET  |
                                                        GPIO_OTYPE_SET  |
                                                    LW_GPIOF_INIT_HIGH))) {
        return  (PX_ERROR);
    }

3.传输流程

GPIO模拟I2C总线驱动和普通的I2C总线驱动的最大区别是普通的I2C总线驱动的数据传输只要将要传输的数据写入寄存器即可,而GPIO模拟I2C总线驱动的数据传输是直接通过GPIO管脚将电平拉高拉低(拉高是1,拉低是0)传输数据。

3.1写数据流程

如程序清单 3.1所示,I2C的写数据流程如下:

主设备发送开始信号;

主设备发送7位从设备地址和1位写操作位;

从设备发送应答信号;

主设备发送要写的8位从设备内部地址;

从设备发送应答信号;

主设备开始对从设备写操作;

主设备发送结束信号。

程序清单 3.1 I2C的写数据流程

static INT  __i2cXferWrite (UINT                    uiChannel,
                                PLW_I2C_MESSAGE     pI2cMsg,
                                INT                     iLength)
{
    INT  iIndex;

    __i2cStart(uiChannel);                          /*  发送开始信号                */

    /*
     *  发送 7 位器件地址和 1 位写操作位
     */
    __i2cSendByte((pI2cMsg->I2CMSG_usAddr & I2C_ADDR_MASK), uiChannel);

if (__i2cWaitAck(uiChannel)) {                /*  等待设备的 ACK 信号         */
        return  (PX_ERROR);
    }

    /*
     *  发送要写的设备的内部地址
     */
    __i2cSendByte(((pI2cMsg->I2CMSG_usAddr) & I2C_INTER_ADDR_MASK), uiChannel);
    if (__i2cWaitAck(uiChannel)) {                /*  等待设备的 ACK 信号         */
        return  (PX_ERROR);
    }

    for (iIndex = 0; iIndex < iLength; iIndex++) {
        __i2cSendByte(*(pI2cMsg->I2CMSG_pucBuffer)++, uiChannel);       
/*  发送字节                    */
        if (__i2cWaitAck(uiChannel)) {            /*  等待设备的 ACK 信号         */
            return  (PX_ERROR);
        }
    }

    __i2cStop(uiChannel);                           /*  产生一个停止信号            */

    udelay(I2C_WRITE_BYTE_DELAY);

    return (ERROR_NONE);
}

3.2读数据流程

如程序清单 3.2所示,I2C的读数据流程如下:

写模式,主设备发送开始信号;

主设备发送7位从设备地址和1位写操作位;

从设备发送应答信号;

主设备发送要写的8位从设备内部地址;

从设备发送应答信号;

进入读取模式,设备再次发送开始信号;

主设备发送7位从设备地址和1位读操作位;

从设备发送应答信号;

主设备开始对从设备读操作;

主设备发送结束信号。

程序清单 3.2 I2C读数据流程

    static INT  __i2cXferRead (UINT                             uiChannel,
                                                 PLW_I2C_MESSAGE    pI2cMsg,
                                                INT                                     iLength)
{
    INT  iIndex;

    __i2cStart(uiChannel);                          /*  发送开始信号                */

    /*
     *  发送 7 位器件地址和 1 位写操作位,(I2CMSG_usAddr 中的 9-15 位为器件地址)
     */
    __i2cSendByte(((pI2cMsg->I2CMSG_usAddr >> 8) & I2C_ADDR_MASK), uiChannel);
    if (__i2cWaitAck(uiChannel)) {                /*  等待设备的 ACK 信号         */
        return  (PX_ERROR);
    }

    /*
     *  发送要读的设备的内部地址
     */
    __i2cSendByte(((pI2cMsg->I2CMSG_usAddr) & I2C_INTER_ADDR_MASK), uiChannel);
    if (__i2cWaitAck(uiChannel)) {                /*  等待设备的 ACK 信号         */
        return  (PX_ERROR);
    }

    /*
     *  进入读取模式
     */
    __i2cStart(uiChannel);                          /*  发送开始信号                */

    /*
     *  发送 7 位器件地址和 1 位读操作位,(I2CMSG_usAddr 中的 8-15 位为器件地址和读写位)
     */
__i2cSendByte(((pI2cMsg->I2CMSG_usAddr >> 8)    &
I2C_ADDR_MASK)                  |
LW_I2C_M_RD, uiChannel);
    if (__i2cWaitAck(uiChannel)) {                /*  等待设备的 ACK 信号         */
        return  (PX_ERROR);
    }

    for (iIndex = 0; iIndex < iLength - 1; iIndex++) {

        /*
         *  读取设备发来的 1 个字节数据
         */
        *(pI2cMsg->I2CMSG_pucBuffer)++ = __i2cReadByte(I2C_ACK_SEND, uiChannel);
    }

    *(pI2cMsg->I2CMSG_pucBuffer) = __i2cReadByte(I2C_NACK_SEND, uiChannel);

    __i2cStop(uiChannel);                           /*  产生停止信号                */

    return  (ERROR_NONE);
}