Commit 67dbbaab by tatsukiishikawa

replacing mic_example

parent ca725554
{
"configurations": [
{
"name": "Pico",
"includePath": [
"${workspaceFolder}/**",
"${userHome}/.pico-sdk/sdk/2.2.0/**"
],
"forcedInclude": [
"${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h",
"${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h"
],
"defines": [],
"compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc",
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"cStandard": "c17",
"cppStandard": "c++14",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}
[
{
"name": "Pico",
"compilers": {
"C": "${command:raspberry-pi-pico.getCompilerPath}",
"CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}"
},
"environmentVariables": {
"PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}"
},
"cmakeSettings": {
"Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}"
}
}
]
\ No newline at end of file
{
"recommendations": [
"ms-vscode.vscode-serial-monitor",
"raspberry-pi.raspberry-pi-pico",
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
"marus25.cortex-debug"
]
}
\ No newline at end of file
{
"version": "0.2.0",
"configurations": [
{
"name": "Pico Debug (Cortex-Debug)",
"cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
"executable": "${command:raspberry-pi-pico.launchTargetPath}",
"request": "launch",
"type": "cortex-debug",
"servertype": "openocd",
"serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
"gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
"device": "${command:raspberry-pi-pico.getChipUppercase}",
"configFiles": [
"interface/cmsis-dap.cfg",
"target/${command:raspberry-pi-pico.getTarget}.cfg"
],
"svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
"runToEntryPoint": "main",
// Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
// Also works fine for flash binaries
"overrideLaunchCommands": [
"monitor reset init",
"load \"${command:raspberry-pi-pico.launchTargetPath}\""
],
"openOCDLaunchCommands": [
"adapter speed 5000"
]
},
{
"name": "Pico Debug (Cortex-Debug with external OpenOCD)",
"cwd": "${workspaceRoot}",
"executable": "${command:raspberry-pi-pico.launchTargetPath}",
"request": "launch",
"type": "cortex-debug",
"servertype": "external",
"gdbTarget": "localhost:3333",
"gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
"device": "${command:raspberry-pi-pico.getChipUppercase}",
"svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
"runToEntryPoint": "main",
// Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
// Also works fine for flash binaries
"overrideLaunchCommands": [
"monitor reset init",
"load \"${command:raspberry-pi-pico.launchTargetPath}\""
]
},
]
}
{
"cmake.showSystemKits": false,
"cmake.options.statusBarVisibility": "hidden",
"cmake.options.advanced": {
"build": {
"statusBarVisibility": "hidden"
},
"launch": {
"statusBarVisibility": "hidden"
},
"debug": {
"statusBarVisibility": "hidden"
}
},
"cmake.configureOnEdit": true,
"cmake.automaticReconfigure": true,
"cmake.configureOnOpen": true,
"cmake.generator": "Ninja",
"cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake",
"C_Cpp.debugShortcut": false,
"terminal.integrated.env.windows": {
"PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0",
"PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1",
"Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}"
},
"terminal.integrated.env.osx": {
"PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
"PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
"PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
},
"terminal.integrated.env.linux": {
"PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
"PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
"PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
},
"raspberry-pi-pico.cmakeAutoConfigure": false,
"raspberry-pi-pico.useCmakeTools": true,
"raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake",
"raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja"
}
{
"version": "2.0.0",
"tasks": [
{
"label": "Compile Project",
"type": "process",
"isBuildCommand": true,
"command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja",
"args": ["-C", "${workspaceFolder}/build"],
"group": "build",
"presentation": {
"reveal": "always",
"panel": "dedicated"
},
"problemMatcher": "$gcc",
"windows": {
"command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe"
}
},
{
"label": "Run Project",
"type": "process",
"command": "${env:HOME}/.pico-sdk/picotool/2.2.0/picotool/picotool",
"args": [
"load",
"${command:raspberry-pi-pico.launchTargetPath}",
"-fx"
],
"presentation": {
"reveal": "always",
"panel": "dedicated"
},
"problemMatcher": [],
"windows": {
"command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0/picotool/picotool.exe"
}
},
{
"label": "Flash",
"type": "process",
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
"args": [
"-s",
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/${command:raspberry-pi-pico.getTarget}.cfg",
"-c",
"adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit"
],
"problemMatcher": [],
"windows": {
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
}
},
{
"label": "Rescue Reset",
"type": "process",
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
"args": [
"-s",
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/${command:raspberry-pi-pico.getChip}-rescue.cfg",
"-c",
"adapter speed 5000; reset halt; exit"
],
"problemMatcher": [],
"windows": {
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
}
},
{
"label": "Risc-V Reset (RP2350)",
"type": "process",
"command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
"args": [
"-s",
"${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
"-c",
"set USE_CORE { rv0 rv1 cm0 cm1 }",
"-f",
"interface/cmsis-dap.cfg",
"-f",
"target/rp2350.cfg",
"-c",
"adapter speed 5000; init;",
"-c",
"write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];",
"-c",
"reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit"
],
"problemMatcher": [],
"windows": {
"command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
}
}
]
}
......@@ -11,72 +11,34 @@ set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
include(${picoVscode})
endif()
# ====================================================================================
# ========================================================================================
set(PICO_BOARD pico CACHE STRING "Board type")
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
project(main C CXX ASM)
project(pico_microphone C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# --- Executable and microphone sources ---
# build driver lib from src/
add_subdirectory(src)
# your application
add_executable(main
main.c
${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.c
${CMAKE_CURRENT_LIST_DIR}/src/OpenPDM2PCM/OpenPDMFilter.c
)
# Generate PIO header for the mic
pico_generate_pio_header(main
${CMAKE_CURRENT_LIST_DIR}/src/pdm_microphone.pio
)
# Include directories (note the src/include path gets added)
target_include_directories(main PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/src/include
${CMAKE_CURRENT_LIST_DIR}/src/OpenPDM2PCM
${CMAKE_CURRENT_BINARY_DIR} # for staged "pico/pdm_microphone.h"
)
# --- Stage a virtual include so "pico/pdm_microphone.h" resolves ---
set(PDM_HDR_SRC ${CMAKE_CURRENT_LIST_DIR}/src/include/pico/pdm_microphone.h)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/pico
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PDM_HDR_SRC}
${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
DEPENDS ${PDM_HDR_SRC}
COMMENT "Staging pico/pdm_microphone.h from src/include into build dir"
VERBATIM
)
add_custom_target(pdm_header ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/pico/pdm_microphone.h
)
add_dependencies(main pdm_header)
# Link libraries
target_link_libraries(main PRIVATE
# link your app to the driver lib (and stdlib, for good measure)
target_link_libraries(main
pico_pdm_microphone
pico_stdlib
hardware_dma
hardware_pio
)
# Program metadata and I/O
pico_set_program_name(main "main")
pico_set_program_version(main "0.1")
pico_enable_stdio_uart(main 0)
# enable usb output, disable uart output
pico_enable_stdio_usb(main 1)
pico_enable_stdio_uart(main 0)
# Create UF2, bin, etc.
# create map/bin/hex/uf2 file in addition to ELF.
pico_add_extra_outputs(main)
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "stdlib.h"
#include "src/include/pico/pdm_microphone.h"
#include "pico/stdlib.h"
#include "pico/pdm_microphone.h"
#include "tusb.h"
#define AUDIO_SAMPLE_RATE 16000
#define SAMPLE_BUFFER_SIZE 256
pdm_microphone_config config = {
// PDM ID
.pdm_id = 0,
// configuration
const struct pdm_microphone_config config = {
// GPIO pin for the PDM DAT signal
.gpio_data = 2,
.dma_irq = DMA_IRQ_0,
// GPIO pin for the PDM CLK signal
.gpio_clk = 3,
// PIO
// PIO instance to use
.pio = pio0,
// GPIO pin for the PDM DAT signal
.gpio_data = 18,
// GPIO pin for the PDM CLK signal
.gpio_clk = 19,
// PIO State Machine instance to use
.pio_sm = 0,
// sample rate in Hz
.sample_rate = AUDIO_SAMPLE_RATE,
.sample_rate = 8000,
// number of samples to buffer
.sample_buffer_size = SAMPLE_BUFFER_SIZE,
.sample_buffer_size = 256,
};
pdm_mic_obj* pdm_mic;
// variables
int16_t sample_buffer[256];
volatile int samples_read = 0;
int16_t sample_buffer[SAMPLE_BUFFER_SIZE];
volatile bool data_valid = false;
void on_pdm_samples_ready(uint8_t pdm_id) {
(void)pdm_id;
data_valid = true;
void on_pdm_samples_ready()
{
// callback from library when all the samples in the library
// internal sample buffer are ready for reading
samples_read = pdm_microphone_read(sample_buffer, 256);
}
int main(void)
int main( void )
{
// initialize stdio and wait for USB CDC connect
stdio_init_all();
while (!tud_cdc_connected()) {
sleep_ms(10);
tight_loop_contents();
}
printf("hello PDM microphone\n");
pdm_mic = pdm_microphone_init(&config);
if (!pdm_mic) {
// initialize the PDM microphone
if (pdm_microphone_init(&config) < 0) {
printf("PDM microphone initialization failed!\n");
while (1) { tight_loop_contents(); }
}
printf("PDM microphone initialized\n");
pdm_microphone_set_samples_ready_handler(pdm_mic, on_pdm_samples_ready);
// set callback that is called when all the samples in the library
// internal sample buffer are ready for reading
pdm_microphone_set_samples_ready_handler(on_pdm_samples_ready);
if (pdm_microphone_start(pdm_mic) != 0) {
// start capturing data from the PDM microphone
if (pdm_microphone_start() < 0) {
printf("PDM microphone start failed!\n");
while (1) { tight_loop_contents(); }
}
printf("PDM microphone started\n");
uint32_t printed = 0;
while (1) {
// wait for new samples
while (samples_read == 0) { tight_loop_contents(); }
while (true) {
if (data_valid) {
data_valid = false;
// store and clear the samples read from the callback
int sample_count = samples_read;
samples_read = 0;
int n = pdm_microphone_read(pdm_mic, sample_buffer, SAMPLE_BUFFER_SIZE);
if (n > 0) {
for (int i = 0; i < n; i++) {
printf("%04x\n", (uint16_t)sample_buffer[i]);
}
// loop through any new collected samples
for (int i = 0; i < sample_count; i++) {
printf("%d\n", sample_buffer[i]);
}
}
sleep_ms(1);
}
return 0;
}
# No VSCode boilerplate, no pico_sdk_import, no project(), no pico_sdk_init() here
add_library(pico_pdm_microphone STATIC
pdm_microphone.c
OpenPDM2PCM/OpenPDMFilter.c
)
target_include_directories(pico_pdm_microphone PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include
)
# Generate PIO header for the library target
pico_generate_pio_header(pico_pdm_microphone
${CMAKE_CURRENT_LIST_DIR}/pdm_microphone.pio
)
# Link the hardware libs needed by the driver
target_link_libraries(pico_pdm_microphone PUBLIC
pico_stdlib
hardware_dma
hardware_pio
)
# Remove this unless you actually have src/mic_example/
# add_subdirectory(mic_example)
; Receive a mono or stereo I2S audio stream as stereo
; This is 64 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 64.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 0 | 31 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 32 bit words to the PIO for mono, 64 bit words for stereo
.program audio_i2s_rx_32b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 30 side 0b00
left_channel:
in pins, 1 side 0b01
jmp x-- left_channel side 0b00
in pins, 1 side 0b11
set x, 30 side 0b10
right_channel:
in pins, 1 side 0b11
jmp x-- right_channel side 0b10
in pins, 1 side 0b01
.wrap
% c-sdk {
%}
\ No newline at end of file
; Transmit a mono or stereo I2S audio stream as stereo
; This is 16 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 32.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 16 | 15 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 16 bit words to the PIO for mono, 32 bit words for stereo
.program audio_i2s_tx_16b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 14 side 0b01
left_channel:
out pins, 1 side 0b00
jmp x-- left_channel side 0b01
out pins, 1 side 0b10
set x, 14 side 0b11
right_channel:
out pins, 1 side 0b10
jmp x-- right_channel side 0b11
public entry_point:
out pins, 1 side 0b00
.wrap
% c-sdk {
static inline void audio_i2s_tx_16b_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
pio_sm_config sm_config = audio_i2s_tx_16b_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, 32);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_tx_16b_offset_entry_point));
}
%}
\ No newline at end of file
; Transmit a mono or stereo I2S audio stream as stereo
; This is 64 bits per sample; can be altered by modifying the "set" params,
; or made programmable by replacing "set x" with "mov x, y" and using Y as a config register.
;
; Autopull must be enabled, with threshold set to 64.
; Since I2S is MSB-first, shift direction should be to left.
; Hence the format of the FIFO word is:
;
; | 31 : 0 | 31 : 0 |
; | sample ws=0 | sample ws=1 |
;
; Data is output at 1 bit per clock. Use clock divider to adjust frequency.
; Fractional divider will probably be needed to get correct bit clock period,
; but for common syslck freqs this should still give a constant word select period.
;
; One output pin is used for the data output.
; Two side-set pins are used. Bit 0 is clock, bit 1 is word select.
; Send 32 bit words to the PIO for mono, 64 bit words for stereo
.program audio_i2s_tx_32b
.side_set 2
; /--- LRCLK
; |/-- BCLK
; ||
.wrap_target
set x, 30 side 0b01
left_channel:
out pins, 1 side 0b00
jmp x-- left_channel side 0b01
out pins, 1 side 0b10
set x, 30 side 0b11
right_channel:
out pins, 1 side 0b10
jmp x-- right_channel side 0b11
public entry_point:
out pins, 1 side 0b00
.wrap
% c-sdk {
static inline void audio_i2s_tx_32b_program_init(PIO pio, uint sm, uint offset, uint data_pin, uint clock_pin_base) {
pio_sm_config sm_config = audio_i2s_tx_32b_program_get_default_config(offset);
sm_config_set_out_pins(&sm_config, data_pin, 1);
sm_config_set_sideset_pins(&sm_config, clock_pin_base);
sm_config_set_out_shift(&sm_config, false, true, 32);
pio_sm_init(pio, sm, offset, &sm_config);
uint pin_mask = (1u << data_pin) | (3u << clock_pin_base);
pio_sm_set_pindirs_with_mask(pio, sm, pin_mask, pin_mask);
pio_sm_set_pins(pio, sm, 0); // clear pins
pio_sm_exec(pio, sm, pio_encode_jmp(offset + audio_i2s_tx_32b_offset_entry_point));
}
%}
\ No newline at end of file
#include "pico/dc_offset_filter.h"
void dc_offset_filter_init(dc_offset_filter_t* self, int32_t apply_delay_samples){
self->sample_mean_value_sum = 0;
self->sample_mean_value_cntr = 0;
self->sample_mean_value = 0;
self->apply_delay_samples = apply_delay_samples;
}
int32_t dc_offset_filter_main(dc_offset_filter_t* self, int32_t input_sample, bool update_mean_val){
int32_t sample_mean_value_approx;
// Update mean value
if((update_mean_val == true) && (self->sample_mean_value_cntr >= self->apply_delay_samples)){
self->sample_mean_value = self->sample_mean_value_sum / self->sample_mean_value_cntr;
}
// Use mean value if we have enough input samples
if(self->sample_mean_value_cntr <= self->apply_delay_samples)
sample_mean_value_approx = 0;
else
sample_mean_value_approx = self->sample_mean_value;
// increase counters
self->sample_mean_value_sum = self->sample_mean_value_sum + input_sample;
self->sample_mean_value_cntr ++;
// return output value subtracting mean value:
return input_sample - sample_mean_value_approx;
}
\ No newline at end of file
#ifndef DC_OFFSET_FILTER__H
#define DC_OFFSET_FILTER__H
#include <stdio.h>
#include <stdbool.h>
typedef struct _dc_offset_filter_t {
int64_t sample_mean_value_sum;
int32_t sample_mean_value_cntr;
int32_t sample_mean_value;
int32_t apply_delay_samples;
} dc_offset_filter_t;
void dc_offset_filter_init(dc_offset_filter_t* self, int32_t apply_delay_samples);
int32_t dc_offset_filter_main(dc_offset_filter_t* self, int32_t input_sample, bool update_mean_val);
#endif //DC_OFFSET_FILTER__H
#ifndef DEFAULT_I2S_BOARD_DEFINES__H
#define DEFAULT_I2S_BOARD_DEFINES__H
//-------------------------
// I2s defines
//-------------------------
#define I2S_MIC_INMP441
#ifdef I2S_MIC_INMP441
#ifndef I2S_MIC_SD
#define I2S_MIC_SD 14
#endif //I2S_MIC_SD
#ifndef I2S_MIC_SCK
#define I2S_MIC_SCK 15
#endif //I2S_MIC_SCK
#ifndef I2S_MIC_WS
#define I2S_MIC_WS (I2S_MIC_SCK+1) // needs to be I2S_MIC_SCK +1
#endif //I2S_MIC_WS
#else //I2S_MIC_INMP441
#ifndef I2S_MIC_SPH_DC_OFFSET
#define I2S_MIC_SPH_DC_OFFSET 0xf8c80000
#endif //I2S_MIC_SPH_DC_OFFSET
#ifndef I2S_MIC_SD
#define I2S_MIC_SD 10
#endif //I2S_MIC_SD
#ifndef I2S_MIC_SCK
#define I2S_MIC_SCK 11
#endif //I2S_MIC_SCK
#ifndef I2S_MIC_WS
#define I2S_MIC_WS (I2S_MIC_SCK+1) // needs to be I2S_MIC_SCK +1
#endif //I2S_MIC_WS
#endif //I2S_MIC_INMP441
#ifndef I2S_MIC_BPS
#define I2S_MIC_BPS 32 // 24 is not valid in this implementation, but INMP441 outputs 24 bits samples
#endif //I2S_MIC_BPS
#ifndef I2S_MIC_RATE_DEF
#define I2S_MIC_RATE_DEF (16000)
#endif //I2S_MIC_RATE_DEF
#ifndef I2S_SPK_SD
#define I2S_SPK_SD 2
#endif //I2S_SPK_SD
#ifndef I2S_SPK_SCK
#define I2S_SPK_SCK 3
#endif //I2S_SPK_SCK
#ifndef I2S_SPK_WS
#define I2S_SPK_WS (I2S_SPK_SCK+1) // needs to be SPK_SCK +1
#endif //I2S_SPK_WS
#ifndef I2S_SPK_BPS
#define I2S_SPK_BPS 32
#endif //I2S_SPK_BPS
#ifndef I2S_SPK_RATE_DEF
#define I2S_SPK_RATE_DEF (48000)
#endif //I2S_SPK_RATE_DEF
typedef struct {
uint32_t left;
uint32_t right;
} i2s_32b_audio_sample;
typedef struct {
uint16_t left;
uint16_t right;
} i2s_16b_audio_sample;
//-------------------------
#endif //DEFAULT_I2S_BOARD_DEFINES__H
\ No newline at end of file
#ifndef MACHINE_I2S__H
#define MACHINE_I2S__H
#include <stdlib.h>
#include <string.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "pico/ring_buf.h"
// Notes on this port's specific implementation of I2S:
// - the DMA IRQ handler is used to implement the asynchronous background operations, for non-blocking mode
// - the PIO is used to drive the I2S bus signals
// - all sample data transfers use non-blocking DMA
// - the DMA controller is configured with 2 DMA channels in chained mode
#define MAX_I2S_RP2 (2)
// The DMA buffer size was empirically determined. It is a tradeoff between:
// 1. memory use (smaller buffer size desirable to reduce memory footprint)
// 2. interrupt frequency (larger buffer size desirable to reduce interrupt frequency)
#define SIZEOF_DMA_BUFFER_IN_BYTES (96*8*2) // Max frequency is 96000. in worst case. 1ms contains 96 samples. Each sample is 8 bytes. Need to hold 2 buffers of this size
#define SIZEOF_HALF_DMA_BUFFER_IN_BYTES (SIZEOF_DMA_BUFFER_IN_BYTES / 2)
#define I2S_NUM_DMA_CHANNELS (2)
// For non-blocking mode, to avoid underflow/overflow, sample data is written/read to/from the ring buffer at a rate faster
// than the DMA transfer rate
#define NON_BLOCKING_RATE_MULTIPLIER (4)
#define SIZEOF_NON_BLOCKING_COPY_IN_BYTES (SIZEOF_HALF_DMA_BUFFER_IN_BYTES * NON_BLOCKING_RATE_MULTIPLIER)
#define NUM_I2S_USER_FORMATS (4)
#define I2S_RX_FRAME_SIZE_IN_BYTES (8)
#define SAMPLES_PER_FRAME (2)
#define PIO_INSTRUCTIONS_PER_BIT (2)
#define STATIC static
#define mp_hal_pin_obj_t uint
#ifndef m_new
#define m_new(type, num) ((type *)(malloc(sizeof(type) * (num))))
#endif //m_new
#ifndef m_new_obj
#define m_new_obj(type) (m_new(type, 1))
#endif //m_new_obj
typedef enum {
RX,
TX
} i2s_mode_t;
typedef enum {
MONO,
STEREO
} format_t;
typedef enum {
BLOCKING,
NON_BLOCKING,
UASYNCIO
} io_mode_t;
typedef enum {
GP_INPUT = 0,
GP_OUTPUT = 1
} gpio_dir_t;
// Buffer protocol
typedef struct _mp_buffer_info_t {
void *buf; // can be NULL if len == 0
size_t len; // in bytes
int typecode; // as per binary.h
} mp_buffer_info_t;
typedef struct _non_blocking_descriptor_t {
mp_buffer_info_t appbuf;
uint32_t index;
bool copy_in_progress;
} non_blocking_descriptor_t;
typedef struct _machine_i2s_obj_t {
uint8_t i2s_id;
mp_hal_pin_obj_t sck;
mp_hal_pin_obj_t ws;
mp_hal_pin_obj_t sd;
i2s_mode_t mode;
int8_t bits;
format_t format;
int32_t rate;
int32_t ibuf;
io_mode_t io_mode;
PIO pio;
uint8_t sm;
const pio_program_t *pio_program;
uint prog_offset;
int dma_channel[I2S_NUM_DMA_CHANNELS];
uint8_t dma_buffer[SIZEOF_DMA_BUFFER_IN_BYTES];
ring_buf_t ring_buffer;
uint8_t *ring_buffer_storage;
non_blocking_descriptor_t non_blocking_descriptor;
uint32_t sizeof_half_dma_buffer_in_bytes;
uint32_t sizeof_non_blocking_copy_in_bytes;
} machine_i2s_obj_t;
machine_i2s_obj_t* create_machine_i2s(uint8_t i2s_id,
mp_hal_pin_obj_t sck, mp_hal_pin_obj_t ws, mp_hal_pin_obj_t sd,
i2s_mode_t i2s_mode, int8_t i2s_bits, format_t i2s_format,
int32_t ring_buffer_len, int32_t i2s_rate);
int machine_i2s_read_stream(machine_i2s_obj_t *self, void *buf_in, size_t size);
int machine_i2s_write_stream(machine_i2s_obj_t *self, void *buf_in, size_t size);
//void update_pio_frequency(machine_i2s_obj_t *self, uint32_t sample_freq);
#endif //MACHINE_I2S__H
\ No newline at end of file
......@@ -10,63 +10,28 @@
#include "hardware/pio.h"
#include "OpenPDMFilter.h"
typedef void (*pdm_samples_ready_handler_t)(void);
#define MAX_PDM_RP2 (2)
#ifndef STATIC
#define STATIC static
#endif //STATIC
#ifndef m_new
#define m_new(type, num) ((type *)(malloc(sizeof(type) * (num))))
#endif //m_new
#ifndef m_new_obj
#define m_new_obj(type) (m_new(type, 1))
#endif //m_new_obj
#define PDM_DECIMATION 64
#define PDM_RAW_BUFFER_COUNT 2
typedef void (*pdm_samples_ready_handler_t)(uint8_t pdm_id);
typedef struct __pdm_microphone_config{
uint8_t pdm_id;
PIO pio;
uint dma_irq;
struct pdm_microphone_config {
uint gpio_data;
uint gpio_clk;
PIO pio;
uint pio_sm;
uint sample_rate;
uint sample_buffer_size;
} pdm_microphone_config;
typedef struct __pdm_mic_obj{
uint8_t pdm_id;
pdm_microphone_config* config;
uint pio_sm;
uint pio_sm_offset;
int dma_channel;
uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT];
volatile int raw_buffer_write_index;
volatile int raw_buffer_read_index;
uint raw_buffer_size;
TPDMFilter_InitStruct filter;
uint16_t filter_volume;
pdm_samples_ready_handler_t samples_ready_handler;
} pdm_mic_obj;
};
pdm_mic_obj* pdm_microphone_init(pdm_microphone_config* config);
void pdm_microphone_deinit(pdm_mic_obj *pdm_mic);
int pdm_microphone_init(const struct pdm_microphone_config* config);
void pdm_microphone_deinit();
int pdm_microphone_start(pdm_mic_obj *pdm_mic);
void pdm_microphone_stop(pdm_mic_obj *pdm_mic);
int pdm_microphone_start();
void pdm_microphone_stop();
void pdm_microphone_set_samples_ready_handler(pdm_mic_obj *pdm_mic, pdm_samples_ready_handler_t handler);
void pdm_microphone_set_filter_max_volume(pdm_mic_obj *pdm_mic, uint8_t max_volume);
void pdm_microphone_set_filter_gain(pdm_mic_obj *pdm_mic, uint8_t gain);
void pdm_microphone_set_filter_volume(pdm_mic_obj *pdm_mic, uint16_t volume);
void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler);
void pdm_microphone_set_filter_max_volume(uint8_t max_volume);
void pdm_microphone_set_filter_gain(uint8_t gain);
void pdm_microphone_set_filter_volume(uint16_t volume);
int pdm_microphone_read(pdm_mic_obj *pdm_mic, int16_t* buffer, size_t samples);
int pdm_microphone_read(int16_t* buffer, size_t samples);
#endif
#ifndef RING_BUF__H
#define RING_BUF__H
#include <stdio.h>
#include <stdbool.h>
typedef struct _ring_buf_t {
uint8_t *buffer;
size_t head;
size_t tail;
size_t size;
} ring_buf_t;
void ringbuf_init(ring_buf_t *rbuf, uint8_t *buffer, size_t size);
bool ringbuf_push(ring_buf_t *rbuf, uint8_t data);
bool ringbuf_pop(ring_buf_t *rbuf, uint8_t *data);
bool ringbuf_is_empty(ring_buf_t *rbuf);
bool ringbuf_is_full(ring_buf_t *rbuf);
size_t ringbuf_available_data(ring_buf_t *rbuf);
size_t ringbuf_available_space(ring_buf_t *rbuf);
#endif //RING_BUF__H
\ No newline at end of file
#pragma once
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "stdbool.h"
// todo this seemed like aood guess, but is not correct
static const uint16_t db_to_vol[91] = {
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002,
0x0002, 0x0002, 0x0003, 0x0003, 0x0004, 0x0004, 0x0005, 0x0005,
0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000d, 0x000e,
0x0010, 0x0012, 0x0014, 0x0017, 0x001a, 0x001d, 0x0020, 0x0024,
0x0029, 0x002e, 0x0033, 0x003a, 0x0041, 0x0049, 0x0052, 0x005c,
0x0067, 0x0074, 0x0082, 0x0092, 0x00a4, 0x00b8, 0x00ce, 0x00e7,
0x0104, 0x0124, 0x0147, 0x016f, 0x019c, 0x01ce, 0x0207, 0x0246,
0x028d, 0x02dd, 0x0337, 0x039b, 0x040c, 0x048a, 0x0518, 0x05b7,
0x066a, 0x0732, 0x0813, 0x090f, 0x0a2a, 0x0b68, 0x0ccc, 0x0e5c,
0x101d, 0x1214, 0x1449, 0x16c3, 0x198a, 0x1ca7, 0x2026, 0x2413,
0x287a, 0x2d6a, 0x32f5, 0x392c, 0x4026, 0x47fa, 0x50c3, 0x5a9d,
0x65ac, 0x7214, 0x7fff
};
// actually windows doesn't seem to like this in the middle, so set top range to 0db
#define CENTER_VOLUME_INDEX 91
#define ENCODE_DB(x) ((uint16_t)(int16_t)((x)*256))
#define MIN_VOLUME ENCODE_DB(-CENTER_VOLUME_INDEX)
#define DEFAULT_VOLUME ENCODE_DB(0)
#define MAX_VOLUME ENCODE_DB(count_of(db_to_vol)-CENTER_VOLUME_INDEX)
#define VOLUME_RESOLUTION ENCODE_DB(1)
uint16_t vol_to_db_convert(bool channel_mute, uint16_t channel_volume);
\ No newline at end of file
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Mike Teachman
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// This file is never compiled standalone, it's included directly from
// extmod/machine_i2s.c via MICROPY_PY_MACHINE_I2S_INCLUDEFILE.
#include "pico/machine_i2s.h"
#include "audio_i2s_tx_16b.pio.h"
#include "audio_i2s_tx_32b.pio.h"
#include "audio_i2s_rx_32b.pio.h"
STATIC machine_i2s_obj_t* machine_i2s_obj[MAX_I2S_RP2] = {NULL, NULL};
// The frame map is used with the readinto() method to transform the audio sample data coming
// from DMA memory (32-bit stereo) to the format specified
// in the I2S constructor. e.g. 16-bit mono
STATIC const int8_t i2s_frame_map[NUM_I2S_USER_FORMATS][I2S_RX_FRAME_SIZE_IN_BYTES] = {
{-1, -1, 0, 1, -1, -1, -1, -1 }, // Mono, 16-bits
{ 0, 1, 2, 3, -1, -1, -1, -1 }, // Mono, 32-bits
{-1, -1, 0, 1, -1, -1, 2, 3 }, // Stereo, 16-bits
{ 0, 1, 2, 3, 4, 5, 6, 7 }, // Stereo, 32-bits
};
STATIC const PIO pio_instances[NUM_PIOS] = {pio0, pio1};
// PIO program for 16-bit write
// set(x, 14) .side(0b01)
// label('left_channel')
// out(pins, 1) .side(0b00)
// jmp(x_dec, "left_channel") .side(0b01)
// out(pins, 1) .side(0b10)
// set(x, 14) .side(0b11)
// label('right_channel')
// out(pins, 1) .side(0b10)
// jmp(x_dec, "right_channel") .side(0b11)
// out(pins, 1) .side(0b00)
STATIC const uint16_t pio_instructions_write_16[] = {59438, 24577, 2113, 28673, 63534, 28673, 6213, 24577};
STATIC const pio_program_t pio_write_16 = {
pio_instructions_write_16,
sizeof(pio_instructions_write_16) / sizeof(uint16_t),
-1
};
// PIO program for 32-bit write
// set(x, 30) .side(0b01)
// label('left_channel')
// out(pins, 1) .side(0b00)
// jmp(x_dec, "left_channel") .side(0b01)
// out(pins, 1) .side(0b10)
// set(x, 30) .side(0b11)
// label('right_channel')
// out(pins, 1) .side(0b10)
// jmp(x_dec, "right_channel") .side(0b11)
// out(pins, 1) .side(0b00)
STATIC const uint16_t pio_instructions_write_32[] = {59454, 24577, 2113, 28673, 63550, 28673, 6213, 24577};
STATIC const pio_program_t pio_write_32 = {
pio_instructions_write_32,
sizeof(pio_instructions_write_32) / sizeof(uint16_t),
-1
};
// PIO program for 32-bit read
// set(x, 30) .side(0b00)
// label('left_channel')
// in_(pins, 1) .side(0b01)
// jmp(x_dec, "left_channel") .side(0b00)
// in_(pins, 1) .side(0b11)
// set(x, 30) .side(0b10)
// label('right_channel')
// in_(pins, 1) .side(0b11)
// jmp(x_dec, "right_channel") .side(0b10)
// in_(pins, 1) .side(0b01)
STATIC const uint16_t pio_instructions_read_32[] = {57406, 18433, 65, 22529, 61502, 22529, 4165, 18433};
STATIC const pio_program_t pio_read_32 = {
pio_instructions_read_32,
sizeof(pio_instructions_read_32) / sizeof(uint16_t),
-1
};
STATIC uint8_t dma_get_bits(i2s_mode_t mode, int8_t bits);
STATIC void dma_irq0_handler(void);
STATIC void dma_irq1_handler(void);
STATIC void machine_i2s_deinit(machine_i2s_obj_t *self);
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
STATIC int8_t get_frame_mapping_index(int8_t bits, format_t format) {
if (format == MONO) {
if (bits == 16) {
return 0;
} else { // 32 bits
return 1;
}
} else { // STEREO
if (bits == 16) {
return 2;
} else { // 32 bits
return 3;
}
}
}
STATIC uint32_t fill_appbuf_from_ringbuf(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) {
// copy audio samples from the ring buffer to the app buffer
// loop, copying samples until the app buffer is filled
// For uasyncio mode, the loop will make an early exit if the ring buffer becomes empty
// Example:
// a MicroPython I2S object is configured for 16-bit mono (2 bytes per audio sample).
// For every frame coming from the ring buffer (8 bytes), 2 bytes are "cherry picked" and
// copied to the supplied app buffer.
// Thus, for every 1 byte copied to the app buffer, 4 bytes are read from the ring buffer.
// If a 8kB app buffer is supplied, 32kB of audio samples is read from the ring buffer.
uint32_t num_bytes_copied_to_appbuf = 0;
uint8_t *app_p = (uint8_t *)appbuf->buf;
uint8_t appbuf_sample_size_in_bytes = (self->bits == 16? 2 : 4) * (self->format == STEREO ? 2: 1);
uint32_t num_bytes_needed_from_ringbuf = appbuf->len * (I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes);
uint8_t discard_byte;
while (num_bytes_needed_from_ringbuf) {
uint8_t f_index = get_frame_mapping_index(self->bits, self->format);
for (uint8_t i = 0; i < I2S_RX_FRAME_SIZE_IN_BYTES; i++) {
int8_t r_to_a_mapping = i2s_frame_map[f_index][i];
if (r_to_a_mapping != -1) {
if (self->io_mode == BLOCKING) {
// poll the ringbuf until a sample becomes available, copy into appbuf using the mapping transform
while (ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping) == false) {
;
}
num_bytes_copied_to_appbuf++;
} else if (self->io_mode == UASYNCIO) {
if (ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping) == false) {
// ring buffer is empty, exit
goto exit;
} else {
num_bytes_copied_to_appbuf++;
}
} else {
return 0; // should never get here (non-blocking mode does not use this function)
}
} else { // r_a_mapping == -1
// discard unused byte from ring buffer
if (self->io_mode == BLOCKING) {
// poll the ringbuf until a sample becomes available
while (ringbuf_pop(&self->ring_buffer, &discard_byte) == false) {
;
}
} else if (self->io_mode == UASYNCIO) {
if (ringbuf_pop(&self->ring_buffer, &discard_byte) == false) {
// ring buffer is empty, exit
goto exit;
}
} else {
return 0; // should never get here (non-blocking mode does not use this function)
}
}
num_bytes_needed_from_ringbuf--;
}
app_p += appbuf_sample_size_in_bytes;
}
exit:
return num_bytes_copied_to_appbuf;
}
STATIC uint32_t copy_appbuf_to_ringbuf(machine_i2s_obj_t *self, mp_buffer_info_t *appbuf) {
// copy audio samples from the app buffer to the ring buffer
// loop, reading samples until the app buffer is emptied
// for uasyncio mode, the loop will make an early exit if the ring buffer becomes full
uint32_t a_index = 0;
while (a_index < appbuf->len) {
if (self->io_mode == BLOCKING) {
// copy a byte to the ringbuf when space becomes available
while (ringbuf_push(&self->ring_buffer, ((uint8_t *)appbuf->buf)[a_index]) == false) {
;
}
a_index++;
} else if (self->io_mode == UASYNCIO) {
if (ringbuf_push(&self->ring_buffer, ((uint8_t *)appbuf->buf)[a_index]) == false) {
// ring buffer is full, exit
break;
} else {
a_index++;
}
} else {
return 0; // should never get here (non-blocking mode does not use this function)
}
}
return a_index;
}
// function is used in IRQ context
static void copy_appbuf_to_ringbuf_non_blocking(machine_i2s_obj_t *self) {
// copy audio samples from app buffer into ring buffer
uint32_t num_bytes_remaining_to_copy = self->non_blocking_descriptor.appbuf.len - self->non_blocking_descriptor.index;
uint32_t num_bytes_to_copy = MIN(self->sizeof_non_blocking_copy_in_bytes, num_bytes_remaining_to_copy);
if (ringbuf_available_space(&self->ring_buffer) >= num_bytes_to_copy) {
for (uint32_t i = 0; i < num_bytes_to_copy; i++) {
ringbuf_push(&self->ring_buffer,
((uint8_t *)self->non_blocking_descriptor.appbuf.buf)[self->non_blocking_descriptor.index + i]);
}
self->non_blocking_descriptor.index += num_bytes_to_copy;
if (self->non_blocking_descriptor.index >= self->non_blocking_descriptor.appbuf.len) {
self->non_blocking_descriptor.copy_in_progress = false;
//mp_sched_schedule(self->callback_for_non_blocking, MP_OBJ_FROM_PTR(self));
}
}
}
static void fill_appbuf_from_ringbuf_non_blocking(machine_i2s_obj_t *self) {
// attempt to copy a block of audio samples from the ring buffer to the supplied app buffer.
// audio samples will be formatted as part of the copy operation
uint32_t num_bytes_copied_to_appbuf = 0;
uint8_t *app_p = &(((uint8_t *)self->non_blocking_descriptor.appbuf.buf)[self->non_blocking_descriptor.index]);
uint8_t appbuf_sample_size_in_bytes = (self->bits == 16? 2 : 4) * (self->format == STEREO ? 2: 1);
uint32_t num_bytes_remaining_to_copy_to_appbuf = self->non_blocking_descriptor.appbuf.len - self->non_blocking_descriptor.index;
uint32_t num_bytes_remaining_to_copy_from_ring_buffer = num_bytes_remaining_to_copy_to_appbuf *
(I2S_RX_FRAME_SIZE_IN_BYTES / appbuf_sample_size_in_bytes);
uint32_t num_bytes_needed_from_ringbuf = MIN(self->sizeof_non_blocking_copy_in_bytes, num_bytes_remaining_to_copy_from_ring_buffer);
uint8_t discard_byte;
if (ringbuf_available_data(&self->ring_buffer) >= num_bytes_needed_from_ringbuf) {
while (num_bytes_needed_from_ringbuf) {
uint8_t f_index = get_frame_mapping_index(self->bits, self->format);
for (uint8_t i = 0; i < I2S_RX_FRAME_SIZE_IN_BYTES; i++) {
int8_t r_to_a_mapping = i2s_frame_map[f_index][i];
if (r_to_a_mapping != -1) {
ringbuf_pop(&self->ring_buffer, app_p + r_to_a_mapping);
num_bytes_copied_to_appbuf++;
} else { // r_a_mapping == -1
// discard unused byte from ring buffer
ringbuf_pop(&self->ring_buffer, &discard_byte);
}
num_bytes_needed_from_ringbuf--;
}
app_p += appbuf_sample_size_in_bytes;
}
self->non_blocking_descriptor.index += num_bytes_copied_to_appbuf;
if (self->non_blocking_descriptor.index >= self->non_blocking_descriptor.appbuf.len) {
self->non_blocking_descriptor.copy_in_progress = false;
//mp_sched_schedule(self->callback_for_non_blocking, MP_OBJ_FROM_PTR(self));
}
}
}
//-----------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------
// function is used in IRQ context
STATIC void empty_dma(machine_i2s_obj_t *self, uint8_t *dma_buffer_p) {
// when space exists, copy samples into ring buffer
if (ringbuf_available_space(&self->ring_buffer) >= self->sizeof_half_dma_buffer_in_bytes) {
for (uint32_t i = 0; i < self->sizeof_half_dma_buffer_in_bytes; i++) {
ringbuf_push(&self->ring_buffer, dma_buffer_p[i]);
}
}
}
// function is used in IRQ context
STATIC uint32_t feed_dma(machine_i2s_obj_t *self, uint8_t *dma_buffer_p) {
// when data exists, copy samples from ring buffer
uint32_t available_data_bytes = ringbuf_available_data(&self->ring_buffer);
if(available_data_bytes > self->sizeof_half_dma_buffer_in_bytes)
{
available_data_bytes = self->sizeof_half_dma_buffer_in_bytes;
}
uint32_t transfer_size_in_bytes = dma_get_bits(self->mode, self->bits) / 8;
uint32_t available_data_transfers = (available_data_bytes==0) ? 0 : (available_data_bytes/transfer_size_in_bytes);
//if (available_data >= self->sizeof_half_dma_buffer_in_bytes) {
if (available_data_bytes >= self->sizeof_half_dma_buffer_in_bytes) {
// copy a block of samples from the ring buffer to the dma buffer.
// STM32 HAL API has a stereo I2S implementation, but not mono
// mono format is implemented by duplicating each sample into both L and R channels.
if ((self->format == MONO) && (self->bits == 16)) {
for (uint32_t i = 0; i < available_data_transfers; i++) {
for (uint8_t b = 0; b < sizeof(uint16_t); b++) {
ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i * 4 + b]);
dma_buffer_p[i * 4 + b + 2] = dma_buffer_p[i * 4 + b]; // duplicated mono sample
}
}
} else if ((self->format == MONO) && (self->bits == 32)) {
for (uint32_t i = 0; i < available_data_transfers; i++) {
for (uint8_t b = 0; b < sizeof(uint32_t); b++) {
ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i * 8 + b]);
dma_buffer_p[i * 8 + b + 4] = dma_buffer_p[i * 8 + b]; // duplicated mono sample
}
}
} else { // STEREO, both 16-bit and 32-bit
for (uint32_t i = 0; i < available_data_transfers*transfer_size_in_bytes; i++) {
ringbuf_pop(&self->ring_buffer, &dma_buffer_p[i]);
}
}
} else {
// underflow. clear buffer to transmit "silence" on the I2S bus
available_data_bytes = self->sizeof_half_dma_buffer_in_bytes;
available_data_transfers = available_data_bytes/transfer_size_in_bytes;
memset(dma_buffer_p, 0, self->sizeof_half_dma_buffer_in_bytes);
}
return available_data_transfers;
}
STATIC void irq_configure(machine_i2s_obj_t *self) {
if (self->i2s_id == 0) {
irq_add_shared_handler(DMA_IRQ_0, dma_irq0_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_0, true);
} else {
irq_add_shared_handler(DMA_IRQ_1, dma_irq1_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_1, true);
}
}
STATIC void irq_deinit(machine_i2s_obj_t *self) {
if (self->i2s_id == 0) {
irq_remove_handler(DMA_IRQ_0, dma_irq0_handler);
} else {
irq_remove_handler(DMA_IRQ_1, dma_irq1_handler);
}
}
STATIC int pio_configure(machine_i2s_obj_t *self) {
if (self->mode == TX) {
if (self->bits == 16) {
self->pio_program = &audio_i2s_tx_16b_program; //&pio_write_16;
} else {
self->pio_program = &audio_i2s_tx_32b_program; //&pio_write_32;
}
} else { // RX
self->pio_program = &audio_i2s_rx_32b_program; //&pio_read_32;
}
// find a PIO with a free state machine and adequate program space
PIO candidate_pio;
bool is_free_sm;
bool can_add_program;
for (uint8_t p = 0; p < NUM_PIOS; p++) {
candidate_pio = pio_instances[p];
is_free_sm = false;
can_add_program = false;
for (uint8_t sm = 0; sm < NUM_PIO_STATE_MACHINES; sm++) {
if (!pio_sm_is_claimed(candidate_pio, sm)) {
is_free_sm = true;
break;
}
}
if (pio_can_add_program(candidate_pio, self->pio_program)) {
can_add_program = true;
}
if (is_free_sm && can_add_program) {
break;
}
}
if (!is_free_sm) {
//mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("no free state machines"));
return -1;
}
if (!can_add_program) {
//mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("not enough PIO program space"));
return -2;
}
self->pio = candidate_pio;
self->sm = pio_claim_unused_sm(self->pio, false);
self->prog_offset = pio_add_program(self->pio, self->pio_program);
pio_sm_init(self->pio, self->sm, self->prog_offset, NULL);
pio_sm_config config = pio_get_default_sm_config();
float pio_freq = self->rate *
SAMPLES_PER_FRAME *
dma_get_bits(self->mode, self->bits) *
PIO_INSTRUCTIONS_PER_BIT;
float clkdiv = (float)(clock_get_hz(clk_sys)) / pio_freq;
sm_config_set_clkdiv(&config, clkdiv);
if (self->mode == TX) {
sm_config_set_out_pins(&config, self->sd, 1);
sm_config_set_out_shift(&config, false, true, dma_get_bits(self->mode, self->bits));
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); // double TX FIFO size
} else { // RX
sm_config_set_in_pins(&config, self->sd);
sm_config_set_in_shift(&config, false, true, dma_get_bits(self->mode, self->bits));
sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX); // double RX FIFO size
}
sm_config_set_sideset(&config, 2, false, false);
sm_config_set_sideset_pins(&config, self->sck);
sm_config_set_wrap(&config, self->prog_offset, self->prog_offset + self->pio_program->length - 1);
pio_sm_set_config(self->pio, self->sm, &config);
return 0;
}
STATIC void pio_deinit(machine_i2s_obj_t *self) {
if (self->pio) {
pio_sm_set_enabled(self->pio, self->sm, false);
pio_sm_unclaim(self->pio, self->sm);
pio_remove_program(self->pio, self->pio_program, self->prog_offset);
}
}
STATIC void gpio_init_i2s(PIO pio, uint8_t sm, mp_hal_pin_obj_t pin_num, uint8_t pin_val, gpio_dir_t pin_dir) {
uint32_t pinmask = 1 << pin_num;
pio_sm_set_pins_with_mask(pio, sm, pin_val << pin_num, pinmask);
pio_sm_set_pindirs_with_mask(pio, sm, pin_dir << pin_num, pinmask);
pio_gpio_init(pio, pin_num);
}
STATIC void gpio_configure(machine_i2s_obj_t *self) {
gpio_init_i2s(self->pio, self->sm, self->sck, 0, GP_OUTPUT);
gpio_init_i2s(self->pio, self->sm, self->ws, 0, GP_OUTPUT);
if (self->mode == TX) {
gpio_init_i2s(self->pio, self->sm, self->sd, 0, GP_OUTPUT);
} else { // RX
gpio_init_i2s(self->pio, self->sm, self->sd, 0, GP_INPUT);
}
}
STATIC uint8_t dma_get_bits(i2s_mode_t mode, int8_t bits) {
if (mode == TX) {
return bits;
} else { // RX
// always read 32 bit words for I2S e.g. I2S MEMS microphones
return 32;
}
}
// determine which DMA channel is associated to this IRQ
STATIC uint dma_map_irq_to_channel(uint irq_index) {
for (uint ch = 0; ch < NUM_DMA_CHANNELS; ch++) {
if ((dma_irqn_get_channel_status(irq_index, ch))) {
return ch;
}
}
// This should never happen
return -1;
}
// note: first DMA channel is mapped to the top half of buffer, second is mapped to the bottom half
STATIC uint8_t *dma_get_buffer(machine_i2s_obj_t *i2s_obj, uint channel) {
for (uint8_t ch = 0; ch < I2S_NUM_DMA_CHANNELS; ch++) {
if (i2s_obj->dma_channel[ch] == channel) {
return i2s_obj->dma_buffer + (i2s_obj->sizeof_half_dma_buffer_in_bytes * ch);
}
}
// This should never happen
return NULL;
}
STATIC int dma_configure(machine_i2s_obj_t *self) {
uint8_t num_free_dma_channels = 0;
for (uint8_t ch = 0; ch < NUM_DMA_CHANNELS; ch++) {
if (!dma_channel_is_claimed(ch)) {
num_free_dma_channels++;
}
}
if (num_free_dma_channels < I2S_NUM_DMA_CHANNELS) {
//mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("cannot claim 2 DMA channels"));
return -1;
}
for (uint8_t ch = 0; ch < I2S_NUM_DMA_CHANNELS; ch++) {
self->dma_channel[ch] = dma_claim_unused_channel(false);
}
// The DMA channels are chained together. The first DMA channel is used to access
// the top half of the DMA buffer. The second DMA channel accesses the bottom half of the DMA buffer.
// With chaining, when one DMA channel has completed a data transfer, the other
// DMA channel automatically starts a new data transfer.
enum dma_channel_transfer_size dma_size = (dma_get_bits(self->mode, self->bits) == 16) ? DMA_SIZE_16 : DMA_SIZE_32;
for (uint8_t ch = 0; ch < I2S_NUM_DMA_CHANNELS; ch++) {
dma_channel_config dma_config = dma_channel_get_default_config(self->dma_channel[ch]);
channel_config_set_transfer_data_size(&dma_config, dma_size);
channel_config_set_chain_to(&dma_config, self->dma_channel[(ch + 1) % I2S_NUM_DMA_CHANNELS]);
uint8_t *dma_buffer = self->dma_buffer + (self->sizeof_half_dma_buffer_in_bytes * ch);
if (self->mode == TX) {
channel_config_set_dreq(&dma_config, pio_get_dreq(self->pio, self->sm, true));
channel_config_set_read_increment(&dma_config, true);
channel_config_set_write_increment(&dma_config, false);
dma_channel_configure(self->dma_channel[ch],
&dma_config,
(void *)&self->pio->txf[self->sm], // dest = PIO TX FIFO
dma_buffer, // src = DMA buffer
self->sizeof_half_dma_buffer_in_bytes / (dma_get_bits(self->mode, self->bits) / 8),
false);
} else { // RX
channel_config_set_dreq(&dma_config, pio_get_dreq(self->pio, self->sm, false));
channel_config_set_read_increment(&dma_config, false);
channel_config_set_write_increment(&dma_config, true);
dma_channel_configure(self->dma_channel[ch],
&dma_config,
dma_buffer, // dest = DMA buffer
(void *)&self->pio->rxf[self->sm], // src = PIO RX FIFO
self->sizeof_half_dma_buffer_in_bytes / (dma_get_bits(self->mode, self->bits) / 8),
false);
}
}
for (uint8_t ch = 0; ch < I2S_NUM_DMA_CHANNELS; ch++) {
dma_irqn_acknowledge_channel(self->i2s_id, self->dma_channel[ch]); // clear pending. e.g. from SPI
dma_irqn_set_channel_enabled(self->i2s_id, self->dma_channel[ch], true);
}
return 0;
}
STATIC void dma_deinit(machine_i2s_obj_t *self) {
for (uint8_t ch = 0; ch < I2S_NUM_DMA_CHANNELS; ch++) {
int channel = self->dma_channel[ch];
// unchain the channel to prevent triggering a transfer in the chained-to channel
dma_channel_config dma_config = dma_get_channel_config(channel);
channel_config_set_chain_to(&dma_config, channel);
dma_channel_set_config(channel, &dma_config, false);
dma_irqn_set_channel_enabled(self->i2s_id, channel, false);
dma_channel_abort(channel); // in case a transfer is in flight
dma_channel_unclaim(channel);
}
}
STATIC void dma_irq_handler(uint8_t irq_index) {
int dma_channel = dma_map_irq_to_channel(irq_index);
if (dma_channel == -1) {
// This should never happen
return;
}
machine_i2s_obj_t *self = machine_i2s_obj[irq_index];
if (self == NULL) {
// This should never happen
return;
}
uint8_t *dma_buffer = dma_get_buffer(self, dma_channel);
if (dma_buffer == NULL) {
// This should never happen
return;
}
if (self->mode == TX) {
// for non-blocking operation handle the write() method requests.
if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) {
copy_appbuf_to_ringbuf_non_blocking(self);
}
uint32_t trans_count = feed_dma(self, dma_buffer);
//dma_channel_set_trans_count (dma_channel, trans_count, false);
dma_channel_set_read_addr(dma_channel, dma_buffer, false);
dma_irqn_acknowledge_channel(irq_index, dma_channel);
} else { // RX
empty_dma(self, dma_buffer);
dma_irqn_acknowledge_channel(irq_index, dma_channel);
dma_channel_set_write_addr(dma_channel, dma_buffer, false);
// for non-blocking operation handle the readinto() method requests.
if ((self->io_mode == NON_BLOCKING) && (self->non_blocking_descriptor.copy_in_progress)) {
fill_appbuf_from_ringbuf_non_blocking(self);
}
}
}
STATIC void dma_irq0_handler(void) {
dma_irq_handler(0);
}
STATIC void dma_irq1_handler(void) {
dma_irq_handler(1);
}
STATIC int machine_i2s_init_helper(machine_i2s_obj_t *self,
mp_hal_pin_obj_t sck, mp_hal_pin_obj_t ws, mp_hal_pin_obj_t sd,
i2s_mode_t i2s_mode, int8_t i2s_bits, format_t i2s_format,
int32_t ring_buffer_len, int32_t i2s_rate) {
//
// ---- Check validity of arguments ----
//
// does WS pin follow SCK pin?
// note: SCK and WS are implemented as PIO sideset pins. Sideset pins must be sequential.
if (ws != (sck + 1)) {
//mp_raise_ValueError(MP_ERROR_TEXT("invalid ws (must be sck+1)"));
return -1;
}
// is Mode valid?
if ((i2s_mode != RX) &&
(i2s_mode != TX)) {
//mp_raise_ValueError(MP_ERROR_TEXT("invalid mode"));
return -2;
}
// is Bits valid?
if ((i2s_bits != 16) &&
(i2s_bits != 32)) {
//mp_raise_ValueError(MP_ERROR_TEXT("invalid bits"));
return -3;
}
// is Format valid?
if ((i2s_format != MONO) &&
(i2s_format != STEREO)) {
//mp_raise_ValueError(MP_ERROR_TEXT("invalid format"));
return -4;
}
// is Rate valid?
// Not checked
// is Ibuf valid?
if (ring_buffer_len > 0) {
self->ring_buffer_storage = m_new(uint8_t, ring_buffer_len);
;
ringbuf_init(&self->ring_buffer, self->ring_buffer_storage, ring_buffer_len);
} else {
//mp_raise_ValueError(MP_ERROR_TEXT("invalid ibuf"));
return -5;
}
self->sck = sck;
self->ws = ws;
self->sd = sd;
self->mode = i2s_mode;
self->bits = i2s_bits;
self->format = i2s_format;
self->rate = i2s_rate;
self->ibuf = ring_buffer_len;
self->non_blocking_descriptor.copy_in_progress = false;
self->io_mode = BLOCKING;
//memset(self->dma_buffer, 0, SIZEOF_DMA_BUFFER_IN_BYTES);
self->sizeof_half_dma_buffer_in_bytes = ((self->rate+999)/1000) * ((i2s_bits == 32) ? 8 : 4);
self->sizeof_non_blocking_copy_in_bytes = self->sizeof_half_dma_buffer_in_bytes * NON_BLOCKING_RATE_MULTIPLIER;
irq_configure(self);
int err = pio_configure(self);
if (err != 0) {
return err;
}
gpio_configure(self);
err = dma_configure(self);
if (err != 0) {
return err;
}
pio_sm_set_enabled(self->pio, self->sm, true);
dma_channel_start(self->dma_channel[0]);
return 0;
}
// STATIC machine_i2s_obj_t *mp_machine_i2s_make_new_instance(mp_int_t i2s_id) {
// if (i2s_id >= MAX_I2S_RP2) {
// mp_raise_ValueError(MP_ERROR_TEXT("invalid id"));
// }
// machine_i2s_obj_t *self;
// if (MP_STATE_PORT(machine_i2s_obj[i2s_id]) == NULL) {
// self = mp_obj_malloc(machine_i2s_obj_t, &machine_i2s_type);
// MP_STATE_PORT(machine_i2s_obj[i2s_id]) = self;
// self->i2s_id = i2s_id;
// } else {
// self = MP_STATE_PORT(machine_i2s_obj[i2s_id]);
// machine_i2s_deinit(self);
// }
// return self;
// }
STATIC machine_i2s_obj_t* machine_i2s_make_new(uint8_t i2s_id,
mp_hal_pin_obj_t sck, mp_hal_pin_obj_t ws, mp_hal_pin_obj_t sd,
i2s_mode_t i2s_mode, int8_t i2s_bits, format_t i2s_format,
int32_t ring_buffer_len, int32_t i2s_rate) {
if (i2s_id >= MAX_I2S_RP2) {
return NULL;
}
machine_i2s_obj_t *self;
// Deinit a machine if it already created
if (machine_i2s_obj[i2s_id] != NULL) {
self = machine_i2s_obj[i2s_id];
machine_i2s_deinit(self);
}
self = m_new_obj(machine_i2s_obj_t);
machine_i2s_obj[i2s_id] = self;
self->i2s_id = i2s_id;
if (machine_i2s_init_helper(self, sck, ws, sd, i2s_mode, i2s_bits,
i2s_format, ring_buffer_len, i2s_rate) != 0) {
return NULL;
}
return self;
}
STATIC void machine_i2s_deinit(machine_i2s_obj_t *self){
// use self->pio as in indication that I2S object has already been de-initialized
if (self != NULL) {
pio_deinit(self);
dma_deinit(self);
irq_deinit(self);
self->pio = NULL; // flag object as de-initialized
machine_i2s_obj[self->i2s_id] == NULL;
free(self->ring_buffer_storage);
free(self);
}
}
STATIC int machine_i2s_stream_read(machine_i2s_obj_t *self, void *buf_in, size_t size) {
if (self->mode != RX) {
return -1;
}
uint8_t appbuf_sample_size_in_bytes = (self->bits / 8) * (self->format == STEREO ? 2: 1);
if (size % appbuf_sample_size_in_bytes != 0) {
return -2;
}
if (size == 0) {
return 0;
}
mp_buffer_info_t appbuf;
appbuf.buf = (void *)buf_in;
appbuf.len = size;
uint32_t num_bytes_read = fill_appbuf_from_ringbuf(self, &appbuf);
return num_bytes_read;
}
STATIC int machine_i2s_stream_write(machine_i2s_obj_t *self, void *buf_in, size_t size) {
if (self->mode != TX) {
return -1;
}
uint8_t appbuf_sample_size_in_bytes = (self->bits / 8) * (self->format == STEREO ? 2: 1);
if (size % appbuf_sample_size_in_bytes != 0) {
return -2;
}
if (size == 0) {
return 0;
}
mp_buffer_info_t appbuf;
appbuf.buf = (void *)buf_in;
appbuf.len = size;
uint32_t num_bytes_write = copy_appbuf_to_ringbuf(self, &appbuf);
return num_bytes_write;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
machine_i2s_obj_t* create_machine_i2s(uint8_t i2s_id,
mp_hal_pin_obj_t sck, mp_hal_pin_obj_t ws, mp_hal_pin_obj_t sd,
i2s_mode_t i2s_mode, int8_t i2s_bits, format_t i2s_format,
int32_t ring_buffer_len, int32_t i2s_rate)
{
return machine_i2s_make_new(i2s_id, sck, ws, sd, i2s_mode, i2s_bits, i2s_format, ring_buffer_len, i2s_rate);
}
int machine_i2s_read_stream(machine_i2s_obj_t *self, void *buf_in, size_t size)
{
return machine_i2s_stream_read(self, buf_in, size);
}
int machine_i2s_write_stream(machine_i2s_obj_t *self, void *buf_in, size_t size){
return machine_i2s_stream_write(self, buf_in, size);
}
// void update_pio_frequency(machine_i2s_obj_t *self, uint32_t sample_freq) {
// uint8_t dma_bits = dma_get_bits(self->mode, self->bits);
// uint32_t system_clock_frequency = clock_get_hz(clk_sys);
// assert(system_clock_frequency < 0x40000000);
// uint32_t divider = system_clock_frequency * ((dma_bits == 32) ? 2 : 4) / sample_freq; // avoid arithmetic overflow
// assert(divider < 0x1000000);
// pio_sm_set_clkdiv_int_frac(self->pio, self->sm, divider >> 8u, divider & 0xffu);
// self->rate = sample_freq;
// self->sizeof_half_dma_buffer_in_bytes = ((self->rate+999)/1000) * ((self->bits == 32) ? 8 : 4);
// self->sizeof_non_blocking_copy_in_bytes = self->sizeof_half_dma_buffer_in_bytes * NON_BLOCKING_RATE_MULTIPLIER;
// }
\ No newline at end of file
......@@ -5,317 +5,240 @@
*
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/dma.h"
#include "hardware/irq.h"
#include "OpenPDM2PCM/OpenPDMFilter.h"
#include "pdm_microphone.pio.h"
#include "pico/pdm_microphone.h"
STATIC pdm_mic_obj* pdm_mic_inst[MAX_PDM_RP2] = {NULL, NULL};
STATIC const PIO pio_instances[NUM_PIOS] = {pio0, pio1};
#define PDM_DECIMATION 64
#define PDM_RAW_BUFFER_COUNT 2
static struct {
struct pdm_microphone_config config;
int dma_channel;
uint8_t* raw_buffer[PDM_RAW_BUFFER_COUNT];
volatile int raw_buffer_write_index;
volatile int raw_buffer_read_index;
uint raw_buffer_size;
uint dma_irq;
TPDMFilter_InitStruct filter;
uint16_t filter_volume;
pdm_samples_ready_handler_t samples_ready_handler;
} pdm_mic;
void pdm_dma_handler(uint8_t pdm_id);
STATIC void pdm_dma_irq0_handler();
STATIC void pdm_dma_irq1_handler();
static void pdm_dma_handler();
pdm_mic_obj* pdm_microphone_init(pdm_microphone_config* config) {
uint8_t pdm_id = config->pdm_id;
if(pdm_id >= MAX_PDM_RP2)
return NULL;
int pdm_microphone_init(const struct pdm_microphone_config* config) {
memset(&pdm_mic, 0x00, sizeof(pdm_mic));
memcpy(&pdm_mic.config, config, sizeof(pdm_mic.config));
if (config->sample_buffer_size % (config->sample_rate / 1000)) {
return NULL;
}
pdm_mic_obj* pdm_mic;
if(pdm_mic_inst[pdm_id] == NULL){
pdm_mic = m_new_obj(pdm_mic_obj);
pdm_mic_inst[pdm_id] = pdm_mic;
} else {
pdm_mic = pdm_mic_inst[pdm_id];
pdm_microphone_deinit(pdm_mic);
return -1;
}
memset(pdm_mic, 0x00, sizeof(pdm_mic));
pdm_mic->config = config;
pdm_mic->pdm_id = pdm_id;
// if(pdm_mic->pdm_id == 0)
// {
// pdm_mic->config->dma_irq = DMA_IRQ_0;
// }
// else
// {
// pdm_mic->config->dma_irq = DMA_IRQ_1;
// }
// pdm_mic->config->dma_irq = DMA_IRQ_0;
pdm_mic->raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8);
pdm_mic.raw_buffer_size = config->sample_buffer_size * (PDM_DECIMATION / 8);
for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
pdm_mic->raw_buffer[i] = malloc(pdm_mic->raw_buffer_size);
if (pdm_mic->raw_buffer[i] == NULL) {
pdm_microphone_deinit(pdm_mic);
pdm_mic.raw_buffer[i] = malloc(pdm_mic.raw_buffer_size);
if (pdm_mic.raw_buffer[i] == NULL) {
pdm_microphone_deinit();
return NULL;
return -1;
}
}
pdm_mic->dma_channel = dma_claim_unused_channel(true);
if (pdm_mic->dma_channel < 0) {
pdm_microphone_deinit(pdm_mic);
pdm_mic.dma_channel = dma_claim_unused_channel(true);
if (pdm_mic.dma_channel < 0) {
pdm_microphone_deinit();
return NULL;
return -1;
}
pdm_mic->pio_sm_offset = pio_add_program(pdm_mic->config->pio, &pdm_microphone_data_program);
pdm_mic->pio_sm = pio_claim_unused_sm(pdm_mic->config->pio, true);
uint pio_sm_offset = pio_add_program(config->pio, &pdm_microphone_data_program);
float clk_div = clock_get_hz(clk_sys) / (config->sample_rate * PDM_DECIMATION * 4.0);
pdm_microphone_data_init(
pdm_mic->config->pio,
pdm_mic->pio_sm,
pdm_mic->pio_sm_offset,
config->pio,
config->pio_sm,
pio_sm_offset,
clk_div,
config->gpio_data,
config->gpio_clk
);
dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic->dma_channel);
dma_channel_config dma_channel_cfg = dma_channel_get_default_config(pdm_mic.dma_channel);
channel_config_set_transfer_data_size(&dma_channel_cfg, DMA_SIZE_8);
channel_config_set_read_increment(&dma_channel_cfg, false);
channel_config_set_write_increment(&dma_channel_cfg, true);
channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(pdm_mic->config->pio, pdm_mic->pio_sm, false));
channel_config_set_dreq(&dma_channel_cfg, pio_get_dreq(config->pio, config->pio_sm, false));
pdm_mic.dma_irq = DMA_IRQ_0;
dma_channel_configure(
pdm_mic->dma_channel,
pdm_mic.dma_channel,
&dma_channel_cfg,
pdm_mic->raw_buffer[0],
(void *)&pdm_mic->config->pio->rxf[pdm_mic->pio_sm],
pdm_mic->raw_buffer_size,
pdm_mic.raw_buffer[0],
&config->pio->rxf[config->pio_sm],
pdm_mic.raw_buffer_size,
false
);
pdm_mic->filter.Fs = config->sample_rate;
pdm_mic->filter.LP_HZ = config->sample_rate / 2;
pdm_mic->filter.HP_HZ = 10;
pdm_mic->filter.In_MicChannels = 1;
pdm_mic->filter.Out_MicChannels = 1;
pdm_mic->filter.Decimation = PDM_DECIMATION;
pdm_mic->filter.MaxVolume = 64;
pdm_mic->filter.Gain = 16;
pdm_mic.filter.Fs = config->sample_rate;
pdm_mic.filter.LP_HZ = config->sample_rate / 2;
pdm_mic.filter.HP_HZ = 10;
pdm_mic.filter.In_MicChannels = 1;
pdm_mic.filter.Out_MicChannels = 1;
pdm_mic.filter.Decimation = PDM_DECIMATION;
pdm_mic.filter.MaxVolume = 64;
pdm_mic.filter.Gain = 16;
pdm_mic->filter_volume = pdm_mic->filter.MaxVolume;
pdm_mic->raw_buffer_write_index = 0;
pdm_mic->raw_buffer_read_index = 0;
return pdm_mic;
pdm_mic.filter_volume = pdm_mic.filter.MaxVolume;
}
void pdm_microphone_deinit(pdm_mic_obj *pdm_mic) {
void pdm_microphone_deinit() {
for (int i = 0; i < PDM_RAW_BUFFER_COUNT; i++) {
if (pdm_mic->raw_buffer[i]) {
free(pdm_mic->raw_buffer[i]);
if (pdm_mic.raw_buffer[i]) {
free(pdm_mic.raw_buffer[i]);
pdm_mic->raw_buffer[i] = NULL;
pdm_mic.raw_buffer[i] = NULL;
}
}
if (pdm_mic->dma_channel > -1) {
dma_channel_unclaim(pdm_mic->dma_channel);
if (pdm_mic.dma_channel > -1) {
dma_channel_unclaim(pdm_mic.dma_channel);
pdm_mic->dma_channel = -1;
pdm_mic.dma_channel = -1;
}
pdm_mic_inst[pdm_mic->pdm_id] == NULL;
}
static int irq0_handler_cntr = 0;
static int irq1_handler_cntr = 0;
int pdm_microphone_start(pdm_mic_obj *pdm_mic) {
irq_set_enabled(pdm_mic->config->dma_irq, true);
dma_irqn_acknowledge_channel(pdm_mic->config->dma_irq, pdm_mic->dma_channel);
dma_irqn_set_channel_enabled(pdm_mic->config->dma_irq, pdm_mic->dma_channel, true);
if(pdm_mic->config->dma_irq == DMA_IRQ_0)
{
dma_channel_set_irq0_enabled(pdm_mic->dma_channel, true);
if(irq0_handler_cntr == 0)
{
irq_set_exclusive_handler(pdm_mic->config->dma_irq, pdm_dma_irq0_handler);
}
irq0_handler_cntr++;
}
else
{
dma_channel_set_irq1_enabled(pdm_mic->dma_channel, true);
if(irq1_handler_cntr == 0)
{
irq_set_exclusive_handler(pdm_mic->config->dma_irq, pdm_dma_irq1_handler);
}
irq1_handler_cntr++;
int pdm_microphone_start() {
irq_set_enabled(pdm_mic.dma_irq, true);
irq_set_exclusive_handler(pdm_mic.dma_irq, pdm_dma_handler);
if (pdm_mic.dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(pdm_mic.dma_channel, true);
} else if (pdm_mic.dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(pdm_mic.dma_channel, true);
} else {
return -1;
}
Open_PDM_Filter_Init(&pdm_mic->filter);
Open_PDM_Filter_Init(&pdm_mic.filter);
pio_sm_set_enabled(
pdm_mic->config->pio,
pdm_mic->pio_sm,
pdm_mic.config.pio,
pdm_mic.config.pio_sm,
true
);
pdm_mic->raw_buffer_write_index = 0;
pdm_mic->raw_buffer_read_index = 0;
pdm_mic.raw_buffer_write_index = 0;
pdm_mic.raw_buffer_read_index = 0;
//-------- Strange workaround ------------
dma_channel_transfer_to_buffer_now(
pdm_mic->dma_channel,
pdm_mic->raw_buffer[0],
pdm_mic->raw_buffer_size
pdm_mic.dma_channel,
pdm_mic.raw_buffer[0],
pdm_mic.raw_buffer_size
);
//----------------------------------------
return 0;
pio_sm_set_enabled(
pdm_mic.config.pio,
pdm_mic.config.pio_sm,
true
);
}
void pdm_microphone_stop(pdm_mic_obj *pdm_mic) {
void pdm_microphone_stop() {
pio_sm_set_enabled(
pdm_mic->config->pio,
pdm_mic->pio_sm,
pdm_mic.config.pio,
pdm_mic.config.pio_sm,
false
);
dma_channel_abort(pdm_mic->dma_channel);
dma_channel_abort(pdm_mic.dma_channel);
if (pdm_mic->config->dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(pdm_mic->dma_channel, false);
} else if (pdm_mic->config->dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(pdm_mic->dma_channel, false);
if (pdm_mic.dma_irq == DMA_IRQ_0) {
dma_channel_set_irq0_enabled(pdm_mic.dma_channel, false);
} else if (pdm_mic.dma_irq == DMA_IRQ_1) {
dma_channel_set_irq1_enabled(pdm_mic.dma_channel, false);
}
irq_set_enabled(pdm_mic->config->dma_irq, false);
irq_set_enabled(pdm_mic.dma_irq, false);
}
STATIC uint dma_map_irq_to_channel(uint irq_index) {
for (uint ch = 0; ch < NUM_DMA_CHANNELS; ch++) {
if ((dma_irqn_get_channel_status(irq_index, ch))) {
return ch;
}
static void pdm_dma_handler() {
// clear IRQ
if (pdm_mic.dma_irq == DMA_IRQ_0) {
dma_hw->ints0 = (1u << pdm_mic.dma_channel);
} else if (pdm_mic.dma_irq == DMA_IRQ_1) {
dma_hw->ints1 = (1u << pdm_mic.dma_channel);
}
// This should never happen
return -1;
}
STATIC void pdm_dma_handle_mic(pdm_mic_obj *pdm_mic)
{
// get the current buffer index
pdm_mic.raw_buffer_read_index = pdm_mic.raw_buffer_write_index;
// get the next capture index to send the dma to start
pdm_mic->raw_buffer_read_index = pdm_mic->raw_buffer_write_index;
pdm_mic->raw_buffer_write_index = (pdm_mic->raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT;
pdm_mic.raw_buffer_write_index = (pdm_mic.raw_buffer_write_index + 1) % PDM_RAW_BUFFER_COUNT;
// give the channel a new buffer to write to and re-trigger it
dma_channel_transfer_to_buffer_now(
pdm_mic->dma_channel,
pdm_mic->raw_buffer[pdm_mic->raw_buffer_write_index],
pdm_mic->raw_buffer_size
pdm_mic.dma_channel,
pdm_mic.raw_buffer[pdm_mic.raw_buffer_write_index],
pdm_mic.raw_buffer_size
);
// dma_channel_set_write_addr(
// pdm_mic->dma_channel,
// pdm_mic->raw_buffer[pdm_mic->raw_buffer_write_index],
// false);
if (pdm_mic->samples_ready_handler) {
pdm_mic->samples_ready_handler(pdm_mic->pdm_id);
if (pdm_mic.samples_ready_handler) {
pdm_mic.samples_ready_handler();
}
}
void pdm_dma_handler(uint8_t irq_index) {
for(uint pdm_idx =0; pdm_idx < MAX_PDM_RP2; pdm_idx++) {
if((pdm_mic_inst[pdm_idx] != NULL) && (pdm_mic_inst[pdm_idx]->config->dma_irq == irq_index)){
uint ch = pdm_mic_inst[pdm_idx]->dma_channel;
if ((dma_irqn_get_channel_status(irq_index, ch))) {
pdm_mic_obj *pdm_mic = pdm_mic_inst[pdm_idx];
pdm_dma_handle_mic(pdm_mic);
// clear IRQ
dma_irqn_acknowledge_channel(irq_index, ch);
}
}
}
void pdm_microphone_set_samples_ready_handler(pdm_samples_ready_handler_t handler) {
pdm_mic.samples_ready_handler = handler;
}
STATIC void pdm_dma_irq0_handler()
{
pdm_dma_handler(DMA_IRQ_0);
void pdm_microphone_set_filter_max_volume(uint8_t max_volume) {
pdm_mic.filter.MaxVolume = max_volume;
}
STATIC void pdm_dma_irq1_handler()
{
pdm_dma_handler(DMA_IRQ_1);
void pdm_microphone_set_filter_gain(uint8_t gain) {
pdm_mic.filter.Gain = gain;
}
void pdm_microphone_set_samples_ready_handler(pdm_mic_obj *pdm_mic, pdm_samples_ready_handler_t handler) {
pdm_mic->samples_ready_handler = handler;
void pdm_microphone_set_filter_volume(uint16_t volume) {
pdm_mic.filter_volume = volume;
}
void pdm_microphone_set_filter_max_volume(pdm_mic_obj *pdm_mic, uint8_t max_volume) {
pdm_mic->filter.MaxVolume = max_volume;
}
void pdm_microphone_set_filter_gain(pdm_mic_obj *pdm_mic, uint8_t gain) {
pdm_mic->filter.Gain = gain;
}
void pdm_microphone_set_filter_volume(pdm_mic_obj *pdm_mic, uint16_t volume) {
pdm_mic->filter_volume = volume;
}
int pdm_microphone_read(pdm_mic_obj *pdm_mic, int16_t* buffer, size_t samples) {
int filter_stride = (pdm_mic->filter.Fs / 1000);
int pdm_microphone_read(int16_t* buffer, size_t samples) {
int filter_stride = (pdm_mic.filter.Fs / 1000);
samples = (samples / filter_stride) * filter_stride;
if (samples > pdm_mic->config->sample_buffer_size) {
samples = pdm_mic->config->sample_buffer_size;
if (samples > pdm_mic.config.sample_buffer_size) {
samples = pdm_mic.config.sample_buffer_size;
}
if (pdm_mic->raw_buffer_write_index == pdm_mic->raw_buffer_read_index) {
if (pdm_mic.raw_buffer_write_index == pdm_mic.raw_buffer_read_index) {
return 0;
}
uint8_t* in = pdm_mic->raw_buffer[pdm_mic->raw_buffer_read_index];
uint8_t* in = pdm_mic.raw_buffer[pdm_mic.raw_buffer_read_index];
int16_t* out = buffer;
// get the current buffer index
pdm_mic->raw_buffer_read_index = (pdm_mic->raw_buffer_read_index + 1) % PDM_RAW_BUFFER_COUNT;
pdm_mic.raw_buffer_read_index++;
for (int i = 0; i < samples; i += filter_stride) {
#if PDM_DECIMATION == 64
Open_PDM_Filter_64(in, out, pdm_mic->filter_volume, &pdm_mic->filter);
Open_PDM_Filter_64(in, out, pdm_mic.filter_volume, &pdm_mic.filter);
#elif PDM_DECIMATION == 128
Open_PDM_Filter_128(in, out, pdm_mic->filter_volume, &pdm_mic->filter);
Open_PDM_Filter_128(in, out, pdm_mic.filter_volume, &pdm_mic.filter);
#else
#error "Unsupported PDM_DECIMATION value!"
#endif
......@@ -324,7 +247,5 @@ int pdm_microphone_read(pdm_mic_obj *pdm_mic, int16_t* buffer, size_t samples) {
out += filter_stride;
}
return samples;
}
......@@ -20,27 +20,19 @@ static inline void pdm_microphone_data_init(PIO pio, uint sm, uint offset, float
pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, clk_pin, 1, true);
pio_gpio_init(pio, clk_pin);
pio_gpio_init(pio, data_pin);
//gpio_pull_up(pin); //?????
pio_sm_config c = pdm_microphone_data_program_get_default_config(offset);
sm_config_set_sideset_pins(&c, clk_pin);
sm_config_set_in_pins(&c, data_pin);
pio_sm_config c = pdm_microphone_data_program_get_default_config(offset);
sm_config_set_in_pins(&c, data_pin); // Data in pi
sm_config_set_sideset_pins(&c, clk_pin); // Clock controlled by side set
pio_gpio_init(pio, clk_pin);
pio_gpio_init(pio, data_pin);
// Shift to left, autopush disabled
sm_config_set_in_shift(&c, false, false, 8);
// Join RX channed to have deeper fifo. TX is not used
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
// Set clock divider
sm_config_set_clkdiv(&c, clk_div);
pio_sm_init(pio, sm, offset, &c);
// Need to call from app to sync microphones
//pio_sm_set_enabled(pio, sm, true);
}
%}
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
endif ()
if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
endif()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
)
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
GIT_SUBMODULES_RECURSE FALSE
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
else ()
FetchContent_Populate(
pico_sdk
QUIET
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
)
endif ()
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})
#include "pico/ring_buf.h"
// Ring Buffer
// Thread safe when used with these constraints:
// - Single Producer, Single Consumer
// - Sequential atomic operations
// One byte of capacity is used to detect buffer empty/full
void ringbuf_init(ring_buf_t *rbuf, uint8_t *buffer, size_t size) {
rbuf->buffer = buffer;
rbuf->size = size;
rbuf->head = 0;
rbuf->tail = 0;
}
bool ringbuf_push(ring_buf_t *rbuf, uint8_t data) {
size_t next_tail = (rbuf->tail + 1) % rbuf->size;
if (next_tail != rbuf->head) {
rbuf->buffer[rbuf->tail] = data;
rbuf->tail = next_tail;
return true;
}
// full
return false;
}
bool ringbuf_pop(ring_buf_t *rbuf, uint8_t *data) {
stdio_flush();
if (rbuf->head == rbuf->tail) {
// empty
return false;
}
*data = rbuf->buffer[rbuf->head];
rbuf->head = (rbuf->head + 1) % rbuf->size;
return true;
}
bool ringbuf_is_empty(ring_buf_t *rbuf) {
return rbuf->head == rbuf->tail;
}
bool ringbuf_is_full(ring_buf_t *rbuf) {
return ((rbuf->tail + 1) % rbuf->size) == rbuf->head;
}
size_t ringbuf_available_data(ring_buf_t *rbuf) {
return (rbuf->tail - rbuf->head + rbuf->size) % rbuf->size;
}
size_t ringbuf_available_space(ring_buf_t *rbuf) {
return rbuf->size - ringbuf_available_data(rbuf) - 1;
}
\ No newline at end of file
#include "pico/volume_ctrl.h"
uint16_t vol_to_db_convert(bool channel_mute, uint16_t channel_volume){
if(channel_mute)
return 0;
// todo interpolate
channel_volume += CENTER_VOLUME_INDEX * 256;
if (channel_volume < 0) channel_volume = 0;
if (channel_volume >= count_of(db_to_vol) * 256) channel_volume = count_of(db_to_vol) * 256 - 1;
uint16_t vol_mul = db_to_vol[((uint16_t)channel_volume) >> 8u];
return vol_mul;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment