Programming the pcDuino

This Tutorial is Retired!

This tutorial covers concepts or technologies that are no longer current. It's still here for you to read and enjoy, but may not be as useful as our newest tutorials.

Pages
Contributors: SFUptownMaker
Favorited Favorite 3

Serial Communications

The pcDuino comes with several built-in serial ports. Here's how to use those ports to send and receive data from within a program.

Hardware

Serial port pin mapping on the pcDuino

The A10 processor on the pcDuino has eight serial ports on board. Only two of these ports actually map to a Linux device: /dev/ttyS0 is the debugging port, and /dev/ttyS1 is the UART which is pinned out on the Arduino-ish header.

Pin mode settings

As mentioned on the GPIO page, the "mode" registers for each pin control the functionality of that pin. For serial I/O, you may need to write '3' to the mode files for pins 0 and 1. The C++ code includes that; the serial library for Python does it by default.

Serial Communications in C++

Using C++ to access the serial port is not too hard. There are lots of good sites out there about serial port access in Linux; here's a simple example program to get your started. All it does is configure the port, send "Hello, world!" across it, wait for an input keystroke to come back, then exit.

language:cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include "serial_test.h"

// Life is easier if we make a constant for our port name.
static const char* portName = "/dev/ttyS1";

int main(void)
{

  // The very first thing we need to do is make sure that the pins are set
  //   to SERIAL mode, rather than, say, GPIO mode.
  char path[256];

  for (int i = 0; i<=1; i++)
  {
    // Clear the path variable...
    memset(path,0,sizeof(path));
    // ...then assemble the path variable for the current pin mode file...
    sprintf(path, "%s%s%d", GPIO_MODE_PATH, GPIO_FILENAME, i);
    // ...and create a file descriptor...
    int pinMode = open(path, O_RDWR);
    // ...which we then use to set the pin mode to SERIAL...
    setPinMode(pinMode, SERIAL);
    // ...and then, close the pinMode file.
    close(pinMode);
  }

  int serialPort; // File descriptor for serial port
  struct termios portOptions; // struct to hold the port settings
  // Open the serial port as read/write, not as controlling terminal, and
  //   don't block the CPU if it takes too long to open the port.
  serialPort = open(portName, O_RDWR | O_NOCTTY | O_NDELAY );

  // Fetch the current port settings
  tcgetattr(serialPort, &portOptions);

  // Flush the port's buffers (in and out) before we start using it
  tcflush(serialPort, TCIOFLUSH);

  // Set the input and output baud rates
  cfsetispeed(&portOptions, B115200);
  cfsetospeed(&portOptions, B115200);

  // c_cflag contains a few important things- CLOCAL and CREAD, to prevent
  //   this program from "owning" the port and to enable receipt of data.
  //   Also, it holds the settings for number of data bits, parity, stop bits,
  //   and hardware flow control. 
  portOptions.c_cflag |= CLOCAL;
  portOptions.c_cflag |= CREAD;
  // Set up the frame information.
  portOptions.c_cflag &= ~CSIZE; // clear frame size info
  portOptions.c_cflag |= CS8;    // 8 bit frames
  portOptions.c_cflag &= ~PARENB;// no parity
  portOptions.c_cflag &= ~CSTOPB;// one stop bit

  // Now that we've populated our options structure, let's push it back to the
  //   system.
  tcsetattr(serialPort, TCSANOW, &portOptions);

  // Flush the buffer one more time.
  tcflush(serialPort, TCIOFLUSH);

  // Let's write the canonical test string to the serial port.
  write(serialPort, "Hello, World!", 13);

  // Now, let's wait for an input from the serial port.
  fcntl(serialPort, F_SETFL, 0); // block until data comes in   

  char dataIn = 0;
  do
  {
    read(serialPort,&dataIn,1);
  } while(dataIn == 0);

  printf("You entered: %c\n", dataIn);

  // Don't forget to close the port! Failing to do so can cause problems when
  //   attempting to execute code in another program.
  close(serialPort);
}

void setPinMode(int pinID, int mode)
{
  writeFile(pinID, mode);
}

// While it seems okay to only *read* the first value from the file, you
//   seemingly must write four bytes to the file to get the I/O setting to
//   work properly. This function does that.
void writeFile(int fileID, int value)
{
  char buffer[4];  // A place to build our four-byte string.
  memset((void *)buffer, 0, sizeof(buffer)); // clear the buffer out.
  sprintf(buffer, "%d", value);
  lseek(fileID, 0, SEEK_SET);   // Make sure we're at the top of the file!
  int res = write(fileID, buffer, sizeof(buffer));
}

Serial Communications in Python

Python requires an external library to use serial ports; however, it's very easy to install. Just open up an LXTerm window and type

language:text
sudo apt-get install python-serial

Pyserial, the Python serial library, will be automatically installed.

Now, let's run our first Python serial communications program. We'll do the same thing we did in C++: open the port, wait for the user to input a keystroke, echo back the keystroke, then exit. Here's the code:

language:python
#!/usr/bin/env python

import serial ## Load the serial library

## Select and configure the port
myPort = serial.Serial('/dev/ttyS1', 115200, timeout = 10)

## Dump some data out of the port
myPort.write("Hello, world!")

## Wait for data to come in- one byte, only
x = myPort.read()

## Echo the data to the command prompt
print "You entered " + x

## Close the port so other applications can use it.
myPort.close()