NU32: Serial Communication with the PC

From Mech
Jump to navigationJump to search

-----***UNDER CONSTRUCTION NDM 1/23***-----

The NU32 has the ability to talk directly with a computer over a virtual serial port connection. This ability can be used to debug code, transfer data, and interact with the microcontroller using a computer.

Overview

The NU32 uses an on board FT2232H chip from FTDI to convert RS-232 TTL serial communication to USB. Using drivers from FTDI, the communication can be opened by any program that can access a virtual serial port. On the NU32, sample code has been created to enable the UART module on the PIC32. Using this code you can write strings to the computer, and generate an interrupt when characters are received. On the computer, several programs that can access the serial connection are discussed, including Processing, MATLAB, and terminal programs.

Details

The FT2232H chip is hardwired the to UART1 (F2 and F8, for general communication) and UART4 (D14 and D15, for bootloading) modules on the PIC32MX795F512L. The Receive (RX) pins, F2 and D14, and Transmit (TX) pins, F8 and D15, are also brought out to the sides of the NU32 board. They should not be used as general IO.

Serial communication is asynchronous, it does not use an external clock signal to know when to read individual bits. Instead, the sender and receiver decide beforehand what frequency to transmit data. This frequency is called the baud rate and must match on both the NU32 and computer for the data to be interpreted correctly.

The NU32 uses the UART1 and UART4 modules at 115200 baud, as does the sample code below.

Library Functions

This code contains functions to read characters and write strings over the serial port. It uses the same header files as "Hello World".

  • NU32.c contains the functions to initialize the LEDs and USER button on the NU32 board, and to initialize the UART modules.
  • NU32.h contains function prototypes for the .c file.
    • You do not need to modify these files to use serial communication.

The functions that control the serial communication are:

  • void initSerialNU32(void) - enables UART1 and UART4 at 115200 baud with an interrupt at priority level 2 and 3
  • void WriteString(UART_MODULE, const char *) - call with UART1 and the character array you wish to send
  • void PutCharacter(UART_MODULE, const char) - writes an individual character to the computer when the module is able to, called by WriteString()

NU32_serial_example.c in the .zip file above demonstrates how to use the functions.

Sample Code

To initialize the serial communication, call initSerialNU32():

  initSerialNU32();

To write a string to the computer, use WriteString(UART1, charArray). The special characters '\r' and '\n' are carriage return and newline. Using them together puts the cursor on the next line.

  WriteString(UART1, "\r\nHello World!\r\n");

To write a string with the value of a variable in it, use sprintf(RS232_Out_Buffer,charArray) and WriteString(UART1, charArray). RS232_Out_Buffer is declared in NU32.h as a char array with 32 elements.

  sprintf(RS232_Out_Buffer, "the value of i is: %d\r\n", i);
  WriteString(UART3, RS232_Out_Buffer);

To receive characters in an interrupt, use this interrupt service routine (ISR):

void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) {
  // Is this an RX interrupt?
  if(INTGetFlag(INT_SOURCE_UART_RX(UART1))){
    char data = UARTGetDataByte(UART1);

    // now do something with data

    // Clear the RX interrupt Flag
    INTClearFlag(INT_SOURCE_UART_RX(UART1)); 
  }

  // If the interrupt was on TX (ignore it)
  if(INTGetFlag(INT_SOURCE_UART_TX(UART1))) {
    INTClearFlag(INT_SOURCE_UART_TX(UART1));
  }
}

NU32_serial_example.c initializes the serial communication, and then waits until it receives the character 'a' from the computer. When it gets an 'a', it replies back with numbers 0 through 9, each on their own line, and then waits for another 'a'.

//////LIBRARIES
#include <plib.h> // for PIC specific functions
#include "NU32.h" // for NU32 board specific functions

//////GLOBAL VARIABLES

//////FUNCTION PROTOTYPES

//////MAIN
int main(void)
{
    startup(); // from NU32.c, maximizes performance
    initialize(); // from NU32.c, inits USER, L1 and L2, serial fns

    // infinite loop
    while(1)
    {
        // nothing, do everything in the UART1 ISR
    }
    return 0;
}

//////FUNCTIONS

//////ISRs
// interrupt when the NU32 gets a character from the computer
void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void)
{
    int i = 0;

    // Is this an RX interrupt?
    if(INTGetFlag(INT_SOURCE_UART_RX(UART1)))
    {
        // read the byte
        char data = UARTGetDataByte(UART1);

        // if the byte is an 'a', send back 0-9
        if (data == 'a') {
            for (i=0; i<10; i++) {
                sprintf(RS232_Out_Buffer,"%d\r\n", i);
                WriteString(UART1, RS232_Out_Buffer);
            }
        }

        // Clear the RX interrupt Flag
        INTClearFlag(INT_SOURCE_UART_RX(UART1));
    }

    // We don't care about TX interrupt
    if(INTGetFlag(INT_SOURCE_UART_TX(UART1)))
    {
        INTClearFlag(INT_SOURCE_UART_TX(UART1));
    }
}

Communicating with NU32_Utility

The right hand side of the NU32_Utility program is a debugger that will display any character received from the 'Debug' port, and will send any characters in the text entry using 'Send'.

Compile the code and put it on your NU32. Send an 'a' with the debugger and you should see the following:

NU32 utility serial ex.png


Communicating with a Terminal Program

The NU32_Utility debugger is based on a more traditional Terminal emulator program. Terminal programs let you choose the baud rate, flow control, and many other aspects of serial communication, and let you view the incoming bytes as ASCII text or hex bytes. A Terminal program is useful when you want to see hidden characters that the NU32_Utility can not display.

On Windows, you can use PuTTY or RealTerm.

On a MAC, you can use iTerm2.

When you use a Terminal program, make sure that you know the communication port name and baud before opening the program. Also be sure that the port is not already open in another program. A port can only be open in one place at a time!

The Terminal programs vary in use, but for example, launch Putty.exe. Set up your communication by:

  • Selecting the 'Serial' radio button

Select the 'Serial' branch in the tree on the bottom left.

  • Enter the name of your COM port in 'Serial line to connect to'
  • Enter 115200 in 'Speed (baud)'
  • Change 'Flow control' from 'XON/XOFF' to 'None'

Go back to the 'Session' screen by selecting the 'Session' in the top left of the tree.

  • Name your settings in 'Saved Sessions' and click 'Save'
  • Click the 'Open' button

Type an 'a'. Notice you do not get to see what you typed in! In this case it would be the responsibility of the NU32 to echo back any characters it receives so that you can verify what was typed in.

PuTTY serial ex.png


Processing

Processing is a free IDE that is easy to use, makes nice graphics, and easily performs serial communication. The NU32_Utility was written in Processing. See the Processing page for more specific information on Processing.

The following code is a simple Processing sketch that will open a serial port, print any characters received to the Debug window, and send an 'a' if a key on the keyboard is pressed and released:

// Based on the SimpleRead example
import processing.serial.*;

Serial myPort;  // Create object from Serial class

// setup() runs first but only once, use it to initialize things
void setup() 
{
  size(200, 200); // the size of the Processing window in pixels
  background(255); // Set background to white
  println(Serial.list()); // print all of the serial ports that
                          // are available, you need to know what
                          // port to open in the next line
  // guess which port to open, if you get it wrong rerun the progam
  // after changing which port you open
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 115200); // open the port
}

// draw runs at 30fps (default)
void draw()
{
  if ( myPort.available() > 0) {  // If data is available,
    println(myPort.read()); // read it and store it in val
  }
}

// if a key is pressed, on release send an 'a'
void keyReleased() {
  myPort.write('a');
}

The following is output in the Debug window:

Processing serial ex1.png


What is that? Note that there are 30 characters. Processing has printed the decimal value of each character received!

Try something else- Processing has the ability to check for incoming characters in parallel to running code in the Draw() routine:

// Based on the SimpleRead example
import processing.serial.*;

Serial myPort;  // Create object from Serial class

// setup() runs first but only once, use it to initialize things
void setup() 
{
  size(200, 200); // the size of the Processing window in pixels
  background(255); // Set background to white
  println(Serial.list()); // print all of the serial ports that
                          // are available, you need to know what
                          // port to open in the next line
  // guess which port to open, if you get it wrong rerun the progam
  // after changing which port you open
  String portName = Serial.list()[1];
  myPort = new Serial(this, portName, 115200); // open the port
}

// draw runs at 30fps (default)
void draw()
{
  // nothing, do it in the serialEvent
}

void serialEvent(Serial thisPort) {
  // read a byte from the port:
  int inByte = thisPort.read();
  println((char)inByte);
}

// if a key is pressed, on release send an 'a'
void keyReleased() {
  myPort.write('a');
}


Processing serial ex2.png


Using

  int inByte = thisPort.read();
  println((char)inByte);

shows the actual ASCII values for the numbers, but the '\r' and '\n' are not displayed! This is why it is sometimes nice to use the Terminal emulator.

MATLAB

MATLAB can open a serial port and send and receive data as well. This is useful when you need to plot data, perform a complicated mathematical function, or error check a function that you wrote in C to the function in MATLAB.

To open a port in MATLAB, be sure to check if ports were not closed correctly, then try to open them:

    % check to see if any ports are open
    % matlab is easily hung up if 
    % the port is already open
    if ~isempty(instrfind)
        fclose(instrfind);
        delete(instrfind);
    end
    
    % open up the port
    COM = serial('COM14');
    set(COM,'BaudRate',115200);
    set(COM,'OutputBufferSize',100);
    % set how long to wait when
    % doing fscanf
    set(COM,'Timeout',20);
    fopen(COM);

To send data:

    fwrite(COM,'a');

To receive data:

    reading = fscanf(COM,'%i');

Put together, the code looks like:

    % clear the screen and memory
    clc;
    clear;

    % check to see if any ports are open
    % matlab is easily hung up if 
    % the port is already open
    if ~isempty(instrfind)
        fclose(instrfind);
        delete(instrfind);
    end
    
    % open up the port
    COM = serial('COM14');
    set(COM,'BaudRate',115200);
    set(COM,'OutputBufferSize',100);
    % set how long to wait when
    % doing fscanf
    set(COM,'Timeout',20);
    fopen(COM);
      
    % send an 'a'
    fwrite(COM,'a');
    % print to the screen 
    % that we sent an 'a'
    disp('sent a');
    
    % we know to expect 10 numbers
    i = 0;
    while (i<10)
        % read the number and print
        % to the screen
        reading = fscanf(COM,'%i');
        disp(num2str(reading));
        i = i+1;
    end
    
    % be sure to close the port
    fclose(COM);
    disp('done');

The result looks like:

Matlab serial ex.png


More Information

NA