picoco/buzzer/main.c
2025-03-05 10:43:11 +01:00

179 lines
4.7 KiB
C

//#define PICO
#ifdef PICO
#include <pico/printf.h>
#include <pico/stdlib.h>
#include "FreeRTOS.h"
#include "task.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#define PICO_DEFAULT_LED_PIN 0
#define GPIO_OUT 1
#define GPIO_IN 0
#endif
#include <math.h>
#include "log.h"
#include "jobs.h"
#include "gpio.h"
#include "utils.h"
#define ALARM_BASE_FREQ 1500 // auditive frequency human range: [20; 20000] hertz
#define ALARM_PERIOD_MS 200
#define PI 3.14
/**
* Oscillator for passive buzzer hardware.
*
* It generates desired `freq_hz` for `duration_ms`.
*
* 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 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) {
gpio_set_state(g, 0);
return;
}
float period = (1.0 / freq_hz) * 1000000; // signal period in micro seconds
size_t duty_period = period / 2; // working period = period / 2
size_t nb_period = duration_ms * 1000 / period; // number of period to play
debug("freq_hz = (%ld), period_us = (%.2f), duty_period_us = (%ld), nb_period = (%ld)", freq_hz, period,
duty_period, nb_period);
for (size_t i = 0; i < nb_period; i++) {
gpio_set_state(g, true);
_sleep_us(duty_period);
gpio_set_state(g, false);
_sleep_us(duty_period);
}
}
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;
const size_t duration_ms = ALARM_PERIOD_MS / 36;
for (size_t i = 0; i < 360; i+=10) {
sin_val = sinf(i * (PI / 180));
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 =
xTaskCreateAffinitySet(check_btn_state, "main", configMINIMAL_STACK_SIZE,
(void *const)params, configMAX_PRIORITIES - 1U, 1 << 0, NULL);
if (res != pdPASS)
return -1;
return 0;
#endif
}
/**
* 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).
*
* Hardware:
* - 1x Passive Buzzer
* - 1x Push button
* - 1 NPN transistor for signal amplification
* - 1x 1k ohm resistor (NPN collector)
* - 2x 10k ohm resistor
*
* 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)
fatal(stop_app_fallback, "unable to initilize the application");
TaskParam params = (TaskParam) {
.s = &as,
.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(&params) != 0)
fatal(stop_app_fallback, "unable to launch to FreeRTOS main task");
info("application init with success");
vTaskStartScheduler();
#else
info("application init with success");
check_btn_state(&params);
#endif
fatal(stop_app_fallback, "unable to launch the application");
}