diff --git a/README.md b/README.md index e64e137..c09e1c7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A C89 driver for the 3-axis accelerometer LIS3DH. Supports both I2C and SPI. > - Free-fall detection > - Single-click detection > - Double-click detection -> - 4D/6D orientation detection (soon) +> - 4D/6D orientation detection ## Examples diff --git a/example/4d-movement.c b/example/4d-movement.c new file mode 100644 index 0000000..06e041a --- /dev/null +++ b/example/4d-movement.c @@ -0,0 +1,135 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "lis3dh.h" +#include "interrupt.h" +#include "i2c.h" + +#define GPIO_INTERRUPT_PIN_INT1 12 + +int main() { + + lis3dh_t lis; + + /* set fn ptrs to rw on bus (i2c or SPI) */ + lis.dev.init = i2c_init; + lis.dev.read = i2c_read; + lis.dev.write = i2c_write; + lis.dev.sleep = usleep; + lis.dev.deinit = i2c_deinit; + + /* initialise LIS3DH struct */ + if (lis3dh_init(&lis)) { + /* error handling */ + } + + /* reset device because it sometimes corrupts itself */ + if (lis3dh_reset(&lis)) { + /* error handling */ + } + + /* register interrupt */ + if (int_register(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* set up config */ + lis.cfg.mode = LIS3DH_MODE_HR; + lis.cfg.range = LIS3DH_FS_2G; + lis.cfg.rate = LIS3DH_ODR_400_HZ; + + lis.cfg.pin1.ia1 = 1; /* allow INT1 through INT_PIN1 */ + + /* 1 LSb = 16 mg @ FS_2G + * 0.1g threshold = 100/16 = 6.25 + * add read error, +40mg => 140/16 ~= 9 + */ + lis.cfg.int1_ths = 9; + + /* Duration time is measured in N/ODR where: + * --- N = The content of the intX_dur integer + * --- ODR = the data rate, eg 100, 400... + * [ODR] [1 LSb in milliseconds] + * 400 2.5 + * + * For ODR=400: + * 10 ms => 10/2.5 = 5 + * lis.cfg.int1_dur = 5; <== 10 ms minimum duration to trig + */ + lis.cfg.int1_dur = 0; /* instantaneous */ + + /* enable Y-axis and X-axis, + and - */ + lis.cfg.int1.yh = 1; + lis.cfg.int1.xh = 1; + lis.cfg.int1.yl = 1; + lis.cfg.int1.xl = 1; + + /* 4D movement recognition */ + lis.cfg.int1.aoi = 0; + lis.cfg.int1.en_6d = 1; + lis.cfg.int1.en_4d = 1; + + /* latch interrupt */ + lis.cfg.int1.latch = 1; + + /* set up a HP filter to ignore constant earth acceleration */ + lis.cfg.filter.mode = LIS3DH_FILTER_MODE_NORMAL_REF; + lis.cfg.filter.cutoff = LIS3DH_FILTER_CUTOFF_8; + lis.cfg.filter.ia1 = 1; /* enable filter for INT1 generator */ + + /* write device config */ + if (lis3dh_configure(&lis)) { + /* error handling */ + } + + /* read REFERENCE to set filter to current accel field */ + if (lis3dh_reference(&lis)) { + /* error handling */ + } + + /* read INT1_SRC to clear old interrupt if any */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + for( ;; ) { + + /* wait for INT1 to go active */ + if (int_poll(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* read INT1_SRC and by doing so clear IA on device */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + /* only print if INT1 interrupt active = 1*/ + /* ZH and ZL are always 0 in 4D mode */ + if (LIS3DH_INT_SRC_IA(lis.src.int1)) { + /* print received interrupt .. */ + printf("IA=%d ZH=%d ZL=%d YH=%d YL=%d XH=%d XL=%d\n", + LIS3DH_INT_SRC_IA(lis.src.int1), + LIS3DH_INT_SRC_Z_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Z_LOW(lis.src.int1), + LIS3DH_INT_SRC_Y_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Y_LOW(lis.src.int1), + LIS3DH_INT_SRC_X_HIGH(lis.src.int1), + LIS3DH_INT_SRC_X_LOW(lis.src.int1)); + } + } + + /* unregister interrupt */ + if (int_unregister(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* deinitalise struct */ + if (lis3dh_deinit(&lis)) { + /* error handling */ + } + + return 0; +} \ No newline at end of file diff --git a/example/4d-position.c b/example/4d-position.c new file mode 100644 index 0000000..f3cae98 --- /dev/null +++ b/example/4d-position.c @@ -0,0 +1,135 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "lis3dh.h" +#include "interrupt.h" +#include "i2c.h" + +#define GPIO_INTERRUPT_PIN_INT1 12 + +int main() { + + lis3dh_t lis; + + /* set fn ptrs to rw on bus (i2c or SPI) */ + lis.dev.init = i2c_init; + lis.dev.read = i2c_read; + lis.dev.write = i2c_write; + lis.dev.sleep = usleep; + lis.dev.deinit = i2c_deinit; + + /* initialise LIS3DH struct */ + if (lis3dh_init(&lis)) { + /* error handling */ + } + + /* reset device because it sometimes corrupts itself */ + if (lis3dh_reset(&lis)) { + /* error handling */ + } + + /* register interrupt */ + if (int_register(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* set up config */ + lis.cfg.mode = LIS3DH_MODE_HR; + lis.cfg.range = LIS3DH_FS_2G; + lis.cfg.rate = LIS3DH_ODR_400_HZ; + + lis.cfg.pin1.ia1 = 1; /* allow INT1 through INT_PIN1 */ + + /* 1 LSb = 16 mg @ FS_2G + * 0.1g threshold = 100/16 = 6.25 + * add read error, +40mg => 140/16 ~= 9 + */ + lis.cfg.int1_ths = 9; + + /* Duration time is measured in N/ODR where: + * --- N = The content of the intX_dur integer + * --- ODR = the data rate, eg 100, 400... + * [ODR] [1 LSb in milliseconds] + * 400 2.5 + * + * For ODR=400: + * 10 ms => 10/2.5 = 5 + * lis.cfg.int1_dur = 5; <== 10 ms minimum duration to trig + */ + lis.cfg.int1_dur = 0; /* instantaneous */ + + /* enable Y-axis and X-axis, + and - */ + lis.cfg.int1.yh = 1; + lis.cfg.int1.xh = 1; + lis.cfg.int1.yl = 1; + lis.cfg.int1.xl = 1; + + /* 4D position recognition */ + lis.cfg.int1.aoi = 1; + lis.cfg.int1.en_6d = 1; + lis.cfg.int1.en_4d = 1; + + /* latch interrupt */ + lis.cfg.int1.latch = 1; + + /* set up a HP filter to ignore constant earth acceleration */ + lis.cfg.filter.mode = LIS3DH_FILTER_MODE_NORMAL_REF; + lis.cfg.filter.cutoff = LIS3DH_FILTER_CUTOFF_8; + lis.cfg.filter.ia1 = 1; /* enable filter for INT1 generator */ + + /* write device config */ + if (lis3dh_configure(&lis)) { + /* error handling */ + } + + /* read REFERENCE to set filter to current accel field */ + if (lis3dh_reference(&lis)) { + /* error handling */ + } + + /* read INT1_SRC to clear old interrupt if any */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + for( ;; ) { + + /* wait for INT1 to go active */ + if (int_poll(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* read INT1_SRC and by doing so clear IA on device */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + /* only print if INT1 interrupt active = 1*/ + /* ZH and ZL are always 0 in 4D mode */ + if (LIS3DH_INT_SRC_IA(lis.src.int1)) { + /* print received interrupt .. */ + printf("IA=%d ZH=%d ZL=%d YH=%d YL=%d XH=%d XL=%d\n", + LIS3DH_INT_SRC_IA(lis.src.int1), + LIS3DH_INT_SRC_Z_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Z_LOW(lis.src.int1), + LIS3DH_INT_SRC_Y_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Y_LOW(lis.src.int1), + LIS3DH_INT_SRC_X_HIGH(lis.src.int1), + LIS3DH_INT_SRC_X_LOW(lis.src.int1)); + } + } + + /* unregister interrupt */ + if (int_unregister(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* deinitalise struct */ + if (lis3dh_deinit(&lis)) { + /* error handling */ + } + + return 0; +} \ No newline at end of file diff --git a/example/6d-movement.c b/example/6d-movement.c new file mode 100644 index 0000000..fc49a11 --- /dev/null +++ b/example/6d-movement.c @@ -0,0 +1,135 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "lis3dh.h" +#include "interrupt.h" +#include "i2c.h" + +#define GPIO_INTERRUPT_PIN_INT1 12 + +int main() { + + lis3dh_t lis; + + /* set fn ptrs to rw on bus (i2c or SPI) */ + lis.dev.init = i2c_init; + lis.dev.read = i2c_read; + lis.dev.write = i2c_write; + lis.dev.sleep = usleep; + lis.dev.deinit = i2c_deinit; + + /* initialise LIS3DH struct */ + if (lis3dh_init(&lis)) { + /* error handling */ + } + + /* reset device because it sometimes corrupts itself */ + if (lis3dh_reset(&lis)) { + /* error handling */ + } + + /* register interrupt */ + if (int_register(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* set up config */ + lis.cfg.mode = LIS3DH_MODE_HR; + lis.cfg.range = LIS3DH_FS_2G; + lis.cfg.rate = LIS3DH_ODR_400_HZ; + + lis.cfg.pin1.ia1 = 1; /* allow INT1 through INT_PIN1 */ + + /* 1 LSb = 16 mg @ FS_2G + * 0.1g threshold = 100/16 = 6.25 + * add read error, +40mg => 140/16 ~= 9 + */ + lis.cfg.int1_ths = 9; + + /* Duration time is measured in N/ODR where: + * --- N = The content of the intX_dur integer + * --- ODR = the data rate, eg 100, 400... + * [ODR] [1 LSb in milliseconds] + * 400 2.5 + * + * For ODR=400: + * 10 ms => 10/2.5 = 5 + * lis.cfg.int1_dur = 5; <== 10 ms minimum duration to trig + */ + lis.cfg.int1_dur = 0; /* instantaneous */ + + /* enable all axes */ + lis.cfg.int1.yh = 1; + lis.cfg.int1.zh = 1; + lis.cfg.int1.xh = 1; + lis.cfg.int1.yl = 1; + lis.cfg.int1.zl = 1; + lis.cfg.int1.xl = 1; + + /* 6D movement recognition */ + lis.cfg.int1.aoi = 0; + lis.cfg.int1.en_6d = 1; + + /* latch interrupt */ + lis.cfg.int1.latch = 1; + + /* set up a HP filter to ignore constant earth acceleration */ + lis.cfg.filter.mode = LIS3DH_FILTER_MODE_NORMAL_REF; + lis.cfg.filter.cutoff = LIS3DH_FILTER_CUTOFF_8; + lis.cfg.filter.ia1 = 1; /* enable filter for INT1 generator */ + + /* write device config */ + if (lis3dh_configure(&lis)) { + /* error handling */ + } + + /* read REFERENCE to set filter to current accel field */ + if (lis3dh_reference(&lis)) { + /* error handling */ + } + + /* read INT1_SRC to clear old interrupt if any */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + for( ;; ) { + + /* wait for INT1 to go active */ + if (int_poll(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* read INT1_SRC and by doing so clear IA on device */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + /* only print if INT1 interrupt active = 1*/ + if (LIS3DH_INT_SRC_IA(lis.src.int1)) { + /* print received interrupt .. */ + printf("IA=%d ZH=%d ZL=%d YH=%d YL=%d XH=%d XL=%d\n", + LIS3DH_INT_SRC_IA(lis.src.int1), + LIS3DH_INT_SRC_Z_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Z_LOW(lis.src.int1), + LIS3DH_INT_SRC_Y_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Y_LOW(lis.src.int1), + LIS3DH_INT_SRC_X_HIGH(lis.src.int1), + LIS3DH_INT_SRC_X_LOW(lis.src.int1)); + } + } + + /* unregister interrupt */ + if (int_unregister(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* deinitalise struct */ + if (lis3dh_deinit(&lis)) { + /* error handling */ + } + + return 0; +} \ No newline at end of file diff --git a/example/6d-position.c b/example/6d-position.c new file mode 100644 index 0000000..bafb0c3 --- /dev/null +++ b/example/6d-position.c @@ -0,0 +1,135 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include "lis3dh.h" +#include "interrupt.h" +#include "i2c.h" + +#define GPIO_INTERRUPT_PIN_INT1 12 + +int main() { + + lis3dh_t lis; + + /* set fn ptrs to rw on bus (i2c or SPI) */ + lis.dev.init = i2c_init; + lis.dev.read = i2c_read; + lis.dev.write = i2c_write; + lis.dev.sleep = usleep; + lis.dev.deinit = i2c_deinit; + + /* initialise LIS3DH struct */ + if (lis3dh_init(&lis)) { + /* error handling */ + } + + /* reset device because it sometimes corrupts itself */ + if (lis3dh_reset(&lis)) { + /* error handling */ + } + + /* register interrupt */ + if (int_register(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* set up config */ + lis.cfg.mode = LIS3DH_MODE_HR; + lis.cfg.range = LIS3DH_FS_2G; + lis.cfg.rate = LIS3DH_ODR_400_HZ; + + lis.cfg.pin1.ia1 = 1; /* allow INT1 through INT_PIN1 */ + + /* 1 LSb = 16 mg @ FS_2G + * 0.1g threshold = 100/16 = 6.25 + * add read error, +40mg => 140/16 ~= 9 + */ + lis.cfg.int1_ths = 9; + + /* Duration time is measured in N/ODR where: + * --- N = The content of the intX_dur integer + * --- ODR = the data rate, eg 100, 400... + * [ODR] [1 LSb in milliseconds] + * 400 2.5 + * + * For ODR=400: + * 10 ms => 10/2.5 = 5 + * lis.cfg.int1_dur = 5; <== 10 ms minimum duration to trig + */ + lis.cfg.int1_dur = 0; /* instantaneous */ + + /* enable all axes */ + lis.cfg.int1.yh = 1; + lis.cfg.int1.zh = 1; + lis.cfg.int1.xh = 1; + lis.cfg.int1.yl = 1; + lis.cfg.int1.zl = 1; + lis.cfg.int1.xl = 1; + + /* 6D position recognition */ + lis.cfg.int1.aoi = 1; + lis.cfg.int1.en_6d = 1; + + /* latch interrupt */ + lis.cfg.int1.latch = 1; + + /* set up a HP filter to ignore constant earth acceleration */ + lis.cfg.filter.mode = LIS3DH_FILTER_MODE_NORMAL_REF; + lis.cfg.filter.cutoff = LIS3DH_FILTER_CUTOFF_8; + lis.cfg.filter.ia1 = 1; /* enable filter for INT1 generator */ + + /* write device config */ + if (lis3dh_configure(&lis)) { + /* error handling */ + } + + /* read REFERENCE to set filter to current accel field */ + if (lis3dh_reference(&lis)) { + /* error handling */ + } + + /* read INT1_SRC to clear old interrupt if any */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + for( ;; ) { + + /* wait for INT1 to go active */ + if (int_poll(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* read INT1_SRC and by doing so clear IA on device */ + if (lis3dh_read_int1(&lis)) { + /* error handling */ + } + + /* only print if INT1 interrupt active = 1*/ + if (LIS3DH_INT_SRC_IA(lis.src.int1)) { + /* print received interrupt .. */ + printf("IA=%d ZH=%d ZL=%d YH=%d YL=%d XH=%d XL=%d\n", + LIS3DH_INT_SRC_IA(lis.src.int1), + LIS3DH_INT_SRC_Z_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Z_LOW(lis.src.int1), + LIS3DH_INT_SRC_Y_HIGH(lis.src.int1), + LIS3DH_INT_SRC_Y_LOW(lis.src.int1), + LIS3DH_INT_SRC_X_HIGH(lis.src.int1), + LIS3DH_INT_SRC_X_LOW(lis.src.int1)); + } + } + + /* unregister interrupt */ + if (int_unregister(GPIO_INTERRUPT_PIN_INT1)) { + /* error handling */ + } + + /* deinitalise struct */ + if (lis3dh_deinit(&lis)) { + /* error handling */ + } + + return 0; +} \ No newline at end of file diff --git a/example/README.md b/example/README.md index e644df6..16bd2a1 100644 --- a/example/README.md +++ b/example/README.md @@ -71,4 +71,27 @@ Inertial interrupt example in OR mode (easily changed to AND mode) with configur ### file: free-fall.c -Inertial interrupt example activating upon free-fall. It works by using an AND mode interrupt of all the negative axes and comparing them to a threshold value (in the case of negative axis the threshold is multiplied by -1), recommended to be at 350mg (for >30 ms) and activating when the experienced negative acceleration is greater (abs. sense) than the negative threshold. \ No newline at end of file +Inertial interrupt example activating upon free-fall. It works by using an AND mode interrupt of all the negative axes and comparing them to a threshold value (in the case of negative axis the threshold is multiplied by -1), recommended to be at 350mg (for >30 ms) and activating when the experienced negative acceleration is greater (abs. sense) than the negative threshold. + +### file: 6d-movement.c + +Inertial interrupt example, generates an interrupt when some acceleration, `threshold` is experienced on any configured axis for `duration` time. Supposedly the device knows what a "known" direction is. + +### file: 6d-position.c + +Inertial interrupt example, the interrupt line is kept active so long as the device is stable (ie acceleration on configured axes does not exceed `threshold` for `duration` time). + +--- + +### 4D detection + +4D detection is a subset of 6D detection meant for detecting portrait/landscape screen rotations on mobile phones, etc. It functionally works the same as the 6D modes, except that detection along the Z-axis is disabled. + +### file: 4d-movement.c + +Inertial interrupt example, generates an interrupt when some acceleration, `threshold` is experienced on any configured axis for `duration` time. Supposedly the device knows what a "known" direction is. + +### file: 4d-position.c + +Inertial interrupt example, the interrupt line is kept active so long as the device is stable (ie acceleration on configured axes does not exceed `threshold` for `duration` time). +