diff --git a/buzzer/CMakeLists.txt b/buzzer/CMakeLists.txt index 3194566..8415d68 100644 --- a/buzzer/CMakeLists.txt +++ b/buzzer/CMakeLists.txt @@ -62,7 +62,7 @@ pico_enable_stdio_usb(buzzer 1) # Add the standard library to the build target_link_libraries(buzzer - pico_stdlib hardware_pwm pico_multicore hardware_exception) + pico_stdlib hardware_pwm pico_multicore hardware_exception hardware_adc) # Add the standard include files to the build target_include_directories(buzzer PRIVATE diff --git a/buzzer/README.md b/buzzer/README.md new file mode 100644 index 0000000..edfd6cf --- /dev/null +++ b/buzzer/README.md @@ -0,0 +1,39 @@ +# buzzer + + +A program to run alarm on button pushed. + +To activate the alarm, push the button. Same for desactivation. +The alarm is launched in a separate task and controlled by +the main task (which holds the push button logic). + +You can run the application in "DEBUG" mode using: +`make run-debug name=buzzer` +The FreeRTOS engine is replaced by threads. + +## Hardware + +### Variant 1 + +* 1x Passive Buzzer +* 1x Push button +* 1 NPN transistor for signal amplification +* 1x 1k ohm resistor (NPN collector) +* 2x 10k ohm resistor + +
+ +
+ +### Variant 2 +You can play with the frequency [1500; 2000] hz thanks to the potentiometer. + +* Variant 1 +* 1x Rotary potentiometer +* 1x blue LED + +
+ +
+ +Enjoy ! 😉 \ No newline at end of file diff --git a/buzzer/circuit_variant.jpg b/buzzer/circuit_variant.jpg new file mode 100644 index 0000000..1db738a Binary files /dev/null and b/buzzer/circuit_variant.jpg differ diff --git a/buzzer/gpio.c b/buzzer/gpio.c index 74869ab..59f4cfe 100644 --- a/buzzer/gpio.c +++ b/buzzer/gpio.c @@ -4,8 +4,7 @@ #include #include #include - -#define DEFAULT_WRAP_LEVEL 20 +#include #else #include #include @@ -37,6 +36,21 @@ void _sleep_us(size_t value) { #endif } +/** + * Returns a ratio of the ADC value input. + * + * The range of the ADC on Raspberry Pico 2 is 10 bits so the resolution is 1024 (2^10). + * + */ +double gpio_get_adc() { +#ifdef PICO + return adc_read() / 1023.0 ; // 2^10 - 1 +#else + trace("has_pwm && g->direction == GPIO_OUT) { g->level = level; @@ -44,7 +58,7 @@ void gpio_set_level(gpio *g, uint16_t level) { #ifdef PICO pwm_set_gpio_level(g->pin, level); #else - debug("", g->pin, level); + trace("", g->pin, level); #endif } } @@ -70,7 +84,7 @@ bool gpio_get_input(gpio* g) { } g->state = GPIO_INPUT_MOCK_COUNTER++ % value == 0; #endif - trace("", g->pin, g->state); + trace("", g->pin, g->state); return g->state; } return false; @@ -91,12 +105,15 @@ void gpio_onoff(gpio* g, uint32_t delay_ms, size_t times) { void init_gpio(void) { #ifdef PICO stdio_init_all(); + adc_init(); + adc_select_input(0); // pin: 26 for (size_t i = 0; i < NB_GPIO; i++) { gpio_init(GPIOS[i]->pin); gpio_set_dir(GPIOS[i]->pin, GPIOS[i]->direction); if (GPIOS[i]->has_pwm) { uint slice_num = pwm_gpio_to_slice_num(GPIOS[i]->pin); + gpio_set_function(GPIOS[i]->pin, GPIO_FUNC_PWM); pwm_set_enabled(slice_num, true); pwm_set_wrap(slice_num, DEFAULT_WRAP_LEVEL); } diff --git a/buzzer/gpio.h b/buzzer/gpio.h index 70119a3..20da477 100644 --- a/buzzer/gpio.h +++ b/buzzer/gpio.h @@ -22,7 +22,8 @@ #define GPIO_IN 0 #endif -#define NB_GPIO 3 +#define NB_GPIO 5 +#define DEFAULT_WRAP_LEVEL 20 typedef struct gpio gpio; struct gpio { @@ -45,11 +46,20 @@ static gpio GPIO_BUZZER = (gpio) { .pin = 15, .direction = GPIO_OUT, .has_pwm = false, .level = 0, .state = false }; -static gpio* GPIOS[NB_GPIO] = {&GPIO_DEFAULT_LED, &GPIO_BUTTON, &GPIO_BUZZER}; +static gpio GPIO_ADC = (gpio) { + .pin = 26, .direction = GPIO_IN, .has_pwm = false, .level = 0, .state = false +}; + +static gpio GPIO_LED = (gpio) { + .pin = 20, .direction = GPIO_OUT, .has_pwm = true, .level = 0, .state = false +}; + +static gpio* GPIOS[NB_GPIO] = {&GPIO_DEFAULT_LED, &GPIO_BUTTON, &GPIO_BUZZER, &GPIO_ADC, &GPIO_LED}; void gpio_set_level(gpio *g, uint16_t level); void gpio_set_state(gpio *g, bool state); bool gpio_get_input(gpio* g); +double gpio_get_adc(void); void gpio_onoff(gpio* g, uint32_t delay_ms, size_t times); void init_gpio(void); diff --git a/buzzer/jobs.c b/buzzer/jobs.c index d128b0d..c84d283 100644 --- a/buzzer/jobs.c +++ b/buzzer/jobs.c @@ -29,6 +29,7 @@ int init_app_state(AppState *s) { return -1; } #endif + s->freq_multiplicator = 0; return 0; } @@ -60,6 +61,7 @@ int cancel_task(AppState *s) { vTaskDelete(*s->task); free(s->task); gpio_set_state(GPIOS[2], false); + gpio_set_level(GPIOS[4],0); return 0; } #else @@ -76,6 +78,7 @@ int cancel_task(AppState *s) { free(s->task); s->task = NULL; } + gpio_set_level(GPIOS[4],0); return 0; #endif } @@ -91,8 +94,8 @@ int launch_task(AppState *s, FnJob f) { } BaseType_t res = - xTaskCreate(f, "task", configMINIMAL_STACK_SIZE, - NULL, configMAX_PRIORITIES - 1U, task); + xTaskCreateAffinitySet(f, "task", configMINIMAL_STACK_SIZE, + s, configMAX_PRIORITIES - 1U, 1 << 1,task); if (res != pdPASS) { error("unable to launch run alarm task task, code error: %d", @@ -117,6 +120,37 @@ int launch_task(AppState *s, FnJob f) { return 0; } +/** + * + */ +void check_acd_value(void* args) { + AppState* s = (AppState*) args; + uint16_t current_freq_multiplicator = 0; + + for (;;) { + double freq_mul = gpio_get_adc(); + if (freq_mul != current_freq_multiplicator) { +#ifdef PICO + gpio_set_level(GPIOS[4], current_freq_multiplicator * DEFAULT_WRAP_LEVEL); +#endif + + if (lock(s) != 0) + fatal(stop_app_fallback, "unable to lock app mutex"); + + s->freq_multiplicator = freq_mul; + current_freq_multiplicator = freq_mul; + + if (release(s) != 0) + fatal(stop_app_fallback, "unable to release app mutex"); + + debug("freq multiplicator updated: %d\n", current_freq_multiplicator); + } +#ifdef PICO + gpio_set_level(GPIOS[4], current_freq_multiplicator * DEFAULT_WRAP_LEVEL); +#endif + } +} + /** * Main app loop. * diff --git a/buzzer/jobs.h b/buzzer/jobs.h index 00082cd..8bc7c21 100644 --- a/buzzer/jobs.h +++ b/buzzer/jobs.h @@ -17,6 +17,7 @@ enum ACTION {STOP, START}; typedef struct AppState AppState; struct AppState { Action action; + double freq_multiplicator; #ifdef PICO TaskHandle_t* task; @@ -38,6 +39,7 @@ struct TaskParam { int init_app_state(AppState *s); void check_btn_state(void* args); +void check_acd_value(void* args); int lock(AppState *s); int release(AppState *s); diff --git a/buzzer/main.c b/buzzer/main.c index 64a676a..deaccb8 100644 --- a/buzzer/main.c +++ b/buzzer/main.c @@ -24,8 +24,8 @@ #include "gpio.h" #include "utils.h" -#define ALARM_BASE_FREQ 2000 // auditive frequency human range: [20; 20000] hertz -#define ALARM_PERIOD_MS 500 +#define ALARM_BASE_FREQ 1500 // auditive frequency human range: [20; 20000] hertz +#define ALARM_PERIOD_MS 200 #define PI 3.14 /** @@ -35,7 +35,8 @@ * * NOTE: `sleep_us` method does not impact FreeRTOS engine * since, the scheduler started on two cores (RP2350). The behavior may differs - * on one core. Update the FreeRTOS tick freq and implement `pdUS_TO_TICKS` if needed. + * on one core. Update the FreeRTOS tick freq and implement `pdUS_TO_TICKS` if needed or, + * run the alarm in dedicated core see: `xTaskCreateAffinitySet`. */ void gen_freq(gpio* g, size_t freq_hz, size_t duration_ms) { if (!freq_hz) { @@ -59,7 +60,22 @@ void gen_freq(gpio* g, size_t freq_hz, size_t duration_ms) { } void run_alarm(void *const params) { + AppState* as = (AppState* ) params; + for(;;) { + if (lock(as) != 0) + fatal(stop_app_fallback, "unable to lock the app state"); + + uint16_t freq = as->freq_multiplicator * 500 + ALARM_BASE_FREQ; + + if (release(as) != 0) + fatal(stop_app_fallback, "unable to release the app state"); + + +#ifdef PICO + gpio_set_level(GPIOS[4], as->freq_multiplicator * DEFAULT_WRAP_LEVEL); +#endif + debug("running alarm..."); float sin_val; int tone_val; @@ -68,18 +84,37 @@ void run_alarm(void *const params) { for (size_t i = 0; i < 360; i+=10) { sin_val = sinf(i * (PI / 180)); - tone_val = ALARM_BASE_FREQ + sin_val * (ALARM_BASE_FREQ / 10); + tone_val = freq + sin_val * (freq / 10); gen_freq(GPIOS[2], tone_val, duration_ms); } } } +int init_check_adc_value(AppState* as) { +#ifdef PICO + BaseType_t res = + xTaskCreateAffinitySet(check_acd_value, "check_adc_value", configMINIMAL_STACK_SIZE, + (void *const)as, configMAX_PRIORITIES - 1U, 1 << 0, NULL); + + if (res != pdPASS) + return -1; + + return 0; +#else + pthread_t task; + if (pthread_create(&task, NULL, check_acd_value, as) != 0) { + return -1; + } + return 0; +#endif +} + // Initialize the main FreeRTOS task. int init_rtos_main(TaskParam *params) { #ifdef PICO BaseType_t res = - xTaskCreate(check_btn_state, "main", configMINIMAL_STACK_SIZE, - (void *const)params, configMAX_PRIORITIES - 1U, NULL); + xTaskCreateAffinitySet(check_btn_state, "main", configMINIMAL_STACK_SIZE, + (void *const)params, configMAX_PRIORITIES - 1U, 1 << 0, NULL); if (res != pdPASS) return -1; @@ -105,9 +140,14 @@ int init_rtos_main(TaskParam *params) { * You can run the application in "DEBUG" mode using: * `make run-debug name=buzzer` * The FreeRTOS engine is replaced by threads. + * + * A variant to play with the frequency can be done adding a + * potentiometer and set `check_adc` to true. See README.md for details. + * */ int main() { init_gpio(); + bool check_adc = false; AppState as; if (init_app_state(&as) != 0) @@ -118,6 +158,11 @@ int main() { .f = run_alarm, }; + // launch the check of the ADC value reveived by the potentiometer + if (check_adc) + if (init_check_adc_value(&as) != 0) + fatal(stop_app_fallback, "unable to launch check adc value task"); + #ifdef PICO if (init_rtos_main(¶ms) != 0) fatal(stop_app_fallback, "unable to launch to FreeRTOS main task");