Raspberry Pi with Ham

Introduction

A few days ago I was suffering the lack of output from a bare-metal Raspberry Pi - very little as output to hint what is going on inside the electronics. I had struggled through the first few "Baking Pi" tutorials and got as far as programming the GPIO to switch the "OK" LED on and off. Despite the fiddliness of the assembly language programming, it was delightful to see at least some sort of activity. After failing to connect a serial UART cable, for usable output, I decided to progress with my operating system experimentation using just the OK LED.

One LED may not seem like much, but it can communicate a surprising amount of information if you use it right.

First though, I wanted to get programming in something a little more friendly than raw assembler. I tried fiddling with the makefile and project structure from"Baking Pi", but it did not really suit what I needed. After looking around a bit, I found some web articles which led me to https://github.com/MichaelBell/RasPi-bare. This turned out to be a great starting point. Everything was set up for just dropping C source code and header files into the "raspi" directory and calling them from the start function("not_main").

I recommend you grab a copy (either of the basic "RasPi-bare" project, or of my modified one with the changes from this article.

Getting Set Up

To get it going you do need to tell it where to find a toolchain ( I continued to use the yagarto toolchain which I had installed for "Baking Pi"..). There are two essential ways of doing this. One is to add the yagarto "bin" directory to your PATH environment variable before running "make":

export PATH=/drives/c/devtools/yagarto-20121222/bin:$PATH

This can be done in your .profile or .bashrc if you are developing on Linux or cygwin, or just typed on the command line if you are using a MobaXterm local session. bear in mind though, that settings you type on the command line will be discarded when your session ends, and need to be re-typed next time.

In my case, I decided to make the setting a bit more permanent, by editing the makefile to refer directly to the toolchain: I changed

ARCH = ../gcc-arm-none-eabi-4_6-2012q2/bin/arm-none-eabi
CC = ${ARCH}-gcc
CPP = ${ARCH}-g++
AS = ${ARCH}-as
...

to

ARCH = /drives/c/devtools/yagarto-20121222/bin/arm-none-eabi
CC = ${ARCH}-gcc
CPP = ${ARCH}-g++
AS = ${ARCH}-as
...

A quick test of running "make" gave me an image file which would run on the Raspberry Pi. Now I could start changing it to suit what I need.

My first step was to get the

__attribute__((no_instrument_function))  void not_main(void) { ...

. function out of the way of my code. The slightly odd looking annotation is needed so it can be called from the assembly code in setup.s. I created a new C file os_init.c:

extern int main();
__attribute__((no_instrument_function))  void os_init(void) { main(); }

and altered the jump in setup.s from

   bl not_main
hang: b hang

to

   bl os_init
hang: b hang

so that main.c could start at a more familiar and legible:

int main(void) {...

The next step was to move the example GPIO code from main.c into its own C and H files, and add in a C version of the "timer" code from "Baking Pi":

In gpio.h:

#ifndef GPIO_H
#define GPIO_H

void gpio_init();

void raspi_okled_set(int state);
void raspi_timer_wait(int usec);

#endif

in >gpio.c:

#include "raspi.h"
#include "gpio.h"

#define OKLED (1<<16)
void raspi_okled_set(int state) {
  if (state) {
    PUT32(GPCLR0, OKLED);
  } else {
    PUT32(GPSET0, OKLED);
  }
}

void raspi_okled_init() {
  uint32_t sel = GET32(GPFSEL1);
  sel &= ~(0b111 << 18);
  sel |= (0b001 << 18);
  PUT32(GPFSEL1,sel);
}

void raspi_timer_wait(int usec) {
  uint32_t start = GET32(COUNTER);
  uint32_t end = start + usec;
  while (GET32(COUNTER) < end)
    ;
}

void gpio_init() {
  raspi_okled_init();
}

With this in place, a rough equivalent of the original "flasing LED demo" in main can be done as:

#include "gpio.h"

int main(void) {
  gpio_init();

  while(1) {
    raspi_okled_set(1);
    raspi_timer_wait(500000);

    raspi_okled_set(0);
    raspi_timer_wait(500000);
  }
} 

Now for the Ham

Finally, I was at a point where I could Write the code I really wanted to. getting some useful output from the OK LED.

Seaman_send_Morse_code_signals

Sending information over a single wire or light has a long and illustrious history. Telegraph cables, spark transmitters and Aldis lamps. The key to their success was a way of encoding ordinary letters and numbers in terms of switching something on or off. By far the most popular such encoding (for people to understand, at least) is Morse Code. I won't give all the details here, but the essence is simple. Each letter or number is transmitted as a pattern of long and short pulses. For example, the letter "A" is one short pulse, followed by one long pulse. With a little practice, pretty much anyone can start to understand Morse Code transmissions in their own language.

Using the raspi_okled_set() and raspi_timer_wait functions and a bit of basic C we can make some functions to send text as Morse code using the OK LED.

in morse.h:

#ifndef MORSE_H
#define MORSE_H

void switch_on(int usec);
void switch_off(int usec);

void morse_glyph(char glyph);
void morse_symbol(const char* symbol);
void morse_char(char c);
void morse_string(const char* message);

void flash_dots(int ndots);
void halt(const char* message);

#endif

These functions should be pretty straightforward. switch_on() and switch_off() control the light for the given number of microseconds. morse_glyph() sends a short pulse if given a dot ('.'), a long pulse if given a dash (-'), or keeps the light off for a bit if given a space (' '). morse_symbol() takes a zero-terminated string containing '.', '-' and ' ' characters, and sends each one to morse_glyph(). morse_char() looks up the dots and dashes for a (lower case) letter or number and gives it to morse_symbol() and to top it all off morse_string() works its way through a zero-terminated string of letters and numbers, sending each oue out to the LED.

The last two functions are a bit extra, specifically for instrumenting code. Although Morse code supports digits, they are relatively long and among the hardest to learn. In cases where only a number is needed it can be easier to just show the number as a series of dots, so that's what flash_dots() does. halt() can be useful to put a "breakpoint" in some code; it goes into an infinite loop, flashing the supplied string over and over again.

In morse.c

#include "gpio.h"

struct Morse { char letter; const char* symbol; } code[] = {
    { 'a', ".-" },
    { 'b', "-..." },
    { 'c', "-.-." },
    { 'd', "-.." },
    { 'e', "." },
    { 'f', "..-." },
    { 'g', "--." },
    { 'h', "...." },
    { 'i', ".." },
    { 'j', ".---" },
    { 'k', "-.-" },
    { 'l', ".-.." },
    { 'm', "--" },
    { 'n', "-." },
    { 'o', "---" },
    { 'p', ".--." },
    { 'q', "--.-" },
    { 'r', ".-." },
    { 's', "..." },
    { 't', "-" },
    { 'u', "..-" },
    { 'v', "...-" },
    { 'w', ".--" },
    { 'x', "-..-" },
    { 'y', "-.--" },
    { 'z', "--.." },
    { ' ', " " },
    { '0', "-----" },
    { '1', ".----" },
    { '2', "..---" },
    { '3', "...--" },
    { '4', "....-" },
    { '5', "....." },
    { '6', "-...." },
    { '7', "--..." },
    { '8', "---.." },
    { '9', "----." }
};
static int nchars = sizeof(code) / sizeof (struct Morse);

// how fast do you want to go?
// a value of 300000 takes about a minute to say "raspberry alpha omega"
// smaller numbers make it faster
#define dot_pause 300000

#define dash_pause (dot_pause * 3)
#define gap_pause dot_pause
#define letter_pause (dot_pause * 3)
#define word_pause (gap_pause * 7)

void switch_off(int duration) {
  raspi_okled_set(0);
  raspi_timer_wait(duration);
}

void switch_on(int duration) {
  raspi_okled_set(1);
  raspi_timer_wait(duration);
}

void morse_glyph(char c) {
  switch(c) {
  case '.':
    switch_on(dot_pause);
    break;
  case '-':
    switch_on(dash_pause);
    break;
  default:
    raspi_timer_wait(word_pause);
    break;
  }

  switch_off(gap_pause);
}

void morse_symbol(const char* symbol) {
  struct Crate* crate;

  for (char c = *symbol; c != 0; c = *++symbol) {
    morse_glyph(c);
  }

  raspi_timer_wait(letter_pause);
}

void morse_char(char c) {
  for (int i = 0; i < nchars; ++i) {
    if (c == code[i].letter) {
      morse_symbol(code[i].symbol);
      return;
    }
  }
}

void morse_string(const char* message) {
  for (char c = *message; c != 0; c = *++message) {
    morse_char(c);
  }
}

void flash_dots(int ndots) {
  for (int i = 0; i < ndots; ++i) {
    morse_glyph('.');
  }

  raspi_timer_wait(letter_pause);
}

void halt(const char* message) {
  for (;;) {
    morse_string(message);
  }
}

Finally, to bring this all together, I Changed main.c to flash a message:

#include "gpio.h"
#include "morse.h"

int main(void) {
  gpio_init();
  halt("hello world ");
}

To prove that it works, here's a video. Can you work out what it says?

3 Comments

  1. Pingback: I now have a screen, but can I use it? | Raspberry Alpha Omega

  2. Pingback: Data representation – squashing Morse code | Raspberry Alpha Omega

  3. Pingback: Morse code on a big, bright, LEDBorg | Raspberry Alpha Omega

Leave a Reply

Your email address will not be published. Required fields are marked *