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) * | javax.comm.zip | 35 kB |
Serial communication in C/C++ (Windows) ** | serial.zip | 13 kB |
Terminal for Windows (author unknown) | SerialPorts.exe | 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