269
edits
Changes
no edit summary
Have you ever wondered what a little Arduino is really capable of? Ever wanted to ''really'' understand a chip? Learn lots of nitty-gritty details about the Microchip AVR architecture, the processor architecture that powers the Arduino and many other products.
Slides: [[Media:AVR-v0.pptx]]
Video: https://video.hacksburg.org/videos/watch/8c9e3a8d-41d5-4c56-8eef-814f9966ce3f
== Code Samples ==
// --- setup() and loop()
void setup() {
doLowLevelConfig();
pinMode(2, INPUT_PULLUP); // Changing this pin requires changes in doLowLevelConfig()
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// Initialize communication with PC.
Serial.begin(115200);
Serial.println(getHeader());
}
void loop() {
if (readComplete) {
readComplete = false;
// TODO - format output using C-style strings instead
sample = sampleTime;
for (uint8_t i = 0; i <= maxIndex; i++) {
sample += "," + String(rawADC[i]);
}
Serial.println(sample); // Blocking? Technically, but this is usually fast.
}
// Handle button presses (or releases in our case).
if (btnReleased) {
btnReleased = false;
if (recording) {
// If we're recording, stop.
bitClear(ADCSRA, ADEN);
recording = false;
}
else {
// If we're not recording, start.
recording = true;
bitSet(ADCSRA, ADEN);
}
}
}
// --- Subsidiary functions
String getHeader() {
// TODO - re-implement using C-style strings
String header = "microseconds";
String colName;
for (byte i = 0; i <= maxIndex; i++) {
switch (channelSequence[i]) {
case A_0 | ADCref: colName = ",CH_0"; break;
case A_1 | ADCref: colName = ",CH_1"; break;
case A_2 | ADCref: colName = ",CH_2"; break;
case A_3 | ADCref: colName = ",CH_3"; break;
case A_4 | ADCref: colName = ",CH_4"; break;
case A_5 | ADCref: colName = ",CH_5"; break;
default: colName = ",unknown"; break;
}
header += colName;
}
return header;
}
void doLowLevelConfig() {
noInterrupts();
// Combine the ADC reference bits with the channel selection bits. Each value
// in channelSequence should be the complete value of ADMUX for recording the
// desired channel. Tables 23-3 and 23-4.
// TODO - make this separate from the user-specified channel selection array?
for (byte i = 0; i <= maxIndex; i++){
channelSequence[i] = channelSequence[i] | ADCref;
}
// Configure the Timer 0 Compare Match A interrupt.
OCR0A = 64; // Keep away from the overflow (0) so micros() can update.
bitSet(TIMSK0, OCIE0A); // Enable the interrupt.
// Configure Timer 1 Compare Match A interrupt to trigger @ freq Hz
TCCR1A = TCCR1B = TCNT1 = 0; // Clear Timer 1 settings.
TCCR1B = bit(WGM12) | bit(CS12); // Count @ 62500 Hz: CTC mode, prescaler = 256. Tables 15-5 and 15-5.
OCR1A = CTCmatch; // Count up to CTCmatch. Section 15.11.6.
bitSet(TIMSK1, OCIE1A); // Enable Output Compare Match A interrupt. Section 15.11.8.
// Enable the ADC-conversion-complete interrupt, configure ADC for
// single-conversion mode, and disable the ADC. Section 23.9.2.
ADCSRA |= bit(ADIE) | bit(ADIF); // sets these bits
ADCSRA &= ~(bit(ADATE) | bit(ADEN)); // clears these bits
ADMUX = channelSequence[0];
interrupts();
}
// --- Interrupt service routines
ISR(TIMER0_COMPA_vect) {
// Debounce the button. See comments in 'AccelCalib.ino'.
static volatile uint16_t btnHistory = 0xFFFF;
btnHistory = (btnHistory << 1) | bitRead(PIND, PIND2);
if (btnHistory =/= 0x7FFF) btnReleased = true; //MediaWiki won't let me post the equality operator
}
ISR(TIMER1_COMPA_vect) {
// Begin a conversion on the first channel in the sequence and record the time.
readComplete = false;
index = 0;
ADMUX = channelSequence[index];
bitSet(ADCSRA, ADSC); // ADSC =/= "Analog-Digital converter Start //MediaWiki won't let me post the equality operatorConversion"
sampleTime = micros();
}
ISR(ADC_vect) {
// Store the conversion result.
rawADC[index] = ADC;
// Check if all channels have been read. If not, start the next one.
if (index =/= maxIndex) readComplete = true; //MediaWiki won't let me post the equality operator
else {
index++;
ADMUX = channelSequence[index];
bitSet(ADCSRA, ADSC);
}
}
</pre>