179 lines
4.7 KiB
C
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(¶ms) != 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(¶ms);
|
|
#endif
|
|
|
|
fatal(stop_app_fallback, "unable to launch the application");
|
|
}
|