/sqwak_box.c
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define F_CPU 8000000UL
#include <util/delay.h>
#define USI_PULSE_0 (1<<USIWM0)|(1<<USITC)
#define USI_PULSE_1 (1<<USIWM0)|(1<<USICLK)|(1<<USITC)
#define SAMPLE_DEF_SIZE 6
register struct {
uint8_t tick: 1;
uint8_t play: 1;
} flags asm("r15");
register uint8_t sample_buffer asm("r14");
uint8_t n_samples;
uint8_t button_state;
uint8_t channels;
uint16_t button_debounce;
uint32_t sample_count;
void usi_init() {
// Set CS high initially
PORTA = 0b10000000;
// Set PA7 (CS) PA5 (DO) and PA4 (USCK) to outputs
DDRA = 0b10110000;
}
void usi_deinit() {
PORTA = 0;
DDRA = 0;
}
#define spi_cs_enable() (PORTA &= ~0b10000000)
#define spi_cs_disable() (PORTA |= 0b10000000)
uint8_t spi_transfer(uint8_t d) {
cli();
USIDR = d;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
USICR = USI_PULSE_0;
USICR = USI_PULSE_1;
sei();
return USIDR;
}
void flash_id() {
spi_cs_enable();
spi_transfer(0x9F);
spi_transfer(0x00);
spi_transfer(0x00);
spi_transfer(0x00);
spi_cs_disable();
}
void begin_playback(uint8_t sample) {
sample_buffer = 0x7F;
uint8_t addr[3];
uint16_t dir_addr = sample * SAMPLE_DEF_SIZE + 1;
uint8_t* sc = (uint8_t*) &sample_count;
sc[3] = 0;
usi_init();
// Fetch directory information
spi_cs_enable();
spi_transfer(0x03);
spi_transfer(0);
spi_transfer(dir_addr >> 8);
spi_transfer(dir_addr & 0xFF);
addr[0] = spi_transfer(0);
addr[1] = spi_transfer(0);
addr[2] = spi_transfer(0);
sc[2] = spi_transfer(0);
sc[1] = spi_transfer(0);
sc[0] = spi_transfer(0);
spi_cs_disable();
// Begin SPI transfer
spi_cs_enable();
spi_transfer(0x03);
spi_transfer(addr[0]);
spi_transfer(addr[1]);
spi_transfer(addr[2]);
if (channels == 0) {
// Wait for the amp to power up
_delay_ms(200);
}
channels = 1;
// Clear OC0A on compare match, Fast PWM
TCCR0A = 0b10000011;
// Reset counter
TCNT0 = 0;
// Enable overflow interrupt
TIMSK0 = _BV(TOIE0);
// Match at sample
OCR0A = sample_buffer;
// Enable timer - Fast PWM, no prescaler
TCCR0B = 0b00000001;
// Switch to idle sleep because we need the timer/counter to function
set_sleep_mode(SLEEP_MODE_IDLE);
}
void end_playback() {
// Disable overflow interrupt
TIMSK0 = 0;
// disable timer
TCCR0B = 0;
// End SPI transfer
spi_cs_disable();
// Return USI pins to Hi-Z
usi_deinit();
// Return to power-off sleep
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
channels = 0;
button_debounce = 0;
}
uint8_t adc_read() {
// VCC reference, select PA0
ADMUX = 0;
// Enable, Start conversion, Prescaler divisor 16
ADCSRA = _BV(ADEN) | _BV(ADSC) | 0b100;
// Wait for conversion to finish
while (!(ADCSRA & _BV(ADIF)));
// Clear interrupt flag
ADCSRA = _BV(ADIF);
// Both registers must be read
uint8_t tmp = ADCL;
ADCH;
// Shut off the ADC
ADCSRA = 0;
return tmp;
}
uint16_t lfsr_state;
void lfsr_init() {
lfsr_state = adc_read();
}
uint8_t lfsr_read() {
lfsr_state ^= lfsr_state >> 7;
lfsr_state ^= lfsr_state << 9;
lfsr_state ^= lfsr_state >> 13;
return lfsr_state & 0xFF;
}
void main() {
// Set PB2 to output
DDRB = 0b00000100;
// Set pull-up on PB0 (button)
PORTB = 0b00000001;
// Enable interrupts for pin changes on PORTB
GIMSK = _BV(PCIE1);
// Enable pin change interrupt on PB0
PCMSK1 = _BV(PCINT8);
button_state = 0b00000001;
button_debounce = 0;
channels = 0;
flags.tick = flags.play = 0;
lfsr_init();
// Grab the number of samples from the first byte of flash
usi_init();
spi_cs_enable();
spi_transfer(0x03);
spi_transfer(0x00);
spi_transfer(0x00);
spi_transfer(0x00);
n_samples = spi_transfer(0);
spi_cs_disable();
usi_deinit();
// Enable interrupts
sei();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while (1) {
sleep_mode();
if (button_debounce > 0) {
button_debounce--;
}
if (flags.tick) {
// fetch the next sample
sample_buffer = spi_transfer(0x00);
if (--sample_count == 0) {
end_playback();
}
flags.tick = 0;
}
if (flags.play) {
spi_cs_disable();
uint8_t n = lfsr_read() % n_samples;
begin_playback(n);
flags.play = 0;
}
}
}
ISR(TIM0_OVF_vect) {
OCR0A = sample_buffer;
flags.tick = 1;
}
ISR(PCINT1_vect) {
uint8_t current_state = PINB & 0b00000001;
if (current_state == button_state) {
return;
}
uint8_t pressed = button_state & ~current_state;
if (button_debounce == 0 && (pressed & _BV(PB0))) {
flags.play = 1;
button_debounce = 4000;
}
button_state = current_state;
}