代码是纯C写的,大于115200的非标准波特率是和平台相关的,这里是基于PC104的,写读取操作时参考了AUPE,写非标准波特率时参考了stackoverflow的一个帖子,当时竟然忘了存了。。。
#ifndef USER_TYPE_H_
#define USER_TYPE_H_
#define SUCCESS 0
#define FAILURE 1
#define STRING(x) #x
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned int UINT;
typedef unsigned long ULONG;
typedef unsigned int uint24;
#endif
#ifndef SERIAL_PORT_H_
#define SERIAL_PORT_H_
#include "user_type.h"
/*
* configuration of a serial port
*/
struct Config{
int baudRate; //baud rate
int dataBits; //data bits
char parity; //parity
int stopBits; //stop bits
int minToRead; //minimum bytes to return when calling read
int timeoutBetweenBytes; //time out between bytes, unit in 100ms
//baseAddr is used only when baudRate is supported nonstandard baud rate
USHORT baseAddr; //serial port's hardware base address
};
/*
* This function is used to check if the baud rate is supported.
*@param baudRate: baud rate to check
*@return:
1 if the baud rate is supported
0 otherwise
*/
int isSupportedBaudRate(int baudRate);
/*
* This function is used to get the type of given baudRate
*@param baudRate: baud rate to check
*@return:
0 if given baud is supported standard baud rate
1 if given baud is supported nonstandard baud rate
-1 if given baud is not supported
*/
int getBaudRateType(int baudRate);
/*
* This function is used to check if the data bits is supported.
*@param dataBits: data bits to check
*@return:
1 if the dataBits is supported
0 otherwise
*/
int isSupportedDataBits(int dataBits);
/*
* This function is used to check if the parity is supported.
*@param parity: parity to check
*@return:
1 if the parity is supported
0 otherwise
*/
int isSupportedParity(char parity);
/*
* This function is used to check if the stop bits is supported.
*@param stopBits: stop bits to check
*@return:
1 if the stopBits is supported
0 otherwise
*/
int isSupportedDataBits(int stopBits);
/*
* This function is used to open a serial port.
*@param dev: serial port's system file path
*@param mode: "ReadOnly","WriteOnly","ReadWrite"
*@param blocking: whether to set the read mode to be blocking or not
*@return:
corresponding file handle if open the serial port successfully
a negative number otherwise
*/
int openSerialPort(
const char* dev,
const char* mode,
int blocking
);
/*
* This function is used to set configuration for the serial port.
*@param fd: file handle of target serial port
*@param baud: 38400, 19200, 9600, 4800, 2400, 1200, 300
*@param dataBits: 7 or 8
*@param stopBits: 1 or 2
*@param parity: 'N' or 'E' or 'O' or 'S'
*@param minToRead: minimum bytes to return when calling read
*@param timeoutBetweenBytes: time out between bytes, unit in 100ms
*@return:
0 if done configuration successfully
negative numbers if error happens
*/
int configurateSerialPort(
int fd,
int baud,
int dataBits,
char parity,
int stopBits,
int minToRead,
int timeoutBetweenBytes
);
/*
* This function is used to open a serial port.
*@param dev: serial port's system file path
*@param mode: "ReadOnly","WriteOnly","ReadWrite"
*@param blocking: whether to set the read mode to be blocking or not
*@param pConfig: const pointer to a Config structure
*@return:
corresponding file handle if open the serial port successfully
a negative number otherwise
*/
int openAndConfigurateSerialPort(
const char* dev,
const char* mode,
int blocking,
const struct Config* pConfig
);
/*
* This function is used to close a serial port.
* @param fd: serial port handle
* @return:
* SUCCESS if successfully closed the port
* FAILURE if fail to close
*/
int closeSerialPort(int fd);
/*
* This function is send frame.
*@param fd: file descriptor of a serial port
*@param frame: pointer of the frame
*@param len: bytes to send
*@return:
bytes successfully sent
*/
int send(
int fd,
void* frame,
int len
);
/*
* This function is receive within given time limit.
*@param fd: file descriptor of a serial port
*@param buf: pointer to where to hold bytes received
*@param nbytes: target bytes to receive
*@param timeoutSec: second part of timeout
*@param timeoutMicrosec: microsecond part of timeout
*@return:
if time runs out before nbytes has been received, returns how many actually received
else return nbytes
*/
int treadn(int fd, void *buf, int nbytes, int timeoutSec, int timeoutMicrosec);
/*
* This function is receive frame.
*@param fd: file descriptor of a serial port
*@param frame: pointer to where to hold bytes received
*@param maxlen: maximum bytes to receive
*@return:
bytes successfully read once
*/
int receive(
int fd,
void* frame,
int maxLen
);
#endif /* SERIAL_PORT_H_ */
//local headers
#include "user_type.h"
#include "serial_port.h"
//standard C headers
#include
#include
#include
#include
//linux headers
#include
#include
#include
#include
#include
#include
#include
int getBaudRateType(int baudRate)
{
//if not in valid range
if(baudRate <= 0 || baudRate > 1500000) return -1;
//if supported standard
if(baudRate == 110 ||
baudRate == 300 ||
baudRate == 600 ||
baudRate == 1200 ||
baudRate == 2400 ||
baudRate == 4800 ||
baudRate == 9600 ||
baudRate == 14400 ||
baudRate == 19200 ||
baudRate == 38400 ||
baudRate == 57600 ||
baudRate == 115200
) return 0;
//if supported nonstandard
if(115200 % baudRate == 0 && 115200 / baudRate < 256) return 1;
if(460800 % baudRate == 0 && 460800 / baudRate < 256) return 1;
if(921600 % baudRate == 0 && 921600 / baudRate < 256) return 1;
if(1500000 % baudRate == 0 && 1500000 / baudRate < 256) return 1;
return -1;
}
int isSupportedBaudRate(int baudRate)
{
return getBaudRateType(baudRate) >= 0;
}
int isSupportedDataBits(int dataBits)
{
return dataBits >= 5 && dataBits <= 8;
}
int isSupportedParity(char parity)
{
return parity == 'N' || parity == 'n' ||
parity == 'O' || parity == 'o' ||
parity == 'E' || parity == 'e' ||
parity == 'S' || parity == 's';
}
int isSupportedStopBits(int stopBits)
{
return stopBits == 1 || stopBits == 2;
}
int closeSerialPort(int fd)
{
if(0 == close(fd)) return SUCCESS;
return FAILURE;
}
int openSerialPort(const char* dev, const char* mode, int blocking)
{
int fd = -1;
if(strcmp(mode, "ReadOnly") == 0)
fd = open(dev, O_RDONLY | O_NOCTTY | O_NDELAY);
else if(strcmp(mode, "WriteOnly") == 0)
fd = open(dev, O_WRONLY | O_NOCTTY | O_NDELAY);
else if(strcmp(mode, "ReadWrite") == 0)
fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
else
fprintf(stderr, "error@openSerialPort: invalid open mode\n");
if(fd > 0 && blocking){
if(0 > fcntl(fd, F_SETFL, 0)) perror("fcntl F_SETFL");
}
return fd;
}
int configurateSerialPort(int fd, int baud, int dataBits, char parity, int stopBits, int minToRead, int timeoutBetweenBytes)
{
struct termios config;
//step 0: get current attribute of the serial port
if(tcgetattr(fd, &config) != 0){
fprintf(stderr, "error@configSerialPort: fail to tcgetattr(fd, &config)\n");
return FAILURE;
}
//step 1: translate baud into a system parameter
switch(baud){
case 115200: baud = B115200; break;
case 57600: baud = B57600; break;
case 38400: baud = B38400; break;
case 19200: baud = B19200; break;
case 9600: baud = B9600; break;
case 4800: baud = B4800; break;
case 2400: baud = B2400; break;
case 1200: baud = B1200; break;
case 300: baud = B300; break;
case 110: baud = B110; break;
default:
fprintf(stderr, "error@configSerialPort: invalid baud rate\n");
return FAILURE;
}
//step 2: set input/output baud
if(cfsetispeed(&config, baud) != 0){
fprintf(stderr, "error@configSerialPort: fail to cfsetispeed(&config, baud)\n");
return FAILURE;
}
if(cfsetospeed(&config, baud) != 0){
fprintf(stderr, "error@configSerialPort: fail to cfsetospeed(&config, baud)\n");
return FAILURE;
}
//step 3: set stream attribute
//to disable input software stream control
config.c_iflag &= ~(IXON | IXOFF | IXANY);
config.c_iflag &= ~(INLCR | ICRNL | IGNCR);
//to let every char for every char
config.c_oflag &= ~OPOST;
config.c_oflag &= ~(ONLCR | OCRNL);
//to make output no wait for a '\n'
config.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//step 4: set data bits
config.c_cflag &= ~CSIZE;
switch (dataBits){
case 5: config.c_cflag |= CS5; break;
case 6: config.c_cflag |= CS6; break;
case 7: config.c_cflag |= CS7; break;
case 8: config.c_cflag |= CS8; break;
default:
fprintf(stderr, "error@configSerialPort: invalid data bits\n");
return FAILURE;
}
//step 5: set parity
switch (parity){
case 'n': case 'N':
config.c_cflag &= ~PARENB; /* Clear parity enable */
config.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o': case 'O':
config.c_cflag |= (PARODD | PARENB); /* 设置为奇效验*/
config.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e': case 'E':
config.c_cflag |= PARENB; /* Enable parity */
config.c_cflag &= ~PARODD; /* 转æ¢ä¸ºå¶æ•ˆéªŒ*/
config.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S': case 's':
config.c_cflag &= ~PARENB; /*as no parity*/
config.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr, "error@configSerialPort: invalid parity\n");
return FAILURE;
}
//step 6: set stop bits
switch (stopBits){
case 1:
config.c_cflag &= ~CSTOPB; break;
case 2:
config.c_cflag |= CSTOPB; break;
default:
fprintf(stderr, "error@configSerialPort: invalid stop bits\n");
return FAILURE;
}
//step 7: set read attribute
config.c_cc[VMIN] = minToRead;
config.c_cc[VTIME] = timeoutBetweenBytes;
//step 8: set configuration to serial port
if(tcsetattr(fd, TCSANOW, &config) != 0){
fprintf(stderr, "error@configSerialPort: fail to tcsetattr(fd, TCSANOW, &config)\n");
return FAILURE;
}
//step 9: flush input/output queues
if(tcflush(fd, TCIOFLUSH) != 0){
fprintf(stderr, "error@configSerialPort: fail to tcflush(fd, TCIOFLUSH)\n");
return FAILURE;
}
return SUCCESS;
}
static int getBaseBaud(int customBaud)
{
if(customBaud <= 0) return 0;
if(115200 % customBaud == 0 && 115200 / customBaud < 256) return 115200;
if(460800 % customBaud == 0 && 460800 / customBaud < 256) return 460800;
if(921600 % customBaud == 0 && 921600 / customBaud < 256) return 921600;
if(1500000 % customBaud == 0 && 1500000 / customBaud < 256) return 1500000;
return -1;
}
static void setSerialPortRegister(unsigned short serialPortAddr, int baseBaud, int customBaud)
{
unsigned short enableRegAddr = serialPortAddr + 3;
unsigned char DLM = 0x00, DLL = (baseBaud / customBaud) & 0xFF, tmp;
//step 1: set high byte of divisor latch
switch(baseBaud){
case 115200: DLM = 0x00; break;
case 460800: DLM = 0x80; break;
case 921600: DLM = 0xC0; break;
case 1500000: DLM = 0x20; break;
}
//step 2: change permission and change register
ioperm(enableRegAddr, 1, 1);
tmp = inb(enableRegAddr);
outb(0x80 | tmp, enableRegAddr);
ioperm(serialPortAddr, 2, 1);
outb(DLL, serialPortAddr + 0);
outb(DLM, serialPortAddr + 1);
//step 3: retrieve permission
outb(tmp, enableRegAddr);
ioperm(enableRegAddr, 1, 0);
ioperm(serialPortAddr, 2, 0);
}
static int openSerialPortAtNonStandardBaud(const char* dev, const char* mode, int blocking, const struct Config* pConfig)
{
int customBaud = pConfig->baudRate, baseBaud = getBaseBaud(pConfig->baudRate);
//step 0: check if customBaud is supported
if(baseBaud < 0){
fprintf(stderr, "error@openSerialPortAtNonStandardBaud: invalid baud rate\n");
return -1;
}
//step 1: open serial port
int fd = openSerialPort(dev, mode, blocking);
if(fd < 0) return -2;
//set hardware control
struct serial_struct ss;
if(ioctl(fd, TIOCGSERIAL, &ss) < 0){
fprintf(stderr, "error@openSerialPortAtNonStandardBaud: fail to ioctl(fd, TIOCGSERIAL, &ss)\n");
return -3;
}
ss.flags = ASYNC_SPD_CUST;
ss.baud_base = baseBaud;
ss.custom_divisor = ss.baud_base / customBaud;
//add hardware control
if(ioctl(fd, TIOCSSERIAL, &ss) < 0){
fprintf(stderr, "error@openSerialPortAtNonStandardBaud: fail to ioctl(fd, TIOCSSERIAL, &ss)\n");
return -4;
}
if(ioctl(fd, TIOCGSERIAL, &ss) < 0){
fprintf(stderr, "error@openSerialPortAtNonStandardBaud: fail to ioctl(fd, TIOCGSERIAL, &ss)\n");
return -5;
}
//step 3: set configuration
if(SUCCESS != configurateSerialPort(fd, 38400, //must set baud rate to 38400 to get custom baud
pConfig->dataBits,
pConfig->parity,
pConfig->stopBits,
pConfig->minToRead,
pConfig->timeoutBetweenBytes)){
fprintf(stderr, "error@openSerialPortAtNonStandardBaud: fail to configurateSerialPort(...)\n");
return -6;
}
//step 4: change register to reinterpret 38400
setSerialPortRegister(pConfig->baseAddr, baseBaud, customBaud);
return fd;
}
int openAndConfigurateSerialPort(const char* dev, const char* mode, int blocking, const struct Config* pConfig)
{
int fd, type = getBaudRateType(pConfig->baudRate);
//check if baud rate supported
if(type < 0){
fprintf(stderr, "error@openSerialPort: invalid baud rate\n");
return -1;
}
//check if data bits supported
if(!isSupportedDataBits(pConfig->dataBits)){
fprintf(stderr, "error@openSerialPort: invalid data bits\n");
return -2;
}
//check if parity supported
if(!isSupportedParity(pConfig->parity)){
fprintf(stderr, "error@openSerialPort: invalid parity\n");
return -3;
}
//check if stop bits supported
if(!isSupportedStopBits(pConfig->stopBits)){
fprintf(stderr, "error@openSerialPort: invalid stop bits\n");
return -4;
}
//baud rate is supported nonstandard baud rate
if(type == 1){
fd = openSerialPortAtNonStandardBaud(dev, mode, blocking, pConfig);
if(fd < 0) fprintf(stderr, "error@openSerialPort: fail to openSerialPortAtNonStandardBaud(dev, mode, pConfig)\n");
return fd;
}
//baud rate is standard baud rate
fd = openSerialPort(dev, mode, blocking);
if(fd < 0){
fprintf(stderr, "error@openSerialPort: fail to openSerialPort(dev, mode)\n");
return -5;
}
if(SUCCESS != configurateSerialPort(
fd,
pConfig->baudRate,
pConfig->dataBits,
pConfig->parity,
pConfig->stopBits,
pConfig->minToRead,
pConfig->timeoutBetweenBytes)
){
fprintf(stderr, "error@openSerialPort: fail to configurateSerialPort(...)\n");
return -6;
}
return fd;
}
int send(int fd, void* frame, int len)
{
int nsent = 0, alreadySent = 0;
for(; alreadySent < len; alreadySent += nsent){
nsent = write(fd, frame + alreadySent, len - alreadySent);
if(nsent <= 0){
fprintf(stderr, "error@sendFrame: fail to write(fd, frame + alreadySent, len - alreadySent)\n");
break;
}
}
return alreadySent;
}
static int tread(int fd, void *buf, int maxSize, struct timeval* ptv)
{
int nfds;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
nfds = select(fd+1, &readfds, NULL, NULL, ptv);
if(nfds <= 0){
if(nfds == 0) errno = ETIME;
return -1;
}
return read(fd, buf, maxSize);
}
int treadn(int fd, void *buf, int nbytes, int timeoutSec, int timeoutMicrosec)
{
int nleft;
int nread;
struct timeval tv;
tv.tv_sec = timeoutSec;
tv.tv_usec = timeoutMicrosec;
nleft = nbytes;
while(nleft > 0){
if((nread = tread(fd, buf, nleft, &tv)) < 0){
if (nleft == nbytes) return -1; /* error, received nothing at all return -1 */
else break; /* error, return amount received so far */
}
else if (nread == 0) break;
nleft -= nread;
buf += nread;
}
return nbytes - nleft; /* return >= 0 */
}
int receive(int fd, void* frame, int maxLen)
{
return read(fd, frame, maxLen);
}