diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index cf7a7f350e9f50a06e5692deff129a065a2f87d6..470a41853895ef3fbaec66ebea29cb2a8e68490c 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -35,6 +35,9 @@ typedef unsigned int size_t; #define API_STREAM_READ 0x6 #define API_INTERRUPT_ENABLE 0x7 #define API_INTERRUPT_DISABLE 0x8 +#define API_LIGHT_SENSOR_RUN 0x9 +#define API_LIGHT_SENSOR_GET 0xa +#define API_LIGHT_SENSOR_STOP 0xb #define API_DISP_OPEN 0x10 #define API_DISP_CLOSE 0x11 @@ -398,4 +401,42 @@ API(API_DISP_CIRC, uint16_t pixelsize) ); +/** + * Start continuous readout of the light sensor. Will read light level + * at preconfigured interval and make it available via `epic_light_sensor_get()`. + * + * If the continuous readout was already running, this function will silently pass. + * + * + * :return: `0` if the start was successful or a negative error value + * if an error occured. Possible errors: + * + * - ``-EBUSY``: The timer could not be scheduled. + */ +API(API_LIGHT_SENSOR_RUN, int epic_light_sensor_run()); + +/** + * Get the last light level measured by the continuous readout. + * + * :param uint16_t* value: where the last light level should be written. + * :return: `0` if the readout was successful or a negative error + * value. Possible errors: + * + * - ``-ENODATA``: Continuous readout not currently running. + */ +API(API_LIGHT_SENSOR_GET, int epic_light_sensor_get(uint16_t* value)); + + +/** + * Stop continuous readout of the light sensor. + * + * If the continuous readout wasn't running, this function will silently pass. + * + * :return: `0` if the stop was sucessful or a negative error value + * if an error occured. Possible errors: + * + * - ``-EBUSY``: The timer stop could not be scheduled. + */ +API(API_LIGHT_SENSOR_STOP, int epic_light_sensor_stop()); + #endif /* _EPICARDIUM_H */ diff --git a/epicardium/modules/light_sensor.c b/epicardium/modules/light_sensor.c new file mode 100644 index 0000000000000000000000000000000000000000..ea5c4ccc3bf012dbc7e730bd2818ffdec57aeb45 --- /dev/null +++ b/epicardium/modules/light_sensor.c @@ -0,0 +1,76 @@ +#include "FreeRTOS.h" +#include "timers.h" +#include "led.h" +#include "mxc_config.h" +#include "adc.h" +#include "gpio.h" +#include <errno.h> + +#define READ_FREQ pdMS_TO_TICKS(100) + +static uint16_t last_value; +static TimerHandle_t poll_timer; +static StaticTimer_t poll_timer_buffer; + +int epic_light_sensor_init() +{ + const sys_cfg_adc_t sys_adc_cfg = + NULL; /* No system specific configuration needed. */ + if (ADC_Init(0x9, &sys_adc_cfg) != E_NO_ERROR) { + return -EINVAL; + } + GPIO_Config(&gpio_cfg_adc7); + return 0; +} + +void readAdcCallback() +{ + ADC_StartConvert(ADC_CH_7, 0, 0); + ADC_GetData(&last_value); +} + +int epic_light_sensor_run() +{ + epic_light_sensor_init(); + + if (!poll_timer) { + poll_timer = xTimerCreateStatic( + "light_sensor_adc", + READ_FREQ, + pdTRUE, + NULL, + readAdcCallback, + &poll_timer_buffer + ); + // since &poll_timer_buffer is not NULL, xTimerCreateStatic should allways succeed, so + // we don't need to check for poll_timer being NULL. + } + if (xTimerIsTimerActive(poll_timer) == pdFALSE) { + if (xTimerStart(poll_timer, 0) != pdPASS) { + return -EBUSY; + } + } + return 0; +} + +int epic_light_sensor_stop() +{ + if (!poll_timer || xTimerIsTimerActive(poll_timer) == pdFALSE) { + // timer wasn't running (or never started), just silently pass + return 0; + } + + if (xTimerStop(poll_timer, 0) != pdPASS) { + return -EBUSY; + } + return 0; +} + +int epic_light_sensor_get(uint16_t *value) +{ + if (!poll_timer || !xTimerIsTimerActive(poll_timer)) { + return -ENODATA; + } + *value = last_value; + return 0; +} diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index d552623e237ef83efa144b5dbf7690f61e922deb..68c1b9d909170d69157fe844fe2100f119b00423 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -7,4 +7,5 @@ module_sources = files( 'serial.c', 'stream.c', 'vibra.c', + 'light_sensor.c', ) diff --git a/pycardium/meson.build b/pycardium/meson.build index ad48e91ccff44b82d88dfafbae695de210764947..895be888ee81bdd886b990d12f7c6d63321a6c2e 100644 --- a/pycardium/meson.build +++ b/pycardium/meson.build @@ -6,6 +6,7 @@ modsrc = files( 'modules/sys_display.c', 'modules/utime.c', 'modules/vibra.c', + 'modules/light_sensor.c' ) ################################# diff --git a/pycardium/modules/light_sensor.c b/pycardium/modules/light_sensor.c new file mode 100644 index 0000000000000000000000000000000000000000..39f612a91a87946f426b7b1292121b0d0518a9dd --- /dev/null +++ b/pycardium/modules/light_sensor.c @@ -0,0 +1,60 @@ +#include "py/obj.h" +#include "py/runtime.h" +#include "py/builtin.h" +#include "epicardium.h" + +STATIC mp_obj_t mp_light_sensor_start() +{ + int status = epic_light_sensor_run(); + if (status == -EBUSY) { + mp_raise_msg( + &mp_type_RuntimeError, "timer could not be scheduled" + ); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(light_sensor_start_obj, mp_light_sensor_start); + +STATIC mp_obj_t mp_light_sensor_get_reading() +{ + uint16_t last; + int status = epic_light_sensor_get(&last); + if (status == -ENODATA) { + mp_raise_ValueError("sensor not running"); + return mp_const_none; + } + return mp_obj_new_int_from_uint(last); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0( + light_sensor_get_obj, mp_light_sensor_get_reading +); + +STATIC mp_obj_t mp_light_sensor_stop() +{ + int status = epic_light_sensor_stop(); + if (status == -EBUSY) { + mp_raise_msg( + &mp_type_RuntimeError, "timer could not be stopped" + ); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(light_sensor_stop_obj, mp_light_sensor_stop); + +STATIC const mp_rom_map_elem_t light_sensor_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_light_sensor) }, + { MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&light_sensor_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&light_sensor_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_reading), MP_ROM_PTR(&light_sensor_get_obj) } +}; +STATIC MP_DEFINE_CONST_DICT( + light_sensor_module_globals, light_sensor_module_globals_table +); + +const mp_obj_module_t light_sensor_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&light_sensor_module_globals, +}; + +/* clang-format off */ +MP_REGISTER_MODULE(MP_QSTR_light_sensor, light_sensor_module, MODULE_LIGHT_SENSOR_ENABLED); diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 707a27af2db328ff0d6c29699e7726d755065dbd..3dda54d667557f66d39978788fbde813f8e39390 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -40,3 +40,9 @@ Q(line) Q(rect) Q(circ) Q(clear) + +/* ambient */ +Q(light_sensor) +Q(start) +Q(get_reading) +Q(stop) diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index b348861acb31ca0b6697f266e4edeb133c7adf10..6bfb2c6717fcddb0bfcd585fb13c401b5df86bb9 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -42,6 +42,7 @@ #define MODULE_VIBRA_ENABLED (1) #define MODULE_INTERRUPT_ENABLED (1) #define MODULE_DISPLAY_ENABLED (1) +#define MODULE_LIGHT_SENSOR_ENABLED (1) /* * This port is intended to be 32-bit, but unfortunately, int32_t for