-
-
Save parsley72/e855d8e146827beeb3c0a9c4f82ce17f to your computer and use it in GitHub Desktop.
Simple unix serial implementation.This uses pthreads to receive and buffer incoming data for later use.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://gist.github.com/ryankurte/8143058 | |
#include "uart.h" | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <termios.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <pthread.h> | |
#include <signal.h> | |
#include <poll.h> | |
#include <errno.h> | |
/** | |
* @struct Serial device structure. | |
* Encapsulates a serial connection. | |
*/ | |
struct serial_s { | |
int fd; //>! Connection file descriptor. | |
int state; //>! Signifies connection state. | |
int running; //>! Signifies thread state. | |
char rxbuff[BUFF_SIZE]; //>! Buffer for RX data. | |
int start, end; //>! Pointers to start and end of buffer. | |
pthread_t rx_thread; //>! Listening thread. | |
}; | |
// --------------- Internal Functions --------------- | |
static int serial_resolve_baud(int baud); | |
/** | |
* Recieve data. | |
* Retrieves data from the serial device. | |
* @param s - serial structure. | |
* @param data - pointer to a buffer to read data into. | |
* @param maxLength - size of input buffer. | |
* @return amount of data recieved. | |
*/ | |
static int serial_recieve(serial_t* obj, uint8_t data[], int maxLength); | |
/** | |
* @brief Serial Listener Thread. | |
* This blocks waiting for data to be recieved from the serial device, | |
* and calls the serial_callback method with appropriate context when | |
* data is recieved. | |
* Exits when close method is called, or serial error occurs. | |
* @param param - context passed from thread instantiation. | |
*/ | |
static void *serial_data_listener(void *param); | |
/** | |
* @brief Start the serial threads. | |
* This spawns the listening and transmitting threads | |
* if they are not already running. | |
* @param s - serial structure. | |
* @return 0 on success, or -1 on error. | |
*/ | |
static int serial_start(serial_t* s); | |
/** | |
* Stop serial listener thread. | |
* @param s - serial structure. | |
* @return 0; | |
*/ | |
static int serial_stop(serial_t* s); | |
/** | |
* Callback to handle recieved data. | |
* Puts recieved data into the rx buffer. | |
* @param s - serial structure. | |
* @param data - data to be stored. | |
* @param length - length of recieved data. | |
*/ | |
static void serial_rx_callback(serial_t* s, char data[], int length); | |
// Put character in rx buffer. | |
static int buffer_put(serial_t* s, char c) | |
{ | |
//if there is space in the buffer | |
if ( s->end != ((s->start + BUFF_SIZE - 1) % BUFF_SIZE)) { | |
s->rxbuff[s->end] = c; | |
s->end ++; | |
s->end = s->end % BUFF_SIZE; | |
//printf("Put: %x start: %d, end: %d\r\n", c, s->start, s->end); | |
return 0; //No error | |
} else { | |
//buffer is full, this is a bad state | |
return -1; //Report error | |
} | |
} | |
// Get character from rx buffer. | |
static char buffer_get(serial_t* s) | |
{ | |
char c = (char)0; | |
//if there is data to process | |
if (s->end != s->start) { | |
c = (s->rxbuff[s->start]); | |
s->start ++; | |
//wrap around | |
s->start = s->start % BUFF_SIZE; | |
} else { | |
} | |
//printf("Get: %x start: %d, end: %d\r\n", c, s->start, s->end); | |
return c; | |
} | |
// Get data available in the rx buffer. | |
static int buffer_available(serial_t* s) | |
{ | |
return (s->end - s->start + BUFF_SIZE) % BUFF_SIZE; | |
} | |
// --------------- External Functions --------------- | |
serial_t* serial_create() | |
{ | |
// Allocate serial object. | |
serial_t* s = malloc(sizeof(serial_t)); | |
// Reconfigure buffer object. | |
s->start = 0; | |
s->end = 0; | |
// Return pointer. | |
return s; | |
} | |
void serial_destroy(serial_t* s) | |
{ | |
free(s); | |
} | |
int serial_connect(serial_t* s, const char device[], int baud) | |
{ | |
struct termios oldtio; | |
// Resolve baud. | |
int speed = serial_resolve_baud(baud); | |
if (speed < 0) { | |
printf("Error: Baud rate not recognized.\r\n"); | |
return -1; | |
} | |
// Open device. | |
s->fd = open(device, O_RDWR | O_NOCTTY); | |
// Catch file open error. | |
if (s->fd < 0) { | |
perror(device); | |
return -2; | |
} | |
// https://blog.mbedded.ninja/programming/operating-systems/linux/linux-serial-ports-using-c-cpp/ | |
// Retrieve settings. | |
tcgetattr(s->fd, &oldtio); | |
// Set baud rate. | |
cfsetspeed(&oldtio, speed); | |
// Control Modes (c_cflags) | |
// oldtio.c_cflag &= ~PARENB; // Clear parity bit, disabling parity (most common) | |
oldtio.c_cflag |= PARENB; // Set parity bit, enabling parity | |
oldtio.c_cflag &= ~PARODD; // If set, then parity for input and output is odd; otherwise even parity is used. | |
oldtio.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used in communication (most common) | |
oldtio.c_cflag &= ~CSIZE; // Clear all the size bits, then use one of the statements below | |
oldtio.c_cflag |= CS8; // 8 bits per byte (most common) | |
oldtio.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control (most common) | |
// Local Modes (c_lflag) | |
// Disable Canonical Mode | |
oldtio.c_lflag &= ~ICANON; | |
// Disable Echo | |
oldtio.c_lflag &= ~ECHO; // Disable echo | |
oldtio.c_lflag &= ~ECHOE; // Disable erasure | |
oldtio.c_lflag &= ~ECHONL; // Disable new-line echo | |
// Disable Signal Chars | |
oldtio.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP | |
// CREAD and CLOCAL | |
oldtio.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1) | |
// Input Modes (c_iflag) | |
// Software Flow Control | |
oldtio.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl | |
// Disable Special Handling Of Bytes On Receive | |
oldtio.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL); // Disable any special handling of received bytes | |
// Output Modes (c_oflag) | |
oldtio.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars) | |
oldtio.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed | |
// Flush cache. | |
tcflush(s->fd, TCIFLUSH); | |
// Apply settings. | |
tcsetattr(s->fd, TCSANOW, &oldtio); | |
// Start listener thread. | |
int res = serial_start(s); | |
// Catch error. | |
if (res < 0) { | |
printf("Error: serial thread could not be spawned\r\n"); | |
return -3; | |
} | |
// Indicate connection was successful. | |
s->state = 1; | |
return 0; | |
} | |
int serial_set_baud(serial_t* s, int baud) | |
{ | |
struct termios oldtio; | |
// Resolve baud. | |
int speed = serial_resolve_baud(baud); | |
if (speed < 0) { | |
printf("Error: Baud rate not recognized.\r\n"); | |
return -1; | |
} | |
// Retrieve settings. | |
tcgetattr(s->fd, &oldtio); | |
// Set baud rate. | |
cfsetspeed(&oldtio, speed); | |
// Flush cache. | |
tcflush(s->fd, TCIFLUSH); | |
// Apply settings. | |
tcsetattr(s->fd, TCSANOW, &oldtio); | |
return 0; | |
} | |
int serial_send(serial_t* s, const uint8_t data[], int length) | |
{ | |
int res = write(s->fd, data, length); | |
return res; | |
} | |
void serial_put(serial_t* s, uint8_t data) | |
{ | |
char arr[1]; | |
arr[0] = data; | |
write(s->fd, arr, 1); | |
} | |
int serial_available(serial_t* s) | |
{ | |
return buffer_available(s); | |
} | |
char serial_get(serial_t* s) | |
{ | |
char c = buffer_get(s); | |
return c; | |
} | |
char serial_blocking_get(serial_t* s) | |
{ | |
while (serial_available(s) == 0); | |
return serial_get(s); | |
} | |
void serial_clear(serial_t* s) | |
{ | |
// Clear the buffer. | |
while (buffer_available(s)) { | |
buffer_get(s); | |
} | |
} | |
int serial_close(serial_t* s) | |
{ | |
// Stop thread. | |
serial_stop(s); | |
return 0; | |
} | |
// --------------- Internal Functions -------------- | |
// Stop serial listener thread. | |
static int serial_stop(serial_t* s) | |
{ | |
s->running = 0; | |
return 0; | |
} | |
// Resolves standard baud rates to linux constants. | |
static int serial_resolve_baud(int baud) | |
{ | |
int speed; | |
// Switch common baud rates to temios constants. | |
switch (baud) { | |
case 9600: | |
speed = B9600; | |
break; | |
case 19200: | |
speed = B19200; | |
break; | |
case 38400: | |
speed = B38400; | |
break; | |
case 57600: | |
speed = B57600; | |
break; | |
case 115200: | |
speed = B115200; | |
break; | |
case 460800: | |
speed = B460800; | |
break; | |
case 921600: | |
speed = B921600; | |
break; | |
case 2000000: | |
speed = B2000000; | |
break; | |
case 3000000: | |
speed = B3000000; | |
break; | |
default: | |
speed = -1; | |
break; | |
} | |
// Return. | |
return speed; | |
} | |
// Start serial listener. | |
static int serial_start(serial_t* s) | |
{ | |
// Only start if it is not currently running. | |
if (s->running != 1) { | |
//Set running. | |
s->running = 1; | |
//Spawn thread. | |
int res; | |
res = pthread_create(&s->rx_thread, NULL, serial_data_listener, (void*) s); | |
if (res != 0) { | |
return -2; | |
} | |
// Return result. | |
return 0; | |
} else { | |
return -1; | |
} | |
} | |
// Recieve data. | |
static int serial_recieve(serial_t* s, uint8_t data[], int maxLength) | |
{ | |
return read(s->fd, data, maxLength); | |
} | |
// Callback to store data in buffer. | |
static void serial_rx_callback(serial_t* s, char data[], int length) | |
{ | |
// Put data into buffer. | |
int i; | |
for (i = 0; i < length; i++) { | |
buffer_put(s, data[i]); | |
} | |
} | |
// Serial data listener thread. | |
static void *serial_data_listener(void *param) | |
{ | |
int res = 0; | |
int err = 0; | |
struct pollfd ufds; | |
uint8_t buff[BUFF_SIZE]; | |
// Retrieve parameters and store locally. | |
serial_t* serial = (serial_t*) param; | |
int fd = serial->fd; | |
// Set up poll file descriptors. | |
ufds.fd = fd; // Attach socket to watch. | |
ufds.events = POLLIN; // Set events to notify on. | |
// Run until ended. | |
while (serial->running != 0) { | |
// Poll socket for data. | |
res = poll(&ufds, 1, POLL_TIMEOUT); | |
// If data was recieved. | |
if (res > 0) { | |
// Fetch the data. | |
int count = serial_recieve(serial, buff, BUFF_SIZE - 1); | |
// If data was recieved. | |
if (count > 0) { | |
// Pad end of buffer to ensure there is a termination symbol. | |
buff[count] = '\0'; | |
// Call the serial callback. | |
serial_rx_callback(serial, (char *)buff, count); | |
// If an error occured. | |
} else if (count < 0) { | |
// Inform user and exit thread. | |
printf("Error: Serial disconnect\r\n"); | |
err = 1; | |
break; | |
} | |
// If there was an error. | |
} else if (res < 0) { | |
// Inform user and exit thread. | |
printf("Error: Polling error in serial thread"); | |
err = 1; | |
break; | |
} | |
// Otherwise, keep going around. | |
} | |
// If there was an error, close socket. | |
if (err) { | |
serial_close(serial); | |
//raise(SIGLOST); | |
} | |
// Close file. | |
/*res =*/ close(serial->fd); | |
return NULL; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://gist.github.com/ryankurte/8143058 | |
#ifndef UART_H | |
#define UART_H | |
#define BUFF_SIZE 512 | |
#define POLL_TIMEOUT 2000 | |
#include <stdint.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
typedef struct serial_s serial_t; | |
/** | |
* Create the serial structure. | |
* Convenience method to allocate memory | |
* and instantiate objects. | |
* @return serial structure. | |
*/ | |
serial_t* serial_create(); | |
/** | |
* Destroy the serial structure | |
*/ | |
void serial_destroy(serial_t* s); | |
/** | |
* Connect to a serial device. | |
* @param s - serial structure. | |
* @param device - serial device name. | |
* @param baud - baud rate for connection. | |
* @return -ve on error, 0 on success. | |
*/ | |
int serial_connect(serial_t* s, const char device[], int baud); | |
/** | |
* Set baud rate. | |
* @param s - serial structure. | |
* @param baud - baud rate. | |
* @return -ve on error, 0 on success. | |
*/ | |
int serial_set_baud(serial_t* s, int baud); | |
/** | |
* Send data. | |
* @param s - serial structure. | |
* @param data - character array to transmit. | |
* @param length - size of the data array. | |
*/ | |
int serial_send(serial_t* s, const uint8_t data[], int length); | |
/** | |
* Send a single character. | |
* @param s - serial structure. | |
* @param data - single character to be sent. | |
*/ | |
void serial_put(serial_t* s, uint8_t data); | |
/** | |
* Determine how much data is available | |
* in the serial buffer. | |
* @param s - serial structure. | |
* @return number of characters available. | |
*/ | |
int serial_available(serial_t* s); | |
/** | |
* Fetch one char from the serial buffer. | |
* @param s - serial structure. | |
* @return character. Null if empty. | |
*/ | |
char serial_get(serial_t* s); | |
/** | |
* Fetch one char from the serial buffer. | |
* Blocks until data becomes available. | |
* @param s - serial structure. | |
* @return character. | |
*/ | |
char serial_blocking_get(serial_t* s); | |
/** | |
* Clear the serial buffer. | |
* @param s - serial structure. | |
*/ | |
void serial_clear(serial_t* s); | |
/** | |
* Close the serial port. | |
* @param s - serial structure. | |
* @return value of close(). | |
*/ | |
int serial_close(serial_t* s); | |
#ifdef __cplusplus | |
} | |
#endif | |
#endif // UART_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://gist.github.com/ryankurte/8143058 | |
#ifndef UART_HPP | |
#define UART_HPP | |
#include "uart.h" | |
class Serial | |
{ | |
public: | |
Serial() | |
{ | |
_serial = serial_create(); | |
} | |
~Serial() | |
{ | |
serial_destroy(_serial); | |
} | |
int Connect(const char device[], int baud) | |
{ | |
return serial_connect(_serial, device, baud); | |
} | |
int Set_Baud(int baud) | |
{ | |
return serial_set_baud(_serial, baud); | |
} | |
int Send(const uint8_t data[], int length) | |
{ | |
return serial_send(_serial, data, length); | |
} | |
void Put(uint8_t data) | |
{ | |
return serial_put(_serial, data); | |
} | |
int Available() | |
{ | |
return serial_available(_serial); | |
} | |
char Get() | |
{ | |
return serial_get(_serial); | |
} | |
char Blocking_get() | |
{ | |
return serial_blocking_get(_serial); | |
} | |
void Clear() | |
{ | |
return serial_clear(_serial); | |
} | |
int Disconnect() | |
{ | |
return serial_close(_serial); | |
} | |
private: | |
serial_t* _serial; | |
}; | |
#endif // UART_HPP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment