Difference between revisions of "Events/2020/AVR"
Jump to navigation
Jump to search
Line 2: | Line 2: | ||
Slides: [[Media:AVR-v0.pptx]] | Slides: [[Media:AVR-v0.pptx]] | ||
+ | |||
+ | Video: https://video.hacksburg.org/videos/watch/8c9e3a8d-41d5-4c56-8eef-814f9966ce3f | ||
== Code Samples == | == Code Samples == |
Latest revision as of 17:20, 24 January 2021
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
#define __AVR_ATmega328P__ #include "https://raw.githubusercontent.com/vancegroup-mirrors/avr-libc/master/avr-libc/include/stdlib.h" #include "https://raw.githubusercontent.com/vancegroup-mirrors/avr-libc/master/avr-libc/include/avr/io.h" #include "https://raw.githubusercontent.com/vancegroup-mirrors/avr-libc/master/avr-libc/include/avr/interrupt.h" #define DIRECTION_PORT DDRB int main() { const int DIRECTION_MASK = 0b00001110; uint8_t direction = 0b00001010; // https://github.com/gnea/grbl/blob/master/grbl/stepper.c#L324 DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (direction & DIRECTION_MASK); cli(); sei(); return 0; }
/* This code takes ADC readings at a constant rate and prints them to the serial port. Output is formatted to be CSV-compatible - simply copy the contents of the serial monitor to a text file. The channels to be digitized are specified by the array 'channelSequence' - use the channel names defined directly above it in the code. Sample rate is controlled by setting 'freq' - this expects an integer value in Hz. The ADC voltage reference is controlled by 'ADCref'. Use one of the three values defined immediately above it. Written exclusively for the Arduino Uno - makes direct use of the ATMEGA328's low-level registers, so not guaranteed to work on other microcontrollers. -nj 2020-10-12 */ // --- User-set variables for sample rate, ADC range and channels sampled // Redefine this to change the frequency of ADC readings. // One channel is stable at up to ~833.33 Hz const uint16_t freq = 150; // Hz // ADMUX values to select ADC voltage reference. Table 23-3. #define EXT 0b00000000 // External voltage reference #define VCC 0b01000000 // 5 V #define I11 0b11000000 // 1.1 V // Redefine this to change what voltage the ADC uses as Vmax. const uint8_t ADCref = I11; // ADMUX values to select different channels. Table 23-4. #define A_0 0b00000000 #define A_1 0b00000001 #define A_2 0b00000010 #define A_3 0b00000011 #define A_4 0b00000100 #define A_5 0b00000101 // Redefine this to change which channels get digitized and in what order. volatile uint8_t channelSequence[] = { A_0, A_1, A_2 }; // --- Variables used for sampling the ADC const volatile uint8_t maxIndex = sizeof(channelSequence) - 1; // sizeof(array) / sizeof(array[0]) for non-byte types volatile uint16_t rawADC[sizeof(channelSequence)]; volatile uint8_t index; volatile unsigned long sampleTime; volatile bool readComplete = false; String sample = ""; const uint16_t CTCmatch = 62500 / freq - 1; // Note deliberate use of integer division // --- Variables used to control recording volatile bool btnReleased = false; bool recording = false; // --- 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); } }