Add initial Nano audio module.

This commit is contained in:
iabdalkader 2020-12-31 23:47:32 +02:00
parent ec99ddbd76
commit e6cc0a6f41
7 changed files with 220 additions and 2 deletions

View File

@ -236,4 +236,8 @@
#endif
#ifndef NRFX_PDM_ENABLED
#define NRFX_PDM_ENABLED 1
#endif
#endif // NRFX_CONFIG_H

@ -1 +1 @@
Subproject commit da17ebeffd1636cd4efc46d05e8f56a6e013d33b
Subproject commit 4349e11357a7664d4269ed4aa150990bf51323b7

View File

@ -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__

View File

@ -13,3 +13,4 @@ MAIN_APP_ADDR=0x00026000
OMV_HSE_VALUE=12000000
DFU_DEVICE=0x0483:0xdf11
MICROPY_PY_ULAB=1
MICROPY_PY_AUDIO=1

View File

@ -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

View File

@ -0,0 +1,189 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2019 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2019 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* Audio Python module.
*/
#include <stdio.h>
#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

View File

@ -0,0 +1,14 @@
/*
* This file is part of the OpenMV project.
*
* Copyright (c) 2013-2019 Ibrahim Abdelkader <iabdalkader@openmv.io>
* Copyright (c) 2013-2019 Kwabena W. Agyeman <kwagyeman@openmv.io>
*
* 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__