// main.c - test using the LIS3LV02DQ

// connections:
//   PORTB Chip Select
//         SPI

// 20080728 tomdean - initial version

// Rough application to test the LIS3LV02DQ accelerometer.
// If you are interested, the header files are available.
// Contact the author.

// This code is released under the GPL.

//  Copyright (C) 2008  tomdean@speakeasy.org
//
//  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., 59 Temple Place - Suite 330,
//  Boston, MA 02111-1307, USA.

// $Id$

////////////////////////////////////////////////////////////////
// includes
#include <avr/io.h>            // device definitions
#include <inttypes.h>          // integer types
#include <avr/sleep.h>         // sleep mode
#include <avr/interrupt.h>
#include <string.h>
#define F_CPU 16000000UL
#define BAUD 38400
#include <util/setbaud.h>

#include "../include/ports.h"  // ports definitions
#include "../include/spi.h"

#define SPI_PORT PORTB

typedef struct spi_struct_t {
  uint8_t count;
  uint8_t data[1]; // declare one, but, this will be overlaid on a buffer
} spi_struct_t ;

////////////////////////////////////////////////////////////////
// prototypes
void bin2bcd(uint16_t, char *);
void bin2ascii(uint16_t , char *);
char *bin_to_hex_ascii(uint8_t, char *);
void serial_init(void);
void adc_init(void);
uint16_t gyro(uint8_t, uint8_t);
void spi_init(void);
void spi_cmd(volatile uint8_t *, uint8_t, spi_struct_t *);
void accel_init(void);

////////////////////////////////////////////////////////////////
// global variables
int32_t loop_count;
uint16_t g_data;
extern uint8_t g_hi, g_lo;

uint8_t spi_buf[20];

////////////////////////////////////////////////////////////////
// serial init
void serial_init(void) {
  UBRRH = UBRRH_VALUE; // defined by setbaud.h values above
  UBRRL = UBRRL_VALUE; // defined by setbaud.h values above
  /* enable tx and rx */
  UCSRB = _BV(RXEN) | _BV(TXEN);
  // set URSEL to write ucsrc
  // 8N1 is the reset default
  UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0);
  return;
}
unsigned char serial_recv(void);
void serial_send(unsigned char);
void serial_print(const char *);
////////////////////////////////////////////////////////////////
// serial receive
unsigned char serial_recv(void) {
  /* wait for rx char */
  while (!(UCSRA & (1 << RXC)));
  /* return the char */
  return UDR;
}

////////////////////////////////////////////////////////////////
// serial send
void serial_send(unsigned char c) {
  /* wait for tx buffer empty */
  while (!(UCSRA & (1 << UDRE)));
  /* send the char */
  UDR = c;
  return;
}

////////////////////////////////////////////////////////////////
// serial print
void serial_print(const char *msg) {
  while (*msg != 0)
    serial_send (*msg++);
  return;
}

uint8_t get_hex_nibble(void);
uint8_t get_hex_byte(void);
////////////////////////////////////////////////////////////////
// get 4 bits
uint8_t get_hex_nibble() {
  char c = serial_recv();
  serial_send(c);
  return c - (c < 'A' ? 0x30 : c < 'a' ? 0x37 : 0x57);
}


////////////////////////////////////////////////////////////////
// get 8 bits
uint8_t get_hex_byte() {
  return (get_hex_nibble() << 4) | ( get_hex_nibble() & 0x0f);
}

//////////////////////////////////////////////////////////////////////
// uart
ISR(SIG_UART_DATA) {
}

volatile uint8_t spi_count;
uint8_t *spi_ptr;
volatile uint8_t spi_done;

///////////////////////////////////////////////////////////////
// initialize the SPI port
void spi_init() {
  // set spi bits output
  DDRB |= _BV(SS) | _BV(SCK) | _BV(MOSI);
  // LCD Slave Select bit
  DDRB |= _BV(03);
  // set spi bits high and lcd ss high
  PORTB |= _BV(SS) | _BV(SCK) | _BV(MOSI) | _BV(03);
  // enable spi interrupts, enable spi, master, F_CPU/16
  SPCR  = _BV(SPIE) | _BV(SPE) | _BV(MSTR) | _BV(SPR0);
  // set spi mode 3
  SPCR |= _BV(CPOL) | _BV(CPHA);
  // since we have not done anything, we are done with the last command
  spi_done = TRUE;
  sei();
}

void spi_cmd(volatile uint8_t *port, uint8_t bit, spi_struct_t *cmd) {
  // make this an avrx wait semaphore
  // This function is blocking.
  // make sure the spi is done
  while (!spi_done) ;
  // select the device
  *port &= ~_BV(bit);
  // set up for the next buffer
  spi_done = FALSE;
  spi_ptr = (uint8_t *)&cmd->data[0];
  spi_count = cmd->count; // decremented in ISR
  // send the first byte
  SPDR = *spi_ptr;        // incremented in the ISR
  // wait until the spi is done
  while (!spi_done) ;
  *port |= _BV(bit);
  // de-select the device
}


///////////////////////////////////////////////////////////////
// SPI interrupt handler
ISR(SIG_SPI) {
  // put the clocked in data into the command structure
  *spi_ptr++ = SPDR;
  // decrement the byte count
  spi_count--;
  if (spi_count == 0) {
	// we are done
	spi_done = TRUE;
  } else {
	// not done, send the next byte
	SPDR = *spi_ptr;
  }
}

#define ACCEL_PORT PORTB
#define ACCEL_CS 2
#define ACCEL_READ    _BV(7)
#define ACCEL_WRITE   0
#define ACCEL_SINGLE  0
#define ACCEL_MULT    _BV(6)
#define ACCEL_WHOAMI  0x0f
#define ACCEL_CTRL_REG1 0x20
#define ACCEL_CTRL_REG2 0x21
#define ACCEL_CTRL_REG3 0x22
#define ACCEL_STATUS 0x27
#define ACCEL_OUTX_L 0x28
#define ACCEL_OUTX_H 0x29
#define ACCEL_OUTY_L 0x2A
#define ACCEL_OUTY_H 0x2B
#define ACCEL_OUTZ_L 0x2C
#define ACCEL_OUTZ_H 0x2D

spi_struct_t *accel_struct = (spi_struct_t *)&spi_buf[0];

////////////////////////////////////////////////////////////////
// do cmd
void do_dump(uint8_t, uint8_t);
void do_dump(uint8_t lo, uint8_t hi) {
  uint8_t idx;
  uint8_t ch;
  for (idx=lo; idx<=hi; idx++) {
	accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | idx;
	accel_struct->data[1] = 0; // the return data from the command will be here
	accel_struct->count = 2; //
	spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
	// send it up the pike
	ch = idx>>4;
	if (ch < 0x0a) ch += '0';
	else           ch += 'A'-0x0a;
	serial_send(ch);
	ch = idx&0x0f;
	if (ch < 0x0a) ch += '0';
	else           ch += 'A'-0x0a;
	serial_send(ch);
	serial_send(':');
	serial_send(' ');
	ch = accel_struct->data[1]>>4;
	if (ch < 0x0a) ch += '0';
	else           ch += 'A'-0x0a;
	serial_send(ch);
	ch = accel_struct->data[1]&0x0f;
	if (ch < 0x0a) ch += '0';
	else           ch += 'A'-0x0a;
	serial_send(ch);
	if (idx != hi) {
	  serial_send(' ');
	  serial_send(' ');
	  serial_send(' ');
	}
  }
}

////////////////////////////////////////////////////////////////
// read
void do_read(void);
void do_read() {
  uint8_t adr;
  uint8_t ch;
  adr=get_hex_byte();
  adr &= 0x3f; // adr[5:0]
  serial_send(':');
  serial_send(' ');
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | adr;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send('\n');
}
 
////////////////////////////////////////////////////////////////
// write
void do_write(void);
void do_write() {
  uint8_t adr, dat;
  uint8_t ch;
  adr=get_hex_byte();
  adr &= 0x3f; // adr[5:0]
  serial_send(' ');
  dat = get_hex_byte();
  serial_send(':');
  serial_send(' ');
  accel_struct->data[0] = ACCEL_WRITE | ACCEL_SINGLE | adr;
  accel_struct->data[1] = dat; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send('\n');
}
 
////////////////////////////////////////////////////////////////
// acceleration
void do_accel(void);
void do_accel(void) {
  uint8_t ch;
  
  serial_send('X');
  serial_send(':');
  serial_send(' '); 
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTX_H;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTX_L;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send(' '); 
  
  serial_send('Y');
  serial_send(':');
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTY_H;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTY_L;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send(' '); 
  
  serial_send('Z');
  serial_send(':');
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTZ_H;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  accel_struct->data[0] = ACCEL_READ | ACCEL_SINGLE | ACCEL_OUTZ_L;
  accel_struct->data[1] = 0; // the return data from the command will be here
  accel_struct->count = 2; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);

  serial_send('\n');
}
  
//////////////////////////////////////////////////////////////
// wait us
void timer_wait_us(unsigned int us_wait) {
  while (us_wait--) {
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
        asm("nop");
  }
}
 
////////////////////////////////////////////////////////////////
// group the accel commands into one transaction
void do_group(void);
void do_group(void) {
  uint8_t ch;

  accel_struct->data[ 0] = ACCEL_READ | ACCEL_MULT | ACCEL_OUTX_L;
  accel_struct->data[ 1] = 0; // the return data from the command will be here
  accel_struct->data[ 2] = 0; // the return data from the command will be here
  accel_struct->data[ 3] = 0; // the return data from the command will be here
  accel_struct->data[ 4] = 0; // the return data from the command will be here
  accel_struct->data[ 5] = 0; // the return data from the command will be here
  accel_struct->data[ 6] = 0; // the return data from the command will be here
  accel_struct->count = 7 ; //
  spi_cmd(&ACCEL_PORT, ACCEL_CS, accel_struct);

  serial_send('X');
  serial_send(':');
  serial_send(' '); 
  ch = accel_struct->data[2]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[2]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[1]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send(' '); 
  
  serial_send('Y');
  serial_send(':');
  ch = accel_struct->data[4]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[4]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[3]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[3]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  serial_send(' '); 
  
  serial_send('Z');
  serial_send(':');
  ch = accel_struct->data[6]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[6]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[5]>>4;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);
  ch = accel_struct->data[5]&0x0f;
  if (ch < 0x0a) ch += '0';
  else           ch += 'A'-0x0a;
  serial_send(ch);

  serial_send('\n');
}

////////////////////////////////////////////////////////////////
// dump
void dump(void);
void dump(void) {
  do_dump(0x16, 0x1b); serial_send('\n');
  do_dump(0x20, 0x23); serial_send('\n');
  do_dump(0x27, 0x2d); serial_send('\n');
  do_dump(0x30, 0x32); serial_send('\n');
  do_dump(0x34, 0x36); serial_send('\n');
  do_dump(0x38, 0x3a); serial_send('\n');
  do_dump(0x3c, 0x3f); serial_send('\n');
}

////////////////////////////////////////////////////////////////
// usage
void usage(void);
void usage(void) {
  serial_print(
			   "d - dump\n"
			   "h - help\n"
			   "? - help\n"
			   "w - write needs addr and data\n"
			   "r - read needs addr\n"
			   );
}

//                        1111111111222222222233333333334444444444
//              01234567890123456789012345678901234567890123456789
char title[] = "   00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n";

////////////////////////////////////////////////////////////////
// main
int main() {
  char cmd;

  spi_init(); // does sei()

  serial_init();

  DDRB |=  _BV(4) | _BV(3) | _BV(2);  // chip selects as output
  PORTB |= _BV(4) | _BV(3) | _BV(2);  // clear select

  DDRC = 0xff;
  DDRD |= 0xf0; // bits 7:4 output

  while(1) {
	// sync
	cmd = serial_recv();
	switch (cmd) {
	case 'a':
	  // acceleration
	  do_accel();
	  break;
	case 'd':
	  dump();
	  break;
	case 'g':
	  // group accel
	  do_group();
	  break;
	case '?':
	case 'h':
	default:
	  usage();
	  break;
	case 'r':
	  // read
	  do_read();
	  break;
	case 'w':
	  // write
	  do_write();
	  break;
	}
  }
  return 0;
}

