TX

Der ATtiny ist nun über eine Serielle Schnittstelle mit dem PC verbunden. Vorerst sendet er nur Daten und kann keine empfangen, aber immerhin.

Da der ATtiny diese Schnittstelle nicht von sich aus unterstützt, musste ich sie ausprogrammieren. So sieht der komplette Sourcecode aus:

// serial 9600,N,8,1
// led on PB3, tx on PB4

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // clear bit
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))  // set bit

volatile uint8_t tx_work = 0;      // work request
volatile uint8_t tx_index;         // loop for on byte
volatile uint8_t tx_buffer;        // bufffer to send

ISR(TIM0_COMPA_vect) {             // interrupt service routine
  if (tx_work) {                   // work has to be done
    if (tx_index == 0) {           // index = 0
      cbi(PORTB, PB3);             //     send start bit
    } else if (tx_index <=8) {     // index = data 
      if (tx_buffer & 1) {         //     from lsb to msb 
        sbi(PORTB, PB3);           //     send data bit 
      } else { 
        cbi(PORTB, PB3);           //     send data bit 
      }
      tx_buffer >>= 1;             //     remove lsb
    } else if (tx_index >= 9) {    // index = 9
      sbi(PORTB, PB3);             //     send stop bit
      tx_work = 0;                 //     work done
    }
    tx_index++;                    // next index
  }
}

void serial_putc(char c) {
  while(tx_work);                  // wait if busy
  tx_index  = 0;                   // reset index
  tx_buffer = c;                   // data to transmit
  tx_work = 1;                     // isr has work
}

void serial_print(const char *str) {
  uint8_t i;
  for (i = 0; str[i]; i++) {
    serial_putc(str[i]);           // write each character
  }
}

int main(void) {
  sbi(DDRB,  PB4);                 // set led pin as output
  sbi(DDRB,  PB3);                 // set tx as output

  sbi(TCCR0A, WGM01);              // CTC mode, clear timer on compare match
  sbi(TCCR0B, CS01);               // prescaler clk/8 -> 1 tic = 1us for 8mhz
  OCR0A = 103;                     // set compare register A
                                   // 103us from wormfood.net/avrbaudcalc.php
  sbi(TIMSK, OCIE0A);              // enable interrrupt for OCROA==TCNT0
  sei();                           // enable interrupts

  uint8_t v;                       // init visual counter
  for(v = 0;; v++) {               // main loop
    PORTB ^= 1<<PB4;               // toggle led
    serial_print("toggle\r\n");    // serial out
    _delay_ms (200);               // wait 200ms
  }
};

Das LED leuchtet dadurch alle 400 ms – so sieht das Ergebnis auf dem PC aus:

toggle
toggle
...

PS: Das Programm verbraucht 324 Bytes. Eine frühere Version welche elegant mit fdevopen(&serial_putc, 0) und printf gearbeitet hat, musste ich verwerfen: sie war mit  2’552 Bytes, also rund 60% des verfügbaren Speichers, viel zu gross.