IIC 通信

IIC 通用文件,文件是在NRF51xx 芯片基础,keil 平台开发测试通过,后期修改为STM32F2xx系列的配置。

文件百度云盘链接 : https://pan.baidu.com/s/1AFxanwzrAViaubtERZMRsA  

注意:在用于STM32Fx 系列时注意引脚读取函数的选择!

下面是示波器现实,IIC通讯读的操作。

理论基础:

  • SCL为高,SDA发生变化,即为发生了特殊状态(ACK, NACK, START, STOP)
  • SCL为低,SDA发生变化,即为数据端发生跳变,这个是无影响的。
  • IIC总线在数据传输时,时钟信号为高电平,数据线上的数据必须保持稳定,只有在SCL上的信号为低电平时,SDA线才允许变
  • IIC_confing.h 主要是SIMU_IIC.c 文件的一些结构体和端口设置, I2c_Str 结构体可以再添加数据属性,包括后期的数据,主要用于 SIMU_IIC.c 文件内部使用。
  • /**************************************************************************//**
     * @file    IIC_confing.h     
     * @brief For IIC communication
     * @author ning lv
     * @version 0.01
     ******************************************************************************
     * @section License
     ******************************************************************************
     *
     *****************************************************************************/
    
    #ifndef _I2C_CONFING_H_
    #define _I2C_CONFING_H_
    
    #ifdef __cplusplus
        extern "C" {
    #endif
    
    #include "SIMU_IIC.h"
    /*
    *** You can choose yours projects ...*/
    //#define NRF51822   //试用在蓝牙芯片上
    #define STM32F205       //
    
    /*
    *** Redefines the exact width signed integer type ...*/
    //typedef unsigned              char uint8;
    //typedef unsigned               int uint32;
    //typedef   signed          char uint8_t;
    
    /*
    *** Define variables and structures ... */
    typedef struct {
        uint32 SDA;            
        uint32 SCL;    
        uint32 GPIO;
    }I2c_Str;
    
    /*
    ***The global variable ... */
    I2c_Str type_Struct; //all 
    //sda  scl
    typedef void(*pI2cLineF)(uint32 sda,uint32 scl);
    pI2cLineF _I2cLine = _NULL;
    // gpio
    typedef void(*pI2cGpioF)(uint32 gpo);
    pI2cGpioF _I2cGpio = _NULL;
    /*
    ***Chip selection ... */
    #if defined NRF51822
    #define CHOOSE_IN                     _TRUE
    #define CHOOSE_OUT                    _NULL
    #elif defined STM32F205
    #define CHOOSE_IN                 (GPIO_Mode_IN )
    #define CHOOSE_OUT                (GPIO_Mode_OUT)
    #define GPIO_PORT                 ((GPIO_TypeDef*)type_Struct.GPIO)
    #endif
    
    /*
    ***Output 0/1 ... */
    #define SDA_OUT_H                           ( Gpio_Pin_Wr(type_Struct.SDA,_TRUE) )
    #define SDA_OUT_L                           ( Gpio_Pin_Wr(type_Struct.SDA,_NULL) )
    #define SCL_OUT_H                           ( Gpio_Pin_Wr(type_Struct.SCL,_TRUE) )
    #define SCL_OUT_L                           ( Gpio_Pin_Wr(type_Struct.SCL,_NULL) )
    
    /*
    ***Choose output/input ... */ 
    #define SDA_SET_IN                    ( I2c_Pin_Dir (type_Struct.SDA,CHOOSE_IN) ) 
    #define SDA_SET_OUT                    ( I2c_Pin_Dir (type_Struct.SDA,CHOOSE_OUT) )
    #define SCL_SET_IN_                    ( I2c_Pin_Dir (type_Struct.SCL,CHOOSE_IN) )
    #define SCL_SET_OUT                    ( I2c_Pin_Dir (type_Struct.SCL,CHOOSE_OUT) )
    /*
    *** Reads lines ... */ 
    #define RD_SDA_IN                      ( Gpio_Pin_Red(type_Struct.SDA) ) 
    #define RD_SCL_IN                      ( Gpio_Pin_Red(type_Struct.SCL) )
    
    /*
    *** times ... */ 
    #define NOP()                                                ( __nop()     )
    #define Iic_Delay(x)                                ( delay_us(x) )
    
    #ifdef __cplusplus
        }
    #endif
    #endif /*_I2C_CONFING_H_*/
    SIMU_IIC.c 文件主要的运行文件,开头是所需的芯片头文件,特别注意的是delay_ms();和delay_us();两个函数要根据芯片的不同,配置设备的不同定向修改,修改的过程中可以用示波器具体调试时间。
  • //Header files ...
    #include "IIC_confing.h"  //master and the explanation is here!!
    #include "usart.h"
    
    //Chip selection ...
    #if defined NRF51822
    #include "nrf_gpio.h"
    #elif defined STM32F205
    #include <stm32f2xx_gpio.h>
    #endif
    
    /*
    ***Function declaration ...*/
    void Delay5us(void);
    void Delay5us(void);
    void Delay10us(void);
    void Gpio_Pin_Wr(uint32 pin ,uint8 wr);
    void I2c_Pin_Dir(uint32 pin, uint8 dir);
    void Choose_I2c_Pin(uint32 sda,uint32 scl);
    void Gpio_Confing(uint32 gpo);
    
    //下面的可以发布在IIC.h 函数中,对外的接口设置
    void I2c_Stop(void);
    void I2c_NoAck(void);
    void I2c_RecAck(void);
    void I2c_Reset (void);
    uint8 I2c_Start(void);
    uint8 I2c_WaitACK(void);
    uint8 I2c_RcvByte(void);
    void I2c_SendByte(uint8 c);
    uint8 Gpio_Pin_Red(uint32 pin);
    void IIC_confing_Pin(uint32 sda,uint32 scl, uint32 gpo);
    void delay_ms(uint32 volatile number_of_ms);
    void delay_us(uint32 volatile number_of_us);
    
    /**@brief Delay 1us
     */
     void Delay1us(void)
    {
      NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
    #if defined STM32F205    
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
        NOP();NOP();NOP();NOP();
    #endif
    }
    /**@brief Delay 5us
     */
    void Delay5us(void)
    {
        Delay1us();
            Delay1us();
            Delay1us();
            Delay1us();
    }
    
    /**@brief Delay 10us
     */
    void Delay10us(void)
    {
        Delay5us();
        Delay5us();
    }
    
    /**@brief Delay(ms)  
     *
     * @param[in] number_of_ms 
     */
    void delay_ms(uint32 volatile number_of_ms)
    {
        number_of_ms = number_of_ms * 1000;
        while(number_of_ms != 0)
        {
                number_of_ms--;
          Delay1us();
        }
    }
    
    /**@brief Delay(us)  
     *
     * @param[in] number_of_us 
     */
    void delay_us(uint32 volatile number_of_us)
    {
             while(number_of_us != 0)
        {
            number_of_us--;
                    Delay1us();
                
        }
    }
    
    /**@brief Select the I2C sda and scl pins. 
     */
    void Choose_I2c_Pin(  uint32 sda,uint32 scl  )
    {
         type_Struct.SDA = sda;
         type_Struct.SCL = scl;
    }
    
    /**@brief Function Pointers set GPIO
     */
    void Gpio_Confing( uint32 gpo  )
    {
        type_Struct.GPIO = gpo;
    }
    
    /**@brief Function Pointers set the sda and SCL pins
     */
    void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo)
    {
    #if defined STM32F205
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    #endif
        _I2cGpio = (pI2cGpioF)Gpio_Confing;
        _I2cGpio(gpo);
        
        _I2cLine = (pI2cLineF)Choose_I2c_Pin ;
        _I2cLine(sda,scl);
    }
    
    /**@brief Iic_Pin_Dir  支持文件 STM32F2xx or NRF51822 
     *
     * @param[in] dir 
     */
    #if defined ( STM32F205 )
    void STM32F205_Pin_Dir (uint32 pin, uint8 dir)
    {
          GPIO_InitTypeDef GPIO_InitStructure;
        if(GPIO_Mode_OUT == dir){
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
        }else if(GPIO_Mode_IN == dir){
            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
        }
        GPIO_InitStructure.GPIO_Pin =  pin;
        GPIO_Init(GPIO_PORT, &GPIO_InitStructure);
    }
    #elif defined ( NRF51822 )
    void nRF51822_Pin_Dir (uint32 pin, uint8 dir)
    {
        if ( dir == 0 ) {
                nrf_gpio_cfg_output(pin);
            }else {
                nrf_gpio_cfg_input(pin,NRF_GPIO_PIN_PULLUP);
            }       
    }
    #endif
    
    /**@brief The pin line outputs 0/1.
     *
     * @param[in] dir 
     */
    void I2c_Pin_Dir (uint32 pin, uint8 dir)
    {
    #if defined ( NRF51822 )
            nRF51822_Pin_Dir (pin, dir);
    #elif defined ( STM32F205 )
            STM32F205_Pin_Dir (pin, dir);
    #endif   
    }
    
    
    /**@brief SDA/SCL line write 0/1.
     *
     * @param[in] pin ,wr
     */
    
    void Gpio_Pin_Wr(uint32 pin ,uint8 wr)
    {
        if ( wr == 0 ) {
    #if defined ( NRF51822 )
         nrf_gpio_pin_write(pin,0);
    #elif defined ( STM32F205 )
         GPIO_WriteBit(GPIO_PORT , pin , 0);
    #endif 
        }
        else {
    #if defined ( NRF51822 )
             nrf_gpio_pin_write(pin,1);
    #elif defined ( STM32F205 )
             GPIO_WriteBit(GPIO_PORT , pin , 1);
    #endif 
        }       
    }
    
    /**@brief SDA/SCL line read 0/1.
     *
     * @param[in] pin 
     */
    uint8 Gpio_Pin_Red(uint32 pin)
    {
    #if defined ( NRF51822 )
         return ((uint8) nrf_gpio_pin_read(pin));
    #elif defined ( STM32F205 ) 
        return (uint8) GPIO_ReadInputDataBit(GPIO_PORT , pin);
    #endif     
    }    
    
    /**@brief IIC communication to prepare.
     *
     * @param[in] i2c_master 
     */
    uint8 I2c_Start(void)
    {
        /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
            I2c_Reset();
        
          SCL_SET_OUT;
          SDA_SET_OUT;  
        
          SDA_OUT_H;
          SCL_OUT_H;
          Iic_Delay(I2C_DELAY_TM);
          if( !RD_SDA_IN ) {
                return I2C_BUSY; //BUSY
            }
            SDA_OUT_L;    
          Iic_Delay(I2C_DELAY_TM);
          if( RD_SDA_IN ) {
                return I2C_ERROR; //ERROR
            }
          SDA_OUT_L;/*CPU占线*/
          return I2C_TRUE;
    }
    
    /**@brief IIC stop the communication .
     *
     * @param[in] i2c_master 
     */
    void I2c_Stop(void)
    {
        /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
            SCL_OUT_L;
          Iic_Delay(I2C_DELAY_TM);
          SDA_OUT_L;
          Iic_Delay(I2C_DELAY_TM);
          SCL_OUT_H;
          Iic_Delay(I2C_DELAY_TM);
          SDA_OUT_H;
    }
    
    /**@brief reset  
     */
    void I2c_Reset (void)
    {
        if( RD_SDA_IN == 0  
            //|| READ_SCL_IN == 0 
            ) 
        {    
            SCL_SET_OUT;
          SDA_SET_OUT;  
        Iic_Delay(I2C_DELAY_TM);
        
          SDA_OUT_H;
          SCL_OUT_H;
          Iic_Delay(I2C_DELAY_TM);
            
            I2c_SendByte(0xFF);
            I2c_NoAck();       
            I2c_Stop();    
        }
    }
    /**@brief Send the data 'c' out,either as data or as an address.
     *
     * @param[in] C 
     */
    void I2c_SendByte(uint8 c)
    {
           uint8 i;
           i = 8;
      
            while(i--) 
            {
                SCL_OUT_L;    /*允许SDA 数据跳变*/
                Iic_Delay(I2C_DELAY_TM); 
                if( c&0x80) {
                    SDA_OUT_H;    
                }
                else {
                    SDA_OUT_L;
                }                
                
                c<<=1;          
                SCL_OUT_H;  /*SDA 数据保持 1us*/
                Iic_Delay(I2C_DELAY_TM);
            }    
           SCL_OUT_L;    
            SDA_OUT_H; /*CPU 释放总线*/
    }
    
    /**@brief Reads a bytes of data and returns it.
     *
     * @param[out] retc 
     */
    uint8 I2c_RcvByte(void)
    {
           uint8 i = 8;
           uint8 retc = 0;
        
           SDA_SET_IN;
           SDA_OUT_L;/*总线默认拉高*/
           while(i--) 
            {
               retc<<=1;
               SCL_OUT_L;  /*CPU 允许 SDA 数据跳变*/
               Iic_Delay(I2C_DELAY_TM);
               SCL_OUT_H;  /*CPU 保持 SDA 数据1us*/
               Iic_Delay(I2C_DELAY_TM);
               if( RD_SDA_IN ) {
                   retc |= 0x01;
               }
           }
           SCL_OUT_L;
           SDA_SET_OUT;
           SDA_OUT_H; /*CPU 释放总线*/
            
           return retc;    
    }
    /**@brief watint ACK 
     */
    uint8 I2c_WaitACK(void)
    {
        uint8 errTime = 250; 
        uint8 retFlag = I2C_TRUE;
        
            SCL_OUT_L;
          SDA_OUT_H;                                 /*CPU 释放总线*/
          Iic_Delay(I2C_DELAY_TM);
        
            SCL_OUT_H;                                 /*CPU驱动SCL = 1, 此时器件会返回ACK应答*/
            SDA_SET_IN;
          Iic_Delay(I2C_DELAY_TM);
            while((RD_SDA_IN)&&((errTime--) > 0))
            {
                if(errTime == 0){
                    retFlag = I2C_FALSE;
                }
            }
            SCL_OUT_L;
            SDA_SET_OUT;
            return retFlag;
    }
    /**@brief ACK response
     */
    void I2c_RecAck(void)
    {
        SCL_OUT_L;    /*CPU 允许 SDA 数据跳变*/
        Iic_Delay(I2C_DELAY_TM);    
        SDA_OUT_L;  /*CPU 拉低SDA ,ACK器件*/
        Iic_Delay(I2C_DELAY_TM);
        SCL_OUT_H;    /*CPU 保持 SDA 数据1us*/
        Iic_Delay(I2C_DELAY_TM);
        SCL_OUT_L;
        Iic_Delay(I2C_DELAY_TM);
      SDA_OUT_H;    /* CPU释放SDA总线 */
    }
    
    /**@brief Don't reply 
     */
    void I2c_NoAck(void)
    {
          SCL_OUT_L;
          Iic_Delay(I2C_DELAY_TM);    
          SDA_OUT_H;
          Iic_Delay(I2C_DELAY_TM);
          SCL_OUT_H;
          Iic_Delay(I2C_DELAY_TM);
          SCL_OUT_L;
          Iic_Delay(I2C_DELAY_TM);
    }

      SIMCU_IIC.h对外发布函数接口和一些宏定义 ,  函数 void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo);//设置 sda 和 scl 的引脚设置. 是主要调用的文件,它是基础。

  • #ifndef SIMU_IIC_H_
    #define SIMU_IIC_H_
    
    /*
    *** Define some of the things ...*/
    #define _NULL          0
    #define _TRUE          1
    #define I2C_ERROR            _NULL     //returns 
    #define I2C_FALSE     _NULL
    #define I2C_TRUE            _TRUE     
    #define I2C_BUSY            3//3     
    #define I2C_DELAY_TM     5          //slot time (4.7us)
    
    /*
    *** Define variables and structures ... */
    typedef   unsigned          char uint8;
    typedef     unsigned               int uint32;
    
    void I2c_Stop(void);
    void I2c_NoAck(void);
    void I2c_RecAck(void);
    void I2c_Reset (void);
    uint8 I2c_Start(void);
    uint8 I2c_WaitACK(void);
    uint8 I2c_RcvByte(void);
    void I2c_SendByte(uint8 c);
    uint8 Gpio_Pin_Red(uint32 pin);
    void delay_ms(uint32 volatile number_of_ms);
    void delay_us(uint32 volatile number_of_us);
    void IIC_confing_Pin( uint32 sda,uint32 scl, uint32 gpo);//设置 sda 和 scl 的引脚设置.
    
    #endif /*SIMU_IIC_H_*/

     device_i2c.c 文件是给出的write和read实例文件。

  • #include "SIMU_IIC.h"
    #include "usart.h"
    
    #define DEVICE_SLAVEADDR_W   0xA0//0x40
    #define DEVICE_SLAVEADDR_R   0xA1
    uint8_t busy;
    uint8_t DEVICE_WriteByte (uint8_t addr,uint8_t *c,uint8_t len )
    {
        uint8_t i = 0;
            lock_busy = 1;
           i = I2c_Start();
    
           if(I2C_BUSY == i )
            {
                printf(" 
     I2C_BUSY  
    ");
                    goto DEVICE_WriteByte_FAIL; //return Fail;    
            }
            else if (I2C_ERROR == i)
            {
                printf(" 
     I2C_ERROR  
    ");
                    goto DEVICE_WriteByte_FAIL; //return Fail;    
            }
                
           I2c_SendByte(DEVICE_SLAVEADDR_W);  //0x40  写    
           if(!I2c_WaitACK()) {
               I2c_Stop();
                printf(" 
     地址失败 :%02x  !
    ",DEVICE_SLAVEADDR_W);
               goto DEVICE_WriteByte_FAIL; //return Fail;
           }    
           I2c_SendByte(addr);        //addr
       if(!I2c_WaitACK()) {
               I2c_Stop();
                printf(" 
     寄存器地址错误 :%02x  !
    ",DEVICE_SLAVEADDR_W);
               goto DEVICE_WriteByte_FAIL; //return Fail;
           }    
        for(i=0;i<len;i++){
            I2c_SendByte(*c);    
            if(!I2c_WaitACK())
            {
                    printf(" 
     写入数据失败  
    ");
            }
            c++;
        }
           I2c_Stop();
            busy = 0;
           return 1;        
    DEVICE_WriteByte_FAIL:
             I2c_Stop();
            busy = 0;
           return 0;        
    } 
    uint8 DEVICE_II_ReadByte (uint8_t addr,uint8_t * c,uint8_t len)
    {
           uint8 i;
          busy = 1;
           if(!I2c_Start())  goto DEVICE_II_ReadByte_FAIL; //return Fail;
        
           I2c_SendByte (DEVICE_SLAVEADDR_W);
           if(!I2c_WaitACK()) 
        {
               I2c_Stop();
               goto DEVICE_II_ReadByte_FAIL; //return Fail;
           }    
           I2c_SendByte(addr);
           I2c_WaitACK();
           I2c_Start();
           I2c_SendByte(DEVICE_SLAVEADDR_R);    
           I2c_WaitACK();
        for(i=0;i<len;i++)
        {
            *c = I2c_RcvByte();
            c++;
        }
       
           I2c_NoAck();
           I2c_Stop();
          busy = 0;
           return 1;
       
    DEVICE_II_ReadByte_FAIL:
            busy = 0;
        return 0;
    }
原文地址:https://www.cnblogs.com/LVNG2018/p/10364328.html