From: kremlin Date: Mon, 16 Feb 2015 08:29:37 +0000 (-0600) Subject: copied the file twice, whoops X-Git-Url: https://uglyman.kremlin.cc/gitweb/gitweb.cgi?a=commitdiff_plain;p=grab_bag.git copied the file twice, whoops --- diff --git a/lsm303d_arm_arduino.c b/lsm303d_arm_arduino.c index decd6ad..0835df2 100644 --- a/lsm303d_arm_arduino.c +++ b/lsm303d_arm_arduino.c @@ -482,488 +482,4 @@ void loop() { stream_temp(); //stream_magno(); } -/* Copyright (c) 2015, Ian Sutton - - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - - /* this is a library for the arduino due that controls the LSM303D accelerometer - * & magnometer. it will not work on other arduino models, it uses API calls - * exclusive to newer ARM-based arduinos. - * - * steps to recreate: - * - * !! be especially careful of the 5V output in on the due's SPI header. it is a high !! - * !! current output & will cook anything nearby. refer to this diagram before making !! - * !! any connections: http://uglyman.kremlin.cc/quick/due-pinout-web.png !! - * - * - wire SPI ports on the LSM to their respective pins on the due's SPI header (not - * ICSP!) - * - * - wire INT2 to digital pin 53 and the LSM's chip select/slave select port to - * pin 10 above the pwm module. - * - * - ground the SPI module, arduino, and LSM together, preferably alone. i had to use - * an audio isolation amplifier to provide a quiet enough ground to support the - * unusually high SPI baud rate i use here, discussed later. - * - * - wire LSM's Vin to the arduino's 3.3V supply. leave Vdd floating */ - -#include - -/* slave select pin */ -#define LSM_CS 10 - -/* wire to INT2 which latches to a magnometer-read-ready signal */ -#define MAGNO_RDY_PIN 53 - -/* SPI clock divider (84MHz divided by this equals SPI frequency) */ -#define SPI_CLK_DIV 3 - -/* frequency of temperature sensor in Hz, set in lsm_config() */ -#define TEMP_FREQ 100 - -/* struct representing magnometer values at shared instant of time */ -struct magno_point { - - signed short x; - signed short y; - signed short z; -}; - -/* hands off */ -const byte BAD_REGS[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x0E, 0x10, 0x11 }; - -/* these are set on successful completion of their respective funcs */ -bool SERIAL_CONFIGURED = 0; -bool SPI_CONFIGURED = 0; -bool SPI_OK = 0; -bool LSM_CONFIGURED = 0; - -/* don't brick the LSM */ -bool matches_badreg(byte addr) { - - short i; - i = 0; - - for (; i < 8; i++) - if (addr == BAD_REGS[i]) - return true; - - return false; -} - -/* fired when critical exception encountered, doesn't return */ -void killspin(String msg) { - - Serial.println(msg); - Serial.println("exception occurred or assertion failed. no-operation until reset"); - while (1); -} - -/* reads n sequential bytes starting @ addr returns byte array of size n - * returned array must be free()'d manually! */ -byte *spi_multiread(byte addr, short n) { - - byte *ret, first; - short cnt; - - if (matches_badreg(addr)) - killspin("tried to read from a reserved, internal register!"); - - if (!SPI_CONFIGURED || !SPI_OK) - killspin("tried to read over SPI before it was configured or tested!"); - - /* addresses are 6 bits wide */ - if (addr > 63) - killspin("invalid lsm register address"); - - /* set msb to read, 2nd msb for multibyte op, and append 6 bit address field */ - first = 128 + 64 + addr; - cnt = 0; - - /* free this! */ - ret = (byte *) calloc(n, 1); - - /* kickoff read operation */ - SPI.transfer(LSM_CS, first, SPI_CONTINUE); - - for (; cnt < n - 1; cnt++) - ret[cnt] = SPI.transfer(LSM_CS, first, SPI_CONTINUE); - - ret[n - 1] = SPI.transfer(LSM_CS, first, SPI_LAST); - - return ret; -} - -/* reads & returns 1 byte @ addr */ -byte spi_read(byte addr) { - - byte first, ret; - - if (matches_badreg(addr)) - killspin("tried to read from a reserved, internal register!"); - - if (!SPI_CONFIGURED || !SPI_OK) - killspin("tried to read over SPI before it was configured or tested!"); - - /* addresses are 6 bits wide */ - if (addr > 63) - killspin("invalid lsm register address"); - - /* set msb to read and append 6 bit address field */ - first = 128 + addr; - - /* perform 16 bit SPI exchange as per LSM datasheet's specification */ - SPI.transfer(LSM_CS, first, SPI_CONTINUE); - ret = SPI.transfer(LSM_CS, 0x00, SPI_LAST); - - return ret; -} - -/* write single byte @ addr */ -void spi_write(byte addr, byte data) { - - byte first; - - if (matches_badreg(addr)) - killspin("tried to write to a reserved, internal register!"); - - if (!SPI_CONFIGURED || !SPI_OK) - killspin("tried to write over SPI before it was configured or tested!"); - - /* addresses are 6 bits wide */ - if (addr > 63) - killspin("INVALID ADDRESS"); - - /* set 6 bit address field */ - first = addr; - - /* perform 16 bit SPI exchange as per LSM datasheet's specification */ - SPI.transfer(LSM_CS, first, SPI_CONTINUE); - SPI.transfer(LSM_CS, data, SPI_LAST); -} - -void spi_config() { - - SPI.begin(LSM_CS); - - /* my arduino due has a 84 MHz cpu which is divided here to provide the - * SPI baud rate. the LSM's data sheet purports the maximum SPI frequency - * is 10MHz, however i've found that it works fine up to 28MHz, which is - * the frequency set below (84 MHz / 3 = 28 MHz) */ - SPI.setClockDivider(LSM_CS, SPI_CLK_DIV); - - /* LSM is big endian */ - SPI.setBitOrder(LSM_CS, MSBFIRST); - - /* clock is active-low, exchange occurs on clock's first falling edge - * CPOL = 1, CKE = 0 */ - SPI.setDataMode(LSM_CS, SPI_MODE3); - - SPI_CONFIGURED = 1; -} - -/* read & check immutable device ID reg a number of times to guarantee LSM - * slave is responding and capable of handling master's SPI clock freq. - * then, write reg & read back to test writing */ -boolean spi_test() { - - bool read_ok, write_ok; - byte i; - - i = 0; - read_ok = true; - write_ok = true; - - if (!SPI_CONFIGURED) - killspin("tried to test SPI before it was configured!"); - - /* cheat a little here */ - SPI_OK = 1; - - /* test reading */ - Serial.print(" [READ: "); - - for (; i < 100; i++) - if (spi_read(0x0F) != 0x49) - read_ok = false; - - if (read_ok) - Serial.print("OK, WRITE: "); - else - Serial.print("FAIL, WRITE: "); - - /* test writing with */ - i = 0; - for (; i < 100; i++) { - - spi_write(0x17, i); - - /* uncomment for write debugging - Serial.print("WROTE "); - Serial.print(i); - Serial.print(" GOT "); - Serial.println(spi_read(0x17));*/ - - if (spi_read(0x17) != i) - write_ok = false; - } - - /* write back datasheet-defined default value to test - * register we used (OFFSET_X_L_M) */ - if (write_ok) - spi_write(0x17, 0x00); - - /* finish up */ - if (write_ok) - Serial.print("OK] "); - else - Serial.print("FAIL] "); - - if (read_ok && write_ok) { - - SPI_OK = 1; - return true; - - } else { - - SPI_OK = 0; - Serial.println(":: failed!"); - return false; - } -} - -void lsm_config() { - - if (!SPI_CONFIGURED || !SPI_OK) - killspin("tried to configure lsm before spi was configured & tested"); - - /* set 16 bit 2's comp. magnetic field offset values for x, y, z. - * these default to zero as correct offset values depend on your geographical - * location. you can find the current magnetic field strength at your coords - * using NOAA's database: http://www.ngdc.noaa.gov/geomag-web/#igrfwmm - * - * in the WMM model, the significant values are north comp (z offset), east comp - * (x offset), and vertical comp (y offset). you can ignore the 'change/year' and - * 'uncertainty' values - * - * you must translate the given tesla values into corresponding gauss equivalents - * and scale them according to the range specified later in this function. you will - * usually use the +2:-2 gauss scale. here is an example conversion for - * latitude 43° 2' 14" N, longitude 76° 7' 36" W (near syracuse university in - * syracuse, NY 13210), 0 meters above sea level, taken on february 16th, 2015 at 07:32 UTC: - * - * [x] :: -4,129.9 nanoteslas :: -0.041299 gauss - * [y] :: 49,817.8 nanoteslas :: 0.498178 gauss - * [z] :: 18,755.9 nanoteslas :: 0.187559 gauss - * - * next, we need to fit these values into our 4-gauss scale (spanning from +2 gauss - * to -2 gauss) in the context of a 16-bit signed number. to do this, take the gauss value - * and divide it by 2. take this number, and multiply it by 2^15 - 1. round to closest integer. - * finally, multiply this value by -1 as the offset value combined with the sensed value should - * result in zero. negatives should be expressed as two's complement. here are the offsets derived - * from the previous values: - * - * [x] :: 677 :: 0x02A5 - * [y] :: -16,324 :: 0xC03C - * [z] :: -6,146 :: 0xE7FE - */ - const byte x_lo_offset = 0xA5; - const byte x_hi_offset = 0x02; - - const byte y_lo_offset = 0x3C; - const byte y_hi_offset = 0xC0; - - const byte z_lo_offset = 0xFE; - const byte z_hi_offset = 0xE7; - - spi_write(0x16, x_lo_offset); - spi_write(0x17, x_hi_offset); - - spi_write(0x18, y_lo_offset); - spi_write(0x19, y_hi_offset); - - spi_write(0x1A, z_lo_offset); - spi_write(0x1B, z_hi_offset); - - /* latch magnometer-ready to INT2 output pin */ - spi_write(0x23, 0x04); - - /* enable temperature sensor, - * select high magnetic resolution, - * select 100Hz sensor rate */ - spi_write(0x24, 0xF4); - - /* set full scale of magnetometer to +2:-2 gauss as - * earth's field is usually between +0.65:-0.65 G */ - spi_write(0x25, 0x00); - - /* switch on magnometer, to continuous conversion mode */ - spi_write(0x26, 0x00); - - LSM_CONFIGURED = 1; -} - -/* returns a size-n array of readings from temperature sensor. function waits - * between reads for a time equal to the period length of the sensor as to avoid - * multiple reads of an un-updated value. this is a hack to get around the fact - * this chip does not have a TEMP_READY bit in a status register like the magnometer - * or accelerometer do. - * caller must free() returned pointer */ -signed short *pull_temp_values(int n) { - - signed short *ret; - int sensor_period, i; - byte *temp_pair, *sync_pair_i, *sync_pair_f; - - /* period length in milliseconds of temperature sensor refresh */ - sensor_period = 1000 / TEMP_FREQ; - - i = 0; - ret = (signed short *) calloc(n, 2); - - sync_pair_i = sync_pair_f = spi_multiread(0x05, 2); - - /* spin until sensor cranks */ - while (sync_pair_i == sync_pair_f) - sync_pair_f = spi_multiread(0x05, 2); - - /* wait until mid-period to read as to avoid edge-case duplicates */ - delay(sensor_period / 2); - - for (; i < n; i++) { - temp_pair = spi_multiread(0x05, 2); - ret[i] = word(temp_pair[1], temp_pair[0]); - free(temp_pair); - delay(sensor_period); - } - - free(sync_pair_i); - free(sync_pair_f); - - return ret; -} - -/* returns a magno_point struct from passed 48 bit input taken from magno sensors */ -struct magno_point parse_raw_magno_data(byte *in) { - - struct magno_point ret; - - ret.x = word(in[1], in[0]); - ret.y = word(in[3], in[2]); - ret.z = word(in[5], in[4]); - - return ret; -} - -/* returns a size-n array of readings from magnometer. result contains 3 words - * describing felt magnetic field strengths x, y, z directions. function polls INT2 - * (latched to magnometer-ready signal) until it goes high before reading from sensor. - * this guarantees each member in returned array is a genuine, non-repeat value from a - * single magnometer sensor cycle - * caller must free() returned pointer */ -struct magno_point *pull_magno_values(int n) { - - struct magno_point *ret; - int i; - - i = 0; - ret = (struct magno_point *) calloc(n, sizeof(struct magno_point)); - - for(; i < n; i++) { - - /* spin until sensors are fresh */ - while(digitalRead(MAGNO_RDY_PIN) != 1); - - ret[i] = parse_raw_magno_data(spi_multiread(0x08, 6)); - } - - return ret; -} - -void setup() { - - /* set up magno. read ready signal */ - pinMode(MAGNO_RDY_PIN, INPUT); - - /* this is about as fast as you can get on the serial console */ - Serial.begin(115200); - SERIAL_CONFIGURED = 1; - - /* spi init */ - Serial.print("configuring spi.."); - spi_config(); - Serial.println("done"); - - /* spi check */ - Serial.print("testing spi.."); - if (spi_test()) - Serial.println("done"); - else - killspin("SPI test failed. perhaps the device is not wired correctly or your frequency is too high"); - - /* lsm init */ - Serial.print("configuring lsm.."); - lsm_config(); - Serial.println("done"); -} - -/* demonstration of realtime temperature stream. does not return */ -void stream_temp() { - - for(;;) { - signed short *temp_vals; - temp_vals = pull_temp_values(100); - - for (int i = 0; i < 100; i++) { - if(!(i % 20) && i) - Serial.print('\n'); - else if(i) - Serial.print(" "); - - Serial.print(temp_vals[i], DEC); - } - - Serial.println("\n--------------------------------------------------------------------------------"); - - free(temp_vals); - } -} - -/* demonstration of realtime magnometer stream. does not return */ -void stream_magno() { - - struct magno_point *read_buf; - - read_buf = pull_magno_values(100); - - for(int i = 0; i < 100; i++) { - - Serial.print("X: "); - Serial.println(read_buf[i].x, DEC); - Serial.print("Y: "); - Serial.println(read_buf[i].y, DEC); - Serial.print("Z: "); - Serial.println(read_buf[i].z, DEC); - Serial.println("---------"); - } - - free(read_buf); -} - -void loop() { - - stream_temp(); - //stream_magno(); -}