kasbert / epsolar-tracer

Tools for EPsolar Tracer BN solar charge controller
Apache License 2.0
120 stars 76 forks source link

Exar driver question #13

Closed palsan closed 1 year ago

palsan commented 7 years ago

hello dear friend! looking at https://github.com/kasbert/epsolar-tracer/commit/8c21f4afdfd6acd77b6adad59a4dabe5cbf2b947 could you please tell where you got this value for 485 mode? gpio_mode = 0x0b; this patch of yours saved me quite a bit of health and time, so thanks. still curious about where you got that information?

kasbert commented 7 years ago

I asked Exar technical support and they kindly told me what to change.

ygator commented 7 years ago

I'm using this hardcoded version as well. I tried to use it without and programmatically set it to 485 mode through libmodbus, but it did not work.

#if 0
        if(modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485) == -1)
                fprintf(stderr, "Set serial mode failed: %s\n", modbus_strerror(errno));
#endif
palsan commented 7 years ago

Yesterday I also asked for help at Exar, following @kasbert experience. I asked about the value of 0x0b and where it is documented. They answered the following, I quote.

Please have a look at the GPIO_MODE register description in the XR21B1411 datasheet. https://www.exar.com/content/document.ashx?id=20394&languageid=1033&type=Datasheet&partnumber=XR21B1411&filename=XR21B1411.pdf&part=XR21B1411

If you look at section "3.1.1.13 GPIO_MODE Register Description (Read / Write)" on page18, there you'll find description of this value - see bits [2:0] and [3]. It stands for "GPIO5 used for auto RS-485 half-duplex control" + "RS485 Polarity GPIO5/RTS#/RS485 High for TX", which is 0b1011 = 0x0b.

What I understood is that all pins of this chip are General Purpose I/O (GPIO), but pin GPIO5 has additional ability of doing a pre-programmed RS485 communication. That way we just switch this functionality ON.

That is why, I think, @ygator was unable to do this via modbus lib. It has nothing to do with modbus, but instead, it must be set via raw usb "set register" command, performed by xr_usb_serial_set_reg function.

kasbert commented 7 years ago

I guess one should add TIOCSRS485 ioctl for the driver, but that might need changing it to a tty driver (or something - I didn't try).

ygator commented 6 years ago

I'm not sure if anyone would be interested, but I am now using a non-hardcoded version of the driver. To change it to rs485 you do the following:

int fd = modbus_get_socket(ctx);
if(m_set_gpio_mode(fd, HAL_GPIO5_AUTO_RS485_MODE_POL_H))
      eprintf("Set exar rs485 mode failed\n");

However, you do have to add the following to your code #include <xr_usb_serial_ioctl.h>

typedef enum {
        HAL_ALL_PIN_GPIO_MODE                           = 0,
        HAL_GPIO4_5_RTS_CTS_MODE                        = 1,
        HAL_GPIO2_3_DTR_DTS_MODE                        = 2,
        HAL_GPIO5_AUTO_RS485_MODE_POL_L         = 3,
        HAL_GPIO5_AUTO_RS485_MODE_POL_H         =(8+3),
        HAL_GPIO5_AUTO_RS485_MODE_AAM_POL_L     = 4,
        HAL_GPIO5_AUTO_RS485_MODE_AAM_POL_H     =(8+4)
} halGpioMode_e;

int get_gpiomode_reg(int fd,int block,int *value) {
int tmp[2];
int result;

        tmp[0] = block;
        tmp[1] = 0;
        result = ioctl(fd, XR_USB_SERIAL_GET_GPIO_MODE_REG, tmp);
        if(result < 0) {
                printf("Cannot do XR_USB_SERIAL_GET_GPIO_MODE_REG ioctl (%d)\n", result);
        }
        *value = tmp[1];
        return(result);
}

int set_gpiomode_reg(int fd,int block,int value) {
int tmp[2];
int result;

        tmp[0] = block;
        tmp[1] = value;
        result = ioctl(fd, XR_USB_SERIAL_SET_GPIO_MODE_REG, tmp);
        if(result < 0) {
                printf("Cannot do XR_USB_SERIAL_SET_GPIO_MODE_REG ioctl (%d)\n", result);
        }
        return(result);
}

static int m_set_gpio_mode(int fd, halGpioMode_e mode) {
int result = 0;
int val;
int set_mode = 0;

        get_gpiomode_reg(fd,-1,&val);
        set_mode = (val&0xff00)| mode;
        result = set_gpiomode_reg(fd, -1, set_mode);
        return(result);
}