Skip to content

Instantly share code, notes, and snippets.

@lemilonkh
Created October 6, 2011 19:09
Show Gist options
  • Save lemilonkh/1268331 to your computer and use it in GitHub Desktop.
Save lemilonkh/1268331 to your computer and use it in GitHub Desktop.
Hexamin - An air controlled synthesizer
// hexamin.c
// (c) 2011 by Milan Gruner
// designed for ATmega8
#define F_CPU 8000000L
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// helpers
#define _(PIN) (1 << PIN)
#define bool uint8_t
#define true 1
#define false 0
#define u8 uint8_t
#define u16 uint16_t
int map(int x, int in_min, int in_max, int out_min, int out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// pinout
#define AUDIO _(PB0) // speaker / phono output
#define SENSORS PORTC // the whole analog port is used by 6 IR distance sensors
#define LED _(PD0) // tempo display
// constants
#define INACTIVE 50 // the ADC value that means >>deactivate feature<<
#define BASE_THRESH 2
#define COUNT 4 // how many ADC measurements will be made for one value
#define MIN_VAL 200 // ADC value
#define MAX_VAL 900 //ADC value
#define PITCH 0 // Control the base tone
#define DELAY 1 // Tone Delay
#define STEP 2 // Main Loop Delay
#define DUTY_CYCLE 3 // Uptime divider
#define DRUMS 4 // Beat generation
#define RECORD 5 // Select the record channel
// vars
int step, delay, half_delay;
bool output_on = true;
u16 base;
u16 data[6] = {0,0,0,0,0,0}; // measured values
u16 val[6] = {0,0,0,0,0,0}; // computed values
bool activated[6] = {0,0,0,0,0,0}; // is the feature activated?
const int range[6][2] = { // min and max values
{300,4000},// Tone (Hz)
{0,500}, // Delay (ms)
{50,200},// Step => Main Loop Delay (ms)
{1,10}, // Duty Cycle Divider
{0,7}, // Drums
{0,9} // Record Channels
};
// functions
u16 adc_read(u8 channel) {
ADMUX = channel; // select the channel
ADCSRA |= _(ADSC); // get the *next* conversion started
while(ADCSRA & _(ADSC)) { } // wait for the ADC to finish measuring
// read the value
u16 result = ADCL;
u16 temp = ADCH;
result += (temp << 8);
return result;
}
u16 adc_average(u8 channel, u16 count) {
u16 val = 0;
for(int i = 0; i <= count; i++) {
val += adc_read(channel);
}
return val / count;
}
// main function
int main(void) {
// init hardware
DDRC = LED;
DDRB = AUDIO;
// ADC init
ADMUX = 0; //external reference (5v), single ended input ADC0
ADCSRA = _(ADEN) | _(ADPS2) | _(ADPS1) | _(ADPS0); // enable ADC; clock prescaler 1/128
ADCSRA |= _(ADSC); // warm the ADC up (do a first conversion)
// init audio timer
TCCR1A = 0b00100011; // set output OC1B at 0 and remove it at OCR1B
TCCR1B = 0b00011010; // Fast PWM with OCR1A as top, timer with CPU-CLK / 8 => 1MHz
// enable interrupts
sei();
// initial measurement to calibrate the sensors
base = (adc_average(0, COUNT) + adc_average(1, COUNT) + adc_average(2, COUNT)
+ adc_average(3, COUNT) + adc_average(4, COUNT) + adc_average(5, COUNT)) / 6;
for(;;) {
// gather the data
for(int i = 0; i < 6; i++) {
data[i] = adc_average(i, COUNT);
activated[i] = (data[i] <= INACTIVE);
}
// evaluate it
for(int i = 0; i < 6; i++) {
if(data[i] < (base- BASE_THRESH)) {
val[i] = map(data[i], MIN_VAL, MAX_VAL, range[i][0], range[i][1]);
if(data[i] == INACTIVE)
val[i] = 0;
if(val[i] > range[i][1])
val[i] = range[i][1];
}
}
// Pitch
if(output_on)
OCR1A = 1000000 / val[PITCH];
// Delay
if(activated[DELAY]) {
if(delay == 0) {
delay = val[DELAY] / step;
half_delay = delay / 2;
} else {
delay--;
}
output_on = delay < half_delay;
} else {
output_on = true;
}
// Step
step = val[STEP];
// Duty Cycle
if(activated[DUTY_CYCLE]) {
if(output_on)
OCR1B = OCR1A / val[DUTY_CYCLE];
} else {
if(output_on)
OCR1B = OCR1A / 2;
}
// Drums
// TODO yet to implement ;)
// Record
// TODO yet to implement ;)
// blink the LED in the beat
PORTD |= LED;
_delay_ms(step);
PORTD &= ~LED;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment