RT-thread学习笔记1 I2C设备驱动学习之EEPROM驱动(上)

RT-thread中关于I2C设备驱动主要有i2c_core.ci2c_dev.ci2c-bit-ops.c这个几个文件,这次的主要任务就是结合提供的i2c驱动库编写eeprom的读写函数,基于ALIENTEK MINI STM32作为开发平台。

 

 第一步:由于RT-thread中的i2c-bit-ops.c文件中已经实现了i2c的总线驱动,我们只需要完成关于结构体rt-i2c-bit-ops中定义的接口就可以了,

    1)主要i2c接口定义如下:      

struct rt_i2c_bit_ops

{

    void *data;           

    void (*set_sda)(void *data, rt_int32_tstate);  // 设置SDA数据线

    void (*set_scl)(void *data, rt_int32_tstate);  // 设置SCL时钟线

    rt_int32_t (*get_sda)(void *data);              // 获取SDA数据线状态

    rt_int32_t (*get_scl)(void *data);              // 获取SCL时钟线状态

 

    void (*udelay)(rt_uint32_t us);

 

    rt_uint32_t delay_us;  /* scl and sda line delay */

    rt_uint32_t timeout;   /* in tick */

};

 

?问题1:一般在读取io口状态时需要改变输入输出口的模式,但根据接口定义和i2c-bit-ops中的驱动看出来都不涉及输入输出口的模式转换。

    刚开始实现上述接口时,尝试在get_sda和get_scl方法实现中加入输入输出转换,但经过调试发现此法不可行,之后又查阅了i2c的总线的原理和stm32GPIO口的原理,发现了如下几点:

     ------>I/O端口配置为输入时:根据输入配置(上拉,下拉或浮动)的不同,弱上拉和下拉电阻被连接,对输入数据寄存器的读访问可得到I/O状态 

    ------>I/O端口被配置为输出时:在开漏模式时,对输入数据寄存器的读访问可得到I/O状态 ,在推挽式模式时,对输出数据寄存器的读访问得到最后一次写的值。

     在i2c的两个端口GPIO的设置时一般设置为推挽输出后,若需要获取端口状态时改变端口为输入;但结合datasheet中的描述,若把端口设置为开漏输出时同样可以读取输入状态,这样也满足了接口的设置。下面就是接口的实现了。

     2)首先关于时钟和端口配置:

/* Define eeprom gpio and clock*/

#define EEPROM_I2C_RCC        RCC_APB2Periph_GPIOC

#define EEPROM_I2C_PORT       GPIOC

#define EEPROM_I2C_SCL_PIN    (GPIO_Pin_12)

#define EEPROM_I2C_SDA_PIN    (GPIO_Pin_11)

/* ALIENTEK MINI 自带的端口驱动(寄存器操作)*/

#define SCL_PIN   12

#define SDA_PIN    11

#define GPIOC_ODR_Addr   (GPIOC_BASE+12) //0x4001100C 

#define GPIOC_IDR_Addr   (GPIOC_BASE+8) //0x40011008 

#define PCout(n)  BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 

#define PCin(n)   BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

//#define SDA_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}

//#define SDA_OUT(){GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}

//#define SCL_IN() {GPIOC->CRH&=0XFFF0FFFF;GPIOC->CRH|=8<<16;}

//#define SCL_OUT(){GPIOC->CRH&=0XFFF0FFFF;GPIOC->CRH|=3<<16;}

#define BITBAND(addr, bitnum)((addr & 0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2)) 

#define MEM_ADDR(addr)  *((volatileunsigned long  *)(addr)) 

#define BIT_ADDR(addr, bitnum)  MEM_ADDR(BITBAND(addr, bitnum)) 

/**

  * @brief Init Eeprom gpio clock

  */

static voidRCC_Configuration(void)

{

         //RCC->APB2ENR|=1<<4;//先使能外设IO PORTC时钟

         RCC_APB2PeriphClockCmd( EEPROM_I2C_RCC, ENABLE );      

}

/**

  * @brief Init Eeprom gpio

  */

static voidGPIO_Configuration(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

   

    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;

    GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_OD ;   // 开漏输出,

    GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;

    GPIO_Init(EEPROM_I2C_PORT, &GPIO_InitStructure);

    PCout(SCL_PIN) = 1;

    PCout(SDA_PIN) = 1;

}

     3)关于struct rt_i2c_bit_ops中定义接口的实现:   

static voiddelay_us(rt_uint32_t us)

{

    rt_uint32_t i = us;

    for(;i>0;i--){;}

}

static void gpio_set_sda(void*data, rt_int32_t state)

{  

    BIT_ADDR(((rt_uint32_t)data+4),SDA_PIN) =state;

}

static void gpio_set_scl(void*data, rt_int32_t state)

{

    BIT_ADDR(((rt_uint32_t)data+4),SCL_PIN) =state;

}

static rt_int32_tgpio_get_sda(void *data)

{

    rt_int32_t value;

    value =(rt_int32_t)BIT_ADDR((rt_uint32_t)data,SDA_PIN);

    return value;

}

static rt_int32_t gpio_get_scl(void*data)

{

    rt_int32_t value;

    value =(rt_int32_t)BIT_ADDR((rt_uint32_t)data,SCL_PIN);

    return value;

}

    接下来就是接口的结构体定义了:

static struct rt_i2c_bit_ops i2c_ops=

{

    .data = (void*)(GPIOC_BASE+8), // bitband port

    .set_sda =gpio_set_sda,

    .set_scl =gpio_set_scl,

    .get_sda =gpio_get_sda,

    .get_scl =gpio_get_scl,

    .udelay  = delay_us,

    .delay_us = 24,

    .timeout = 5

};

到此i2c总线驱动的接口就实现了,下面就是eeprom这个i2c总线设备的驱动实现了, 上篇就先到这里,转载请注出处谢谢。


你可能感兴趣的:(RT-thread学习笔记)