Skip to content

Commit

Permalink
Add tool that can auto control the DE pin state of RS485.
Browse files Browse the repository at this point in the history
  • Loading branch information
is-qian committed Jun 20, 2024
1 parent 730544e commit 5bb6ff5
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 0 deletions.
25 changes: 25 additions & 0 deletions tools/rs485_control_DE/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Usage
### Install Required Libraries
``` bash
sudo apt-get install libgpiod-dev
```

### Compilation
``` bash
gcc -o rs485_DE rs485_DE.c -lgpiod
```
### Execution
``` bash
sudo ./rs485_DE TTY_DIR DE_CHIP DE_LINE [rs485_dir] [EN_CHIP] [EN_LINE]
```
### Parameter Explanation
TTY_DIR: Serial port device name, e.g., /dev/ttyAMA0
DE_CHIP: gpiochip device number for the DE pin chip, e.g., /dev/gpiochip0
DE_LINE: Number of the DE pin, e.g., 0
rs485_dir: The directory of the created device file, e.g., "/dev/ttyAMA10"
EN_CHIP: gpiochip device number for the enable control pin chip (optional), e.g., /dev/gpiochip0
EN_LINE: Number of the enable control pin (optional), e.g., 0

## Results

A device file in [rs485_dir] will be created. By utilizing this device file, automatic switching between RS485 transmission and reception states can be effortlessly accomplished.
199 changes: 199 additions & 0 deletions tools/rs485_control_DE/rs485_DE.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <gpiod.h>
#include <pty.h>

#define BUFFER_SIZE 256
#define RS485_CONSUMER "RS485_program"

struct gpiod_chip *chip;
struct gpiod_line *line;

struct gpiod_chip *rs485_chip;
struct gpiod_line *rs485_line;

void setup_gpio(char *rs485_de_chip, int rs485_de_line, char *rs485_en_chip, int rs485_en_line)
{
int ret;

// open GPIO controller
chip = gpiod_chip_open(rs485_de_chip);
if (!chip) {
perror("gpiod_chip_open");
exit(EXIT_FAILURE);
}

// request GPIO line
line = gpiod_chip_get_line(chip, rs485_de_line);
if (!line) {
fprintf(stderr, "Failed to get GPIO line %d\n", rs485_de_line);
gpiod_chip_close(chip);
exit(EXIT_FAILURE);
}

// set GPIO as output
ret = gpiod_line_request_output(line, RS485_CONSUMER, 0);
if (ret < 0) {
fprintf(stderr, "Failed to request GPIO line as output: %s\n", strerror(-ret));
gpiod_chip_close(chip);
exit(EXIT_FAILURE);
}
if (rs485_en_chip)
{
// open GPIO controller
rs485_chip = gpiod_chip_open(rs485_en_chip);
if (!rs485_chip) {
perror("gpiod_chip_open");
exit(EXIT_FAILURE);
}

// request GPIO line
rs485_line = gpiod_chip_get_line(rs485_chip, rs485_en_line);
if (!rs485_line) {
fprintf(stderr, "Failed to get GPIO line %d\n", rs485_de_line);
gpiod_chip_close(rs485_chip);
exit(EXIT_FAILURE);
}

// set GPIO as output
ret = gpiod_line_request_output(rs485_line, RS485_CONSUMER, 0);
if (ret < 0) {
fprintf(stderr, "Failed to request GPIO line as output: %s\n", strerror(-ret));
gpiod_chip_close(rs485_chip);
exit(EXIT_FAILURE);
}
gpiod_line_set_value(rs485_line, 1);
}

}

void toggle_gpio_high() {
gpiod_line_set_value(line, 1);
}

void toggle_gpio_low() {
gpiod_line_set_value(line, 0);
}


int main(int argc, char* argv[]) {
int master_fd, slave_fd, tty_fd;
char slave_name[32];
char *tty_name;
struct termios tty_termios;
struct termios tty;
char buffer[BUFFER_SIZE];
ssize_t n;
int ready;
fd_set readfds, writefds;
int rs485_de_line, rs485_en_line;
char *rs485_de_chip, *rs485_en_chip , *rs485_dir;


if(argc < 4) {
printf("Usage: %s /dev/ttyAMA* DE_CHIP(/dev/gpiochip*) DE_LINE [RS485_dir] [EN_CHIP] [EN_LINE]\n", argv[0]);
return 1;
}

tty_name = argv[1];
rs485_de_chip = argv[2];
rs485_de_line = atoi(argv[3]);
rs485_dir = (argc > 4) ? argv[4] : "/dev/ttyAMA10";
rs485_en_chip = (argc > 6) ? argv[5] : NULL;
rs485_en_line = (argc > 6) ? atoi(argv[6]) : -1;

setup_gpio(rs485_de_chip, rs485_de_line, rs485_en_chip, rs485_en_line);

// create pseudo terminal
if (openpty(&master_fd, &slave_fd, slave_name, NULL, NULL) == -1) {
perror("openpty");
return 1;
}

// create symlink
if(symlink(slave_name, rs485_dir) == -1) {
if(errno != EEXIST) {
perror("symlink");
return 1;
} else {
if (unlink(rs485_dir) == -1) {
perror("unlink");
return 1;
}
if(symlink(slave_name, rs485_dir) == -1) {
perror("symlink");
return 1;
}
}
}

// 0666 permissions
chmod(slave_name, 0666);

printf("slave_name: %s new tty: %s\n", slave_name, rs485_dir);
// open serial port
tty_fd = open(tty_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (tty_fd < 0) {
perror("Unable to open serial port");
return 1;
}

// set serial port attributes
tcgetattr(master_fd, &tty);
cfmakeraw(&tty); // set raw mode
tcsetattr(master_fd, TCSANOW, &tty);
tcsetattr(tty_fd, TCSANOW, &tty);
// monitor file descriptors
FD_ZERO(&readfds);

while (1) {
FD_SET(master_fd, &readfds);
FD_SET(tty_fd, &readfds);
int max_fd = (tty_fd > master_fd) ? tty_fd : master_fd;
ready = select(max_fd + 2, &readfds, NULL, NULL, NULL);
if (ready == -1) {
perror("select");
break;
}

if (FD_ISSET(master_fd, &readfds)) {
toggle_gpio_high();
tcgetattr(master_fd, &tty);
tcsetattr(tty_fd, TCSANOW, &tty);
n = read(master_fd, buffer, BUFFER_SIZE - 1);
if (n > 0) {
buffer[n] = '\0'; // make sure string is null terminated
// send data to serial port
write(tty_fd, buffer, n);
// wait for data to be sent
tcdrain(tty_fd);
}
toggle_gpio_low();
}

if (FD_ISSET(tty_fd, &readfds)) {
tcgetattr(master_fd, &tty);
tcsetattr(tty_fd, TCSANOW, &tty);
n = read(tty_fd, buffer, BUFFER_SIZE - 1);
if (n > 0) {
buffer[n] = '\0';
// send data to pseudo terminal
write(master_fd, buffer, n);
// wait for data to be sent
tcdrain(master_fd);
}
}
}

// close serial port
close(master_fd);
close(slave_fd);

return 0;
}

0 comments on commit 5bb6ff5

Please sign in to comment.