diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 92bb5ade7bbee771bcb7118a7a6233c2e52823fa..0b440189db0de69265428055ae0fc5bee070ca1a 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -138,6 +138,8 @@ typedef _Bool bool; #define API_USB_STORAGE 0x111 #define API_USB_CDCACM 0x112 +#define API_WS2812_WRITE 0x0120 + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -1843,4 +1845,20 @@ API(API_USB_STORAGE, int epic_usb_storage(void)); */ API(API_USB_CDCACM, int epic_usb_cdcacm(void)); +/** + * WS2812 + * ====== + */ + +/** + * Takes a gpio pin specified with the gpio module and transmits + * the led data. The format `GG:RR:BB` is expected. + * + * :param uint8_t pin: The gpio pin to be used for data. + * :param uint8_t * pixels: The buffer, in which the pixel data is stored. + * :param uint32_t n_bytes: The size of the buffer. + */ +API(API_WS2812_WRITE, void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes)); + #endif /* _EPICARDIUM_H */ + diff --git a/epicardium/modules/gpio.c b/epicardium/modules/gpio.c index 6b6977391f878fa58ad6618b98d08dcd9cf7f5a7..a923812f5f49d90604bdce150a29d01821dc9ff6 100644 --- a/epicardium/modules/gpio.c +++ b/epicardium/modules/gpio.c @@ -11,7 +11,7 @@ * Despite what the schematic (currently, 2019-08-18) says these are the correct * pins for wristband GPIO 1-4 (not 0-3 as the schematic states) */ -static gpio_cfg_t gpio_configs[] = { +gpio_cfg_t gpio_configs[] = { [EPIC_GPIO_WRISTBAND_1] = { PORT_0, PIN_21, GPIO_FUNC_OUT, diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 628249a305098911b6dfe3c861b8759d6d5a5a62..e943af4fedd6f9996ccb66d098388c17b9549297 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -23,4 +23,5 @@ module_sources = files( 'watchdog.c', 'usb.c', 'config.c', + 'ws2812.c' ) diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 27e6e28ad32cbf5a6b4e34b26267edb555f3840c..330f4f67e9c056df601f12a560029047c1872b59 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -3,6 +3,7 @@ #include "FreeRTOS.h" #include "semphr.h" +#include "gpio.h" #include <stdint.h> #include <stdbool.h> @@ -106,4 +107,8 @@ void vBhi160Task(void *pvParameters); void vMAX30001Task(void *pvParameters); void max30001_mutex_init(void); +/* ---------- GPIO --------------------------------------------------------- */ +#define MAX30001_MUTEX_WAIT_MS 50 +extern gpio_cfg_t gpio_configs[]; + #endif /* MODULES_H */ diff --git a/epicardium/modules/ws2812.c b/epicardium/modules/ws2812.c new file mode 100644 index 0000000000000000000000000000000000000000..4d16e5a20b4d7ee90a0e36f1bd275bc60140dcce --- /dev/null +++ b/epicardium/modules/ws2812.c @@ -0,0 +1,76 @@ +#include "leds.h" +#include "pmic.h" +#include "FreeRTOS.h" +#include "task.h" +#include "epicardium.h" +#include "max32665.h" +#include "gpio.h" +#include "modules.h" + +#include <stdbool.h> + +#define OVERHEAD 33 + +#define EPIC_WS2812_ZERO_HIGH_TICKS 34 - OVERHEAD +#define EPIC_WS2812_ZERO_LOW_TICKS 86 - OVERHEAD +#define EPIC_WS2812_ONE_HIGH_TICKS 86 - OVERHEAD +#define EPIC_WS2812_ONE_LOW_TICKS 34 - OVERHEAD +#define EPIC_WS2812_RESET_TCKS 4800 - OVERHEAD + +static volatile uint32_t counter = 0; + +static inline __attribute__((always_inline)) void +epic_ws2812_delay_ticks(uint32_t ticks) +{ + counter = ticks; + while (--counter) { + }; +} + +static inline __attribute__((always_inline)) void +epic_ws2812_transmit_bit(uint32_t pin, uint8_t bit) +{ + if (bit != 0) { + GPIO_OutSet(pin); + epic_ws2812_delay_ticks(EPIC_WS2812_ONE_HIGH_TICKS); + GPIO_OutClr(pin); + epic_ws2812_delay_ticks(EPIC_WS2812_ONE_LOW_TICKS); + } else { + GPIO_OutSet(pin); + epic_ws2812_delay_ticks(EPIC_WS2812_ZERO_HIGH_TICKS); + GPIO_OutClr(pin); + epic_ws2812_delay_ticks(EPIC_WS2812_ZERO_LOW_TICKS); + } +} + +static inline __attribute__((always_inline)) void +epic_ws2812_transmit_byte(uint32_t pin, uint8_t byte) +{ + epic_ws2812_transmit_bit(pin, byte & 0b10000000); + epic_ws2812_transmit_bit(pin, byte & 0b01000000); + epic_ws2812_transmit_bit(pin, byte & 0b00100000); + epic_ws2812_transmit_bit(pin, byte & 0b00010000); + epic_ws2812_transmit_bit(pin, byte & 0b00001000); + epic_ws2812_transmit_bit(pin, byte & 0b00000100); + epic_ws2812_transmit_bit(pin, byte & 0b00000010); + epic_ws2812_transmit_bit(pin, byte & 0b00000001); +} + +void epic_ws2812_write(uint8_t pin, uint8_t *pixels, uint32_t n_bytes) +{ + uint8_t *pixels_end = pixels + n_bytes; + gpio_cfg_t *pin_cfg = &gpio_configs[pin]; + + taskENTER_CRITICAL(); + + epic_gpio_set_pin_mode(pin, EPIC_GPIO_MODE_OUT); + + do { + epic_ws2812_transmit_byte(pin_cfg, *pixels); + } while (++pixels != pixels_end); + + GPIO_OutClr(pin); + epic_ws2812_delay_ticks(EPIC_WS2812_RESET_TCKS); + + taskEXIT_CRITICAL(); +} diff --git a/pycardium/meson.build b/pycardium/meson.build index 32973bae0c945a0208ba4c1040367dac75b84fb9..1d45e9704351cc8450275bdc66a9263ce7316cfc 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -16,7 +16,8 @@ modsrc = files( 'modules/sys_display.c', 'modules/utime.c', 'modules/vibra.c', - 'modules/bme680.c' + 'modules/bme680.c', + 'modules/ws2812.c' ) ################################# diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 68c40810e0c286d57e689653f007adb360982096..50178cc8e578158a6362add774f04d67c5e50715 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -170,3 +170,7 @@ Q(COMMUNICATION) Q(CAMP) Q(MAX30001_ECG) + +/* ws2812 */ +Q(ws2812) +Q(set_all) diff --git a/pycardium/modules/ws2812.c b/pycardium/modules/ws2812.c new file mode 100644 index 0000000000000000000000000000000000000000..4df1d84a047981ef28d00d3f3801d1845cd44a46 --- /dev/null +++ b/pycardium/modules/ws2812.c @@ -0,0 +1,53 @@ +#include "epicardium.h" + +#include "py/obj.h" + +#include <stdlib.h> +#include <stdio.h> + +/* Define the pixel set_all function in this module */ +static mp_obj_t mp_ws2812_set_all(mp_obj_t pin, mp_obj_t color_in) +{ + mp_int_t pin_int = mp_obj_get_int(pin); + mp_int_t len = mp_obj_get_int(mp_obj_len(color_in)); + mp_int_t pixels_len = len * 3; + uint8_t *pixels_arr = alloca(pixels_len * sizeof(uint8_t)); + + for (int i = 0; i < len; i++) { + mp_obj_t color = mp_obj_subscr( + color_in, mp_obj_new_int(i), MP_OBJ_SENTINEL + ); + + pixels_arr[i * 3] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(1), MP_OBJ_SENTINEL) + ); + pixels_arr[i * 3 + 1] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(0), MP_OBJ_SENTINEL) + ); + pixels_arr[i * 3 + 2] = mp_obj_get_int(mp_obj_subscr( + color, mp_obj_new_int(2), MP_OBJ_SENTINEL) + ); + } + + /* call epicardium to be fast enough */ + epic_ws2812_write(pin_int, pixels_arr, pixels_len); + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(ws2812_set_all_obj, mp_ws2812_set_all); + +/* The globals table for this module */ +static const mp_rom_map_elem_t ws2812_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ws2812) }, + { MP_ROM_QSTR(MP_QSTR_set_all), MP_ROM_PTR(&ws2812_set_all_obj) }, +}; +static MP_DEFINE_CONST_DICT(ws2812_module_globals, ws2812_module_globals_table); + +const mp_obj_module_t ws2812_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&ws2812_module_globals, +}; + +/* This is a special macro that will make MicroPython aware of this module */ +/* clang-format off */ +MP_REGISTER_MODULE(MP_QSTR_ws2812, ws2812_module, MODULE_WS2812_ENABLED); diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 8ee59352b18439b511b6ae3e99d66742b58b945d..767a69fbd78465b10c0ec5ea54608c8a0270670b 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -60,6 +60,7 @@ int mp_hal_trng_read_int(void); #define MODULE_POWER_ENABLED (1) #define MODULE_UTIME_ENABLED (1) #define MODULE_VIBRA_ENABLED (1) +#define MODULE_WS2812_ENABLED (1) /* * This port is intended to be 32-bit, but unfortunately, int32_t for