[LAB] Comunicação Serial
Nesta atividade iremos realizar a implementação de uma aplicação simples que envia dados via serial. Para a implementação iremos adotar duas abordagem: a primeira será a utilização explicita de file descriptors e a segunda será utilizando as abstrações fornecidas pela biblioteca PIGPIO
.
Comunicação Serial utilizando File Descriptors
#define SERIAL "/dev/ttyS0"
#define SERIAL_BAUD 115200
int serial_fd;
uint8_t serial_st;
serial_st = serial_initialize(SERIAL, &serial_fd, SERIAL_BAUD);
uint8_t serial_initialize(const char *p_path, int32_t *p_fd, const uint32_t speed);
A primeira etapa que devemos realizar é justamente tentar abrir o dispositivo (porta serial), tal como fizemos com os os LED0
e LED1
, no projeto anterior. Então utilizamos o caminho e o file descriptor passados por parâmetro.
*p_fd = open(p_path, O_RDWR);
if (p_fd < 0)
{
printf("Error: Failed to open serial %s \n\n", p_path);
ret = 0;
}
Caso contrário, ou seja, o file descriptor tenha sido devidamente relacionado com um dispositivo, precisamos verificar se ele é de fato uma porta serial e caso seja, devemos configura-la de acordo com o que desejamos.
Para fazer a configuração, utilizaremos uma estrutura de dados, config
, para adquirir e atribuir os parâmetros do dispositivos.
struct termios config;
Como mencionado, a primeira verificação que devemos fazer é justamente avaliar se o dispositivo relacionado ao file descriptor é um interface serial. Para tal, carregamos todos os atributos do dispositivo para nossa estrutura de dados.
if (tcgetattr(*p_fd, &config) < 0)
{
printf("Error: Could not get the %s attributes!\n\n", p_path);
}
Então, realizamos as validações, primeiramente se o dispositivo em questão é ou não uma interface serial:
if (!isatty(*p_fd))
{
printf("Error: %s isnt pointing to a TTY device!!\n\n", p_path);
}
Em seguida, tentamos atribuir o baud rate especificado
if (cfsetspeed(&config, (speed_t)speed) < 0)
{
printf("Error: Couldn't set the %s baudrate!!\n\n", p_path);
}
Então, especificamos os outros parâmetros:
config.c_cflag &= ~PARENB;
config.c_cflag &= ~CSTOPB;
config.c_cflag &= ~CSIZE;
config.c_cflag |= CS8;
config.c_cflag &= ~CRTSCTS;
config.c_cc[VMIN] = 1;
config.c_cc[VTIME] = 5;
config.c_cflag |= CREAD | CLOCAL;
cfmakeraw(&config);
E enfim, atribuimos os atributos ao dispositivo.
if (tcsetattr(*p_fd, TCSAFLUSH, &config) < 0)
{
printf("Error: Couldn't set the %s attributes!!\n\n", p_path);
}
uint8_t serial_initialize(const char *p_path, int32_t *p_fd, const uint32_t speed)
{
uint8_t ret;
/** Tries to open the serial device (p_path).
* O_RDWR: Request read and write permissions. */
*p_fd = open(p_path, O_RDWR);
if (p_fd < 0)
{
printf("Error: Failed to open serial %s \n\n", p_path);
ret = 0;
}
else
{
printf("Serial (%s) opened!!\n\n", p_path);
/** The structure to set and get the device parameters. */
struct termios config;
/** Get the current configuration of the serial interface. */
if (tcgetattr(*p_fd, &config) < 0)
{
printf("Error: Could not get the %s attributes!\n\n", p_path);
}
else
{
printf("Serial (%s) attributes read.. \n\n", p_path);
/** Check if the fd is pointing to a TTY device or not. */
if (!isatty(*p_fd))
{
printf("Error: %s isnt pointing to a TTY device!!\n\n", p_path);
}
else
{
printf("%s is a tty device. Continuing...\n\n", p_path);
/** Setting the baudrate. */
if (cfsetspeed(&config, (speed_t)speed) < 0)
{
printf("Error: Couldn't set the %s baudrate!!\n\n", p_path);
}
else
{
/** No parity. */
config.c_cflag &= ~PARENB;
/** One stop bit. */
config.c_cflag &= ~CSTOPB;
/** Zeroes the char size mask. */
config.c_cflag &= ~CSIZE;
/** Sets data size = 8 bits. */
config.c_cflag |= CS8;
/** Disables HW flow control. */
config.c_cflag &= ~CRTSCTS;
/** Minimum number of characters for read. */
config.c_cc[VMIN] = 1;
/** Timeout in deciseconds for read. (0.5s) */
config.c_cc[VTIME] = 5;
/** Enables READ and ignores control lines. */
config.c_cflag |= CREAD | CLOCAL;
/** Set the terminal to "raw mode". */
cfmakeraw(&config);
/** Flushes the serial dev and sets the new attributes. */
if (tcsetattr(*p_fd, TCSAFLUSH, &config) < 0)
{
printf("Error: Couldn't set the %s attributes!!\n\n", p_path);
}
else
{
ret = 1;
}
}
}
}
}
return ret;
}
uint8_t serial_st;
serial_st = serial_initialize(SERIAL, &serial_fd, SERIAL_BAUD);
if (serial_st == 0)
{
printf("Error: Failed to open serial %s \n\n", SERIAL);
}
write(serial_fd, "Running.. Serial is easy\r\n", sizeof("Running.. Serial is easy\r\n"));
close(serial_fd);
/*
============================================================================
Name : ex03.c
Author : gbs|jms
Version :
Copyright : Your copyright notice
Description : A simple C program for demonstrating the serial communication.
============================================================================
*/
//==============================================================================
// USED INTERFACES
//==============================================================================
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Required by open / write system calls. */
#include <pthread.h> /* POSIX threads Library. Required by PIGPIO. */
#include <stdint.h> /* Standard Int Library.(uint32_t, uint8_t...)*/
#include <stdio.h> /* Standard Input/Ouput Library. (printf...) */
#include <stdlib.h> /* General (standard) Library. (EXIT_SUCCESS. */
#include <termios.h> /* Terminal Input/Output interfaces. */
#include <unistd.h> /* Complement (flags, constants, definitions..)
for the POSIX API. */
#include "pigpio.h" /* PIGPIO Library header. */
//==============================================================================
// MACROS AND DATATYPE DEFINITIONS
//==============================================================================
/* HW and Interfaces */
#define SERIAL "/dev/ttyS0"
#define SERIAL_BAUD 115200
//==============================================================================
// STATIC (PRIVATE) FUNCTION PROTOTYPES
//==============================================================================
/**
* @brief A function to verify if any key was pressed on terminal.
*
* @return TRUE If a key was pressed.
* @return FALSE Otherwise.
*/
static int kbhit(void);
/**
* @brief Initializes the p_path serial device. Opens the serial device and
* sets minimal attributes to get it working.
*
* @param[in] p_path The serial device path.
* @param[out] p_fd A file descriptor to link to the serial device above.
* @param[in] speed The serial BAUDRATE.
*
* @return 1 If the serial device was initialized.
* @return 0 If an error has occurred.
*/
uint8_t serial_initialize(
const char *p_path,
int32_t *p_fd,
const uint32_t speed);
//==============================================================================
// STATIC GLOBAL VARIABLES
//==============================================================================
//==============================================================================
// IMPLEMENTATION OF PUBLIC FUNCTIONS
//==============================================================================
int main(void)
{
int serial_fd; /* A file descriptor to manipulate the SERIAL.*/
uint8_t serial_st; /* A status for the serial_initialize func. */
/* Opens SERIAL for reading and writing at SERIAL_BAUD baudrate.*/
serial_st = serial_initialize(SERIAL, &serial_fd, SERIAL_BAUD);
if (serial_st == 0)
{
printf("Error: Failed to open serial %s \r\n", SERIAL);
}
while(!kbhit()) /* While a key from the keyboard isn't pressed... */
{
write(serial_fd, "Running.. Serial is easy\r\n", sizeof("Running.. Serial is easy\r\n"));
}
close(serial_fd);
return EXIT_SUCCESS;
}
//==============================================================================
// IMPLEMENTATION OF STATIC (PRIVATE) FUNCTIONS
//==============================================================================
static int kbhit(void)
{
struct termios oldt, newt;
int ch;
int oldf;
/** Gets the attributes from the standard input file descriptor. */
tcgetattr(STDIN_FILENO, &oldt);
/** Enables raw input. (unprocessed). */
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
/** Sets the new attributes. */
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
/** Gets the file access mode and status flags. */
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
/** Sets the same above read values + the O_NONBLOCK flag. */
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
/** Checks if there is a char on the standard terminal. (nonblock). */
ch = getchar();
/** Make changes now, without waiting for data to complete. */
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
/** Removes the O_NONBLOCK flag. */
fcntl(STDIN_FILENO, F_SETFL, oldf);
/** If any key was hitted, return true. And puts back the pressed key onto
* the terminal. */
if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}
uint8_t serial_initialize(
const char *p_path,
int32_t *p_fd,
const uint32_t speed)
{
uint8_t ret;
/** Tries to open the serial device (p_path).
* O_RDWR: Request read and write permissions. */
*p_fd = open(p_path, O_RDWR);
if (p_fd < 0)
{
printf("Error: Failed to open serial %s \n\n", p_path);
ret = 0;
}
else
{
printf("Serial (%s) opened!!\n\n", p_path);
/** The structure to set and get the device parameters. */
struct termios config;
/** Get the current configuration of the serial interface. */
if (tcgetattr(*p_fd, &config) < 0)
{
printf("Error: Could not get the %s attributes!\n\n", p_path);
}
else
{
printf("Serial (%s) attributes read.. \n\n", p_path);
/** Check if the fd is pointing to a TTY device or not. */
if (!isatty(*p_fd))
{
printf("Error: %s isnt pointing to a TTY device!!\n\n", p_path);
}
else
{
printf("%s is a tty device. Continuing...\n\n", p_path);
/** Setting the baudrate. */
if (cfsetspeed(&config, (speed_t)speed) < 0)
{
printf("Error: Couldn't set the %s baudrate!!\n\n", p_path);
}
else
{
/** No parity. */
config.c_cflag &= ~PARENB;
/** One stop bit. */
config.c_cflag &= ~CSTOPB;
/** Zeroes the char size mask. */
config.c_cflag &= ~CSIZE;
/** Sets data size = 8 bits. */
config.c_cflag |= CS8;
/** Disables HW flow control. */
config.c_cflag &= ~CRTSCTS;
/** Minimum number of characters for read. */
config.c_cc[VMIN] = 1;
/** Timeout in deciseconds for read. (0.5s) */
config.c_cc[VTIME] = 5;
/** Enables READ and ignores control lines. */
config.c_cflag |= CREAD | CLOCAL;
/** Set the terminal to "raw mode". */
cfmakeraw(&config);
/** Flushes the serial dev and sets the new attributes. */
if (tcsetattr(*p_fd, TCSAFLUSH, &config) < 0)
{
printf("Error: Couldn't set the %s attributes!!\n\n",
p_path);
}
else
{
ret = 1;
}
}
}
}
}
return ret;
}
Comunicação Serial utilizando a Bilbioteca PIGPIO
PIGPIO
Similarmente com a prática anterior é possível abstrair o uso do file descriptors através de encapsulamentos que já realizam essa tarefa mecânica para nós. Desta forma, vamos realizar o mesmo exercício utilizando as implementações fornecidas pela biblioteca PIGPIO.
Primeiramente devemos definir o nosso file descriptor para a manipulação do dispositivo serial.
int serial_fd; /* A file descriptor to manipulate the SERIAL.*/
Então inicializamos as funções da bilbioteca PIGPIO:
if (gpioInitialise() < 0)
{
printf("PIGPIO init error\n");
}
Em seguida, devemos atrelar o nosso file descriptor com o arquivo de dispositivo que queremos utilizar por meio da função serOpen
:
/* Opens SERIAL for reading and writing at SERIAL_BAUD baudrate.*/
serial_fd = serOpen(SERIAL, SERIAL_BAUD, 0);
if (serial_fd < 0)
{
printf("Failed to open SERIAL\r\n");
}
Por fim, basta utilizar a função serWrite
passando o nosso file descriptor e a mensagem a ser enviada:
if (serWrite(serial_fd, (char *)"Running.. Serial is easy\r\n",
sizeof("Running.. Serial is easy\r\n")) != 0)
{
printf("Error: Failed to send message.\r\n");
}
Se tudo ocorrer bem a mensagem será enviada através da porta serial ttyS0
.
/*
============================================================================
Name : ex03b.c
Author : gbs|jms
Version :
Copyright : Your copyright notice
Description : A simple C program for demonstrating the serial communication.
============================================================================
*/
//==============================================================================
// USED INTERFACES
//==============================================================================
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Required by open / write system calls. */
#include <pthread.h> /* POSIX threads Library. Required by PIGPIO. */
#include <stdint.h> /* Standard Int Library.(uint32_t, uint8_t...)*/
#include <stdio.h> /* Standard Input/Ouput Library. (printf...) */
#include <stdlib.h> /* General (standard) Library. (EXIT_SUCCESS. */
#include <termios.h> /* Terminal Input/Output interfaces. */
#include <unistd.h> /* Complement (flags, constants, definitions..)
for the POSIX API. */
#include "pigpio.h" /* PIGPIO Library header. */
//==============================================================================
// MACROS AND DATATYPE DEFINITIONS
//==============================================================================
/* HW and Interfaces */
#define SERIAL "/dev/ttyS0"
#define SERIAL_BAUD 115200
//==============================================================================
// STATIC (PRIVATE) FUNCTION PROTOTYPES
//==============================================================================
/**
* @brief A function to verify if any key was pressed on terminal.
*
* @return TRUE If a key was pressed.
* @return FALSE Otherwise.
*/
static int kbhit(void);
//==============================================================================
// STATIC GLOBAL VARIABLES
//==============================================================================
//==============================================================================
// IMPLEMENTATION OF PUBLIC FUNCTIONS
//==============================================================================
int main(void)
{
int serial_fd; /* A file descriptor to manipulate the SERIAL.*/
if (gpioInitialise() < 0)
{
printf("PIGPIO init error\n");
}
/* Opens SERIAL for reading and writing at SERIAL_BAUD baudrate.*/
serial_fd = serOpen(SERIAL, SERIAL_BAUD, 0);
if (serial_fd < 0)
{
printf("Failed to open SERIAL\r\n");
}
while(!kbhit())
{
sleep(1);
if (serWrite(serial_fd, (char *)"Running.. Serial is easy\r\n",
sizeof("Running.. Serial is easy\r\n")) != 0)
{
printf("Error: Failed to send message.\r\n");
}
}
/* Similar to the gpioInitialise function, this one must be called at the end of
* your program.*/
gpioTerminate();
return EXIT_SUCCESS;
}
//==============================================================================s
// IMPLEMENTATION OF STATIC (PRIVATE) FUNCTIONS
//==============================================================================
static int kbhit(void)
{
struct termios oldt, newt;
int ch;
int oldf;
/** Gets the attributes from the standard input file descriptor. */
tcgetattr(STDIN_FILENO, &oldt);
/** Enables raw input. (unprocessed). */
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
/** Sets the new attributes. */
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
/** Gets the file access mode and status flags. */
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
/** Sets the same above read values + the O_NONBLOCK flag. */
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
/** Checks if there is a char on the standard terminal. (nonblock). */
ch = getchar();
/** Make changes now, without waiting for data to complete. */
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
/** Removes the O_NONBLOCK flag. */
fcntl(STDIN_FILENO, F_SETFL, oldf);
/** If any key was hitted, return true. And puts back the pressed key onto
* the terminal. */
if(ch != EOF)
{
ungetc(ch, stdin);
return 1;
}
return 0;
}
Last updated