Kionix KX123 accelerometer C++ driver. Can be used for some extend also with kx022, kx023, kx122, etc. when used features are present in sensor.

Dependents:   kionix-kx123-hello

Files at this revision

API Documentation at this revision

Comitter:
MikkoZ
Date:
Thu Oct 06 10:23:48 2016 +0000
Parent:
1:f328083fb80b
Child:
3:4fd5361ed180
Commit message:
Added many features for setting up and reading the sensor registers via driver functions.

Changed in this revision

kx123.cpp Show annotated file Show diff for this revision Revisions of this file
kx123.h Show annotated file Show diff for this revision Revisions of this file
kx123_registers.h Show annotated file Show diff for this revision Revisions of this file
--- a/kx123.cpp	Fri Sep 30 11:52:29 2016 +0000
+++ b/kx123.cpp	Thu Oct 06 10:23:48 2016 +0000
@@ -28,12 +28,31 @@
 KX123::KX123(RegisterWriter &i2c_obj, uint8_t sad, uint8_t wai) : i2c_rw(i2c_obj) {
     _sad = sad;
     _wai = wai;        
+    setup_mode_on = false;
 }
 
 KX123::~KX123(){
 }
 
 /**
+* Start setup/stand-by mode and shut off measurements.
+* @return true on error, false on ok
+*/
+bool KX123::start_setup_mode(void){
+    setup_mode_on = true;
+    return i2c_rw.change_bits(_sad, KX122_CNTL1, KX122_CNTL1_PC1, KX122_CNTL1_PC1);
+}
+
+/**
+* Start operating/measurement mode. Setup is not allowed while in this mode.
+* @return true on error, false on ok
+*/
+bool KX123::start_measurement_mode(void){
+    setup_mode_on = false;
+    return i2c_rw.change_bits(_sad, KX122_CNTL1, KX122_CNTL1_PC1, 0);
+}
+
+/**
 * Check if sensor is connected, setup defaults to sensor and start measuring.
 * @return true on error, false on ok
 */
@@ -85,6 +104,7 @@
 
     //First set CNTL1 PC1-bit to stand-by mode, after that setup can be made
     i2c_rw.write_register(_sad, KX122_CNTL1, 0 );
+    setup_mode_on = true;
 
     //set_tilt_position_defaults();
 
@@ -92,6 +112,7 @@
     i2c_rw.write_register(_sad, KX122_ODCNTL, KX122_ODCNTL_OSA_25600);
     //Setup G-range and 8/16-bit resolution + set CNTL1 PC1-bit to operating mode (also WUF_EN, TP_EN and DT_EN)
     i2c_rw.write_register(_sad, KX122_CNTL1, ( KX122_CNTL1_PC1 | KX122_CNTL1_GSEL_8G | KX122_CNTL1_RES ) );
+    setup_mode_on = false;
 
     //resolution_divider = 32768/2;  //KX122_CNTL1_GSEL_2G
     //resolution_divider = 32768/4;  //KX122_CNTL1_GSEL_4G
@@ -102,9 +123,10 @@
 
 
 /**
-* CNTL PC1-bit has to be set to stand-by before this command.
+* Setup default settings for a tilt position
 **/
 void KX123::set_tilt_position_defaults(){
+    if (setup_mode_on == false) return;
     //CNTL3: Tilt position control, directional tap control and motion wakeup control
     i2c_rw.write_register(_sad, KX122_CNTL3, ( KX122_CNTL3_OTP_50 | KX122_CNTL3_OTDT_400 ) ); 
     //TILT_TIMER: Setup tilt position timer (~=filter)
@@ -114,6 +136,26 @@
 
 
 /**
+* Get filtered uint16_t XYZ-values from sensor
+* @param *buf to uint16_t[3] for results
+* @return true on error, false on read ok.
+**/
+bool KX123::getresults_highpass(uint16_t* buf) {
+    #define RESULTS_LEN 6
+    uint8_t tmp[RESULTS_LEN];     //XYZ (lhlhlh)
+    uint8_t read_bytes;
+
+    read_bytes = i2c_rw.read_register(_sad, KX122_XHP_L, &tmp[0], sizeof(tmp));
+    if (read_bytes != RESULTS_LEN){
+        return true;
+        }
+    buf[0] = ( tmp[1] << 8 ) | tmp[0];  //X
+    buf[1] = ( tmp[3] << 8 ) | tmp[2];  //Y
+    buf[2] = ( tmp[5] << 8 ) | tmp[4];  //Z
+    return false;
+}
+
+/**
 * Get raw uint16_t XYZ-values from sensor
 * @param *buf to uint16_t[3] for results
 * @return true on error, false on read ok.
@@ -155,4 +197,319 @@
     return false;
 }
 
+/**
+* Get axes of current tilt and previous tilt
+* @param *current_previous space for storing 2 (uint8_t) values
+* @return true on error
+*/
+bool KX123::get_tilt(enum e_axis* current_previous){
+    #define GET_TILT_READ_LEN 2
+    uint8_t read_bytes;
 
+    read_bytes = i2c_rw.read_register(_sad, KX122_TSCP, (uint8_t*)current_previous, GET_TILT_READ_LEN);
+
+    return (read_bytes != GET_TILT_READ_LEN);
+}
+
+/**
+* Get axis of triggered tap/double tap interrupt from INS1
+* @param *axis space for storing 1 (uint8_t) value in e_axis format
+* @return true on error
+*/
+bool KX123::get_tap_interrupt_axis(enum e_axis* axis){
+    #define GET_TAP_READ_LEN 1
+    uint8_t read_bytes;
+    
+    read_bytes = i2c_rw.read_register(_sad, KX122_INS1, (uint8_t*)axis, GET_TAP_READ_LEN);
+
+    return (read_bytes != GET_TAP_READ_LEN);
+}
+
+/**
+* Get axis of triggered motion detect interrupt from INS3
+* @param *axis space for storing 1 (uint8_t) value in e_axis format
+* @return true on error
+*/
+bool KX123::get_detected_motion_axis(enum e_axis* axis){
+    #define GET_MOTION_READ_LEN 1
+    uint8_t read_bytes;
+    
+    read_bytes = i2c_rw.read_register(_sad, KX122_INS3, (uint8_t*)axis, GET_MOTION_READ_LEN);
+
+    return (read_bytes != GET_MOTION_READ_LEN);
+}
+
+/**
+* Set axis of triggered motion detect interrupt from CNTL2
+* @param cnltl2_tilt_mask Orred e_axis values for axes directions to cause interrupt
+* @return true on error or setup mode off
+*/
+bool KX123::set_tilt_axis_mask(uint8_t cnltl2_tilt_mask){
+    if (setup_mode_on == false) return true;
+    //MSb 00 == no_action
+    return i2c_rw.write_register(_sad, KX122_CNTL2, (cnltl2_tilt_mask & KX123_AXIS_MASK) );
+}
+
+/**
+* get cause of interrupt trigger from INS2
+* @param *int_reason space for storing 1 (uint8_t) value in e_interrupt_reason format
+* @return true on error
+*/
+bool KX123::get_interrupt_reason(enum e_interrupt_reason* int_reason){
+    #define INT_REASON_LEN 1
+    uint8_t read_bytes;
+    
+    read_bytes = i2c_rw.read_register(_sad, KX122_INS2, (uint8_t*)int_reason, INT_REASON_LEN);
+
+    return (read_bytes != INT_REASON_LEN);
+}
+
+/**
+* Check from sensor register if interrupt has occured. Usable feature when
+* multiple sensor interrupts are connected to same interrupt pin.
+* @return true when interrupt has occured. False on read fail and no interrupt.
+*/
+bool KX123::has_interrupt_occured(){
+    uint8_t status_reg;
+    uint8_t read_bytes;
+    
+    read_bytes = i2c_rw.read_register(_sad, KX122_INS2, &status_reg, 1);
+    return ((read_bytes == 1) && (status_reg & KX122_STATUS_REG_INT));
+}
+
+/**
+* Clear interrupt flag when latched. (==Interrupt release) Doesn't work for FIFO
+* Buffer Full or FIFO Watermark -interrupts.
+*/
+void KX123::clear_interrupt(){
+    uint8_t value_discarded;
+
+    i2c_rw.read_register(_sad, KX122_INT_REL, &value_discarded, 1);
+
+    return;
+}
+
+/**
+* Initiate software reset and RAM reboot routine and wait untill finished.
+*/
+void KX123::soft_reset(){
+    uint8_t reset_ongoing;
+
+    i2c_rw.change_bits(_sad, KX122_CNTL2, KX122_CNTL2_SRST, KX122_CNTL2_SRST );
+    do{
+        uint8_t cntl2, read_bytes;
+        
+        read_bytes = i2c_rw.read_register(_sad, KX122_CNTL2, &cntl2, 1);
+        reset_ongoing = ((read_bytes == 0) || (cntl2 & KX122_CNTL2_SRST));
+    } while (reset_ongoing);
+    
+    return;
+}
+
+/**
+* Verify proper integrated circuit functionality
+* @return true on test fail or setup mode off, false on test ok.
+**/
+bool KX123::self_test(){
+    uint8_t cotr_value;
+    bool read_ok;
+
+    if (setup_mode_on == false) return true;
+    //first read to make sure COTR is in default value
+    read_ok = i2c_rw.read_register(_sad, KX122_COTR, &cotr_value, 1);
+    read_ok = i2c_rw.read_register(_sad, KX122_COTR, &cotr_value, 1);
+    if ((cotr_value != 0x55) || (!read_ok) ){
+        return true;
+        }
+    i2c_rw.change_bits(_sad, KX122_CNTL2, KX122_CNTL2_COTC, KX122_CNTL2_COTC );
+    read_ok = i2c_rw.read_register(_sad, KX122_COTR, &cotr_value, 1);
+    if ((cotr_value != 0xAA) || (!read_ok) ){
+        return true;
+        }
+    return false;
+}
+
+/**
+* Setup ODR values for Tilt Position, Directional Tap and Motion Detect.
+* @param tilt_position_odr KX122_CNTL3_OTP_* -value or 0xff to skip.
+* @param directional_tap_odr KX122_CNTL3_OTDT_* -value or 0xff to skip.
+* @param motion_wuf_odr KX122_CNTL3_OWUF_* -value or 0xff to skip.
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_cntl3_odrs(uint8_t tilt_position_odr, uint8_t directional_tap_odr, uint8_t motion_wuf_odr){
+    uint8_t cntl3;
+    bool read_ok;
+
+    if (setup_mode_on == false) return true;
+
+    read_ok = i2c_rw.read_register(_sad, KX122_CNTL3, &cntl3, 1);
+    if(!read_ok) return true;
+    
+    if (tilt_position_odr != 0xff){
+        cntl3 = (cntl3 & ~KX122_CNTL3_OTP_MASK) | (tilt_position_odr & KX122_CNTL3_OTP_MASK);
+        }
+    if (directional_tap_odr != 0xff){
+        cntl3 = (cntl3 & ~KX122_CNTL3_OTDT_MASK) | (directional_tap_odr & KX122_CNTL3_OTDT_MASK);
+        }
+    if (motion_wuf_odr != 0xff){
+        cntl3 = (cntl3 & ~KX122_CNTL3_OWUF_MASK) | (motion_wuf_odr & KX122_CNTL3_OWUF_MASK);
+        }
+    return i2c_rw.write_register(_sad, KX122_CNTL3, cntl3);
+}
+
+/**
+* Setup ODR value, IIR-filter on/off and lowpass filter corner frequency.
+* @param iir_filter_off False to filter ON or true to filter OFF.
+* @param lowpass_filter_freq_half Filter corner frequency setup. False to ODR/9. True to ODR/2.
+* @param odr Output Data Rate using KX122_ODCNTL_OSA_* -definitions.
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_odcntl(bool iir_filter_off, uint8_t lowpass_filter_freq_half, uint8_t odr){
+    uint8_t odcntl;
+
+    if (setup_mode_on == false) return true;
+
+    odcntl = (odr & KX122_ODCNTL_OSA_MASK);
+    if (iir_filter_off){
+        odcntl = (odcntl | KX122_ODCNTL_IIR_BYPASS);
+        }
+    if (lowpass_filter_freq_half){
+        odcntl = (odcntl | KX122_ODCNTL_LPRO);
+        }
+
+    return i2c_rw.write_register(_sad, KX122_ODCNTL, odcntl);
+}
+
+/**
+* Setup physical int1 pin, selftest polarity and SPI 3-wire on/off.
+* @param pwsel Pulse width configuration in KX122_INC1_PWSEL1_* values
+* @param physical_int_pin_enabled
+* @param physical_int_pin_active_high (default true)
+* @param physical_int_pin_latch_disabled
+* @param self_test_polarity_positive
+* @param spi3wire_enabled Use 3 wires instead of 4.
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::int1_setup(uint8_t pwsel,
+    bool physical_int_pin_enabled,
+    bool physical_int_pin_active_high,
+    bool physical_int_pin_latch_disabled,
+    bool self_test_polarity_positive,
+    bool spi3wire_enabled){
+
+    uint8_t inc1;
+
+    if (setup_mode_on == false) return true;
+
+    inc1 = (pwsel & KX122_INC1_PWSEL1_MASK);
+    if (physical_int_pin_enabled){
+        inc1 = (inc1 | KX122_INC1_IEN1);
+        }
+    if (physical_int_pin_active_high){
+        inc1 = (inc1 | KX122_INC1_IEA1);
+        }
+    if (physical_int_pin_latch_disabled){
+        inc1 = (inc1 | KX122_INC1_IEL1);
+        }
+    if (self_test_polarity_positive){
+        inc1 = (inc1 | KX122_INC1_STPOL);
+        }
+    if (spi3wire_enabled){
+        inc1 = (inc1 | KX122_INC1_SPI3E);
+        }
+    return i2c_rw.write_register(_sad, KX122_INC1, inc1);
+}
+
+
+/**
+* Setup physical int2 pin and int1/2 autoclear. 
+* @param pwsel Pulse width configuration in KX122_INC1_PWSEL1_* values
+* @param physical_int_pin_enabled
+* @param physical_int_pin_active_high (default true)
+* @param physical_int_pin_latch_disabled
+* @param aclr2_enabled
+* @param aclr1_enabled 
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::int2_setup(uint8_t pwsel,
+    bool physical_int_pin_enabled,
+    bool physical_int_pin_active_high,
+    bool physical_int_pin_latch_disabled,
+    bool aclr2_enabled,
+    bool aclr1_enabled){
+
+    uint8_t inc5;
+
+    if (setup_mode_on == false) return true;
+
+    inc5 = (pwsel & KX122_INC5_PWSEL2_MASK);
+    if (physical_int_pin_enabled){
+        inc5 = (inc5 | KX122_INC5_IEN2);
+        }
+    if (physical_int_pin_active_high){
+        inc5 = (inc5 | KX122_INC5_IEA2);
+        }
+    if (physical_int_pin_latch_disabled){
+        inc5 = (inc5 | KX122_INC5_IEL2);
+        }
+    if (aclr2_enabled){
+        inc5 = (inc5 | KX122_INC5_ACLR2);
+        }
+    if (aclr1_enabled){
+        inc5 = (inc5 | KX122_INC5_ACLR1);
+        }
+    return i2c_rw.write_register(_sad, KX122_INC5, inc5);
+}
+
+/**
+* Select interrupt reasons that can trigger interrupt for int1.
+* @param interrupt_reason One or more e_interrupt_reason -values except doubletap.
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_int1_interrupt_reason(uint8_t interrupt_reason){
+    if (setup_mode_on == false) return true;
+    return i2c_rw.write_register(_sad, KX122_INC4, interrupt_reason);
+}
+
+/**
+* Select interrupt reasons that can trigger interrupt for int2.
+* @param interrupt_reason One or more e_interrupt_reason -values except doubletap.
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_int2_interrupt_reason(uint8_t interrupt_reason){
+    if (setup_mode_on == false) return true;
+    return i2c_rw.write_register(_sad, KX122_INC6, interrupt_reason);
+}
+
+/**
+* Select axis that are monitored for motion detect interrupt.
+* @param xxyyzz combination of e_axis -values for enabling axis
+* @param axis_and_combination_enabled true for AND or false for OR
+*
+* true for AND configuration = (XN || XP) && (YN || YP) && (ZN || ZP)
+*
+* false for OR configuration = (XN || XP || YN || TP || ZN || ZP)
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_motion_detect_axis(uint8_t xxyyzz, bool axis_and_combination_enabled){
+    uint8_t inc2;
+
+    if (setup_mode_on == false) return true;
+
+    inc2 = (xxyyzz & KX122_INC2_WUE_MASK);
+    if (axis_and_combination_enabled){
+        inc2 = (inc2 | KX122_INC2_AOI_AND);
+        }
+    return i2c_rw.write_register(_sad, KX122_INC2, inc2);
+}
+
+/**
+* Select axis that are monitored for tap/doubletap interrupt.
+* @param xxyyzz combination of e_axis -values for enabling axis
+* @return true on error or setup mode off, false on setup ok.
+**/
+bool KX123::set_tap_axis(uint8_t xxyyzz){
+    if (setup_mode_on == false) return true;
+    return i2c_rw.write_register(_sad, KX122_INC3, xxyyzz);
+}
+
--- a/kx123.h	Fri Sep 30 11:52:29 2016 +0000
+++ b/kx123.h	Thu Oct 06 10:23:48 2016 +0000
@@ -24,6 +24,7 @@
 * Kionix KX123 accelerometer i2c driver. For some extend can be used also with
 * kx012, kx022, kx023, kx23h, kx112, kx122, kx124, kx222 and kx224. Driver uses
 * RegisterWriter -class as (i2c) hardware abstraction layer.
+* Note that when doing setup, sensor has to be in setup mode, not in operating mode.
 */
 class KX123
 {
@@ -32,11 +33,53 @@
     KX123(RegisterWriter &i2c_obj, uint8_t sad = KX123_DEFAULT_SLAVE_ADDRESS, uint8_t wai = KX123_WHO_AM_I_WAI_ID);
     ~KX123();
 
+    bool start_setup_mode(void);
+    bool start_measurement_mode(void);
     bool set_defaults(void);
+    bool getresults_highpass(uint16_t* buf);
     bool getresults_raw(int16_t* buf);
     bool getresults_g(float* buf);
 
+    
+    bool get_tilt(enum e_axis* current_previous);
+    bool get_tap_interrupt_axis(enum e_axis* axis);
+    bool get_detected_motion_axis(enum e_axis* axis);
+    bool set_tilt_axis_mask(uint8_t cnltl2_tilt_mask);
+    
+    bool get_interrupt_reason(enum e_interrupt_reason* int_reason);
+
+    bool has_interrupt_occured();
+    void clear_interrupt();
+    void soft_reset();
+    bool self_test();
+
+    bool set_cntl3_odrs(uint8_t tilt_position_odr, uint8_t directional_tap_odr, uint8_t motion_wuf_odr); //0xff for DONT_SET
+    bool set_odcntl(bool iir_filter_off, uint8_t lowpass_filter_freq_half, uint8_t odr);
+    bool int1_setup(uint8_t pwsel,
+        bool physical_int_pin_enabled,
+        bool physical_int_pin_active_high,
+        bool physical_int_pin_latch_disabled,
+        bool self_test_polarity_positive,
+        bool spi3wire_enabled);
+    bool int2_setup(uint8_t pwsel,
+        bool physical_int_pin_enabled,
+        bool physical_int_pin_active_high,
+        bool physical_int_pin_latch_disabled,
+        bool aclr2_enabled,
+        bool aclr1_enabled);
+    bool set_int1_interrupt_reason(uint8_t interrupt_reason);
+    bool set_int2_interrupt_reason(uint8_t interrupt_reason);
+
+    bool set_motion_detect_axis(uint8_t xxyyzz, bool axis_and_combination_enabled = false);
+    bool set_tap_axis(uint8_t xxyyzz);
+/**
+* Note that not all sensor setup registers are exposed with this driver yet.
+* There are also setup registers for motion detect, tap/double tap detect, free
+* fall detect, tilt hysteresis, low power and FIFO.
+*/
+
 private:
+    bool setup_mode_on;
     void set_tilt_position_defaults();
     
     RegisterWriter i2c_rw;
--- a/kx123_registers.h	Fri Sep 30 11:52:29 2016 +0000
+++ b/kx123_registers.h	Thu Oct 06 10:23:48 2016 +0000
@@ -19,6 +19,29 @@
 #define KX123_DEFAULT_SLAVE_ADDRESS 0x1F
 #define KX123_SECONDARY_SLAVE_ADDRESS 0x1E
 
+enum e_axis {
+    KX123_X_N = (uint8_t) (0x01 << 5),
+    KX123_X_P = (uint8_t) (0x01 << 4),
+    KX123_Y_N = (uint8_t) (0x01 << 3),
+    KX123_Y_P = (uint8_t) (0x01 << 2),
+    KX123_Z_N = (uint8_t) (0x01 << 1),
+    KX123_Z_P = (uint8_t) (0x01 << 0)
+    };
+
+#define KX123_AXIS_MASK 0x3f
+
+enum e_interrupt_reason {
+    KX123_FREEFALL          = (uint8_t) (0x01 << 7),
+    KX122_BUFFER_FULL       = (uint8_t) (0x01 << 6),
+    KX122_WATERMARK         = (uint8_t) (0x01 << 5),
+    KX122_DATAREADY         = (uint8_t) (0x01 << 4),
+    KX122_DOUBLE_TAP        = (uint8_t) (0x02 << 2),
+    KX122_SINGLE_TAP        = (uint8_t) (0x01 << 2),
+    KX122_MOTION_INTERRUPT  = (uint8_t) (0x01 << 1),
+    KX122_TILT_CHANGED      = (uint8_t) (0x01 << 0)
+    };
+
+    
 /* registers */
 // x- hp filter output
 #define KX122_XHP_L 0x00