Hey Jan, so I managed make it output on 2 different pins and I am not struggling to make output at different frequencies. Here’s my code:
#include <stdio.h>
#include <math.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pio.h"
#include "hardware/clocks.h"
#include "pico-dco.pio.h"
#include "hardware/pwm.h"
#include "bsp/board.h"
#include "tusb.h"
#include "hardware/uart.h"
#define NUM_VOICES 2
#define MIDI_CHANNEL 1
const float BASE_NOTE = 440.0f;
const uint8_t RESET_PINS[NUM_VOICES] = {13, 8, 12, 9, 11, 10};
const uint8_t RANGE_PINS[NUM_VOICES] = {16, 19, 15, 18, 14, 17};
const uint8_t GATE_PINS[NUM_VOICES] = {2, 3, 4, 5, 6, 7};
const uint8_t VOICE_TO_PIO[NUM_VOICES] = {0, 0, 0, 0, 1, 1};
const uint8_t VOICE_TO_SM[NUM_VOICES] = {0, 1, 2, 3, 0, 1};
const uint16_t DIV_COUNTER = 1250;
uint8_t RANGE_PWM_SLICES[NUM_VOICES];
uint8_t NOTES[128];
uint32_t VOICES[NUM_VOICES];
uint8_t VOICE_NOTES[NUM_VOICES];
uint8_t NEXT_VOICE = 0;
uint32_t LED_BLINK_START = 0;
PIO pio[2] = {pio0, pio1};
uint8_t midi_serial_status = 0;
uint16_t midi_pitch_bend = 0x2000, last_midi_pitch_bend = 0x2000;
void init_sm(PIO pio, uint sm, uint offset, uint pin);
void set_frequency(PIO pio, uint sm, float freq);
float get_freq_from_midi_note(uint8_t note);
void led_blinking_task();
uint8_t get_free_voice();
void usb_midi_task();
void serial_midi_task();
void note_on_osc1(uint8_t note, uint8_t velocity);
void note_on_osc2(uint8_t note, uint8_t velocity);
void note_off(uint8_t note);
void pitch_bend_task();
int main() {
board_init();
tusb_init();
// use more accurate PWM mode for buck-boost converter
gpio_init(23);
gpio_set_dir(23, GPIO_OUT);
gpio_put(23, 1);
// init serial midi
uart_init(uart0, 31250);
uart_set_fifo_enabled(uart0, true);
gpio_set_function(0, GPIO_FUNC_UART);
gpio_set_function(1, GPIO_FUNC_UART);
// pwm init
for (int i=0; i<NUM_VOICES; i++) {
gpio_set_function(RANGE_PINS[i], GPIO_FUNC_PWM);
RANGE_PWM_SLICES[i] = pwm_gpio_to_slice_num(RANGE_PINS[i]);
pwm_set_wrap(RANGE_PWM_SLICES[i], DIV_COUNTER);
pwm_set_enabled(RANGE_PWM_SLICES[i], true);
}
// pio init
uint offset[2];
offset[0] = pio_add_program(pio[0], &frequency_program);
offset[1] = pio_add_program(pio[1], &frequency_program);
for (int i=0; i<NUM_VOICES; i++) {
init_sm(pio[VOICE_TO_PIO[i]], VOICE_TO_SM[i], offset[VOICE_TO_PIO[i]], RESET_PINS[i]);
}
// gate gpio init
for (int i=0; i<NUM_VOICES; i++) {
gpio_init(GATE_PINS[i]);
gpio_set_dir(GATE_PINS[i], GPIO_OUT);
}
// init voices
for (int i=0; i<NUM_VOICES; i++) {
VOICES[i] = 0;
}
while (1) {
tud_task();
usb_midi_task();
serial_midi_task();
pitch_bend_task();
led_blinking_task();
}
}
void init_sm(PIO pio, uint sm, uint offset, uint pin) {
init_sm_pin(pio, sm, offset, pin);
pio_sm_set_enabled(pio, sm, true);
}
void set_frequency(PIO pio, uint sm, float freq) {
uint32_t clk_div = clock_get_hz(clk_sys) / 2 / freq;
if (freq == 0) clk_div = 0;
pio_sm_put(pio, sm, clk_div);
pio_sm_exec(pio, sm, pio_encode_pull(false, false));
pio_sm_exec(pio, sm, pio_encode_out(pio_y, 32));
}
float get_freq_from_midi_note(uint8_t note) {
return pow(2, (note-69)/12.0f) * BASE_NOTE;
}
void usb_midi_task() {
if (tud_midi_available() < 4) return;
uint8_t buff[4];
LED_BLINK_START = board_millis();
board_led_write(true);
if (tud_midi_packet_read(buff)) {
if (buff[1] == (0x90 | (MIDI_CHANNEL-1))) {
if (buff[3] > 0) {
note_on_osc1(buff[2], buff[3]);
note_on_osc2(buff[2], buff[3]);
} else {
note_off(buff[2]);
}
}
if (buff[1] == (0x80 | (MIDI_CHANNEL-1))) {
note_off(buff[2]);
}
if (buff[1] == (0xE0 | (MIDI_CHANNEL-1))) {
midi_pitch_bend = buff[2] | (buff[3]<<7);
}
}
}
void serial_midi_task() {
if (!uart_is_readable(uart0)) return;
uint8_t lsb = 0, msb = 0;
uint8_t data = uart_getc(uart0);
LED_BLINK_START = board_millis();
board_led_write(true);
// cc status
if (data >= 0xF0 && data <= 0xF7) {
midi_serial_status = 0;
return;
}
// realtime message
if (data >= 0xF8 && data <= 0xFF) {
return;
}
if (data >= 0x80 && data <= 0xEF) {
midi_serial_status = data;
}
if (midi_serial_status >= 0x80 && midi_serial_status <= 0x90 ||
midi_serial_status >= 0xE0 && midi_serial_status <= 0xEF) {
lsb = uart_getc(uart0);
msb = uart_getc(uart0);
}
if (midi_serial_status == (0x90 | (MIDI_CHANNEL-1))) {
if (msb > 0) {
note_on_osc1(lsb, msb);
note_on_osc1(lsb, msb);
} else {
note_off(lsb);
}
}
if (midi_serial_status == (0x80 | (MIDI_CHANNEL-1))) {
note_off(lsb);
}
if (midi_serial_status == (0xE0 | (MIDI_CHANNEL-1))) {
midi_pitch_bend = lsb | (msb<<7);
}
}
void note_on_osc1(uint8_t note, uint8_t velocity) {
if (NOTES[note] > 0) return; // note already playing
uint8_t voice_num = 0;
NOTES[note] = voice_num;
VOICES[voice_num] = board_millis();
VOICE_NOTES[voice_num] = note;
float freq = get_freq_from_midi_note(note);
set_frequency(pio[VOICE_TO_PIO[0]], VOICE_TO_SM[0], freq);
// amplitude adjustment
pwm_set_chan_level(RANGE_PWM_SLICES[0], pwm_gpio_to_channel(RANGE_PINS[0]), (int)(DIV_COUNTER*(freq*0.00025f-1/(100*freq))));
// gate on
gpio_put(GATE_PINS[0], 1);
last_midi_pitch_bend = 0;
}
void note_on_osc2(uint8_t note, uint8_t velocity) {
if (NOTES[note] > 0) return; // note already playing
uint8_t voice_num = 1;
NOTES[note] = voice_num;
VOICES[voice_num] = board_millis();
VOICE_NOTES[voice_num] = note;
float freq = get_freq_from_midi_note(note);
set_frequency(pio[VOICE_TO_PIO[1]], VOICE_TO_SM[1], freq);
// amplitude adjustment
pwm_set_chan_level(RANGE_PWM_SLICES[0], pwm_gpio_to_channel(RANGE_PINS[1]), (int)(DIV_COUNTER*(freq*0.00025f-1/(100*freq))));
// gate on
gpio_put(GATE_PINS[0], 1);
last_midi_pitch_bend = 0;
}
void note_off(uint8_t note) {
// gate off
gpio_put(GATE_PINS[NOTES[note]], 0);
VOICE_NOTES[NOTES[note]] = 0;
VOICES[NOTES[note]] = 0;
NOTES[note] = 0;
}
uint8_t get_free_voice() {
uint32_t oldest_time = board_millis();
uint8_t oldest_voice = 0;
for (int i=0; i<NUM_VOICES; i++) {
uint8_t n = (NEXT_VOICE+i)%NUM_VOICES;
if (VOICES[n] == 0) {
NEXT_VOICE = (n+1)%NUM_VOICES;
return n;
}
if (VOICES[i]<oldest_time) {
oldest_time = VOICES[i];
oldest_voice = i;
}
}
return oldest_voice;
}
void pitch_bend_task() {
if (midi_pitch_bend != last_midi_pitch_bend) {
last_midi_pitch_bend = midi_pitch_bend;
for (int i=0; i<NUM_VOICES; i++) {
if (VOICE_NOTES[i] > 0) {
float freq = get_freq_from_midi_note(VOICE_NOTES[i]);
if (midi_pitch_bend != 0x2000) {
freq = freq-(freq*((0x2000-midi_pitch_bend)/67000.0f));
}
set_frequency(pio[VOICE_TO_PIO[i]], VOICE_TO_SM[i], freq);
}
}
}
}
void led_blinking_task() {
if (board_millis() - LED_BLINK_START < 50) return;
board_led_write(false);
}
For example: I was trying to double the frequency of one oscillator to make it play an octave higher like so:
void note_on_osc2(uint8_t note, uint8_t velocity) {
if (NOTES[note] > 0) return; // note already playing
uint8_t voice_num = 1;
NOTES[note] = voice_num;
VOICES[voice_num] = board_millis();
VOICE_NOTES[voice_num] = note;
float freq = get_freq_from_midi_note(note);
set_frequency(pio[VOICE_TO_PIO[1]], VOICE_TO_SM[1], freq * 2);
// amplitude adjustment
pwm_set_chan_level(RANGE_PWM_SLICES[0], pwm_gpio_to_channel(RANGE_PINS[1]), (int)(DIV_COUNTER*(freq*0.00025f-1/(100*freq))));
// gate on
gpio_put(GATE_PINS[0], 1);
last_midi_pitch_bend = 0;
}
But it had no effect. By the look of things the parameter is not coming from a Globalor static variable so why am I not able to change it?
Édit:
I have found how to make this happen with a bit of tinkering! I created a new function that calls set_frequency() inside of main() in the while loop! I am going to set-up 2x 3 way switches and will use the pins state to choose the frequency multiplier (0.5, 1, 2) to switch the octave in software. Then a good old tuning pots for each oscillators and we’ll have a mono dual DCO . I’ll fork the code when I am done and post it on GitHub this way people can get either the mono or poly version!