From 0d5288a4f35db153b0eaa925970aaedc59379277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 8 Jun 2017 12:19:43 +0200 Subject: [PATCH] adc improvements --- Makefile | 2 +- lib/adc.c | 46 ++++++++++++++++++++++++++++++++++--------- lib/adc.h | 20 +++++++++++++++---- main.c | 58 +++++++++++++++++++++++++++++++++++-------------------- pinout.h | 2 +- 5 files changed, 92 insertions(+), 36 deletions(-) diff --git a/Makefile b/Makefile index b79c0ff..c785a9e 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ CFLAGS += -ffunction-sections -fdata-sections -Os LDFLAGS = -Wl,--gc-sections -Wl,--relax -lm #LD_FLAGS += -Wl,-u,vfprintf -lprintf_flt -lm ## for floating-point printf -#LD_FLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf +LD_FLAGS += -Wl,-u,vfprintf -lprintf_min ## for smaller printf ############################################# diff --git a/lib/adc.c b/lib/adc.c index d2b1da7..8202ff6 100644 --- a/lib/adc.c +++ b/lib/adc.c @@ -1,5 +1,6 @@ #include #include +#include #include "calc.h" #include "adc.h" @@ -8,7 +9,7 @@ void adc_init(enum ADC_Prescaller presc) { ADCSRA |= presc; // 128 prescaler -> 125 kHz - ADMUX |= _BV(REFS0); // Voltage reference + ADMUX |= _BV(REFS0); // Voltage reference sbi(ADCSRA, ADEN); // Enable ADC } @@ -24,26 +25,53 @@ void adc_enable(void) sbi(ADCSRA, ADEN); } -static void adc_convert(uint8_t channel) +/** Start a new conversion */ +void adc_start_conversion(uint8_t channel) { set_low_nibble(ADMUX, channel); // Select channel to sample - sbi(ADMUX, ADLAR); // Align result to left + cbi(ADMUX, ADLAR); // Align result to right sbi(ADCSRA, ADSC); // Start conversion +} + +/** Check if ADC is done converting */ +bool adc_ready(void) +{ + return bit_is_low(ADCSRA, ADSC); +} + +/** Read the result of last conversion with 8bit precision */ +uint8_t adc_read_8bit() +{ + uint8_t low = ADCL; + uint8_t high = ADCH; + return low >> 2 | high << 6; +} - while (bit_is_high(ADCSRA, ADSC)); // Wait for it... +/** Read the result of last conversion with 10bit precision */ +uint16_t adc_read_10bit() +{ + uint8_t low = ADCL; + uint8_t high = ADCH; + return ((uint16_t) high << 8) | low; +} + +/** Start ADC conversion and wait for the result */ +static void adc_convert(uint8_t channel) +{ + adc_start_conversion(channel); + while (!adc_ready()); // Wait for it... } /** Sample analog pin with 8-bit precision */ -uint8_t adc_read_byte(uint8_t channel) +uint8_t adc_convert_8bit(uint8_t channel) { adc_convert(channel); - return ADCH; // The upper 8 bits of ADC result + return adc_read_8bit(); } - /** Sample analog pin with 10-bit precision */ -uint16_t adc_read_word(uint8_t channel) +uint16_t adc_convert_10bit(uint8_t channel) { adc_convert(channel); - return ADCW; // The whole ADC word (10 bits) + return adc_read_10bit(); } diff --git a/lib/adc.h b/lib/adc.h index f879b8a..af93226 100644 --- a/lib/adc.h +++ b/lib/adc.h @@ -25,8 +25,20 @@ void adc_disable(void); /** Enable (already initialized) ADC */ void adc_enable(void); -/** Sample analog pin with 8-bit precision */ -uint8_t adc_read_byte(uint8_t channel); +/** Start a new conversion */ +void adc_start_conversion(uint8_t channel); -/** Sample analog pin with 10-bit precision */ -uint16_t adc_read_word(uint8_t channel); +/** Check if ADC is done converting */ +bool adc_ready(void); + +/** Read the result of last conversion with 8bit precision */ +uint8_t adc_read_8bit(void); + +/** Read the result of last conversion with 10bit precision */ +uint16_t adc_read_10bit(void); + +/** Sample analog pin with 8-bit precision. Blocking! */ +uint8_t adc_convert_8bit(uint8_t channel); + +/** Sample analog pin with 10-bit precision. Blocking! */ +uint16_t adc_convert_10bit(uint8_t channel); diff --git a/main.c b/main.c index 88ce8d2..03203ff 100644 --- a/main.c +++ b/main.c @@ -2,9 +2,9 @@ #include // storing data in program memory #include // interrupt vectors #include // delay functions - #include // C header for int types like uint8_t #include // C header for the bool type +#include // Include stuff from the library #include "lib/iopins.h" @@ -14,6 +14,9 @@ #include "pinout.h" +/** + * Configure pins + */ void setup_io(void) { as_output(PIN_DISP_CP); @@ -35,35 +38,56 @@ void setup_io(void) } // --- LED display brightness control --- -#define DISP_BRIGHTNESS OCR2B +volatile uint8_t disp_brightness; +#define LIGHT_ADC_CHANNEL 6 + +/** + * PWM for LED display dimming + */ void setup_pwm(void) { - // PWM for LED display dimming - TCCR2A |= (1 << WGM20) | (1 << WGM21) | (1 << COM2B1); - TCCR2B |= (1 << CS20); + OCR2B = disp_brightness = 0xFF; + TCCR2A |= _BV(WGM20) | _BV(WGM21) | _BV(COM2B1); + TIMSK2 |= _BV(TOIE2); + TCCR2B |= _BV(CS20); + + adc_start_conversion(LIGHT_ADC_CHANNEL); +} + +/** ISR that writes the PWM register - to avoid glitches */ +ISR(TIMER2_OVF_vect) +{ + // convert in background + if (adc_ready()) { + disp_brightness = 255 - adc_read_8bit(); + adc_start_conversion(LIGHT_ADC_CHANNEL); + } - DISP_BRIGHTNESS = 0x7F; + OCR2B = disp_brightness; } +/** + * Let's gooo + */ void main() { usart_init(BAUD_115200); - usart_isr_rx_enable(true); // enable RX interrupt handler + //usart_isr_rx_enable(true); // enable RX interrupt handler + adc_init(ADC_PRESC_128); setup_io(); setup_pwm(); - adc_init(ADC_PRESC_128); // SPI conf - spi_init_master(SPI_LSB_FIRST, - CPOL_1, CPHA_0, - SPI_DIV_2); + // TODO verify the cpha and cpol. those seem to work, but it's a guess + spi_init_master(SPI_LSB_FIRST, CPOL_1, CPHA_0, SPI_DIV_2); // globally enable interrupts sei(); uint8_t cnt = 0; + char buf[100]; while (1) { pin_down(PIN_DISP_STR); spi_send(cnt); @@ -73,15 +97,7 @@ void main() _delay_ms(100); - // Brightness directly proportional to light level - DISP_BRIGHTNESS = 255 - adc_read_byte(6); + sprintf(buf, "%d\n", disp_brightness); + usart_puts(buf); } } - -// UART receive handler -ISR(USART_RX_vect) -{ - // "ECHO" function: - uint8_t b = usart_rx(); - usart_tx(b); // send back -} diff --git a/pinout.h b/pinout.h index d48d993..9f56f64 100644 --- a/pinout.h +++ b/pinout.h @@ -30,7 +30,7 @@ #define PIN_PWR_KEY A4 // Direct input from the power key, used for power-off #define PIN_PWR_HOLD A5 // Hold the buck enabled. Set 0 for shutdown -// Ambient light sensor (Vdd -> 10k -> * -> phototransistor -> GND) +// Ambient light sensor (Vdd -> 10k -> * -> photo transistor -> GND) #define PIN_LIGHT_SENSE A6 // ADC exclusive pin #endif //FIRMWARE_PINOUT_H