Hello community!
I try to set up the TI AM62’s UART interfaces in linux to operate in RS485 mode with multi-drop partiy mode (see TI’s TRM 12.2.4.4.8.3.3.10 Multi-drop Parity Mode with Address Match). As far as I understood it, a RS485 “Master” is sending data to multiple slaves and uses the bit, that is normally used for the parity bit, as a marker to identify that the databits are an address (parity=1) or databits (parity = 0).
When i try to activate rs485 addressing: (code without this line causes no error in the configuration)
rs485conf.flags |= SER_RS485_ADDRB;
I am always getting the error EINVAL
I think it is not supported by the termios driver? At least, the SoC is possible to do multidrop according to the TRM.
My code so far:
int rt485_open(uint8_t port, int baudrate, int databits, int parity, int stopbits)
{
struct termios tty;
if (port >= RT485_NUM_DEVICES) //ports numbers are from 0 to 3
return RT485_ERROR_WRONG_DEV_NUM;
//create pointer (will point to rt485 port structure)
rt485_port_t* rt485_port_ptr = NULL;
//set pointer to selected port structure
rt485_port_ptr = &(g_rt485_ports[port]);
// Lock port mutex
rt485_mutex_lock(&(rt485_port_ptr->mutex));
// Close if already open (do it directly without calling rt485_close to avoid deadlock)
if (rt485_port_ptr->fd != -1)
{
close(rt485_port_ptr->fd);
rt485_port_ptr->fd = -1;
rt485_port_ptr->state = RT485_STATE_CLOSED;
}
// Open TTY device
rt485_port_ptr->fd = open(uart_devices[port], O_RDWR | O_NOCTTY);
if (rt485_port_ptr->fd < 0)
{
rt485_port_ptr->fd = -1;
//in case of an error, unlock mutex before return
rt485_mutex_unlock(&(rt485_port_ptr->mutex));
return RT485_ERROR_OPENING;
}
// Configure TTY
memset(&tty, 0, sizeof(tty));
if (tcgetattr(rt485_port_ptr->fd, &tty) != 0)
{
//in case of an error, close fd, reset it, and unlock mutex before return
close(rt485_port_ptr->fd);
rt485_port_ptr->fd = -1;
rt485_mutex_unlock(&(rt485_port_ptr->mutex));
return RT485_ERROR_CONFIG;
}
// Set baud rate - convert baudrate parameter to speed_t
speed_t speed;
switch (baudrate) {
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 230400: speed = B230400; break;
case 460800: speed = B460800; break;
case 500000: speed = B500000; break;
case 576000: speed = B576000; break;
default: speed = B19200; break; // Default fallback
}
cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);
// Configure parity
switch (parity) {
case RT485_PARITY_NONE:
tty.c_cflag &= ~PARENB;
break;
case RT485_PARITY_EVEN:
tty.c_cflag |= PARENB;
tty.c_cflag &= ~PARODD;
break;
case RT485_PARITY_ODD:
tty.c_cflag |= PARENB;
tty.c_cflag |= PARODD;
break;
default:
tty.c_cflag &= ~PARENB;
break;
}
// Configure stop bits
if (stopbits == 2)
tty.c_cflag |= CSTOPB; // 2 stop bits
else
tty.c_cflag &= ~CSTOPB; // 1 stop bit
// Configure data bits
tty.c_cflag &= ~CSIZE;
switch (databits) {
case 5: tty.c_cflag |= CS5; break;
case 6: tty.c_cflag |= CS6; break;
case 7: tty.c_cflag |= CS7; break;
case 8: tty.c_cflag |= CS8; break;
default:
tty.c_cflag |= CS8;
break;
}
tty.c_cflag |= CREAD | CLOCAL;
// Raw mode, no echo
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_oflag &= ~OPOST;
tty.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL);
// Enable hardware flow control //not necessary
//tty.c_cflag |= CRTSCTS;
// Disable hardware flow control
//tty.c_cflag &= ~CRTSCTS;
// Apply settings
if (tcsetattr(rt485_port_ptr->fd, TCSANOW, &tty) != 0)
{
printf("Error: tcsetattr failed on port %d\n", port);
//in case of an error, close fd, reset it, and unlock mutex before return
close(rt485_port_ptr->fd);
rt485_port_ptr->fd = -1;
rt485_mutex_unlock(&(rt485_port_ptr->mutex));
return RT485_ERROR_SETATTR;
}
// Enable RS485 mode if available
struct serial_rs485 rs485conf;
memset(&rs485conf, 0, sizeof(rs485conf));
rs485conf.flags |= SER_RS485_ENABLED; //| SER_RS485_RTS_ON_SEND;
rs485conf.flags &=~ SER_RS485_RTS_ON_SEND;
rs485conf.flags |= SER_RS485_RTS_AFTER_SEND; // RTS low after sending (receive mode)
rs485conf.delay_rts_before_send = 0; //delays are in milliseconds
rs485conf.delay_rts_after_send = 0; // probably: 1/baudrate * x
if(databits == 9)
{
// Enable 9-bit addressing mode
rs485conf.flags |= SER_RS485_ADDRB;
//rs485conf.addr_recv = 0x05; // This device's address
//rs485conf.addr_dest = 0x01; // Send to this address
}
if(ioctl(rt485_port_ptr->fd, TIOCSRS485, &rs485conf) < 0)
{
switch(errno) {
case ENOTTY:
printf("Warning: RS485 ioctl not supported by this driver (port %d)\n", port);
// Driver doesn't support RS485 mode - continue with manual RTS control
break;
case EINVAL:
printf("Warning: Invalid RS485 configuration (port %d)\n", port);
// Invalid flags or parameters - may need to adjust rs485conf
break;
case ENODEV:
printf("Error: Device disconnected (port %d)\n", port);
close(rt485_port_ptr->fd);
rt485_port_ptr->fd = -1;
rt485_mutex_unlock(&(rt485_port_ptr->mutex));
return RT485_ERROR_OPENING;
case EACCES:
case EPERM:
printf("Error: Permission denied for RS485 config (port %d)\n", port);
break;
default:
printf("Warning: RS485 ioctl failed with errno %d: %s (port %d)\n",
errno, strerror(errno), port);
break;
}
}
rt485_port_ptr->state = RT485_STATE_OPEN;
rt485_mutex_unlock(&(rt485_port_ptr->mutex));
return 0;
}
btw my device tree overlay:
&main_uart2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sodimm_uart2_pins>;
uart-has-rtscts;
linux,rs485-enabled-at-boot-time;
rs485-rts-active-low;
status = "okay";
};
/* Verdin UART_8 */
&main_uart3{
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sodimm_uart3_pins>;
uart-has-rtscts;
linux,rs485-enabled-at-boot-time;
rs485-rts-active-low;
status = "okay";
};