This shows you the differences between two versions of the page.
|
pm:prj2026:ciprian.popescu0411:remus.berevoescu [2026/05/27 11:53] remus.berevoescu |
pm:prj2026:ciprian.popescu0411:remus.berevoescu [2026/05/27 11:53] (current) remus.berevoescu |
||
|---|---|---|---|
| Line 236: | Line 236: | ||
| * `README.md` | * `README.md` | ||
| * `CHANGELOG.txt` | * `CHANGELOG.txt` | ||
| - | |||
| - | </note> | ||
| - | ===== Journal ====== | ||
| - | |||
| - | #define F_CPU 16000000UL | ||
| - | |||
| - | #include <avr/io.h> | ||
| - | #include <avr/interrupt.h> | ||
| - | #include <avr/pgmspace.h> | ||
| - | #include <util/atomic.h> | ||
| - | #include <util/delay.h> | ||
| - | #include <stdint.h> | ||
| - | |||
| - | // ===================================================== | ||
| - | // PIN DEFINITIONS | ||
| - | // ===================================================== | ||
| - | |||
| - | #define STATUS_LED PB5 | ||
| - | |||
| - | #define BUTTON_EFFECT PD2 // INT0 | ||
| - | #define BUTTON_MODE PD3 // INT1 | ||
| - | |||
| - | #define PWM_OUT PD5 // OC0B | ||
| - | |||
| - | #define ADC_AUDIO 0 // PC0 / ADC0 | ||
| - | #define ADC_GAIN 1 // PC1 / ADC1 | ||
| - | #define ADC_TREMOLO 2 // PC2 / ADC2 | ||
| - | |||
| - | // OLED pins pe PORTB | ||
| - | #define OLED_SCL PB0 | ||
| - | #define OLED_SDA PB1 | ||
| - | #define OLED_RST PB2 | ||
| - | #define OLED_DC PB4 | ||
| - | |||
| - | #define OLED_PORT PORTB | ||
| - | #define OLED_DDR DDRB | ||
| - | |||
| - | #define OLED_WIDTH 128 | ||
| - | #define OLED_PAGES 8 // 8 pentru 128x64; daca e 128x32 pune 4 | ||
| - | |||
| - | // ===================================================== | ||
| - | // AUDIO TUNING | ||
| - | // ===================================================== | ||
| - | // | ||
| - | // Gain-urile sunt in Q4: | ||
| - | // 16 = 1x | ||
| - | // 32 = 2x | ||
| - | // 64 = 4x | ||
| - | // 80 = 5x | ||
| - | // 128 = 8x | ||
| - | // | ||
| - | // Pentru ca ai deja zgomot mare, distortion-ul este intentionat subtil. | ||
| - | // | ||
| - | |||
| - | #define CLEAN_GAIN_Q4 64 // clean ramane 4x | ||
| - | |||
| - | #define TREM_GAIN_Q4 80 // tremolo putin mai tare | ||
| - | |||
| - | #define DIST_GAIN_MIN_Q4 96 // 6x | ||
| - | #define DIST_GAIN_MAX_Q4 180 // 11.25x, distortion mai clar | ||
| - | |||
| - | #define DIST_CLIP_MAX_8BIT 46 // clipping mai vizibil | ||
| - | #define DIST_CLIP_MIN_8BIT 18 // clipping mai agresiv la pot mare | ||
| - | |||
| - | #define NOISE_GATE_10BIT 4 | ||
| - | #define AUDIO_SMOOTH_SHIFT 0 // mai direct, mai puternic, dar si mai zgomotos | ||
| - | // ===================================================== | ||
| - | // EFFECT STATE | ||
| - | // ===================================================== | ||
| - | |||
| - | #define EFFECT_DIST 0 | ||
| - | #define EFFECT_TREM 1 | ||
| - | |||
| - | volatile uint8_t effect_enabled = 0; | ||
| - | volatile uint8_t selected_effect = EFFECT_DIST; | ||
| - | |||
| - | volatile uint8_t effect_button_event = 0; | ||
| - | volatile uint8_t mode_button_event = 0; | ||
| - | |||
| - | volatile uint32_t g_millis = 0; | ||
| - | |||
| - | volatile uint32_t last_effect_interrupt_ms = 0; | ||
| - | volatile uint32_t last_mode_interrupt_ms = 0; | ||
| - | |||
| - | #define DEBOUNCE_MS 50 | ||
| - | |||
| - | // valori citite din potentiometre, 0..255 | ||
| - | volatile uint8_t pot_gain_raw = 128; | ||
| - | volatile uint8_t pot_tremolo_raw = 128; | ||
| - | |||
| - | // parametri audio derivati din potentiometre | ||
| - | volatile uint8_t g_dist_gain_q4 = DIST_GAIN_MIN_Q4; | ||
| - | volatile uint8_t g_dist_clip = DIST_CLIP_MAX_8BIT; | ||
| - | volatile uint8_t g_trem_step = 8; | ||
| - | |||
| - | // pentru ADC multiplexing | ||
| - | volatile uint8_t current_adc_channel = ADC_AUDIO; | ||
| - | volatile uint8_t adc_skip_sample = 0; | ||
| - | volatile uint16_t audio_sample_counter = 0; | ||
| - | |||
| - | // ===================================================== | ||
| - | // SMALL UTILS | ||
| - | // ===================================================== | ||
| - | |||
| - | static inline uint16_t abs16(int16_t x) | ||
| - | { | ||
| - | return (x < 0) ? (uint16_t)(-x) : (uint16_t)x; | ||
| - | } | ||
| - | |||
| - | static inline int16_t clamp_i16(int16_t x, int16_t min_value, int16_t max_value) | ||
| - | { | ||
| - | if (x < min_value) | ||
| - | { | ||
| - | return min_value; | ||
| - | } | ||
| - | |||
| - | if (x > max_value) | ||
| - | { | ||
| - | return max_value; | ||
| - | } | ||
| - | |||
| - | return x; | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // TIMER1: 1 ms system clock | ||
| - | // ===================================================== | ||
| - | |||
| - | void timer1_init_1ms(void) | ||
| - | { | ||
| - | TCCR1A = 0; | ||
| - | TCCR1B = 0; | ||
| - | |||
| - | // 16 MHz / 64 = 250 kHz | ||
| - | // 250 ticks = 1 ms | ||
| - | OCR1A = 249; | ||
| - | |||
| - | TCCR1B |= (1 << WGM12); // CTC mode | ||
| - | TCCR1B |= (1 << CS11) | (1 << CS10); // prescaler 64 | ||
| - | |||
| - | TIMSK1 |= (1 << OCIE1A); | ||
| - | } | ||
| - | |||
| - | ISR(TIMER1_COMPA_vect) | ||
| - | { | ||
| - | g_millis++; | ||
| - | } | ||
| - | |||
| - | uint32_t millis_get(void) | ||
| - | { | ||
| - | uint32_t value; | ||
| - | |||
| - | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) | ||
| - | { | ||
| - | value = g_millis; | ||
| - | } | ||
| - | |||
| - | return value; | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // BUTTONS: INT0 / INT1 | ||
| - | // ===================================================== | ||
| - | |||
| - | void buttons_init(void) | ||
| - | { | ||
| - | // PD2, PD3 input | ||
| - | DDRD &= ~(1 << BUTTON_EFFECT); | ||
| - | DDRD &= ~(1 << BUTTON_MODE); | ||
| - | |||
| - | // internal pull-up | ||
| - | PORTD |= (1 << BUTTON_EFFECT); | ||
| - | PORTD |= (1 << BUTTON_MODE); | ||
| - | |||
| - | // INT0 falling edge | ||
| - | EICRA |= (1 << ISC01); | ||
| - | EICRA &= ~(1 << ISC00); | ||
| - | |||
| - | // INT1 falling edge | ||
| - | EICRA |= (1 << ISC11); | ||
| - | EICRA &= ~(1 << ISC10); | ||
| - | |||
| - | // clear old flags | ||
| - | EIFR |= (1 << INTF0) | (1 << INTF1); | ||
| - | |||
| - | // enable INT0, INT1 | ||
| - | EIMSK |= (1 << INT0) | (1 << INT1); | ||
| - | } | ||
| - | |||
| - | ISR(INT0_vect) | ||
| - | { | ||
| - | uint32_t now = g_millis; | ||
| - | |||
| - | if ((uint32_t)(now - last_effect_interrupt_ms) >= DEBOUNCE_MS) | ||
| - | { | ||
| - | effect_button_event = 1; | ||
| - | last_effect_interrupt_ms = now; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | ISR(INT1_vect) | ||
| - | { | ||
| - | uint32_t now = g_millis; | ||
| - | |||
| - | if ((uint32_t)(now - last_mode_interrupt_ms) >= DEBOUNCE_MS) | ||
| - | { | ||
| - | mode_button_event = 1; | ||
| - | last_mode_interrupt_ms = now; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // PWM OUTPUT: PD5 / OC0B | ||
| - | // ===================================================== | ||
| - | |||
| - | void pwm_init_pd5(void) | ||
| - | { | ||
| - | DDRD |= (1 << PWM_OUT); | ||
| - | |||
| - | // Timer0 Fast PWM, 8-bit | ||
| - | // OC0B non-inverting pe PD5 | ||
| - | // PWM freq = 16 MHz / 256 = 62.5 kHz | ||
| - | TCCR0A = (1 << COM0B1) | (1 << WGM01) | (1 << WGM00); | ||
| - | TCCR0B = (1 << CS00); | ||
| - | |||
| - | OCR0B = 128; | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // ADC: audio + potentiometers | ||
| - | // ===================================================== | ||
| - | |||
| - | void adc_select_channel(uint8_t channel) | ||
| - | { | ||
| - | ADMUX = (1 << REFS0) | (channel & 0x0F); | ||
| - | } | ||
| - | |||
| - | void adc_init(void) | ||
| - | { | ||
| - | current_adc_channel = ADC_AUDIO; | ||
| - | |||
| - | // AVcc reference, ADC0 first | ||
| - | adc_select_channel(ADC_AUDIO); | ||
| - | |||
| - | // ADC enable, ADC interrupt, auto trigger, prescaler 64 | ||
| - | // ADC clock = 16 MHz / 64 = 250 kHz | ||
| - | ADCSRA = (1 << ADEN) | | ||
| - | (1 << ADIE) | | ||
| - | (1 << ADATE) | | ||
| - | (1 << ADPS2) | | ||
| - | (1 << ADPS1); | ||
| - | |||
| - | // free running mode | ||
| - | ADCSRB = 0; | ||
| - | |||
| - | // disable digital input buffers on ADC0, ADC1, ADC2 | ||
| - | DIDR0 = (1 << ADC0D) | (1 << ADC1D) | (1 << ADC2D); | ||
| - | |||
| - | // start conversions | ||
| - | ADCSRA |= (1 << ADSC); | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // AUDIO PROCESSING | ||
| - | // ===================================================== | ||
| - | |||
| - | void audio_process_sample(uint16_t raw10) | ||
| - | { | ||
| - | static int32_t dc_q8 = ((int32_t)512 << 8); | ||
| - | static int16_t smoothed_pwm = 128; | ||
| - | static uint16_t trem_phase = 0; | ||
| - | |||
| - | // DC removal foarte lent. | ||
| - | // Semnalul este centrat in jur de 512, dar bias-ul real poate varia. | ||
| - | dc_q8 += ((((int32_t)raw10 << 8) - dc_q8) >> 10); | ||
| - | |||
| - | int16_t centered10 = (int16_t)((((int32_t)raw10 << 8) - dc_q8) >> 8); | ||
| - | |||
| - | // noise gate foarte bland | ||
| - | uint16_t ax = abs16(centered10); | ||
| - | |||
| - | if (ax < NOISE_GATE_10BIT) | ||
| - | { | ||
| - | centered10 = 0; | ||
| - | } | ||
| - | else if (ax < (NOISE_GATE_10BIT * 2)) | ||
| - | { | ||
| - | centered10 /= 2; | ||
| - | } | ||
| - | |||
| - | uint8_t local_enabled = effect_enabled; | ||
| - | uint8_t local_effect = selected_effect; | ||
| - | |||
| - | int16_t y = 0; | ||
| - | |||
| - | if (!local_enabled) | ||
| - | { | ||
| - | // Software bypass / clean passthrough | ||
| - | y = ((int32_t)centered10 * CLEAN_GAIN_Q4) >> 6; | ||
| - | } | ||
| - | else if (local_effect == EFFECT_DIST) | ||
| - | { | ||
| - | // Distortion subtil: | ||
| - | // 1. boost moderat | ||
| - | // 2. soft clipping simetric | ||
| - | uint8_t gain_q4 = g_dist_gain_q4; | ||
| - | uint8_t clip_value = g_dist_clip; | ||
| - | |||
| - | y = ((int32_t)centered10 * gain_q4) >> 6; | ||
| - | |||
| - | if (y > clip_value) | ||
| - | { | ||
| - | y = clip_value; | ||
| - | } | ||
| - | else if (y < -(int16_t)clip_value) | ||
| - | { | ||
| - | y = -(int16_t)clip_value; | ||
| - | } | ||
| - | |||
| - | // limit final mai larg, dar cu clipping clar | ||
| - | y = clamp_i16(y, -125, 125); | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | // Tremolo: | ||
| - | // clean signal * LFO triunghiular | ||
| - | y = ((int32_t)centered10 * TREM_GAIN_Q4) >> 6; | ||
| - | |||
| - | trem_phase += g_trem_step; | ||
| - | |||
| - | uint8_t tri; | ||
| - | |||
| - | if (trem_phase & 0x8000) | ||
| - | { | ||
| - | tri = (uint8_t)((65535 - trem_phase) >> 8); // 127..0 | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | tri = (uint8_t)(trem_phase >> 8); // 0..127 | ||
| - | } | ||
| - | |||
| - | // tremolo mai pronuntat: volum intre ~15% si ~100% | ||
| - | uint8_t modulation = 40 + ((uint16_t)tri * 215) / 127; | ||
| - | |||
| - | y = ((int32_t)y * modulation) >> 8; | ||
| - | } | ||
| - | |||
| - | y = clamp_i16(y, -120, 120); | ||
| - | |||
| - | int16_t target_pwm = 128 + y; | ||
| - | |||
| - | if (target_pwm < 0) | ||
| - | { | ||
| - | target_pwm = 0; | ||
| - | } | ||
| - | else if (target_pwm > 255) | ||
| - | { | ||
| - | target_pwm = 255; | ||
| - | } | ||
| - | |||
| - | #if AUDIO_SMOOTH_SHIFT > 0 | ||
| - | smoothed_pwm += ((target_pwm - smoothed_pwm) >> AUDIO_SMOOTH_SHIFT); | ||
| - | OCR0B = (uint8_t)smoothed_pwm; | ||
| - | #else | ||
| - | OCR0B = (uint8_t)target_pwm; | ||
| - | #endif | ||
| - | } | ||
| - | |||
| - | ISR(ADC_vect) | ||
| - | { | ||
| - | uint16_t value = ADC; | ||
| - | |||
| - | // Dupa schimbarea canalului ADC, aruncam prima mostra. | ||
| - | if (adc_skip_sample) | ||
| - | { | ||
| - | adc_skip_sample = 0; | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | if (current_adc_channel == ADC_AUDIO) | ||
| - | { | ||
| - | audio_process_sample(value); | ||
| - | |||
| - | audio_sample_counter++; | ||
| - | |||
| - | // Din cand in cand citim potentiometrele. | ||
| - | // Nu le citim continuu, ca sa pastram audio-ul cat mai stabil. | ||
| - | if (audio_sample_counter >= 256) | ||
| - | { | ||
| - | audio_sample_counter = 0; | ||
| - | |||
| - | current_adc_channel = ADC_GAIN; | ||
| - | adc_select_channel(ADC_GAIN); | ||
| - | adc_skip_sample = 1; | ||
| - | } | ||
| - | } | ||
| - | else if (current_adc_channel == ADC_GAIN) | ||
| - | { | ||
| - | pot_gain_raw = (uint8_t)(value >> 2); | ||
| - | |||
| - | current_adc_channel = ADC_TREMOLO; | ||
| - | adc_select_channel(ADC_TREMOLO); | ||
| - | adc_skip_sample = 1; | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | pot_tremolo_raw = (uint8_t)(value >> 2); | ||
| - | |||
| - | current_adc_channel = ADC_AUDIO; | ||
| - | adc_select_channel(ADC_AUDIO); | ||
| - | adc_skip_sample = 1; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // OLED LOW LEVEL | ||
| - | // ===================================================== | ||
| - | |||
| - | static void oled_pin_high(uint8_t pin) | ||
| - | { | ||
| - | OLED_PORT |= (1 << pin); | ||
| - | } | ||
| - | |||
| - | static void oled_pin_low(uint8_t pin) | ||
| - | { | ||
| - | OLED_PORT &= ~(1 << pin); | ||
| - | } | ||
| - | |||
| - | void oled_spi_write(uint8_t data) | ||
| - | { | ||
| - | for (uint8_t i = 0; i < 8; i++) | ||
| - | { | ||
| - | if (data & 0x80) | ||
| - | { | ||
| - | oled_pin_high(OLED_SDA); | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | oled_pin_low(OLED_SDA); | ||
| - | } | ||
| - | |||
| - | oled_pin_high(OLED_SCL); | ||
| - | oled_pin_low(OLED_SCL); | ||
| - | |||
| - | data <<= 1; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | void oled_command(uint8_t cmd) | ||
| - | { | ||
| - | oled_pin_low(OLED_DC); | ||
| - | oled_spi_write(cmd); | ||
| - | } | ||
| - | |||
| - | void oled_data(uint8_t data) | ||
| - | { | ||
| - | oled_pin_high(OLED_DC); | ||
| - | oled_spi_write(data); | ||
| - | } | ||
| - | |||
| - | void oled_reset(void) | ||
| - | { | ||
| - | oled_pin_low(OLED_RST); | ||
| - | _delay_ms(50); | ||
| - | |||
| - | oled_pin_high(OLED_RST); | ||
| - | _delay_ms(50); | ||
| - | } | ||
| - | |||
| - | void oled_set_cursor(uint8_t page, uint8_t column) | ||
| - | { | ||
| - | oled_command(0xB0 + page); | ||
| - | oled_command(0x00 + (column & 0x0F)); | ||
| - | oled_command(0x10 + ((column >> 4) & 0x0F)); | ||
| - | } | ||
| - | |||
| - | void oled_clear(void) | ||
| - | { | ||
| - | for (uint8_t page = 0; page < OLED_PAGES; page++) | ||
| - | { | ||
| - | oled_set_cursor(page, 0); | ||
| - | |||
| - | for (uint8_t col = 0; col < OLED_WIDTH; col++) | ||
| - | { | ||
| - | oled_data(0x00); | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| - | void oled_init(void) | ||
| - | { | ||
| - | OLED_DDR |= (1 << OLED_SCL) | | ||
| - | (1 << OLED_SDA) | | ||
| - | (1 << OLED_RST) | | ||
| - | (1 << OLED_DC); | ||
| - | |||
| - | oled_pin_low(OLED_SCL); | ||
| - | oled_pin_low(OLED_SDA); | ||
| - | |||
| - | oled_reset(); | ||
| - | |||
| - | oled_command(0xAE); // display OFF | ||
| - | |||
| - | oled_command(0xD5); | ||
| - | oled_command(0x80); | ||
| - | |||
| - | oled_command(0xA8); | ||
| - | oled_command(0x3F); // 128x64 | ||
| - | |||
| - | oled_command(0xD3); | ||
| - | oled_command(0x00); | ||
| - | |||
| - | oled_command(0x40); | ||
| - | |||
| - | oled_command(0x8D); | ||
| - | oled_command(0x14); | ||
| - | |||
| - | oled_command(0x20); | ||
| - | oled_command(0x00); | ||
| - | |||
| - | oled_command(0xA1); | ||
| - | oled_command(0xC8); | ||
| - | |||
| - | oled_command(0xDA); | ||
| - | oled_command(0x12); | ||
| - | |||
| - | oled_command(0x81); | ||
| - | oled_command(0xCF); | ||
| - | |||
| - | oled_command(0xD9); | ||
| - | oled_command(0xF1); | ||
| - | |||
| - | oled_command(0xDB); | ||
| - | oled_command(0x40); | ||
| - | |||
| - | oled_command(0xA4); | ||
| - | oled_command(0xA6); | ||
| - | oled_command(0xAF); | ||
| - | |||
| - | oled_clear(); | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // OLED FONT MINIMAL 5x7 | ||
| - | // ===================================================== | ||
| - | |||
| - | const uint8_t FONT_SPACE[5] PROGMEM = {0x00,0x00,0x00,0x00,0x00}; | ||
| - | const uint8_t FONT_COLON[5] PROGMEM = {0x00,0x36,0x36,0x00,0x00}; | ||
| - | |||
| - | const uint8_t FONT_A[5] PROGMEM = {0x7E,0x11,0x11,0x11,0x7E}; | ||
| - | const uint8_t FONT_C[5] PROGMEM = {0x3E,0x41,0x41,0x41,0x22}; | ||
| - | const uint8_t FONT_D[5] PROGMEM = {0x7F,0x41,0x41,0x22,0x1C}; | ||
| - | const uint8_t FONT_E[5] PROGMEM = {0x7F,0x49,0x49,0x49,0x41}; | ||
| - | const uint8_t FONT_F[5] PROGMEM = {0x7F,0x09,0x09,0x09,0x01}; | ||
| - | const uint8_t FONT_I[5] PROGMEM = {0x00,0x41,0x7F,0x41,0x00}; | ||
| - | const uint8_t FONT_L[5] PROGMEM = {0x7F,0x40,0x40,0x40,0x40}; | ||
| - | const uint8_t FONT_M[5] PROGMEM = {0x7F,0x02,0x0C,0x02,0x7F}; | ||
| - | const uint8_t FONT_N[5] PROGMEM = {0x7F,0x04,0x08,0x10,0x7F}; | ||
| - | const uint8_t FONT_O[5] PROGMEM = {0x3E,0x41,0x41,0x41,0x3E}; | ||
| - | const uint8_t FONT_P[5] PROGMEM = {0x7F,0x09,0x09,0x09,0x06}; | ||
| - | const uint8_t FONT_R[5] PROGMEM = {0x7F,0x09,0x19,0x29,0x46}; | ||
| - | const uint8_t FONT_S[5] PROGMEM = {0x46,0x49,0x49,0x49,0x31}; | ||
| - | const uint8_t FONT_T[5] PROGMEM = {0x01,0x01,0x7F,0x01,0x01}; | ||
| - | const uint8_t FONT_X[5] PROGMEM = {0x63,0x14,0x08,0x14,0x63}; | ||
| - | |||
| - | const uint8_t* get_char_bitmap(char c) | ||
| - | { | ||
| - | switch (c) | ||
| - | { | ||
| - | case 'A': return FONT_A; | ||
| - | case 'C': return FONT_C; | ||
| - | case 'D': return FONT_D; | ||
| - | case 'E': return FONT_E; | ||
| - | case 'F': return FONT_F; | ||
| - | case 'I': return FONT_I; | ||
| - | case 'L': return FONT_L; | ||
| - | case 'M': return FONT_M; | ||
| - | case 'N': return FONT_N; | ||
| - | case 'O': return FONT_O; | ||
| - | case 'P': return FONT_P; | ||
| - | case 'R': return FONT_R; | ||
| - | case 'S': return FONT_S; | ||
| - | case 'T': return FONT_T; | ||
| - | case 'X': return FONT_X; | ||
| - | case ':': return FONT_COLON; | ||
| - | case ' ': return FONT_SPACE; | ||
| - | default: return FONT_SPACE; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | void oled_print_char(char c) | ||
| - | { | ||
| - | const uint8_t* bitmap = get_char_bitmap(c); | ||
| - | |||
| - | for (uint8_t i = 0; i < 5; i++) | ||
| - | { | ||
| - | oled_data(pgm_read_byte(&bitmap[i])); | ||
| - | } | ||
| - | |||
| - | oled_data(0x00); | ||
| - | } | ||
| - | |||
| - | void oled_print(const char* text) | ||
| - | { | ||
| - | while (*text) | ||
| - | { | ||
| - | oled_print_char(*text); | ||
| - | text++; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | void oled_print_at(uint8_t page, uint8_t column, const char* text) | ||
| - | { | ||
| - | oled_set_cursor(page, column); | ||
| - | oled_print(text); | ||
| - | } | ||
| - | |||
| - | void oled_show_status(uint8_t pedal_on, uint8_t effect) | ||
| - | { | ||
| - | oled_clear(); | ||
| - | |||
| - | oled_print_at(1, 0, "PEDAL:"); | ||
| - | |||
| - | if (pedal_on) | ||
| - | { | ||
| - | oled_print("ON"); | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | oled_print("OFF"); | ||
| - | } | ||
| - | |||
| - | oled_print_at(3, 0, "FX:"); | ||
| - | |||
| - | if (effect == EFFECT_DIST) | ||
| - | { | ||
| - | oled_print("DIST"); | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | oled_print("TREM"); | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // CONTROL PARAMETER UPDATE | ||
| - | // ===================================================== | ||
| - | |||
| - | void update_audio_parameters_from_pots(void) | ||
| - | { | ||
| - | uint8_t gain = pot_gain_raw; | ||
| - | uint8_t trem = pot_tremolo_raw; | ||
| - | |||
| - | // distortion gain subtil | ||
| - | uint8_t dist_gain = DIST_GAIN_MIN_Q4 + | ||
| - | (uint8_t)(((uint16_t)gain * (DIST_GAIN_MAX_Q4 - DIST_GAIN_MIN_Q4)) / 255); | ||
| - | |||
| - | // la gain mare, clip-ul scade putin | ||
| - | uint8_t dist_clip = DIST_CLIP_MAX_8BIT - | ||
| - | (uint8_t)(((uint16_t)gain * (DIST_CLIP_MAX_8BIT - DIST_CLIP_MIN_8BIT)) / 255); | ||
| - | |||
| - | // tremolo speed: aproximativ lent -> rapid | ||
| - | uint8_t trem_step = 2 + (uint8_t)(((uint16_t)trem * 60) / 255); | ||
| - | |||
| - | g_dist_gain_q4 = dist_gain; | ||
| - | g_dist_clip = dist_clip; | ||
| - | g_trem_step = trem_step; | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // STATUS LED | ||
| - | // ===================================================== | ||
| - | |||
| - | void status_led_update(void) | ||
| - | { | ||
| - | static uint32_t last_toggle_ms = 0; | ||
| - | static uint8_t led_state = 0; | ||
| - | |||
| - | uint32_t now = millis_get(); | ||
| - | |||
| - | if (!effect_enabled) | ||
| - | { | ||
| - | PORTB &= ~(1 << STATUS_LED); | ||
| - | led_state = 0; | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | if (selected_effect == EFFECT_DIST) | ||
| - | { | ||
| - | PORTB |= (1 << STATUS_LED); | ||
| - | return; | ||
| - | } | ||
| - | |||
| - | // Tremolo mode: blink status LED | ||
| - | if ((uint32_t)(now - last_toggle_ms) >= 250) | ||
| - | { | ||
| - | last_toggle_ms = now; | ||
| - | led_state = !led_state; | ||
| - | |||
| - | if (led_state) | ||
| - | { | ||
| - | PORTB |= (1 << STATUS_LED); | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | PORTB &= ~(1 << STATUS_LED); | ||
| - | } | ||
| - | } | ||
| - | } | ||
| - | |||
| - | // ===================================================== | ||
| - | // MAIN | ||
| - | // ===================================================== | ||
| - | |||
| - | int main(void) | ||
| - | { | ||
| - | // status LED | ||
| - | DDRB |= (1 << STATUS_LED); | ||
| - | |||
| - | // Init output and controls | ||
| - | pwm_init_pd5(); | ||
| - | |||
| - | // OLED init inainte de audio interrupts | ||
| - | oled_init(); | ||
| - | oled_show_status(effect_enabled, selected_effect); | ||
| - | |||
| - | timer1_init_1ms(); | ||
| - | buttons_init(); | ||
| - | adc_init(); | ||
| - | |||
| - | sei(); | ||
| - | |||
| - | uint32_t last_params_update_ms = 0; | ||
| - | uint8_t screen_update_needed = 1; | ||
| - | |||
| - | while (1) | ||
| - | { | ||
| - | uint8_t local_effect_event = 0; | ||
| - | uint8_t local_mode_event = 0; | ||
| - | |||
| - | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) | ||
| - | { | ||
| - | if (effect_button_event) | ||
| - | { | ||
| - | local_effect_event = 1; | ||
| - | effect_button_event = 0; | ||
| - | } | ||
| - | |||
| - | if (mode_button_event) | ||
| - | { | ||
| - | local_mode_event = 1; | ||
| - | mode_button_event = 0; | ||
| - | } | ||
| - | } | ||
| - | |||
| - | if (local_effect_event) | ||
| - | { | ||
| - | effect_enabled = !effect_enabled; | ||
| - | screen_update_needed = 1; | ||
| - | } | ||
| - | |||
| - | if (local_mode_event) | ||
| - | { | ||
| - | if (selected_effect == EFFECT_DIST) | ||
| - | { | ||
| - | selected_effect = EFFECT_TREM; | ||
| - | } | ||
| - | else | ||
| - | { | ||
| - | selected_effect = EFFECT_DIST; | ||
| - | } | ||
| - | |||
| - | screen_update_needed = 1; | ||
| - | } | ||
| - | |||
| - | uint32_t now = millis_get(); | ||
| - | |||
| - | if ((uint32_t)(now - last_params_update_ms) >= 20) | ||
| - | { | ||
| - | last_params_update_ms = now; | ||
| - | update_audio_parameters_from_pots(); | ||
| - | } | ||
| - | |||
| - | if (screen_update_needed) | ||
| - | { | ||
| - | screen_update_needed = 0; | ||
| - | oled_show_status(effect_enabled, selected_effect); | ||
| - | } | ||
| - | |||
| - | status_led_update(); | ||
| - | } | ||
| - | } | ||
| ===== Bibliography/Resources ====== | ===== Bibliography/Resources ====== | ||
| <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | ||