#include <msp430.h>
#include <msp430xgeneric.h>
#include <stdint.h>
+#include <stdio.h>
#include "mw_main.h"
+#include "mw_uart.h"
#include "mw_acc.h"
-void mw_init_acc_i2c(void)
+#include "oswald_main.h"
+
+#define ACCEL_STATE_DISABLED 0x00
+#define ACCEL_STATE_ENABLED 0x01
+
+static uint8_t AccelState;
+static uint8_t AccelerometerBusy;
+static uint8_t LengthCount;
+static uint8_t Index;
+static uint8_t *pAccelerometerData;
+
+/*
+ * Accelerometer is a Kionix KXTF9-4100 connected to I2C
+ * I2C is pretty slow so reading and writing should be done non blocking
+ * using interrupts.
+ */
+
+#define ACCELEROMETER_NO_INTERRUPTS 0x00
+#define ACCELEROMETER_ALIFG 0x02
+#define ACCELEROMETER_NACKIFG 0x04
+#define ACCELEROMETER_STTIFG 0x06
+#define ACCELEROMETER_STPIFG 0x08
+#define ACCELEROMETER_RXIFG 0x0a
+#define ACCELEROMETER_TXIFG 0x0c
+
+#pragma vector=USCI_B1_VECTOR
+__interrupt void ACCERLEROMETER_I2C_ISR(void)
+{
+ // debug_uart_tx("ACC i2c irq\n");
+ switch (USCI_ACCELEROMETER_IV) {
+ case ACCELEROMETER_NO_INTERRUPTS:
+ break;
+ case ACCELEROMETER_ALIFG:
+ break;
+ case ACCELEROMETER_NACKIFG:
+ nop();
+ break;
+ case ACCELEROMETER_STTIFG:
+ nop();
+ break;
+ case ACCELEROMETER_STPIFG:
+ break;
+ case ACCELEROMETER_RXIFG:
+ if (LengthCount > 0) {
+ pAccelerometerData[Index++] = ACCELEROMETER_RXBUF;
+ LengthCount--;
+ if ( LengthCount == 1 ) {
+ /* All but one byte received. Send stop */
+ ACCELEROMETER_CTL1 |= UCTXSTP;
+ } else if ( LengthCount == 0 ) {
+ /* Last byte received; disable rx interrupt */
+ ACCELEROMETER_IE &= ~UCRXIE;
+ AccelerometerBusy = 0;
+ }
+ }
+ break;
+ case ACCELEROMETER_TXIFG:
+ if ( LengthCount > 0 ) {
+ ACCELEROMETER_TXBUF = pAccelerometerData[Index++];
+ LengthCount--;
+ } else {
+ /* disable transmit interrupt and send stop */
+ ACCELEROMETER_IE &= ~UCTXIE;
+ ACCELEROMETER_CTL1 |= UCTXSTP;
+ AccelerometerBusy = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+void mw_acc_init_i2c(void)
{
/* enable reset before configuration */
ACCELEROMETER_CTL1 |= UCSWRST;
ACCELEROMETER_CTL1 &= ~UCSWRST;
}
-/*
- * DMA2 = SPI for LCD
- */
-static void mw_acc_i2c_write_byte(uint8_t byte)
+void mw_acc_disable_i2c(void)
{
- ACCELEROMETER_TXBUF = byte;
- while ((ACCELEROMETER_CTL1 & ACCELEROMETER_IFG) == 0)
- nop();
+ /* enable reset to hold it */
+ ACCELEROMETER_CTL1 |= UCSWRST;
}
-/* OK this is polling write, but data is small and 400kHz I2C, it should "just work" :) */
-void mw_acc_i2c_write(const uint8_t addr, const void *data, const uint8_t len)
+void mw_acc_i2c_write(uint8_t RegisterAddress, uint8_t *pData, uint8_t Length)
{
- int i;
-
- if (len == 0) {
+ if (Length == 0 || pData == 0)
return;
- }
while (UCB1STAT & UCBBUSY)
nop();
+ AccelerometerBusy = 1;
+ LengthCount = Length;
+ Index = 0;
+ pAccelerometerData = pData;
+
/*
+ * enable transmit interrupt and
* setup for write and send the start condition
*/
ACCELEROMETER_IFG = 0;
ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
- while (!(ACCELEROMETER_IFG & UCTXIFG))
+ while(!(ACCELEROMETER_IFG & UCTXIFG))
nop();
/*
- * clear transmit interrupt flag,
+ * clear transmit interrupt flag, enable interrupt,
* send the register address
*/
ACCELEROMETER_IFG = 0;
+ ACCELEROMETER_IE |= UCTXIE;
+ ACCELEROMETER_TXBUF = RegisterAddress;
- mw_acc_i2c_write_byte(addr);
+ while (AccelerometerBusy)
+ nop();
+
+ while (ACCELEROMETER_CTL1 & UCTXSTP)
+ nop();
- for (i=0; i<len; i++)
- mw_acc_i2c_write_byte(*(uint8_t *)(data+i));
+ /* the rest of TX will be handled by the ISR */
+}
+void mw_acc_i2c_read_single(const uint8_t RegisterAddress, const uint8_t *pData)
+{
+ if ( pData == 0 )
+ return;
+
+ /* wait for bus to be free */
+ while (UCB1STAT & UCBBUSY)
+ nop();
+
+ AccelerometerBusy = 1;
+ LengthCount = 1;
+ Index = 0;
+ pAccelerometerData = (uint8_t *)pData;
+
+ /* transmit address */
+ ACCELEROMETER_IFG = 0;
+ ACCELEROMETER_CTL1 |= UCTR + UCTXSTT;
+ while (!(ACCELEROMETER_IFG & UCTXIFG))
+ nop();
+
+ /* write register address */
+ ACCELEROMETER_IFG = 0;
+ ACCELEROMETER_TXBUF = RegisterAddress;
+ while (!(ACCELEROMETER_IFG & UCTXIFG))
+ nop();
+
+ /* send a repeated start (same slave address now it is a read command)
+ * read possible extra character from rxbuffer
+ */
+ ACCELEROMETER_RXBUF;
+ ACCELEROMETER_IFG = 0;
+ ACCELEROMETER_IE |= UCRXIE;
+ ACCELEROMETER_CTL1 &= ~UCTR;
+ /* for a read of a single byte the stop must be sent while the byte is being
+ * received. If this is interrupted an extra byte may be read.
+ * however, it will be discarded during the next read
+ */
+ if ( LengthCount == 1 ) {
+ /* errata usci30: prevent interruption of sending stop
+ * so that only one byte is read
+ * this requires 62 us @ 320 kHz, 51 @ 400 kHz
+ */
+ ACCELEROMETER_CTL1 |= UCTXSTT;
+
+ while(ACCELEROMETER_CTL1 & UCTXSTT)
+ nop();
+
+ ACCELEROMETER_CTL1 |= UCTXSTP;
+ } else {
+ ACCELEROMETER_CTL1 |= UCTXSTT;
+ }
+
+ /* wait until all data has been received and the stop bit has been sent */
+ while (AccelerometerBusy)
+ nop();
while (ACCELEROMETER_CTL1 & UCTXSTP)
nop();
+ Index = 0;
+ pAccelerometerData = 0;
}
+/* errata usci30: only perform single reads
+ * second solution: use DMA
+ */
+void mw_acc_i2c_read(const uint8_t RegisterAddress, uint8_t *pData, const uint8_t Length)
+{
+ int i;
+
+ for ( i = 0; i < Length; i++ ) {
+ mw_acc_i2c_read_single(RegisterAddress + i, (pData + i));
+ }
+}
+
+
+void mw_acc_init(void)
+{
+ uint8_t WriteRegisterData;
+ uint8_t pReadRegisterData[4];
+#if defined MW_DEVBOARD_V2
+ char tstr[16];
+#endif
+
+ ENABLE_ACCELEROMETER_POWER();
+
+ mw_acc_init_i2c();
+
+ /*
+ * make sure part is in standby mode because some registers can only
+ * be changed when the part is not active.
+ */
+ WriteRegisterData = PC1_STANDBY_MODE;
+ mw_acc_i2c_write(KIONIX_CTRL_REG1, &WriteRegisterData, 1);
+
+ /* enable face-up and face-down detection */
+ WriteRegisterData = TILT_FDM | TILT_FUM;
+ mw_acc_i2c_write(KIONIX_CTRL_REG2, &WriteRegisterData, 1);
+
+ /*
+ * the interrupt from the accelerometer can be used to get periodic data
+ * the real time clock can also be used
+ */
+
+ /* change to output data rate to 25 Hz */
+ WriteRegisterData = WUF_ODR_25HZ | TAP_ODR_400HZ;
+ mw_acc_i2c_write(KIONIX_CTRL_REG3, &WriteRegisterData, 1);
+
+ /* enable interrupt and make it active high */
+ WriteRegisterData = IEN | IEA;
+ mw_acc_i2c_write(KIONIX_INT_CTRL_REG1, &WriteRegisterData, 1);
+
+ /* enable motion detection interrupt for all three axis */
+ WriteRegisterData = XBW | YBW | ZBW;
+ mw_acc_i2c_write(KIONIX_INT_CTRL_REG2, &WriteRegisterData, 1);
+
+ /* enable tap interrupt for Z-axis */
+ WriteRegisterData = TFDM;
+ mw_acc_i2c_write(KIONIX_INT_CTRL_REG3, &WriteRegisterData, 1);
+
+ /* set TDT_TIMER to 0.2 secs*/
+ WriteRegisterData = 0x50;
+ mw_acc_i2c_write(KIONIX_TDT_TIMER, &WriteRegisterData, 1);
+
+ /* set tap low and high thresholds (default: 26 and 182) */
+ WriteRegisterData = 40; //78;
+ mw_acc_i2c_write(KIONIX_TDT_L_THRESH, &WriteRegisterData, 1);
+ WriteRegisterData = 128;
+ mw_acc_i2c_write(KIONIX_TDT_H_THRESH, &WriteRegisterData, 1);
+
+ /* set WUF_TIMER counter */
+ WriteRegisterData = 10;
+ mw_acc_i2c_write(KIONIX_WUF_TIMER, &WriteRegisterData, 1);
+
+ /* this causes data to always be sent */
+ // WriteRegisterData = 0x00;
+ WriteRegisterData = 0x02 /*0x08*/;
+ mw_acc_i2c_write(KIONIX_WUF_THRESH, &WriteRegisterData, 1);
+
+ /* single byte read test */
+ mw_acc_i2c_read(KIONIX_DCST_RESP, pReadRegisterData, 1);
+#if defined MW_DEVBOARD_V2
+ snprintf(tstr, 16, "acc DCST 0x%02x\n", pReadRegisterData[0]);
+ debug_uart_tx(tstr);
+#endif
+
+ /* multiple byte read test */
+ mw_acc_i2c_read(KIONIX_WHO_AM_I, pReadRegisterData, 2);
+#if defined MW_DEVBOARD_V2
+ snprintf(tstr, 16, "acc is 0x%02x 0x%02x\n", pReadRegisterData[0], pReadRegisterData[1]);
+ debug_uart_tx(tstr);
+#endif
+
+ /*
+ * KIONIX_CTRL_REG3 and DATA_CTRL_REG can remain at their default values
+ *
+ * 50 Hz
+ */
+#if 0
+ /* KTXF9 300 uA; KTXI9 165 uA */
+ WriteRegisterData = PC1_OPERATING_MODE | TAP_ENABLE_TDTE;
+
+ /* 180 uA; KTXI9 115 uA */
+ WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_8BIT | WUF_ENABLE;
+
+ /* 180 uA; KTXI9 8.7 uA */
+ WriteRegisterData = PC1_OPERATING_MODE | TILT_ENABLE_TPE;
+
+ /* 720 uA; KTXI9 330 uA */
+ WriteRegisterData = PC1_OPERATING_MODE | RESOLUTION_12BIT | WUF_ENABLE;
+#endif
+
+ /* setup the default for the AccelerometerEnable command */
+#if 0
+ OperatingModeRegister = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
+ InterruptControl = INTERRUPT_CONTROL_DISABLE_INTERRUPT;
+ SidControl = SID_CONTROL_SEND_DATA;
+ SidAddr = KIONIX_XOUT_L;
+ SidLength = XYZ_DATA_LENGTH;
+
+ AccelState = ACCEL_STATE_INIT;
+#endif
+}
+
+void mw_acc_enable(void)
+{
+ uint8_t sdata;
+
+ mw_acc_init();
+
+ sdata = PC1_OPERATING_MODE | RESOLUTION_12BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE | WUF_ENABLE;
+ //sdata = PC1_OPERATING_MODE | RESOLUTION_8BIT | TAP_ENABLE_TDTE | TILT_ENABLE_TPE; // | WUF_ENABLE;
+ mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
+
+ ACCELEROMETER_INT_ENABLE();
+ mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
+ AccelState = ACCEL_STATE_ENABLED;
+}
+
+void mw_acc_disable(void)
+{
+ uint8_t sdata;
+
+ if (AccelState == ACCEL_STATE_ENABLED) {
+ sdata = PC1_STANDBY_MODE;
+ mw_acc_i2c_write(KIONIX_CTRL_REG1, &sdata, 1);
+
+ ACCELEROMETER_INT_DISABLE();
+ mw_acc_disable_i2c();
+ DISABLE_ACCELEROMETER_POWER();
+ AccelState = ACCEL_STATE_DISABLED;
+ }
+}
+
+void mw_acc_read(int16_t *x, int16_t *y, int16_t *z)
+{
+ uint8_t rdata[6];
+
+ if (AccelState == ACCEL_STATE_ENABLED) {
+ mw_acc_i2c_read(KIONIX_XOUT_L, rdata, 6);
+
+ *x = rdata[0] | (rdata[1] << 8);
+ *y = rdata[2] | (rdata[3] << 8);
+ *z = rdata[4] | (rdata[5] << 8);
+ } else {
+ *x = 0;
+ *y = 0;
+ *z = 0;
+ }
+}
+
+void mw_acc_handle_irq(void)
+{
+ uint8_t sdata, srcreg1, srcreg2;
+#if defined MW_DEVBOARD_V2
+ char tstr[16];
+#endif
+
+ mw_acc_i2c_read(KIONIX_INT_SRC_REG1, &srcreg1, 1);
+#if defined MW_DEVBOARD_V2
+ snprintf(tstr, 16, "accsrc1: 0x%02x\n", srcreg1);
+ debug_uart_tx(tstr);
+#endif
+ mw_acc_i2c_read(KIONIX_INT_SRC_REG2, &srcreg2, 1);
+#if defined MW_DEVBOARD_V2
+ snprintf(tstr, 16, "accsrc2: 0x%02x\n", srcreg2);
+ debug_uart_tx(tstr);
+#endif
+ if (srcreg1 & INT_TAP_SINGLE) {
+ };
+ if (srcreg1 & INT_TAP_DOUBLE) {
+ };
+ if (srcreg2 & INT_WUFS) {
+ int16_t x, y, z;
+ mw_acc_read(&x, &y, &z);
+ oswald_handle_accel_event((int8_t)(x / (32768 / 255)), (int8_t)(y / (32768 / 255)), (int8_t)(z / (32768 / 255)));
+ }
+
+ mw_acc_i2c_read(KIONIX_INT_REL, &sdata, 1);
+}