/* * Serial communications are based on the following code: * Written by Windell Oskay, http://www.evilmadscientist.com/ * Adapted from the Arduino sketch "Serial Call and Response," by Tom Igoe. * * Copyright 2009 Windell H. Oskay * Copyright (c) 2014 Pete Zaitcev * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #define F_CPU 8000000 // Oscillator frequency. #define BaudRate 9600 #define MYUBRR (F_CPU/16/BaudRate - 1) /* * XXX The baud rate ends 1200, not 9600 (1:8 error). Oscillator fused * to 1 MHz instead of 8 MHz? Or what? * * "The baud rate is implemented using a non-obvious but relatively compact * scheme taken from the datasheet." * * Default fuse is to divide the clock by 8 internally (CKDIV8). */ /* * See the schematic. Pins of USI are used, plus PB4. This works even with * the programmer plugged in, because it goes 3-state when we're running. */ #define A_SS PB4 #define A_MISO PB5 #define A_MOSI PB6 #define A_SCK PB7 /* * Arguments to spi_xfer(). */ #define LIS_RD 0x80 #define LIS_R_STATUS_AUX 0x07 #define LIS_R_OUT_TEMP_L 0x0c #define LIS_R_OUT_TEMP_H 0x0d #define LIS_R_WHO_AM_I 0x0f #define LIS_R_CTRL_REG1 0x20 #define LIS_R_CTRL_REG2 0x21 #define LIS_R_CTRL_REG3 0x22 #define LIS_R_CTRL_REG4 0x23 #define LIS_R_CTRL_REG5 0x24 #define LIS_R_CTRL_REG6 0x25 #define LIS_R_REF_DATA 0x26 #define LIS_R_STATUS_REG 0x27 #define LIS_R_OUT_X_L 0x28 #define LIS_R_OUT_X_H 0x29 #define LIS_R_OUT_Y_L 0x2a #define LIS_R_OUT_Y_H 0x2b #define LIS_R_OUT_Z_L 0x2c #define LIS_R_OUT_Z_H 0x2d #define LIS_R_FIFO_CTRL_REG 0x2e // no idea why "REG" is in the name XXX #define LIS_R_FIFO_SRC_REG 0x2f #define LIS_R_CLICK_CFG 0x38 #define LIS_R_CLICK_SRC 0x39 #define LIS_R_CLICK_THS 0x3a #define LIS_R_TIME_LIMIT 0x3b #define LIS_R_TIME_LATENCY 0x3c #define LIS_R_TIME_WINDOW 0x3d static unsigned int gval_to_leds(int gval); unsigned int spi_xfer(unsigned int word); static void leds(unsigned int val); static void half_cycle_delay(void); static void delay(void); unsigned char serialCheckRxComplete(void) { return( UCSRA & _BV(RXC)) ; // nonzero if serial data is available to read. } unsigned char serialCheckTxReady(void) { return( UCSRA & _BV(UDRE) ) ; // nonzero if transmit register is ready to receive new data. } unsigned char serialRead(void) { while (serialCheckRxComplete() == 0) // While data is NOT available to read {;;} return UDR; } void serialWrite(unsigned char DataOut) { while (serialCheckTxReady() == 0) // while NOT ready to transmit {;;} UDR = DataOut; } int main(void) { unsigned int c; unsigned int gval; /* * Set PortB for output. * PB4: software - A_CS * PB6: USI DO - A_MOSI * PB7: USI SCK - A_SCK */ DDRB = _BV(4) | _BV(6) | _BV(7); /* * Set PortD for output. * PD1: Serial TxD * PD2: SER for the 74HC595 * PD3: SRCLK for the 74HC595 * PD4: RCLK for the 74HC595 */ DDRD = _BV(1) | _BV(2) | _BV(3) | _BV(4); PORTB |= _BV(PB5); // pullup on (DI) /* * Datasheet says: * When the Transmitter is enabled, the normal port operation of the * TxD pin is overridden by the USART and given the function as the * Transmitter’s serial output. * So, nothing else to set aside of TXEN. */ /* Set baud rate */ UBRRH = (unsigned char)(MYUBRR >> 8); UBRRL = (unsigned char) MYUBRR; /* Enable receiver and transmitter */ UCSRB = (1<'); /* * In CTRL_REG2: * "Normal mode (reset reading REFERENCE/DATACAPTURE (26h) register)" */ spi_xfer((LIS_RD | LIS_R_REF_DATA) << 8); for (;;) { c = spi_xfer((LIS_RD | LIS_R_STATUS_REG) << 8); if ((c & 0x02) != 0) { /* * "The value is expressed as two’s complement left * justified." */ c = spi_xfer((LIS_RD | LIS_R_OUT_Y_L) << 8); gval = c & 0xff; c = spi_xfer((LIS_RD | LIS_R_OUT_Y_H) << 8); gval |= (c << 8); leds(gval_to_leds((int) gval)); //delay(); } } } struct level { int threshold; unsigned int leds; }; static unsigned int gval_to_leds(int gval) { static const struct level lev_pos[8] = { { 10 * 64, 0x0100 }, { 19 * 64, 0x0300 }, { 32 * 64, 0x0700 }, { 70 * 64, 0x0f00 }, { 108 * 64, 0x1f00 }, { 142 * 64, 0x3f00 }, { 178 * 64, 0x7f00 }, { 240 * 64, 0xff00 } }; static const struct level lev_neg[8] = { { -10 * 64, 0x0080 }, { -19 * 64, 0x00c0 }, { -32 * 64, 0x00e0 }, { -70 * 64, 0x00f0 }, { -108 * 64, 0x00f8 }, { -142 * 64, 0x00fc }, { -178 * 64, 0x00fe }, { -240 * 64, 0x00ff } }; int i; const struct level *lp, *lowp; if (gval < 0) { lp = lev_neg; if (gval >= lp->threshold) return 0; lowp = lp; for (i = 0; i < 8; i++) { if (gval < lp->threshold) lowp = lp; lp++; } } else { lp = lev_pos; if (gval < lp->threshold) return 0; lowp = lp; for (i = 0; i < 8; i++) { if (gval >= lp->threshold) lowp = lp; lp++; } } return lowp->leds; } //// From the ATtiny2313 datasheet: // ; The code example assumes that the DO and USCK pins are enabled as // ; output in the DDRB Register. // SPITransfer: // out USIDR,r16 // ; clears the USI Counter Overflow Flag and the USI counter value // ldi r16,(1<> 8; USISR = _BV(USIOIF); while ((USISR & _BV(USIOIF)) == 0) { USICR = _BV(USIWM0)|_BV(USICS1)|_BV(USICLK)|_BV(USITC); } /* * This should be pointless, since the LIS2DH does not send anything * in the first 8 clocks, but we save the value for hardware debugging. */ ret = USIDR << 8; USIDR = word & 0xff; USISR = _BV(USIOIF); while ((USISR & _BV(USIOIF)) == 0) { USICR = _BV(USIWM0)|_BV(USICS1)|_BV(USICLK)|_BV(USITC); } ret |= USIDR; PORTB |= (1 << PB4); return ret; #endif #if 0 /* bitbang -- incomplete since we found the bug (address was wrong). */ unsigned int ret; unsigned char bits; /* * Drive high to give lines a moment to stabilize, although strictly * speaking this is not necessary. */ bits = (1<> 15) << A_MOSI); PORTB = bits; half_cycle_delay(); ret = 1; // PORTB |= (1 << PB4); return ret; #endif } /* * Shift the value into the 2 595s. */ #define Z_SER PD2 #define Z_SRCLK PD3 #define Z_RCLK PD4 static void leds(unsigned int val) { int i; unsigned char bits; bits = PORTD; /* Set storage clock low (we'll need an edge later). */ bits &= ~(1<>= 1; } /* Raise the storage clock to latch the data from the shift register. */ bits |= 1 << Z_RCLK; PORTD = bits; half_cycle_delay(); } static void half_cycle_delay(void) { /* * Datasheet for 74HC595 says it can be driven at 25 MHz at 4.5 V. * Signals need 25 ns or less to settle (such as SER before SRCLK * low-high). At 8 MHz, one clock cycle takes 125 ns. So, we don't * need anything in this function at all, just a nop. * XXX change this to an asm("nop"), see how well it works. */ volatile unsigned int del = 1; while (del--) {} } static void delay(void) { /* * The "volatile" is necessary here to prevent over-optimization. * gcc version 4.8.2 (Fedora 4.8.2-1.fc20) */ volatile unsigned int del = 40000; while (del--) {} }