diff --git a/firmware/Makefile b/firmware/Makefile index 8e6338b..aa9d906 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -4,12 +4,13 @@ #curl http://seb.exse.net/ledtest.hex > ledtest.hex && avrflash.sh m168 -F -v -v -U flash:w:ledtest.hex -#MCU = atmega88p #MCU = atmega328p - #MCU = atmega644 -F_CPU = 8000000 +MCU = atmega88p + #F_CPU = 20000000 +F_CPU = 8000000 + QUARZ = 8MHZ #internal RC/8 @@ -35,7 +36,7 @@ OPT = s ########################################################################################################## # List C source files here. (C dependencies are automatically generated.) -SRC = main.c lib/synth.c lib/usart.c +SRC = main.c lib/synth.c lib/usart.c lib/bughal.c ########################################################################################################## diff --git a/firmware/lib/bughal.c b/firmware/lib/bughal.c new file mode 100644 index 0000000..ba09cc6 --- /dev/null +++ b/firmware/lib/bughal.c @@ -0,0 +1,39 @@ +#include +#include "bughal.h" + +/* Hardware abstraction layer for Pentabug hardware */ + +/* + * initialize LEDs on C0-C3 + */ +void init_leds(void){ + //enable LED channels as output + DDRC |= (1 << PORTC0) | (1 << PORTC1) | (1 << PORTC2) | (1 << PORTC3); + // both LEDs off + PORTC &= ~(1 << PORTC0) | (1 << PORTC1) | (1 << PORTC2) | (1 << PORTC3); + + + //TCCR2A = (1 << WGM21); + //TCCR2B = (1 << CS20)|(1 << CS21); + //OCR2A = 255; /* TOP */ + // TCNT2 = 0; + // /*enable interrupt*/ + // TIMSK2 |= (1< +#include "synth.h" +#include "freq_table.h" +#include +#include + +// sample rate is 8M / (3 * 64) + +enum { + channel_count = 3, + //tick_length = 400, +// tick_length = 256, + row_length = 4, + pattern_length = 16 +}; + + + +static const synth_instrument_t instruments[] = { + { 1<<15, 100, 12 }, + { 0, 100, 12 }, + { 0, 200, 10 }, + { 1<<13, 0, 0, 2 }, + { 1<<13, 0, 5, 2 }, +}; + +static const uint8_t wave_table[][2] = { + { 0, WAVE_PULSE }, + { 3, WAVE_PULSE }, + { 7, WAVE_PULSE }, + { 12, WAVE_PULSE }, + { 256 - 4, 0xff }, + + { 0, WAVE_PULSE }, + { 2, WAVE_PULSE }, + { 7, WAVE_PULSE }, + { 10, WAVE_PULSE }, + { 256 - 4, 0xff }, + + { 0, WAVE_NOISE }, + { 0, WAVE_PULSE }, + { 0xff, 0xff }, + + { 0, WAVE_PULSE }, + { 0xff, 0xff }, + +}; + + +static const uint8_t patterns[][pattern_length][2] PROGMEM = { + {}, + { + { 33 - 12, 0 }, + { 0, 0 }, + { 0xff, 1 }, + { 0, 0 }, + { 33, 1 }, + { 0xff, 1 }, + { 33, 1 }, + { 0xff, 1 }, + { 33, 1 }, + { 0xff, 1 }, + { 33 - 12, 1 }, + { 0xff, 1 }, + { 33 - 12, 1 }, + { 0xff, 1 }, + { 33, 1 }, + { 0xff, 1 }, + }, + { + { 28 - 12, 0 }, + { 0, 0 }, + { 0xff, 1 }, + { 0, 0 }, + { 28, 1 }, + { 0xff, 1 }, + { 28, 1 }, + { 0xff, 1 }, + { 28, 1 }, + { 0xff, 1 }, + { 28 - 12, 1 }, + { 0xff, 1 }, + { 28 - 12, 1 }, + { 0xff, 1 }, + { 28, 1 }, + { 0xff, 1 }, + }, + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 57, 3 }, + }, + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 57, 4 }, + }, + + + { + { 60, 2 }, + }, + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 57, 2 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 55, 2 }, + { 0, 0 }, + { 57, 2 }, + { 0, 0 }, + }, + { + { 55, 2 }, + }, + { + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 57, 2 }, + }, + { + { 55-3, 2 }, + }, + + + + +}; + +static const uint8_t pattern_table[][channel_count] = { + { 1, 0, 5 }, + { 1, 3, 0 }, + { 1, 0, 7 }, + { 1, 3, 6 }, + { 2, 0, 7 }, + { 2, 4, 8 }, + { 2, 0, 9 }, + { 2, 4, 0 }, +}; +enum { + pattern_table_length = sizeof(pattern_table) / sizeof(pattern_table[0]) +}; + + +static synth_channel_t channels[channel_count]; +static int8_t sample; +static int8_t tick; +static int8_t row; +static int8_t seq; + + +/* PROTOTYPES */ +uint8_t synth_mix(void); + +static uint8_t timeslots[SYNTH_BUFSIZE]; +static uint8_t timeslots_write; // current write head +static uint8_t timeslots_read; // current read head + +/*register for atomic ++ and -- */ +register uint8_t timeslots_fill asm("r2"); + + +static void enqueue_timeslot(uint8_t synthval); +static uint8_t dequeue_timeslot(void); + +void synth_init(void) +{ + sample = 0; + tick = 0; + row = 0; + seq = 0; + timeslots_fill = 0; +} + +inline uint8_t synth_mix(void) +{ + if(sample == 0) { // new tick + for(int i = 1; i < channel_count; i++) { + synth_channel_t* chan = &channels[i]; + + const synth_instrument_t* inst = &instruments[chan->inst_nr]; + + if(chan->level > inst->decay) chan->level -= inst->decay; + else chan->level = 0; + + chan->pulse_width += inst->pulse_sweep; + + chan->pos++; + if(wave_table[chan->pos][1] == 0xff) chan->pos += wave_table[chan->pos][0]; + + + // enter new rol + // w + if(tick == 0) { + uint8_t pattern_nr = pattern_table[seq][i]; + uint8_t note = pgm_read_byte(&patterns[pattern_nr][row][0]); + + if(note) { // new note, maybe? + if(note == 0xff) { + chan->level = 0; + } else { + chan->level = 80; // TODO: less? + chan->note = note; + chan->inst_nr = pgm_read_byte(&patterns[pattern_nr][row][1]); + inst = &instruments[chan->inst_nr]; + chan->pos = inst->wave_table_pos; + if(inst->pulse_width) chan->pulse_width = inst->pulse_width; + } + } + } + } + } + if(++sample == 0){ + if(++tick == row_length) { + tick = 0; + if(++row == pattern_length) { + row = 0; + if(++seq == pattern_table_length) { + seq = 0; + } + } + } + } + + + uint8_t output = 0; + for(int i = 0; i < channel_count; i++) { + synth_channel_t* chan = &channels[i]; + // const synth_instrument_t* inst = &instruments[chan->inst_nr]; + + chan->phase += pgm_read_word(&freq_table[(uint8_t)(chan->note + wave_table[chan->pos][0])]); + + uint8_t amp; + switch(wave_table[chan->pos][1]) { + case WAVE_PULSE: + amp = -(chan->phase < chan->pulse_width); + break; + + case WAVE_SAW: + amp = (chan->phase >> 8); + break; + + case WAVE_NOISE: // shitty noise + chan->phase = (chan->phase >> 1) ^ (-(chan->phase & 1) & 0xb400); + amp = (chan->phase >> 8); + break; + + default: + amp = 0; + break; + } + + output += (((amp & 0xff) * chan->level) >> 8); + } + + return output; +} + + +/* fill all the timeslots */ +inline void synth_poll(void) { + /* refill timeslots queue */ +// while (timeslots_fill < (SYNTH_BUFSIZE-1)) + if (timeslots_fill < (SYNTH_BUFSIZE-1)) + enqueue_timeslot(synth_mix()); +} + +/* timeslot queue handling */ +static inline void enqueue_timeslot(uint8_t synthval) { + timeslots[timeslots_write & SYNTH_BUFMASK] = synthval; + timeslots_fill++; + timeslots_write++; +} + +static inline uint8_t dequeue_timeslot() { + uint8_t t = timeslots[timeslots_read & SYNTH_BUFMASK]; + if(timeslots_fill){ + /* buffer not underrun... move forward in readbuffer */ + + timeslots_fill --; + timeslots_read ++; + } + + return t; +} + + +ISR(TIMER0_COMPA_vect) +{ + /* calculate next analog sample value in synth mixer:*/ + OCR1B = dequeue_timeslot(); +} + + diff --git a/firmware/lib/synth.h b/firmware/lib/synth.h new file mode 100644 index 0000000..920d419 --- /dev/null +++ b/firmware/lib/synth.h @@ -0,0 +1,34 @@ +#ifndef SYNTH_H +#define SYNTH_H + +#define SYNTH_BUFSIZE (16) +#define SYNTH_BUFMASK (0b00001111) + +enum { WAVE_OFF, WAVE_PULSE, WAVE_SAW, WAVE_NOISE }; + +typedef struct { + uint8_t note; + uint8_t inst_nr; + uint8_t pos; + + uint16_t phase; + uint16_t pulse_width; + + uint8_t level; // envelop level + +} synth_channel_t; + + +typedef struct { + uint16_t pulse_width; + uint8_t pulse_sweep; + uint8_t wave_table_pos; + uint8_t decay; + +} synth_instrument_t; + + +void synth_init(void); +void synth_poll(void); + +#endif diff --git a/firmware/lib/usart.c b/firmware/lib/usart.c new file mode 100644 index 0000000..0f738f5 --- /dev/null +++ b/firmware/lib/usart.c @@ -0,0 +1,136 @@ +/* + * PentaFnord Firmware + * + * by Alexander Lorz + * + * + * The USART control code is derived by code from the 4CHLED project + * by sebseb7: https://github.com/sebseb7/eagle/tree/master/4CHLED/firmware + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include + +//#include "main.h" +#include "usart.h" + +#define UART_RXBUFSIZE 32 + +volatile static uint8_t rxbuf0[UART_RXBUFSIZE]; +volatile static uint8_t *volatile rxhead0, *volatile rxtail0; +//volatile uint8_t xon = 0; + + +ISR (USART_RX_vect) +{ + UCSR0B &= ~(1 << RXCIE0); + asm volatile("sei"); + + int diff; + uint8_t c; + c=UDR0; + diff = rxhead0 - rxtail0; + if (diff < 0) diff += UART_RXBUFSIZE; + if (diff < UART_RXBUFSIZE -1) + { + *rxhead0 = c; + ++rxhead0; + if (rxhead0 == (rxbuf0 + UART_RXBUFSIZE)) rxhead0 = rxbuf0; +// if((diff > 100)&&(xon==0)) +// { +// xon=1; +// //set the CTS pin +// } + } + UCSR0B |= (1 << RXCIE0); +} + + +void USART0_Init (void) +{ + // set baudrate + #undef BAUD + #define BAUD 115200 + #include + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + + //#if USE_2X + UCSR0A |= (1 << U2X0); // enable double speed operation + //#else + // UCSR0A &= ~(1 << U2X0); // disable double speed operation + //#endif + + + // flush receive buffer + while ( UCSR0A & (1 << RXC0) ) UDR0; + + // set 8N1 + UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); + UCSR0B &= ~(1 << UCSZ02); + + UCSR0B |= (1 << RXEN0)|(1 << TXEN0); //enable send and receive + + UCSR0B |= (1 << RXCIE0); //enable receive interrup + + rxhead0 = rxtail0 = rxbuf0; + +} + + + +void USART0_putc (char c) +{ + + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; +} + + +uint8_t USART0_Getc_nb(uint8_t *c) +{ + if (rxhead0==rxtail0) return 0; + *c = *rxtail0; + if (++rxtail0 == (rxbuf0 + UART_RXBUFSIZE)) rxtail0 = rxbuf0; + +// uint8_t diff = rxhead0 - rxtail0; +// if((diff < 10)&&(xon==1)) +// { +// xon=0; +// //set the CTS pin +// } + + return 1; +} + +void USART0_crlf(){ + USART0_putc(0x0A); //newline + USART0_putc(0x0D); //carriage return +}; + +void USART0_put_uint8(uint8_t x){ + uint8_t highchar=((x & 0b11110000)>>4)+0x30; + uint8_t lowchar = (x & 0b00001111)+0x30; + highchar = highchar>0x39 ? highchar + 0x07 : highchar; //chars A to F start with 0x41 not 0x3A + lowchar = lowchar>0x39 ? lowchar + 0x07 : lowchar; + USART0_putc(highchar); + USART0_putc(lowchar); + +} +void USART0_put_uint16(uint16_t x){ + USART0_put_uint8 ((x & 0xFF00)>>8); + USART0_put_uint8 (x & 0x00FF); + +} diff --git a/firmware/lib/usart.h b/firmware/lib/usart.h new file mode 100644 index 0000000..d0ddebc --- /dev/null +++ b/firmware/lib/usart.h @@ -0,0 +1,34 @@ +/* + * PentaFnord Firmware + * + * by Alexander Lorz + * + * + * The USART control code is derived by code from the 4CHLED project + * by sebseb7: https://github.com/sebseb7/eagle/tree/master/4CHLED/firmware + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _USART_H +#define _USART_H + +void USART0_Init (void); +void USART0_putc (char c); +uint8_t USART0_Getc_nb(uint8_t*); +void USART0_put_uint8(uint8_t); +void USART0_crlf(); +void USART0_put_uint16(uint16_t); + +#endif + diff --git a/firmware/main.c b/firmware/main.c index 146cbdb..6a178c4 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -49,29 +49,6 @@ static inline void init_pwm(void) return; } -static void init_leds(void) -{ - /* enable LED channels as output */ - DDRC |= (1 << PORTC0) | (1 << PORTC2) | - (1 << PORTC3) | (1 << PORTC1) ; - /* initially one led is on */ - PORTC = 0b00000101; - - /* - * Timer 2 - */ - - /* set timer2 to CTC & prescaler 64 → ???125kHz increment */ - TCCR2A = (1 << WGM21); - TCCR2B = (1 << CS20)|(1 << CS21); - - OCR2A = 255; /* TOP */ - TCNT2 = 0; - /*enable interrupt*/ - TIMSK2 |= (1<