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