Skip to content

Instantly share code, notes, and snippets.

@christophmccann
Created September 24, 2014 18:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christophmccann/15bb1c2b34022b4ff2c5 to your computer and use it in GitHub Desktop.
Save christophmccann/15bb1c2b34022b4ff2c5 to your computer and use it in GitHub Desktop.
/*
>> Pulse Sensor Amped 1.2 <<
This code is for Pulse Sensor Amped by Joel Murphy and Yury Gitman
www.pulsesensor.com
>>> Pulse Sensor purple wire goes to Analog Pin A2 (see Interrupt.h for details) <<<
Pulse Sensor sample aquisition and processing happens in the background via a hardware Timer interrupt. 2mS sample rate.
PWM on selectable pins A0 and A1 will not work when using this code, because the first allocated timer is TIMR2!
The following variables are automatically updated:
Signal : int that holds the analog signal data straight from the sensor. updated every 2mS.
IBI : int that holds the time interval between beats. 2mS resolution.
BPM : int that holds the heart rate value, derived every beat, from averaging previous 10 IBI values.
QS : boolean that is made true whenever Pulse is found and BPM is updated. User must reset.
Pulse : boolean that is true when a heartbeat is sensed then false in time with pin13 LED going out.
This code is designed with output serial data to Processing sketch "PulseSensorAmped_Processing-xx"
The Processing sketch is a simple data visualizer.
All the work to find the heartbeat and determine the heartrate happens in the code below.
Pin D7 LED (onboard LED) will blink with heartbeat.
If you want to use pin D7 for something else, specifiy different pin in Interrupt.h
It will also fade an LED on pin fadePin with every beat. Put an LED and series resistor from fadePin to GND.
Check here for detailed code walkthrough:
http://pulsesensor.myshopify.com/pages/pulse-sensor-amped-arduino-v1dot1
Code Version 1.2 by Joel Murphy & Yury Gitman Spring 2013
This update fixes the firstBeat and secondBeat flag usage so that realistic BPM is reported.
>>> Adapted for Spark Core by Paul Kourany, May 2014 <<<
*/
#include "SparkIntervalTimer.h"
#include "SFE_LSM9DS0.h"
void interruptSetup(void);
extern int pulsePin;
extern int blinkPin;
extern int BPM;
extern int Signal;
extern int IBI;
extern volatile boolean Pulse;
extern volatile boolean QS;
char latest_results[128];
extern int fadePin;
extern int fadeRate;
#define LSM9DS0_XM 0x1D // Would be 0x1E if SDO_XM is LOW
#define LSM9DS0_G 0x6B // Would be 0x6A if SDO_G is LOW
#define PRINT_CALCULATED
#define DELAY 100
LSM9DS0 dof(MODE_I2C, LSM9DS0_G, LSM9DS0_XM);
void setup() {
Serial.println("Hi");
pinMode(blinkPin,OUTPUT); // pin that will blink to your heartbeat!
pinMode(fadePin,OUTPUT); // pin that will fade to your heartbeat!
Serial.begin(115200); // we agree to talk fast!
uint16_t status = dof.begin();
interruptSetup(); // sets up to read Pulse Sensor signal every 2mS
}
void loop() {
Serial.println("Here");
Signal = (int) map(Signal, 0, 4096, 0, 1023); // The processing sketch expects values from 0-1023, so map it to be compatible with the arduino code
sendDataToProcessing('S', Signal); // send Processing the raw Pulse Sensor data
if (QS == true) { // Quantified Self flag is true when arduino finds a heartbeat
fadeRate = 255; // Set 'fadeRate' Variable to 255 to fade LED with pulse
sendDataToProcessing('B',BPM); // send heart rate with a 'B' prefix
sendDataToProcessing('Q',IBI); // send time between beats with a 'Q' prefix
QS = false; // reset the Quantified Self flag for next time
}
printGyro(); // Print "G: gx, gy, gz"
printAccel(); // Print "A: ax, ay, az"
printMag(); // Print "M: mx, my, mz"
sprintf(latest_results,"{ \"Signal\": %u, \"BPM\": %u, \"IBI\" : %u }", Signal, BPM, IBI);
Spark.variable("results", latest_results, STRING);
ledFadeToBeat();
delay(DELAY); // take a break
}
void ledFadeToBeat() {
fadeRate -= 15; // set LED fade value
fadeRate = constrain(fadeRate,0,255); // keep LED fade value from going into negative numbers!
analogWrite(fadePin,fadeRate); // fade LED
}
void sendDataToProcessing(char symbol, int data) {
Serial.print(symbol); // symbol prefix tells Processing what type of data is coming
Serial.println(data); // the data to send culminating in a carriage return
}
void printGyro()
{
// To read from the gyroscope, you must first call the
// readGyro() function. When this exits, it'll update the
// gx, gy, and gz variables with the most current data.
dof.readGyro();
// Now we can use the gx, gy, and gz variables as we please.
// Either print them as raw ADC values, or calculated in DPS.
Serial.print("G: ");
#ifdef PRINT_CALCULATED
// If you want to print calculated values, you can use the
// calcGyro helper function to convert a raw ADC value to
// DPS. Give the function the value that you want to convert.
Serial.print(dof.calcGyro(dof.gx), 2);
Serial.print(", ");
Serial.print(dof.calcGyro(dof.gy), 2);
Serial.print(", ");
Serial.println(dof.calcGyro(dof.gz), 2);
#elif defined PRINT_RAW
Serial.print(dof.gx);
Serial.print(", ");
Serial.print(dof.gy);
Serial.print(", ");
Serial.println(dof.gz);
#endif
}
void printAccel()
{
// To read from the accelerometer, you must first call the
// readAccel() function. When this exits, it'll update the
// ax, ay, and az variables with the most current data.
dof.readAccel();
// Now we can use the ax, ay, and az variables as we please.
// Either print them as raw ADC values, or calculated in g's.
Serial.print("A: ");
#ifdef PRINT_CALCULATED
// If you want to print calculated values, you can use the
// calcAccel helper function to convert a raw ADC value to
// g's. Give the function the value that you want to convert.
Serial.print(dof.calcAccel(dof.ax), 2);
Serial.print(", ");
Serial.print(dof.calcAccel(dof.ay), 2);
Serial.print(", ");
Serial.println(dof.calcAccel(dof.az), 2);
#elif defined PRINT_RAW
Serial.print(dof.ax);
Serial.print(", ");
Serial.print(dof.ay);
Serial.print(", ");
Serial.println(dof.az);
#endif
}
void printMag()
{
// To read from the magnetometer, you must first call the
// readMag() function. When this exits, it'll update the
// mx, my, and mz variables with the most current data.
dof.readMag();
// Now we can use the mx, my, and mz variables as we please.
// Either print them as raw ADC values, or calculated in Gauss.
Serial.print("M: ");
#ifdef PRINT_CALCULATED
// If you want to print calculated values, you can use the
// calcMag helper function to convert a raw ADC value to
// Gauss. Give the function the value that you want to convert.
Serial.print(dof.calcMag(dof.mx), 2);
Serial.print(", ");
Serial.print(dof.calcMag(dof.my), 2);
Serial.print(", ");
Serial.println(dof.calcMag(dof.mz), 2);
#elif defined PRINT_RAW
Serial.print(dof.mx);
Serial.print(", ");
Serial.print(dof.my);
Serial.print(", ");
Serial.println(dof.mz);
#endif
}
#include "application.h"
#include "PulseSensor_Spark.h"
void interruptSetup(void){
// Allocate a timer to throw an interrupt every 2mS.
pulseTimer.begin(pulseISR, 2000, uSec); // blinkLED to run every 2ms (2000 * 1us period)
/*
TCCR2A = 0x02; // DISABLE PWM ON DIGITAL PINS 3 AND 11, AND GO INTO CTC MODE
TCCR2B = 0x06; // DON'T FORCE COMPARE, 256 PRESCALER
OCR2A = 0X7C; // SET THE TOP OF THE COUNT TO 124 FOR 500Hz SAMPLE RATE
TIMSK2 = 0x02; // ENABLE INTERRUPT ON MATCH BETWEEN TIMER2 AND OCR2A
sei(); // MAKE SURE GLOBAL INTERRUPTS ARE ENABLED
*/
}
// THIS IS THE TIMER 2 INTERRUPT SERVICE ROUTINE.
// Timer 2 makes sure that we take a reading every 2 miliseconds
//ISR(TIMER2_COMPA_vect){ // triggered when Timer2 counts to 124
void pulseISR(void) {
// cli(); // disable interrupts while we do this
noInterrupts();
Signal = analogRead(pulsePin); // read the Pulse Sensor
sampleCounter += 2; // keep track of the time in mS with this variable
int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise
// find the peak and trough of the pulse wave
if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI
if (Signal < T){ // T is the trough
T = Signal; // keep track of lowest point in pulse wave
}
}
if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise
P = Signal; // P is the peak
} // keep track of highest point in pulse wave
// NOW IT'S TIME TO LOOK FOR THE HEART BEAT
// signal surges up in value every time there is a pulse
if (N > 250){ // avoid high frequency noise
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){
Pulse = true; // set the Pulse flag when we think there is a pulse
digitalWrite(blinkPin,HIGH); // turn on pin 13 LED
IBI = sampleCounter - lastBeatTime; // measure time between beats in mS
lastBeatTime = sampleCounter; // keep track of time for next pulse
if(secondBeat){ // if this is the second beat, if secondBeat == TRUE
secondBeat = false; // clear secondBeat flag
for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup
rate[i] = IBI;
}
}
if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE
firstBeat = false; // clear firstBeat flag
secondBeat = true; // set the second beat flag
interrupts();
// sei(); // enable interrupts again
return; // IBI value is unreliable so discard it
}
// keep a running total of the last 10 IBI values
uint16_t runningTotal = 0; // clear the runningTotal variable
for(int i=0; i<=8; i++){ // shift data in the rate array
rate[i] = rate[i+1]; // and drop the oldest IBI value
runningTotal += rate[i]; // add up the 9 oldest IBI values
}
rate[9] = IBI; // add the latest IBI to the rate array
runningTotal += rate[9]; // add the latest IBI to runningTotal
runningTotal /= 10; // average the last 10 IBI values
BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM!
QS = true; // set Quantified Self flag
// QS FLAG IS NOT CLEARED INSIDE THIS ISR
}
}
if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over
digitalWrite(blinkPin,LOW); // turn off pin 13 LED
Pulse = false; // reset the Pulse flag so we can do it again
amp = P - T; // get amplitude of the pulse wave
thresh = amp/2 + T; // set thresh at 50% of the amplitude
P = thresh; // reset these for next time
T = thresh;
}
if (N > 2500){ // if 2.5 seconds go by without a beat
thresh = 512; // set thresh default
P = 512; // set P default
T = 512; // set T default
lastBeatTime = sampleCounter; // bring the lastBeatTime up to date
firstBeat = true; // set these to avoid noise
secondBeat = false; // when we get the heartbeat back
}
interrupts();
// sei(); // enable interrupts when youre done!
}// end isr
#ifndef PULSE_INTERRUPT_H
#define PULSE_INTERRUPT_H
#include "application.h"
#include "SparkIntervalTimer.h"
volatile int rate[10]; // array to hold last ten IBI values
volatile unsigned long sampleCounter = 0; // used to determine pulse timing
volatile unsigned long lastBeatTime = 0; // used to find IBI
volatile int P = 2048; // used to find peak in pulse wave, seeded
volatile int T = 2048; // used to find trough in pulse wave, seeded
volatile int thresh = 2048; // used to find instant moment of heart beat, seeded
volatile int amp = 410; // used to hold amplitude of pulse waveform, seeded
volatile boolean firstBeat = true; // used to seed rate array so we startup with reasonable BPM
volatile boolean secondBeat = false; // used to seed rate array so we startup with reasonable BPM
// these variables are volatile because they are used during the interrupt service routine!
volatile int BPM; // used to hold the pulse rate
volatile int Signal; // holds the incoming raw data
volatile int IBI = 600; // holds the time between beats, must be seeded!
volatile boolean Pulse = false; // true when pulse wave is high, false when it's low
volatile boolean QS = false; // becomes true when Arduoino finds a beat.
// SparkIntervalTimer uses hardware timers that are otherwise allocated for PIN functions (ADC/PWM)
// The first allocated timer is TIMR2 which is assigned to A0 & A1 ADC/PWM channels
// So use A2 (though A0 & A1 ADC should still work) for pulse input, D7 (onboard LED) for blink LED and D6 for fancy LED
// VARIABLES
int pulsePin = A2; // Pulse Sensor purple wire connected to analog pin 2
int blinkPin = D7; // pin to blink led at each beat
int fadePin = D6; // pin to do fancy classy fading blink at each beat
int fadeRate = 0; // used to fade LED on with PWM on fadePin
void interruptSetup();
void pulseISR(void);
// Setup a 2ms h/w timer
//extern IntervalTimer pulseTimer;
IntervalTimer pulseTimer;
#endif
/******************************************************************************
SFE_LSM9DS0.cpp
SFE_LSM9DS0 Library Source File
Jim Lindblom @ SparkFun Electronics
Original Creation Date: February 14, 2014 (Happy Valentines Day!)
https://github.com/sparkfun/LSM9DS0_Breakout
This file implements all functions of the LSM9DS0 class. Functions here range
from higher level stuff, like reading/writing LSM9DS0 registers to low-level,
hardware reads and writes. Both SPI and I2C handler functions can be found
towards the bottom of this file.
Development environment specifics:
IDE: Arduino 1.0.5
Hardware Platform: Arduino Pro 3.3V/8MHz
LSM9DS0 Breakout Version: 1.0
This code is beerware; if you see me (or any other SparkFun employee) at the
local, and you've found our code helpful, please buy us a round!
Distributed as-is; no warranty is given.
******************************************************************************/
#include "SFE_LSM9DS0.h"
//#include <Wire.h> // Wire library is used for I2C
//#include <SPI.h> // SPI library is used for...SPI.
//#if defined(ARDUINO) && ARDUINO >= 100
// #include "Arduino.h"
//#else
// #include "WProgram.h"
//#endif
LSM9DS0::LSM9DS0(interface_mode interface, uint8_t gAddr, uint8_t xmAddr)
{
// interfaceMode will keep track of whether we're using SPI or I2C:
interfaceMode = interface;
// xmAddress and gAddress will store the 7-bit I2C address, if using I2C.
// If we're using SPI, these variables store the chip-select pins.
xmAddress = xmAddr;
gAddress = gAddr;
}
uint16_t LSM9DS0::begin(gyro_scale gScl, accel_scale aScl, mag_scale mScl,
gyro_odr gODR, accel_odr aODR, mag_odr mODR)
{
// Store the given scales in class variables. These scale variables
// are used throughout to calculate the actual g's, DPS,and Gs's.
gScale = gScl;
aScale = aScl;
mScale = mScl;
// Once we have the scale values, we can calculate the resolution
// of each sensor. That's what these functions are for. One for each sensor
calcgRes(); // Calculate DPS / ADC tick, stored in gRes variable
calcmRes(); // Calculate Gs / ADC tick, stored in mRes variable
calcaRes(); // Calculate g / ADC tick, stored in aRes variable
// Now, initialize our hardware interface.
if (interfaceMode == MODE_I2C) // If we're using I2C
initI2C(); // Initialize I2C
else if (interfaceMode == MODE_SPI) // else, if we're using SPI
initSPI(); // Initialize SPI
// To verify communication, we can read from the WHO_AM_I register of
// each device. Store those in a variable so we can return them.
uint8_t gTest = gReadByte(WHO_AM_I_G); // Read the gyro WHO_AM_I
uint8_t xmTest = xmReadByte(WHO_AM_I_XM); // Read the accel/mag WHO_AM_I
// Gyro initialization stuff:
initGyro(); // This will "turn on" the gyro. Setting up interrupts, etc.
setGyroODR(gODR); // Set the gyro output data rate and bandwidth.
setGyroScale(gScale); // Set the gyro range
// Accelerometer initialization stuff:
initAccel(); // "Turn on" all axes of the accel. Set up interrupts, etc.
setAccelODR(aODR); // Set the accel data rate.
setAccelScale(aScale); // Set the accel range.
// Magnetometer initialization stuff:
initMag(); // "Turn on" all axes of the mag. Set up interrupts, etc.
setMagODR(mODR); // Set the magnetometer output data rate.
setMagScale(mScale); // Set the magnetometer's range.
// Once everything is initialized, return the WHO_AM_I registers we read:
return (xmTest << 8) | gTest;
}
void LSM9DS0::initGyro()
{
/* CTRL_REG1_G sets output data rate, bandwidth, power-down and enables
Bits[7:0]: DR1 DR0 BW1 BW0 PD Zen Xen Yen
DR[1:0] - Output data rate selection
00=95Hz, 01=190Hz, 10=380Hz, 11=760Hz
BW[1:0] - Bandwidth selection (sets cutoff frequency)
Value depends on ODR. See datasheet table 21.
PD - Power down enable (0=power down mode, 1=normal or sleep mode)
Zen, Xen, Yen - Axis enable (o=disabled, 1=enabled) */
gWriteByte(CTRL_REG1_G, 0x0F); // Normal mode, enable all axes
/* CTRL_REG2_G sets up the HPF
Bits[7:0]: 0 0 HPM1 HPM0 HPCF3 HPCF2 HPCF1 HPCF0
HPM[1:0] - High pass filter mode selection
00=normal (reset reading HP_RESET_FILTER, 01=ref signal for filtering,
10=normal, 11=autoreset on interrupt
HPCF[3:0] - High pass filter cutoff frequency
Value depends on data rate. See datasheet table 26.
*/
gWriteByte(CTRL_REG2_G, 0x00); // Normal mode, high cutoff frequency
/* CTRL_REG3_G sets up interrupt and DRDY_G pins
Bits[7:0]: I1_IINT1 I1_BOOT H_LACTIVE PP_OD I2_DRDY I2_WTM I2_ORUN I2_EMPTY
I1_INT1 - Interrupt enable on INT_G pin (0=disable, 1=enable)
I1_BOOT - Boot status available on INT_G (0=disable, 1=enable)
H_LACTIVE - Interrupt active configuration on INT_G (0:high, 1:low)
PP_OD - Push-pull/open-drain (0=push-pull, 1=open-drain)
I2_DRDY - Data ready on DRDY_G (0=disable, 1=enable)
I2_WTM - FIFO watermark interrupt on DRDY_G (0=disable 1=enable)
I2_ORUN - FIFO overrun interrupt on DRDY_G (0=disable 1=enable)
I2_EMPTY - FIFO empty interrupt on DRDY_G (0=disable 1=enable) */
// Int1 enabled (pp, active low), data read on DRDY_G:
gWriteByte(CTRL_REG3_G, 0x88);
/* CTRL_REG4_G sets the scale, update mode
Bits[7:0] - BDU BLE FS1 FS0 - ST1 ST0 SIM
BDU - Block data update (0=continuous, 1=output not updated until read
BLE - Big/little endian (0=data LSB @ lower address, 1=LSB @ higher add)
FS[1:0] - Full-scale selection
00=245dps, 01=500dps, 10=2000dps, 11=2000dps
ST[1:0] - Self-test enable
00=disabled, 01=st 0 (x+, y-, z-), 10=undefined, 11=st 1 (x-, y+, z+)
SIM - SPI serial interface mode select
0=4 wire, 1=3 wire */
gWriteByte(CTRL_REG4_G, 0x00); // Set scale to 245 dps
/* CTRL_REG5_G sets up the FIFO, HPF, and INT1
Bits[7:0] - BOOT FIFO_EN - HPen INT1_Sel1 INT1_Sel0 Out_Sel1 Out_Sel0
BOOT - Reboot memory content (0=normal, 1=reboot)
FIFO_EN - FIFO enable (0=disable, 1=enable)
HPen - HPF enable (0=disable, 1=enable)
INT1_Sel[1:0] - Int 1 selection configuration
Out_Sel[1:0] - Out selection configuration */
gWriteByte(CTRL_REG5_G, 0x00);
// Temporary !!! For testing !!! Remove !!! Or make useful !!!
configGyroInt(0x2A, 0, 0, 0, 0); // Trigger interrupt when above 0 DPS...
}
void LSM9DS0::initAccel()
{
/* CTRL_REG0_XM (0x1F) (Default value: 0x00)
Bits (7-0): BOOT FIFO_EN WTM_EN 0 0 HP_CLICK HPIS1 HPIS2
BOOT - Reboot memory content (0: normal, 1: reboot)
FIFO_EN - Fifo enable (0: disable, 1: enable)
WTM_EN - FIFO watermark enable (0: disable, 1: enable)
HP_CLICK - HPF enabled for click (0: filter bypassed, 1: enabled)
HPIS1 - HPF enabled for interrupt generator 1 (0: bypassed, 1: enabled)
HPIS2 - HPF enabled for interrupt generator 2 (0: bypassed, 1 enabled) */
xmWriteByte(CTRL_REG0_XM, 0x00);
/* CTRL_REG1_XM (0x20) (Default value: 0x07)
Bits (7-0): AODR3 AODR2 AODR1 AODR0 BDU AZEN AYEN AXEN
AODR[3:0] - select the acceleration data rate:
0000=power down, 0001=3.125Hz, 0010=6.25Hz, 0011=12.5Hz,
0100=25Hz, 0101=50Hz, 0110=100Hz, 0111=200Hz, 1000=400Hz,
1001=800Hz, 1010=1600Hz, (remaining combinations undefined).
BDU - block data update for accel AND mag
0: Continuous update
1: Output registers aren't updated until MSB and LSB have been read.
AZEN, AYEN, and AXEN - Acceleration x/y/z-axis enabled.
0: Axis disabled, 1: Axis enabled */
xmWriteByte(CTRL_REG1_XM, 0x57); // 100Hz data rate, x/y/z all enabled
//Serial.println(xmReadByte(CTRL_REG1_XM));
/* CTRL_REG2_XM (0x21) (Default value: 0x00)
Bits (7-0): ABW1 ABW0 AFS2 AFS1 AFS0 AST1 AST0 SIM
ABW[1:0] - Accelerometer anti-alias filter bandwidth
00=773Hz, 01=194Hz, 10=362Hz, 11=50Hz
AFS[2:0] - Accel full-scale selection
000=+/-2g, 001=+/-4g, 010=+/-6g, 011=+/-8g, 100=+/-16g
AST[1:0] - Accel self-test enable
00=normal (no self-test), 01=positive st, 10=negative st, 11=not allowed
SIM - SPI mode selection
0=4-wire, 1=3-wire */
xmWriteByte(CTRL_REG2_XM, 0x00); // Set scale to 2g
/* CTRL_REG3_XM is used to set interrupt generators on INT1_XM
Bits (7-0): P1_BOOT P1_TAP P1_INT1 P1_INT2 P1_INTM P1_DRDYA P1_DRDYM P1_EMPTY
*/
// Accelerometer data ready on INT1_XM (0x04)
xmWriteByte(CTRL_REG3_XM, 0x04);
}
void LSM9DS0::initMag()
{
/* CTRL_REG5_XM enables temp sensor, sets mag resolution and data rate
Bits (7-0): TEMP_EN M_RES1 M_RES0 M_ODR2 M_ODR1 M_ODR0 LIR2 LIR1
TEMP_EN - Enable temperature sensor (0=disabled, 1=enabled)
M_RES[1:0] - Magnetometer resolution select (0=low, 3=high)
M_ODR[2:0] - Magnetometer data rate select
000=3.125Hz, 001=6.25Hz, 010=12.5Hz, 011=25Hz, 100=50Hz, 101=100Hz
LIR2 - Latch interrupt request on INT2_SRC (cleared by reading INT2_SRC)
0=interrupt request not latched, 1=interrupt request latched
LIR1 - Latch interrupt request on INT1_SRC (cleared by readging INT1_SRC)
0=irq not latched, 1=irq latched */
xmWriteByte(CTRL_REG5_XM, 0x14); // Mag data rate - 100 Hz
/* CTRL_REG6_XM sets the magnetometer full-scale
Bits (7-0): 0 MFS1 MFS0 0 0 0 0 0
MFS[1:0] - Magnetic full-scale selection
00:+/-2Gauss, 01:+/-4Gs, 10:+/-8Gs, 11:+/-12Gs */
xmWriteByte(CTRL_REG6_XM, 0x00); // Mag scale to +/- 2GS
/* CTRL_REG7_XM sets magnetic sensor mode, low power mode, and filters
AHPM1 AHPM0 AFDS 0 0 MLP MD1 MD0
AHPM[1:0] - HPF mode selection
00=normal (resets reference registers), 01=reference signal for filtering,
10=normal, 11=autoreset on interrupt event
AFDS - Filtered acceleration data selection
0=internal filter bypassed, 1=data from internal filter sent to FIFO
MLP - Magnetic data low-power mode
0=data rate is set by M_ODR bits in CTRL_REG5
1=data rate is set to 3.125Hz
MD[1:0] - Magnetic sensor mode selection (default 10)
00=continuous-conversion, 01=single-conversion, 10 and 11=power-down */
xmWriteByte(CTRL_REG7_XM, 0x00); // Continuous conversion mode
/* CTRL_REG4_XM is used to set interrupt generators on INT2_XM
Bits (7-0): P2_TAP P2_INT1 P2_INT2 P2_INTM P2_DRDYA P2_DRDYM P2_Overrun P2_WTM
*/
xmWriteByte(CTRL_REG4_XM, 0x04); // Magnetometer data ready on INT2_XM (0x08)
/* INT_CTRL_REG_M to set push-pull/open drain, and active-low/high
Bits[7:0] - XMIEN YMIEN ZMIEN PP_OD IEA IEL 4D MIEN
XMIEN, YMIEN, ZMIEN - Enable interrupt recognition on axis for mag data
PP_OD - Push-pull/open-drain interrupt configuration (0=push-pull, 1=od)
IEA - Interrupt polarity for accel and magneto
0=active-low, 1=active-high
IEL - Latch interrupt request for accel and magneto
0=irq not latched, 1=irq latched
4D - 4D enable. 4D detection is enabled when 6D bit in INT_GEN1_REG is set
MIEN - Enable interrupt generation for magnetic data
0=disable, 1=enable) */
xmWriteByte(INT_CTRL_REG_M, 0x09); // Enable interrupts for mag, active-low, push-pull
}
void LSM9DS0::readAccel()
{
uint8_t temp[6]; // We'll read six bytes from the accelerometer into temp
xmReadBytes(OUT_X_L_A, temp, 6); // Read 6 bytes, beginning at OUT_X_L_A
ax = (temp[1] << 8) | temp[0]; // Store x-axis values into ax
ay = (temp[3] << 8) | temp[2]; // Store y-axis values into ay
az = (temp[5] << 8) | temp[4]; // Store z-axis values into az
}
void LSM9DS0::readMag()
{
uint8_t temp[6]; // We'll read six bytes from the mag into temp
xmReadBytes(OUT_X_L_M, temp, 6); // Read 6 bytes, beginning at OUT_X_L_M
mx = (temp[1] << 8) | temp[0]; // Store x-axis values into mx
my = (temp[3] << 8) | temp[2]; // Store y-axis values into my
mz = (temp[5] << 8) | temp[4]; // Store z-axis values into mz
}
void LSM9DS0::readGyro()
{
uint8_t temp[6]; // We'll read six bytes from the gyro into temp
gReadBytes(OUT_X_L_G, temp, 6); // Read 6 bytes, beginning at OUT_X_L_G
gx = (temp[1] << 8) | temp[0]; // Store x-axis values into gx
gy = (temp[3] << 8) | temp[2]; // Store y-axis values into gy
gz = (temp[5] << 8) | temp[4]; // Store z-axis values into gz
}
float LSM9DS0::calcGyro(int16_t gyro)
{
// Return the gyro raw reading times our pre-calculated DPS / (ADC tick):
return gRes * gyro;
}
float LSM9DS0::calcAccel(int16_t accel)
{
// Return the accel raw reading times our pre-calculated g's / (ADC tick):
return aRes * accel;
}
float LSM9DS0::calcMag(int16_t mag)
{
// Return the mag raw reading times our pre-calculated Gs / (ADC tick):
return mRes * mag;
}
void LSM9DS0::setGyroScale(gyro_scale gScl)
{
// We need to preserve the other bytes in CTRL_REG4_G. So, first read it:
uint8_t temp = gReadByte(CTRL_REG4_G);
// Then mask out the gyro scale bits:
temp &= 0xFF^(0x3 << 4);
// Then shift in our new scale bits:
temp |= gScl << 4;
// And write the new register value back into CTRL_REG4_G:
gWriteByte(CTRL_REG4_G, temp);
// We've updated the sensor, but we also need to update our class variables
// First update gScale:
gScale = gScl;
// Then calculate a new gRes, which relies on gScale being set correctly:
calcgRes();
}
void LSM9DS0::setAccelScale(accel_scale aScl)
{
// We need to preserve the other bytes in CTRL_REG2_XM. So, first read it:
uint8_t temp = xmReadByte(CTRL_REG2_XM);
// Then mask out the accel scale bits:
temp &= 0xFF^(0x3 << 3);
// Then shift in our new scale bits:
temp |= aScl << 3;
// And write the new register value back into CTRL_REG2_XM:
xmWriteByte(CTRL_REG2_XM, temp);
// We've updated the sensor, but we also need to update our class variables
// First update aScale:
aScale = aScl;
// Then calculate a new aRes, which relies on aScale being set correctly:
calcaRes();
}
void LSM9DS0::setMagScale(mag_scale mScl)
{
// We need to preserve the other bytes in CTRL_REG6_XM. So, first read it:
uint8_t temp = xmReadByte(CTRL_REG6_XM);
// Then mask out the mag scale bits:
temp &= 0xFF^(0x3 << 5);
// Then shift in our new scale bits:
temp |= mScl << 5;
// And write the new register value back into CTRL_REG6_XM:
xmWriteByte(CTRL_REG6_XM, temp);
// We've updated the sensor, but we also need to update our class variables
// First update mScale:
mScale = mScl;
// Then calculate a new mRes, which relies on mScale being set correctly:
calcmRes();
}
void LSM9DS0::setGyroODR(gyro_odr gRate)
{
// We need to preserve the other bytes in CTRL_REG1_G. So, first read it:
uint8_t temp = gReadByte(CTRL_REG1_G);
// Then mask out the gyro ODR bits:
temp &= 0xFF^(0xF << 4);
// Then shift in our new ODR bits:
temp |= (gRate << 4);
// And write the new register value back into CTRL_REG1_G:
gWriteByte(CTRL_REG1_G, temp);
}
void LSM9DS0::setAccelODR(accel_odr aRate)
{
// We need to preserve the other bytes in CTRL_REG1_XM. So, first read it:
uint8_t temp = xmReadByte(CTRL_REG1_XM);
// Then mask out the accel ODR bits:
temp &= 0xFF^(0xF << 4);
// Then shift in our new ODR bits:
temp |= (aRate << 4);
// And write the new register value back into CTRL_REG1_XM:
xmWriteByte(CTRL_REG1_XM, temp);
}
void LSM9DS0::setMagODR(mag_odr mRate)
{
// We need to preserve the other bytes in CTRL_REG5_XM. So, first read it:
uint8_t temp = xmReadByte(CTRL_REG5_XM);
// Then mask out the mag ODR bits:
temp &= 0xFF^(0x7 << 2);
// Then shift in our new ODR bits:
temp |= (mRate << 2);
// And write the new register value back into CTRL_REG5_XM:
xmWriteByte(CTRL_REG5_XM, temp);
}
void LSM9DS0::configGyroInt(uint8_t int1Cfg, uint16_t int1ThsX, uint16_t int1ThsY, uint16_t int1ThsZ, uint8_t duration)
{
gWriteByte(INT1_CFG_G, int1Cfg);
gWriteByte(INT1_THS_XH_G, (int1ThsX & 0xFF00) >> 8);
gWriteByte(INT1_THS_XL_G, (int1ThsX & 0xFF));
gWriteByte(INT1_THS_YH_G, (int1ThsY & 0xFF00) >> 8);
gWriteByte(INT1_THS_YL_G, (int1ThsY & 0xFF));
gWriteByte(INT1_THS_ZH_G, (int1ThsZ & 0xFF00) >> 8);
gWriteByte(INT1_THS_ZL_G, (int1ThsZ & 0xFF));
if (duration)
gWriteByte(INT1_DURATION_G, 0x80 | duration);
else
gWriteByte(INT1_DURATION_G, 0x00);
}
void LSM9DS0::calcgRes()
{
// Possible gyro scales (and their register bit settings) are:
// 245 DPS (00), 500 DPS (01), 2000 DPS (10). Here's a bit of an algorithm
// to calculate DPS/(ADC tick) based on that 2-bit value:
switch (gScale)
{
case G_SCALE_245DPS:
gRes = 245.0 / 32768.0;
break;
case G_SCALE_500DPS:
gRes = 500.0 / 32768.0;
break;
case G_SCALE_2000DPS:
gRes = 2000.0 / 32768.0;
break;
}
}
void LSM9DS0::calcaRes()
{
// Possible accelerometer scales (and their register bit settings) are:
// 2 g (000), 4g (001), 6g (010) 8g (011), 16g (100). Here's a bit of an
// algorithm to calculate g/(ADC tick) based on that 3-bit value:
aRes = aScale == A_SCALE_16G ? 16.0 / 32768.0 :
(((float) aScale + 1.0) * 2.0) / 32768.0;
}
void LSM9DS0::calcmRes()
{
// Possible magnetometer scales (and their register bit settings) are:
// 2 Gs (00), 4 Gs (01), 8 Gs (10) 12 Gs (11). Here's a bit of an algorithm
// to calculate Gs/(ADC tick) based on that 2-bit value:
mRes = mScale == M_SCALE_2GS ? 2.0 / 32768.0 :
(float) (mScale << 2) / 32768.0;
}
void LSM9DS0::gWriteByte(uint8_t subAddress, uint8_t data)
{
// Whether we're using I2C or SPI, write a byte using the
// gyro-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
I2CwriteByte(gAddress, subAddress, data);
else if (interfaceMode == MODE_SPI)
SPIwriteByte(gAddress, subAddress, data);
}
void LSM9DS0::xmWriteByte(uint8_t subAddress, uint8_t data)
{
// Whether we're using I2C or SPI, write a byte using the
// accelerometer-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
return I2CwriteByte(xmAddress, subAddress, data);
else if (interfaceMode == MODE_SPI)
return SPIwriteByte(xmAddress, subAddress, data);
}
uint8_t LSM9DS0::gReadByte(uint8_t subAddress)
{
// Whether we're using I2C or SPI, read a byte using the
// gyro-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
return I2CreadByte(gAddress, subAddress);
else if (interfaceMode == MODE_SPI)
return SPIreadByte(gAddress, subAddress);
}
void LSM9DS0::gReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
{
// Whether we're using I2C or SPI, read multiple bytes using the
// gyro-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
I2CreadBytes(gAddress, subAddress, dest, count);
else if (interfaceMode == MODE_SPI)
SPIreadBytes(gAddress, subAddress, dest, count);
}
uint8_t LSM9DS0::xmReadByte(uint8_t subAddress)
{
// Whether we're using I2C or SPI, read a byte using the
// accelerometer-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
return I2CreadByte(xmAddress, subAddress);
else if (interfaceMode == MODE_SPI)
return SPIreadByte(xmAddress, subAddress);
}
void LSM9DS0::xmReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count)
{
// Whether we're using I2C or SPI, read multiple bytes using the
// accelerometer-specific I2C address or SPI CS pin.
if (interfaceMode == MODE_I2C)
I2CreadBytes(xmAddress, subAddress, dest, count);
else if (interfaceMode == MODE_SPI)
SPIreadBytes(xmAddress, subAddress, dest, count);
}
void LSM9DS0::initSPI()
{
pinMode(gAddress, OUTPUT);
digitalWrite(gAddress, HIGH);
pinMode(xmAddress, OUTPUT);
digitalWrite(xmAddress, HIGH);
SPI.begin();
// Maximum SPI frequency is 10MHz, could divide by 2 here:
SPI.setClockDivider(SPI_CLOCK_DIV4);
// Data is read and written MSb first.
SPI.setBitOrder(MSBFIRST);
// Data is captured on rising edge of clock (CPHA = 0)
// Base value of the clock is HIGH (CPOL = 1)
SPI.setDataMode(SPI_MODE1);
}
void LSM9DS0::SPIwriteByte(uint8_t csPin, uint8_t subAddress, uint8_t data)
{
digitalWrite(csPin, LOW); // Initiate communication
// If write, bit 0 (MSB) should be 0
// If single write, bit 1 should be 0
SPI.transfer(subAddress & 0x3F); // Send Address
SPI.transfer(data); // Send data
digitalWrite(csPin, HIGH); // Close communication
}
uint8_t LSM9DS0::SPIreadByte(uint8_t csPin, uint8_t subAddress)
{
uint8_t temp;
// Use the multiple read function to read 1 byte.
// Value is returned to `temp`.
SPIreadBytes(csPin, subAddress, &temp, 1);
return temp;
}
void LSM9DS0::SPIreadBytes(uint8_t csPin, uint8_t subAddress,
uint8_t * dest, uint8_t count)
{
digitalWrite(csPin, LOW); // Initiate communication
// To indicate a read, set bit 0 (msb) to 1
// If we're reading multiple bytes, set bit 1 to 1
// The remaining six bytes are the address to be read
if (count > 1)
SPI.transfer(0xC0 | (subAddress & 0x3F));
else
SPI.transfer(0x80 | (subAddress & 0x3F));
for (int i=0; i<count; i++)
{
dest[i] = SPI.transfer(0x00); // Read into destination array
}
digitalWrite(csPin, HIGH); // Close communication
}
void LSM9DS0::initI2C()
{
Wire.begin(); // Initialize I2C library
}
void LSM9DS0::I2CwriteByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
// Begin transmission at device write address
Wire.beginTransmission(address);
Wire.write(subAddress); // Write register to be written to
Wire.write(data); // Transmit byte to write
Wire.endTransmission(); // End I2C transmission
}
uint8_t LSM9DS0::I2CreadByte(uint8_t address, uint8_t subAddress)
{
uint8_t data; // `data` will store the register data
// Begin I2C transmission using device write address
Wire.beginTransmission(address);
// Write the register to be read:
Wire.write(subAddress);
// End write, but send a restart to keep connection alive:
Wire.endTransmission(false);
// Transmit device read address:
Wire.requestFrom(address, (uint8_t) 1);
while (Wire.available() < 1) // Wait until data becomes available
;
data = Wire.read(); // Read register data into `data` variable
Wire.endTransmission(); // End I2C transmission
return data; // Return data from register
}
void LSM9DS0::I2CreadBytes(uint8_t address, uint8_t subAddress, uint8_t * dest,
uint8_t count)
{
// Begin I2C transmission and send device address
Wire.beginTransmission(address);
// Next send the register to be read. OR with 0x80 to indicate multi-read.
Wire.write(subAddress | 0x80);
// End write, but send a restart to keep connection alive:
Wire.endTransmission(false);
// Request `count` bytes of data from the device
Wire.requestFrom(address, count);
// Wait until the data has been read in
while (Wire.available() < count)
;
// Store all `count` bytes into the given destination array.
for (int i=0; i<count ;i++)
dest[i] = Wire.read();
// End I2C Transmission
Wire.endTransmission();
}
/******************************************************************************
SFE_LSM9DS0.h
SFE_LSM9DS0 Library Header File
Jim Lindblom @ SparkFun Electronics
Original Creation Date: February 14, 2014 (Happy Valentines Day!)
https://github.com/sparkfun/LSM9DS0_Breakout
This file prototypes the LSM9DS0 class, implemented in SFE_LSM9DS0.cpp. In
addition, it defines every register in the LSM9DS0 (both the Gyro and Accel/
Magnetometer registers).
Development environment specifics:
IDE: Arduino 1.0.5
Hardware Platform: Arduino Pro 3.3V/8MHz
LSM9DS0 Breakout Version: 1.0
This code is beerware; if you see me (or any other SparkFun employee) at the
local, and you've found our code helpful, please buy us a round!
Distributed as-is; no warranty is given.
******************************************************************************/
#ifndef __SFE_LSM9DS0_H__
#define __SFE_LSM9DS0_H__
#include "application.h"
//#if defined(ARDUINO) && ARDUINO >= 100
// #include "Arduino.h"
//#else
// #include "WProgram.h"
// #include "pins_arduino.h"
//#endif
////////////////////////////
// LSM9DS0 Gyro Registers //
////////////////////////////
#define WHO_AM_I_G 0x0F
#define CTRL_REG1_G 0x20
#define CTRL_REG2_G 0x21
#define CTRL_REG3_G 0x22
#define CTRL_REG4_G 0x23
#define CTRL_REG5_G 0x24
#define REFERENCE_G 0x25
#define STATUS_REG_G 0x27
#define OUT_X_L_G 0x28
#define OUT_X_H_G 0x29
#define OUT_Y_L_G 0x2A
#define OUT_Y_H_G 0x2B
#define OUT_Z_L_G 0x2C
#define OUT_Z_H_G 0x2D
#define FIFO_CTRL_REG_G 0x2E
#define FIFO_SRC_REG_G 0x2F
#define INT1_CFG_G 0x30
#define INT1_SRC_G 0x31
#define INT1_THS_XH_G 0x32
#define INT1_THS_XL_G 0x33
#define INT1_THS_YH_G 0x34
#define INT1_THS_YL_G 0x35
#define INT1_THS_ZH_G 0x36
#define INT1_THS_ZL_G 0x37
#define INT1_DURATION_G 0x38
//////////////////////////////////////////
// LSM9DS0 Accel/Magneto (XM) Registers //
//////////////////////////////////////////
#define OUT_TEMP_L_XM 0x05
#define OUT_TEMP_H_XM 0x06
#define STATUS_REG_M 0x07
#define OUT_X_L_M 0x08
#define OUT_X_H_M 0x09
#define OUT_Y_L_M 0x0A
#define OUT_Y_H_M 0x0B
#define OUT_Z_L_M 0x0C
#define OUT_Z_H_M 0x0D
#define WHO_AM_I_XM 0x0F
#define INT_CTRL_REG_M 0x12
#define INT_SRC_REG_M 0x13
#define INT_THS_L_M 0x14
#define INT_THS_H_M 0x15
#define OFFSET_X_L_M 0x16
#define OFFSET_X_H_M 0x17
#define OFFSET_Y_L_M 0x18
#define OFFSET_Y_H_M 0x19
#define OFFSET_Z_L_M 0x1A
#define OFFSET_Z_H_M 0x1B
#define REFERENCE_X 0x1C
#define REFERENCE_Y 0x1D
#define REFERENCE_Z 0x1E
#define CTRL_REG0_XM 0x1F
#define CTRL_REG1_XM 0x20
#define CTRL_REG2_XM 0x21
#define CTRL_REG3_XM 0x22
#define CTRL_REG4_XM 0x23
#define CTRL_REG5_XM 0x24
#define CTRL_REG6_XM 0x25
#define CTRL_REG7_XM 0x26
#define STATUS_REG_A 0x27
#define OUT_X_L_A 0x28
#define OUT_X_H_A 0x29
#define OUT_Y_L_A 0x2A
#define OUT_Y_H_A 0x2B
#define OUT_Z_L_A 0x2C
#define OUT_Z_H_A 0x2D
#define FIFO_CTRL_REG 0x2E
#define FIFO_SRC_REG 0x2F
#define INT_GEN_1_REG 0x30
#define INT_GEN_1_SRC 0x31
#define INT_GEN_1_THS 0x32
#define INT_GEN_1_DURATION 0x33
#define INT_GEN_2_REG 0x34
#define INT_GEN_2_SRC 0x35
#define INT_GEN_2_THS 0x36
#define INT_GEN_2_DURATION 0x37
#define CLICK_CFG 0x38
#define CLICK_SRC 0x39
#define CLICK_THS 0x3A
#define TIME_LIMIT 0x3B
#define TIME_LATENCY 0x3C
#define TIME_WINDOW 0x3D
#define ACT_THS 0x3E
#define ACT_DUR 0x3F
// The LSM9DS0 functions over both I2C or SPI. This library supports both.
// But the interface mode used must be sent to the LSM9DS0 constructor. Use
// one of these two as the first parameter of the constructor.
enum interface_mode
{
MODE_SPI,
MODE_I2C,
};
class LSM9DS0
{
public:
// gyro_scale defines the possible full-scale ranges of the gyroscope:
enum gyro_scale
{
G_SCALE_245DPS, // 00: +/- 245 degrees per second
G_SCALE_500DPS, // 01: +/- 500 dps
G_SCALE_2000DPS, // 10: +/- 2000 dps
};
// accel_scale defines all possible FSR's of the accelerometer:
enum accel_scale
{
A_SCALE_2G, // 000: +/- 2g
A_SCALE_4G, // 001: +/- 4g
A_SCALE_6G, // 010: +/- 6g
A_SCALE_8G, // 011: +/- 8g
A_SCALE_16G // 100: +/- 16g
};
// mag_scale defines all possible FSR's of the magnetometer:
enum mag_scale
{
M_SCALE_2GS, // 00: +/- 2Gs
M_SCALE_4GS, // 01: +/- 4Gs
M_SCALE_8GS, // 10: +/- 8Gs
M_SCALE_12GS, // 11: +/- 12Gs
};
// gyro_odr defines all possible data rate/bandwidth combos of the gyro:
enum gyro_odr
{ // ODR (Hz) --- Cutoff
G_ODR_95_BW_125 = 0x0, // 95 12.5
G_ODR_95_BW_25 = 0x1, // 95 25
// 0x2 and 0x3 define the same data rate and bandwidth
G_ODR_190_BW_125 = 0x4, // 190 12.5
G_ODR_190_BW_25 = 0x5, // 190 25
G_ODR_190_BW_50 = 0x6, // 190 50
G_ODR_190_BW_70 = 0x7, // 190 70
G_ODR_380_BW_20 = 0x8, // 380 20
G_ODR_380_BW_25 = 0x9, // 380 25
G_ODR_380_BW_50 = 0xA, // 380 50
G_ODR_380_BW_100 = 0xB, // 380 100
G_ODR_760_BW_30 = 0xC, // 760 30
G_ODR_760_BW_35 = 0xD, // 760 35
G_ODR_760_BW_50 = 0xE, // 760 50
G_ODR_760_BW_100 = 0xF, // 760 100
};
// accel_oder defines all possible output data rates of the accelerometer:
enum accel_odr
{
A_POWER_DOWN, // Power-down mode (0x0)
A_ODR_3125, // 3.125 Hz (0x1)
A_ODR_625, // 6.25 Hz (0x2)
A_ODR_125, // 12.5 Hz (0x3)
A_ODR_25, // 25 Hz (0x4)
A_ODR_50, // 50 Hz (0x5)
A_ODR_100, // 100 Hz (0x6)
A_ODR_200, // 200 Hz (0x7)
A_ODR_400, // 400 Hz (0x8)
A_ODR_800, // 800 Hz (9)
A_ODR_1600 // 1600 Hz (0xA)
};
// accel_oder defines all possible output data rates of the magnetometer:
enum mag_odr
{
M_ODR_3125, // 3.125 Hz (0x00)
M_ODR_625, // 6.25 Hz (0x01)
M_ODR_125, // 12.5 Hz (0x02)
M_ODR_25, // 25 Hz (0x03)
M_ODR_50, // 50 (0x04)
M_ODR_100, // 100 Hz (0x05)
};
// We'll store the gyro, accel, and magnetometer readings in a series of
// public class variables. Each sensor gets three variables -- one for each
// axis. Call readGyro(), readAccel(), and readMag() first, before using
// these variables!
// These values are the RAW signed 16-bit readings from the sensors.
int16_t gx, gy, gz; // x, y, and z axis readings of the gyroscope
int16_t ax, ay, az; // x, y, and z axis readings of the accelerometer
int16_t mx, my, mz; // x, y, and z axis readings of the magnetometer
// LSM9DS0 -- LSM9DS0 class constructor
// The constructor will set up a handful of private variables, and set the
// communication mode as well.
// Input:
// - interface = Either MODE_SPI or MODE_I2C, whichever you're using
// to talk to the IC.
// - gAddr = If MODE_I2C, this is the I2C address of the gyroscope.
// If MODE_SPI, this is the chip select pin of the gyro (CSG)
// - xmAddr = If MODE_I2C, this is the I2C address of the accel/mag.
// If MODE_SPI, this is the cs pin of the accel/mag (CSXM)
LSM9DS0(interface_mode interface, uint8_t gAddr, uint8_t xmAddr);
// begin() -- Initialize the gyro, accelerometer, and magnetometer.
// This will set up the scale and output rate of each sensor. It'll also
// "turn on" every sensor and every axis of every sensor.
// Input:
// - gScl = The scale of the gyroscope. This should be a gyro_scale value.
// - aScl = The scale of the accelerometer. Should be a accel_scale value.
// - mScl = The scale of the magnetometer. Should be a mag_scale value.
// - gODR = Output data rate of the gyroscope. gyro_odr value.
// - aODR = Output data rate of the accelerometer. accel_odr value.
// - mODR = Output data rate of the magnetometer. mag_odr value.
// Output: The function will return an unsigned 16-bit value. The most-sig
// bytes of the output are the WHO_AM_I reading of the accel. The
// least significant two bytes are the WHO_AM_I reading of the gyro.
// All parameters have a defaulted value, so you can call just "begin()".
// Default values are FSR's of: +/- 245DPS, 2g, 2Gs; ODRs of 95 Hz for
// gyro, 100 Hz for accelerometer, 100 Hz for magnetometer.
// Use the return value of this function to verify communication.
uint16_t begin(gyro_scale gScl = G_SCALE_245DPS,
accel_scale aScl = A_SCALE_2G, mag_scale mScl = M_SCALE_2GS,
gyro_odr gODR = G_ODR_95_BW_125, accel_odr aODR = A_ODR_50,
mag_odr mODR = M_ODR_50);
// readGyro() -- Read the gyroscope output registers.
// This function will read all six gyroscope output registers.
// The readings are stored in the class' gx, gy, and gz variables. Read
// those _after_ calling readGyro().
void readGyro();
// readAccel() -- Read the accelerometer output registers.
// This function will read all six accelerometer output registers.
// The readings are stored in the class' ax, ay, and az variables. Read
// those _after_ calling readAccel().
void readAccel();
// readMag() -- Read the magnetometer output registers.
// This function will read all six magnetometer output registers.
// The readings are stored in the class' mx, my, and mz variables. Read
// those _after_ calling readMag().
void readMag();
// calcGyro() -- Convert from RAW signed 16-bit value to degrees per second
// This function reads in a signed 16-bit value and returns the scaled
// DPS. This function relies on gScale and gRes being correct.
// Input:
// - gyro = A signed 16-bit raw reading from the gyroscope.
float calcGyro(int16_t gyro);
// calcAccel() -- Convert from RAW signed 16-bit value to gravity (g's).
// This function reads in a signed 16-bit value and returns the scaled
// g's. This function relies on aScale and aRes being correct.
// Input:
// - accel = A signed 16-bit raw reading from the accelerometer.
float calcAccel(int16_t accel);
// calcMag() -- Convert from RAW signed 16-bit value to Gauss (Gs)
// This function reads in a signed 16-bit value and returns the scaled
// Gs. This function relies on mScale and mRes being correct.
// Input:
// - mag = A signed 16-bit raw reading from the magnetometer.
float calcMag(int16_t mag);
// setGyroScale() -- Set the full-scale range of the gyroscope.
// This function can be called to set the scale of the gyroscope to
// 245, 500, or 200 degrees per second.
// Input:
// - gScl = The desired gyroscope scale. Must be one of three possible
// values from the gyro_scale enum.
void setGyroScale(gyro_scale gScl);
// setAccelScale() -- Set the full-scale range of the accelerometer.
// This function can be called to set the scale of the accelerometer to
// 2, 4, 6, 8, or 16 g's.
// Input:
// - aScl = The desired accelerometer scale. Must be one of five possible
// values from the accel_scale enum.
void setAccelScale(accel_scale aScl);
// setMagScale() -- Set the full-scale range of the magnetometer.
// This function can be called to set the scale of the magnetometer to
// 2, 4, 8, or 12 Gs.
// Input:
// - mScl = The desired magnetometer scale. Must be one of four possible
// values from the mag_scale enum.
void setMagScale(mag_scale mScl);
// setGyroODR() -- Set the output data rate and bandwidth of the gyroscope
// Input:
// - gRate = The desired output rate and cutoff frequency of the gyro.
// Must be a value from the gyro_odr enum (check above, there're 14).
void setGyroODR(gyro_odr gRate);
// setAccelODR() -- Set the output data rate of the accelerometer
// Input:
// - aRate = The desired output rate of the accel.
// Must be a value from the accel_odr enum (check above, there're 11).
void setAccelODR(accel_odr aRate);
// setMagODR() -- Set the output data rate of the magnetometer
// Input:
// - mRate = The desired output rate of the mag.
// Must be a value from the mag_odr enum (check above, there're 6).
void setMagODR(mag_odr mRate);
// configGyroInt() -- Configure the gyro interrupt output.
// Triggers can be set to either rising above or falling below a specified
// threshold. This function helps setup the interrupt configuration and
// threshold values for all axes.
// Input:
// - int1Cfg = A 8-bit value that is sent directly to the INT1_CFG_G
// register. This sets AND/OR and high/low interrupt gen for each axis
// - int1ThsX = 16-bit interrupt threshold value for x-axis
// - int1ThsY = 16-bit interrupt threshold value for y-axis
// - int1ThsZ = 16-bit interrupt threshold value for z-axis
// - duration = Duration an interrupt holds after triggered. This value
// is copied directly into the INT1_DURATION_G register.
// Before using this function, read about the INT1_CFG_G register and
// the related INT1* registers in the LMS9DS0 datasheet.
void configGyroInt(uint8_t int1Cfg, uint16_t int1ThsX = 0,
uint16_t int1ThsY = 0, uint16_t int1ThsZ = 0,
uint8_t duration = 0);
private:
// xmAddress and gAddress store the I2C address or SPI chip select pin
// for each sensor.
uint8_t xmAddress, gAddress;
// interfaceMode keeps track of whether we're using SPI or I2C to talk
interface_mode interfaceMode;
// gScale, aScale, and mScale store the current scale range for each
// sensor. Should be updated whenever that value changes.
gyro_scale gScale;
accel_scale aScale;
mag_scale mScale;
// gRes, aRes, and mRes store the current resolution for each sensor.
// Units of these values would be DPS (or g's or Gs's) per ADC tick.
// This value is calculated as (sensor scale) / (2^15).
float gRes, aRes, mRes;
// initGyro() -- Sets up the gyroscope to begin reading.
// This function steps through all five gyroscope control registers.
// Upon exit, the following parameters will be set:
// - CTRL_REG1_G = 0x0F: Normal operation mode, all axes enabled.
// 95 Hz ODR, 12.5 Hz cutoff frequency.
// - CTRL_REG2_G = 0x00: HPF set to normal mode, cutoff frequency
// set to 7.2 Hz (depends on ODR).
// - CTRL_REG3_G = 0x88: Interrupt enabled on INT_G (set to push-pull and
// active high). Data-ready output enabled on DRDY_G.
// - CTRL_REG4_G = 0x00: Continuous update mode. Data LSB stored in lower
// address. Scale set to 245 DPS. SPI mode set to 4-wire.
// - CTRL_REG5_G = 0x00: FIFO disabled. HPF disabled.
void initGyro();
// initAccel() -- Sets up the accelerometer to begin reading.
// This function steps through all accelerometer related control registers.
// Upon exit these registers will be set as:
// - CTRL_REG0_XM = 0x00: FIFO disabled. HPF bypassed. Normal mode.
// - CTRL_REG1_XM = 0x57: 100 Hz data rate. Continuous update.
// all axes enabled.
// - CTRL_REG2_XM = 0x00: +/- 2g scale. 773 Hz anti-alias filter BW.
// - CTRL_REG3_XM = 0x04: Accel data ready signal on INT1_XM pin.
void initAccel();
// initMag() -- Sets up the magnetometer to begin reading.
// This function steps through all magnetometer-related control registers.
// Upon exit these registers will be set as:
// - CTRL_REG4_XM = 0x04: Mag data ready signal on INT2_XM pin.
// - CTRL_REG5_XM = 0x14: 100 Hz update rate. Low resolution. Interrupt
// requests don't latch. Temperature sensor disabled.
// - CTRL_REG6_XM = 0x00: +/- 2 Gs scale.
// - CTRL_REG7_XM = 0x00: Continuous conversion mode. Normal HPF mode.
// - INT_CTRL_REG_M = 0x09: Interrupt active-high. Enable interrupts.
void initMag();
// gReadByte() -- Reads a byte from a specified gyroscope register.
// Input:
// - subAddress = Register to be read from.
// Output:
// - An 8-bit value read from the requested address.
uint8_t gReadByte(uint8_t subAddress);
// gReadBytes() -- Reads a number of bytes -- beginning at an address
// and incrementing from there -- from the gyroscope.
// Input:
// - subAddress = Register to be read from.
// - * dest = A pointer to an array of uint8_t's. Values read will be
// stored in here on return.
// - count = The number of bytes to be read.
// Output: No value is returned, but the `dest` array will store
// the data read upon exit.
void gReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count);
// gWriteByte() -- Write a byte to a register in the gyroscope.
// Input:
// - subAddress = Register to be written to.
// - data = data to be written to the register.
void gWriteByte(uint8_t subAddress, uint8_t data);
// xmReadByte() -- Read a byte from a register in the accel/mag sensor
// Input:
// - subAddress = Register to be read from.
// Output:
// - An 8-bit value read from the requested register.
uint8_t xmReadByte(uint8_t subAddress);
// xmReadBytes() -- Reads a number of bytes -- beginning at an address
// and incrementing from there -- from the accelerometer/magnetometer.
// Input:
// - subAddress = Register to be read from.
// - * dest = A pointer to an array of uint8_t's. Values read will be
// stored in here on return.
// - count = The number of bytes to be read.
// Output: No value is returned, but the `dest` array will store
// the data read upon exit.
void xmReadBytes(uint8_t subAddress, uint8_t * dest, uint8_t count);
// xmWriteByte() -- Write a byte to a register in the accel/mag sensor.
// Input:
// - subAddress = Register to be written to.
// - data = data to be written to the register.
void xmWriteByte(uint8_t subAddress, uint8_t data);
// calcgRes() -- Calculate the resolution of the gyroscope.
// This function will set the value of the gRes variable. gScale must
// be set prior to calling this function.
void calcgRes();
// calcmRes() -- Calculate the resolution of the magnetometer.
// This function will set the value of the mRes variable. mScale must
// be set prior to calling this function.
void calcmRes();
// calcaRes() -- Calculate the resolution of the accelerometer.
// This function will set the value of the aRes variable. aScale must
// be set prior to calling this function.
void calcaRes();
///////////////////
// SPI Functions //
///////////////////
// initSPI() -- Initialize the SPI hardware.
// This function will setup all SPI pins and related hardware.
void initSPI();
// SPIwriteByte() -- Write a byte out of SPI to a register in the device
// Input:
// - csPin = The chip select pin of the slave device.
// - subAddress = The register to be written to.
// - data = Byte to be written to the register.
void SPIwriteByte(uint8_t csPin, uint8_t subAddress, uint8_t data);
// SPIreadByte() -- Read a single byte from a register over SPI.
// Input:
// - csPin = The chip select pin of the slave device.
// - subAddress = The register to be read from.
// Output:
// - The byte read from the requested address.
uint8_t SPIreadByte(uint8_t csPin, uint8_t subAddress);
// SPIreadBytes() -- Read a series of bytes, starting at a register via SPI
// Input:
// - csPin = The chip select pin of a slave device.
// - subAddress = The register to begin reading.
// - * dest = Pointer to an array where we'll store the readings.
// - count = Number of registers to be read.
// Output: No value is returned by the function, but the registers read are
// all stored in the *dest array given.
void SPIreadBytes(uint8_t csPin, uint8_t subAddress,
uint8_t * dest, uint8_t count);
///////////////////
// I2C Functions //
///////////////////
// initI2C() -- Initialize the I2C hardware.
// This function will setup all I2C pins and related hardware.
void initI2C();
// I2CwriteByte() -- Write a byte out of I2C to a register in the device
// Input:
// - address = The 7-bit I2C address of the slave device.
// - subAddress = The register to be written to.
// - data = Byte to be written to the register.
void I2CwriteByte(uint8_t address, uint8_t subAddress, uint8_t data);
// I2CreadByte() -- Read a single byte from a register over I2C.
// Input:
// - address = The 7-bit I2C address of the slave device.
// - subAddress = The register to be read from.
// Output:
// - The byte read from the requested address.
uint8_t I2CreadByte(uint8_t address, uint8_t subAddress);
// I2CreadBytes() -- Read a series of bytes, starting at a register via SPI
// Input:
// - address = The 7-bit I2C address of the slave device.
// - subAddress = The register to begin reading.
// - * dest = Pointer to an array where we'll store the readings.
// - count = Number of registers to be read.
// Output: No value is returned by the function, but the registers read are
// all stored in the *dest array given.
void I2CreadBytes(uint8_t address, uint8_t subAddress, uint8_t * dest, uint8_t count);
};
#endif // SFE_LSM9DS0_H //
/* Copyright (c) 2014 Paul Kourany, based on work by Dianel Gilbert
Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "SparkIntervalTimer.h"
// ------------------------------------------------------------
// static class variables need to be reiterated here before use
// ------------------------------------------------------------
bool IntervalTimer::SIT_used[];
IntervalTimer::ISRcallback IntervalTimer::SIT_CALLBACK[];
// ------------------------------------------------------------
// Define interval timer ISR hooks for three available timers
// TIM2, TIM3 and TIM4 with callbacks to user code.
// These default as infinite loop stubs by Spark
// ------------------------------------------------------------
extern "C" void Wiring_TIM2_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
//ISR for TIM2 code here
IntervalTimer::SIT_CALLBACK[0]();
}
}
extern "C" void Wiring_TIM3_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
//ISR code for TIM3 here
IntervalTimer::SIT_CALLBACK[1]();
}
}
extern "C" void Wiring_TIM4_Interrupt_Handler_override()
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
//ISR code for TIM3 here
IntervalTimer::SIT_CALLBACK[2]();
}
}
// ------------------------------------------------------------
// this function inits and starts the timer, using the specified
// function as a callback and the period provided. must be passed
// the name of a function taking no arguments and returning void.
// make sure this function can complete within the time allowed.
// attempts to allocate a timer using available resources,
// returning true on success or false in case of failure.
// Period units is defined by scale, where scale = uSec or hmSec
// and = 1-65535 microsecond (uSec)
// or 1-65535 0.5ms increments (hmSec)
// ------------------------------------------------------------
bool IntervalTimer::beginCycles(void (*isrCallback)(), uint16_t newValue, bool scale) {
// if this interval timer is already running, stop it
if (status == TIMER_SIT) {
stop_SIT();
status = TIMER_OFF;
}
// store callback pointer
myISRcallback = isrCallback;
// attempt to allocate this timer
if (allocate_SIT(newValue, scale)) status = TIMER_SIT;
else status = TIMER_OFF;
// check for success and return
if (status != TIMER_OFF) return true;
return false;
}
// ------------------------------------------------------------
// stop the timer if it's currently running, using its status
// to determine what hardware resources the timer may be using
// ------------------------------------------------------------
void IntervalTimer::end() {
if (status == TIMER_SIT) stop_SIT();
status = TIMER_OFF;
}
// ------------------------------------------------------------
// enables the SIT clock if not already enabled, then checks to
// see if any SITs are available for use. if one is available,
// it's initialized and started with the specified value, and
// the function returns true, otherwise it returns false
// ------------------------------------------------------------
bool IntervalTimer::allocate_SIT(uint16_t newValue, bool scale) {
// check for an available SIT, and if so, start it
for (uint8_t id = 0; id < NUM_SIT; id++) {
if (!SIT_used[id]) {
SIT_id = id;
start_SIT(newValue, scale);
SIT_used[id] = true;
return true;
}
}
// no SIT available
return false;
}
// ------------------------------------------------------------
// configuters a SIT's TIMER registers, etc and enables
// interrupts, effectively starting the timer upon completion
// ------------------------------------------------------------
void IntervalTimer::start_SIT(uint16_t newValue, bool scale) {
TIM_TimeBaseInitTypeDef timerInitStructure;
NVIC_InitTypeDef nvicStructure;
uint16_t prescaler;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
case 0: // TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
}
// Initialize Timer
switch (scale) {
case uSec:
prescaler = SIT_PRESCALERu; // Set prescaler for 1MHz clock, 1us period
break;
case hmSec:
prescaler = SIT_PRESCALERm; // Set prescaler for 2Hz clock, .5ms period
break;
default:
scale == uSec; // Default to microseconds
prescaler = SIT_PRESCALERu;
break;
}
timerInitStructure.TIM_Prescaler = prescaler;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = newValue;
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIMx, &timerInitStructure);
TIM_Cmd(TIMx, ENABLE);
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
// point to the correct SIT ISR
SIT_CALLBACK[SIT_id] = myISRcallback;
//Enable Timer Interrupt
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
nvicStructure.NVIC_IRQChannelSubPriority = 1;
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicStructure);
}
// ------------------------------------------------------------
// stops an active SIT by disabling its interrupt and TIMER
// and freeing up its state for future use.
// ------------------------------------------------------------
void IntervalTimer::stop_SIT() {
NVIC_InitTypeDef nvicStructure;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
case 0: // TIM2
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
}
// disable counter
TIM_Cmd(TIMx, DISABLE);
// disable interrupt
nvicStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&nvicStructure);
// disable timer peripheral
TIM_DeInit(TIMx);
// free SIT for future use
SIT_used[SIT_id] = false;
}
// ------------------------------------------------------------
// Enables or disables an active SIT's interrupt without
// removing the SIT.
// ------------------------------------------------------------
void IntervalTimer::interrupt_SIT(action ACT)
{
NVIC_InitTypeDef nvicStructure;
TIM_TypeDef* TIMx;
//use SIT_id to identify TIM#
switch (SIT_id) {
case 0: // TIM2
nvicStructure.NVIC_IRQChannel = TIM2_IRQn;
TIMx = TIM2;
break;
case 1: // TIM3
nvicStructure.NVIC_IRQChannel = TIM3_IRQn;
TIMx = TIM3;
break;
case 2: // TIM4
nvicStructure.NVIC_IRQChannel = TIM4_IRQn;
TIMx = TIM4;
break;
}
switch (ACT) {
case INT_ENABLE:
//Enable Timer Interrupt
nvicStructure.NVIC_IRQChannelPreemptionPriority = 0;
nvicStructure.NVIC_IRQChannelSubPriority = 1;
nvicStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicStructure);
break;
case INT_DISABLE:
// disable interrupt
nvicStructure.NVIC_IRQChannelCmd = DISABLE;
NVIC_Init(&nvicStructure);
break;
default:
//Do nothing
break;
}
}
/* Copyright (c) 2014 Paul Kourany, based on work by Dianel Gilbert
Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#ifndef __INTERVALTIMER_H__
#define __INTERVALTIMER_H__
#include "application.h"
enum {uSec, hmSec}; // microseconds or half-milliseconds
#ifdef __cplusplus
extern "C" {
#endif
extern void (*Wiring_TIM2_Interrupt_Handler)(void);
extern void (*Wiring_TIM3_Interrupt_Handler)(void);
extern void (*Wiring_TIM4_Interrupt_Handler)(void);
extern void Wiring_TIM2_Interrupt_Handler_override(void);
extern void Wiring_TIM3_Interrupt_Handler_override(void);
extern void Wiring_TIM4_Interrupt_Handler_override(void);
enum action {INT_DISABLE, INT_ENABLE};
class IntervalTimer {
private:
typedef void (*ISRcallback)();
enum {TIMER_OFF, TIMER_SIT};
static const uint8_t NUM_SIT = 3;
// Timer ClockDivision = DIV4
const uint16_t SIT_PRESCALERu = (uint16_t)(SystemCoreClock / 1000000) - 1; //To get TIM counter clock = 1MHz
const uint16_t SIT_PRESCALERm = (uint16_t)(SystemCoreClock / 2000) - 1; //To get TIM counter clock = 2KHz
const uint16_t MAX_PERIOD = UINT16_MAX; // 1-65535 us
static bool SIT_used[NUM_SIT];
bool allocate_SIT(uint16_t newValue, bool scale);
void start_SIT(uint16_t newValue, bool scale);
void stop_SIT();
bool status;
uint8_t SIT_id;
ISRcallback myISRcallback;
bool beginCycles(void (*isrCallback)(), uint16_t cycles, bool scale);
public:
IntervalTimer() {
status = TIMER_OFF;
Wiring_TIM2_Interrupt_Handler = Wiring_TIM2_Interrupt_Handler_override;
Wiring_TIM3_Interrupt_Handler = Wiring_TIM3_Interrupt_Handler_override;
Wiring_TIM4_Interrupt_Handler = Wiring_TIM4_Interrupt_Handler_override;
}
~IntervalTimer() { end(); }
bool begin(void (*isrCallback)(), unsigned int newPeriod, bool scale) {
if (newPeriod == 0 || newPeriod > MAX_PERIOD)
return false;
return beginCycles(isrCallback, newPeriod, scale);
}
bool begin(void (*isrCallback)(), int newPeriod, bool scale) {
if (newPeriod < 0)
return false;
return begin(isrCallback, (unsigned int)newPeriod, scale);
}
bool begin(void (*isrCallback)(), unsigned long newPeriod, bool scale) {
return begin(isrCallback, (unsigned int)newPeriod, scale);
}
bool begin(void (*isrCallback)(), long newPeriod, bool scale) {
return begin(isrCallback, (int)newPeriod, scale);
}
bool begin(unsigned int newPeriod, bool scale) { //Set different time for timer that's already allocated
if (newPeriod == 0 || newPeriod > MAX_PERIOD)
return false;
if (status == TIMER_SIT) {
stop_SIT();
start_SIT(newPeriod, scale);
}
}
void end();
void interrupt_SIT(action ACT);
static ISRcallback SIT_CALLBACK[NUM_SIT];
};
#ifdef __cplusplus
}
#endif
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment