From 91b2c78a43d143fc95b32449b02f00594fdcc5c8 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Sat, 6 Jul 2019 23:26:06 +0200 Subject: [PATCH] feat(bma400): Add basic API and pycardium module Signed-off-by: Rahix <rahix@rahix.de> --- Documentation/index.rst | 1 + Documentation/pycardium/bma400.rst | 8 +++ epicardium/epicardium.h | 33 +++++++++ epicardium/modules/bma400.c | 104 +++++++++++++++++++++++++++++ epicardium/modules/meson.build | 1 + pycardium/meson.build | 1 + pycardium/modules/bma400.c | 37 ++++++++++ pycardium/modules/qstrdefs.h | 4 ++ pycardium/mpconfigport.h | 1 + 9 files changed, 190 insertions(+) create mode 100644 Documentation/pycardium/bma400.rst create mode 100644 epicardium/modules/bma400.c create mode 100644 pycardium/modules/bma400.c diff --git a/Documentation/index.rst b/Documentation/index.rst index 1fe90f2e..f4b3defd 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -21,6 +21,7 @@ Last but not least, if you want to start hacking the lower-level firmware, the :caption: Pycardium pycardium/overview + pycardium/bma400 pycardium/color pycardium/display pycardium/leds diff --git a/Documentation/pycardium/bma400.rst b/Documentation/pycardium/bma400.rst new file mode 100644 index 00000000..77e5390a --- /dev/null +++ b/Documentation/pycardium/bma400.rst @@ -0,0 +1,8 @@ +``bma400`` - Accelerometer +========================== + +.. py:function:: bma400.get_accel() + + Get acceleration vector. + + :return: Tuple containing ``x``, ``y``, and ``z`` acceleration. diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index cb3d9b42..5828c06b 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -67,6 +67,8 @@ typedef unsigned int size_t; #define API_LIGHT_SENSOR_RUN 0x80 #define API_LIGHT_SENSOR_GET 0x81 #define API_LIGHT_SENSOR_STOP 0x82 + +#define API_BMA400_GET_ACCEL 0x90 /* clang-format on */ typedef uint32_t api_int_id_t; @@ -279,6 +281,37 @@ API(API_LEDS_SET, void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)); */ API(API_STREAM_READ, int epic_stream_read(int sd, void *buf, size_t count)); +/** + * BMA400 + * ====== + */ + +/** + * An acceleration vector. + */ +struct acceleration { + /** Acceleration component along x axis. */ + float x; + /** Acceleration component along y axis. */ + float y; + /** Acceleration component along z axis. */ + float z; +}; + +/** + * Get the current acceleration vector. + * + * :param data: Where to store the acceleration vector. + * :return: 0 on success or ``-Exxx`` on error. The following + * errors might occur: + * + * - ``-EFAULT``: On NULL-pointer. + * - ``-EINVAL``: Invalid configuration. + * - ``-EIO``: Communication with the device failed. + * - ``-ENODEV``: Device was not found. + */ +API(API_BMA400_GET_ACCEL, int epic_bma400_get_accel(struct acceleration *data)); + /** * Vibration Motor * =============== diff --git a/epicardium/modules/bma400.c b/epicardium/modules/bma400.c new file mode 100644 index 00000000..8520aa68 --- /dev/null +++ b/epicardium/modules/bma400.c @@ -0,0 +1,104 @@ +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> + +#include "bma400.h" +#include "bosch.h" +#include "card10.h" + +#include "epicardium.h" +#include "modules/log.h" + +static bool initialized; +static struct bma400_dev bma400; + +#define GRAVITY_EARTH (9.80665f) /* Earth's gravity in m/s^2 */ +static float lsb_to_ms2(int16_t val, float g_range, uint8_t bit_width) +{ + float half_scale = (float)(1 << bit_width) / 2.0f; + return GRAVITY_EARTH * val * g_range / half_scale; +} + +static int convert_error(int8_t error) +{ + switch (error) { + case BMA400_E_NULL_PTR: + return EFAULT; + case BMA400_E_COM_FAIL: + return EIO; + case BMA400_E_DEV_NOT_FOUND: + return ENODEV; + case BMA400_E_INVALID_CONFIG: + return EINVAL; + default: + return 1; + } +} + +int epic_bma400_get_accel(struct acceleration *data) +{ + uint8_t result; + + if (__builtin_expect(!initialized, 0)) { + bma400.intf_ptr = NULL; + bma400.delay_ms = card10_bosch_delay; + bma400.dev_id = BMA400_I2C_ADDRESS_SDO_LOW; + bma400.read = card10_bosch_i2c_read_ex; + bma400.write = card10_bosch_i2c_write_ex; + bma400.intf = BMA400_I2C_INTF; + + result = bma400_init(&bma400); + if (result != BMA400_OK) { + LOG_ERR("bma400", "init error: %d", result); + return -convert_error(result); + } + + result = bma400_soft_reset(&bma400); + if (result != BMA400_OK) { + LOG_ERR("bma400", "soft_reset error: %d", result); + return -convert_error(result); + } + + struct bma400_sensor_conf conf; + conf.type = BMA400_ACCEL; + + result = bma400_get_sensor_conf(&conf, 1, &bma400); + if (result != BMA400_OK) { + LOG_ERR("bma400", "get_sensor_conf error: %d", result); + return -convert_error(result); + } + + conf.param.accel.odr = BMA400_ODR_100HZ; + conf.param.accel.range = BMA400_2G_RANGE; + conf.param.accel.data_src = BMA400_DATA_SRC_ACCEL_FILT_1; + + result = bma400_set_sensor_conf(&conf, 1, &bma400); + if (result != BMA400_OK) { + LOG_ERR("bma400", "set_sensor_conf error: %d", result); + return -convert_error(result); + } + + result = bma400_set_power_mode(BMA400_LOW_POWER_MODE, &bma400); + if (result != BMA400_OK) { + LOG_ERR("bma400", "set_power_mode error: %d", result); + return -convert_error(result); + } + + initialized = true; + } + + struct bma400_sensor_data data_in; + result = bma400_get_accel_data( + BMA400_DATA_SENSOR_TIME, &data_in, &bma400 + ); + if (result != BMA400_OK) { + LOG_ERR("bma400", "get_accel_data error: %d\n", result); + return -convert_error(result); + } + + data->x = lsb_to_ms2(data_in.x, 2, 12); + data->y = lsb_to_ms2(data_in.y, 2, 12); + data->z = lsb_to_ms2(data_in.z, 2, 12); + + return 0; +} diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 113e3cb8..d67e6616 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -1,4 +1,5 @@ module_sources = files( + 'bma400.c', 'display.c', 'fatfs.c', 'fatfs_fileops.c', diff --git a/pycardium/meson.build b/pycardium/meson.build index cf5f5ba5..6e1f9260 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -1,6 +1,7 @@ name = 'pycardium' modsrc = files( + 'modules/bma400.c', 'modules/interrupt.c', 'modules/leds.c', 'modules/sys_display.c', diff --git a/pycardium/modules/bma400.c b/pycardium/modules/bma400.c new file mode 100644 index 00000000..e09e1695 --- /dev/null +++ b/pycardium/modules/bma400.c @@ -0,0 +1,37 @@ +#include "py/obj.h" +#include "py/objlist.h" +#include "py/runtime.h" + +#include "epicardium.h" + +static mp_obj_t mp_bma400_get_accel() +{ + struct acceleration values; + int ret = epic_bma400_get_accel(&values); + + if (ret < 0) { + mp_raise_OSError(-ret); + } + + mp_obj_t values_list[] = { + mp_obj_new_float(values.x), + mp_obj_new_float(values.y), + mp_obj_new_float(values.z), + }; + return mp_obj_new_tuple(3, values_list); +} +static MP_DEFINE_CONST_FUN_OBJ_0(bma400_get_accel_obj, mp_bma400_get_accel); + +static const mp_rom_map_elem_t bma400_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bma400) }, + { MP_ROM_QSTR(MP_QSTR_get_accel), MP_ROM_PTR(&bma400_get_accel_obj) }, +}; +static MP_DEFINE_CONST_DICT(bma400_module_globals, bma400_module_globals_table); + +const mp_obj_module_t bma400_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&bma400_module_globals, +}; + +/* Register the module to make it available in Python */ +MP_REGISTER_MODULE(MP_QSTR_bma400, bma400_module, MODULE_BMA400_ENABLED); diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index dedc57c0..b57dc36e 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -4,6 +4,10 @@ #define Q(x) #endif +/* bma400 */ +Q(bma400) +Q(get_accel) + /* leds */ Q(leds) Q(BOTTOM_LEFT) diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 233c01bb..0ae791f0 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -40,6 +40,7 @@ /* Modules */ #define MODULE_UTIME_ENABLED (1) #define MODULE_LEDS_ENABLED (1) +#define MODULE_BMA400_ENABLED (1) #define MODULE_VIBRA_ENABLED (1) #define MODULE_INTERRUPT_ENABLED (1) #define MODULE_DISPLAY_ENABLED (1) -- GitLab