RT-thread中关于I2C设备驱动主要有i2c_core.c、i2c_dev.c、i2c-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总线设备的驱动实现了, 上篇就先到这里,转载请注出处谢谢。