diff --git a/Documentation/index.rst b/Documentation/index.rst index d9a7339a5fcb8f241fad2a66395d91ed318123f9..69e8cb9f07b0814aad4ac326142acff423eda5db 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/leds pycardium/vibra diff --git a/Documentation/pycardium/bma400.rst b/Documentation/pycardium/bma400.rst new file mode 100644 index 0000000000000000000000000000000000000000..77e5390a57c88edc97b0376e92444cf0d47a8ba1 --- /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 4e52d85c51c38d897f38dbee03ed96cbbb7b0de1..7c0ce6d33038bf51cbad4abfb564610d4e1aa5da 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -15,6 +15,7 @@ #define API_VIBRA_SET 0x4 #define API_VIBRA_VIBRATE 0x5 #define API_STREAM_READ 0x6 +#define API_BMA400_GET_ACCEL 0x99 /* clang-format on */ /** @@ -120,6 +121,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)); + /** * Misc * ==== diff --git a/epicardium/modules/bma400.c b/epicardium/modules/bma400.c new file mode 100644 index 0000000000000000000000000000000000000000..8520aa68e41fa7cf8f759268db6434bb116f72c0 --- /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 a038fb21d7f5ef7c650a10851503150a27c0b1eb..42e1c7aed16da454684ef54cd263b0c5f201975f 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -1,5 +1,6 @@ module_sources = files( 'fatfs.c', + 'bma400.c', 'leds.c', 'log.c', 'pmic.c', diff --git a/pycardium/meson.build b/pycardium/meson.build index 67698112ed8be52f569fec2d4d087371bbe5cb91..371a0017ccd525990e0495fbf78acb3d49bccc13 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -2,6 +2,7 @@ name = 'pycardium' modsrc = files( 'modules/utime.c', + 'modules/bma400.c', 'modules/leds.c', 'modules/vibra.c', ) diff --git a/pycardium/modules/bma400.c b/pycardium/modules/bma400.c new file mode 100644 index 0000000000000000000000000000000000000000..e09e169534d4e4403df8a7cf1cd73c101f46c1e6 --- /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 2a6dcbe6beb7ebe5a4a5a29226b7eca0a6b9c901..faff67e9000d254902b5fc2ce521275789937b85 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 a99c63acfe3d50a083b6a428e1b511d41edf13e5..3851faf5028df767dc12472f03acb9fd71010d86 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -37,6 +37,7 @@ /* Modules */ #define MODULE_UTIME_ENABLED (1) #define MODULE_LEDS_ENABLED (1) +#define MODULE_BMA400_ENABLED (1) #define MODULE_VIBRA_ENABLED (1) /*