297 lines
8.8 KiB
C
297 lines
8.8 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include "driver/gpio.h"
|
|
#include "driver/gptimer.h"
|
|
#include "esp_attr.h"
|
|
#include "esp_err.h"
|
|
#include "esp_random.h"
|
|
#include "esp_log.h"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/task.h"
|
|
#include "freertos/queue.h"
|
|
|
|
#include "driver/ledc.h"
|
|
#include "hal/gpio_types.h"
|
|
#include "hal/ledc_types.h"
|
|
#include "hal/timer_types.h"
|
|
#include "soc/clk_tree_defs.h"
|
|
|
|
#define SEQ_MAX_LEN 8
|
|
|
|
/* Inputs */
|
|
// Clock In
|
|
// Reset 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
|
|
|
|
gptimer_handle_t clk_cv_timer = NULL;
|
|
gptimer_handle_t gate_cv_timer = NULL;
|
|
|
|
/* LED GPIO Pins */
|
|
#define LEDC_CLK_OUT (5)
|
|
#define LEDC_GATE_OUT (6)
|
|
|
|
gptimer_handle_t clk_blink_timer = NULL;
|
|
gptimer_handle_t gate_blink_timer = NULL;
|
|
|
|
uint8_t sequence[SEQ_MAX_LEN] = {0};
|
|
uint8_t seq_len = SEQ_MAX_LEN;
|
|
|
|
static const uint32_t DEFAULT_BPM = 120;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static void IRAM_ATTR gpio_isr_handler(void* arg) {
|
|
uint32_t gpio_num = (uint32_t)arg;
|
|
xQueueSendFromISR(control_event_queue, &gpio_num, NULL);
|
|
}
|
|
|
|
static void control_event_handler(void* arg) {
|
|
uint32_t gpio_num;
|
|
while (1) {
|
|
if(xQueueReceive(control_event_queue, &gpio_num, portMAX_DELAY)) {
|
|
ESP_LOGI(TAG, "Handling control from GPIO %" PRIu32 , gpio_num);
|
|
switch (gpio_num) {
|
|
case GPIO_MUTATE_BTN:
|
|
mutate_seq(sequence);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void clk_cv_off_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
|
|
gptimer_stop(clk_cv_timer);
|
|
gpio_set_level(GPIO_CLK_OUT, 0);
|
|
}
|
|
|
|
static void gate_cv_off_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
|
|
gptimer_stop(gate_cv_timer);
|
|
gpio_set_level(GPIO_GATE_OUT, 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 CV output 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, &clk_cv_timer));
|
|
ESP_ERROR_CHECK(gptimer_new_timer(&cv_out_timer_config, &gate_cv_timer));
|
|
|
|
// Configure timer alarm
|
|
gptimer_alarm_config_t cv_off_alarm_config = {
|
|
.alarm_count = 10 * 1000, // 10ms @ 1MHz
|
|
.reload_count = 0,
|
|
.flags.auto_reload_on_alarm = true
|
|
};
|
|
ESP_ERROR_CHECK(gptimer_set_alarm_action(clk_cv_timer, &cv_off_alarm_config));
|
|
ESP_ERROR_CHECK(gptimer_set_alarm_action(gate_cv_timer, &cv_off_alarm_config));
|
|
|
|
//register clk callback
|
|
gptimer_event_callbacks_t clk_cbs = {.on_alarm = clk_cv_off_alarm_cb};
|
|
ESP_ERROR_CHECK(
|
|
gptimer_register_event_callbacks(clk_cv_timer, &clk_cbs, NULL));
|
|
ESP_ERROR_CHECK(gptimer_enable(clk_cv_timer));
|
|
|
|
// register gate callback
|
|
gptimer_event_callbacks_t gate_cbs = {.on_alarm = gate_cv_off_alarm_cb};
|
|
ESP_ERROR_CHECK(
|
|
gptimer_register_event_callbacks(gate_cv_timer, &gate_cbs, NULL));
|
|
ESP_ERROR_CHECK(gptimer_enable(gate_cv_timer));
|
|
}
|
|
|
|
static void gate_led_off_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
|
|
gptimer_stop(gate_blink_timer);
|
|
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));
|
|
}
|
|
|
|
static void clk_led_off_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) {
|
|
gptimer_stop(clk_blink_timer);
|
|
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 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));
|
|
|
|
// Setup LED blink timer
|
|
gptimer_config_t led_blink_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(&led_blink_timer_config, &clk_blink_timer));
|
|
ESP_ERROR_CHECK(gptimer_new_timer(&led_blink_timer_config, &gate_blink_timer));
|
|
|
|
// Configure timer alarm
|
|
gptimer_alarm_config_t led_off_alarm_config = {
|
|
.alarm_count = 50 * 1000, // 50ms @ 1MHz
|
|
.reload_count = 0,
|
|
.flags.auto_reload_on_alarm = true
|
|
};
|
|
ESP_ERROR_CHECK(gptimer_set_alarm_action(clk_blink_timer, &led_off_alarm_config));
|
|
ESP_ERROR_CHECK(gptimer_set_alarm_action(gate_blink_timer, &led_off_alarm_config));
|
|
|
|
//register clk callback
|
|
gptimer_event_callbacks_t clk_cbs = {.on_alarm = clk_led_off_alarm_cb};
|
|
ESP_ERROR_CHECK(
|
|
gptimer_register_event_callbacks(clk_blink_timer, &clk_cbs, NULL));
|
|
ESP_ERROR_CHECK(gptimer_enable(clk_blink_timer));
|
|
|
|
// register gate callback
|
|
gptimer_event_callbacks_t gate_cbs = {.on_alarm = gate_led_off_alarm_cb};
|
|
ESP_ERROR_CHECK(
|
|
gptimer_register_event_callbacks(gate_blink_timer, &gate_cbs, NULL));
|
|
ESP_ERROR_CHECK(gptimer_enable(gate_blink_timer));
|
|
}
|
|
|
|
static void blink_led(ledc_channel_t channel) {
|
|
ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, channel, 4095));
|
|
ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, channel));
|
|
|
|
if (channel == LEDC_CHANNEL_0) {
|
|
ESP_ERROR_CHECK(gptimer_start(clk_blink_timer));
|
|
} else if (channel == LEDC_CHANNEL_1) {
|
|
ESP_ERROR_CHECK(gptimer_start(gate_blink_timer));
|
|
}
|
|
}
|
|
|
|
static void pulse_cv(uint8_t gpio_num) {
|
|
gpio_set_level(gpio_num, 1);
|
|
if (gpio_num == GPIO_CLK_OUT) {
|
|
ESP_ERROR_CHECK(gptimer_start(clk_cv_timer));
|
|
} else if (gpio_num == GPIO_GATE_OUT) {
|
|
ESP_ERROR_CHECK(gptimer_start(gate_cv_timer));
|
|
}
|
|
}
|
|
|
|
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++) {
|
|
|
|
blink_led(LEDC_CHANNEL_0); // blink clock led
|
|
pulse_cv(GPIO_CLK_OUT);
|
|
|
|
// if step has been set for sequence
|
|
if (sequence[step] == 1) {
|
|
pulse_cv(GPIO_GATE_OUT);
|
|
blink_led(LEDC_CHANNEL_1); // blink gate triggered led
|
|
}
|
|
printf("%i", sequence[step]);
|
|
vTaskDelay(bpm_to_ms(DEFAULT_BPM)/ portTICK_PERIOD_MS);
|
|
}
|
|
printf("\n");
|
|
|
|
}
|
|
}
|