2.1.6.4.3 Interrupt-Driven Bufferred OCD Messaging
A more complex method of using AVR MCU OCD messaging involves a small I/O buffer into which a printf function can inject data which will be gradually transferred to the debugger. A timer interrupt is used to periodically service the printf buffer. On each interrupt a character will be sent from the buffer, if the message channel is ready and data is available. This example runs on a megaAVR® device with JTAG interface, but a similar mechanism can be employed on other AVR device architectures supporting OCD messaging.
#include <avr/io.h> #include <stdio.h> #include <avr/interrupt.h> // Buffer allocated to OCD messaging #define OCDR_BUFFER_SIZE 32 static uint8_t ocdr_buffer[OCDR_BUFFER_SIZE]; // Buffer pointers static uint8_t head; static uint8_t tail; // Flag to indicate if a debugger is picking up the messages static uint8_t debugger_attached = 1; // Declarations static int ocdr_putchar(char c, FILE *stream); static FILE mystdout = FDEV_SETUP_STREAM(ocdr_putchar, NULL, _FDEV_SETUP_WRITE); // Puts a char into the stream static int ocdr_putchar(char c, FILE *stream) { // If the debugger fails to collect, rather just abort if (!debugger_attached) return 1; // Increment head with wrapping uint8_t tmphead; tmphead = (head + 1 ); if (tmphead >= OCDR_BUFFER_SIZE) tmphead = 0; if (tmphead == tail) { // Overflow, abort debugger_attached = 0; return 0; } // Add data ocdr_buffer[tmphead] = c; head = tmphead; return 1; } // Timer interrupt regularly sends data ISR(TIMER0_OVF_vect) { // If no data, continue if (head == tail) return; // If the previous byte has not been collected, continue if (OCDR & 0x80) return; // Increment tail uint8_t tmptail = (tail + 1); if (tmptail >= OCDR_BUFFER_SIZE) tmptail = 0x00; tail = tmptail; // Send data to debugger OCDR = ocdr_buffer[tmptail]; // Reset attached flag to allow hot-plugging debugger_attached = 1; } void ocdr_printf_init (void) { // Zero buffer pointers head = 0; tail = 0; // TC setup. 8Mhz DIV32 gives ~1ms overflow ticks TIFR = (1 << TOV0); TIMSK = (1 << TOIE0); TCCR0 = (1 << CS01) | (1 << CS00); sei(); } int main(void) { // Port init DDRB |= 0xFF; PORTB = 0x55; // Buffer init stdout = &mystdout; ocdr_printf_init(); // Demo loop uint8_t c = 0; while(1) { c++; PORTB = ~c; printf("led %d\n", c); // Must delay > ~8ms to guarantee printf delivery uint16_t delay = 0x3FFF; while (delay--) ; } }