RT-thread 设备驱动组件之IIC总线设备
本文主要介绍RT-thread中IIC总线设备驱动,涉及到的主要文件有:驱动框架文件(i2c_core.c,i2c_dev.c,i2c-bit-ops.c,i2c_dev.h,i2c.h);底层硬件驱动文件(i2c_soft.c,i2c_soft.h)。这里的i2c_soft.c和i2c_soft.h是指利用MCU的GPIO口模拟IIC总线时序,而不是利用MCU的硬件IIC接口。应用IIC总线设备驱动时,需要在rtconfig.h中添加宏定义#define RT_USING_I2C。若使用GPIO口模拟IIC总线,则还需要添加宏定义#define RT_USING_I2C_BITOPS。
一、IIC总线设备驱动框架
先看i2c.h中定义的一些数据结构:
#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中相关数据结构(struct rt_i2c_priv_data用于i2c_bus_device_control()函数中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总线时序时需要的数据结构:
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主要实现IIC设备驱动统一接口函数: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中实现IIC总线设备注册,以及使用IIC总线进行数据传输,如: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 == ) bus->timeout = RT_TICK_PER_SECOND; res = rt_i2c_bus_device_device_init(bus, bus_name); i2c_dbg("I2C bus [%s] registered\n", bus_name); return res;
}
i2c-bit-ops.c中主要实现了利用GPIO模拟IIC总线时序的相关接口函数,如: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()等。并且实现了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);
}
二、底层硬件驱动
本文采用的是模拟IIC,即用GPIO口模拟IIC时序。在i2c_soft.c中主要实现struct rt_i2c_bit_ops中的指针函数:
void stm32_set_sda(void *data, rt_int32_t state)
{
if(state == )
GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SDA); //GPIOB->BSRRL = I2C1_GPIO_SDA
else if(state == )
GPIO_ResetBits(I2C1_GPIO , I2C1_GPIO_SDA); //GPIOB->BSRRH = I2C1_GPIO_SDA
} void stm32_set_scl(void *data, rt_int32_t state)
{
if(state == )
GPIO_SetBits(I2C1_GPIO , I2C1_GPIO_SCL); //GPIOB->BSRRL = I2C1_GPIO_SCL
else if(state == )
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ÑÓʱËùÐè¼ÆÊýÖµ£»sysTick->LOAD=21000, RT_TICK_PER_SECOND=1000 */
us = us * (SysTick->LOAD/(/RT_TICK_PER_SECOND));
/* »ñÈ¡µ±Ç°àÖ઼ÆÊýÖµ */
delta = SysTick->VAL;
/* ÑÓʱus */
while (delta - SysTick->VAL< us);
} void stm32_mdelay(rt_uint32_t ms)
{
stm32_udelay(ms * );
} 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,
, };
最后,实现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, , sizeof(struct rt_i2c_bus_device));
stm32_i2c.priv = (void *)&stm32_i2c_bit_ops;
rt_i2c_bit_add_bus(&stm32_i2c, "i2c1"); return ;
}
INIT_BOARD_EXPORT(rt_hw_i2c_init);//rt_hw_i2c_init will be called in rt_components_board_init()
三、IIC总线设备初始化
这里以cs43l22数字音频放大器为例:
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("\ni2c_bus %s for cs43l22 not found!\n", 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("\ni2c_bus %s for cs43l22 opened failed!\n", 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 ;
} int rt_cs43l22_init(void)
{
rt_sem_init(&sem_cs43l22, "cs43l22", , RT_IPC_FLAG_FIFO); cs43l22_init("i2c1"); return ;
}
INIT_APP_EXPORT(rt_cs43l22_init);
注意事项:
1、在应用IIC总线设备驱动时,需要用到rt_device_read或rt_device_write,因此在初始化函数中需要调用rt_device_open将IIC总线设备打开。
2、下面利用rt_device_read和rt_device_write操作寄存器(每一次调用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 = ; rt_uint16_t flags = 0x00;
rt_uint16_t DevAddr = (rt_uint16_t)CODEC_ADDRESS >> ;
rt_off_t pos = (rt_off_t)((flags << ) | DevAddr); rt_uint8_t buffer[];
buffer[] = RegisterAddr;
buffer[] = 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)? :;
if(result == )
rt_kprintf("\nthe reg 0x%02x verify passed\n",RegisterAddr);
else
rt_kprintf("\nthe reg 0x%02x verify failed\n",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 >> ;
rt_off_t pos = (rt_off_t)((flags << ) | DevAddr); rt_uint8_t buffer;
buffer = RegisterAddr; rt_device_write(&i2c_bus->parent, pos, &buffer, );
rt_device_read(&i2c_bus->parent, pos, &buffer, ); /* Return the byte read from Codec */
return buffer;
}
在上面两个函数中,有符号整型32位pos的高16位表示flags,低16位表示IIC器件地址。flags取值如i2c.h文件中宏定义所示。
这里说明一个问题:在rt_i2c_master_send和rt_i2c_master_recv函数中均有“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、根据i2c_bit_send_address()函数中:
else
{
/* 7-bit addr */
addr1 = msg->addr << ;
if (flags & RT_I2C_RD)
addr1 |= ;
ret = i2c_send_address(bus, addr1, retries);
if ((ret != ) && !ignore_nack)
return -RT_EIO;
}
可得,若IIC器件地址为7位,则pos低16位所表示的地址值DevAddr不包括读写标志位(最低位)。而cs43l22数据手册中的8位地址值包含了读写标志位,因此设置DevAddr为CODEC_ADDRESS >> 1。
RT-thread 设备驱动组件之IIC总线设备的更多相关文章
- Linux设备驱动模型之I2C总线
一.I2C子系统总体架构 1.三大组成部分 (1)I2C核心(i2c-core):I2C核心提供了I2C总线驱动(适配器)和设备驱动的注册.注销方法,提供了与具体硬件无关的I2C读写函数. (2)I2 ...
- linux 块设备驱动 (三)块设备驱动开发
一: 块设备驱动注册与注销 块设备驱动中的第1个工作通常是注册它们自己到内核,完成这个任务的函数是 register_blkdev(),其原型为:int register_blkdev(unsigne ...
- RT thread 设备驱动组件之USART设备
本文以stm32f4xx平台介绍串口驱动,主要目的是:1.RTT中如何编写中断处理程序:2.如何编写RTT设备驱动接口代码:3.了解串行设备的常见处理机制.所涉及的主要源码文件有:驱动框架文件(usa ...
- Linux设备驱动剖析之IIC(二)
953行,适配器的编号大于MAX_ID_MASK是不行的,MAX_ID_MASK是一个宏,展开后的值为61. 957至968行,关于管理小整形ID数的,没怎么了解,略过. 974行,调用i2c_reg ...
- Linux设备驱动剖析之IIC(一)
写在前面 由于IIC总线只需要两根线就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用.但是IIC总线有一个缺点,就是传输速率比较低.本文基于Linux-2.6.36版本 ...
- usb驱动开发5之总线设备与接口
Linux设备模型中的总线落实在USB子系统里就是usb_bus_type,它在usb_init的函数bus_register(&usb_bus_type)里注册.usb_bus_type定义 ...
- usb驱动开发4之总线设备驱动模型
在上文说usb_init函数,却给我们留下了很多岔路口.这次就来好好聊聊关于总线设备驱动模型.这节只讲理论,不讲其中的函数方法,关于函数方法使用参考其他资料. 总线.设备.驱动对应内核结构体分别为bu ...
- Linux设备驱动剖析之IIC(四)
558行,又重试2次. 560行,调用s3c24xx_i2c_doxfer函数: static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, stru ...
- RT-thread 设备驱动组件之SPI设备
本文主要介绍RT-thread中的SPI设备驱动,涉及到的文件主要有:驱动框架文件(spi_dev.c,spi_core.c,spi.h),底层硬件驱动文件(spi_hard.c,spi_hard.h ...
随机推荐
- Ubuntu Server 下将HTML页面转换为PNG图片
零.前言 最近做一个网站,需要将网页转换为图片.由于服务器是Ubuntu Server,没有图形界面,所以实现的过程中遇到了很多问题.记录下来备用. 一.安装CutyCapt CutyCapt是一个可 ...
- Android性能优化来龙去脉总结
WeTest 导读 一款app除了要有令人惊叹的功能和令人发指交互之外,在性能上也应该追求丝滑的要求,这样才能更好地提高用户体验. 以下是本人在工作中对经历过的性能优化的一些总结,依据故事的发展路线, ...
- HTML5项目笔记10:使用HTML5 IndexDB设计离线数据库
之前的文章(http://www.cnblogs.com/wzh2010/archive/2012/05/22/2514017.html)里面描述了HTML5 离线数据存储的Web SQL,一个基于S ...
- C# Builder
如下: class Program { static void Main(string[] args) { ).BuildB(2.1).BuildUp(); Console.Read(); } } p ...
- Python 的非正式介绍
在下面的例子中,通过提示符 (>>> 与 ...) 的出现与否来区分输入和输出:如果你想复现这些例子,当提示符出现后,你必须在提示符后键入例子中的每一个词:不以提示符开头的那些行是解 ...
- 【system.string】使用说明
对象:system.string 说明:提供一系列针对字符串类型的操作 目录: 方法 返回 说明 system.string.isBlank( string ) [True | False] 检测参 ...
- Linux命令详解----ln
ln命令 ln命令为文件或文件夹创建连接,连接类型有硬链接和符号连接两种,符号连接需要使用"-s"选项 ln语法 ln [选项] 参数 使用 ln --help查看可用选项 [ro ...
- Linux系统查看系统版本命令
以下操作在centos系统上实现,有些方式可能只适用centos/redhat版本系统 uname -a |uname -r查看内核版本信息 [root@node1 ~]# uname -a Linu ...
- 《javascript模式--by Stoyan Stefanov》书摘--字面量和构造函数
二.字面量和构造函数 1,能够使用对象字面量时,就没理由使用new Object构造函数 // 一个空对象var 0 = new Object();console.log( o.constructor ...
- apache不解析php文件遍历目录
程序目录下有index.php缺不能正常解析,直接刷出整个目录. 解决:在后面添加index.php的解析即可.. DirectoryIndex index.html index.html.var i ...