UKHAS Wiki

UK High Altitude Society

User Tools

Site Tools


guides:interrupt_driven_rtty

This is an old revision of the document!


Interrupt driven RTTY on AVR – the proper way to use a microcontroller

Ever since I began developing software for embedded electronics for high altitude ballooning, I’ve been separating the execution of various instructions in the code using delays, where the running program simply waits for a few processing cycles before resuming normal operation. This is usually perfectly suitable for most applications, but when power consumption is a chief concern, it is often wise to wonder, how can I get the most out of my microcontroller without increasing the power consumption? And with that, we enter a whole new world of microcontroller operation – interrupts.

881951_721113604583622_1292041006_o.jpg

Interrupts work by doing exactly what they say on the tin, they interrupt the running program to do something, before allowing the main program to continue on its way. This means that instead of waiting for an instruction to be executed before moving on to the next step, the main program can be doing something useful, and the interrupt will intervene when necessary to execute the instruction in question.

There are many types of interrupt, and each type can be triggered in a certain way, details of which can be found in the interrupt vectors section of any AVR datasheet. And interrupt can be triggered by a pin being pulled low, or perhaps each time an external oscillator ticks. In this example, we will be focusing on using a software based timer interrupt using the popular ATMEGA328P, a chip used on many Arduino platforms. I will assume prior knowledge of the RTTY (radio teletype) radio mode.

Step 1: Create the interrupt service routine (ISR)

The interrupt service routine, or ISR for short, is simply the routine which runs when the interrupt is triggered. You may think of it as a self-defined function you might create in C or Arduino as void function_name() except here the name is specific to the interrupt vector being applied. Here the function will begin ISR(TIMER1_COMPA_vect) as we will be using the TIMER1 interrupt vector as described in the datasheet. Perhaps the most difficult part of this guide comes next as we prepare to write the code in the ISR itself.

Let us assume that we will be transmitting 50 baud RTTY. For 50 baud RTTY, there must be a delay of 20ms between the transmissions of each bit. This was quite easy to achieve with our delay driven RTTY, but for interrupt driven RTTY, a little more thought is required.

Below is a working example of a RTTY ISR as well as a function used at first setup to enable the interrupt. The initialize_interrupt() routine does this and also sets the interval for how often the interrupt should trigger. I strongly recommend that you trace the code through in your mind and try to understand what is going on prior to reading the following explanation of each part – this will help both your understanding of interrupt driven RTTY and your ability to understand code.

#include <avr/io.h>
#include <avr/interrupt.h>
 
#define RADIOPIN 9
 
#define BAUD_RATE 50 // change as required
 
volatile int tx_status = 0;
volatile char *ptr = NULL;
char currentbyte;
int currentbitcount;
 
volatile boolean sentence_needed;
 
char send_datastring[102] = "";
 
 
 
// RTTY Interrupt Routine
ISR(TIMER1_COMPA_vect){
  switch (tx_status){
 
    case 0: // when the next byte needs to be gotten
      if (ptr){
	currentbyte = *ptr; // read first byte where pointer is pointing too
        if (currentbyte){
          tx_status = 1;
          sentence_needed = false;
          digitalWrite(LED_1, LOW);
        }
        else {
          sentence_needed = true;
          digitalWrite(LED_1, HIGH);
          break;
        }
      }
      else {
        sentence_needed = true;
        digitalWrite(LED_1, HIGH);
        break;
      }
 
    case 1: // first bit about to be sent
      rtty_txbit(0); // send start bit
      tx_status = 2;
      currentbitcount = 1; // set bit count to 0 ready for incrementing to 7 for last bit of a ASCII-7 byte
      break;
 
    case 2: // normal status, transmitting bits of byte (including first and last)
 
      rtty_txbit(currentbyte & 1); // send the currentb bit
 
      if (currentbitcount == 7){ // if we've just transmitted the final bit of the byte
        tx_status = 3;
      }
 
      currentbyte = currentbyte >> 1; // shift all bits in byte 1 to right so next bit is LSB
      currentbitcount++;
      break;
 
    case 3: // if all bits have been transmitted and we need to send the first of two stop bits
      rtty_txbit(1); // send first stop bit
      tx_status = 4;
      break;
 
    case 4: // ready to send the last of two stop bits
      rtty_txbit(1); // send the final stop bit
      ptr++; // increment the pointer for reading next byte in buffer
      tx_status = 0;
      break;
 
    }
 
}
 
 
// function to toggle radio pin high and low as per the bit
void rtty_txbit (int bit)
{
  digitalWrite(RADIOPIN, bit);
}  
 
 
 
void initialise_interrupt() 
{
  // initialize Timer1
  cli();          // disable global interrupts
  TCCR1A = 0;     // set entire TCCR1A register to 0
  TCCR1B = 0;     // same for TCCR1B
  OCR1A = F_CPU / 1024 / (BAUD_RATE - 1);  // set compare match register to desired timer count
  TCCR1B |= (1 << WGM12);   // turn on CTC mode:
  // Set CS10 and CS12 bits for:
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS12);
  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1A);
  sei();          // enable global interrupts
}
 
 
 
void setup()
{
  pinMode(RADIOPIN, OUTPUT);
  initialise_interrupt();
}
 
void loop()
{
}
 
guides/interrupt_driven_rtty.1397309465.txt.gz · Last modified: 2014/04/12 13:31 by ibanezmatt13

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki