This repository has been archived on 2023-02-06. You can view files and clone it, but cannot push or open issues/pull-requests.
esp32-sequencer/main/esp32-sequencer.c

273 lines
8.2 KiB
C

#include <stdio.h>
#include <stdint.h>
#include "driver/gpio.h"
#include "driver/gptimer.h"
#include "driver/ledc.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_random.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "hal/gpio_types.h"
#include "hal/ledc_types.h"
#include "hal/timer_types.h"
#include "soc/clk_tree_defs.h"
/* Inputs */
// Clock In
/* Controls */
#define GPIO_MUTATE_BTN (9) // Mutate Button In
#define GPIO_TAP_TEMPO_BTN (10) // Tap Tempo Button In
static QueueHandle_t control_event_queue = NULL;
/* Outputs */
#define GPIO_CLK_OUT (7) // Clock Out
#define GPIO_GATE_OUT (8) // Gate CV Out
/* LED GPIO Pins */
#define LEDC_CLK_OUT (5)
#define LEDC_GATE_OUT (6)
/* Timers */
static gptimer_handle_t tap_tempo_timer = NULL;
static gptimer_handle_t gate_timer = NULL;
static const uint16_t DEBOUNCE_TIMEOUT = 20; // polling period length when debouncing in ms
static uint64_t debounce_time = 0; // system time of last intterupt
static uint8_t btn_state = 1; // state of gpio last recorded
static const uint32_t DEFAULT_BPM = 120;
#define SEQ_MAX_LEN 8
static uint8_t sequence[SEQ_MAX_LEN] = {0};
static uint8_t seq_len = SEQ_MAX_LEN;
static const char *TAG = "sequencer";
uint32_t bpm_to_ms(uint32_t bpm) {
return 60000 / bpm;
}
uint32_t ms_to_bpm(uint32_t ms) {
return 60000 / ms;
}
static void randomize_seq(uint8_t *sequence) {
for (uint8_t i = 0; i < SEQ_MAX_LEN; i++) {
sequence[i] = esp_random() % 2;
}
}
static void mutate_seq(uint8_t *sequence) {
ESP_LOGI(TAG, "Mutating Sequence");
// pick a step to mutate
uint8_t step = esp_random() % SEQ_MAX_LEN;
if (sequence[step] == 1) {
sequence[step] = 0;
} else if (sequence[step] == 0) {
sequence[step] = 1;
}
}
/* Handle tap tempo button presses */
static void tap_tempo_handler(){
// check the count to see if we have already started the timer
uint64_t count;
ESP_ERROR_CHECK(gptimer_get_raw_count(tap_tempo_timer, &count));
if (count == 0) { // the timer hasn't started yet
ESP_ERROR_CHECK(gptimer_start(tap_tempo_timer));
ESP_LOGI(TAG, "Starting tap tempo monitoring");
} else { // stop the timer and calc bpm
ESP_ERROR_CHECK(gptimer_stop(tap_tempo_timer));
ESP_LOGI(TAG, "Stopping tap tempo monitoring at %" PRIu64 "µs", count);
}
}
static void IRAM_ATTR gpio_isr_handler(void* arg) {
debounce_time = xTaskGetTickCount(); // set time of interrupt
uint32_t gpio_num = (uint32_t)arg;
btn_state = gpio_get_level(gpio_num); // set state
xQueueSendFromISR(control_event_queue, &gpio_num, NULL);
}
static void control_event_handler(void* arg) {
uint32_t gpio_num;
uint8_t last_btn_state;
uint64_t last_debounce_time;
while (1) {
// save these so we can make comparisons
last_btn_state = btn_state;
last_debounce_time = debounce_time;
uint8_t current_state = gpio_get_level(gpio_num);
if (xQueueReceive(control_event_queue, &gpio_num, portMAX_DELAY)) {
// Check if interrupt triggered stays the same for long enough
if ((current_state == last_btn_state) &&
(xTaskGetTickCount() - last_debounce_time > DEBOUNCE_TIMEOUT)) {
ESP_LOGI(TAG, "Handling control from GPIO %" PRIu32, gpio_num);
switch (gpio_num) {
case GPIO_MUTATE_BTN:
mutate_seq(sequence);
break;
case GPIO_TAP_TEMPO_BTN:
tap_tempo_handler();
break;
}
}
}
}
}
static void controls_init(void) {
// install gpio isr hservice
gpio_install_isr_service(0);
// Setup Mutate Button
gpio_reset_pin(GPIO_MUTATE_BTN);
gpio_set_direction(GPIO_MUTATE_BTN, GPIO_MODE_INPUT);
gpio_set_intr_type(GPIO_MUTATE_BTN, GPIO_INTR_NEGEDGE);
// Hook in handler for mutate button
gpio_isr_handler_add(GPIO_MUTATE_BTN, gpio_isr_handler, (void*)GPIO_MUTATE_BTN);
// Setup tap tempo Button
gpio_reset_pin(GPIO_TAP_TEMPO_BTN);
gpio_set_direction(GPIO_TAP_TEMPO_BTN, GPIO_MODE_INPUT);
gpio_set_intr_type(GPIO_TAP_TEMPO_BTN, GPIO_INTR_NEGEDGE);
// Hook in handler for tap tempo button
gpio_isr_handler_add(GPIO_TAP_TEMPO_BTN, gpio_isr_handler, (void *)GPIO_TAP_TEMPO_BTN);
// Setup tap tempo timer
gptimer_config_t tap_tempo_timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000 // 1MHz; 1 tick = 1 µs
};
ESP_ERROR_CHECK(gptimer_new_timer(&tap_tempo_timer_config, &tap_tempo_timer));
ESP_ERROR_CHECK(gptimer_enable(tap_tempo_timer));
}
static void gate_off_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
gptimer_stop(gate_timer);
gpio_set_level(GPIO_GATE_OUT, 0);
gpio_set_level(GPIO_CLK_OUT, 0);
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
}
static void gpio_init(void) {
// Setup gpio control event queue
control_event_queue = xQueueCreate(10, sizeof(uint32_t));
// Setup Clock CV Output
gpio_reset_pin(GPIO_CLK_OUT);
gpio_set_direction(GPIO_CLK_OUT, GPIO_MODE_OUTPUT);
// Setup Gate CV Output
gpio_reset_pin(GPIO_GATE_OUT);
gpio_set_direction(GPIO_GATE_OUT, GPIO_MODE_OUTPUT);
// Setup gate timer
gptimer_config_t cv_out_timer_config = {
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 1 * 1000 * 1000 // 1MHz; 1 tick = 1 µs
};
ESP_ERROR_CHECK(gptimer_new_timer(&cv_out_timer_config, &gate_timer));
// Configure gate timer alarm
gptimer_alarm_config_t cv_off_alarm_config = {
.alarm_count = 20 * 1000, // 20ms @ 1MHz
.reload_count = 0,
.flags.auto_reload_on_alarm = true
};
ESP_ERROR_CHECK(gptimer_set_alarm_action(gate_timer, &cv_off_alarm_config));
//register gate timer callback
gptimer_event_callbacks_t clk_cbs = {.on_alarm = gate_off_alarm_cb};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gate_timer, &clk_cbs, NULL));
ESP_ERROR_CHECK(gptimer_enable(gate_timer));
}
static void ledc_init(void) {
// Setup LEDC PWM timer configuration
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_0,
.duty_resolution = LEDC_TIMER_13_BIT,
.freq_hz = 5000,
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// Setup Clock LED
ledc_channel_config_t ledc_clk_channel = {
.channel = LEDC_CHANNEL_0,
.gpio_num = LEDC_CLK_OUT,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_clk_channel));
// Setup Gate LED
ledc_channel_config_t ledc_gate_channel = {
.channel = LEDC_CHANNEL_1,
.gpio_num = LEDC_GATE_OUT,
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_sel = LEDC_TIMER_0,
.intr_type = LEDC_INTR_DISABLE,
.duty = 0,
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_gate_channel));
}
static void pulse_cv(bool triggered) {
ESP_ERROR_CHECK(gptimer_start(gate_timer));
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 4095));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
gpio_set_level(GPIO_CLK_OUT, 1);
if (triggered) {
gpio_set_level(GPIO_GATE_OUT, 1);
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 4095));
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1));
}
}
void app_main(void) {
/* Initialize Peripherals */
/* Initialize GPIO */
gpio_init();
controls_init();
// Start task to handle control events
xTaskCreate(control_event_handler, "control event handler", 2048, NULL, 10, NULL);
/* Initialize Monitor LEDs */
ledc_init();
/* Initialize sequence */
randomize_seq(sequence);
/* Main Loop */
while (1) {
for (uint8_t step = 0; step < seq_len; step++) {
// if step has been set for sequence
if (sequence[step] == 1) {
pulse_cv(true);
} else {
pulse_cv(false);
}
printf("%i", sequence[step]);
vTaskDelay(bpm_to_ms(DEFAULT_BPM)/ portTICK_PERIOD_MS);
}
printf("\n");
}
}