| # https://www.fisheyecollaborative.org/fish-sounds/pempheris-schomburgkii
|
| $ ffmpeg -i pempheris.wav -f u8 -ar 22050 -ac 1 fishy.raw
|
| $ xxd -i fishy.raw > audio_data.h
|
|
|
| # CMakelist.txt
|
| cmake_minimum_required(VERSION 3.13)
|
| include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
| project(myapp C CXX ASM)
|
| set(CMAKE_C_STANDARD 11)
|
| set(CMAKE_CXX_STANDARD 17)
|
| pico_sdk_init()
|
| add_executable(${PROJECT_NAME} main.c)
|
| pico_add_extra_outputs(${PROJECT_NAME})
|
| # Link necessary libraries
|
| target_link_libraries(${PROJECT_NAME}
|
| pico_stdlib
|
| hardware_pwm
|
| hardware_clocks
|
| )
|
| pico_enable_stdio_usb(${PROJECT_NAME} 1)
|
| pico_enable_stdio_uart(${PROJECT_NAME} 0)
|
|
|
| # main.c
|
| //GPIO 16 → 100-470µF capacitor (+) → Speaker (+)
|
| //Speaker (-) → GND
|
| #include <stdio.h>
|
| #include <pico/stdlib.h>
|
| #include <hardware/pwm.h>
|
| #include <hardware/clocks.h>
|
| #include <hardware/timer.h>
|
| #define AUDIO_PIN 16
|
| #define SAMPLE_RATE 22050 // Hz
|
| #define PWM_FREQ 100000 // PWM carrier frequency
|
| // Example audio data (replace with your PCM data)
|
| const uint8_t audio_data[] = {
|
| // example start on 128 (0): 128, 150, 170, 190, 210, 190, 170, 150, 128, 100, 80, 60, 40, 60, 80, 100
|
| };
|
| const uint32_t audio_data_len = sizeof(audio_data);
|
| // Playback state
|
| volatile uint32_t sample_index = 0;
|
| repeating_timer_t audio_timer;
|
| void init_pwm_audio(uint gpio) {
|
| gpio_set_function(gpio, GPIO_FUNC_PWM);
|
| uint slice = pwm_gpio_to_slice_num(gpio);
|
| pwm_config cfg = pwm_get_default_config();
|
| float clk_hz = clock_get_hz(clk_sys);
|
| // Calculate divider for ~100kHz PWM carrier
|
| float div = clk_hz / (PWM_FREQ * 256.0f);
|
| pwm_config_set_clkdiv(&cfg, div);
|
| pwm_config_set_wrap(&cfg, 255);
|
| pwm_init(slice, &cfg, true);
|
| pwm_set_gpio_level(gpio, 128);
|
| }
|
| // Timer callback to update one sample
|
| bool audio_callback(repeating_timer_t *t) {
|
| if (sample_index >= audio_data_len) {
|
| pwm_set_gpio_level(AUDIO_PIN, 128); // silence
|
| return false; // stop timer
|
| }
|
| pwm_set_gpio_level(AUDIO_PIN, audio_data[sample_index++]);
|
| return true; // continue
|
| }
|
| void play_audio(uint gpio, const uint8_t *samples, uint32_t num_samples, uint32_t sample_rate) {
|
| sample_index = 0;
|
| // Compute interval in microseconds
|
| double interval_us = 1e6 / (double)sample_rate;
|
| // Start repeating timer
|
| add_repeating_timer_us(-interval_us, audio_callback, NULL, &audio_timer);
|
| // Wait until playback finishes
|
| while (audio_timer.alarm_id != 0 && sample_index < num_samples) {
|
| tight_loop_contents();
|
| }
|
| }
|
| void play_tone(uint gpio, uint16_t frequency, uint32_t duration_ms) {
|
| uint32_t period_us = 1000000 / frequency;
|
| uint32_t half = period_us / 2;
|
| uint32_t cycles = (duration_ms * 1000) / period_us;
|
| for (uint32_t i = 0; i < cycles; i++) {
|
| pwm_set_gpio_level(gpio, 255);
|
| sleep_us(half);
|
| pwm_set_gpio_level(gpio, 0);
|
| sleep_us(half);
|
| }
|
| pwm_set_gpio_level(gpio, 128);
|
| }
|
| int main() {
|
| stdio_init_all();
|
| printf("RP2040 PCM Audio Player using Timer\n");
|
| init_pwm_audio(AUDIO_PIN);
|
| sleep_ms(1000);
|
| while (true) {
|
| printf("Playing tone...\n");
|
| play_tone(AUDIO_PIN, 440, 300);
|
| sleep_ms(500);
|
| printf("Playing PCM audio...\n");
|
| play_audio(AUDIO_PIN, audio_data, audio_data_len, SAMPLE_RATE);
|
| sleep_ms(1000);
|
| }
|
| return 0;
|
| }
|