Applications involving microcontrollers, actuators or sensors often require serial communication (RS232) with a PC. I have experimented with the Java, C/C++, and C# code below. The executable is a versatile terminal program for Windows ideal for testing.
| Serial communication in Java (until Windows XP) * |
| 35 kB |
| Serial communication in C/C++ (Windows) ** |
| 13 kB |
| Terminal for Windows (author unknown) |
| 180 kB |
Erfahrung heißt gar nichts.
Man kann seine Sache auch 35 Jahre schlecht machen.
Kurt Tucholsky
Independent of the operating system, I require the following intermediate serial port handling strategy:
Table of contents:
Code serial.hpp adapted from Thierry Schneider. The following C++ code was compiled with Microsoft 32-bit C/C++ Compiler 15. The command line compiler instruction is
cl /EHsc /nologo /I "D:\work\cpp\_utils" javacom.cpp /link ws2_32.lib
All source code and binary included in javacom.zip.
#define NDEBUG
#include <WinSock2.h>
#include <process.h>
#include <iostream>
#include "serial.hpp"
using namespace std;
int running=1;
DCB dcb;
HANDLE hnd;
int hexchar(char chr) {
if ('0'<=chr && chr<='9')
return chr-'0';
if ('A'<=chr && chr<='F')
return chr+10-'A';
if ('a'<=chr && chr<='f')
return chr+10-'a';
running=0;
return -1;
}
unsigned __stdcall serial_tx_thread(void* arg) {
int count=0;
char chr;
while (running) {
int msg=cin.get(); // converts 0x1a into EOF
running=msg!=EOF;
if (running)
if (count) {
chr+=hexchar(msg);
int cnt=serial_send(hnd,&chr,1);
running=cnt==1;
count=0;
} else {
chr=hexchar(msg)<<4;
++count;
}
}
return 0;
}
#define RX_BUFFER_SIZE 32
int main(int argc, char* argv[]) {
if (2<argc) {
serial_init(dcb,atoi(argv[2]),NOPARITY);
hnd=serial_open(dcb,atoi(argv[1]));
if (0<=(int)hnd) {
void* arg=0;
unsigned ret;
_beginthreadex(0,0,serial_tx_thread,(void*) arg,0,&ret);
char msg[RX_BUFFER_SIZE];
while (running) {
int poll=serial_poll(hnd);
if (0<poll) {
poll=min(poll,RX_BUFFER_SIZE);
int res=serial_read(hnd,msg,poll);
for (int c0=0;c0<res;++c0)
printf("%02x",(unsigned char)msg[c0]);
cout.flush();
} else
Sleep(1L); // this is necessary otherwise cpu goes up
}
serial_close(hnd);
}
}
return 0;
}
In case you wish to control the serial port via TCP/IP, I recommend the C++ Socket Class for Windows by René Nyffenegger.
Ian Kamajaya ported the above C++ code to C#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
namespace Javacom {
class Program {
static bool running = true;
private static Thread serial_tx_thread;
static System.IO.Ports.SerialPort UARTport = new System.IO.Ports.SerialPort();
private static int hexnum(int num) {
if ('0' <= num && num <= '9')
return num - '0';
if ('A' <= num && num <= 'F')
return num + 10 - 'A';
if ('a' <= num && num <= 'f')
return num + 10 - 'a';
running = false;
return -1;
}
private static void SerialTxThread() {
bool odd = false;
byte[] by_tx = new byte[1];
while (running) {
int msg = System.Console.Read();
if (msg == 0x1A)
running = false;
if (running) {
if (odd) {
if (msg > 32) {
by_tx[0] += (byte)hexnum(msg);
UARTport.Write(by_tx, 0, 1);
odd = false;
}
} else {
if (msg > 32) {
by_tx[0] = (byte)(hexnum(msg) << 4);
odd = true;
}
}
}
}
}
static void Main(string[] args) {
if (1 < args.Length) {
byte[] uart_received_buffer = new byte[2048];
byte[] uart_send_buffer = new byte[2048];
int length;
UARTport.PortName = "COM" + args[0];
UARTport.BaudRate = Convert.ToInt32(args[1]);
UARTport.Open();
serial_tx_thread = new Thread(new ThreadStart(SerialTxThread));
serial_tx_thread.IsBackground = true;
serial_tx_thread.Start();
while (running) {
length = UARTport.BytesToRead;
if (0 < length) {
UARTport.Read(uart_received_buffer, 0, length);
for (int i = 0; i < length; ++i)
System.Console.Write(uart_received_buffer[i].ToString("x2"));
} else
System.Threading.Thread.Sleep(1);
}
serial_tx_thread.Abort();
UARTport.Close();
}
}
}
}
Under Linux serial communications works as a root used
sudo stty raw crtscts -echo ispeed 9600 ospeed 9600 -F /dev/ttyUSB0
compile and run for instance with
g++ javacom.cpp -o javacom -lpthread ./javacom /dev/ttyUSB0 9600
provides output stream listens to input stream minimal cpu thanks to threading resonsiveness can be adjusted by changing the sleep duration cpu
#include <iostream>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <sys/io.h>
#include <unistd.h>
using namespace std;
int running = 1;
int tty_fid;
void* serial_tx_thread(void *ptr) {
while (running) {
int msg = cin.get();
running = msg != -1; // assumption that EOF == -1 (see libio.h)
if (running) {
char chr = msg;
ssize_t cnt = write(tty_fid, &chr, 1);
running = cnt == 1;
}
}
return 0;
}
#define RX_BUFFER_SIZE 32
// compile with
// g++ javacom.cpp -o javacom -lpthread
// example how to run
// ./javacom /dev/ttyUSB0 115200
int main(int argc, char** argv) {
if (2 < argc) {
tty_fid = open(argv[1], O_RDWR | O_NOCTTY | O_NDELAY);
if (tty_fid == -1)
return 1;
char command[100];
sprintf(command,"stty raw -crtscts -echo ispeed %s ospeed %s -F %s",argv[2],argv[2],argv[1]);
system(command);
fcntl(tty_fid, F_SETFL, O_NONBLOCK);
pthread_t thread;
void* arg = 0;
int ret = pthread_create(&thread, NULL, serial_tx_thread, arg);
// ---
char msg[RX_BUFFER_SIZE];
while (running) {
ssize_t tty_read = read(tty_fid, msg, RX_BUFFER_SIZE);
if (0 < tty_read) {
for (int c0 = 0; c0 < tty_read; ++c0)
cout << msg[c0];
cout.flush();
}
usleep(2000); // in virtual machine this results in 2-3% cpu
}
// pthread_join(thread, NULL);
close(tty_fid);
}
return 0;
}
In order to handle serial port communication in a non-blocking fashion on the c8051 microcontroller, we use the interrupt callback to send and receive messages. The source code below was tested on the chip model C8051F120.
// code adapted from CodeVision AVR by HP
#include <c8051F120.h>
#include "C8051_UART0.h"
// the buffer and buffer size are set in the open function and shall not
// be changed until closing the uart
char* UART0_rx;
short UART0_rx_size;
char* UART0_tx;
short UART0_tx_size;
// these are private variables managed by the uart
short UART0_rx_uart;
short UART0_rx_main;
short UART0_rx_available;
short UART0_tx_uart = 0;
short UART0_tx_main = 0;
short UART0_tx_available = 0;
char UART0_rx_errorCount = 0;
char UART0_tx_errorCount = 0;
// private function
void UART0_resetRx() {
bit ES0_SAVE = ES0;
ES0 = 0;
UART0_rx_uart = 0;
UART0_rx_main = 0;
UART0_rx_available = 0;
ES0 = ES0_SAVE;
}
void UART0_open(char* rx_buffer, short rx_size, char* tx_buffer, short tx_size, char p_uc_SSTA0) {
char SFRPAGE_SAVE = SFRPAGE;
char dummy;
UART0_rx = rx_buffer;
UART0_rx_size = rx_size;
UART0_tx = tx_buffer;
UART0_tx_size = tx_size;
UART0_close();
UART0_resetRx();
SFRPAGE = UART0_PAGE;
SCON0 = 0x50; // 0101 0000
SSTA0 = p_uc_SSTA0;
// Indicate TX0 ready
TI0 = 0;
RI0 = 0;
ES0 = 1; // enable UART0 interrupt
SFRPAGE = SFRPAGE_SAVE;
}
void UART0_close() {
char SFRPAGE_SAVE = SFRPAGE;
SFRPAGE = UART0_PAGE;
ES0 = 0;
TI0 = 0;
RI0 = 0;
UART0_rx_available = 0;
UART0_tx_uart = 0;
SFRPAGE = SFRPAGE_SAVE;
}
short UART0_getRxAvailable() {
short value;
bit ES0_SAVE = ES0;
ES0 = 0;
value = UART0_rx_available;
ES0 = ES0_SAVE;
return value;
}
// returns false if not sufficient data in buffer
char UART0_pollChars(void* ptr, short length) {
char* message = ptr;
short count;
if (length <= UART0_getRxAvailable()) {
for (count = 0; count < length; ++count)
message[count] = UART0_rx[(UART0_rx_main + count) % UART0_rx_size];
return 1;
}
return 0;
}
void UART0_advance(short length) {
bit ES0_SAVE = ES0;
ES0 = 0;
UART0_rx_available -= length;
UART0_rx_main += length;
UART0_rx_main %= UART0_rx_size;
ES0 = ES0_SAVE;
}
void UART0_putChars(void* ptr, short length) {
short count;
char* message = (char*) ptr;
for (count = 0; count < length; ++count)
UART0_putChar(message[count]);
}
void UART0_putChar(char value) {
char SAVE_SFRPAGE;
bit ES0_SAVE;
while (UART0_tx_available == UART0_tx_size)
;
ES0_SAVE = ES0;
ES0 = 0;
if (UART0_tx_available) {
UART0_tx[UART0_tx_main] = value;
++UART0_tx_main;
if (UART0_tx_main == UART0_tx_size)
UART0_tx_main = 0;
} else {
SAVE_SFRPAGE = SFRPAGE;
SFRPAGE = UART0_PAGE;
SBUF0 = value;
SFRPAGE = SAVE_SFRPAGE;
}
++UART0_tx_available;
ES0 = ES0_SAVE;
}
UART0_ISR(void) interrupt (4) {
if (RI0 == 1) {
RI0 = 0;
// data will be loaded in SBUF0 if
// RI0 == 0 && (SM20 == 1 => StopBit == 1)
if (SSTA0 & 0xC0) { // 0xC0 = 1100 0000 frame error and receiver overflow
++UART0_rx_errorCount;
SSTA0 &= 0x3F; // Clear FE0 & RXOV0 flags
} else {
UART0_rx[UART0_rx_uart] = SBUF0; // Read a character from UART0 Data Buffer
++UART0_rx_uart;
if (UART0_rx_uart == UART0_rx_size)
UART0_rx_uart = 0;
++UART0_rx_available;
// we do not check if buffer overflow
}
}
if (TI0 == 1) { // cause of interrupt: previous tx is finished
TI0 = 0;
if (SSTA0 & 0x20) { // tx collision error
++UART0_tx_errorCount;
SSTA0 &= 0xDF;
}
--UART0_tx_available; // Decrease array size
if (UART0_tx_available) { // If buffer not empty
SBUF0 = UART0_tx[UART0_tx_uart]; // Transmit
++UART0_tx_uart; // Update counter
if (UART0_tx_uart == UART0_tx_size)
UART0_tx_uart = 0;
}
}
}
char UART0_getRxErrorCount() {
char count;
bit ES0_SAVE;
ES0_SAVE = ES0;
ES0 = 0;
count = UART0_rx_errorCount;
ES0 = ES0_SAVE;
return count;
}
char UART0_getTxErrorCount() {
char count;
bit ES0_SAVE;
ES0_SAVE = ES0;
ES0 = 0;
count = UART0_tx_errorCount;
ES0 = ES0_SAVE;
return count;
}
Engineers cheat in order to get results.
Mathematicians work on toy problems in order to get results.
Program verifiers cheat on toy problems in order to get results.
Anonymous