Files
AVR_Learning/Make AVR Examples/Chapter18_Using-Flash-Program-Memory/talkingVoltmeter/talkingVoltmeter.c

126 lines
4.0 KiB
C

// Talking Voltmeter Example
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/power.h>
#include "pinDefines.h"
#include "USART.h"
#include "talkingVoltmeter.h"
void startSampleTimer(void) {
sampleNumber = 0; /* back to start of sample table */
TCCR2B = (1 << CS21); /* turn on timer clock */
/* Two clock options above end up ~8kHz on 8MHz system */
}
void stopSampleTimer(void) {
TCCR2B = 0; /* disable sample-playback clock */
OCR0A = 128; /* idle PWM at mid-voltage */
lastout = 0; /* start at 0 next time */
}
void speak(void) {
startSampleTimer();
loop_until_bit_is_clear(TCCR2B, CS21); /* Wait until done */
}
void updatePWMAudio(void) {
OCR0A = out + 128; /* re-center for 0-255 PWM */
lastout = out; /* update last value */
sampleNumber++; /* on to next sample */
}
void unpackByte(uint8_t dataByte) {
/* Select pairs of bits from byte, save out */
differentials[0] = (dataByte >> 6) & 0b00000011;
differentials[1] = (dataByte >> 4) & 0b00000011;
differentials[2] = (dataByte >> 2) & 0b00000011;
differentials[3] = (dataByte & 0b00000011);
}
/* Timer 2 controls sampling speed.
ISR reads new data, loads PWM values into OCR0A */
ISR(TIMER2_COMPA_vect) {
/* Since we can decode 4 2-bit values at once, need to know where
we are in the 4-step mini-cycle. */
uint8_t cycle = sampleNumber & 0b00000011; /* keep last 2 bits */
uint16_t tableEntry;
uint8_t packedData;
if (cycle == 0) { /* at first sample, re-load */
tableEntry = sampleNumber >> 2; /* where we are in table */
if (tableEntry < thisTableLength) {
/* read the next byte from the selected table */
packedData = pgm_read_byte(&thisTableP[tableEntry]);
unpackByte(packedData); /* split up byte into differentials[] */
}
else { /* at end of table, done. */
stopSampleTimer();
}
}
/* Decode the differences: current value = last + difference */
out = lastout + dpcmWeights[differentials[cycle]] - (lastout >> 4);
updatePWMAudio();
} // end ISR (TIMER2_COMPA_vect)
void printString_Progmem(const char *stringP) {
char oneLetter;
while ((oneLetter = pgm_read_byte(stringP))) {
transmitByte(oneLetter);
stringP++;
}
}
int main(void) {
uint16_t voltage;
uint8_t volts;
uint8_t tenths;
uint8_t vcc = 51; /* 10x VCC, in volts */
clock_prescale_set(clock_div_1); /* 8 MHz */
initTimer0();
initTimer2();
sei(); /* for timer2 ISR */
initADC();
initUSART();
printString_Progmem(PSTR("\r\n--=( Talking Voltmeter )=--\r\n"));
selectTable(INTRO);
speak();
while (1) {
ADCSRA |= (1 << ADSC); /* start ADC */
loop_until_bit_is_clear(ADCSRA, ADSC);
voltage = ADC * vcc + vcc / 2; /* vcc/2 to make rounding work */
voltage = voltage >> 10; /* divide by 10-bits for ADC */
/* "voltage" is now actually 10x real-world voltage */
volts = voltage / 10;
tenths = voltage % 10;
transmitByte('0' + volts); /* serial output as well */
selectTable(volts); /* 0 points to ZERO_TABLE, etc */
speak();
transmitByte('.');
selectTable(POINT);
speak();
transmitByte('0' + tenths);
selectTable(tenths);
speak();
printString_Progmem(PSTR(" volts\r\n"));
selectTable(VOLTS);
speak();
_delay_ms(SPEECH_DELAY);
} /* end while */
return 0;
}