Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Morse code from a tiny chip

4.75/5 (9 votes)
8 Aug 2012CPOL7 min read 46.1K   271  
Example PIC microprocessor program on a tiny RF transceiver.

Introduction

We will see how Morse code can be used for communication from a tiny IQRF (see reference [1]) TR-52BA board that contains an LED and a PIC16F886 microprocessor. The program is written in C and compiled by free CC5X C Compiler for the PICmicro Devices version 3.4 from B. Knudsen Data [2].

The Morse code was developed 176 years ago by Samuel F. B. Morse and Alfred Veil to transfer textual information as a series of on/off tones, lights or clicks that can be directly understood by a skilled listener or observer [3]. We will use the Morse code to broadcast information about the current status of the TR-52BA [5] board and application installed.

IQRF platform and the TR-52 transceiver

IQRF [1] is a complete modular platform for wireless connectivity based on smart transceivers that are affordable and very easy to use. The RF transceiver [4] is radio transmitter and receiver built into one. We do not really care about the radio connectivity in this project. The IQRF transceiver is equipped with a built-in PIC microcontroller that we want to program. We will use TR-52BA transceiver that is equipped with PIC 16F688 microcontroller. Our program should run well on any IQRF smart transceiver and principles shown here are applicable to a wide range of PIC microcontrollers.

Timing

The Morse code consists of dots (short beeps called dits) and dashes (longer beeps called dahs). The dash is three times longer than the dot and there is a pause lasting the same time as one dot between beeps in the character and a pause lasting as one dash between characters. The pause between words should last as 7 dots. The dot for faster transmissions known as CODEX should last 50ms, for slower transmissions known as PARIS 60ms. The transceiver timer goes in 10ms intervals. We will define two macros to conveniently handle timing:

C
// macro controls the hand speed (10*ms)
// 1 dot 60ms PARIS
// 1 dot 50ms CODEX
#define dot 6
#define dash 18 

For convenience and better readability of the code we will wrap time handling into three functions.

C
void pause( uns8 delay )
{
    delay *= dot;
    waitDelay( delay );
} 

The function receives parameter delay with required delay length in dots. Therefore we multiply it by the dot macro to get the value in multiples of 10 milliseconds and we send that value to the Operating System function waitDelay(). See [6] for the complete list of all goodies the operating system provides.

BTW: we are modifying the incoming parameter inside of the procedure. This is benign, the C function arguments are sent by value, not by reference.

C
void dit()
{
    _LEDG = 1;
    waitDelay(dot);
    _LEDG = 0;
    waitDelay(dot);
} 

The predefined address _LEDG works like a binary variable that is associated with the leg of the PIC that is soldered to the green LED on the transceiver. Setting the value to 1 will light up the green LED. Settling the _LEDG to 0 will switch the light off. There is also a red LED on the RT-52BA. We can control it the same way using the _LEDR variable.

How did I know to use the _LEDG? The IQRF provides a nice development environment as a free download from [7]. Description of predefined memory locations is in the memory.h file.

Note also that we included a pause lasting one dot length after the beep to separate the beep from the next one. The dah() function is very similar to dit(), the only difference is that the beep is longer.

C
void dah()
{
    _LEDG = 1;
    waitDelay(dash);
    _LEDG = 0;
    waitDelay(dot);
} 

Morse code table encoding

There is not much RAM to waste on tiny PIC chips. Therefore, we need to plan to store the Morse code in the memory efficiently. The Morse code being made of dots and dashes only naturally suggests binary storage with 1 for a dash and 0 for a dot. For example the letter C (dah-di-dah-dit) would be encoded as 1010 or 0xA in hexadecimal. Fortunately, the Knudsen’s C is extended to support binary literals that you can write as bin(xxxxxx) or 0bxxxxxx where the x is either 0 or 1. The syntax allows for a “.” (period) symbols to separate individual bits for readability purposes.

The length of the Morse code for individual characters varies. Since the length is no more than 5 beeps, there are 3 bits left to store the length of the code in the same byte where we store the code. We decided to store the length in rightmost bits. For example the letter C would be 0b0.1010.100. The last 3 digits 100 (decimal value 4) tell us that the code for C has 4 beeps. The central part of the byte “1010” is the code and the extra leftmost 0 is one bit we did not use.

Storing the whole code in an array sorted alphabetically is very natural and helps in picking the code for a given character.

C
static uns8 m[26]; // character codes
m[ 0]=0b000.01.010;//A
m[ 1]=0b0.1000.100;//B
/* and so on */ 
encoded_character = m[ character_to_encode – ‘A’]; 

We take an advantage of knowing that all capital letters are ANSI coded continuously and alphabetically. We can use the same trick for lower case letters.

Once the code for the character was found, it can be broadcasted it by sending it to the following function:

C
void key(uns8 a)
{
    uns8 n = a & 7; // beep count
    for( n = n + 2; n > 2; n--)
    {
        uns8 mask = ( 1 << n );
        if( a & mask )
        {
            dah();
        }
        else
        {
            dit();
        }
    }
    pause(2); // 3 dots between characters, 1 was already after the beep
} 

The number of beeps in the character is extracted from the code byte by masking. The number 7 is 0b000111 in binary, a bitwise AND operation with it removes all the other 1s form the byte. We will store the value in the variable n:

C
uns8 n = a & 7; 

Then we loop through bits in the code byte starting from the position n+2 (bits are indexed from the right, starting with 0) and continuing down up to the index 3. We do not want to broadcast the last 3 bits that we used to store and get the code length.

To pick the indexed bit from the code byte masking can be used again. We create the mask by shifting the value 1 to the left so it is in the position of the indexed bit.

C
uns8 mask = ( 1 << n ); 

Then, if the result of masking is 1 we broadcast dash, otherwise it is dot.

Since the dit() and dah() functions are written to insert a pause lasting one dot after every beep, we need to add a pause with two dots length to finish the character.

Other notable parts of the code

To obtain the information about the status of the user application we call the operating system function appInfo(). The function will fill-in 32 bytes of the bufferINFO memory with the information in ANSI text format. We loop through that buffer and broadcast the text stored there.

PIC microprocessors are equipped with a watchdog timer as a protection against deadlocks [8]. If the program is not informing the system about being active for a long time (in the microelectronics world 4 seconds do look like a long time) the watchdog resets the processor. Since we intentionally delay the program execution, we need to let the watchdog know (kick the dog) from time to time. We do it before broadcasting each character by call to the operating system function clrwdt().

Programming the transceiver

To bring all the parts together we use code template file named template.c that was downloaded with the IQRF IDE [7]. The complete code is attached. Now we will use IQRF TR host USB module CK-USB-04 that connects to PC via USB cable.

  1. Insert the TR-52BA into a SIM card connector on the host module.
  2. Start the IQRF development environment iqrf_ide.exe.
  3. Select the Programming tab, click Open File, browse to our program and click Open.
  4. Click Compile and check the compilation report for errors. There should be none.
  5. Click Enter PROG Mode and click Upload. After a second the green LED starts telling you in Morse the application status of the transceiver.

Our program is now compiled and stored in the transceiver memory. We can remove the TR-52BA from the base USB module and it should start blinking the Morse code every time we reset the processor or reconnect the power. We still want to avoid soldering so we put the programmed transceiver into another IQRF base module DK-EVAL-04 that has built-in accumulator. Now we can walk to friends and impress them by the tiny light blinking the Morse code.

Notes

Our program works with upper/lower case English alphabet and space characters only. Any other character is ignored. One day we might extend the program to cover the complete Morse code.

References

[1] IQRF wireless platform http://IQRF.ORG

[2] CC5X C Compiler for the PICmicro Devices version 3.4 from B. Knudsen Data http://www.bknd.com/cc5x/

[3] Wikipedia Morse code article (February 2012 version) http://en.wikipedia.org/wiki/Morse_code

[4] Wikipedia PIC microcontroller article (February 2012 version) http://en.wikipedia.org/wiki/PIC_microcontroller

[5] IQRF distribution http://IQRF.us

[6] IQRF OS 3.0 Functionality http://iqrf.us/functions.html

[7] IQRF downloads include IQRF IDE development environment with sample code and a new project template http://iqrf.com/weben/index.php?sekce=support&id=download

[8] Wikipedia Watchdog timer (February 2012 version) http://en.wikipedia.org/wiki/Watchdog_timer

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)