2 #include <msp430xgeneric.h>
11 #include "oswald_main.h"
13 #define ACCEL_STATE_DISABLED 0x00
14 #define ACCEL_STATE_ENABLED 0x01
16 static uint8_t AccelState;
17 static uint8_t AccelerometerBusy;
18 static uint8_t LengthCount;
20 static uint8_t *pAccelerometerData;
23 * Accelerometer is a Kionix KXTF9-4100 connected to I2C
24 * I2C is pretty slow so reading and writing should be done non blocking
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
36 #pragma vector=USCI_B1_VECTOR
37 __interrupt void ACCERLEROMETER_I2C_ISR(void)
39 // debug_uart_tx("ACC i2c irq\n");
40 switch (USCI_ACCELEROMETER_IV) {
41 case ACCELEROMETER_NO_INTERRUPTS:
43 case ACCELEROMETER_ALIFG:
45 case ACCELEROMETER_NACKIFG:
48 case ACCELEROMETER_STTIFG:
51 case ACCELEROMETER_STPIFG:
54 case ACCELEROMETER_RXIFG:
55 if (LengthCount > 0) {
56 pAccelerometerData[Index++] = ACCELEROMETER_RXBUF;
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;
68 case ACCELEROMETER_TXIFG:
69 if ( LengthCount > 0 ) {
70 ACCELEROMETER_TXBUF = pAccelerometerData[Index++];
73 /* disable transmit interrupt and send stop */
74 ACCELEROMETER_IE &= ~UCTXIE;
75 ACCELEROMETER_CTL1 |= UCTXSTP;
76 AccelerometerBusy = 0;
85 void mw_acc_init_i2c(void)
87 /* enable reset before configuration */
88 ACCELEROMETER_CTL1 |= UCSWRST;
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;
95 ACCELEROMETER_BR1 = 0;
96 ACCELEROMETER_I2CSA = KIONIX_DEVICE_ADDRESS;
99 ACCELEROMETER_CTL1 &= ~UCSWRST;
102 void mw_acc_disable_i2c(void)
104 /* enable reset to hold it */
105 ACCELEROMETER_CTL1 |= UCSWRST;
108 void mw_acc_i2c_write(uint8_t RegisterAddress, uint8_t *pData, uint8_t Length)
112 if (Length == 0 || pData == 0)
115 while (UCB1STAT & UCBBUSY)
118 AccelerometerBusy = 1;
119 LengthCount = Length;
121 pAccelerometerData = pData;
124 * enable transmit interrupt and
125 * setup for write and send the start condition
127 ACCELEROMETER_IFG = 0;
128 ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
129 while(!(ACCELEROMETER_IFG & UCTXIFG))
133 * clear transmit interrupt flag, enable interrupt,
134 * send the register address
136 ACCELEROMETER_IFG = 0;
137 ACCELEROMETER_IE |= UCTXIE;
138 ACCELEROMETER_TXBUF = RegisterAddress;
141 while (AccelerometerBusy) {
143 __delay_cycles(16000);
145 debug_uart_tx("ACC I2C tx tmo\n");
150 while (ACCELEROMETER_CTL1 & UCTXSTP)
153 /* the rest of TX will be handled by the ISR */
156 void mw_acc_i2c_read_single(const uint8_t RegisterAddress, const uint8_t *pData)
161 /* wait for bus to be free */
162 while (UCB1STAT & UCBBUSY)
165 AccelerometerBusy = 1;
168 pAccelerometerData = (uint8_t *)pData;
170 /* transmit address */
171 ACCELEROMETER_IFG = 0;
172 ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
173 while (!(ACCELEROMETER_IFG & UCTXIFG))
176 /* write register address */
177 ACCELEROMETER_IFG = 0;
178 ACCELEROMETER_TXBUF = RegisterAddress;
179 while (!(ACCELEROMETER_IFG & UCTXIFG))
182 /* send a repeated start (same slave address now it is a read command)
183 * read possible extra character from rxbuffer
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
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
198 __disable_interrupt();
200 ACCELEROMETER_CTL1 |= UCTXSTT;
202 while(ACCELEROMETER_CTL1 & UCTXSTT)
205 ACCELEROMETER_CTL1 |= UCTXSTP;
207 __enable_interrupt();
209 ACCELEROMETER_CTL1 |= UCTXSTT;
212 /* wait until all data has been received and the stop bit has been sent */
213 while (AccelerometerBusy)
215 while (ACCELEROMETER_CTL1 & UCTXSTP)
218 pAccelerometerData = 0;
221 /* errata usci30: only perform single reads
222 * second solution: use DMA
224 void mw_acc_i2c_read(const uint8_t RegisterAddress, uint8_t *pData, const uint8_t Length)
228 for ( i = 0; i < Length; i++ ) {
229 mw_acc_i2c_read_single(RegisterAddress + i, (pData + i));
234 void mw_acc_init(void)
236 uint8_t WriteRegisterData;
237 uint8_t pReadRegisterData[4];
238 #if defined MW_DEVBOARD_V2
242 // it takes at least 20ms to power up
243 ENABLE_ACCELEROMETER_POWER();
244 __delay_cycles(320000);
249 * make sure part is in standby mode because some registers can only
250 * be changed when the part is not active.
252 WriteRegisterData = PC1_STANDBY_MODE;
253 mw_acc_i2c_write(KIONIX_CTRL_REG1, &WriteRegisterData, 1);
255 /* enable face-up and face-down detection */
256 WriteRegisterData = TILT_FDM | TILT_FUM;
257 mw_acc_i2c_write(KIONIX_CTRL_REG2, &WriteRegisterData, 1);
260 * the interrupt from the accelerometer can be used to get periodic data
261 * the real time clock can also be used
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);
268 /* enable interrupt and make it active high */
269 WriteRegisterData = IEN | IEA;
270 mw_acc_i2c_write(KIONIX_INT_CTRL_REG1, &WriteRegisterData, 1);
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);
276 /* enable tap interrupt for Z-axis */
277 WriteRegisterData = TFDM;
278 mw_acc_i2c_write(KIONIX_INT_CTRL_REG3, &WriteRegisterData, 1);
280 /* set TDT_TIMER to 0.2 secs*/
281 WriteRegisterData = 0x50;
282 mw_acc_i2c_write(KIONIX_TDT_TIMER, &WriteRegisterData, 1);
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);
290 /* set WUF_TIMER counter */
291 WriteRegisterData = 10;
292 mw_acc_i2c_write(KIONIX_WUF_TIMER, &WriteRegisterData, 1);
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);
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]);
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]);
314 * KIONIX_CTRL_REG3 and DATA_CTRL_REG can remain at their default values
319 /* KTXF9 300 uA; KTXI9 165 uA */
320 WriteRegisterData = PC1_OPERATING_MODE | TAP_ENABLE_TDTE;
322 /* 180 uA; KTXI9 115 uA */
323 WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_8BIT | WUF_ENABLE;
325 /* 180 uA; KTXI9 8.7 uA */
326 WriteRegisterData = PC1_OPERATING_MODE | TILT_ENABLE_TPE;
328 /* 720 uA; KTXI9 330 uA */
329 WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_12BIT | WUF_ENABLE;
332 /* setup the default for the AccelerometerEnable command */
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;
340 AccelState = ACCEL_STATE_INIT;
344 void mw_acc_enable(void)
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);
354 ACCELEROMETER_INT_ENABLE();
355 mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
356 AccelState = ACCEL_STATE_ENABLED;
359 void mw_acc_disable(void)
363 if (AccelState == ACCEL_STATE_ENABLED) {
364 sdata = PC1_STANDBY_MODE;
365 mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
367 ACCELEROMETER_INT_DISABLE();
368 mw_acc_disable_i2c();
369 /// DISABLE_ACCELEROMETER_POWER();
370 AccelState = ACCEL_STATE_DISABLED;
374 void mw_acc_read(int16_t *x, int16_t *y, int16_t *z)
378 if (AccelState == ACCEL_STATE_ENABLED) {
379 mw_acc_i2c_read(KIONIX_XOUT_L, rdata, 6);
381 *x = rdata[0] | (rdata[1] << 8);
382 *y = rdata[2] | (rdata[3] << 8);
383 *z = rdata[4] | (rdata[5] << 8);
391 void mw_acc_handle_irq(void)
393 uint8_t sdata, srcreg1, srcreg2;
394 #if defined MW_DEVBOARD_V2
398 if (AccelState != ACCEL_STATE_ENABLED)
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);
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);
411 if (srcreg1 & INT_TAP_SINGLE) {
413 if (srcreg1 & INT_TAP_DOUBLE) {
415 if (srcreg2 & INT_WUFS) {
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)));
421 mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);