diff --git a/src/rp2_common/CMakeLists.txt b/src/rp2_common/CMakeLists.txt index c780ab7..9c2af51 100644 --- a/src/rp2_common/CMakeLists.txt +++ b/src/rp2_common/CMakeLists.txt @@ -10,4 +10,5 @@ pico_add_subdirectory(pico_sd_card) pico_add_subdirectory(pico_scanvideo_dpi) pico_add_subdirectory(usb_common) pico_add_subdirectory(usb_device) -pico_add_subdirectory(usb_device_msc) \ No newline at end of file +pico_add_subdirectory(usb_device_msc) +pico_add_subdirectory(pico_tone) diff --git a/src/rp2_common/pico_tone/CMakeLists.txt b/src/rp2_common/pico_tone/CMakeLists.txt new file mode 100644 index 0000000..0adb5dd --- /dev/null +++ b/src/rp2_common/pico_tone/CMakeLists.txt @@ -0,0 +1,11 @@ +if (NOT TARGET pico_tone) + pico_add_library(pico_tone) + + target_include_directories(pico_tone_headers INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/include) + + target_sources(pico_tone INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/tone.c) + + target_link_libraries(pico_tone INTERFACE hardware_pwm) +endif() diff --git a/src/rp2_common/pico_tone/include/pico/tone.h b/src/rp2_common/pico_tone/include/pico/tone.h new file mode 100644 index 0000000..23183fb --- /dev/null +++ b/src/rp2_common/pico_tone/include/pico/tone.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _PICO_TONE_H +#define _PICO_TONE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file tone.h + * \defgroup pico_tone pico_tone + * + * Adds support for playing tones using PWM. + * + * Every sound humans encounter consists of one or more frequencies, and the + * way the ear interprets those sounds is called pitch. + * + * In order to produce a variety of pitches, a digital signal needs to convey + * the frequency of sound it is trying to reproduce. The simplest approach is + * to generate a 50% duty cycle pulse stream and set the frequency to the + * desired pitch. + * + * References: + * - https://www.hackster.io/106958/pwm-sound-synthesis-9596f0#overview + */ + +/*! \brief Initialise the tone generator + * \ingroup pico_tone + * + * Initilise PWM on the given GPIO using the default pwm config. + * + * \param gpio The GPIO to use for the tone generator + */ +void tone_init(uint gpio); + +/*! \brief Play a tone for a given duration + * \ingroup pico_tone + * + * Play a tone on the given GPIO for the given duration. + * + * \param gpio The GPIO to use for the tone generator + * \param freq The frequency of the tone in Hz + * \param duration_ms The duration of the tone in milliseconds + */ +void tone(uint gpio, uint freq, uint32_t duration_ms); + +/*! \brief Do not play any tone. + * \ingroup pico_tone + * + * Stop playing a tone on the given GPIO. + * + * \param gpio The GPIO to use for the tone generator + */ +void no_tone(uint gpio); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/rp2_common/pico_tone/tone.c b/src/rp2_common/pico_tone/tone.c new file mode 100644 index 0000000..02c5a13 --- /dev/null +++ b/src/rp2_common/pico_tone/tone.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include "pico/stdlib.h" +#include "pico/tone.h" +#include "hardware/pwm.h" + +void tone_init(uint gpio) { + // Configure GPIO for PWM output + gpio_set_function(gpio, GPIO_FUNC_PWM); + // Find out which PWM slice is connected to GPIO + uint slice_num = pwm_gpio_to_slice_num(gpio); + // Get default configuration for PWM slice and initialise PWM with it + pwm_config config = pwm_get_default_config(); + pwm_init(slice_num, &config, true); +} + +void no_tone(uint gpio) { + pwm_set_gpio_level(gpio, 0); +} + +void tone(uint gpio, uint freq, uint32_t duration_ms) { + // Calculate and cofigure new clock divider according to the frequency + // This formula is assuming we are running at 125MHz. + // TODO: Make this work for any frequency + float clkdiv = (1.f / freq) * 2000.f; + uint slice_num = pwm_gpio_to_slice_num(gpio); + pwm_set_clkdiv(slice_num, clkdiv); + // Configure duty to 50% ((2**16)-1)/2) to generate a square wave + pwm_set_gpio_level(gpio, 32768U); + sleep_ms(duration_ms); +}