UART initialisation: Prevent UART to pull RTS high - linux

I'm writing a RS485 driver for an ARM AT91SAM9260 board on Linux.
When I initialise the UART, the RTS signal line gets high (1). I guess this would and should be the standard behaviour in RS232 operation mode. In RS485 mode however this is not wanted.
I'm using the standard functions provided by the arm-arch section to initialise the UART. Therefore the significant steps are:
at91_register_uart(AT91SAM9260_ID_US2, 3, ATMEL_UART_CTS | ATMEL_UART_RTS);
//consisting of:
// >> configure/mux the pins
at91_set_A_periph(AT91_PIN_PB10, 1); /* TXD */
at91_set_A_periph(AT91_PIN_PB11, 0); /* RXD */
if (pins & ATMEL_UART_RTS)
at91_set_B_periph(AT91_PIN_PC8, 0); /* RTS */
if (pins & ATMEL_UART_CTS)
at91_set_B_periph(AT91_PIN_PC10, 0); /* CTS */
// >> associate the clock
axm_clock_associate("usart3_clk", &pdev->dev, "usart");
// >> et voilà
As you can see with
at91_set_B_periph(AT91_PIN_PC8, 0);
the pull-up on the RTS pin isn't activated.
Why does the UART set the RTS high?
Just because this would be the standard
behaviour in RS232 mode?
Wouldn't it be a better standard for
the UART to keep silent till the
operation mode is explicitly set?

A high RTS signal after initialisation seems to be the standard behaviour on many platforms. It manly depends which serial operation mode the start-up routines anticipates for the interface.
To prevent RTS-high on a ATMEL AT91SAM9260 board running Linux, you have to put the UART into the right mode BEFORE muxing at91_set_X_periph() the Pins and register the device.
Since Linux Kernel Version 2.6.35, the ATMEL serial driver supports the RS485 mode. In this driver the UART is properly configured before setting the Pins (GPIO) to there role.
For my embedded device which is running an older Linux version, I solved the problem with the following line of codes:
/* write control flags */
control |= ATMEL_US_RTSEN;
mode |= ATMEL_US_USMODE_RS485;
UART_PUT(uartbaseaddr, ATMEL_US_CR, control);
UART_PUT(uartbaseaddr, ATMEL_US_MR,mode);
Now the Pins can be muxed the their role
at91_set_X_periph(RTS_PIN, 0);

Related

How to solve Bad File Descriptor for Linux and Modbus

I am trying to setup a Half Duplex RS-485 communication using libmodbus on a Raspberry Pi running Raspian Buster, with a FTDI USB to Serial adapter. My FTDI adapter shows as ttyUSB0 when I run ls /dev/.
I tried the following sample code:
#include <modbus.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
modbus_t *ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1);
if (ctx == NULL) {
fprintf(stderr, "Unable to create the libmodbus context\n");
return 0;
}
if (modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485) == -1) {
fprintf(stderr, "Error setting the serial port as RS-485\n");
fprintf(stderr, "errno: %d\n (EBADF == 9)", errno);
modbus_free(ctx);
return 0;
}
}
Compiled with gcc test1.c -I/usr/include/modbus -lmodbus.
And I get errno as 9, or EBADF, even if I run this code with sudo.
There is a very easy solution to your problem: just don't set MODBUS_RTU_RS485, quite likely you don't need it.
This mode is actually a workaround for devices without automatic (hardware) direction control. As you know, Modbus RTU works over a half-duplex RS485 link (only one device is allowed to talk while all others must be listening only), and hence requires an additional (to RX and TX) signal to control what device is writing to the bus at all times (direction control).
So you would only need to set MODBUS_RTU_RS485 if your device lacks this feature, which is nowadays quite unlikely or if you are building your own transceiver. Most devices based on the FTDI chip, in particular, should have this feature since the chip itself has a TXDEN (transmit enable) pin. See here for more details and a trick to expose the TXDEN signal to a non-default pin.
It is when you don't have this feature (one frequent scenario is when you want to use the embedded UART on your Rpi for Modbus over RS485, implementing your own transceiver) that you need a software (or hardware) workaround. And that's where MODBUS_RTU_RS485 should come handy, repurposing the RTS flow control signal. Unfortunately, most serial drivers (including ftdi_sio, the one you are probably using) don't support this mode (refer again to the link above).
Luckily, there are workarounds to the workaround: see here for a complete discussion. You can also take a look at this answer where I explained how to set up libmodbus with support for toggling the direction on the bus using a GPIO pin on a Rpi (also applicable to most SBCs, I've used this method successfully with a Pocket Chip computer, for instance).
You can find more background on this issue elsewhere: here and here.

Handling pin modes from devicetree to module

I am trying to write a linux device driver for a SPI device, using Atmels SAMA5d35 MPU. Linux version is 4.19.30.
I have configured the pins in my device tree and set the status to "okay".
After that the pins changed mode as expected. My question is then, how do I manipulate pinmodes during runtime? My SPI device is not always on, neither is my driver always loaded. When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs. My idea was to switch between these pin configuration in the device driver, but unfortunately the device tree complaints about using the same pins for both pinctrl-0 and pinctrl-1 (compiles fine, but system wont boot). Although this was just my logical thoughts. I am sure there is a more correct way to do the things I stated above.
spi0: spi#f0004000 {
status = "okay";
ext_adc: ads1258#1 {
compatible = "ti,ads1258";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pinctrl_spi0_default>;
pinctrl-1 = <&pinctrl_spi0_sleep>;
reg = <1>; /* Hardware chipselect SPI0_NPCS1 */
spi-max-frequency = <16000000>; /* According to datasheet */
start-gpios = <&pioD 5 GPIO_ACTIVE_HIGH>;
drdy-gpios = <&pioD 22 GPIO_ACTIVE_LOW>;
enaadc-gpios = <&pioA 26 GPIO_ACTIVE_HIGH>;
};
};
My question is then, how do I manipulate pinmodes during runtime?
That's called "Runtime pinmuxing", which is described in Documentation/pinctrl.txt.
But you may not have to do that.
When that is the case I want the configured SPI pins to change mode to normal GPIOs (for power-consumption).
Is it the drivers task to do that, or is it something one can define in the device tree, when the driver for example is not loaded?
The default mode for a pin (of Atmel/Microchip SoCs) is to be unassigned to any peripheral mode, and be available for use as a GPIO.
All pins that are not assigned to a peripheral or acquired by a driver as a GPIO, end up in the pool of available GPIOs.
So when your SPI driver is not successfully installed, those pins it would use will be in the pool of available GPIOs.
I have tried to implement a pinctrl-0 for setting the pins to SPI mode, and then a pinctrl-1 for setting the same pins to normal GPIOs.
Assigning pins for use by the SPI controller is proper, but there is no mechanism AFAIK for assigning a pin to be an available GPIO.
You can only define properties in the DT for what a device, such as the SPI controller, requires.
There is no mechanism for an "alternate" assignment.
I am sure there is a more correct way to do the things I stated above.
If your driver does not install, then its resources such as its pins should not be allocated. The pins that the SPI would have used will remain unused, and end up in the pool of available GPIOs (unless something else claims/requests one of those pins).
So your DT needs to simply define what your device uses, and leave it at that.
If you still want your driver to modify the state of its pin, then study the section titled "Pin control requests from drivers" in Documentation/pinctrl.txt.

UART SW and HW flow control, linux

Currently, I'm testing flow control between 2 RS485 UART Port (Just connect Rx and RX, Tx and Tx, DTS/CTS is not connected).
Flag setting (between get and set attribute)
HW Flow control:
tty.c_cflag |= CRTSCTS; // RTS/CTS
tty.c_iflag &= ~(IXOFF|IXON|IXANY);
SW Flow control:
tty.c_cflag &= ~CRTSCTS;
tty.c_iflag |= (IXOFF|IXON|IXANY);
I assume that if I set both of UART1 and UART2 are Hardware flow control and baudrate is high (for eg. 460800 bps) or write into UART1 with higher baud-rate, read() from UART2 with lower baud-rate, FIFO (currently is 64byte) will be overflow as same as kernel send some notification.
But actually, it is always write() and read() successful. Could anyone share me suggestion how to observer buffer overflow?
Sorry if my question is a little dump cuz I'm a new linux leaner.
Thanks so much.
There should be no hardware flow control in the RS485 standard.
Since the API is shared with the RS232C standard, it can be called but it will not work effectively.
Also, the 64-byte FIFO you wrote is a hardware (interface chip) buffer, and the device driver also has a software buffer. Buffers often exist in kilobytes.
It is no wonder that even with high speed, transmission and reception of short data size ends normally.
Perform judgments such as overflow by checking the format of received data, and checking the balance and sequence of commands and responses.

How to set a pin between boot and module load for an embedded Linux?

I am facing an interesting problem. I like to set a pin of my cpu. So far I created a module for controlling the pin. This module is setting the default pin configuration(high). The default pin configuration becomes active when the module is loaded. That is working fine.
Now I like to enable my default pin configuration right away from the start of boot process. To archive that I wrote a patch for the ATMEL bootloader(at91bootstrap) and the uboot(u-boot-at91). This works fine till the moment the kernel starts. At the kernel start the pins is set to the ATMEL default. They pins are defined in the device tree as gpios.
I think I have two possibilities:
1st - patch the /init/main.c at the "right" spot. Where is the right spot?
2nd - modify the device tree to set the GPIOs to an other default value. How to "re set" an gpio a value?
When you modify the pin in u-boot level; it will be override by the kernel. /init/main.c is worse idea and so I think you should not followed this approach
In kernel level if you you want to set the GPIO pin, there are two possibilities (apart from your module)
1) Kernel board file.
2) Device tree file.
Suppose if you want to set the SDIO pin as GPIO in i.MX6 board then you need to modify the code like this.
leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_leds>;
red {
gpios = <&gpio7 0 0>;
default-state = "on";
};
MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 0x1b0b0 //set the pin as GPIO
For the default state of the pin please refer the datasheet of your processor. And one pin may have different functionalities.
And if you want to use the same pin for some other functionalities instead of GPIO then you can choose on of the below configuration.
MX6QDL_PAD_SD3_DAT5__UART2_RX_DATA //UART2 RX Data pin
MX6QDL_PAD_SD3_DAT5__GPIO7_IO00 //As a GPIO pin
MX6QDL_PAD_SD3_DAT6__UART1_RX_DATA // UART1 RX data
MX6QDL_PAD_SD3_DAT6__SD3_DATA6 //SDIO pin which is default in this case
Please add more details if you are looking for something more (like pin number and device tree file name).

D2XX receive unexpected data from UART mode FTDI device on Linux Ubuntu 14.04 64bits

An acquisition system based on a FPGA and a micro-controller is using two FTDI (FT2232H) devices. Basically, the FPGA is generating data (as fast as possible) and sending to a system processor via the FT2232H (asynchronous FIFO mode). Also, the micro-controller is connected to the system processor via another FTDI device in UART mode.
(clickable, from http://postimg.org/image/ovpov6ujv/)
The whole system is already working on Windows platform with success, reaching ~95MB/s in the FTDI mode using the D2XX driver. The other FTDI device IC is connected using Virtual COM driver and the data throughput is not measured (don't need to go fast).
On Linux the results are different. For both devices (UART and FIFO mode) the FTDI's D2XX direct driver is used.
The data transmitted via FIFO mode IC is droping byte (about 10% of the total data, 30MB). The received data from the UART mode IC contain unexpected values. The function FT_GetQueueStatus return without error and the field RxBytes greater than zero (0) even if the micro-controller don’t send any data via UART. The data received usually is something like 0x01 and 0x60, multiples times. The frequency change when the latency time change.
Below the test case details.
Setup the device:
ftStatus |= FT_ResetDevice(*ftHandle); //Reset USB device
ftStatus |= FT_Purge(*ftHandle, FT_PURGE_RX | FT_PURGE_TX); // Purge transmit and receive buffers
ftStatus |= FT_SetUSBParameters(*ftHandle, USB_TRANSFER_SIZE, 0); //Set USB request transfer size
ftStatus |= FT_SetTimeouts(*ftHandle, 100, 100); //Sets the read and write timeouts in milliseconds for the FT2232H
ftStatus |= FT_SetLatencyTimer(*ftHandle, 4); //Set the latency timer
// For the device using UART mode, the following function are also executed:
if(serial) {
ftStatus |= FT_SetBitMode(*ftHandle, 0x00, 0); //Turn off bit bang mode
ftStatus |= FT_SetBaudRate(*ftHandle, 9600); // Set baud rate to 9600
ftStatus |= FT_SetDataCharacteristics(*ftHandle, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); // Set to communicate at 8N1
ftStatus |= FT_SetFlowControl(*ftHandle, FT_FLOW_NONE, 0, 0); // Disable hardware / software flow control
}
Read data:
dwNumInputBuffer = USB_TRANSFER_DATA_SIZE;
ftStatus = FT_Read(ftHandle, &InputBuffer, dwNumInputBuffer,
&dwNumBytesRead); //Read out the data from input buffer
if (ftStatus != FT_OK) {
printf("Fail reading from USB device!\n");
done = 1;
}
if (dwNumBytesRead > 0) {
// move the data to other location memory (and process once all the expected data is received)
}
The system specification:
Windows 7 64 bit, 64GB RAM, SSD hard disk:
FIFO mode IC: D2XX library release date: 2014-09-29
UART mode IC: VCP Drivers release date: 2014-09-29
Ubuntu 14.04 64bits, 8GB RAM, standard hard disk:
FIFO mode IC: D2XX library release date: 2012-06-29, 1.1.12
UART mode IC: D2XX library release date: 2012-06-29, 1.1.12
The D2XX library is used for both FTDI devices because the USB-SERIAL driver need to be shutdown according with FTDI suggestions FTDI Drivers Installation Guide for Linux. When using one (1) UART mode FTDI device and USB-SERIAL driver (standard with Linux Kernel) the application don't receive unexpected results.
Thanks for any suggestions.

Resources