Fixed zerocopy dac module for drum sequencer

This commit is contained in:
Daniele Lacamera 2020-04-11 15:28:44 +02:00
parent 9dab007462
commit 9a6c8e5caa
14 changed files with 459 additions and 92 deletions

View file

@ -13,6 +13,8 @@ UMXFLAGS:=-Ilib/unicore-mx/include/ -DSTM32F4
LIBS+=$(UMX)
OBJS+=drumkit_0.o drumkit_1.o drumkit_2.o drumkit_3.o drumkit_4.o drumkit_5.o drumkit_6.o drums.o
LSCRIPT:=target.ld
OBJCOPY:=$(CROSS_COMPILE)objcopy

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# Waveblender
waveblender is a DIY synth drone.
Schematics+instructions coming soon.

163
dac.c
View file

@ -11,80 +11,130 @@
#include <unicore-mx/stm32/gpio.h>
#include <unicore-mx/stm32/rcc.h>
#include <unicore-mx/stm32/timer.h>
#include <stdlib.h>
#include "pot.h"
#include "timer.h"
#include "system.h"
#define DAC_BUFSIZ 512
#define DAC_BUFSIZ (512)
#define DAC_MEMSIZ (4 * DAC_BUFSIZ)
extern const unsigned char raw_au[];
extern volatile uint32_t jiffies;
const unsigned int raw_au_len;
static volatile int dac_written;
static int dac_transfer_size;
static int dac_chunk_size;
static uint8_t dac_outb[DAC_BUFSIZ];
static uint8_t dac_memory[DAC_MEMSIZ];
static volatile uint32_t dac_written;
static volatile int direct = 0;
static volatile int dac_busy = 0;
int dac_is_busy(void)
{
return dac_busy;
}
static void dac_xmit(void)
{
uint32_t size = DAC_BUFSIZ;
if ((dac_transfer_size == 0) || (dac_written >= dac_transfer_size)) {
dac_written = 0;
dac_transfer_size = 0;
memset(dac_memory, 0, DAC_MEMSIZ);
} else {
if ((dac_transfer_size - dac_written) < size)
size = dac_transfer_size - dac_written;
dac_chunk_size = size;
if (size == 0) {
dac_transfer_size = 0;
dac_written = 0;
memset(dac_memory, 0, DAC_MEMSIZ);
}
}
if (dac_transfer_size == 0) {
dma_disable_stream(DMA1, DMA_STREAM5);
dac_trigger_disable(CHANNEL_1);
dac_dma_disable(CHANNEL_1);
return;
}
dma_set_number_of_data(DMA1, DMA_STREAM5, size);
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t)(dac_memory + dac_written));
direct = 0;
/* Start DMA transfer of waveform */
dac_trigger_enable(CHANNEL_1);
dac_set_trigger_source(DAC_CR_TSEL1_T2);
dac_dma_enable(CHANNEL_1);
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t) (dac_outb + dac_written));
dma_set_number_of_data(DMA1, DMA_STREAM5, size);
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5);
dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_7);
dma_enable_stream(DMA1, DMA_STREAM5);
}
int dac_write(const void *buf, unsigned int len)
{
if (dac_written < dac_transfer_size) {
if ((len + dac_transfer_size) > DAC_BUFSIZ)
return 0;
}
if (dac_written >= dac_transfer_size) {
dac_written = 0;
dac_transfer_size = 0;
}
memcpy(dac_outb + dac_transfer_size, buf, len);
dac_transfer_size += len;
dac_xmit();
return dac_written;
}
int dac_space(void)
{
if (dac_written >= dac_transfer_size) {
dac_written = 0;
dac_transfer_size = 0;
}
return DAC_BUFSIZ - dac_transfer_size;
return DAC_MEMSIZ - dac_transfer_size;
}
/* IRQ Handler */
void dma1_stream5_isr(void)
{
if (dma_get_interrupt_flag(DMA1, DMA_STREAM5, DMA_TCIF)) {
if (dac_written < dac_transfer_size)
dac_written += dac_chunk_size;
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
dma_disable_stream(DMA1, DMA_STREAM5);
dac_trigger_disable(CHANNEL_1);
dac_dma_disable(CHANNEL_1);
if (dac_written >= dac_transfer_size) {
return;
dac_busy = 0;
if (!direct) {
if (dac_written < dac_transfer_size) {
dac_written += DAC_BUFSIZ;
} else {
dac_written = 0;
dac_transfer_size = 0;
return;
}
dac_xmit();
}
}
}
void dac_stop(void)
{
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
dma_disable_stream(DMA1, DMA_STREAM5);
dac_trigger_disable(CHANNEL_1);
dac_dma_disable(CHANNEL_1);
dac_busy = 0;
}
void dac_play_direct(uint8_t *mem, uint32_t size)
{
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
dma_disable_stream(DMA1, DMA_STREAM5);
dac_trigger_disable(CHANNEL_1);
dac_dma_disable(CHANNEL_1);
dma_set_number_of_data(DMA1, DMA_STREAM5, size);
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t)mem);
direct = 1;
dac_busy = 1;
/* Start DMA transfer of waveform */
dac_trigger_enable(CHANNEL_1);
dac_set_trigger_source(DAC_CR_TSEL1_T2);
dac_dma_enable(CHANNEL_1);
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5);
dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_7);
dma_enable_stream(DMA1, DMA_STREAM5);
}
/* Initialization functions */
@ -147,28 +197,57 @@ static void dac_hw_init(data_channel c)
dac_enable(c);
}
void dac_reset(void)
{
dac_written = 0;
dac_transfer_size = 0;
}
void dac_play(const uint8_t *buf, int len)
{
int i = 0;
int space;
while(i < len) {
space = dac_space();
if (space > 0) {
if (space > (len - i))
space = len - i;
dac_write(buf + i, space);
i += space;
int w = 0;
dac_written = 0;
dac_transfer_size = 0;
while (len > 0) {
space = DAC_MEMSIZ;
if (space > len) {
space = len;
}
if (dac_space() == 0) {
WFI();
continue;
}
memcpy(dac_memory, buf + w, space);
dac_transfer_size = space;
dac_xmit();
len -= space;
w += space;
}
}
}
extern unsigned char drumkit_0_au[];
extern unsigned char drumkit_1_au[];
extern unsigned char drumkit_2_au[];
extern unsigned int drumkit_0_au_len;
extern unsigned int drumkit_1_au_len;
extern unsigned int drumkit_2_au_len;
int dac_init(void)
{
int i;
uint32_t now;
dac_hw_init(CHANNEL_1);
dac_dma_setup();
pot_set_master(200);
dac_play(raw_au, raw_au_len);
pot_set_master(100);
// dac_play(drumkit_0_au, drumkit_0_au_len);
// dac_play(raw_au, raw_au_len);
dac_play_direct(drumkit_2_au, drumkit_2_au_len);
while(dac_is_busy())
WFI();
return 0;
}

1
dac.h
View file

@ -8,6 +8,7 @@
void dac_init(void);
int dac_write(const void *buf, unsigned int len);
int dac_play(const void *buf, unsigned int len);
void dac_play_direct(uint8_t *mem, uint32_t size);
#define pot_get_master() pot_get(2)
#define pot_set_master(x) pot_set(2,x)

102
drums.c Normal file
View file

@ -0,0 +1,102 @@
#include <stdint.h>
#include "system.h"
#include "display.h"
#include "systick.h"
#include "button.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ui.h"
#include "timer.h"
#include "led.h"
#define MAX_PATTERN_LEN 64
#define DRUMS_KICK (1 << 0)
#define DRUMS_HIHAT (1 << 1)
#define DRUMS_SNARE (1 << 2)
#define DRUMS_TOM1 (1 << 3)
#define DRUMS_TOM2 (1 << 4)
#define DRUMS_CYM (1 << 6)
#define DRUMS_CLAP (1 << 7)
#define TRACKS 8
static uint32_t pattern[MAX_PATTERN_LEN] = { DRUMS_KICK, DRUMS_HIHAT, DRUMS_SNARE, DRUMS_HIHAT, DRUMS_KICK, DRUMS_KICK, DRUMS_SNARE, DRUMS_HIHAT };
//static uint32_t pattern[MAX_PATTERN_LEN] = { DRUMS_KICK, DRUMS_KICK, DRUMS_KICK, DRUMS_KICK, DRUMS_KICK, DRUMS_KICK, DRUMS_SNARE, DRUMS_HIHAT };
static uint32_t pattern_len = 8;
static uint32_t pattern_pos = 0;
static unsigned char *dsample[TRACKS] = {};
static unsigned int dsample_len[TRACKS] = {};
void beat_cb(uint32_t b)
{
uint32_t key = pattern[pattern_pos];
int i;
for (i = 0; i < TRACKS; i++) {
if ((key & (1 << i)) == (1 << i)) {
if (dsample[i]) {
while(dac_is_busy())
dac_stop();
dac_play_direct(dsample[i], dsample_len[i]);
break;
}
}
}
pattern_pos++;
if (pattern_pos >= pattern_len) {
pattern_pos = 0;
}
led_beat((pattern_pos % 8) + 1);
}
void drums_init(void)
{
dsample[0] = drumkit_0_au;
dsample[1] = drumkit_1_au;
dsample[2] = drumkit_2_au;
dsample[3] = drumkit_3_au;
dsample[4] = drumkit_4_au;
dsample[5] = drumkit_5_au;
dsample[6] = drumkit_6_au;
dsample_len[0] = drumkit_0_au_len;
dsample_len[1] = drumkit_1_au_len;
dsample_len[2] = drumkit_2_au_len;
dsample_len[3] = drumkit_3_au_len;
dsample_len[4] = drumkit_4_au_len;
dsample_len[5] = drumkit_5_au_len;
dsample_len[6] = drumkit_6_au_len;
}
void drums_start(void)
{
timer_set_beat_callback(beat_cb);
}
void drums_stop(void)
{
timer_clear_beat_callback();
}
void drums_set_pattern_len(int l)
{
pattern_len = l;
timer_set_beat(1);
}
void drums_set(uint32_t track, int pos)
{
pattern[pos] |= track;
}
void drums_clear(uint32_t track, int pos)
{
pattern[pos] &= ~track;
}

17
led.c
View file

@ -43,14 +43,15 @@
struct pin Leds[9] = {
{ GPIOE, 1 << 0 },
{ GPIOE, 1 << 10 },
{ GPIOB, 1 << 2 },
{ GPIOE, 1 << 8 },
{ GPIOE, 1 << 9 },
{ GPIOE, 1 << 1 },
{ GPIOB, 1 << 8 },
{ GPIOB, 1 << 9 },
{ GPIOE, LD0 },
{ GPIOE, LD1 },
{ GPIOB, LD2 },
{ GPIOE, LD3 },
{ GPIOE, LD4 },
{ GPIOE, LD5 },
{ GPIOE, LD6 },
{ GPIOB, LD7 },
{ GPIOB, LD8 },
};
void led_setup(void)

11
main.c
View file

@ -39,7 +39,9 @@ static void bootlevel_0(void)
printf("Buttons initialized.\r\n");
}
extern void timer_init(void);
extern int timer_init(uint32_t bpm);
extern int timer_start();
extern int timer_stop();
static void bootlevel_1(void)
{
@ -64,8 +66,9 @@ static void bootlevel_1(void)
printf("Displaying splash screen...\r\n");
ui_init();
printf("UI initialized.\r\n");
//djhero_init();
timer_init();
timer_init(60);
drums_init();
drums_start();
printf("System up and running.\r\n\n\n");
}
@ -126,6 +129,8 @@ int main(void) {
led_on(LED);
poll_time = jiffies;
timer_poll();
if (!keepalive_callback)
ui_keepalive(hb_len);
else

21
make_drumkit.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
## Usage example:
## ./make_drumkit.sh kick1.wav hihat1.wav snare3.wav tom1.wav tom2.wav cowbell.wav clap.wav
IDX=0
for i in $@; do
echo $i
sox $i -r 22050 -t u8 drumkit_$IDX.au || exit 1
#sox $i -t u8 drumkit_$IDX.au
xxd -i drumkit_$IDX.au drumkit_$IDX.c
# xxd -i drumkit_$IDX.au drumkit_tmp_$IDX.c
# cat drumkit_tmp_$IDX.c | sed -e "s/unsigned /const unsigned /g" >drumkit_$IDX.c
# rm drumkit_$IDX.au
IDX=`expr $IDX + 1`
done
#rm -f drumkit_tmp_* drumkit_*.au

View file

@ -1,4 +1,4 @@
const unsigned char raw_au[] = {
unsigned char raw_au[] = {
0x57, 0x5a, 0x58, 0x59, 0x58, 0x58, 0x57, 0x58, 0x57, 0x57, 0x57, 0x56,
0x55, 0x55, 0x56, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54, 0x54, 0x53, 0x54,
0x54, 0x53, 0x53, 0x54, 0x53, 0x53, 0x53, 0x53, 0x52, 0x53, 0x53, 0x52,

View file

@ -1,7 +1,7 @@
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K - 0x100
RAM (rw) : ORIGIN = 0x20001000, LENGTH = 60K
RAM (rw) : ORIGIN = 0x20001000, LENGTH = 252K
RAM_S (rw) : ORIGIN = 0x20000000, LENGTH = 4K
}

113
timer.c
View file

@ -1,37 +1,95 @@
#include <stdint.h>
#include "system.h"
#include "led.h"
#include "timer.h"
#include <stdlib.h>
static uint32_t master_clock = 0;
#define S_PER_MINUTE (60)
/* Timer 4: Use val 60000 with PSC 1400 for 1s tick (84 Mhz) */
/* Timer 4: Use val 52500 with PSC 200 for 1/8 s tick (84 Mhz) */
extern uint32_t cpu_freq;
static uint32_t sys_bpm = 75;
#define TMR4_INIT_VAL 52500
#define TMR4_INIT_PSC 200
void (*beat_callback)(uint32_t b) = NULL;
void timer_init(void)
void timer_set_beat_callback(void (*b_cb)(uint32_t))
{
beat_callback = b_cb;
}
void timer_clear_beat_callback(void)
{
timer_set_beat_callback(NULL);
}
uint32_t timer_get_bpm(void)
{
return sys_bpm;
}
int timer_set_bpm(uint32_t bpm)
{
uint32_t val = 0;
uint32_t psc = 1;
uint32_t err = 0;
uint32_t reg = 0;
uint32_t clock = (cpu_freq / (4 * bpm)) * (S_PER_MINUTE);;
while (psc < 65535) {
val = clock / psc;
err = clock % psc;
if ((val < 65535) && (err < (psc / 2))) {
val--;
break;
}
val = 0;
psc++;
}
if (val == 0)
return -1;
TIM4_CR1 = 0;
__asm__ volatile ("dmb");
TIM4_PSC = psc;
TIM4_ARR = val;
TIM4_CNT = val - 1;
sys_bpm = bpm;
return 0;
}
int timer_start(void)
{
TIM4_CR1 |= TIM_CR1_CLOCK_ENABLE;
TIM4_DIER |= TIM_DIER_UIE;
}
int timer_stop(void)
{
TIM4_CR1 |= TIM_CR1_CLOCK_ENABLE;
TIM4_DIER |= TIM_DIER_UIE;
}
static void timer_irq_setup(void)
{
nvic_irq_enable(NVIC_TIM4_IRQN);
nvic_irq_setprio(NVIC_TIM4_IRQN, 0);
APB1_CLOCK_RST |= TIM4_APB1_CLOCK_ER_VAL;
__asm__ volatile ("dmb");
APB1_CLOCK_RST &= ~TIM4_APB1_CLOCK_ER_VAL;
APB1_CLOCK_ER |= TIM4_APB1_CLOCK_ER_VAL;
}
TIM4_CR1 = 0;
__asm__ volatile ("dmb");
TIM4_PSC = TMR4_INIT_PSC;
TIM4_ARR = TMR4_INIT_VAL;
TIM4_CR1 |= TIM_CR1_CLOCK_ENABLE;
TIM4_DIER |= TIM_DIER_UIE;
__asm__ volatile ("dmb");
int timer_init(uint32_t bpm)
{
timer_stop();
timer_irq_setup();
timer_set_bpm(bpm);
timer_start();
return 0;
}
static volatile uint32_t tim4_ticks = 0;
static volatile int pending_cb = 0;
void isr_tim1(void)
{
TIM1_SR &= ~TIM_SR_UIF;
@ -42,16 +100,35 @@ void isr_tim3(void)
TIM3_SR &= ~TIM_SR_UIF;
}
static int beat = 1;
int timer_get_beat(void)
{
return beat;
}
void timer_set_beat(int b)
{
beat = b;
}
void timer_poll(void)
{
if (beat_callback && (pending_cb > 0)) {
pending_cb--;
beat_callback(beat);
beat++;
}
}
void isr_tim4(void)
{
TIM4_SR &= ~TIM_SR_UIF;
tim4_ticks++;
if (beat_callback) {
pending_cb++;
} else {
led_beat((beat % 8) + 1);
}
void gettime(uint32_t *seconds, uint32_t *microseconds)
{
*microseconds = ((TIM4_CNT * TMR4_INIT_PSC) / 84) + (tim4_ticks & 0x07) * 125000;
*seconds = (tim4_ticks >> 3);
}

28
timer.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED
uint32_t timer_get_bpm(void);
int timer_set_bpm(uint32_t bpm);
int timer_start(void);
int timer_stop(void);
int timer_init(uint32_t bpm);
void timer_set_beat_callback(void (*b_cb)(uint32_t));
void timer_clear_beat_callback(void);
extern unsigned char drumkit_0_au[],
drumkit_1_au[],
drumkit_2_au[],
drumkit_3_au[],
drumkit_4_au[],
drumkit_5_au[],
drumkit_6_au[];
extern unsigned int drumkit_0_au_len,
drumkit_1_au_len,
drumkit_2_au_len,
drumkit_3_au_len,
drumkit_4_au_len,
drumkit_5_au_len,
drumkit_6_au_len;
#endif

4
ui.c
View file

@ -14,6 +14,8 @@
#include <string.h>
#include <stdio.h>
#include "ui.h"
#include "timer.h"
#include "led.h"
/* Uncomment for device initialization */
@ -170,7 +172,7 @@ void ui_init(void)
now = jiffies;
for (i = 1; i < 9; i++) {
led_beat(i);
while((jiffies - now < 100))
while((jiffies - now < 10))
WFI();
now = jiffies;
}

View file

@ -49,16 +49,20 @@ static void display_drone(int line, uint16_t val)
display_text(line, txt);
}
static void display_volume(void)
static void display_thousand(uint32_t val)
{
char txt[4] = "000";
int master_vol = pot_get_master();
txt[0] = '0' + master_vol / 100;
txt[1] = '0' + (master_vol % 100) / 10;
txt[2] = '0' + (master_vol % 10);
txt[0] = '0' + val / 100;
txt[1] = '0' + (val % 100) / 10;
txt[2] = '0' + (val % 10);
display_text(6, txt);
}
static void display_volume(void)
{
display_thousand(pot_get_master());
}
void ui_drone_xy_poll(void)
{
uint16_t x = 0, y = 0;
@ -157,7 +161,7 @@ static void ui_mastervol_input(uint8_t press, int hold)
{
int master_vol = pot_get_master();
if (press == 'U')
if (master_vol < 1000)
if (master_vol < 100)
pot_set_master(master_vol + 5);
if (press == 'D') {
@ -182,6 +186,45 @@ static void ui_mastervol(const void *arg)
display_volume();
}
static void ui_bpm_input(uint8_t press, int hold)
{
uint32_t sys_bpm = timer_get_bpm();
if (press == 'U') {
if (sys_bpm < 300) {
timer_stop();
timer_set_bpm(sys_bpm + 1);
led_beat(1);
timer_set_beat(1);
timer_start();
}
}
if (press == 'D') {
if (sys_bpm > 60) {
timer_stop();
timer_set_bpm(sys_bpm - 1);
led_beat(1);
timer_set_beat(1);
timer_start();
}
}
if (press == '+') {
display_clear(NULL);
clear_input_callback();
ui_display_menu(&MainMenu);
return;
}
display_text(5, " BPM ");
display_thousand(timer_get_bpm());
}
static void ui_bpm(const void *arg)
{
set_input_callback(ui_bpm_input);
display_clear(NULL);
display_text(5, " BPM ");
display_thousand(timer_get_bpm());
}
static void ui_bytebeat_input(uint8_t press, int hold) {
if (press == '+') {
@ -194,22 +237,23 @@ static void ui_bytebeat_input(uint8_t press, int hold) {
#define PLAY_SIZE 512
static uint8_t bb_buffer[PLAY_SIZE];
void ui_bytebeat_keepalive(void)
{
static uint32_t t = 0;
int r = 0;
if (dac_space() >= PLAY_SIZE) {
for(;;t++) {
for(r = 0;;t++) {
while (dac_is_busy())
WFI();
bb_buffer[r++] = (t<<1)|(t>>4)*(((t>>12)|(t>>13)|(t>>6) | ((t>>2)|(t>>4))|(t<<1)|(t<<12)|((t<<5)&~(t>>22))));
if (r >= PLAY_SIZE) {
dac_write(bb_buffer, r);
dac_play_direct(bb_buffer, r);
break;
}
}
}
}
static void ui_bytebeat(const void *arg)
{
@ -254,7 +298,7 @@ const struct display_menu MainMenu = {
.entry_n = 5,
.entry = {
{ "Master Volume ", ui_mastervol, NULL },
{ "Settings ", ui_submenu, &SettingsMenu },
{ "BPM" , ui_bpm, NULL},
{ "Pattern ", ui_submenu, &PatternMenu },
{ "Drone ", ui_submenu, &DroneMenu},
{ "Bytebeat ", ui_bytebeat },