diff --git a/src/hal/nrfx/include/nrfx_config.h b/src/hal/nrfx/include/nrfx_config.h index beb6b34ab..df37cbf67 100644 --- a/src/hal/nrfx/include/nrfx_config.h +++ b/src/hal/nrfx/include/nrfx_config.h @@ -236,4 +236,8 @@ #endif +#ifndef NRFX_PDM_ENABLED +#define NRFX_PDM_ENABLED 1 +#endif + #endif // NRFX_CONFIG_H diff --git a/src/micropython b/src/micropython index da17ebeff..4349e1135 160000 --- a/src/micropython +++ b/src/micropython @@ -1 +1 @@ -Subproject commit da17ebeffd1636cd4efc46d05e8f56a6e013d33b +Subproject commit 4349e11357a7664d4269ed4aa150990bf51323b7 diff --git a/src/omv/boards/NANO33/omv_boardconfig.h b/src/omv/boards/NANO33/omv_boardconfig.h index 74e1ccfd3..8fcdeb2be 100644 --- a/src/omv/boards/NANO33/omv_boardconfig.h +++ b/src/omv/boards/NANO33/omv_boardconfig.h @@ -87,7 +87,7 @@ #define OMV_FB_SIZE (128K) // FB memory: header + QVGA/GS image #define OMV_FB_ALLOC_SIZE (16K) // minimum fb alloc size -#define OMV_STACK_SIZE (10K) +#define OMV_STACK_SIZE (8K) #define OMV_HEAP_SIZE (64K) #define OMV_JPEG_BUF_SIZE (16 * 1024) // IDE JPEG buffer (header + data). @@ -102,4 +102,9 @@ #define FIR_I2C_SDA_PIN (31) #define FIR_I2C_SPEED (CAMBUS_SPEED_FULL) +// PDM/MIC +#define PDM_DIN_PIN (25) +#define PDM_CLK_PIN (26) +#define PDM_PWR_PIN (17) + #endif //__OMV_BOARDCONFIG_H__ diff --git a/src/omv/boards/NANO33/omv_boardconfig.mk b/src/omv/boards/NANO33/omv_boardconfig.mk index cbe77f031..3a9d6bad1 100755 --- a/src/omv/boards/NANO33/omv_boardconfig.mk +++ b/src/omv/boards/NANO33/omv_boardconfig.mk @@ -13,3 +13,4 @@ MAIN_APP_ADDR=0x00026000 OMV_HSE_VALUE=12000000 DFU_DEVICE=0x0483:0xdf11 MICROPY_PY_ULAB=1 +MICROPY_PY_AUDIO=1 diff --git a/src/omv/ports/nrf/main.c b/src/omv/ports/nrf/main.c index 31f5d4673..f56a726d1 100644 --- a/src/omv/ports/nrf/main.c +++ b/src/omv/ports/nrf/main.c @@ -78,6 +78,7 @@ #endif #include "usbdbg.h" +#include "py_audio.h" #include "framebuffer.h" #include "omv_boardconfig.h" @@ -300,6 +301,10 @@ soft_reset: mp_deinit(); + #if MICROPY_PY_AUDIO + py_audio_deinit(); + #endif + #if BLUETOOTH_SD sd_softdevice_disable(); #endif diff --git a/src/omv/ports/nrf/modules/py_audio.c b/src/omv/ports/nrf/modules/py_audio.c new file mode 100644 index 000000000..0a69e8d2a --- /dev/null +++ b/src/omv/ports/nrf/modules/py_audio.c @@ -0,0 +1,189 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2019 Ibrahim Abdelkader + * Copyright (c) 2013-2019 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Audio Python module. + */ +#include +#include "py/obj.h" +#include "py/objarray.h" +#include "py/nlr.h" +#include "py/mphal.h" +#include "py/binary.h" +#include "pendsv.h" +#include "runtime.h" + +#include "py_audio.h" +#include "py_assert.h" +#include "py_helper.h" +#include "fb_alloc.h" +#include "omv_boardconfig.h" +#include "common.h" +#include "nrfx_pdm.h" +#include "hal/nrf_gpio.h" + +#if MICROPY_PY_AUDIO +#define PDM_DEFAULT_GAIN 20 +#define PDM_IRQ_PRIORITY 7 +#define PDM_BUFFER_SIZE (256) + +#define NRF_PDM_FREQ_1280K (nrf_pdm_freq_t)(0x0A000000UL) ///< PDM_CLK= 1.280 MHz (32 MHz / 25) => Fs= 20000 Hz +#define NRF_PDM_FREQ_2000K (nrf_pdm_freq_t)(0x10000000UL) ///< PDM_CLK= 2.000 MHz (32 MHz / 16) => Fs= 31250 Hz +#define NRF_PDM_FREQ_2667K (nrf_pdm_freq_t)(0x15000000UL) ///< PDM_CLK= 2.667 MHz (32 MHz / 12) => Fs= 41667 Hz +#define NRF_PDM_FREQ_3200K (nrf_pdm_freq_t)(0x19000000UL) ///< PDM_CLK= 3.200 MHz (32 MHz / 10) => Fs= 50000 Hz +#define NRF_PDM_FREQ_4000K (nrf_pdm_freq_t)(0x20000000UL) ///< PDM_CLK= 4.000 MHz (32 MHz / 8) => Fs= 62500 Hz +#define RAISE_OS_EXCEPTION(msg) nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg)) + +static mp_obj_array_t *g_pcmbuf = NULL; +static mp_obj_t g_audio_callback = mp_const_none; +static uint32_t g_channels = 1; +static uint32_t g_buf_index = 0; +static int16_t PDM_BUFFERS[2][PDM_BUFFER_SIZE]; + +// Pendsv dispatch callback. +static void audio_pendsv_callback(void); + +static void nrfx_pdm_event_handler(nrfx_pdm_evt_t const *evt) +{ + if (evt->error == NRFX_PDM_NO_ERROR) { + if (evt->buffer_requested) { + if (evt->buffer_released == PDM_BUFFERS[0]) { + nrfx_pdm_buffer_set(PDM_BUFFERS[1], PDM_BUFFER_SIZE); + } else { // NULL (first buffer request) or second buffer is full. + nrfx_pdm_buffer_set(PDM_BUFFERS[0], PDM_BUFFER_SIZE); + } + } + if (evt->buffer_released && g_audio_callback != mp_const_none) { + g_buf_index = (evt->buffer_released == PDM_BUFFERS[0]) ? 0 : 1; + pendsv_schedule_dispatch(PENDSV_DISPATCH_AUDIO, audio_pendsv_callback); + } + } else { + // TODO + } +} + +static mp_obj_t py_audio_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) +{ + // Read Args. + g_channels = py_helper_keyword_int(n_args, args, 0, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_channels), 2); + uint32_t frequency = py_helper_keyword_int(n_args, args, 1, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_frequency), 16000); + int gain_db = py_helper_keyword_int(n_args, args, 2, kw_args, MP_OBJ_NEW_QSTR(MP_QSTR_gain_db), PDM_DEFAULT_GAIN); + + nrfx_pdm_config_t nrfx_pdm_config = { + .pin_clk = PDM_CLK_PIN, + .pin_din = PDM_DIN_PIN, + .gain_l = gain_db, + .gain_r = gain_db, + .interrupt_priority = PDM_IRQ_PRIORITY, + }; + + // configure the sample rate and channels + switch (frequency) { + case 16000: + nrfx_pdm_config.clock_freq = NRF_PDM_FREQ_1280K; + break; + case 41667: + nrfx_pdm_config.clock_freq = NRF_PDM_FREQ_2667K; + break; + default: + RAISE_OS_EXCEPTION("Invalid frequency!"); + } + + switch (g_channels) { + case 1: + nrfx_pdm_config.mode = NRF_PDM_MODE_MONO; + nrfx_pdm_config.edge = NRF_PDM_EDGE_LEFTFALLING; + break; + case 2: + #if 0 // Disable 2 channels for now. + nrfx_pdm_config.mode = NRF_PDM_MODE_STEREO; + nrfx_pdm_config.edge = NRF_PDM_EDGE_LEFTFALLING; + break; + #endif + default: + RAISE_OS_EXCEPTION("Invalid number of channels! Expected 1 or 2."); + } + + // Power the mic on. + nrf_gpio_cfg_output(PDM_PWR_PIN); + nrf_gpio_pin_set(PDM_PWR_PIN); + + // Allocate global PCM buffer. + g_pcmbuf = mp_obj_new_bytearray_by_ref(PDM_BUFFER_SIZE * sizeof(int16_t), m_new(int16_t, PDM_BUFFER_SIZE)); + + nrfx_pdm_init(&nrfx_pdm_config, nrfx_pdm_event_handler); + return mp_const_none; +} + +void py_audio_deinit() +{ + // Disable PDM and IRQ + nrfx_pdm_uninit(); + + // Power the mic off + nrf_gpio_cfg_output(PDM_PWR_PIN); + nrf_gpio_pin_clear(PDM_PWR_PIN); + nrf_gpio_cfg_input(PDM_PWR_PIN, NRF_GPIO_PIN_PULLDOWN); + + g_channels = 0; + g_pcmbuf = NULL; + g_buf_index = 0; + g_audio_callback = mp_const_none; +} + +static void audio_pendsv_callback(void) +{ + memcpy(g_pcmbuf->items, PDM_BUFFERS[g_buf_index], PDM_BUFFER_SIZE * sizeof(int16_t)); + // Call user callback + mp_call_function_1(g_audio_callback, MP_OBJ_FROM_PTR(g_pcmbuf)); +} + +static mp_obj_t py_audio_start_streaming(mp_obj_t callback_obj) +{ + g_audio_callback = callback_obj; + + if (!mp_obj_is_callable(g_audio_callback)) { + g_audio_callback = mp_const_none; + RAISE_OS_EXCEPTION("Invalid callback object!"); + } + + // Start PDM. + if (nrfx_pdm_start() != NRFX_SUCCESS) { + g_audio_callback = mp_const_none; + RAISE_OS_EXCEPTION("PDM start failed!"); + } + + return mp_const_none; +} + +static mp_obj_t py_audio_stop_streaming() +{ + // Stop PDM. + nrfx_pdm_stop(); + g_audio_callback = mp_const_none; + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(py_audio_init_obj, 0, py_audio_init); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_audio_start_streaming_obj, py_audio_start_streaming); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(py_audio_stop_streaming_obj, py_audio_stop_streaming); + +static const mp_rom_map_elem_t globals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audio) }, + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&py_audio_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_start_streaming), MP_ROM_PTR(&py_audio_start_streaming_obj)}, + { MP_ROM_QSTR(MP_QSTR_stop_streaming), MP_ROM_PTR(&py_audio_stop_streaming_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(globals_dict, globals_dict_table); + +const mp_obj_module_t audio_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_t)&globals_dict, +}; + +#endif //MICROPY_PY_AUDIO diff --git a/src/omv/ports/nrf/modules/py_audio.h b/src/omv/ports/nrf/modules/py_audio.h new file mode 100644 index 000000000..694c9ea6d --- /dev/null +++ b/src/omv/ports/nrf/modules/py_audio.h @@ -0,0 +1,14 @@ +/* + * This file is part of the OpenMV project. + * + * Copyright (c) 2013-2019 Ibrahim Abdelkader + * Copyright (c) 2013-2019 Kwabena W. Agyeman + * + * This work is licensed under the MIT license, see the file LICENSE for details. + * + * Audio Python module. + */ +#ifndef __PY_AUDIO_H__ +#define __PY_AUDIO_H__ +void py_audio_deinit(); +#endif // __PY_AUDIO_H__