wordbotch.com

Blog


Raspberry Pi Zero UART to PIC16F1789

My latest thing has been trying to get a Raspberry Pi Zero talking to a PIC16F1789 microcontroller over UART. This is the simplest project I could come up with that actually does something.

  1. SSH to the Raspberry Pi Zero
  2. Run a Python script that accepts user input
  3. Enter some text, which the Python sends over UART to the PIC
  4. The PIC converts the text to morse code, and flashes morse on an LED
  5. The PIC sends the original text back over UART to the Pi Zero.
  6. The Pi Zero prints the text it gets back, and prompts for more input

Stuff I used:

  • Raspberry Pi Zero
  • MicroSD card
  • SD card reader
  • PicKit 3 programmer/debugger
  • PIC Development board
  • Breadboard
  • LED
  • Resistor
  • Wires etc

The PicKit 3 and the PIC development board I got together in a kit from kanda.com.

Setting up the Raspberry Pi

Flash Raspbian

I downloaded Raspbian Jessie Lite, then followed this post by Nic Raboy to install it on the SD Card. The short version (on OSX):
> diskutil list #then find disk name of SD card
> diskutil unmountDisk /dev/<diskname>
> sudo dd bs=1m if=/path/to/raspbian.img of=/dev/<diskname>

This can take a really long time, about one-and-a-half hours on my iMac. Disabling computer sleep seemed to help, without doing this sometimes the process would hang forever.

Pi Zero Config

To enable UART, as well as SSH to the Pi Zero over USB, edit the following two files, both at the root of the newly-formatted SD card. This bit comes mainly from this post by Nic Raboy and this post by a Raspberry Pi engineer.

config.txt

Add these two lines at the end of config.txt:

dtoverlay=dwc2
enable_uart=1

cmdline.txt

Add the text below to cmdline.txt, after the rootwait parameter

modules-load=dwc2,g_ether

Also, delete the section "console=serial0,115200" from cmdline.txt to disable serial0 as a terminal.

After these changes, my cmdline.txt has the following parameters in order (space delimited, on a single line).

dwc_otg.lpm_enable=0
console=tty1
root=/dev/mmcblk0p2
rootfstype=ext4
elevator=deadline
fsck.repair=yes
rootwait
modules-load=dwc2,g_ether

SSH

I found I also had to add an empty file called ssh in the root directory to enable SSH.

> cd /Volumes/boot/
> touch ssh

After rebooting the Pi Zero, you should be able to SSH to it from Terminal with:

> ssh pi@raspberrypi.local

The default password is raspberry. Needless to say, probably a good idea to change the default password if you're doing anything lasting. To change the root password, run the command passwd in the Pi and follow the prompts. Of course, if you have to re-flash the Pi (like I did) and get a scary-looking warning the next time around saying remote host identification has changed:

> ssh-keygen -R raspberrypi.local
> ssh pi@raspberrypi.local

Enable internet sharing

I let the Pi Zero share my mac's internet connection, by going to System Preferences -> Sharing and enabling Internet Sharing using RNDIS/Ethernet gadget.

internet-sharing.png

Python

Once the Pi Zero has access to the internet, you can apt-get stuff, but don't go overboard. I ran into some memory problems and had to re-flash the pi and start from scratch. Anyway, the only external Python library I needed for this was pyserial, which I installed with pip:

> sudo apt-get install python-pip
> sudo pip install pyserial

This is the python script I used to transmit from the Pi over UART. With the changes to config.txt and cmdline.txt it should be runnable by the pi user, but if you get a permissions error maybe try sudoing it.

> python send.py
#!/usr/bin/env python

import serial

ser = serial.Serial(
 port='/dev/serial0',
 baudrate = 9600,
 parity=serial.PARITY_NONE,
 stopbits=serial.STOPBITS_ONE,
 bytesize=serial.EIGHTBITS,
 timeout=1
)

try:
 while 1:

  message = raw_input('>').strip()
  if not message:
   continue
  ser.write(message + '\n')

  response = ''
  while not response:
   response = ser.readline().strip()
  print response

finally:
 ser.close()

Setting up the PIC

Connect via USB to the PicKit 3, and connect the PicKit 3 to the development board. Insert the PIC16F1789 into the 40-pin socket.

From Microchip, download and install:

Then open MPLAB X and create a new project:

MPLABX-01.png
MPLABX-02.png
MPLABX-03.png
MPLABX-04.png
MPLABX-05.png

Once the new project is created, make sure it's selected as the main project (it should be listed in bold). If not, right-click on it and select 'Set as Main Project' from the menu.

Change the project configuration so the PicKit3 supplies 3.25 Volts.

  • Right click on the project and select Properties
  • Select PICkit 3 from the Categories list
  • Select Power from Option categories
  • Check Power target circuit from PICkit3
  • Set Voltage Level to 3.375 (or thereabouts)
MPLABX-06.png

This is the C file I programmed the PIC with. Yes, It's probably good practice to break it up into header files etc, but I've kept it all in one file for simplicity.

#include <xc.h>
#include <string.h>

#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = ON
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = ON
#pragma config FCMEN = ON
#pragma config WRT = OFF
#pragma config VCAPEN = OFF
#pragma config PLLEN = OFF
#pragma config STVREN = ON
#pragma config BORV = LO
#pragma config LPBOR = OFF
#pragma config LVP = ON

#define _XTAL_FREQ 8000000
#define BAUD_RATE 9600L
#define BUFFER_SIZE 60

void setup();
void clear_buffer(char *buffer);
void uart_read_text(char *Output, unsigned int length);
void uart_write_text(char *text);
char uart_read();
void uart_write(char data);
void morse_flash_text(char* plaintext);
void morse_flash(char* morse);

char *MORSE[26] = {
    ".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..",
    ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.",
    "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."
};

void main() {
    setup();
    char buf[BUFFER_SIZE];
    do {
        clear_buffer(buf);
        uart_read_text(buf, BUFFER_SIZE);
        morse_flash_text(buf);
        uart_write_text(buf);
    } while (1);
}

void setup() {
    TRISB = 0xFF;
    WPUB = 0xFF;
    ANSELB = 0;
    TRISD = 0x00;
    PORTD = 0x00;

    BRGH = 1;
    SPBRG = (_XTAL_FREQ - BAUD_RATE * 16) / (BAUD_RATE * 16);
    SYNC = 0;
    SPEN = 1;
    TRISC7 = 1;
    TRISC6 = 1;
    CREN = 1;
    TXEN = 1;
}

void clear_buffer(char *buffer) {
    int i;
    for (i = 0; i <= BUFFER_SIZE; i++) {
        buffer[i] = '\0';
    }
}

void uart_read_text(char *Output, unsigned int length) {
    int i = 0;
    while (i < length) {
        Output[i] = uart_read();
        if (Output[i] == '\n') {
            i = length;
        }
        i++;
    }
}

void uart_write_text(char *text) {
    int i;
    for (i = 0; text[i] != '\0'; i++) {
        uart_write(text[i]);
    }
    uart_write('\n');
}

char uart_read() {
    while (!RCIF);
    return RCREG;
}

void uart_write(char data) {
    while (!TRMT);
    TXREG = data;
}

void morse_flash_text(char* plaintext) {
    int i;
    for (i = 0; i < strlen(plaintext); i++) {
        if (plaintext[i] >= 65 && plaintext[i] <= 90) {
            morse_flash(MORSE[plaintext[i] - 65]); //A-Z
        } else if (plaintext[i] >= 97 && plaintext[i] <= 122) {
            morse_flash(MORSE[plaintext[i] - 97]); //a-z
        } else if (plaintext[i] == 32) {
            __delay_ms(800); //space
        }
    }
}

void morse_flash(char* morse) {
    int i;
    for (i = 0; i < strlen(morse); i++) {
        PORTD = 0b11111111;
        if (morse[i] == '.')
            __delay_ms(200);
        else if (morse[i] == '-')
            __delay_ms(600);
        PORTD = 0b00000000;
        __delay_ms(200);
    }
    __delay_ms(400);
}

Adding this file alone under Source Files, the project should build (hammer icon, or Run -> Build Main Project). Click the Run icon to program the PIC. There might be a dialog like the one below, but just acknowledge and continue.

dialog.png

If it's successful, output should look more or less like this:

output-01.png

If you get an error like this one, make sure the PICkit is the only source of power to the board. You might have to remove some wires if things are already wired in. I found that after wiring everything up as described in the next bit, it was enough to disconnect the TX pin on the PIC to allow reprogramming. Without doing that, I got this error every time.

output-02.png

Wiring things

Two important resources for this:

As far as the breadboard goes, all you need is an LED that will light up when a pin on the PIC goes high. I wired mine in series with a 410 ohm resistor. You could probably get by without the resistor, I think.

Anyway, at this point it'd be a good idea to check the LED is actually working, by wiring it directly between ground and VSS.

So assuming the LED is working:

  • Connect the TX pin on the Pi Zero (pin 8) to the RX pin on the PIC (pin RB6)
  • Connect the RX pin on the Pi Zero (pin 10) to the TX pin on the PIC (pin RB7)
  • Connect ground on the Pi Zero to ground (VSS) on the PIC
  • Connect any PORTD pin on the PIC to VCC of the LED
  • Connect ground on the PIC to ground of the LED
morse-img.jpg

Testing it

To see if it works:

  1. Open Terminal and SSH to the Pi -- > ssh pi@raspberrypi.local
  2. Run the python script -- > python send.py
  3. Enter some text and hit Enter.