本文章介紹mcu tinyML 語音識別功能,使用Edge Impulse作機器學習,然後deployment C++ Library在Raspberry Pi Pico(RP2040, Cortex M0+)上使用。
本專案架構分成兩部份,一部分為連接INMP441 I2S microphone使用 Edge Impulse deployment的C++ Library,識別語音,另一部分接MAX98357A I2S指令說明聲音檔與連接Relay控制開關。如下圖所示。
(專案名稱:pico_tinyML_voice)
(專案名稱:pico_tinyML_voice_receiver)
本專案使用到的驅動程式,請參閱之前的文章:
- nRF24L01:
[Raspberry Pi Pico] nRF24L01+ Ep. 1 : Pico-SDK C-code driver using IRQ - storage driver:
[Raspberry Pi Pico (c-sdk)] Storage: Ep 5. TinyUSB USB Mass Storage Device(USB Stick) with 2 LUNs - INMP441 I2S audio recorder:
[Raspberry Pi Pico] Audio Recorder USB device using INMP441 I2S microphone - MAX98357A I2S audio player:
[Raspberry Pi Pico (c-sdk)] PIO I2S audio player
專案pico_tinyML_voice:負責錄製訓練機器學習語音檔案與執行機器學習後推論。關於Edge Impulse匯入資料, 機器學習參數與佈署專案步驟請參閱成果影片動態說明。在專案中使用Continuous audio sampling,因此聲音擷取與推論必須平行處理,因為聲音擷取使用PIO與DMA因此不會佔用CPU,就可以達到平行運作。
專案pico_tinyML_voice_receive:透過nRF24L01接收pico_tinyML_voice聲音識別結果執行開關Relay與播放指令說明聲音檔。
成果影片
程式碼:
pico_tinyML_voice
- pico_tinyML_voice.cpp
#include "edge-impulse-sdk/classifier/ei_run_classifier.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "bsp/board.h"
#include "tusb.h"
#include "pico_storage.h"
#include "hardware/rtc.h"
#include "pico_audio_recorder.h"
#include "hardware/dma.h"
#include "nRF24L01.h"
extern bool new_read_data;
extern int16_t *fw_ptr;
#if DATA_ACQUISITION
#else
int raw_feature_get_data(size_t offset, size_t length, float *out_ptr) {
numpy::int16_to_float(fw_ptr+offset, out_ptr, length);
return 0;
}
#endif
//=== nRF24L01 irq
char const *addr[5] = {"0node", "1node", "2node","3node","4node"};
#define TX_PIN 17
void irq_callback(uint8_t event_type, uint8_t datapipe, uint8_t* data, uint8_t width) {
static uint32_t ack_payload=0;
uint8_t ack_buff[15];
switch(event_type) {
case EVENT_RX_DR:
data[width]='\0';
printf("RECV== pipe:%d, width:%d, %s\n", datapipe, width, data);
break;
case EVENT_TX_DS:
gpio_put(TX_PIN, !gpio_get(TX_PIN));
printf("event_data_sent:%d\n", datapipe);
break;
case EVENT_MAX_RT:
printf("event_max_rt:%d, \n", datapipe);
break;
}
}
/*------------- MAIN -------------*/
int main(void)
{
stdio_init_all();
#if DATA_ACQUISITION
rtc_init();
datetime_t t = {
.year=2024, .month=01, .day=28,
.dotw=6,
.hour=10, .min=32, .sec=50
};
if (!rtc_set_datetime(&t)) printf("set rtc error\n");
storage_driver_init();
board_init();
// init device stack on configured roothub port
tud_init(BOARD_TUD_RHPORT);
#else
gpio_init(TX_PIN);
gpio_set_dir(TX_PIN,true);
// == nRF24L01 init
nRF24_spi_default_init(8, 9, irq_callback);
nRF24_config_mode(TRANSMITTER);
nRF24_enable_feature(FEATURE_EN_DPL, true);
nRF24_set_TX_addr((uint8_t*)addr[0], 5);
nRF24_set_RX_addr(0, (uint8_t*)addr[0], 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
//== edge impulse
run_classifier_init();
EI_IMPULSE_ERROR res;
ei_impulse_result_t result = {nullptr};
signal_t features_signal;
features_signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
features_signal.get_data = &raw_feature_get_data;
#endif
inmp441_pio_init(INMP441_pio, INMP441_SM, INMP441_SD, INMP441_SCK, INMP441_SAMPLE_RATE);
uint8_t f_idx=0;
while (1)
{
#if DATA_ACQUISITION
if (get_recorder_state() == STATE_START_RECORDING) {
inmp441_starting_recording_to_file_wav();
}
tud_task(); // tinyusb device task
#else
if (new_read_data) {
new_read_data = false;
res = run_classifier_continuous(&features_signal, &result, false,true );
if (res != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", res);
}
if (++f_idx >= (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)) {
for (uint16_t i = 0; i < EI_CLASSIFIER_LABEL_COUNT; i++) {
if (result.classification[i].value > 0.8f)
{
ei_printf(" %s: ", ei_classifier_inferencing_categories[i]);
ei_printf("%.5f\r\n", result.classification[i].value);
nRF24_write_payload((uint8_t*)ei_classifier_inferencing_categories[i], strlen(ei_classifier_inferencing_categories[i]));
}
}
f_idx=0;
}
}
#endif
tight_loop_contents();
}
return 0;
}
- CMakeLists.txt
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")
set(PICO_BOARD pico CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
project(app C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(app
pico_tinyML_voice.cpp
usb_descriptors.c)
pico_set_program_name(app "pico_tinyML_voice")
pico_set_program_version(app "0.1")
pico_enable_stdio_uart(app 1)
pico_enable_stdio_usb(app 0)
# Add the standard library to the build
target_link_libraries(app
pico_stdlib
hardware_adc
hardware_dma
)
# Add the standard include files to the build
target_include_directories(app PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
# Add any user requested libraries
target_link_libraries(app
)
add_subdirectory(storage_driver)
# Add any user requested libraries
target_link_libraries(app
tinyusb_device
tinyusb_board
storage_driver
)
add_subdirectory(pico_audio_recorder)
# Add any user requested libraries
target_link_libraries(app
pico_audio_recorder
)
add_subdirectory(nRF24L01)
# Add any user requested libraries
target_link_libraries(app
nRF24L01_drv
)
add_subdirectory(pico_voice_recognize)
pico_add_extra_outputs(app)
- pico_audio_recoder.c
#include "stdio.h"
#include "pico/stdlib.h"
#include "hardware/dma.h"
#include "string.h"
#include "inttypes.h"
#include "pico_audio_recorder.pio.h"
#include "pico_audio_recorder.h"
#include "tusb.h"
#include "hardware/clocks.h"
#include "hardware/rtc.h"
#include "spi_sdmmc.h"
#if DATA_ACQUISITION
static int32_t buff1[PIO_DMA_BUFFER_SIZE/4];
static int32_t buff2[PIO_DMA_BUFFER_SIZE/4];
static int32_t *fw_ptr = buff1;
#else
int16_t buff1[PIO_DMA_BUFFER_SIZE];
int16_t buff2[PIO_DMA_BUFFER_SIZE];
int16_t *fw_ptr = buff1;
#endif
static int dma_read_buff1_channel;
static int dma_read_buff2_channel;
bool new_read_data=false;
static uint8_t recorder_state=STATE_STOP;
void start_stop_button_cb(uint gpio, uint32_t event_mask) {
if (gpio == BUTTON_PIN) {
if (event_mask == GPIO_IRQ_EDGE_RISE) {
gpio_acknowledge_irq(BUTTON_PIN, GPIO_IRQ_EDGE_RISE);
switch (get_recorder_state()) {
case STATE_STOP:
set_recorder_state(STATE_START_RECORDING);
break;
case STATE_RECORDING:
set_recorder_state(STATE_STOP);
break;
case STATE_START_RECORDING:
break;
}
printf("press:%d\n", get_recorder_state());
}
}
}
void pio_irq_read_data() {
if (pio_interrupt_get(INMP441_pio, 0)) {
pio_interrupt_clear(INMP441_pio, 0);
printf("irq 0:\n");
//dma_channel_start(dma_read_buff1_channel);
}
if (pio_interrupt_get(INMP441_pio, 1)) {
pio_interrupt_clear(INMP441_pio, 1);
printf("irq:1\n");
}
}
void dma_handler() {
//printf("dma irq1\n");
if (dma_channel_get_irq1_status(dma_read_buff1_channel)) {
dma_channel_acknowledge_irq1(dma_read_buff1_channel);
dma_channel_set_write_addr(dma_read_buff1_channel, buff1, false);
fw_ptr = buff1;
new_read_data=true;
//printf("dma irq1 channel 1=======\n");
}
if (dma_channel_get_irq1_status(dma_read_buff2_channel)) {
dma_channel_acknowledge_irq1(dma_read_buff2_channel);
dma_channel_set_write_addr(dma_read_buff2_channel, buff2, false);
fw_ptr = buff2;
new_read_data=true;
//printf("=========dma irq1 channel 2\n");
}
}
/*!
* \brief sdio memory card pio initialize
* \param pio: pio number
* \param sm state machine
* \param cmd_pin command pin
* \param clk_pin CLK pin
* \param data_pin_base data 0~4 pins(D0~D3)
* \param clk_int Integer part of the divisor
* \param clk_frac – Fractional part in 1/256ths
*/
void inmp441_pio_init(PIO pio, uint sm, uint sd_pin, uint sideset_pin, uint32_t freq) { // sideset_pin ws_clk
pio_gpio_init(pio, sd_pin);
pio_gpio_init(pio, sideset_pin);
pio_gpio_init(pio, sideset_pin+1);
gpio_pull_down(sd_pin);
//gpio_pull_up(sideset_pin);
//gpio_pull_up(sideset_pin+1);
//== INMP441 SM ==
uint offset = pio_add_program(pio, &inmp441_program);
pio_sm_config c = inmp441_program_get_default_config(offset);
pio_sm_set_consecutive_pindirs(pio, sm, sd_pin, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, sideset_pin, 2, true);
sm_config_set_in_pins(&c, sd_pin);
sm_config_set_set_pins(&c, sideset_pin,2);
sm_config_set_sideset_pins(&c, sideset_pin);
sm_config_set_in_shift(&c, false, true, INMP441_BIT_PER_SAMPLE);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
float div = clock_get_hz(clk_sys)/(freq*64*2);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
// initial state machine but not run
pio_sm_set_enabled(pio, sm, false);
//** for test only
uint pio_irq = pio_get_index(pio)? PIO1_IRQ_0:PIO0_IRQ_0;
pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
//pio_set_irq0_source_enabled(pio, pis_interrupt1, true);
irq_add_shared_handler(pio_irq, pio_irq_read_data, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(pio_irq, true);
uint pio_base = (pio==pio0)?PIO0_BASE:PIO1_BASE;
//==== READ DMA ===
dma_read_buff1_channel = dma_claim_unused_channel(true);
dma_read_buff2_channel = dma_claim_unused_channel(true);
dma_channel_config dc1 = dma_channel_get_default_config(dma_read_buff1_channel);
channel_config_set_write_increment(&dc1, true);
channel_config_set_read_increment(&dc1, false);
//channel_config_set_bswap(&dc1, true);
channel_config_set_dreq(&dc1, pio_get_dreq(pio, sm, false));
channel_config_set_chain_to(&dc1, dma_read_buff2_channel);
channel_config_set_transfer_data_size(&dc1, DMA_SIZE_16); //DMA_SIZE_8,16,32
#if DATA_ACQUISITION
dma_channel_configure(dma_read_buff1_channel,
&dc1, buff1, (void*) (pio_base+PIO_RXF0_OFFSET), // SDIO_DATA_READ_SM = 1
PIO_DMA_BUFFER_SIZE>> DMA_SIZE_16, false); //DMA_SIZE_8 or 16 or 32
#else
dma_channel_configure(dma_read_buff1_channel,
&dc1, buff1, (void*) (pio_base+PIO_RXF0_OFFSET), // SDIO_DATA_READ_SM = 1
PIO_DMA_BUFFER_SIZE, false); //DMA_SIZE_8 or 16 or 32
#endif
dma_channel_config dc2 = dma_channel_get_default_config(dma_read_buff2_channel);
channel_config_set_write_increment(&dc2, true);
channel_config_set_read_increment(&dc2, false);
//channel_config_set_bswap(&dc2, true);
channel_config_set_dreq(&dc2, pio_get_dreq(pio, sm, false));
channel_config_set_chain_to(&dc2, dma_read_buff1_channel);
channel_config_set_transfer_data_size(&dc2, DMA_SIZE_16); //DMA_SIZE_8,16,32
#if DATA_ACQUISITION
dma_channel_configure(dma_read_buff2_channel,
&dc2, buff2, (void*) (pio_base+PIO_RXF0_OFFSET), // SDIO_DATA_READ_SM = 1
PIO_DMA_BUFFER_SIZE>> DMA_SIZE_16, false); //DMA_SIZE_8 or 16 or 32
#else
dma_channel_configure(dma_read_buff2_channel,
&dc2, buff2, (void*) (pio_base+PIO_RXF0_OFFSET), // SDIO_DATA_READ_SM = 1
PIO_DMA_BUFFER_SIZE, false); //DMA_SIZE_8 or 16 or 32
#endif
dma_channel_set_irq1_enabled(dma_read_buff1_channel, true);
dma_channel_set_irq1_enabled(dma_read_buff2_channel, true);
// Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
//irq_set_exclusive_handler(DMA_IRQ_1, dma_handler);
irq_add_shared_handler(DMA_IRQ_1, dma_handler, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_1, true);
recorder_state = STATE_STOP;
gpio_init(BUTTON_PIN);
gpio_set_dir(BUTTON_PIN, false);
gpio_pull_down(BUTTON_PIN);
gpio_init(RED_PIN);
gpio_set_dir(RED_PIN, true);
gpio_init(GREEN_PIN);
gpio_set_dir(GREEN_PIN, true);
set_pin_LED(true);
#if DATA_ACQUISITION
gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, start_stop_button_cb);
#else
dma_channel_set_write_addr(dma_read_buff2_channel, buff2, false);
dma_channel_set_write_addr(dma_read_buff1_channel, buff1, true);
pio_sm_set_enabled(pio, sm, true);
#endif
}
bool write_file_wave_header(FIL *fil) {
UINT bw;
WAVE_HEADER wave_header;
uint16_t channels =1;
wave_header.riff[0] = 'R';wave_header.riff[1] = 'I';
wave_header.riff[2] = 'F';wave_header.riff[3] = 'F';
wave_header.size = (uint32_t)0;
wave_header.wave[0] = 'W';wave_header.wave[1] = 'A';
wave_header.wave[2] = 'V';wave_header.wave[3] = 'E';
wave_header.fmt[0] = 'f';wave_header.fmt[1] = 'm';
wave_header.fmt[2] = 't';wave_header.fmt[3] = ' ';
wave_header.fmt_size = 16;
wave_header.format = 1; // PCM
wave_header.channels = channels; // channels
wave_header.sampleRate=INMP441_SAMPLE_RATE; // sample rate
wave_header.rbc = INMP441_SAMPLE_RATE*INMP441_BIT_PER_SAMPLE*channels/8;
wave_header.bc = INMP441_SAMPLE_RATE*channels/8;
wave_header.bitsPerSample = INMP441_BIT_PER_SAMPLE; //bitsPerSample
wave_header.data[0] = 'd'; wave_header.data[1] = 'a';
wave_header.data[2] = 't'; wave_header.data[3] = 'a';
wave_header.data_size = 0;
if (f_write(fil, (uint8_t*)&wave_header, sizeof(wave_header), &bw)!= FR_OK) return false;
return true;
}
bool modify_file_wave_header(FIL *fil, uint32_t data_length) {
uint bw;
uint32_t total_len = data_length+36;
f_lseek(fil, 4);
f_write(fil, (uint8_t*)&total_len, 4, &bw);
f_lseek(fil, 40);
f_write(fil, (uint8_t*)&data_length, 4, &bw);
return true;
}
void set_pin_LED(bool green) {
if (green) {
gpio_put(GREEN_PIN, true);
gpio_put(RED_PIN, false);
} else {
gpio_put(GREEN_PIN, false);
gpio_put(RED_PIN, true);
}
}
void inmp441_starting_recording_to_file_wav() {
uint32_t wav;
uint bw;
FIL fil;
uint8_t filename[100];
datetime_t t;
FRESULT res;
uint32_t data_length;
FATFS fs;
rtc_get_datetime(&t);
if (tud_disconnect()) printf("disconnect tud\n");
if (f_mount(&fs, SDMMC_PATH,1) != FR_OK) {
tud_connect();
return;
}
sprintf(filename, "%s/V%02d%02d%02d-%02d%02d%02d.wav", SDMMC_PATH,t.year%100,t.month,t.day,t.hour,t.min,t.sec);
if (f_open(&fil, filename, FA_CREATE_ALWAYS|FA_WRITE) != FR_OK) {
printf("open file error:%s\n", filename);
tud_connect();
recorder_state=STATE_STOP;
set_pin_LED(true);
return;
}
if (!write_file_wave_header(&fil)) {
recorder_state=STATE_STOP;
set_pin_LED(true);
tud_connect();
return;
}
recorder_state = STATE_RECORDING;
set_pin_LED(false);
pio_sm_exec(INMP441_pio, INMP441_SM, pio_encode_mov(pio_isr, pio_null)); // enpty ISR
pio_sm_clear_fifos(INMP441_pio, INMP441_SM);
pio_sm_exec(INMP441_pio, INMP441_SM, pio_encode_jmp(inmp441_offset_inmp441_start));
//dma_channel_start(dma_read_buff1_channel);
dma_channel_set_write_addr(dma_read_buff2_channel, buff2, false);
dma_channel_set_write_addr(dma_read_buff1_channel, buff1, true);
pio_sm_set_enabled(INMP441_pio, INMP441_SM, true);
new_read_data=false;
sleep_ms(1200);
data_length=0;
while (recorder_state == STATE_RECORDING) {
if (new_read_data) {
new_read_data=false;
f_write(&fil, (uint8_t*)fw_ptr, PIO_DMA_BUFFER_SIZE, &bw);
data_length += PIO_DMA_BUFFER_SIZE;
}
}
modify_file_wave_header(&fil, data_length);
f_close(&fil);
f_unmount(SDMMC_PATH);
pio_sm_set_enabled(INMP441_pio, INMP441_SM, false);
dma_channel_abort(dma_read_buff1_channel);
dma_channel_abort(dma_read_buff2_channel);
tud_connect();
recorder_state=STATE_STOP;
set_pin_LED(true);
printf("finish\n");
}
uint8_t get_recorder_state() {
return recorder_state;
}
void set_recorder_state(uint8_t state) {
recorder_state = state;
}
- pico_audio_recoder.h
#ifndef __PICO_AUDIO_RECORDER_
#define __PICO_AUDIO_RECORDER_
#include "hardware/pio.h"
#include "ff.h"
#define DATA_ACQUISITION 0
#define INMP441_pio pio0
#define INMP441_SM 0
#define INMP441_SCK 18
#define INMP441_SD 20
#define INMP441_SAMPLE_RATE 16000
#define INMP441_BIT_PER_SAMPLE 16
#define BUTTON_PIN 21
#define RED_PIN 16
#define GREEN_PIN 17
#if DATA_ACQUISITION
#define PIO_DMA_BUFFER_SIZE 8192
#else
#define PIO_DMA_BUFFER_SIZE (16000/4) //EI_CLASSIFIER_SLICE_SIZE
#endif
enum {
STATE_STOP=0,
STATE_START_RECORDING=1,
STATE_RECORDING=2,
} RECORDER_STATE;
typedef struct _WaveHeader{
char riff[4];
uint32_t size;
char wave[4];
char fmt[4];
uint32_t fmt_size;
uint16_t format; //1:PCM
uint16_t channels; // channels
uint32_t sampleRate; // sample rate
uint32_t rbc;//sampleRate*bitsPerSample*channels/8
uint16_t bc; //bitsPerSample*channels/8
uint16_t bitsPerSample; //bitsPerSample
char data[4];
uint32_t data_size;
} WAVE_HEADER;
#ifdef __cplusplus
extern "C" {
#endif
void inmp441_pio_init(PIO pio, uint sm, uint sd_pin, uint sideset_pin, uint32_t freq);
void inmp441_pio_init_no_dma(PIO pio, uint sm, uint sd_pin, uint sideset_pin, uint32_t freq);
void inmp441_starting_recording_to_file_wav();
void inmp441_record_voice_1500ms(int16_t *features, uint32_t max_len);
uint8_t get_recorder_state();
void set_recorder_state(uint8_t state);
void set_pin_LED(bool green);
#ifdef __cplusplus
}
#endif
#endif
- pico_audio_recoder.pio
;======= get 16 bit in 16 bit frame=======
.program inmp441
.origin 0
.side_set 2 ; ws/clk
public inmp441_start:
.wrap_target
; left channel
set y, 2 side 0b00 ; ignore 1+3 bit
left_idle:
nop side 0b01
jmp y--, left_idle side 0b00
nop side 0b01 ; cycles:4
set x, 15 side 0b00
left_channel_loop:
in pins, 1 side 0b01
jmp x--, left_channel_loop side 0b00
set y, 10 side 0b01 ; cycle:4+16+1
left_idle_1:
nop side 0b00
jmp y--, left_idle_1 side 0b01 ;cycle:4+16+1+11=32
; right channel
set y, 2 side 0b10 ; ignore 1+3 bit
right_idle:
nop side 0b11
jmp y--, right_idle side 0b10
nop side 0b11 ; cycles:4
set x, 15 side 0b10
right_channel_loop:
; in pins, 1 side 0b11
nop side 0b11
jmp x--, right_channel_loop side 0b10
set y, 10 side 0b11 ; cycle:4+16+1
right_idle_1:
nop side 0b10
jmp y--, right_idle_1 side 0b11 ;cycle:4+16+1+11=32
.wrap
/*
;====== original =======================
.program inmp441
.origin 0
.side_set 2 ; ws/clk
public inmp441_start:
.wrap_target
; left channel
set x, 22 side 0b00
nop side 0b01
nop side 0b00
left_channel_loop:
in pins, 1 side 0b01
jmp x--, left_channel_loop side 0b00
in pins, 1 side 0b01
set y, 5 side 0b00
left_idle:
nop side 0b01
jmp y--, left_idle side 0b00
in NULL, 8 side 0b01
; right channel
set x, 22 side 0b10
nop side 0b11
nop side 0b10
right_channel_loop:
in pins, 1 side 0b11
jmp x--, right_channel_loop side 0b10
in pins, 1 side 0b11
set y, 5 side 0b10
right_idle:
nop side 0b11
jmp y--, right_idle side 0b10
in NULL, 8 side 0b11
.wrap
*/
- CMakeLists.txt
add_library(pico_audio_recorder INTERFACE)
pico_generate_pio_header(pico_audio_recorder ${CMAKE_CURRENT_LIST_DIR}/pico_audio_recorder.pio)
target_sources(pico_audio_recorder INTERFACE
${CMAKE_CURRENT_LIST_DIR}/pico_audio_recorder.c
)
target_include_directories(pico_audio_recorder INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
target_link_libraries(pico_audio_recorder INTERFACE
hardware_dma
hardware_pio
hardware_rtc
pico_stdlib
)
pico_tinyML_voce_receive
- pico_tinyML_voice_receiver.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "nRF24L01.h"
#include "string.h"
#include "ff.h"
#include "pico_storage.h"
#include "i2s_pio_dma/i2s_pio_dma.h"
#define RELAY_PIN 19
uint8_t addr[5][5] = {"0node", "1node", "2node","3node","4node"};
enum {
NOISE=1,
TURN_ON=2,
TURN_OFF=3,
HELLO=4,
};
uint8_t voice_state = 0;
void irq_callback(uint8_t event_type, uint8_t datapipe, uint8_t* data, uint8_t width) {
static uint32_t ack_payload=0;
uint8_t ack_buff[15];
switch(event_type) {
case EVENT_RX_DR:
data[width]='\0';
printf("RECV== pipe:%d, width:%d, %s\n", datapipe, width, data);
if (strcmp(data, "noise")==0) voice_state=NOISE;
if (strcmp(data, "turn_on")==0) voice_state=TURN_ON;
if (strcmp(data, "turn_off")==0) voice_state=TURN_OFF;
if (strcmp(data, "hello")==0) voice_state=HELLO;
break;
case EVENT_TX_DS:
printf("event_data_sent:%d\n", datapipe);
break;
case EVENT_MAX_RT:
printf("EVENT_MAX_RT:%d\n", datapipe);
break;
}
}
#define NUM_PIXELS 12
FATFS fs;
FIL fil;
char hello_wav[] = SDMMC_PATH"/inst.wav";
void voice_action(uint8_t stat) {
switch(stat) {
case TURN_ON:
gpio_put(RELAY_PIN, true);
break;
case TURN_OFF:
gpio_put(RELAY_PIN, false);
break;
case HELLO:
i2s_playWave(hello_wav);
break;
default:
break;
}
}
int main()
{
FRESULT res;
UINT br;
stdio_init_all();
storage_driver_init();
i2s_pio_init(pio0, 0, 16, 18);
res = f_mount(&fs, SDMMC_PATH,1);
if (res != FR_OK) {
printf("mount sd error\n");
return 0;
}
gpio_init(RELAY_PIN);
gpio_set_dir(RELAY_PIN, true);
uint32_t count=0;
uint8_t status;
nRF24_spi_default_init(8, 9, irq_callback);
nRF24_config_mode(RECEIVER);
nRF24_enable_feature(FEATURE_EN_DPL, true);
nRF24_set_RX_addr(0, addr[0], 5);
nRF24_enable_data_pipe_dynamic_payload_length(0, true);
while(1) {
if (voice_state == TURN_ON || voice_state == TURN_OFF || voice_state == HELLO) {
voice_action(voice_state);
}
tight_loop_contents();
}
return 0;
}
- CMakeLists.txt
# Generated Cmake Pico project file
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Initialise pico_sdk from installed location
# (note this can come from environment, CMake cache etc)
set(PICO_SDK_PATH "/home/duser/pico/pico-sdk")
set(PICO_BOARD pico CACHE STRING "Board type")
# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)
if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0")
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
endif()
project(pico_tinyML_voice_receiver C CXX ASM)
# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(pico_tinyML_voice_receiver pico_tinyML_voice_receiver.c )
pico_set_program_name(pico_tinyML_voice_receiver "pico_guesture_ws2812")
pico_set_program_version(pico_tinyML_voice_receiver "0.1")
pico_enable_stdio_uart(pico_tinyML_voice_receiver 0)
pico_enable_stdio_usb(pico_tinyML_voice_receiver 1)
# Add the standard library to the build
target_link_libraries(pico_tinyML_voice_receiver
pico_stdlib)
# Add the standard include files to the build
target_include_directories(pico_tinyML_voice_receiver PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required
)
# Add any user requested libraries
target_link_libraries(pico_tinyML_voice_receiver
)
add_subdirectory(nRF24L01)
target_link_libraries(pico_tinyML_voice_receiver
nRF24L01_drv
)
add_subdirectory(storage_driver)
# Add any user requested libraries
target_link_libraries(pico_tinyML_voice_receiver
storage_driver
)
add_subdirectory(i2s_pio_dma)
# Add any user requested libraries
target_link_libraries(pico_tinyML_voice_receiver
i2s_pio_dma
)
pico_add_extra_outputs(pico_tinyML_voice_receiver)
沒有留言:
張貼留言