kaiyun官方注册
您所在的位置: 首页> 嵌入式技术> 其他> Linux驱动开发-编写MMA7660三轴加速度传感器

Linux驱动开发-编写MMA7660三轴加速度传感器

2022-09-29
来源:电子发烧友网

  【摘要】 通过MMA7660可以做出很多项目: 比如: 老人防跌倒手环、自行车自动刹车灯,智能闹钟,烤火炉跌倒自动断电、运动手环等等。 这篇文章就介绍如何在Linux下编写MMA7660三轴加速度芯片的驱动,读取当前芯片的方向姿态,得到X,Y,Z三个轴的数据。MMA7660是IIC接口的,当前驱动就采用标准的IIC子系统编写驱动,使用字符设备框架将得到的数据上传递给应用层。

  1. MMA7660芯片介绍

  MMA7660FC 是 ± 1.5 克的三轴数字输出、超低功率、紧凑型电容式微电机的三轴加速度计,是非常低功耗,小型容性 MEMS 的传感器。具有低通滤波器,用于偏移和增益误差补偿, 以及用户可配置的转换成 6 位分辨率,用户可配置输出速率等功能。MMA7660芯片可以通过中断引脚(INT)向外通知传感器数据变化、方向、姿态识别等信息。模拟工作电压范围是 2.4V 至 3.6V,数字工作电压范围是 1.71V 到 3.6V 。常用在手机、掌上电脑、车载导航,便携式电脑的防盗,自动自行车刹车灯、运动检测手环、数码机、自动叫醒闹钟里等等。

  特别是计步的功能是现在最常见,不管是智能手环、还是手机都带有三轴加速度计,可以记录每天的步数,计算运动量等。现在很多的不倒翁,无人机、相机云台,很多常见的产品里都能看到三轴加速计的身影。

  通过MMA7660可以做出很多项目: 比如: 老人防跌倒手环、自行车自动刹车灯,智能闹钟,烤火炉跌倒自动断电、运动手环等等。

  这篇文章就介绍如何在Linux下编写MMA7660三轴加速度芯片的驱动,读取当前芯片的方向姿态,得到X,Y,Z三个轴的数据。MMA7660是IIC接口的,当前驱动就采用标准的IIC子系统编写驱动,使用字符设备框架将得到的数据上传递给应用层。

12.JPG

  2. 硬件连线

  当前使用的开发板是友善之臂Tiny4412开发板,使用三星EXYNOS4412芯片,板子本身自带了一颗MMA7660芯片,芯片的原理图如下:

11.JPG

  内核本身有MMA7660的驱动,下面是源码的路径:

10.JPG

09.JPG

  如果加载自己编写的驱动,还需要去掉原来内核自带的驱动,不然无法匹配。

  Device Drivers --->

  <*> Hardware Monitoring support --->

  <*> Freescale MMA7660 Accelerometer (将*号去掉,编译内核、烧写内核即可)

08.JPG

  3. 源代码

  3.1 mma7660设备端代码: IIC子系统

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  static struct i2c_client *i2cClient = NULL;

  static unsigned short i2c_addr_list[]= {0x4c, I2C_CLIENT_END};/*地址队列*/

  /*

  1. 获取控制器(总线)

  2. 探测设备是否存在

  3. 定义一个名字用于找到驱动端

  */

  static int __init mma7660_dev_init(void)

  {

  /*mach-tiny4412.c*/

  struct i2c_adapter *i2c_adap=NULL; /*获取到的总线存放在这个结构体*/

  struct i2c_board_info i2c_info; /*设备描述结构体,里面存放着设备的名字还有地址*/

  /*1. 获取IIC控制器*/

  i2c_adap = i2c_get_adapter(3); /*要使用IIC_3号总线*/

  if(!i2c_adap)

  {

  printk(“获取IIC控制器信息失败!\n”);

  return -1;

  }

  memset(&i2c_info,0,sizeof(struct i2c_board_info)); /*清空结构体*/

  strlcpy(i2c_info.type,“mma7660_drv”,I2C_NAME_SIZE); /*名称的赋值*/

  i2c_info.irq=EXYNOS4_GPX3(1); /*中断IO口*/

  /*2. 创建IIC设备客户端*/

  i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);

  if(!i2cClient)

  {

  printk(“mma7660_探测地址出现错误!!\n”);

  return -1;

  }

  i2c_put_adapter(i2c_adap);/*设置模块使用计数*/

  printk(“mma7660_dev_init!!\n”);

  return 0;

  }

  static void __exit mma7660_dev_exit(void)//平台设备端的出口函数

  {

  printk(“ mma7660_dev_exit ok!!\n”);

  /*注销设备*/

  i2c_unregister_device(i2cClient);

  /*释放*/

  i2c_release_client(i2cClient);

  }

  module_init(mma7660_dev_init);

  module_exit(mma7660_dev_exit);

  MODULE_LICENSE(“GPL”);

  3.2 mma7660驱动端代码: IIC子系统

  复制

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  #include

  /* MMA7760 Registers */

  #define MMA7660_XOUT0x00// 6-bit output value X

  #define MMA7660_YOUT0x01// 6-bit output value Y

  #define MMA7660_ZOUT0x02// 6-bit output value Z

  #define MMA7660_TILT0x03// Tilt status

  #define MMA7660_SRST0x04// Sampling Rate Status

  #define MMA7660_SPCNT0x05// Sleep Count

  #define MMA7660_INTSU0x06// Interrupt Setup

  #define MMA7660_MODE0x07// Mode

  #define MMA7660_SR0x08// Auto-Wake/Sleep and Debounce Filter

  #define MMA7660_PDET0x09// Tap Detection

  #define MMA7660_PD0x0a// Tap Debounce Count

  static const struct i2c_device_id mma7660_id[] =

  {

  {“mma7660_drv”,0}, /*设备端的名字,0表示不需要私有数据*/

  {}

  };

  static u32 mma7660_irq; /*触摸屏的中断编号*/

  static struct i2c_client *mma7660_client=NULL;

  static intlast_tilt = 0;

  #define __need_retry(__v)(__v & (1 《 6))

  #define __is_negative(__v)(__v & (1 《 5))

  static const char *mma7660_bafro[] = {

  “未知”, “前面”, “背面”

  };

  static const char *mma7660_pola[] = {

  “未知”,

  “左面”, “向右”,

  “保留”, “保留”,

  “向下”, “向上”,

  “保留”,

  };

  /*

  函数功能:读取一个字节的数据

  */

  static int mma7660_read_tilt(struct i2c_client *client, int *tilt)

  {

  int val;

  do {

  val = i2c_smbus_read_byte_data(client, MMA7660_TILT);

  } while (__need_retry(val));

  *tilt = (val & 0xff);

  return 0;

  }

  /*

  函数功能: 读取XYZ坐标数据

  */

  static int mma7660_read_xyz(struct i2c_client *client, int idx, int *xyz)

  {

  int val;

  do {

  val = i2c_smbus_read_byte_data(client, idx + MMA7660_XOUT);

  } while (__need_retry(val));

  *xyz = __is_negative(val) ? (val | ~0x3f) : (val & 0x3f);

  return 0;

  }

  /*

  工作队列处理函数

  */

  static void mma7660_worker(struct work_struct *work)

  {

  int bafro, pola, shake, tap;

  int val = 0;

  mma7660_read_tilt(mma7660_client,&val);

  /* TODO: report it ? */

  bafro = val & 0x03;

  if (bafro != (last_tilt & 0x03)) {

  printk(“%s\n”, mma7660_bafro[bafro]);

  }

  pola = (val 》 2) & 0x07;

  if (pola != ((last_tilt 》 2) & 0x07)) {

  printk(“%s\n”, mma7660_pola[pola]);

  }

  shake = (val 》 5) & 0x01;

  if (shake && shake != ((last_tilt 》 5) & 0x01)) {

  printk(“Shake\n”);

  }

  tap = (val 》 7) & 0x01;

  if (tap && tap != ((last_tilt 》 7) & 0x01)) {

  printk(“Tap\n”);

  }

  /* Save current status */

  last_tilt = val;

  int axis[3];

  int i;

  for (i = 0; i < 3; i++)

  {

  mma7660_read_xyz(mma7660_client, i, &axis[i]);

  }

  printk(“ABS_X=%d\n”,axis[0]);

  printk(“ABS_Y=%d\n”,axis[1]);

  printk(“ABS_Z=%d\n”,axis[2]);

  }

  /*

  函数功能: mma7660初始化

  */

  static int mma7660_initialize(struct i2c_client *client)

  {

  int val;

  /* Using test mode to probe chip */

  i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);

  mdelay(10);

  i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x04);

  mdelay(10);

  i2c_smbus_write_byte_data(client, MMA7660_XOUT, 0x3f);

  i2c_smbus_write_byte_data(client, MMA7660_YOUT, 0x01);

  i2c_smbus_write_byte_data(client, MMA7660_ZOUT, 0x15);

  val = i2c_smbus_read_byte_data(client, MMA7660_ZOUT);

  if (val != 0x15) {

  dev_err(&client->dev, “no device\n”);

  return -ENODEV;

  }

  /* Goto standby mode for configuration */

  i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);

  mdelay(10);

  /* Sample rate: 64Hz / 16Hz; Filt: 3 samples */

  i2c_smbus_write_byte_data(client, MMA7660_SR, ((2《5) | (1《3) | 1));

  /* Sleep count */

  i2c_smbus_write_byte_data(client, MMA7660_SPCNT, 0xA0);

  /* Tap detect and debounce ~4ms */

  i2c_smbus_write_byte_data(client, MMA7660_PDET, 4);

  i2c_smbus_write_byte_data(client, MMA7660_PD, 15);

  /* Enable interrupt except exiting Auto-Sleep */

  i2c_smbus_write_byte_data(client, MMA7660_INTSU, 0xe7);

  /* IPP, Auto-wake, auto-sleep and standby */

  i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x59);

  mdelay(10);

  /* Save current tilt status */

  mma7660_read_tilt(client, &last_tilt);

  mma7660_client = client;

  return 0;

  }

  /*

  静态方式初始化工作队列

  */

  DECLARE_WORK(mma7660_work,mma7660_worker);

  static irqreturn_t mma7660_interrupt(int irq, void *dev_id)

  {

  /*调度共享工作队列*/

  schedule_work(&mma7660_work);

  return IRQ_HANDLED;

  }

  /*

  匹配成功时调用

  */

  static int mma7660_probe(struct i2c_client *client, const struct i2c_device_id *device_id)

  {

  printk(“mma7660_probe!!!\n”);

  printk(“驱动端IIC匹配的地址=0x%x\n”,client->addr);

  mma7660_client=client;

  /*1. 注册中断*/

  mma7660_irq=gpio_to_irq(client->irq);/*获取中断编号*/

  if(request_irq(mma7660_irq,mma7660_interrupt,IRQF_TRIGGER_FALLING,“mma7660_irq”,NULL)!=0)

  {

  printk(“mma7660_中断注册失败!\n”);

  }

  /*2. 初始化mma7660*/

  if(mma7660_initialize(client) < 0)

  {

  printk(“ 初始化mma7660失败!\n”);

  }

  return 0;

  }

  static int mma7660_remove(struct i2c_client *client)

  {

  free_irq(mma7660_irq,NULL);

  printk(“mma7660_remove!!!\n”);

  return 0;

  }

  struct i2c_driver i2c_drv =

  {

  .driver =

  {

  .name = “mma7660”,

  .owner = THIS_MODULE,

  },

  .probe = mma7660_probe, //探测函数

  .remove = mma7660_remove, //资源卸载

  .id_table = mma7660_id, //里面有一个名字的参数用来匹配设备端名字

  };

  static int __init mma7660_drv_init(void)

  {

  /*向iic总线注册一个驱动*/

  i2c_add_driver(&i2c_drv);

  return 0;

  }

  static void __exit mma7660_drv_exit(void)

  {

  /*从iic总线注销一个驱动*/

  i2c_del_driver(&i2c_drv);

  }

  module_init(mma7660_drv_init);

  module_exit(mma7660_drv_exit);

  MODULE_LICENSE(“GPL”);



更多信息可以来这里获取==>>电子技术应用-AET<<

mmexport1621241704608.jpg

本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306116;邮箱:aet@chinaaet.com。
Baidu
map