Tiny4412之重力感应器驱动
一:Tiny4412 裸板重力感应驱动编写
整个编写过程大概分为如下几步:
(1)在底板上找到重力感应器的所在位置,找到芯片型号(我用的板子上重力感应器芯片型号为:MMA7660FC)
(2)通过型号查看重力感应器电路图,了解每个引脚的功能
(3)找到引脚对应的网标(EINT,SDL,SCL)配置其相应的gpio口
(4)配置芯片相关寄存器
(5)使用I2C来读取重力感应器相应寄存器的值
下面是整个驱动的简单流程图:
通过看底板我们知道了重力感应器的控制芯片型号为:MMA7660FC,接下来我们就在看看改芯片的电路图(如下):
由上图我们可以看出它的外部中断XEINT25,用到的i2c总线为SDA3、SCL3
通过网标我们可以看到它是怎么连接到核心板上的(soc)
由上图可知,XEINT25、I2CSDA3、SCL3所对应的gpio分别为GPA1_2、GPA1_3、GPX3_1,之后我们就可以配置gpio口为相应的功能
通过datasheet我们可以找到它的外部中断号为64,如下图:
简单了解了mma7660的电路图之后我们就需要看芯片的datasheet,了解里面寄存器的配置及功能
首先看一下整个芯片与我们核心板的连接图:
由上图我们可以看出,芯片连接到我们核心板(soc)的总共为三根线:INT( 中断信号),SDA、SCL(通i2c来进行寄存器的配置和读取)
下面看一下该芯片的几种工作模式:
今天我们实验的所需要的就是就当板子在晃动的时候它会出发中断,我们利用i2c来读取它相应寄存器的值,来判断前后左右以及X、Y、Z的值
下面是该芯片的寄存器及功能:
由上图我们可以看出寄存器:0x00、0x01、0x02它的底六位分别存储着对应的X、Y、Z的值,因此在之后的试验中我们就可以利用i2c来读取它里面的值
0x03这个寄存器的具体功能如下所所示、它里面存储了前后左右上下的值:
下面的几个寄存器是我们需要配置的:
这个寄存是:该芯片里面的中断寄存器,我们需要配置相应值来响应中断,光配置外部中断是不可以的,我们需要配置该寄存器来响应我们的外部中断
该寄存器是配置相应的模式,我们需要配置它为shake,当我们在晃动板子是可以出发中断;配置好这两个寄存器之后,我们就可以使用I2C来读取对应寄存器的值了
接下来我们需要做的就是设置相应的gpio口,以及配置好I2C的相应寄存器:
I2C总线驱动在上一篇中已经介绍了,I2C总线设备分为四种模式:主端发送、主端接收,从端发送、从端接收,在该驱动中我们用到的只有主发跟主收,下面是相应的流程图,整个I2C的读写都是按照这个流程:
主端发送:
主端接收:
配置好整个I2C之后我们就可以对芯片进行相应的设置和读取了,下面贴出真个重力感应驱动的代码:
1 #ifndef __REGS_H 2 #define __REGS_H 3 4 #define printf(...) (((int (*)(const char *, ...))0xc3e114d8)(__VA_ARGS__)) 5 6 #define GPA1CON (*(volatile unsigned long *)0x11400020) 7 #define I2CCON3 (*(volatile unsigned long *)0x13890000) 8 #define I2CADD3 (*(volatile unsigned long *)0x13890008) 9 #define I2CSTAT3 (*(volatile unsigned long *)0x13890004) 10 #define I2CDS3 (*(volatile unsigned long *)0x1389000C) 11 #define I2CLC3 (*(volatile unsigned long *)0x13890010) 12 13 #define GPD1CON (*(volatile unsigned long *)0x114000C0) 14 #define GPD1PUD (*(volatile unsigned long *)0x114000C8) 15 #define I2CCON1 (*(volatile unsigned long *)0x13870000) 16 #define I2CADD1 (*(volatile unsigned long *)0x13870008) 17 #define I2CSTAT1 (*(volatile unsigned long *)0x13870004) 18 #define I2CDS1 (*(volatile unsigned long *)0x1387000C) 19 #define I2CLC1 (*(volatile unsigned long *)0x13870010) 20 21 #define ICCICR_CPU0 (*(volatile unsigned long *)0x10480000) 22 #define ICCPMR_CPU0 (*(volatile unsigned long *)0x10480004) 23 #define ICDDCR (*(volatile unsigned long *)0x10490000) 24 #define ICDIPR15_CPU0 (*(volatile unsigned long *)0x1049043C) 25 #define ICDIPTR15_CPU0 (*(volatile unsigned long *)0x1049083C) 26 #define ICDISER1_CPU0 (*(volatile unsigned long *)0x10490104) 27 #define ICDSGIR (*(volatile unsigned long *)0x10490f00) 28 #define ICCIAR_CPU0 (*(volatile unsigned long *)0x1048000C) 29 #define ICCEOIR_CPU0 (*(volatile unsigned long *)0x10480010) 30 31 #define GPX1CON (*(volatile unsigned long *)0x11000C20) 32 33 #define EXT_INT41_CON (*(volatile unsigned long *)0x11000E04) 34 #define EXT_INT41_MASK (*(volatile unsigned long *)0x11000F04) 35 #define EXT_INT41_PEND (*(volatile unsigned long *)0x11000F44) 36 37 #define GPX3CON (*(volatile unsigned long *)0x11000c60) 38 #define GPX3DAT (*(volatile unsigned long *)0x11000c64) 39 #define GPD0CON (*(volatile unsigned long *)0x110000a0) 40 #define GPD0DAT (*(volatile unsigned long *)0x110000a4) 41 42 #define ICCICR_CPU0 (*(volatile unsigned long *)0x10480000) 43 #define ICCPMR_CPU0 (*(volatile unsigned long *)0x10480004) 44 #define ICDDCR (*(volatile unsigned long *)0x10490000) 45 #define ICDIPR16_CPU0 (*(volatile unsigned long *)0x10490440) 46 #define ICDIPTR16_CPU0 (*(volatile unsigned long *)0x10490840) 47 #define ICDISER2_CPU0 (*(volatile unsigned long *)0x10490108) 48 #define ICDSGIR (*(volatile unsigned long *)0x10490f00) 49 #define ICCIAR_CPU0 (*(volatile unsigned long *)0x1048000C) 50 #define ICCEOIR_CPU0 (*(volatile unsigned long *)0x10480010) 51 52 #define EXT_INT43_CON (*(volatile unsigned long *)0x11000E0C) 53 #define EXT_INT43_MASK (*(volatile unsigned long *)0x11000F0C) 54 #define EXT_INT43_PEND (*(volatile unsigned long *)0x11000F4C) 55 56 #endif
1 #ifndef __MMA_H 2 #define __MMA_H 3 4 void mma_set(); 5 void mma_read(); 6 void i2c_gpio_set(); 7 int master_write(unsigned char slaveaddr, unsigned char subaddr, unsigned char *data); 8 int master_read(unsigned char slaveaddr, unsigned char subaddr, unsigned char *data); 9 void i2c_init(); 10 void i2c_exit(); 11 void irq_init(); 12 void do_irq(); 13 void enable_mmu(); 14 void init_table(unsigned long *addr); 15 void memcpy(unsigned char *dest, unsigned char *src, unsigned int len); 16 17 extern unsigned long vector_start; 18 19 #endif //MMA_H
1 #include "mma.h" 2 #include "regs.h" 3 4 void (*udelay)(int ) = 0xc3e04fec; 5 6 int main(void) 7 { 8 *(unsigned long *)0x47000000 = do_irq; 9 enable_mmu(); 10 i2c_gpio_set(); 11 mma_set(); 12 irq_init(); 13 memcpy(0x0, vector_start, 0x1000); 14 } 15 16 void do_irq() 17 { 18 if(EXT_INT43_PEND & (1 << 1)) {//judge interrupt occurs 19 EXT_INT43_PEND |= (1 << 1);//clear interrupt 20 mma_read(); 21 } 22 } 23 24 void mma_set() 25 { 26 /*$06: Interrupt Setup Register 27 0x98 <--> 芯片地址 0x6 <---> 寄存器地址 28 */ 29 master_write(0x98, 0x6, 0xe3); // e3 1110 0011 30 /* 31 ----------------------------------------------------------- 32 D7 D6 D5 D4 D3 D2 D1 D0 33 SHINTX SHINTY SHINTZ GINT ASINT PDINT PLINT FBINT 34 1 1 1 0 0 0 1 1 35 ----------------------------------------------------------- 36 FBINT 37 0: Front/Back position change does not cause an interrupt 38 1: Front/Back position change causes an interrupt 39 PLINT 40 0: Up/Down/Right/Left position change does not cause an 41 interrupt 42 1: Up/Down/Right/Left position change causes an interrupt 43 PDINT 44 0: Successful tap detection does not cause an interrupt 45 1: Successful tap detection causes an interrupt 46 ASINT 47 0: Exiting Auto-Sleep does not cause an interrupt 48 1: Exiting Auto-Sleep causes an interrupt 49 GINT 50 0: There is not an automatic interrupt after every 51 measurement 52 1: There is an automatic interrupt after every measurement, 53 SHINTX 54 0: Shake on the X-axis does not cause an interrupt or set the 55 Shake bit in the TILT register 56 1: Shake detected on the X-axis causes an interrupt, and sets 57 the Shake bit in the TILT register 58 SHINTY 59 0: Shake on the Y-axis does not cause an interrupt or set the 60 Shake bit in the TILT register 61 1: Shake detected on the Y-axis causes an interrupt, and sets 62 the Shake bit in the TILT register 63 SHINTZ 64 0: Shake on the Z-axis does not cause an interrupt or set the 65 Shake bit in the TILT register 66 1: Shake detected on the Z-axis causes an interrupt, and sets 67 the Shake bit in the TILT register. 68 69 */ 70 71 /*$07: Mode Register (Read/Write)*/ 72 master_write(0x98, 0x7, 0xd9); //d9 1101 1001 73 /* 74 ----------------------------------------------------------- 75 D7 D6 D5 D4 D3 D2 D1 D0 76 IAH IPP SCPS ASE AWE TON - MODE 77 1 1 0 1 1 0 0 1 78 ----------------------------------------------------------- 79 MODE 80 0: Standby mode or Test Mode depending on state of TON 81 1: Active mode 82 TON 83 0: Standby Mode or Active Mode depending on state of MODE 84 1: Test Mode 85 AWE 86 0: Auto-Wake is disabled 87 1: Auto-Wake is enabled. 88 ASE 89 0: Auto-Sleep is disabled 90 1: Auto-Sleep is enabled 91 SCPS 92 0: The prescaler is divide-by-1. The 8-bit internal Sleep 93 Counter input clock is the samples per second set by 94 AMSR[2:0] 95 1: Prescaler is divide-by-16. The 8-bit Sleep Counter input 96 clock is the samples per second set by AMSR[2:0] divided by 97 16, 98 IPP 99 0: Interrupt output INT is open-drain. 100 1: Interrupt output INT is push-pull 101 IAH 102 0: Interrupt output INT is active low 103 1: Interrupt output INT is active high 104 */ 105 } 106 107 void mma_read() 108 { 109 unsigned char val; 110 unsigned char i; 111 for(i = 0; i < 4; i++) 112 while(1) { 113 master_read(0x98, i, &val); //read 114 if((val & (1 << 6)) == 0) { 115 if(0 == i) { 116 printf("X is %d\n", 0x3f & val); //6-bit output value X 117 } 118 if(1 == i) { 119 printf("Y is %d\n", 0x3f & val); //6-bit output value Y 120 } 121 if(2 == i) { 122 printf("Z is %d\n", 0x3f & val); //6-bit output value Z 123 } 124 if(3 == i) { 125 /*BaFro[1:0] 126 *01: Front 127 *10: Back 128 */ 129 if((val & 3) == 1) 130 printf("Front\n"); 131 132 if((val & 3) == 2) 133 printf("Back\n"); 134 /*PoLa[2:0] 135 * 001: Left 136 * 010: Right 137 * 101: Down 138 * 110: Up 139 * */ 140 if(((val >> 2) & 0x7) == 1) 141 printf("Left\n"); 142 143 if(((val >> 2) & 0x7) == 2) 144 printf("Right\n"); 145 146 if(((val >> 2) & 0x7) == 5) 147 printf("Down\n"); 148 149 if(((val >> 2) & 0x7) == 6) 150 printf("Up\n"); 151 152 } 153 break; 154 } 155 } 156 } 157 158 int master_write(unsigned char slaveaddr, unsigned char subaddr, unsigned char *data) 159 { 160 i2c_init(); 161 162 unsigned char timeout = 50; 163 while(timeout) { 164 udelay(20); 165 if(!(I2CSTAT3 & (1 << 5))){ //judge I2C-bus busy signal (0 busy or no busy) 166 I2CCON3 |= (1 << 7); //I2C-bus acknowledge enable 167 I2CDS3 = slaveaddr; // slave addr 168 I2CSTAT3 = 0xf0; // Master transmit mode 169 udelay(20); 170 171 timeout = 50; 172 while(timeout) { 173 udelay(20); 174 if(I2CCON3 & (1 << 4)) {//Interrupt is pending 175 I2CDS3 = subaddr; //register addr 176 udelay(10); 177 I2CCON3 &= ~(1 << 4); //Clears pending condition and resumes the operation 178 udelay(20); 179 break; 180 } 181 timeout --; 182 } 183 if(0 == timeout) 184 return -1; 185 186 timeout = 50; 187 while(timeout) { 188 udelay(20); 189 190 if(I2CCON3 & (1 << 4)) { // interrupt is pending 191 I2CDS3 = data; //write data 192 udelay(10); 193 I2CCON3 &= ~(1 << 4); //Clear pending bit to resume.(每次都要写0才会发送) 194 udelay(10); 195 break; 196 } 197 timeout--; 198 if(0 == timeout) 199 return -1; 200 } 201 202 timeout = 50; 203 while(timeout) { 204 udelay(20); 205 if(I2CCON3 & (1 << 4)) {//interrupt is pending 206 I2CSTAT3 = 0xd0; //Master transmit mode stop 207 udelay(10); 208 I2CCON3 &= ~(1 << 4); 209 udelay(10); 210 break; 211 } 212 timeout--; 213 } 214 if(0 == timeout) 215 return -1; 216 217 while(1 == (I2CSTAT3 & (1 << 5))) {// Wait until the stop condition takes effect 218 ; 219 } 220 i2c_exit(); //exit i2c 221 return 0; 222 } 223 timeout--; 224 } 225 return -1; 226 } 227 228 int master_read(unsigned char slaveaddr, unsigned char subaddr, unsigned char *data) 229 { 230 i2c_init(); 231 unsigned char count = 0; 232 unsigned char buf[14]; 233 unsigned char timeout = 50; 234 235 //-------------write------------------ 236 while(timeout) 237 { 238 udelay(20); 239 if(!(I2CSTAT3 & (1 << 5)))//判断忙与不忙 240 { 241 I2CCON3 |= (1 << 7); //开启I2C总线ack信号 242 I2CDS3 = slaveaddr; //写入要读的芯片地址 243 I2CSTAT3 = 0xf0; //选择主端发送模式 244 udelay(20); 245 timeout = 50; 246 while(timeout) 247 { 248 udelay(20); 249 if( I2CCON3 & (1<<4)) //等待中断(ACK信号) 250 { 251 I2CDS3 = subaddr; //写入要读的寄存器地址 252 udelay(10); 253 I2CCON3 &= ~(1 << 4); //向这一位写0,发送,清楚等待条件,恢复操作 254 udelay(20); 255 break; 256 257 } 258 timeout--; 259 } 260 if(0 == timeout) 261 return -1; 262 timeout = 50; 263 while(timeout) 264 { 265 udelay(20); 266 if(I2CCON3 & (1 << 4)) 267 { 268 I2CSTAT3 = 0xd0; //set Read Not busy (If Read) 269 udelay(10); 270 I2CCON3 &= ~(1<<4); 271 udelay(10); 272 break; 273 } 274 timeout--; 275 } 276 if(0 == timeout) 277 return -1; 278 279 //------------read------------------ 280 timeout = 50; 281 while(timeout) 282 { 283 udelay(20); 284 if(!(I2CSTAT3 & (1 << 5))) 285 { 286 I2CCON3 |= (1 << 7); 287 I2CDS3 = slaveaddr;//写入芯片地址 288 I2CSTAT3 = 0xb0; //选择主端接收模式 289 udelay(10); 290 break; 291 } 292 timeout--; 293 } 294 if(0 == timeout) 295 return -1; 296 297 timeout = 100; 298 while(timeout) 299 { 300 udelay(20); 301 if(I2CCON3 & (1 << 4)) 302 { 303 buf[count] = I2CDS3; //read 304 count++; 305 if(12 == count) 306 I2CCON3 &= ~(1 << 7);//I2C-bus acknowledge Disable 307 308 I2CCON3 &= ~(1 << 4); //Clear pending bit to resume 309 udelay(10); 310 if(12 == count) 311 break; 312 } 313 timeout--; 314 } 315 if(0 == timeout) 316 return -1; 317 318 timeout = 50; 319 while(timeout) 320 { 321 udelay(20); 322 if(I2CCON3 & (1 << 4))//wait ack 323 { 324 I2CSTAT3 = 0x90;//主端接受模式结束 325 I2CCON3 &= ~(1 << 4); 326 udelay(10); 327 break; 328 } 329 timeout--; 330 } 331 if(0 == timeout) 332 return -1; 333 334 while(1 == (I2CSTAT3 & (1 << 5))) //Wait until the stop condition takes effect 335 336 { 337 printf("in while read \n");; 338 } 339 i2c_exit(); //exit i2c 340 *data = buf[subaddr + 1]; 341 342 return 0; 343 } 344 timeout--; 345 } 346 return -1; 347 } 348 349 /*SDA3 <---> GPA1_2 SCL3 <-->GPA1_3 */ 350 void i2c_gpio_set() 351 { 352 printf("i2c_gpio_set\n"); 353 GPA1CON &= ~(0xff << 8); 354 GPA1CON |= (0x33 << 8); 355 } 356 357 void i2c_init() 358 { 359 I2CCON3 = 1 | (1 << 5) | (1 << 6); 360 I2CSTAT3 = (1 << 4); 361 I2CLC3 = 0x7; 362 } 363 364 void i2c_exit() 365 { 366 I2CCON3 = 0; 367 I2CSTAT3 = 0; 368 I2CLC3 = 0; 369 370 } 371 372 void irq_init() 373 { 374 //step 1: enable cpu cpsr 375 __asm__ __volatile__( 376 "mrs r0, cpsr\n" 377 "bic r0, r0, #0x80\n" 378 "msr cpsr, r0\n" 379 ::: "r0" 380 ); 381 382 //step 2: GIC NO.62 383 ICCICR_CPU0 = 1;//global enable interrupt (total switch) 384 ICCPMR_CPU0 = 0xff;//This register provides an interrupt priority filter. 385 ICDDCR = 1;//This register enables forwarding of pending interrupts to the CPU interfaces 386 387 /*一共有1024个中断源,只有160个中断号*/ 388 //id = 64. 一个ICDIPR 控制4个中断,64 / 4 = 16 ...0, so ICDIPR=16 389 ICDIPR16_CPU0 = (1 << 0);//the zero is the highest priority 390 ICDIPTR16_CPU0 = (1 << 0);//0x1 ---> for cpu0 391 ICDISER2_CPU0 = (1 << 0);// enable interrupt 0 392 393 //step 4:set ext_int 394 EXT_INT43_CON &= ~(0xf << 4); 395 EXT_INT43_CON |= (0x4 << 4);//falling edge; low level 396 EXT_INT43_MASK = 0;//enable extint43_1 397 398 //step 3: set gpio 399 GPX3CON &= ~(0xf << 4); 400 GPX3CON |= (0xf << 4);//xeint25 <==> gpx3_1 ---->0xF = EXT_INT43[1] 401 402 printf("irq over\n"); 403 } 404 405 406 void enable_mmu() 407 { 408 /*构建表*/ 409 unsigned long addr = 0x50000000; 410 init_table(addr); 411 412 /*打开mmu*/ 413 unsigned long mmu = 0; 414 mmu = 1 | (1 << 1) | (1 << 3) | (1 << 8); 415 416 __asm__ __volatile__ ( 417 "mov r0, #3\n" 418 "MCR p15, 0, r0, c3, c0, 0\n"//设置为管理员 419 "MCR p15, 0, %0, c2, c0, 0\n"//设置表的地址 420 "MCR p15, 0, %1, c1, c0, 0\n"//开启mmu 421 : 422 : "r" (addr), "r" (mmu) 423 : 424 ); 425 426 } 427 428 void init_table(unsigned long *addr) 429 { 430 unsigned long va = 0; 431 unsigned long phys = 0; 432 433 //0x40000000-0x80000000 -> 0x40000000-0x80000000 434 for(va = 0x40000000; va < 0x80000000; va += 0x100000) { 435 phys = va; 436 addr[va >> 20] = phys | 2; 437 } 438 439 //0x10000000-0x14000000 -> 0x10000000-0x140000000 440 for(va = 0x10000000; va < 0x14000000; va += 0x100000) { 441 phys = va; 442 addr[va >> 20] = phys | 2; 443 } 444 //0x10000000-0x14000000 -> 0x10000000-0x140000000 445 for(va = 0x0; va < 0x10000000; va += 0x100000) { 446 phys = va + 0x70000000; 447 addr[va >> 20] = phys | 2; 448 } 449 450 } 451 452 void memcpy(unsigned char *dest, unsigned char *src, unsigned int len) 453 { 454 int i = 0; 455 for(i = 0; i < len; i++) { 456 dest[i] = src[i]; 457 } 458 } 459 460 __asm__( 461 462 /*异常向量表*/ 463 "vector: \n" 464 " b reset\n" 465 " b und\n" 466 " b swi\n" 467 " b pre_abt\n" 468 " b data_abt\n" 469 " .word 0x0\n" 470 " b irq\n" 471 " b fiq\n" 472 "reset:\n" 473 "und:\n" 474 " mov sp, #0x47000000\n" 475 " stmdb sp!, {r0-r12, lr}\n" 476 477 " ldr r3, =0x47000004\n" 478 " ldr r2, [r3]\n" 479 " blx r2\n" 480 481 " mov sp, #0x47000000\n" 482 " ldmdb sp, {r0-r12, pc}^ \n" 483 484 "swi:\n" 485 " mov sp, #0x47000000\n" 486 " stmdb sp!, {r0-r12, lr}^\n" 487 488 " mov sp, #0x47000000\n" 489 " ldmdb sp, {r0-r12, pc}^ \n" 490 491 "pre_abt:\n" 492 493 "data_abt:\n" 494 " mov sp, #0x47000000\n" 495 " sub lr, lr, #4\n" 496 " stmdb sp!, {r0-r12, lr}\n" 497 498 " ldr r3, =0x47000008\n" 499 " ldr r2, [r3]\n" 500 " blx r2\n" 501 502 " mov sp, #0x47000000\n" 503 " ldmdb sp, {r0-r12, pc}^ \n" 504 "irq:\n" 505 506 " mov sp, #0x47000000\n" 507 " sub lr, lr, #4\n" 508 " stmdb sp!, {r0-r12, lr}\n" 509 510 " ldr r3, =0x47000000\n" //跳转到c语言 511 " ldr r2, [r3]\n" 512 " blx r2\n" 513 514 " mov sp, #0x47000000\n" 515 " ldmdb sp, {r0-r12, pc}^ \n" 516 517 "fiq:\n" 518 519 ".global vector_start\n" 520 "vector_start: \n" 521 ".word vector \n " 522 523 );