mirror of
https://github.com/livingcomputermuseum/UniBone.git
synced 2026-01-28 12:49:08 +00:00
499 lines
9.9 KiB
C++
499 lines
9.9 KiB
C++
/*
|
|
***************************************************************************
|
|
*
|
|
* Author: Teunis van Beelen
|
|
*
|
|
* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Teunis van Beelen
|
|
*
|
|
* Email: teuniz@gmail.com
|
|
*
|
|
***************************************************************************
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
***************************************************************************
|
|
*/
|
|
|
|
/* 2019, June: made C++, added parity/frame/BREAK option, Joerg Hoppe */
|
|
/* Last revision Teunis van Beelen: November 22, 2017 */
|
|
|
|
/* For more info and how to use this library, visit: http://www.teuniz.net/RS-232/ */
|
|
|
|
#include "rs232.hpp"
|
|
|
|
rs232_c::rs232_c() {
|
|
TransmissionTime_us = 0;
|
|
}
|
|
|
|
// devname without leading "/dev/"
|
|
// returns 0 on success, else error
|
|
int rs232_c::OpenComport(const char *devname, int baudrate, const char *mode,
|
|
bool par_and_break) {
|
|
char full_devname[256];
|
|
|
|
int baudr;
|
|
int status;
|
|
|
|
strcpy(full_devname, "/dev/");
|
|
strncat(full_devname, devname, 255);
|
|
full_devname[255] = 0;
|
|
|
|
switch (baudrate) {
|
|
case 50:
|
|
baudr = B50;
|
|
break;
|
|
case 75:
|
|
baudr = B75;
|
|
break;
|
|
case 110:
|
|
baudr = B110;
|
|
break;
|
|
case 134:
|
|
baudr = B134;
|
|
break;
|
|
case 150:
|
|
baudr = B150;
|
|
break;
|
|
case 200:
|
|
baudr = B200;
|
|
break;
|
|
case 300:
|
|
baudr = B300;
|
|
break;
|
|
case 600:
|
|
baudr = B600;
|
|
break;
|
|
case 1200:
|
|
baudr = B1200;
|
|
break;
|
|
case 1800:
|
|
baudr = B1800;
|
|
break;
|
|
case 2400:
|
|
baudr = B2400;
|
|
break;
|
|
case 4800:
|
|
baudr = B4800;
|
|
break;
|
|
case 9600:
|
|
baudr = B9600;
|
|
break;
|
|
case 19200:
|
|
baudr = B19200;
|
|
break;
|
|
case 38400:
|
|
baudr = B38400;
|
|
break;
|
|
case 57600:
|
|
baudr = B57600;
|
|
break;
|
|
case 115200:
|
|
baudr = B115200;
|
|
break;
|
|
case 230400:
|
|
baudr = B230400;
|
|
break;
|
|
case 460800:
|
|
baudr = B460800;
|
|
break;
|
|
case 500000:
|
|
baudr = B500000;
|
|
break;
|
|
case 576000:
|
|
baudr = B576000;
|
|
break;
|
|
case 921600:
|
|
baudr = B921600;
|
|
break;
|
|
case 1000000:
|
|
baudr = B1000000;
|
|
break;
|
|
case 1152000:
|
|
baudr = B1152000;
|
|
break;
|
|
case 1500000:
|
|
baudr = B1500000;
|
|
break;
|
|
case 2000000:
|
|
baudr = B2000000;
|
|
break;
|
|
case 2500000:
|
|
baudr = B2500000;
|
|
break;
|
|
case 3000000:
|
|
baudr = B3000000;
|
|
break;
|
|
case 3500000:
|
|
baudr = B3500000;
|
|
break;
|
|
case 4000000:
|
|
baudr = B4000000;
|
|
break;
|
|
default:
|
|
printf("invalid baudrate\n");
|
|
return (1);
|
|
break;
|
|
}
|
|
|
|
int cbits = CS8;
|
|
int cpar = 0;
|
|
int ipar = IGNPAR;
|
|
int bstop = 0;
|
|
|
|
if (strlen(mode) != 3) {
|
|
printf("invalid mode \"%s\"\n", mode);
|
|
return (1);
|
|
}
|
|
unsigned bitcount = 1; // start bit
|
|
switch (mode[0]) {
|
|
case '8':
|
|
cbits = CS8;
|
|
bitcount += 8;
|
|
break;
|
|
case '7':
|
|
cbits = CS7;
|
|
bitcount += 7;
|
|
break;
|
|
case '6':
|
|
cbits = CS6;
|
|
bitcount += 6;
|
|
break;
|
|
case '5':
|
|
cbits = CS5;
|
|
bitcount += 5;
|
|
break;
|
|
default:
|
|
printf("invalid number of data-bits '%c'\n", mode[0]);
|
|
return (1);
|
|
break;
|
|
}
|
|
|
|
switch (mode[1]) {
|
|
case 'N':
|
|
case 'n':
|
|
cpar = 0;
|
|
ipar = IGNPAR;
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
cpar = PARENB;
|
|
ipar = INPCK;
|
|
bitcount += 1;
|
|
break;
|
|
case 'O':
|
|
case 'o':
|
|
cpar = (PARENB | PARODD);
|
|
ipar = INPCK;
|
|
bitcount += 1;
|
|
break;
|
|
default:
|
|
printf("invalid parity '%c'\n", mode[1]);
|
|
return (1);
|
|
break;
|
|
}
|
|
|
|
switch (mode[2]) {
|
|
case '1':
|
|
bstop = 0;
|
|
bitcount += 1;
|
|
break;
|
|
case '2':
|
|
bstop = CSTOPB;
|
|
bitcount += 2;
|
|
break;
|
|
default:
|
|
printf("invalid number of stop bits '%c'\n", mode[2]);
|
|
return (1);
|
|
break;
|
|
}
|
|
// bit count is 10 for 8N1
|
|
// Calc time to transmit on character
|
|
TransmissionTime_us = (1000000 * bitcount) / baudrate;
|
|
|
|
/* scan for BREAK and frame/parity errors?
|
|
To read BREAK not as \0:
|
|
PARMRK=1 and parity checking -> BREAK violates frame pattern -> is recieved as \377 \0 \0
|
|
*/
|
|
int iflag;
|
|
if (par_and_break)
|
|
iflag = PARMRK | INPCK;
|
|
else
|
|
iflag = ipar;
|
|
|
|
/*
|
|
http://pubs.opengroup.org/onlinepubs/7908799/xsh/termios.h.html
|
|
|
|
http://man7.org/linux/man-pages/man3/termios.3.html
|
|
*/
|
|
|
|
Cport = open(full_devname, O_RDWR | O_NOCTTY | O_NDELAY);
|
|
if (Cport == -1) {
|
|
perror("unable to open comport ");
|
|
return (1);
|
|
}
|
|
|
|
/* lock access so that another process can't also use the port */
|
|
if (flock(Cport, LOCK_EX | LOCK_NB) != 0) {
|
|
close(Cport);
|
|
perror("Another process has locked the comport.");
|
|
return (1);
|
|
}
|
|
|
|
error = tcgetattr(Cport, &old_port_settings);
|
|
if (error == -1) {
|
|
close(Cport);
|
|
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
|
|
perror("unable to read portsettings ");
|
|
return (1);
|
|
}
|
|
memset(&new_port_settings, 0, sizeof(new_port_settings)); /* clear the new struct */
|
|
|
|
new_port_settings.c_cflag = cbits | cpar | bstop | CLOCAL | CREAD;
|
|
new_port_settings.c_iflag = iflag;
|
|
new_port_settings.c_oflag = 0;
|
|
new_port_settings.c_lflag = 0;
|
|
new_port_settings.c_cc[VMIN] = 0; /* block until n bytes are received */
|
|
new_port_settings.c_cc[VTIME] = 0; /* block until a timer expires (n * 100 mSec.) */
|
|
|
|
cfsetispeed(&new_port_settings, baudr);
|
|
cfsetospeed(&new_port_settings, baudr);
|
|
|
|
error = tcsetattr(Cport, TCSANOW, &new_port_settings);
|
|
if (error == -1) {
|
|
tcsetattr(Cport, TCSANOW, &old_port_settings);
|
|
close(Cport);
|
|
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
|
|
perror("unable to adjust portsettings ");
|
|
return (1);
|
|
}
|
|
|
|
/* http://man7.org/linux/man-pages/man4/tty_ioctl.4.html */
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
tcsetattr(Cport, TCSANOW, &old_port_settings);
|
|
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
|
|
perror("unable to get portstatus");
|
|
return (1);
|
|
}
|
|
|
|
status |= TIOCM_DTR; /* turn on DTR */
|
|
status |= TIOCM_RTS; /* turn on RTS */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
tcsetattr(Cport, TCSANOW, &old_port_settings);
|
|
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
|
|
perror("unable to set portstatus");
|
|
return (1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int rs232_c::PollComport(unsigned char *buf, int size) {
|
|
int n;
|
|
|
|
n = read(Cport, buf, size);
|
|
|
|
if (n < 0) {
|
|
if (errno == EAGAIN)
|
|
return 0;
|
|
}
|
|
|
|
return (n);
|
|
}
|
|
|
|
int rs232_c::SendByte(unsigned char byte) {
|
|
int n = write(Cport, &byte, 1);
|
|
if (n < 0) {
|
|
if (errno == EAGAIN) {
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int rs232_c::SendBuf(unsigned char *buf, int size) {
|
|
int n = write(Cport, buf, size);
|
|
if (n < 0) {
|
|
if (errno == EAGAIN) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return (n);
|
|
}
|
|
|
|
// put byte in to rcv queue
|
|
void rs232_c::LoopbackByte(unsigned char byte) {
|
|
if (ioctl(Cport, TIOCSTI, &byte) == -1) {
|
|
perror("unable to insert byte into input queue");
|
|
}
|
|
}
|
|
|
|
void rs232_c::SetBreak(int break_state) {
|
|
if (ioctl(Cport, break_state ? TIOCSBRK : TIOCCBRK) == -1) {
|
|
perror("unable to set break status");
|
|
}
|
|
}
|
|
|
|
void rs232_c::CloseComport(void) {
|
|
int status;
|
|
TransmissionTime_us = 0;
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
perror("unable to get portstatus");
|
|
}
|
|
|
|
status &= ~TIOCM_DTR; /* turn off DTR */
|
|
status &= ~TIOCM_RTS; /* turn off RTS */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
perror("unable to set portstatus");
|
|
}
|
|
|
|
tcsetattr(Cport, TCSANOW, &old_port_settings);
|
|
close(Cport);
|
|
|
|
flock(Cport, LOCK_UN); /* free the port so that others can use it. */
|
|
}
|
|
|
|
/*
|
|
Constant Description
|
|
TIOCM_LE DSR (data set ready/line enable)
|
|
TIOCM_DTR DTR (data terminal ready)
|
|
TIOCM_RTS RTS (request to send)
|
|
TIOCM_ST Secondary TXD (transmit)
|
|
TIOCM_SR Secondary RXD (receive)
|
|
TIOCM_CTS CTS (clear to send)
|
|
TIOCM_CAR DCD (data carrier detect)
|
|
TIOCM_CD see TIOCM_CAR
|
|
TIOCM_RNG RNG (ring)
|
|
TIOCM_RI see TIOCM_RNG
|
|
TIOCM_DSR DSR (data set ready)
|
|
|
|
http://man7.org/linux/man-pages/man4/tty_ioctl.4.html
|
|
*/
|
|
|
|
int rs232_c::IsDCDEnabled(void) {
|
|
int status;
|
|
|
|
ioctl(Cport, TIOCMGET, &status);
|
|
|
|
if (status & TIOCM_CAR)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
int rs232_c::IsCTSEnabled(void) {
|
|
int status;
|
|
|
|
ioctl(Cport, TIOCMGET, &status);
|
|
|
|
if (status & TIOCM_CTS)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
int rs232_c::IsDSREnabled(void) {
|
|
int status;
|
|
|
|
ioctl(Cport, TIOCMGET, &status);
|
|
|
|
if (status & TIOCM_DSR)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
void rs232_c::enableDTR(void) {
|
|
int status;
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
perror("unable to get portstatus");
|
|
}
|
|
|
|
status |= TIOCM_DTR; /* turn on DTR */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
perror("unable to set portstatus");
|
|
}
|
|
}
|
|
|
|
void rs232_c::disableDTR(void) {
|
|
int status;
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
perror("unable to get portstatus");
|
|
}
|
|
|
|
status &= ~TIOCM_DTR; /* turn off DTR */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
perror("unable to set portstatus");
|
|
}
|
|
}
|
|
|
|
void rs232_c::enableRTS(void) {
|
|
int status;
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
perror("unable to get portstatus");
|
|
}
|
|
|
|
status |= TIOCM_RTS; /* turn on RTS */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
perror("unable to set portstatus");
|
|
}
|
|
}
|
|
|
|
void rs232_c::disableRTS(void) {
|
|
int status;
|
|
|
|
if (ioctl(Cport, TIOCMGET, &status) == -1) {
|
|
perror("unable to get portstatus");
|
|
}
|
|
|
|
status &= ~TIOCM_RTS; /* turn off RTS */
|
|
|
|
if (ioctl(Cport, TIOCMSET, &status) == -1) {
|
|
perror("unable to set portstatus");
|
|
}
|
|
}
|
|
|
|
void rs232_c::flushRX(void) {
|
|
tcflush(Cport, TCIFLUSH);
|
|
}
|
|
|
|
void rs232_c::flushTX(void) {
|
|
tcflush(Cport, TCOFLUSH);
|
|
}
|
|
|
|
void rs232_c::flushRXTX(void) {
|
|
tcflush(Cport, TCIOFLUSH);
|
|
}
|
|
|
|
void rs232_c::cputs(const char *text) /* sends a string to serial port */
|
|
{
|
|
while (*text != 0)
|
|
SendByte(*(text++));
|
|
}
|
|
|