]> git.karo-electronics.de Git - oswald.git/blob - metawatch/mw_acc.c
Power saving changes, add new fonts, bitmaps and screens
[oswald.git] / metawatch / mw_acc.c
1 #include <msp430.h>
2 #include <msp430xgeneric.h>
3 #include <stdint.h>
4 #include <stdio.h>
5
6 #include "mw_main.h"
7 #include "mw_uart.h"
8
9 #include "mw_acc.h"
10
11 #include "oswald_main.h"
12
13 #define ACCEL_STATE_DISABLED            0x00
14 #define ACCEL_STATE_ENABLED             0x01
15
16 static uint8_t AccelState;
17 static uint8_t AccelerometerBusy;
18 static uint8_t LengthCount;
19 static uint8_t Index;
20 static uint8_t *pAccelerometerData;
21
22 /*
23  * Accelerometer is a Kionix KXTF9-4100 connected to I2C
24  * I2C is pretty slow so reading and writing should be done non blocking
25  * using interrupts.
26  */
27
28 #define ACCELEROMETER_NO_INTERRUPTS     0x00
29 #define ACCELEROMETER_ALIFG             0x02
30 #define ACCELEROMETER_NACKIFG           0x04
31 #define ACCELEROMETER_STTIFG            0x06
32 #define ACCELEROMETER_STPIFG            0x08
33 #define ACCELEROMETER_RXIFG             0x0a
34 #define ACCELEROMETER_TXIFG             0x0c
35
36 #pragma vector=USCI_B1_VECTOR
37 __interrupt void ACCERLEROMETER_I2C_ISR(void)
38 {
39         // debug_uart_tx("ACC i2c irq\n");
40         switch (USCI_ACCELEROMETER_IV) {
41                 case ACCELEROMETER_NO_INTERRUPTS: 
42                         break;
43                 case ACCELEROMETER_ALIFG: 
44                         break;
45                 case ACCELEROMETER_NACKIFG:
46                         nop();
47                         break; 
48                 case ACCELEROMETER_STTIFG:
49                         nop();
50                         break; 
51                 case ACCELEROMETER_STPIFG: 
52                         nop();
53                         break;
54                 case ACCELEROMETER_RXIFG:
55                         if (LengthCount > 0) {
56                                 pAccelerometerData[Index++] = ACCELEROMETER_RXBUF;
57                                 LengthCount--;
58                                 if ( LengthCount == 1 ) {
59                                         /* All but one byte received. Send stop */
60                                         ACCELEROMETER_CTL1 |= UCTXSTP;
61                                 } else if ( LengthCount == 0 ) {
62                                         /* Last byte received; disable rx interrupt */
63                                         ACCELEROMETER_IE &= ~UCRXIE;
64                                         AccelerometerBusy = 0;
65                                 }
66                         }
67                         break;
68                 case ACCELEROMETER_TXIFG:
69                         if ( LengthCount > 0 ) {
70                                 ACCELEROMETER_TXBUF = pAccelerometerData[Index++];
71                                 LengthCount--;
72                         } else {
73                                 /* disable transmit interrupt and send stop */
74                                 ACCELEROMETER_IE &= ~UCTXIE;
75                                 ACCELEROMETER_CTL1 |= UCTXSTP;
76                                 AccelerometerBusy = 0;
77                         }
78                         break;
79                 default:
80                         break;
81         }
82
83 }
84
85 void mw_acc_init_i2c(void)
86 {
87         /* enable reset before configuration */
88         ACCELEROMETER_CTL1 |= UCSWRST;
89
90         /* configure as master using smclk / 40 = 399.5 kHz */
91         ACCELEROMETER_CTL0 = UCMST + UCMODE_3 + UCSYNC;
92         ACCELEROMETER_CTL1 = UCSSEL__SMCLK + UCSWRST;
93         ACCELEROMETER_BR0 = 42;
94
95         ACCELEROMETER_BR1 = 0;
96         ACCELEROMETER_I2CSA = KIONIX_DEVICE_ADDRESS;
97
98         /* release reset */
99         ACCELEROMETER_CTL1 &= ~UCSWRST;
100 }
101
102 void mw_acc_disable_i2c(void)
103 {
104         /* enable reset to hold it */
105         ACCELEROMETER_CTL1 |= UCSWRST;
106 }
107
108 void mw_acc_i2c_write(uint8_t RegisterAddress, uint8_t *pData, uint8_t Length)
109 {
110         int tmo;
111
112         if (Length == 0 || pData == 0)
113                 return;  
114   
115         while (UCB1STAT & UCBBUSY)
116                 nop();
117   
118         AccelerometerBusy = 1;
119         LengthCount = Length;
120         Index = 0;
121         pAccelerometerData = pData;
122   
123         /* 
124          * enable transmit interrupt and 
125          * setup for write and send the start condition
126          */
127         ACCELEROMETER_IFG = 0;
128         ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
129         while(!(ACCELEROMETER_IFG & UCTXIFG))
130                 nop();
131   
132         /* 
133          * clear transmit interrupt flag, enable interrupt, 
134          * send the register address
135          */
136         ACCELEROMETER_IFG = 0;
137         ACCELEROMETER_IE |= UCTXIE;
138         ACCELEROMETER_TXBUF = RegisterAddress;
139
140         tmo = 0;
141         while (AccelerometerBusy) {
142                 while (tmo++ < 1000)
143                         __delay_cycles(16000);
144                 if (tmo >= 1000) {
145                         debug_uart_tx("ACC I2C tx tmo\n");
146                         return;
147                 }
148         }
149
150         while (ACCELEROMETER_CTL1 & UCTXSTP)
151                 nop();
152
153         /* the rest of TX will be handled by the ISR */
154 }
155
156 void mw_acc_i2c_read_single(const uint8_t RegisterAddress, const uint8_t *pData)
157 {
158         if ( pData == 0 )
159                 return;
160   
161         /* wait for bus to be free */
162         while (UCB1STAT & UCBBUSY)
163                 nop();
164   
165         AccelerometerBusy = 1;
166         LengthCount = 1;
167         Index = 0;
168         pAccelerometerData = (uint8_t *)pData;
169
170         /* transmit address */
171         ACCELEROMETER_IFG = 0;
172         ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
173         while (!(ACCELEROMETER_IFG & UCTXIFG))
174                 nop();
175   
176         /* write register address */
177         ACCELEROMETER_IFG = 0;
178         ACCELEROMETER_TXBUF = RegisterAddress;
179         while (!(ACCELEROMETER_IFG & UCTXIFG))
180                 nop();
181
182         /* send a repeated start (same slave address now it is a read command) 
183          * read possible extra character from rxbuffer
184          */
185         ACCELEROMETER_RXBUF;
186         ACCELEROMETER_IFG = 0;
187         ACCELEROMETER_IE |= UCRXIE;
188         ACCELEROMETER_CTL1 &= ~UCTR;
189         /* for a read of a single byte the stop must be sent while the byte is being 
190          * received. If this is interrupted an extra byte may be read.
191          * however, it will be discarded during the next read
192          */
193         if (LengthCount == 1) {
194                 /* errata usci30: prevent interruption of sending stop 
195                  * so that only one byte is read
196                  * this requires 62 us @ 320 kHz, 51 @ 400 kHz
197                  */
198                 __disable_interrupt();
199
200                 ACCELEROMETER_CTL1 |= UCTXSTT;
201   
202                 while(ACCELEROMETER_CTL1 & UCTXSTT)
203                         nop();
204
205                 ACCELEROMETER_CTL1 |= UCTXSTP;
206
207                 __enable_interrupt();
208         } else {
209                 ACCELEROMETER_CTL1 |= UCTXSTT;
210         }
211   
212         /* wait until all data has been received and the stop bit has been sent */
213         while (AccelerometerBusy)
214                 nop();
215         while (ACCELEROMETER_CTL1 & UCTXSTP)
216                 nop();
217         Index = 0;
218         pAccelerometerData = 0;
219 }
220
221 /* errata usci30: only perform single reads 
222  * second solution: use DMA
223  */
224 void mw_acc_i2c_read(const uint8_t RegisterAddress, uint8_t *pData, const uint8_t Length)
225 {
226         int i;
227
228         for ( i = 0; i < Length; i++ ) {
229                 mw_acc_i2c_read_single(RegisterAddress + i, (pData + i));
230         }
231 }
232
233
234 void mw_acc_init(void)
235 {
236         uint8_t WriteRegisterData;
237         uint8_t pReadRegisterData[4];
238 #if defined MW_DEVBOARD_V2
239         char tstr[16];
240 #endif
241
242         // it takes at least 20ms to power up
243         ENABLE_ACCELEROMETER_POWER();
244         __delay_cycles(320000);
245
246         mw_acc_init_i2c();
247
248         /*
249          * make sure part is in standby mode because some registers can only
250          * be changed when the part is not active.
251          */
252         WriteRegisterData = PC1_STANDBY_MODE;
253         mw_acc_i2c_write(KIONIX_CTRL_REG1, &WriteRegisterData, 1);
254
255         /* enable face-up and face-down detection */
256         WriteRegisterData = TILT_FDM | TILT_FUM;
257         mw_acc_i2c_write(KIONIX_CTRL_REG2, &WriteRegisterData, 1);
258     
259         /* 
260          * the interrupt from the accelerometer can be used to get periodic data
261          * the real time clock can also be used
262          */
263   
264         /* change to output data rate to 25 Hz */
265         WriteRegisterData = WUF_ODR_25HZ | TAP_ODR_400HZ;
266         mw_acc_i2c_write(KIONIX_CTRL_REG3, &WriteRegisterData, 1);
267   
268         /* enable interrupt and make it active high */
269         WriteRegisterData = IEN | IEA;
270         mw_acc_i2c_write(KIONIX_INT_CTRL_REG1, &WriteRegisterData, 1);
271   
272         /* enable motion detection interrupt for all three axis */
273         WriteRegisterData = XBW | YBW | ZBW;
274         mw_acc_i2c_write(KIONIX_INT_CTRL_REG2, &WriteRegisterData, 1);
275
276         /* enable tap interrupt for Z-axis */
277         WriteRegisterData = TFDM;
278         mw_acc_i2c_write(KIONIX_INT_CTRL_REG3, &WriteRegisterData, 1);
279   
280         /* set TDT_TIMER to 0.2 secs*/
281         WriteRegisterData = 0x50;
282         mw_acc_i2c_write(KIONIX_TDT_TIMER, &WriteRegisterData, 1);
283   
284         /* set tap low and high thresholds (default: 26 and 182) */
285         WriteRegisterData = 40; //78;
286         mw_acc_i2c_write(KIONIX_TDT_L_THRESH, &WriteRegisterData, 1);
287         WriteRegisterData = 128;
288         mw_acc_i2c_write(KIONIX_TDT_H_THRESH, &WriteRegisterData, 1);
289     
290         /* set WUF_TIMER counter */
291         WriteRegisterData = 10;
292         mw_acc_i2c_write(KIONIX_WUF_TIMER, &WriteRegisterData, 1);
293     
294         /* this causes data to always be sent */
295         // WriteRegisterData = 0x00;
296         WriteRegisterData = 0x01 /*0x08*/;
297         mw_acc_i2c_write(KIONIX_WUF_THRESH, &WriteRegisterData, 1);
298      
299         /* single byte read test */
300         mw_acc_i2c_read(KIONIX_DCST_RESP, pReadRegisterData, 1);
301 #if defined MW_DEVBOARD_V2
302         snprintf(tstr, 16, "acc DCST 0x%02x\n", pReadRegisterData[0]);
303         debug_uart_tx(tstr);
304 #endif
305   
306         /* multiple byte read test */
307         mw_acc_i2c_read(KIONIX_WHO_AM_I, pReadRegisterData, 2);
308 #if defined MW_DEVBOARD_V2
309         snprintf(tstr, 16, "acc is 0x%02x 0x%02x\n", pReadRegisterData[0], pReadRegisterData[1]);
310         debug_uart_tx(tstr);
311 #endif
312
313         /* 
314          * KIONIX_CTRL_REG3 and DATA_CTRL_REG can remain at their default values 
315          *
316          * 50 Hz
317          */
318 #if 0  
319         /* KTXF9 300 uA; KTXI9 165 uA */
320         WriteRegisterData = PC1_OPERATING_MODE | TAP_ENABLE_TDTE;
321   
322         /* 180 uA; KTXI9 115 uA */
323         WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_8BIT | WUF_ENABLE;
324
325         /* 180 uA; KTXI9 8.7 uA */
326         WriteRegisterData = PC1_OPERATING_MODE | TILT_ENABLE_TPE;
327
328         /* 720 uA; KTXI9 330 uA */  
329         WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_12BIT | WUF_ENABLE;
330 #endif
331   
332         /* setup the default for the AccelerometerEnable command */
333 #if 0
334         OperatingModeRegister = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
335         InterruptControl = INTERRUPT_CONTROL_DISABLE_INTERRUPT;
336         SidControl = SID_CONTROL_SEND_DATA;
337         SidAddr = KIONIX_XOUT_L;
338         SidLength = XYZ_DATA_LENGTH;  
339
340         AccelState = ACCEL_STATE_INIT;
341 #endif
342 }
343
344 void mw_acc_enable(void)
345 {
346         uint8_t sdata;
347
348         mw_acc_init();
349
350         sdata = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE | WUF_ENABLE;
351         //sdata = PC1_OPERATING_MODE | RESOLUTION_8BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
352         mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
353   
354         ACCELEROMETER_INT_ENABLE();
355         mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
356         AccelState = ACCEL_STATE_ENABLED;
357 }
358
359 void mw_acc_disable(void)
360 {
361         uint8_t sdata;
362
363         if (AccelState == ACCEL_STATE_ENABLED) {
364                 sdata = PC1_STANDBY_MODE;
365                 mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
366
367                 ACCELEROMETER_INT_DISABLE();
368                 mw_acc_disable_i2c();
369                 /// DISABLE_ACCELEROMETER_POWER();
370                 AccelState = ACCEL_STATE_DISABLED;
371         }
372 }
373
374 void mw_acc_read(int16_t *x, int16_t *y, int16_t *z)
375 {
376         uint8_t rdata[6];
377
378         if (AccelState == ACCEL_STATE_ENABLED) {
379                 mw_acc_i2c_read(KIONIX_XOUT_L, rdata, 6);
380
381                 *x = rdata[0] | (rdata[1] << 8);
382                 *y = rdata[2] | (rdata[3] << 8);
383                 *z = rdata[4] | (rdata[5] << 8);
384         } else {
385                 *x = 0;
386                 *y = 0;
387                 *z = 0;
388         }
389 }
390
391 void mw_acc_handle_irq(void)
392 {
393         uint8_t sdata, srcreg1, srcreg2;
394 #if defined MW_DEVBOARD_V2
395         char tstr[16];
396 #endif
397
398         if (AccelState != ACCEL_STATE_ENABLED)
399                 return;
400
401         mw_acc_i2c_read(KIONIX_INT_SRC_REG1, &srcreg1, 1);
402 #if defined MW_DEVBOARD_V2
403         snprintf(tstr, 16, "accsrc1: 0x%02x\n", srcreg1);
404         debug_uart_tx(tstr);
405 #endif
406         mw_acc_i2c_read(KIONIX_INT_SRC_REG2, &srcreg2, 1);
407 #if defined MW_DEVBOARD_V2
408         snprintf(tstr, 16, "accsrc2: 0x%02x\n", srcreg2);
409         debug_uart_tx(tstr);
410 #endif
411         if (srcreg1 & INT_TAP_SINGLE) {
412         };
413         if (srcreg1 & INT_TAP_DOUBLE) {
414         };
415         if (srcreg2 & INT_WUFS) {
416                 int16_t x, y, z;
417                 mw_acc_read(&x, &y, &z);
418                 oswald_handle_accel_event((int8_t)(x / (32768 / 255)), (int8_t)(y / (32768 / 255)), (int8_t)(z / (32768 / 255)));
419         }
420
421         mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
422 }
423