RT-thread 设备驱动组件之IIC总线设备
本文主要介紹RT-thread中IIC總線設備驅(qū)動,涉及到的主要文件有:驅(qū)動框架文件(i2c_core.c,i2c_dev.c,i2c-bit-ops.c,i2c_dev.h,i2c.h);底層硬件驅(qū)動文件(i2c_soft.c,i2c_soft.h)。這里的i2c_soft.c和i2c_soft.h是指利用MCU的GPIO口模擬IIC總線時序,而不是利用MCU的硬件IIC接口。應用IIC總線設備驅(qū)動時,需要在rtconfig.h中添加宏定義#define RT_USING_I2C。若使用GPIO口模擬IIC總線,則還需要添加宏定義#define RT_USING_I2C_BITOPS。
一、IIC總線設備驅(qū)動框架
先看i2c.h中定義的一些數(shù)據(jù)結構:
#define RT_I2C_WR 0x0000
#define RT_I2C_RD (1u << 0)
#define RT_I2C_ADDR_10BIT (1u << 2) /* this is a ten bit chip address */
#define RT_I2C_NO_START (1u << 4)
#define RT_I2C_IGNORE_NACK (1u << 5)
#define RT_I2C_NO_READ_ACK (1u << 6) /* when I2C reading, we do not ACK */
struct rt_i2c_msg
{
rt_uint16_t addr;
rt_uint16_t flags;
rt_uint16_t len;
rt_uint8_t *buf;
};
struct rt_i2c_bus_device;
struct rt_i2c_bus_device_ops
{
rt_size_t (*master_xfer)(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num);
rt_size_t (*slave_xfer)(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num);
rt_err_t (*i2c_bus_control)(struct rt_i2c_bus_device *bus,
rt_uint32_t,
rt_uint32_t);
};
/*for i2c bus driver*/
struct rt_i2c_bus_device
{
struct rt_device parent;
const struct rt_i2c_bus_device_ops *ops;
rt_uint16_t flags;
rt_uint16_t addr;
struct rt_mutex lock;
rt_uint32_t timeout;
rt_uint32_t retries;
void *priv;
};
i2c_dev.h中相關數(shù)據(jù)結構(struct rt_i2c_priv_data用于i2c_bus_device_control()函數(shù)中RT_I2C_DEV_CTRL_RW控制標志):
#define RT_I2C_DEV_CTRL_10BIT 0x20
#define RT_I2C_DEV_CTRL_ADDR 0x21
#define RT_I2C_DEV_CTRL_TIMEOUT 0x22
#define RT_I2C_DEV_CTRL_RW 0x23
struct rt_i2c_priv_data
{
struct rt_i2c_msg *msgs;
rt_size_t number;
};
i2c-bit-ops.h中主要定義了模擬IIC總線時序時需要的數(shù)據(jù)結構:
struct rt_i2c_bit_ops
{
void *data; /* private data for lowlevel routines */
void (*set_sda)(void *data, rt_int32_t state);
void (*set_scl)(void *data, rt_int32_t state);
rt_int32_t (*get_sda)(void *data);
rt_int32_t (*get_scl)(void *data);
void (*udelay)(rt_uint32_t us);
rt_uint32_t delay_us; /* scl and sda line delay */
rt_uint32_t timeout; /* in tick */
};
在i2c_dev.c主要實現(xiàn)IIC設備驅(qū)動統(tǒng)一接口函數(shù):i2c_bus_device_read(),i2c_bus_device_write(),i2c_bus_device_control()以及rt_i2c_bus_device_device_init()。
rt_err_t rt_i2c_bus_device_device_init(struct rt_i2c_bus_device *bus,
const char *name)
{
struct rt_device *device;
RT_ASSERT(bus != RT_NULL);
device = &bus->parent;
device->user_data = bus;
/* set device type */
device->type = RT_Device_Class_I2CBUS;
/* initialize device interface */
device->init = RT_NULL;
device->open = RT_NULL;
device->close = RT_NULL;
device->read = i2c_bus_device_read;
device->write = i2c_bus_device_write;
device->control = i2c_bus_device_control;
/* register to device manager */
rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
return RT_EOK;
}
i2c_core.c中實現(xiàn)IIC總線設備注冊,以及使用IIC總線進行數(shù)據(jù)傳輸,如:rt_i2c_transfer(),rt_i2c_master_send(),rt_i2c_master_recv()。
rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
rt_err_t res = RT_EOK;
rt_mutex_init(&bus->lock, "i2c_bus_lock", RT_IPC_FLAG_FIFO);
if (bus->timeout == 0) bus->timeout = RT_TICK_PER_SECOND;
res = rt_i2c_bus_device_device_init(bus, bus_name);
i2c_dbg("I2C bus [%s] registered
", bus_name);
return res;
}
i2c-bit-ops.c中主要實現(xiàn)了利用GPIO模擬IIC總線時序的相關接口函數(shù),如:i2c_start(),i2c_restart(),i2c_stop(),i2c_waitack(),i2c_writeb(),i2c_readb(),i2c_send_bytes(),i2c_send_ack_or_nack(),i2c_recv_bytes(),i2c_send_address(),i2c_bit_send_address()等。并且實現(xiàn)了i2c_bit_xfer():
static const struct rt_i2c_bus_device_ops i2c_bit_bus_ops =
{
i2c_bit_xfer,
RT_NULL,
RT_NULL
};
rt_err_t rt_i2c_bit_add_bus(struct rt_i2c_bus_device *bus,
const char *bus_name)
{
bus->ops = &i2c_bit_bus_ops;
return rt_i2c_bus_device_register(bus, bus_name);
}
二、底層硬件驅(qū)動
本文采用的是模擬IIC,即用GPIO口模擬IIC時序。在i2c_soft.c中主要實現(xiàn)struct rt_i2c_bit_ops中的指針函數(shù):
void stm32_set_sda(void *data, rt_int32_t state)
{
if(state == 1)
GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SDA); //GPIOB->BSRRL = I2C1_GPIO_SDA
else if(state == 0)
GPIO_ResetBits(I2C1_GPIO , I2C1_GPIO_SDA); //GPIOB->BSRRH = I2C1_GPIO_SDA
}
void stm32_set_scl(void *data, rt_int32_t state)
{
if(state == 1)
GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SCL); //GPIOB->BSRRL = I2C1_GPIO_SCL
else if(state == 0)
GPIO_ResetBits(I2C1_GPIO , I2C1_GPIO_SCL); //GPIOB->BSRRH = I2C1_GPIO_SCL
}
rt_int32_t stm32_get_sda(void *data)
{
return (rt_int32_t)GPIO_ReadInputDataBit(I2C1_GPIO , I2C1_GPIO_SDA);//return(GPIOB->IDR & I2C1_GPIO_SDA)
}
rt_int32_t stm32_get_scl(void *data)
{
return (rt_int32_t)GPIO_ReadInputDataBit(I2C1_GPIO , I2C1_GPIO_SCL);//return(GPIOB->IDR & I2C1_GPIO_SCL)
}
void stm32_udelay(rt_uint32_t us)
{
rt_uint32_t delta;
/* ????us?óê±?ùDè??êy?μ£?sysTick->LOAD=21000, RT_TICK_PER_SECOND=1000 */
us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));
/* ??è?μ±?°à?àa??êy?μ */
delta = SysTick->VAL;
/* ?óê±us */
while (delta - SysTick->VAL< us);
}
void stm32_mdelay(rt_uint32_t ms)
{
stm32_udelay(ms * 1000);
}
static const struct rt_i2c_bit_ops stm32_i2c_bit_ops =
{
(void*)0xaa, //no use in set_sda,set_scl,get_sda,get_scl
stm32_set_sda,
stm32_set_scl,
stm32_get_sda,
stm32_get_scl,
stm32_udelay,
20,
5
};
最后,實現(xiàn)IIC總線硬件初始化(包括RCC時鐘配置和GPIO配置,最重要的是將stm32_i2c_bit_ops初始化為IIC總線設備結構體的priv變量,即stm32_i2c.priv = (void *)&stm32_i2c_bit_ops):
int rt_hw_i2c_init(void)
{
static struct rt_i2c_bus_device stm32_i2c;//"static" add by me. It must be add "static", or it will be hard fault
RCC_Configuration();
GPIO_Configuration();
rt_memset((void *)&stm32_i2c, 0, sizeof(struct rt_i2c_bus_device));
stm32_i2c.priv = (void *)&stm32_i2c_bit_ops;
rt_i2c_bit_add_bus(&stm32_i2c, "i2c1");
return 0;
}
INIT_BOARD_EXPORT(rt_hw_i2c_init);//rt_hw_i2c_init will be called in rt_components_board_init()
三、IIC總線設備初始化
這里以cs43l22數(shù)字音頻放大器為例:
static rt_err_t cs43l22_init(const char * i2c_bus_name)
{
i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(i2c_bus_name);
if(i2c_bus == RT_NULL)
{
rt_kprintf("
i2c_bus %s for cs43l22 not found!
", i2c_bus_name);
return -RT_ENOSYS;
}
/* oflag has no meaning for spi device , so set to RT_NULL */
if(rt_device_open(&i2c_bus->parent, RT_NULL) != RT_EOK)
{
rt_kprintf("
i2c_bus %s for cs43l22 opened failed!
", i2c_bus_name);
return -RT_EEMPTY;
}
EVAL_AUDIO_Init(OUTPUT_DEVICE_AUTO, volume, I2S_AudioFreq_48k);
/* it must be at the back of EVAL_AUDIO_Init, which reset the cs43l22 */
uint8_t chip_id = Codec_ReadRegister(i2c_bus, 0x01);
rt_kprintf("(chip_id of cs43l22 is 0x%02x)", chip_id);
return 0;
}
int rt_cs43l22_init(void)
{
rt_sem_init(&sem_cs43l22, "cs43l22", 1, RT_IPC_FLAG_FIFO);
cs43l22_init("i2c1");
return 0;
}
INIT_APP_EXPORT(rt_cs43l22_init);
注意事項:
1、在應用IIC總線設備驅(qū)動時,需要用到rt_device_read或rt_device_write,因此在初始化函數(shù)中需要調(diào)用rt_device_open將IIC總線設備打開。
2、下面利用rt_device_read和rt_device_write操作寄存器(每一次調(diào)用rt_device_read或rt_device_write都包括了i2c_start,i2c_bit_send_address,i2c_recv_bytes/i2c_send_bytes,i2c_stop這4個步驟):
static uint32_t Codec_WriteRegister(struct rt_i2c_bus_device * i2c_bus, uint8_t RegisterAddr, uint8_t RegisterValue)
{
uint32_t result = 0;
rt_uint16_t flags = 0x00;
rt_uint16_t DevAddr = (rt_uint16_t)CODEC_ADDRESS >> 1;
rt_off_t pos = (rt_off_t)((flags << 16) | DevAddr);
rt_uint8_t buffer[2];
buffer[0] = RegisterAddr;
buffer[1] = RegisterValue;
rt_device_write(&i2c_bus->parent, pos, buffer, sizeof(buffer));
#ifdef VERIFY_WRITTENDATA
/* Verify that the data has been correctly written */
result = (Codec_ReadRegister(i2c_bus, RegisterAddr) == RegisterValue)? 0:1;
if(result == 0)
rt_kprintf("
the reg 0x%02x verify passed
",RegisterAddr);
else
rt_kprintf("
the reg 0x%02x verify failed
",RegisterAddr);
#endif /* VERIFY_WRITTENDATA */
/* Return the verifying value: 0 (Passed) or 1 (Failed) */
return result;
}
static uint8_t Codec_ReadRegister(struct rt_i2c_bus_device * i2c_bus, uint8_t RegisterAddr)
{
rt_uint16_t flags = 0x00;
rt_uint16_t DevAddr = (rt_uint16_t)CODEC_ADDRESS >> 1;
rt_off_t pos = (rt_off_t)((flags << 16) | DevAddr);
rt_uint8_t buffer;
buffer = RegisterAddr;
rt_device_write(&i2c_bus->parent, pos, &buffer, 1);
rt_device_read(&i2c_bus->parent, pos, &buffer, 1);
/* Return the byte read from Codec */
return buffer;
}
在上面兩個函數(shù)中,有符號整型32位pos的高16位表示flags,低16位表示IIC器件地址。flags取值如i2c.h文件中宏定義所示。
這里說明一個問題:在rt_i2c_master_send和rt_i2c_master_recv函數(shù)中均有“msg.flags = flags & RT_I2C_ADDR_10BIT;”這一語句,該句用于標志IIC器件地址是否為10位地址,但是這條語句會將其他預置好的標志全部清除,如RT_I2C_NO_START,RT_I2C_IGNORE_NACK或RT_I2C_NO_READ_ACK。所以,在一般情況下,flags標志只能事先預置RT_I2C_ADDR_10BIT,若IIC器件地址為7位,則直接設置flags為0。
3、根據(jù)i2c_bit_send_address()函數(shù)中:
else
{
/* 7-bit addr */
addr1 = msg->addr << 1;
if (flags & RT_I2C_RD)
addr1 |= 1;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != 1) && !ignore_nack)
return -RT_EIO;
}
可得,若IIC器件地址為7位,則pos低16位所表示的地址值DevAddr不包括讀寫標志位(最低位)。而cs43l22數(shù)據(jù)手冊中的8位地址值包含了讀寫標志位,因此設置DevAddr為CODEC_ADDRESS >> 1。
總結
以上是生活随笔為你收集整理的RT-thread 设备驱动组件之IIC总线设备的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 100层楼2个鸡蛋,如何得知鸡蛋能承受几
- 下一篇: 计量经济学_一元线性回归_估计量与估计值