打印
[STM32]

高度提炼的模拟I2C程序

[复制链接]
5060|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
nicholasldf|  楼主 | 2015-12-16 10:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 nicholasldf 于 2015-12-18 10:10 编辑

以下是模拟I2C高度提炼的程序,只要提供GPIO端口、SCL和SDA引脚号、从设备addr,就可以开始使用,基于STM32,对于其他CPU,稍作修改即可。

优点:如果系统中用到了多个模拟I2C通信实例,,就不用每一个模拟I2C实例都编写一套几乎一摸一样的代码,,以下代码对于所有实例都通用。

就像我的系统,很多地方用到了模拟I2C,至少3-4个,有EEPROM,motion运动传感器、ADC等等,,都在不同的引脚上面,但是只用这一套代码。
对于单个I2C,下面也是实用的。

当然:以下代码是根据I2C协议标准编写的,,所有的延时函数都是根据标准所定,,如果不符合您的系统,,可以将delay_us延时加大。


//-------------------------------------------------------------------------------头文件-----------------------------------------------------------------------------------------
/*
*********************************************************************************************************
*    \'-.__.-'/      | Company  : o--Shen Zhen xxxxx Technology Co,.Ltd--o
*    / (o)(o) \     | Website    : o--http://www.xxxxx .com.cn--o
*     \   \/   /      | Copyright  :
*     /'------'\      | Product     :  
*   /,   ....  , \     | File            : bsp_SimulateI2C.h
*  /// .::::. \\\    | Descript     : use GPIOs to Simulate I2C communication
* ///\ :::::: /\\\  | Version      : V0.10
*  ''   ).''''.(   ``  | Author      : nicholasldf
*=(((====)))= | EditTime : 2015-09-06-10:00
*********************************************************************************************************
*/

#ifndef  __BSP_SIMULATE_I2C__
#define  __BSP_SIMULATE_I2C__

#include  "stm32f4xx_hal.h"

//模拟I2C结构体声明
//Simulate I2C Port struct define
struct   SimuI2cPortType   {
        GPIO_TypeDef         *SCLPort;//GPIO PORT
        uint32_t                    SCLPin;  //GPIO PIN
        GPIO_TypeDef         *SDAPort;//GPIO PORT
        uint32_t                    SDAPin;  //GPIO PIN
        uint8_t                      address; //slave address
};

//模拟I2C的SCL、SDA管脚控制宏定义
//SCL output hardware operate
#define SIMUI2C_SCL_SET                HAL_GPIO_WritePin(pSimuI2cPort->SCLPort, pSimuI2cPort->SCLPin, GPIO_PIN_SET)
#define SIMUI2C_SCL_CLR                HAL_GPIO_WritePin(pSimuI2cPort->SCLPort, pSimuI2cPort->SCLPin, GPIO_PIN_RESET)
//SDA output hardware operate
#define SIMUI2C_SDA_SET                HAL_GPIO_WritePin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin, GPIO_PIN_SET)
#define SIMUI2C_SDA_CLR                HAL_GPIO_WritePin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin, GPIO_PIN_RESET)
//SDA input hardware operate
#define SIMUI2C_SDA_IN                   HAL_GPIO_ReadPin(pSimuI2cPort->SDAPort, pSimuI2cPort->SDAPin)

//simulate i2c APIs
void bsp_SimuI2C_start(struct  SimuI2cPortType   *pSimuI2cPort);
void bsp_SimuI2C_stop(struct  SimuI2cPortType   *pSimuI2cPort);
void bsp_SimuI2C_SandAck(struct  SimuI2cPortType   *pSimuI2cPort);
void bsp_SimuI2C_SandNack(struct  SimuI2cPortType   *pSimuI2cPort);
uint32_t bsp_SimuI2C_ReadAck(struct  SimuI2cPortType   *pSimuI2cPort);
uint8_t bsp_SimuI2C_read_byte(struct  SimuI2cPortType   *pSimuI2cPort);
void bsp_SimuI2C_write_byte(struct  SimuI2cPortType   *pSimuI2cPort,    uint8_t   data);

#endif /* End of module include */




//-------------------------------------------------------------------------------源文件-----------------------------------------------------------------------------------------
/*
*********************************************************************************************************
*    \'-.__.-'/      | Company  : o--Shen Zhen xxxxx Technology Co,.Ltd--o
*    / (o)(o) \     | Website    : o--http://www.xxxxx .com.cn--o
*     \   \/   /      | Copyright  :  
*     /'------'\      | Product     :  
*   /,   ....  , \     | File            : bsp_SimulateI2C.c
*  /// .::::. \\\    | Descript     : use GPIOs to Simulate I2C communication
* ///\ :::::: /\\\  | Version      : V0.10
*  ''   ).''''.(   ``  | Author      : nicholasldf
*=(((====)))= | EditTime : 2015-09-06-10:00
*********************************************************************************************************
*/

#include  "bsp_SimulateI2C.h"
#include  "bsp_common.h"

/*  配置SDA管脚为输入方式
*********************************************************************************************************
* function    :  config_sda_in
* Description : config SDA GPIO for input
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
static void config_sda_in(struct SimuI2cPortType *pSimuI2cPort)
{
        GPIO_InitTypeDef GPIO_InitStruct;
        
        /* Configure SDA GPIO pin */
        GPIO_InitStruct.Pin = pSimuI2cPort->SDAPin;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        HAL_GPIO_Init(pSimuI2cPort->SDAPort, &GPIO_InitStruct);
}

/*  配置SDA管脚为输出方式
*********************************************************************************************************
* function    : config_sda_out
* Description : config SDA GPIO for output
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
static void config_sda_out(struct SimuI2cPortType *pSimuI2cPort)
{
        GPIO_InitTypeDef GPIO_InitStruct;
        
        /* Configure SDA GPIO pin */
        GPIO_InitStruct.Pin = pSimuI2cPort->SDAPin;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        HAL_GPIO_Init(pSimuI2cPort->SDAPort, &GPIO_InitStruct);
}

/*  生成一个I2C的start开始条件,或者restart重新开始条件
*********************************************************************************************************
* function    : bsp_SimuI2C_start
* Description : generate a i2c start or restart
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
void bsp_SimuI2C_start(struct SimuI2cPortType *pSimuI2cPort)
{
        //config sda pin output
        config_sda_out(pSimuI2cPort);
        
        //here may be a stop
        SIMUI2C_SCL_SET;
        delay_us(1);//SCL setup time for STOP condition, 0.6uS
        SIMUI2C_SDA_SET;
        delay_us(2);//the bus must be free before a new transmission can start, 1.2uS
        
        //start
        SIMUI2C_SDA_CLR;
        delay_us(1);//SCL hold time for START condition, 0.6uS
        SIMUI2C_SCL_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
}

/*  生成一个I2C的stop停止条件
*********************************************************************************************************
* function    : bsp_SimuI2C_stop
* Description : generate a i2c stop
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
void bsp_SimuI2C_stop(struct SimuI2cPortType *pSimuI2cPort)
{
        //config sda pin output
        config_sda_out(pSimuI2cPort);
        
        //set SCL and SDA low first
        SIMUI2C_SCL_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        SIMUI2C_SDA_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        
        //stop
        SIMUI2C_SCL_SET;
        delay_us(1);//SCL setup time for STOP condition
        SIMUI2C_SDA_SET;
        delay_us(2);//Time the bus must be free before a new transmission can start, 1.2uS
}

/*  给从设备发送一个ack应答信号
*********************************************************************************************************
* function    : bsp_SimuI2C_SandAck
* Description : generate a i2c ack to slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
void bsp_SimuI2C_SandAck(struct SimuI2cPortType *pSimuI2cPort)
{
        //config sda pin output
        config_sda_out(pSimuI2cPort);
        
        //set sda=0
        //delay_us(1);//SCL low period * 0.5 = 0.65uS
        SIMUI2C_SDA_CLR;//SDA=0
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        
        //scl pulse
        SIMUI2C_SCL_SET;
        delay_us(1);//SCL high period, 0.6uS
        SIMUI2C_SCL_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
}

/*   给从设备发送一个no ack非应答信号
*********************************************************************************************************
* function    : bsp_SimuI2C_SandNack
* Description : generate a i2c noack to slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : none
*********************************************************************************************************
*/
void bsp_SimuI2C_SandNack(struct SimuI2cPortType *pSimuI2cPort)
{
        //config sda pin output
        config_sda_out(pSimuI2cPort);
        
        //set sda=1
        //delay_us(1);//SCL low period * 0.5 = 0.65uS
        SIMUI2C_SDA_SET;//SDA=1
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        
        //scl pulse
        SIMUI2C_SCL_SET;
        delay_us(1);//SCL high period, 0.6uS
        SIMUI2C_SCL_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
}

/*   读取从设备的ack应答信号状态, 0表示应答, 1表示非应答
*********************************************************************************************************
* function    : bsp_SimuI2C_ReadAck
* Description : check i2c ack from slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : 0: ack, 1: nack
*********************************************************************************************************
*/
uint32_t bsp_SimuI2C_ReadAck(struct SimuI2cPortType *pSimuI2cPort)
{
        uint32_t ack;

        //config sda pin input
        config_sda_in(pSimuI2cPort);
        
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        SIMUI2C_SCL_SET;
        delay_us(1);//SCL high period, 0.6uS
        ack = SIMUI2C_SDA_IN;
        SIMUI2C_SCL_CLR;
        delay_us(1);//SCL low period * 0.5 = 0.65uS
        
        return ack;
}

/*   主机读取从设备,返回一个8bit数据
*********************************************************************************************************
* function    : bsp_SimuI2C_read_byte
* Description : read a byte from i2c slave
* Argument(s) : point to struct SimuI2cPortType
* Return(s)   : the read data
*********************************************************************************************************
*/
uint8_t bsp_SimuI2C_read_byte(struct SimuI2cPortType *pSimuI2cPort)
{
        uint32_t i;
        uint8_t  data;
        
        //config sda pin input
        config_sda_in(pSimuI2cPort);
        
        data = 0;
        for(i=0; i<8; i++) {
                delay_us(1);//SCL low period * 0.5 = 0.65uS
                SIMUI2C_SCL_SET;
                delay_us(1);//SCL high period, 0.6uS
                //read data in
                data<<=1;
                if(GPIO_PIN_SET == SIMUI2C_SDA_IN)        data |= 0x01;
                SIMUI2C_SCL_CLR;
                delay_us(1);//SCL low period * 0.5 = 0.65uS
        }

        return data;
}

/*  主机写8bit数据到从设备
*********************************************************************************************************
* function    : bsp_SimuI2C_write_byte
* Description : write a byte to i2c slave
* Argument(s) : pSimuI2cPort: point to struct SimuI2cPortType, data: data to write
* Return(s)   : none
*********************************************************************************************************
*/
void bsp_SimuI2C_write_byte(struct SimuI2cPortType *pSimuI2cPort, uint8_t data)
{
        uint32_t i;
        
        //config sda pin output
        config_sda_out(pSimuI2cPort);
        
        for(i=0; i<8; i++) {
                delay_us(1);//SCL low period * 0.5 = 0.65uS
                //sda bit output
                if(data & 0x80)
                        SIMUI2C_SDA_SET;
                else
                        SIMUI2C_SDA_CLR;
                delay_us(1);//SCL low period * 0.5 = 0.65uS
                //scl pulse
                SIMUI2C_SCL_SET;               
                delay_us(1);//SCL high period, 0.6uS
                SIMUI2C_SCL_CLR;
                //next bit
                data <<= 1;
        }
        delay_us(1);//SCL low period * 0.5 = 0.65uS
}

/**  End of bsp_SimulateI2C.c **/



//-------------------------------------------------------------------------------如何使用-----------------------------------------------------------------------------------------
        使用方法:定义一个模拟I2C结构体,初始化IO口,设置好GPIO端口和pin引脚号,,即可开始使用        
        //定义好模拟I2C结构体
        struct SimuI2cPortType  TouchI2cPort;

        //管脚初始化,设置SCL、SDA为普通IO口,输出方式
        //SCL
        /* Configure SCL PIN - PB0 */
        GPIO_InitStruct.Pin = GPIO_PIN_0;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);


        //SDA
        /* Configure SDA PIN - PF11 */
        GPIO_InitStruct.Pin = GPIO_PIN_11;
        HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
        HAL_GPIO_WritePin(GPIOF, GPIO_PIN_13, GPIO_PIN_SET);
        
        
        //设置好模拟I2C结构体的端口号和引脚号:SimuI2C PORT, SCL-PB0, SDA-PF11
        //SCL的端口号和引脚号
        TouchI2cPort.SCLPort = GPIOB;
        TouchI2cPort.SCLPin  = GPIO_PIN_0;
        //SDA的端口号和引脚号
        TouchI2cPort.SDAPort = GPIOF;
        TouchI2cPort.SDAPin  = GPIO_PIN_11;
        
       //设置该模拟I2C结构体对应从设备的地址
       TouchI2cPort.address  =  xxxx; //slave address

       //let‘s  go   ^_^

相关帖子

沙发
阿南| | 2015-12-16 13:54 | 只看该作者
很好,谢谢分享

使用特权

评论回复
板凳
john-deng| | 2015-12-16 15:16 | 只看该作者
碉堡了

使用特权

评论回复
地板
一木随风| | 2015-12-16 19:23 | 只看该作者
学习一下

使用特权

评论回复
5
zhangbin_abc| | 2015-12-16 19:55 | 只看该作者
不错。

使用特权

评论回复
6
justtest111| | 2015-12-16 20:43 | 只看该作者
请问楼主,模拟I2C中的延时等待在系统对实时性要求很高时怎么处理呢?一直没想到好方法。

使用特权

评论回复
7
undersky| | 2015-12-17 08:44 | 只看该作者
mark一下,不错

使用特权

评论回复
8
penguin008| | 2015-12-17 09:08 | 只看该作者
关注一下,IIC之类的程序,一般我也习惯自己用I/O去模拟

使用特权

评论回复
9
huarana| | 2015-12-17 10:27 | 只看该作者
penguin008 发表于 2015-12-17 09:08
关注一下,IIC之类的程序,一般我也习惯自己用I/O去模拟

支持。mark一下。
空了研究研究

使用特权

评论回复
10
tianhaolan| | 2015-12-17 12:15 | 只看该作者
一般我也习惯自己用I/O去模拟

使用特权

评论回复
11
joujoulian| | 2015-12-17 13:02 | 只看该作者
太好了,lz分享达人 顶一个

使用特权

评论回复
12
fpgafpgaCCD| | 2015-12-17 15:14 | 只看该作者
顶一个

使用特权

评论回复
13
1021352522| | 2015-12-17 22:35 | 只看该作者
楼主整的那个备注碉堡了

使用特权

评论回复
14
nicholasldf|  楼主 | 2015-12-18 09:55 | 只看该作者
本帖最后由 nicholasldf 于 2015-12-18 10:15 编辑
justtest111 发表于 2015-12-16 20:43
请问楼主,模拟I2C中的延时等待在系统对实时性要求很高时怎么处理呢?一直没想到好方法。 ...

如果实时性要求高,尽量用I2C控制器吧,打开DMA
如果是模拟I2C通信,时钟最多也就是400K,实时性不高,,为了尽快提高速度,,可以将调用I2C通信的任务优先级设置为最高。

如果怕I2C的延时影响系统实时性,,可以将调用I2C的应用程序优先级调低一些

如果是前后台系统(裸奔),,实时性高的放在中断里面处理,模拟I2C可以被中断的,通信一般不会收到影响。

一点点个人观点,不知道对不对。   ^_^

使用特权

评论回复
15
菜鸟同学| | 2015-12-18 12:57 | 只看该作者
试试编译器O-3优化,看看会不会一样的坚挺。

使用特权

评论回复
16
justtest111| | 2015-12-18 17:50 | 只看该作者
nicholasldf 发表于 2015-12-18 09:55
如果实时性要求高,尽量用I2C控制器吧,打开DMA
如果是模拟I2C通信,时钟最多也就是400K,实时性不高,, ...

多谢回复,感谢。

使用特权

评论回复
17
sensitive| | 2015-12-18 21:45 | 只看该作者
分享是美德

使用特权

评论回复
18
yuwenbin| | 2015-12-21 14:04 | 只看该作者
好美的注释,好美的鸟

使用特权

评论回复
19
eyesee2008| | 2015-12-25 17:12 | 只看该作者
好东西!

使用特权

评论回复
20
ranqingfa| | 2015-12-26 18:35 | 只看该作者
多谢分享,曾经确实为多个模拟I2C接口困扰过

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

61

主题

261

帖子

10

粉丝