Bell 202 modem for AVR and other MCUs

Bell 202 is a quite old modem standard that is still used today in amateur radio for data transmission over VHF (Packet Radio and APRS) and industrial automation (HART). It is a very simple FSK modem. The speed is limited to 1200 baud, which makes it very easy to implement on any small microcontroller. My implementation is built on top of sinewave generator code for XMEGA described in a previous post.

Bell 202 uses two frequencies – 1200 Hz and 2200 Hz for mark and space. Mark and space are not bits 0 and 1. The modem (in amateur radio and APRS) is governed by the following rules:

  1. The input data are bytes (8-bits).
  2. Bits are transmitted LSB first.
  3. A zero bit is sent as a change of frequency (from mark to space or the other way around).
  4. A one bit is sent as a no change of frequency.
  5. If more than 5 one bits have been sent – there is always a change in frequency (bit stuffing).
  6. Bit stuffing is not applied if the byte is 0x7E (it is the start flag of an AX.25 frame, specific to ham radio).
  7. Symbols are sent at 1200 baud (ie. the bit clock runs at 1200Hz, but some cycles can do bit stuffing, so a data bit is not necessarily sent every time).

Bit stuffing is needed by the receiver for correct data reception as there is no separate clock sent. Imagine a person with a flashlight at night, far away. You have agreed to transmit 1 as light and 0 as darkness, with a speed of 1 bit per second. You need a watch to count time as you look at the flashlight afar. If alternative zeros and ones are sent – the reception is easy. If several identical bits are sent in a row followed by the opposite bit – it is also manageable, but if 100 identical bits are sent – are you sure that 100 were sent? Or perhaps 99? or? 102? Bit stuffing helps to solve this problem by assuring that there will always be a change in the transmitted signal, even if the data stays the same for a long period.

The code

The transmission code depends on sinewave generator functions described in a previous post. Core functionality is executed within function modem_bit_clock_tick_from_ISR that is called from a timer interrupt 1200 times a second. This function handles the whole transmit state machine. At the end of transmission it stops the timer and sets a flag, that is checked from a periodic task. The periodic task delivers callbacks to application code.

Setup of a new transmission requires a pointer to the data, its length and optionally a function to be called when the transmission is done. modem_send function is responsible for setting all state variables and starting the timer. While the modem is transmitting the CPU can be in sleep mode as everything happens in interrupts.

I have successfully tested this code with Xastir and Direwolf 🙂