Difference between revisions of "NU32: Serial Communication with the PC"
NickMarchuk (talk | contribs) |
|||
(34 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
'''THIS PAGE REFERS TO A PRE-RELEASE VERSION OF THE NU32 PIC32 DEVELOPMENT BOARD. FOR INFORMATION, SAMPLE CODE, AND VIDEOS RELATED TO THE PRODUCTION VERSION (2016 AND LATER), AND TO THE CORRESPONDING BOOK "EMBEDDED COMPUTING AND MECHATRONICS WITH THE PIC32 MICROCONTROLLER," VISIT [[NU32|THE NU32 PAGE]].''' |
|||
'''-----***UNDER CONSTRUCTION NDM 1/17***-----''' |
|||
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. |
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. |
||
Line 5: | Line 6: | ||
== Overview == |
== Overview == |
||
The NU32 uses an on board [http://www.ftdichip.com/Products/ICs/ |
The NU32 uses an on board [http://www.ftdichip.com/Products/ICs/FT2232H.htm FT2232H] chip from FTDI to convert [http://en.wikipedia.org/wiki/RS-232 RS-232 TTL serial communication] to USB. Using drivers from FTDI, the communication can be opened by any program that can access a [http://en.wikipedia.org/wiki/Serial_port 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, Python, and terminal programs. |
||
== Details == |
== Details == |
||
The |
The FT2232H chip is hardwired to the 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 (bits per second) 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 code below, but this can be changed if desired. |
|||
The NU32 uses the UART1 and UART4 modules at 115200 baud, as does the sample code below. |
|||
== Library Functions == |
|||
[[Media:NU32_serial_example.zip | This code]] contains functions to read characters and write strings over the serial port. |
|||
== Library Functions == |
|||
NU32.h contains function prototypes, in addition to the serial functions, to initialize the LEDs on the NU32 board as well as the bootloader button. |
|||
[[Media:NU32SerialExample.zip | 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 |
*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: |
The functions that control the serial communication are: |
||
*void |
*void NU32_Initialize(void) - enables UART1 and UART4 at 115200 baud. UART4 starts with the interrupt at priority level 3, UART1 starts with no interrupt enabled |
||
*void NU32_EnableUART1Interrupt(void) - enables the UART1 interrupt |
|||
*void WriteString(UART_MODULE, const char *) - call with UART1 and the character array you wish to send |
|||
*void |
*void NU32_WriteUART1(const char *) - call with the character array you wish to send, make the array with sprintf() |
||
*void NU32_ReadUART1(char *, int) - call with the array you want to read into, and the maximum size of the array. The code will wait at this function and buffer all characters received until you send a '\r' or '\n' (by pressing [enter] on your keyboard) |
|||
NU32SerialExample.c in the .zip file above demonstrates how to use the functions. |
|||
NU32_serial_example.c contains example code that shows how to use the functions. It also contains the ISR for UART1, with an example of how to receive characters. |
|||
== Sample Code == |
== Sample Code == |
||
To initialize the serial communication, call |
To initialize the serial communication, call NU32_Initialize(), which internally calls NU32_InitializeLEDs() and NU32_InitializeSerialPort(): |
||
<pre> |
<pre> |
||
NU32_Initialize(); |
|||
</pre> |
</pre> |
||
To write a string to the computer, use |
To write a string to the computer, use NU32_WriteUART1(charArray). The special characters '\r' and '\n' are carriage return and newline. Using them together puts the cursor on the next line. |
||
<pre> |
<pre> |
||
NU32_WriteUART1("\r\nHello World!\r\n"); |
|||
</pre> |
</pre> |
||
To write a string with the value of a variable in it, use sprintf( |
To write a string with the value of a variable in it, use sprintf(charArray,"%d") and NU32_WriteUART1(charArray). |
||
<pre> |
<pre> |
||
char charArray[32]; |
|||
sprintf(RS232_Out_Buffer, "the value of i is: %d\r\n", i); |
|||
sprintf(charArray, "the value of i is: %d\r\n", i); |
|||
WriteString(UART3, RS232_Out_Buffer); |
|||
NU32_WriteUART1(charArray); |
|||
</pre> |
</pre> |
||
To receive characters |
To receive characters from the computer without an interrupt, use NU32_ReadUART1(charArray, numberElementsInCharArray): |
||
<pre> |
|||
char charArray[32]; |
|||
NU32_ReadUART1(charArray, 32); |
|||
</pre> |
|||
This function will run until the computer sends '\r' or '\n', and will put all characters received until then into charArray. Then you can use sscanf() to parse out the message in the array. For example, if the user entered '100 200\n', the next lines in your program might be: |
|||
<pre> |
|||
int var1, var2; |
|||
sscanf(charArray, "%d %d", &var1, &var2) |
|||
</pre> |
|||
Now var1 = 100 and var2 = 200. |
|||
To receive characters in an interrupt, enable the interrupt with NU32_EnableUART1Interrupt(): |
|||
<pre> |
|||
NU32_EnableUART1Interrupt(); |
|||
</pre> |
|||
And use this interrupt service routine (ISR): |
|||
<pre> |
<pre> |
||
void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) { |
void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) { |
||
// Is this an RX interrupt? |
// Is this an RX interrupt? |
||
if(INTGetFlag(INT_SOURCE_UART_RX(UART1))){ |
if (INTGetFlag(INT_SOURCE_UART_RX(UART1))) { |
||
char data = UARTGetDataByte(UART1); |
char data = UARTGetDataByte(UART1); |
||
// |
// do something with the data, like echo it back |
||
PutCharacter(UART1, data); // echo |
|||
// Clear the RX interrupt Flag |
// Clear the RX interrupt Flag |
||
INTClearFlag(INT_SOURCE_UART_RX(UART1)); |
INTClearFlag(INT_SOURCE_UART_RX(UART1)); |
||
} |
} |
||
// We don't care about TX interrupt |
// We don't care about TX interrupt |
||
if(INTGetFlag(INT_SOURCE_UART_TX(UART1))) { |
if (INTGetFlag(INT_SOURCE_UART_TX(UART1))) { |
||
INTClearFlag(INT_SOURCE_UART_TX(UART1)); |
INTClearFlag(INT_SOURCE_UART_TX(UART1)); |
||
} |
} |
||
Line 68: | Line 95: | ||
</pre> |
</pre> |
||
NU32SerialExample.c initializes the serial communication, and waits to receive some text from the computer. It echos that text back after [enter] is pressed, then it initializes the interrupt and echos charaters back as they show up. |
|||
== PuTTY == |
|||
PuTTY is a terminal program that will let you open the virtual serial port created by the FTDI driver. |
|||
<pre> |
|||
Directions to install PuTTY can be found in [[NU32: Software to Install#PuTTY Terminal Emulator]] |
|||
//////LIBRARIES |
|||
#include <plib.h> // for PIC specific functions |
|||
#include "NU32.h" // for NU32 board specific functions |
|||
//////GLOBAL VARIABLES |
|||
// none used here |
|||
//////FUNCTION PROTOTYPES |
|||
//////MAIN |
|||
int main(void) { |
|||
char messageArray[100]; // char array for a received message |
|||
int max_message_size = 100; // max number of chars in the array |
|||
NU32_Startup(); // from NU32.c, maximizes performance |
|||
NU32_Initialize(); // from NU32.c, inits NU32USER, NU32LED1, NU32LED2, RS232 |
|||
// send a string to the computer |
|||
// first you have to use sprintf to make the string |
|||
sprintf(messageArray, "Initialization complete.\r\n"); // '\r\n' move cursor to next line |
|||
NU32_WriteUART1(messageArray); |
|||
// you can send the contents of a variable by using sprintf |
|||
int k = 12; |
|||
sprintf(messageArray, "k = %d\r\n", k); |
|||
NU32_WriteUART1(messageArray); |
|||
// get a message from UART1 and put it into messageArray |
|||
// the code will wait and buffer all characters until it gets '\r' or '\n' |
|||
sprintf(messageArray, "Enter text followed by [enter]: "); |
|||
NU32_WriteUART1(messageArray); |
|||
// the message will be put into messageArray |
|||
// send the max number of chars in the array so that you don't |
|||
// accidentally run over memory |
|||
NU32_ReadUART1(messageArray, max_message_size); |
|||
// now that messageArray has charaters in it, you can parse it using sscanf |
|||
// for example, if 2 integers were sent over seperated by a space: |
|||
//num_ints_in_string = sscanf(messageArray,"%d %d",&int1,&int2); |
|||
// would put the 2 integers into int1 and int2, |
|||
// and num_ints_in_string would be 2, for the number of variables |
|||
// the sscanf was able to read |
|||
// now we can send what was received back to the user: |
|||
NU32_WriteUART1(messageArray); |
|||
// the other way to use UART1 to receive data is to use the interrupt |
|||
// turn on UART1 interrupt |
|||
// now each received charater generates an ISR |
|||
// so don't call NU32_ReadUART1 anymore |
|||
sprintf(messageArray, "\r\nTurning on interrupt:\r\n"); |
|||
NU32_WriteUART1(messageArray); |
|||
NU32_EnableUART1Interrupt(); |
|||
// infinite loop, but UART1 IRS will echo back all characters received |
|||
while (1) {} |
|||
return 0; |
|||
} |
|||
//////FUNCTIONS |
|||
//////ISRs |
|||
// interrupt when UART1 gets a character from the computer |
|||
// this will only happen if you have called NU32_EnableUART1Interrupt() |
|||
void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) { |
|||
// Is this an RX interrupt? |
|||
if (INTGetFlag(INT_SOURCE_UART_RX(UART1))) { |
|||
char data = UARTGetDataByte(UART1); |
|||
// do something with the data, like echo it back |
|||
PutCharacter(UART1, data); // echo |
|||
// 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)); |
|||
} |
|||
} |
|||
</pre> |
|||
== 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'. |
|||
[[Image:NU32_utility_serial_ex.png|thumb|300 px|left]]<br clear=all> |
|||
== Communicating with a Terminal Program == |
|||
The NU32_Utility debugger is based on a more traditional [http://en.wikipedia.org/wiki/Terminal_emulator 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 [http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html PuTTY] or [http://realterm.sourceforge.net/index.html#downloads_Download RealTerm]. |
|||
On a MAC, you can try [http://homepage.mac.com/dalverson/zterm/ ZTerm]. |
|||
In Linux people commonly use [https://help.ubuntu.com/community/Minicom minicom] or [https://fedorahosted.org/gtkterm/ gtkterm]. |
|||
Before launching PuTTY, note the name of your COM port, same name as you use in the NU32 Utility Application for debugging (not bootloading). |
|||
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 |
*Selecting the 'Serial' radio button |
||
Line 87: | Line 221: | ||
*Name your settings in 'Saved Sessions' and click 'Save' |
*Name your settings in 'Saved Sessions' and click 'Save' |
||
*Click the 'Open' button |
*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. |
|||
[[Image:PuTTY_serial_ex.png|thumb|300 px|left]]<br clear=all> |
|||
== Processing == |
== Processing == |
||
[http://processing.org/ 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. |
|||
'''**To Do - Processing basics, see [[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: |
|||
The [[Media:NU32v2_stripchart.zip | NU32v2 Stripchart plotting tool]] can be used to plot 1000 data points in each of six traces, with values between 0 and 511, where each new data point is added to the right side of the plot, stripchart style. For example, |
|||
<pre> |
<pre> |
||
// Based on the SimpleRead example |
|||
for (i=0; i<1000; i++) { |
|||
import processing.serial.*; |
|||
// plot on trace a the refValue and on trace b the ADC value |
|||
sprintf(RS232_Out_Buffer, "%d %d\r\n", refValue[i]/2, adcValue[i]/2); // max adc is 1023, max plotted value is 511, |
|||
// so divide by 2 before sending |
|||
WriteString(UART1, RS232_Out_Buffer); |
|||
} |
|||
</pre> |
|||
Serial myPort; // Create object from Serial class |
|||
The [[Media:NU32v2_plot.zip | NU32v2 Plotting tool]] can be used to plot 1000 data points in each of four traces, with values between 0 and 511. |
|||
// setup() runs first but only once, use it to initialize things |
|||
To plot the values of an array of 1000 integers to trace a, use something like: |
|||
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) |
|||
<pre> |
|||
void draw() |
|||
for (i=0; i<1000; i++) { |
|||
{ |
|||
sprintf(RS232_Out_Buffer, "a %d %d\r\n", i, adcValue[i]/2); // max adc is 1023, max plotted value is 511, |
|||
if ( myPort.available() > 0) { // If data is available, |
|||
// so divide by 2 before sending |
|||
println(myPort.read()); // read it and store it in val |
|||
WriteString(UART1, RS232_Out_Buffer); |
|||
} |
|||
} |
|||
// if a key is pressed, on release send an 'a' |
|||
void keyReleased() { |
|||
myPort.write('a'); |
|||
} |
|||
</pre> |
</pre> |
||
The following is output in the Debug window: |
|||
Typing a key while the tool is running will send that character to the microcontroller. |
|||
[[Image:Processing_serial_ex1.png|thumb|300 px|left]]<br clear=all> |
|||
To write a message to the tool from the microcontroller, '''send a string that does not start with a, b, c or d'''. |
|||
What is that? Note that there are 30 characters. Processing has printed the decimal value of each character received! |
|||
For example, to change the value of a control gain when the microcontroller receives an 'a' and return the new value of the control gain, try: |
|||
Try something else- Processing has the ability to check for incoming characters in parallel to running code in the Draw() routine: |
|||
<pre> |
<pre> |
||
// Based on the SimpleRead example |
|||
void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) { |
|||
import processing.serial.*; |
|||
// Is this an RX interrupt? |
|||
if(INTGetFlag(INT_SOURCE_UART_RX(UART1))){ |
|||
char data = UARTGetDataByte(UART1); |
|||
Serial myPort; // Create object from Serial class |
|||
// now do something with data |
|||
if (data == 'a') { // increase kp |
|||
kp=kp+0.01; |
|||
sprintf(RS232_Out_Buffer, " kp=%4.2f\r\n", kp); |
|||
WriteString(UART1, RS232_Out_Buffer); |
|||
} |
|||
// setup() runs first but only once, use it to initialize things |
|||
// Clear the RX interrupt Flag |
|||
void setup() |
|||
INTClearFlag(INT_SOURCE_UART_RX(UART1)); |
|||
{ |
|||
} |
|||
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) |
|||
// We don't care about TX interrupt |
|||
void draw() |
|||
if(INTGetFlag(INT_SOURCE_UART_TX(UART1))) { |
|||
{ |
|||
INTClearFlag(INT_SOURCE_UART_TX(UART1)); |
|||
// 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'); |
|||
} |
|||
</pre> |
</pre> |
||
[[Image:Processing_serial_ex2.png|thumb|300 px|left]]<br clear=all> |
|||
Using |
|||
<pre> |
|||
int inByte = thisPort.read(); |
|||
println((char)inByte); |
|||
</pre> |
|||
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 == |
||
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 Do**''' |
|||
To open a port in MATLAB, be sure to check if ports were not closed correctly, then try to open them: |
|||
<pre> |
|||
% 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); |
|||
</pre> |
|||
To send data: |
|||
<pre> |
|||
fwrite(COM,'a'); |
|||
</pre> |
|||
To receive data: |
|||
<pre> |
|||
reading = fscanf(COM,'%i'); |
|||
</pre> |
|||
Put together, the code looks like: |
|||
<pre> |
|||
% 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'); |
|||
</pre> |
|||
The result looks like: |
|||
[[Image:Matlab_serial_ex.png|thumb|300 px|left]]<br clear=all> |
|||
== Python == |
|||
For those familiar with Python, its [http://pyserial.sf.net/ serial library] can provide several useful, platform-independent functions for interfacing with the NU32 board. The latest installation comes with a terminal emulator written in Python called [http://pyserial.sourceforge.net/examples.html#miniterm miniterm] (source code [http://pyserial.svn.sourceforge.net/viewvc/*checkout*/pyserial/trunk/pyserial/serial/tools/miniterm.py here]). To run miniterm with the NU32 you would run a command on the command line similar to |
|||
<pre> |
|||
python miniterm.py -p COM7 -b 115200 -e |
|||
</pre> |
|||
Note that the argument after the "-p" will be different depending on which port the NU32 is attached to on your computer. On a MAC it would be something like "/DEV/TTY.USBSERIAL-000030FDA" and in Linux it might be "/dev/ttyUSB0". Also note that the "-e" argument is for turning on local echo. In other words, when you hit a key it will show up in the output window. |
|||
The following is a piece of sample Python code that replicates the other serial examples from above: |
|||
<pre> |
|||
## First we import the system module |
|||
import sys |
|||
## First thing that we need to do is import the serial library: |
|||
try: |
|||
import serial |
|||
except ImportError: |
|||
print "[FATAL] Serial library not found" |
|||
sys.exit(0) |
|||
## open comm port |
|||
com = serial.Serial('COM7',baudrate=115200) |
|||
## send 'a' character |
|||
com.write('a') |
|||
print "sent 'a'" |
|||
## read in values sent by PIC |
|||
data = com.read(30) ## we read 30 characters because Python counts the |
|||
## \n and \r as characters |
|||
print "Incoming data:" |
|||
print data |
|||
## close serial port: |
|||
com.close() |
|||
</pre> |
|||
== More Information == |
== More Information == |
||
NA |
|||
More detailed information on how the the serial module on the PIC32 can be configured is on this page.'''**To Do**''' |
Latest revision as of 05:34, 16 January 2016
THIS PAGE REFERS TO A PRE-RELEASE VERSION OF THE NU32 PIC32 DEVELOPMENT BOARD. FOR INFORMATION, SAMPLE CODE, AND VIDEOS RELATED TO THE PRODUCTION VERSION (2016 AND LATER), AND TO THE CORRESPONDING BOOK "EMBEDDED COMPUTING AND MECHATRONICS WITH THE PIC32 MICROCONTROLLER," VISIT THE NU32 PAGE.
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, Python, and terminal programs.
Details
The FT2232H chip is hardwired to the 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 (bits per second) 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 NU32_Initialize(void) - enables UART1 and UART4 at 115200 baud. UART4 starts with the interrupt at priority level 3, UART1 starts with no interrupt enabled
- void NU32_EnableUART1Interrupt(void) - enables the UART1 interrupt
- void NU32_WriteUART1(const char *) - call with the character array you wish to send, make the array with sprintf()
- void NU32_ReadUART1(char *, int) - call with the array you want to read into, and the maximum size of the array. The code will wait at this function and buffer all characters received until you send a '\r' or '\n' (by pressing [enter] on your keyboard)
NU32SerialExample.c in the .zip file above demonstrates how to use the functions.
Sample Code
To initialize the serial communication, call NU32_Initialize(), which internally calls NU32_InitializeLEDs() and NU32_InitializeSerialPort():
NU32_Initialize();
To write a string to the computer, use NU32_WriteUART1(charArray). The special characters '\r' and '\n' are carriage return and newline. Using them together puts the cursor on the next line.
NU32_WriteUART1("\r\nHello World!\r\n");
To write a string with the value of a variable in it, use sprintf(charArray,"%d") and NU32_WriteUART1(charArray).
char charArray[32]; sprintf(charArray, "the value of i is: %d\r\n", i); NU32_WriteUART1(charArray);
To receive characters from the computer without an interrupt, use NU32_ReadUART1(charArray, numberElementsInCharArray):
char charArray[32]; NU32_ReadUART1(charArray, 32);
This function will run until the computer sends '\r' or '\n', and will put all characters received until then into charArray. Then you can use sscanf() to parse out the message in the array. For example, if the user entered '100 200\n', the next lines in your program might be:
int var1, var2; sscanf(charArray, "%d %d", &var1, &var2)
Now var1 = 100 and var2 = 200.
To receive characters in an interrupt, enable the interrupt with NU32_EnableUART1Interrupt():
NU32_EnableUART1Interrupt();
And 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); // do something with the data, like echo it back PutCharacter(UART1, data); // echo // 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)); } }
NU32SerialExample.c initializes the serial communication, and waits to receive some text from the computer. It echos that text back after [enter] is pressed, then it initializes the interrupt and echos charaters back as they show up.
//////LIBRARIES #include <plib.h> // for PIC specific functions #include "NU32.h" // for NU32 board specific functions //////GLOBAL VARIABLES // none used here //////FUNCTION PROTOTYPES //////MAIN int main(void) { char messageArray[100]; // char array for a received message int max_message_size = 100; // max number of chars in the array NU32_Startup(); // from NU32.c, maximizes performance NU32_Initialize(); // from NU32.c, inits NU32USER, NU32LED1, NU32LED2, RS232 // send a string to the computer // first you have to use sprintf to make the string sprintf(messageArray, "Initialization complete.\r\n"); // '\r\n' move cursor to next line NU32_WriteUART1(messageArray); // you can send the contents of a variable by using sprintf int k = 12; sprintf(messageArray, "k = %d\r\n", k); NU32_WriteUART1(messageArray); // get a message from UART1 and put it into messageArray // the code will wait and buffer all characters until it gets '\r' or '\n' sprintf(messageArray, "Enter text followed by [enter]: "); NU32_WriteUART1(messageArray); // the message will be put into messageArray // send the max number of chars in the array so that you don't // accidentally run over memory NU32_ReadUART1(messageArray, max_message_size); // now that messageArray has charaters in it, you can parse it using sscanf // for example, if 2 integers were sent over seperated by a space: //num_ints_in_string = sscanf(messageArray,"%d %d",&int1,&int2); // would put the 2 integers into int1 and int2, // and num_ints_in_string would be 2, for the number of variables // the sscanf was able to read // now we can send what was received back to the user: NU32_WriteUART1(messageArray); // the other way to use UART1 to receive data is to use the interrupt // turn on UART1 interrupt // now each received charater generates an ISR // so don't call NU32_ReadUART1 anymore sprintf(messageArray, "\r\nTurning on interrupt:\r\n"); NU32_WriteUART1(messageArray); NU32_EnableUART1Interrupt(); // infinite loop, but UART1 IRS will echo back all characters received while (1) {} return 0; } //////FUNCTIONS //////ISRs // interrupt when UART1 gets a character from the computer // this will only happen if you have called NU32_EnableUART1Interrupt() void __ISR(_UART_1_VECTOR, ipl2) IntUart1Handler(void) { // Is this an RX interrupt? if (INTGetFlag(INT_SOURCE_UART_RX(UART1))) { char data = UARTGetDataByte(UART1); // do something with the data, like echo it back PutCharacter(UART1, data); // echo // 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'.
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 try ZTerm.
In Linux people commonly use minicom or gtkterm.
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.
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:
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'); }
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:
Python
For those familiar with Python, its serial library can provide several useful, platform-independent functions for interfacing with the NU32 board. The latest installation comes with a terminal emulator written in Python called miniterm (source code here). To run miniterm with the NU32 you would run a command on the command line similar to
python miniterm.py -p COM7 -b 115200 -e
Note that the argument after the "-p" will be different depending on which port the NU32 is attached to on your computer. On a MAC it would be something like "/DEV/TTY.USBSERIAL-000030FDA" and in Linux it might be "/dev/ttyUSB0". Also note that the "-e" argument is for turning on local echo. In other words, when you hit a key it will show up in the output window.
The following is a piece of sample Python code that replicates the other serial examples from above:
## First we import the system module import sys ## First thing that we need to do is import the serial library: try: import serial except ImportError: print "[FATAL] Serial library not found" sys.exit(0) ## open comm port com = serial.Serial('COM7',baudrate=115200) ## send 'a' character com.write('a') print "sent 'a'" ## read in values sent by PIC data = com.read(30) ## we read 30 characters because Python counts the ## \n and \r as characters print "Incoming data:" print data ## close serial port: com.close()
More Information
NA