1. MMA7660芯片介紹
MMA7660FC 是 ± 1.5 克的三軸數(shù)字輸出、超低功率、緊湊型電容式微電機的三軸加速度計,是非常低功耗,小型容性 MEMS 的傳感器。具有低通濾波器,用于偏移和增益誤差補償, 以及用戶可配置的轉(zhuǎn)換成 6 位分辨率,用戶可配置輸出速率等功能。MMA7660芯片可以通過中斷引腳(INT)向外通知傳感器數(shù)據(jù)變化、方向、姿態(tài)識別等信息。模擬工作電壓范圍是 2.4V 至 3.6V,數(shù)字工作電壓范圍是 1.71V 到 3.6V 。常用在手機、掌上電腦、車載導(dǎo)航,便攜式電腦的防盜,自動自行車剎車燈、運動檢測手環(huán)、數(shù)碼機、自動叫醒鬧鐘里等等。

特別是計步的功能是現(xiàn)在最常見,不管是智能手環(huán)、還是手機都帶有三軸加速度計,可以記錄每天的步數(shù),計算運動量等。現(xiàn)在很多的不倒翁,無人機、相機云臺,很多常見的產(chǎn)品里都能看到三軸加速計的身影。
通過MMA7660可以做出很多項目: 比如: 老人防跌倒手環(huán)、自行車自動剎車燈,智能鬧鐘,烤火爐跌倒自動斷電、運動手環(huán)等等。
這篇文章就介紹如何在linux下編寫MMA7660三軸加速度芯片的驅(qū)動,讀取當前芯片的方向姿態(tài),得到X,Y,Z三個軸的數(shù)據(jù)。MMA7660是IIC接口的,當前驅(qū)動就采用標準的IIC子系統(tǒng)編寫驅(qū)動,使用字符設(shè)備框架將得到的數(shù)據(jù)上傳遞給應(yīng)用層。
2. 硬件連線
當前使用的開發(fā)板是友善之臂Tiny4412開發(fā)板,使用三星EXYNOS4412芯片,板子本身自帶了一顆MMA7660芯片,芯片的原理圖如下:
內(nèi)核本身有MMA7660的驅(qū)動,下面是源碼的路徑:
如果加載自己編寫的驅(qū)動,還需要去掉原來內(nèi)核自帶的驅(qū)動,不然無法匹配。
Device Drivers ---> <*> Hardware Monitoring support ---> <*> Freescale MMA7660 Accelerometer (將*號去掉,編譯內(nèi)核、燒寫內(nèi)核即可)
3. 源代碼
3.1 mma7660設(shè)備端代碼: IIC子系統(tǒng)
#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. 探測設(shè)備是否存在 3. 定義一個名字用于找到驅(qū)動端 */ static int __init mma7660_dev_init(void) { /*mach-tiny4412.c*/ struct i2c_adapter *i2c_adap=NULL; /*獲取到的總線存放在這個結(jié)構(gòu)體*/ struct i2c_board_info i2c_info; /*設(shè)備描述結(jié)構(gòu)體,里面存放著設(shè)備的名字還有地址*/ /*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)); /*清空結(jié)構(gòu)體*/ strlcpy(i2c_info.type,"mma7660_drv",I2C_NAME_SIZE); /*名稱的賦值*/ i2c_info.irq=EXYNOS4_GPX3(1); /*中斷IO口*/ /*2. 創(chuàng)建IIC設(shè)備客戶端*/ i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL); if(!i2cClient) { printk("mma7660_探測地址出現(xiàn)錯誤!!\n"); return -1; } i2c_put_adapter(i2c_adap);/*設(shè)置模塊使用計數(shù)*/ printk("mma7660_dev_init!!\n"); return 0; } static void __exit mma7660_dev_exit(void)//平臺設(shè)備端的出口函數(shù) { printk(" mma7660_dev_exit ok!!\n"); /*注銷設(shè)備*/ i2c_unregister_device(i2cClient); /*釋放*/ i2c_release_client(i2cClient); } module_init(mma7660_dev_init); module_exit(mma7660_dev_exit); MODULE_LICENSE("GPL");
3.2 mma7660驅(qū)動端代碼: IIC子系統(tǒng)
#include #include #include #include #include #include #include #include #include #include #include /* MMA7760 Registers */ #define MMA7660_XOUT 0x00 // 6-bit output value X #define MMA7660_YOUT 0x01 // 6-bit output value Y #define MMA7660_ZOUT 0x02 // 6-bit output value Z #define MMA7660_TILT 0x03 // Tilt status #define MMA7660_SRST 0x04 // Sampling Rate Status #define MMA7660_SPCNT 0x05 // Sleep Count #define MMA7660_INTSU 0x06 // Interrupt Setup #define MMA7660_MODE 0x07 // Mode #define MMA7660_SR 0x08 // Auto-Wake/Sleep and Debounce Filter #define MMA7660_PDET 0x09 // Tap Detection #define MMA7660_PD 0x0a // Tap Debounce Count static const struct i2c_device_id mma7660_id[] = { {"mma7660_drv",0}, /*設(shè)備端的名字,0表示不需要私有數(shù)據(jù)*/ {} }; static u32 mma7660_irq; /*觸摸屏的中斷編號*/ static struct i2c_client *mma7660_client=NULL; static int last_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[] = { "未知", "左面", "向右", "保留", "保留", "向下", "向上", "保留", }; /* 函數(shù)功能:讀取一個字節(jié)的數(shù)據(jù) */ 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; } /* 函數(shù)功能: 讀取XYZ坐標數(shù)據(jù) */ 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; } /* 工作隊列處理函數(shù) */ 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]); } /* 函數(shù)功能: 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; } /* 靜態(tài)方式初始化工作隊列 */ DECLARE_WORK(mma7660_work,mma7660_worker); static irqreturn_t mma7660_interrupt(int irq, void *dev_id) { /*調(diào)度共享工作隊列*/ schedule_work(&mma7660_work); return IRQ_HANDLED; } /* 匹配成功時調(diào)用 */ static int mma7660_probe(struct i2c_client *client, const struct i2c_device_id *device_id) { printk("mma7660_probe!!!\n"); printk("驅(qū)動端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, //探測函數(shù) .remove = mma7660_remove, //資源卸載 .id_table = mma7660_id, //里面有一個名字的參數(shù)用來匹配設(shè)備端名字 }; static int __init mma7660_drv_init(void) { /*向iic總線注冊一個驅(qū)動*/ i2c_add_driver(&i2c_drv); return 0; } static void __exit mma7660_drv_exit(void) { /*從iic總線注銷一個驅(qū)動*/ i2c_del_driver(&i2c_drv); } module_init(mma7660_drv_init); module_exit(mma7660_drv_exit); MODULE_LICENSE("GPL");
Linux 硬件開發(fā)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。