diff --git a/Documentation/pycardium/bhi160.rst b/Documentation/pycardium/bhi160.rst index 350f43c72b9bffbfcfea8b398f572d270ac3e491..1aaa362e0f238177fe59e2310f0659fd8b51ac89 100644 --- a/Documentation/pycardium/bhi160.rst +++ b/Documentation/pycardium/bhi160.rst @@ -12,8 +12,16 @@ The coordinate system of the BHI160 sensor data looks like this: .. image:: ../static/bhi160-coordinates.png -All angles and angular velocities (like gyroscope, orientation) rotate counter -clockwise around their respective axis. +* The **accelerometer** axes are just the ones shown in the picture. +* The **gyroscope**'s angular velocities rotate counter clockwise around + their respective axis. +* The **orientation** sensor uses the following mapping: + + +---------------------+----------------------+-------------------+ + | X | Y | Z | + +=====================+======================+===================+ + | Azimuth (0° - 360°) | Pitch (-180° - 180°) | Roll (-90° - 90°) | + +---------------------+----------------------+-------------------+ **Example**: diff --git a/epicardium/FreeRTOSConfig.h b/epicardium/FreeRTOSConfig.h index 22c6dfb0f050c38c2d33b7f1d64b1e7940d8ea97..8ff4a4b468f9ea938e92491aa41eafb9f5dad1e3 100644 --- a/epicardium/FreeRTOSConfig.h +++ b/epicardium/FreeRTOSConfig.h @@ -50,6 +50,7 @@ #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelay 1 +#define INCLUDE_vTaskDelete 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 #define INCLUDE_xTimerPendFunctionCall 1 #define INCLUDE_xSemaphoreGetMutexHolder 1 diff --git a/epicardium/modules/bhi.c b/epicardium/modules/bhi.c index 1332c12ea0c3164b56087d45c4099fa21e894ec4..c767a8cc24979bb661e6421d90161e7c95151151 100644 --- a/epicardium/modules/bhi.c +++ b/epicardium/modules/bhi.c @@ -9,7 +9,6 @@ #include "FreeRTOS.h" #include "task.h" -#include "semphr.h" #include "queue.h" #include "api/interrupt-sender.h" @@ -18,9 +17,6 @@ #include "modules/modules.h" #include "modules/stream.h" -/* Ticks to wait when trying to acquire lock */ -#define LOCK_WAIT pdMS_TO_TICKS(BHI160_MUTEX_WAIT_MS) - /* BHI160 Firmware Blob. Contents are defined in libcard10. */ extern uint8_t bhy1_fw[]; @@ -57,8 +53,7 @@ static size_t start_index = 0; static TaskHandle_t bhi160_task_id = NULL; /* BHI160 Mutex */ -static StaticSemaphore_t bhi160_mutex_data; -static SemaphoreHandle_t bhi160_mutex = NULL; +static struct mutex bhi160_mutex = { 0 }; /* Streams */ static struct stream_info bhi160_streams[10]; @@ -66,6 +61,13 @@ static struct stream_info bhi160_streams[10]; /* Active */ static bool bhi160_sensor_active[10] = { 0 }; +/* + * Driver State: A flag that is set when an unrecoverable error occurred. + * Effectively, this means the sensor will be disabled until next reboot and any + * API calls will fail immediately. + */ +static bool bhi160_driver_b0rked = false; + /* -- Utilities -------------------------------------------------------- {{{ */ /* * Retrieve the data size for a sensor. This value is needed for the creation @@ -135,14 +137,12 @@ int epic_bhi160_enable_sensor( return -ENODEV; } - result = hwlock_acquire_timeout(HWLOCK_I2C, portMAX_DELAY); - if (result < 0) { - return result; - } + mutex_lock(&bhi160_mutex); + hwlock_acquire(HWLOCK_I2C); - if (xSemaphoreTake(bhi160_mutex, LOCK_WAIT) != pdTRUE) { - result = -EBUSY; - goto out_free_i2c; + if (bhi160_driver_b0rked) { + result = -ENODEV; + goto out_free; } struct stream_info *stream = &bhi160_streams[sensor_type]; @@ -152,12 +152,12 @@ int epic_bhi160_enable_sensor( xQueueCreate(config->sample_buffer_len, stream->item_size); if (stream->queue == NULL) { result = -ENOMEM; - goto out_free_both; + goto out_free; } result = stream_register(bhi160_lookup_sd(sensor_type), stream); if (result < 0) { - goto out_free_both; + goto out_free; } result = bhy_enable_virtual_sensor( @@ -170,16 +170,16 @@ int epic_bhi160_enable_sensor( config->dynamic_range /* dynamic range is sensor dependent */ ); if (result != BHY_SUCCESS) { - goto out_free_both; + goto out_free; } bhi160_sensor_active[sensor_type] = true; - result = bhi160_lookup_sd(sensor_type); + /* Return the sensor stream descriptor */ + result = bhi160_lookup_sd(sensor_type); -out_free_both: - xSemaphoreGive(bhi160_mutex); -out_free_i2c: +out_free: hwlock_release(HWLOCK_I2C); + mutex_unlock(&bhi160_mutex); return result; } @@ -192,36 +192,33 @@ int epic_bhi160_disable_sensor(enum bhi160_sensor_type sensor_type) return -ENODEV; } - result = hwlock_acquire_timeout(HWLOCK_I2C, portMAX_DELAY); - if (result < 0) { - return result; - } + mutex_lock(&bhi160_mutex); + hwlock_acquire(HWLOCK_I2C); - if (xSemaphoreTake(bhi160_mutex, LOCK_WAIT) != pdTRUE) { - result = -EBUSY; - goto out_free_i2c; + if (bhi160_driver_b0rked) { + result = -ENODEV; + goto out_free; } struct stream_info *stream = &bhi160_streams[sensor_type]; result = stream_deregister(bhi160_lookup_sd(sensor_type), stream); if (result < 0) { - goto out_free_both; + goto out_free; } vQueueDelete(stream->queue); stream->queue = NULL; result = bhy_disable_virtual_sensor(vs_id, VS_WAKEUP); if (result < 0) { - goto out_free_both; + goto out_free; } bhi160_sensor_active[sensor_type] = false; result = 0; -out_free_both: - xSemaphoreGive(bhi160_mutex); -out_free_i2c: +out_free: hwlock_release(HWLOCK_I2C); + mutex_unlock(&bhi160_mutex); return result; } @@ -347,15 +344,8 @@ static int bhi160_fetch_fifo(void) /* Number of bytes left in BHI160's FIFO buffer */ uint16_t bytes_left_in_fifo = 1; - result = hwlock_acquire_timeout(HWLOCK_I2C, portMAX_DELAY); - if (result < 0) { - return result; - } - - if (xSemaphoreTake(bhi160_mutex, LOCK_WAIT) != pdTRUE) { - result = -EBUSY; - goto out_free_i2c; - } + mutex_lock(&bhi160_mutex); + hwlock_acquire(HWLOCK_I2C); while (bytes_left_in_fifo) { /* Fill local FIFO buffer with as many bytes as possible */ @@ -398,9 +388,8 @@ static int bhi160_fetch_fifo(void) start_index = bytes_left; } - xSemaphoreGive(bhi160_mutex); -out_free_i2c: hwlock_release(HWLOCK_I2C); + mutex_unlock(&bhi160_mutex); return result; } @@ -412,7 +401,7 @@ static void bhi160_interrupt_callback(void *_) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - if (bhi160_task_id != NULL) { + if (bhi160_task_id != NULL && !bhi160_driver_b0rked) { vTaskNotifyGiveFromISR( bhi160_task_id, &xHigherPriorityTaskWoken ); @@ -426,33 +415,27 @@ void vBhi160Task(void *pvParameters) int ret; bhi160_task_id = xTaskGetCurrentTaskHandle(); - bhi160_mutex = xSemaphoreCreateMutexStatic(&bhi160_mutex_data); - /* - * Wait a little before initializing BHI160. - */ - vTaskDelay(pdMS_TO_TICKS(3)); - - int lockret = hwlock_acquire_timeout(HWLOCK_I2C, portMAX_DELAY); - if (lockret < 0) { - LOG_CRIT("bhi160", "Failed to acquire I2C lock!"); - vTaskDelay(portMAX_DELAY); - } - - /* Take Mutex during initialization, just in case */ - if (xSemaphoreTake(bhi160_mutex, 0) != pdTRUE) { - LOG_CRIT("bhi160", "Failed to acquire BHI160 mutex!"); - vTaskDelay(portMAX_DELAY); - } + mutex_create(&bhi160_mutex); + mutex_lock(&bhi160_mutex); + hwlock_acquire(HWLOCK_I2C); memset(bhi160_streams, 0x00, sizeof(bhi160_streams)); - /* Install interrupt callback */ + /* + * The BHI160, coming out of power-on-reset will hold its interrupt line + * high until a firmware image is loaded. Once that firmware is loaded + * and running, the interrupt line is deasserted and from then on, + * interrupts are signaled using a rising edge. + * + * So, initially we need to configure the IRQ for a falling edge, load + * the firmware and then reconfigure for a rising edge. + */ GPIO_Config(&bhi160_interrupt_pin); GPIO_RegisterCallback( &bhi160_interrupt_pin, bhi160_interrupt_callback, NULL ); - GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_RISING); + GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_FALLING); GPIO_IntEnable(&bhi160_interrupt_pin); NVIC_SetPriority( (IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port), 2 @@ -462,25 +445,37 @@ void vBhi160Task(void *pvParameters) /* Upload firmware */ ret = bhy_driver_init(bhy1_fw); if (ret) { - LOG_CRIT("bhi160", "BHy1 init failed!"); - vTaskDelay(portMAX_DELAY); + LOG_CRIT("bhi160", "BHy1 init failed! Disabling."); + + /* Disable BHI160 until next reboot */ + bhi160_driver_b0rked = true; + hwlock_release(HWLOCK_I2C); + mutex_unlock(&bhi160_mutex); + vTaskDelete(NULL); } - /* Wait for first interrupt */ + /* Wait for first interrupt, a falling edge */ hwlock_release(HWLOCK_I2C); - ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100)); - lockret = hwlock_acquire_timeout(HWLOCK_I2C, portMAX_DELAY); - if (lockret < 0) { - LOG_CRIT("bhi160", "Failed to acquire I2C lock!"); - vTaskDelay(portMAX_DELAY); + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)) == 0) { + LOG_CRIT( + "bhi160", + "Sensor firmware was not loaded correctly. Disabling." + ); + + /* Disable BHI160 until next reboot */ + bhi160_driver_b0rked = true; + mutex_unlock(&bhi160_mutex); + vTaskDelete(NULL); } + hwlock_acquire(HWLOCK_I2C); + + /* + * The firmware is now loaded; as stated above, we now need to + * reconfigure the IRQ for a rising edge. + */ + GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_RISING); /* Remap axes to match card10 layout */ - /* Due to a known issue (#133) the first call to - * bhy_mapping_matrix_set might fail. */ - bhy_mapping_matrix_set( - PHYSICAL_SENSOR_INDEX_ACC, bhi160_mapping_matrix - ); bhy_mapping_matrix_set( PHYSICAL_SENSOR_INDEX_ACC, bhi160_mapping_matrix ); @@ -494,8 +489,8 @@ void vBhi160Task(void *pvParameters) /* Set "SIC" matrix. TODO: Find out what this is about */ bhy_set_sic_matrix(bhi160_sic_array); - xSemaphoreGive(bhi160_mutex); hwlock_release(HWLOCK_I2C); + mutex_unlock(&bhi160_mutex); /* ----------------------------------------- */ diff --git a/preload/apps/bhi160/__init__.py b/preload/apps/bhi160/__init__.py index c1f4325092a83b4b23975f9057bec7b2caee3e67..a6de8fc23ade4b2b34d05f5f32031cd5dd12fb6b 100644 --- a/preload/apps/bhi160/__init__.py +++ b/preload/apps/bhi160/__init__.py @@ -14,19 +14,23 @@ STATUS_COLORS = [ color.GREEN, ] -with contextlib.ExitStack() as cx: - disp = cx.enter_context(display.open()) +def sensors(**args): + while True: + with bhi160.BHI160Orientation(**args) as s: + yield s, "Orientation" + with bhi160.BHI160Accelerometer(**args) as s: + yield s, "Accel" + with bhi160.BHI160Gyroscope(**args) as s: + yield s, "Gyro" + with bhi160.BHI160Magnetometer(**args) as s: + yield s, "Magnetic" + + +with display.open() as disp: args = {"sample_rate": 10, "sample_buffer_len": 20} - sensor_iter = itertools.cycle( - [ - (cx.enter_context(bhi160.BHI160Orientation(**args)), "Orientation"), - (cx.enter_context(bhi160.BHI160Accelerometer(**args)), "Accel"), - (cx.enter_context(bhi160.BHI160Gyroscope(**args)), "Gyro"), - (cx.enter_context(bhi160.BHI160Magnetometer(**args)), "Magnetic"), - ] - ) + sensor_iter = sensors(**args) sensor, sensor_name = next(sensor_iter) for ev in simple_menu.button_events(timeout=0.1):