/* ========================================================================== * * Voluminizer 2.x * 3-channel analog audio volume controller * * Target: ATMega 48/88/168 at 20MHz * Author: Arvid Staub * * License: GPL v2 * * 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. * * Changelog: * 2011-01-29 added life sign, analog input offset compensation * 2011-01-28 fixed pin definitions, added T[CS->CLK] delay, ADC code added * 2011-01-27 first draft * * ========================================================================== */ #include #include #include #include #include #include #define F_CPU 20000000UL #include /* binary data word for 1:1 amplification (zero db) */ #define ZERODB 192 #define CYCLE_DELAY 1 #define RAMP_DT 1 /* LEDs */ #define LED_BTN1_PINNR 1 #define LED_BTN2_PINNR 0 #define LED_IR_PINNR 5 #define LED_SENSOR_PINNR 2 #define LED_BTN1_PORT PORTD #define LED_BTN2_PORT PORTD #define LED_IR_PORT PORTD #define LED_SENSOR_PORT PORTC #define LED_BTN1_DDR DDRD #define LED_BTN2_DDR DDRD #define LED_IR_DDR DDRD #define LED_SENSOR_DDR DDRC #define SELECT_DDR DDRB #define SELECT_PORT PORTB #define SELECT_PINNR 1 #define UNMUTE_DDR DDRD #define UNMUTE_PORT PORTD #define UNMUTE_PINNR 6 #define BTN1_DDR DDRD #define BTN1_PORT PORTD #define BTN1_PINNR 2 #define BTN1_PIN PIND #define BTN2_DDR DDRD #define BTN2_PORT PORTD #define BTN2_PINNR 3 #define BTN2_PIN PIND #define SPICLK_DDR DDRB #define SPICLK_PINNR 5 #define SPICLK_PORT PORTB #define SPIMOSI_DDR DDRB #define SPIMOSI_PINNR 3 #define SPIMOSI_PORT PORTB #define SPISS_DDR DDRB #define SPISS_PINNR 2 #define SPISS_PORT PORTB #define pin_make_output(x) do{ x##_DDR |= _BV( x##_PINNR ); }while(0) #define pin_set(x) do{ x##_PORT |= _BV( x##_PINNR ); }while(0) #define pin_clear(x) do{ x##_PORT &= ~_BV( x##_PINNR); }while(0) #define pin_value(x) (x##_PIN & _BV(x##_PINNR)) #define led_on(x) pin_set(x) #define led_off(x) pin_clear(x) static void spi_send(uint8_t data) { SPDR = data; /* wait until the current transmission (if any) is complete */ while(!(SPSR & _BV(SPIF))); } int main(void) { uint8_t vol, volreg; uint8_t adch; uint8_t cycle; float scalingfactor; uint8_t sensor_raw, sensor_offset, sensor_max; pin_make_output(LED_BTN1); pin_make_output(LED_BTN2); pin_make_output(LED_IR); pin_make_output(LED_SENSOR); pin_make_output(SPICLK); pin_make_output(SPIMOSI); /* this pin must be HIGH or OUTPUT to keep the SPI module in master mode * just do both to be sure */ pin_make_output(SPISS); pin_set(SPISS); pin_make_output(LED_SENSOR); pin_set(SELECT); pin_set(UNMUTE); pin_make_output(SELECT); pin_make_output(UNMUTE); /* activate pull-ups */ pin_set(BTN1); pin_set(BTN2); /* configure and enable SPI */ SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR1) | _BV(SPR0); /* configure analog input */ adch = 3; ADMUX = _BV(REFS0) | _BV(ADLAR) | adch; ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); DIDR0 = _BV(ADC3D); /* unused for now */ sleep_enable(); cycle = 0; sensor_offset = 0; sensor_max = 200; scalingfactor = 1.7; volreg = 127; for(;;) { /* led_on(LED_BTN1); _delay_ms(BLINKDELAY); led_off(LED_BTN1); _delay_ms(BLINKDELAY); led_on(LED_BTN2); _delay_ms(BLINKDELAY); led_off(LED_BTN2); _delay_ms(BLINKDELAY); led_on(LED_IR); _delay_ms(BLINKDELAY); led_off(LED_IR); _delay_ms(BLINKDELAY); led_on(LED_SENSOR); _delay_ms(BLINKDELAY); led_off(LED_SENSOR); _delay_ms(BLINKDELAY); */ //sleep_cpu(); _delay_ms(CYCLE_DELAY); /* life sign */ if( (cycle & _BV(7)) && 0 == (cycle & 0x78)) led_on(LED_IR); else led_off(LED_IR); /* perform an AD conversion to read the IR distance sensor */ ADCSRA |= _BV(ADSC); while(0 != (ADCSRA & _BV(ADSC))); sensor_raw = ADCH; if( sensor_raw > sensor_offset) led_on(LED_SENSOR); else led_off(LED_SENSOR); /* crunch, crunch! this should probably be more elaborate */ vol = 110 + scalingfactor*(sensor_raw-sensor_offset); /* don't allow amplification > 0db for now */ if(vol > ZERODB) vol = ZERODB; /* don't set the new volume immediately. use a ramp to avoid audible * clicks with large changes */ if(vol > volreg) volreg+=RAMP_DT; if(vol < volreg) volreg-=RAMP_DT; /* update the analog amplifier */ pin_clear(SELECT); _delay_us(3); spi_send(volreg); spi_send(volreg); spi_send(volreg); /* channel 3 is not connected */ spi_send(volreg); _delay_us(3); pin_set(SELECT); /* BTNx/LEDx placement is swapped in the layout. damn! */ if( 0 == pin_value(BTN2)) { if(sensor_raw > sensor_offset) sensor_offset = sensor_raw; //scalingfactor = (float)ZERODB / (sensor_max - sensor_offset); led_on(LED_BTN1); } while( 0 == pin_value(BTN1)) { sensor_offset = 0; led_on(LED_BTN2); } led_off(LED_BTN1); led_off(LED_BTN2); cycle++; } return 0; }