-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tool that can auto control the DE pin state of RS485.
- Loading branch information
Showing
2 changed files
with
224 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
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. |
This file contains 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
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; | ||
} |