Linux下IIC驱动编写(驱动adxl345传感器)
基于IMX6ULL Mini开发板,硬件连接:SCL->43, SDA->42,CS->3.3V INT1->7
一、搭建基础框架
1、设置私有数据client,linux/i2c.h下其结构体为
struct i2c_client {unsigned short flags; /* 标志 */unsigned short addr; /* 芯片地址,7 位,存在低 7 位*/char name[I2C_NAME_SIZE]; /* 名字 */struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */struct device dev; /* 设备结构体 */int irq; /* 中断 */struct list_head detected;};
struct adxl345_drvdata{//set private datastruct i2c_client *client;struct timer_list timer;//timerstruct work_struct wq;//work queue
};
修改设备树
2、编写probe与remove函数
static int adxl345_probe(struct i2c_client* client,const struct i2c_device_id* id)
{int ret = 0;struct adxl345_drvdata *ddata;printk("%s called\n",__func__);//malloc memoryddata = (struct adxl345_drvdata*)kzalloc(sizeof(struct adxl345_drvdata),GFP_KERNEL);if(!ddata){printk(KERN_ERR"failed to malloc for driver data\n");ret = -ENOMEM;goto ERR_MALLOC_DRVDATA;}ddata->client = client;//set client as private data for transmit ddata to other functionsi2c_set_clientdata(client,ddata);return 0;ERR_MALLOC_DRVDATA:return ret;
}static int adxl345_remove(struct i2c_client *client)
{struct adxl345_drvdata *ddata = i2c_get_clientdata(client);printk("%s called\n",__func__);kfree(ddata);ddata=NULL;return 0;
}
3、编写i2c的设备驱动,其成员与一般的platform略微不同,需要查看源码定义
static struct i2c_device_id adxl345_id[]={{"adxl345",0},{},
};MODULE_DEVICE_TABLE(i2c,adxl345_id);static struct i2c_driver adxl345_driver={.probe = adxl345_probe,.remove = adxl345_remove,.driver = {.name = "adxl345",.owner = THIS_MODULE,},.id_table = adxl345_id,
};
4、编写加载、卸载函数
调用了i2c_add_driver和i2c_del_driver函数
static int __init adxl345_init(void)
{printk("%s called\n",__func__);return i2c_add_driver(&adxl345_driver);}static void __exit adxl345_exit(void)
{ printk("%s called\n",__func__);i2c_del_driver(&adxl345_driver);
}
驱动框架总体与platform相似,但又有所不同,需要多查看源码。
二、设置调试接口
待续。。。
三、ADXL345测试
3.1定时器接收数据
①向DATA_FORMAT 数据格式寄存器0X31写入0X2B。FULL_RES位值设置为1,该器件为全分辨率模式。Justify位设置为0,选择右对齐模式,并带有符号扩展功能。Range位设置为11设置±16g。
②向BW_RATE 数据速率及功率模式控制寄存器0X2C写入0A,设置转换为100Hz的输出数据速率。
③向POWER_CTL 省电特性控制寄存器0X2D写入0X28,将有使能活动和静止功能的链接位设置为1,延迟活动功能开始,直到检测到静止。检测到活动后,静止检测开始,活动检测停止。测量位设置为1,置于测
量模式。
④向INT_ENABLE中断使能控制寄存器0X2E写入0X00关闭中断。
⑤将X,Y,Z轴偏移寄存器设置为0,
⑥设置定时器,每秒读取三轴数据。
init_timer(&ddata->timer);
ddata->timer.function=adxl345_timer_func;
ddata->timer.expires=jiffies+HZ;
ddata->timer.data=(unsigned long)ddata;
add_timer(&ddata->timer);
定时器内读取数据
low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAX0_REG);
high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAX1_REG);
x_data = high_data<<8 | low_data;low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAY0_REG);
high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAY1_REG);
y_data = high_data<<8 | low_data;low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAZ0_REG);
high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAZ1_REG);
z_data = high_data<<8 | low_data;printk("x_data=%d,y_data=%d,z_data=%d \n",x_data,y_data,z_data);
此时出现bug
查资料发现,在定时器中调用i2c_transfer或者i2c_smbus_read_byte将会导致内核崩溃。i2c_transfer或者i2c_smbus_read_byte都会进行schedule,也就是任务切换,但是若在非进程上下文环境中schedule那么也就必然会导致内核崩溃。
解决方法:使用工作队列
struct work_struct wq;INIT_WORK(&ddata->wq,adxl345_workqueue_func);
在定时器中断函数调度工作队列
static void adxl345_timer_func(unsigned long data)
{struct adxl345_drvdata* ddata = (struct adxl345_drvdata*)data;schedule_work(&ddata->wq);mod_timer(&ddata->timer,jiffies+HZ);
}
在工作队列函数读取角度打印。成功测试读取三轴数据。
精妙之处:container_of
工作队列函数如下
void adxl345_workqueue_func(struct work_struct *work)
{struct adxl345_drvdata* ddata = container_of(work,struct adxl345_drvdata,wq);int16_t x_data=0,y_data=0,z_data=0;uint8_t high_data,low_data;low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAX0_REG);high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAX1_REG);x_data = high_data<<8 | low_data;low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAY0_REG);high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAY1_REG);y_data = high_data<<8 | low_data;low_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAZ0_REG);high_data = i2c_smbus_read_byte_data(ddata->client,ADXL345_DATAZ1_REG);z_data = high_data<<8 | low_data;printk("x_data=%d,y_data=%d,z_data=%d \n",x_data,y_data,z_data);
}
i2c_smbus_read_byte_data需要用到结构体adxl345_drvdata下的i2c_client。
于是使用container_of函数:container_of用来根据成员变量查找所在基结构体的地址,,其宏定义如下
#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})
ptr : 成员变量的地址
type : 要查找基结构体的变量类型
member : 成员变量名字
传入参数即可找到ddata的地址从而得到client。
3.2中断接收数据
在3.1的基础上修改内容
初始化adxl345寄存器配置
向INT_ENABLE中断使能控制寄存器0X2E写入0X00关闭中断。
向DATA_FORMAT 数据格式寄存器0X31写入0X0B。INT_INVERT位值为0,设置中断至高电平有效。
向BW_RATE 数据速率及功率模式控制寄存器0X2C写入0A,设置转换为100Hz的输出数据速率。
向POWER_CTL 省电特性控制寄存器0X2D写入0X28,将有使能活动和静止功能的链接位设置为1,延迟活动功能开始,直到检测到静止。检测到活动后,静止检测开始,活动检测停止。测量位设置为1,置于测量模式。
向INT_MAP 中断映射控制寄存器0X2F写入0X00,映射到INT1引脚
向INT_ENABLE中断使能控制寄存器0X2E写入0X80使能data ready中断。
中断调用报错i2c_smbus_read_byte_data,中断调度工作队列和tasklet仍然报错
查资料与源码发现i2c_smbus_read_byte_data函数内部使用mutex,而mutex 可以导致休眠,因此不能在中断中使用 mutex,中断中只能使用自旋锁。
未解决。
3.22解决中断报错问题
重写读取ic函数:
@description : 读取 I2C 设备多个寄存器数据@param – client : I2C client结构体@param – reg : 要读取的寄存器首地址@param – val : 读取到的数据@param – len : 要读取的数据长度@return : 操作结果static int adxl345_read_regs(struct i2c_client *client,u8 reg,void *val,int len)
{int ret;struct i2c_msg m[2];m[0].addr = ADXL345_ADDR;m[0].flags = 0;m[0].buf = ®m[0].len = 1;m[1].addr = ADXL345_ADDR;m[1].flags = I2C_M_RD;m[1].buf = val;m[1].len = len;ret = i2c_transfer(client->adapter,m,2);if(ret==2){ret = 0;}else{ret = -ENOMEM;}return ret;}
添加工作队列,在中断底半部执行该函数,最后成功获取数据。
m[0].len = 1;m[1].addr = ADXL345_ADDR;
m[1].flags = I2C_M_RD;
m[1].buf = val;
m[1].len = len;ret = i2c_transfer(client->adapter,m,2);
if(ret==2){ret = 0;
}
else{ret = -ENOMEM;
}
return ret;
}
添加工作队列,在中断底半部执行该函数,最后成功获取数据。![在这里插入图片描述](https://img-blog.csdnimg.cn/403d367859a5492abf09d1c1e5c463f4.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASmF5QHpoZW4=,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
Linux下IIC驱动编写(驱动adxl345传感器)相关推荐
- Linux下IIC子系统和触摸屏驱动
Linux下IIC子系统和触摸屏驱动 1.IIC简介 I2C( Inter-Integrated Circuit)总线是由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备. ...
- linux下IIC驱动开发分析
1. IIC规范 IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备.IIC总线产生于在80年代,最初为音频和 ...
- 如何编写Linux 下的 USB 键盘驱动
如何编写Linux 下的 USB 键盘驱动 1. 指定 USB 键盘驱动所需的头文件: #include <linux/kernel.h>/*内核头文件,含有内核一些常用函数的原型定 ...
- 什么是 Linux 下的 platform 设备驱动
Linux下的字符设备驱动一般都比较简单,只是对IO进行简单的读写操作.但是I2C.SPI.LCD.USB等外设的驱动就比较复杂了,需要考虑到驱动的可重用性,以避免内核中存在大量重复代码,为此人们提出 ...
- 南京邮电大学嵌入式系统开发实验5:嵌入式Linux下LED报警灯驱动设计及编程
实验5 嵌入式Linux下LED报警灯驱动设计及编程 一.实验目的 理解驱动本质,掌握嵌入式Linux系统下驱动开发相关知识,包括端口寄存器访问.接口函数编写.和文件系统挂接.注册及相关应用编程等知 ...
- Linux下的USB总线驱动 mouse
Linux下的USB总线驱动(03)--USB鼠标驱动 usbmouse.c USB鼠标驱动 usbmouse.c 原文链接:http://www.linuxidc.com/Linux/2012-12 ...
- *Linux下的USB总线驱动 u盘驱动分析*
Linux下的USB总线驱动(三) u盘驱动分析 版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127 https://www.xuebuyuan.com/13 ...
- 在Fedora 16 linux下安装USB无线网卡驱动88x2bu
在Fedora 16 linux下安装USB无线网卡驱动88x2bu USB无线网卡翼联EP-AC1610兼容linux系统 我之前已经买了一个USB无线网卡是水星mw150us,但是没有linux驱 ...
- linux 安装水星无线网卡驱动,Linux下安装RTL8188CE网卡驱动(Mercury MW150U)
先说明下我的系统: kernel: 3.0.0-32-generic 今天买了个无线网卡Mercury 150Mbps MW150U系列,我发现在我的笔记本的Ubuntu 12.10下不用安装驱动就能 ...
- Linux下PCI转串口卡驱动安装方法
Linux下PCI转串口卡驱动安装方法 ----------------------------------- 由于公司产品要做行业市场,而产品与行业用户间PC的通讯为RS232串口方式.而行业用户那 ...
最新文章
- Linux下安装配置virtualenv与virtualenvwrapper
- 顶象深度画像亮相GMIC,用AI提升金融反欺诈
- java 类型 装箱和拆箱
- C#事件-什么是事件
- 【11】MINST数据集的分类与效果验证
- ASP.Net网站管理工具配置
- mac tortoisesvn客户端_tortoisesvn mac版下载
- 数仓及其维度(分层)建模(ODS DWD DWS DWT ADS)
- 睁眼、耸肩、觉醒:人形机器人的吊诡与最终幻想
- 计算机ei浏览器没有了,ie浏览器不见了_我的电脑桌面上怎么IE浏览器没有了啊...
- [UNR#2]黎明前的巧克力
- SIMXXX 在高德地图定位到我的位置
- 论文阅读《Do Pre-trained Models Benefit Knowledge Graph Completion?A Reliable Evaluation and a Reasonab》
- 数据结构——非线性结构(树与二叉树)
- samp自建服务器教程,网管实战:十分钟建立SAMP开发环境
- CASIA手写体数据集HWDB1.0 gnt和dgrl格式解析
- 如何利用业余时间快乐的赚钱
- 渗透实战:dedeCMS任意用户密码重置到内网getshell
- 服务器系统坏道检测,使用Smartctl查Dell服务器坏道实录
- Pygame入门 2022 (4) 使用精灵类重构