zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.64k stars 6.52k forks source link

Modbus RTU does not work on stm32 with constantly active receive on rs485 transceiver #42366

Open mkolchurin opened 2 years ago

mkolchurin commented 2 years ago

When trying to start modbus on a device with a st485bdr transceiver, where receive is constantly active (RE always low, DE controls by software), a busfault occurs during transmission. This problem occurs on both STM32F105 and STM32F427 boards.

I was able to determine that when waiting for a transmit complete interrupt (TC) and before turning off transmit mode (after all bytes have been sent to the shift register), an unexpected receive interrupt occurs that sends the device into a busfault. I think this is due to the fact that the signal transmitted to the RS485 line is also present at the RO output and creates interference.

I added to the existing disable/enable interrupt functions (in uart_stm32.c) disable/enable the transmitter and receiver:

static void uart_stm32_irq_rx_disable(const struct device *dev)
{
    USART_TypeDef *UartInstance = UART_STRUCT(dev);

    LL_USART_DisableIT_RXNE(UartInstance);
    LL_USART_SetTransferDirection(UartInstance,
                      LL_USART_DIRECTION_TX);
}

after which modbus worked, however, I understand the problems of this solution, and I hope there is a successful solution to this problem.

FRASTM commented 2 years ago

Could PR #40907 help ?

FRASTM commented 2 years ago

The LL_USART_SetTransferDirection is set in both Tx and RX during the uart_stm32_init When disabling the Rx, we expect the LL_USART_DIRECTION_TX is still present, and at one point, the LL_USART_DIRECTION_RX should be restored.

I guess that controlling the direction is possible keeping the LL_USART_GetTransferDirection uart_stm32_irq_rx_disable --> reset LL_USART_DIRECTION_RX but keep LL_USART_DIRECTION_TX if present uart_stm32_irq_rx_enable --> set LL_USART_DIRECTION_RX and keep LL_USART_DIRECTION_TX if present uart_stm32_irq_tx_disable --> reset LL_USART_DIRECTION_TX but keep LL_USART_DIRECTION_RX if present uart_stm32_irq_tx_enable --> set LL_USART_DIRECTION_TX and keep LL_USART_DIRECTION_RX if present

mkolchurin commented 2 years ago

I extended the standard Zephyr API with the function

int uart_direction(const struct device *dev, uint8_t dir);

which chooses one of three directions

enum uart_config_direction{
    UART_CFG_RX_DIRECTION,
    UART_CFG_TX_DIRECTION,
    UART_CFG_RX_TX_DIRECTION,
};

and added this to modbus_serial_rx_on, modbus_serial_tx_on and modbus_serial_disable (UART_CFG_RX_TX_DIRECTION)

I don't know if it's necessary or possible to create a pull request to extend the standard API, but this solved the problem

FRASTM commented 2 years ago

I think so. Maybe you could test with this change : https://github.com/FRASTM/zephyr/commit/190c3f881a5546d9b78ca90eedec13729cbdc005

erwango commented 2 years ago

I extended the standard Zephyr API with the function

int uart_direction(const struct device *dev, uint8_t dir);

which chooses one of three directions

enum uart_config_direction{
    UART_CFG_RX_DIRECTION,
    UART_CFG_TX_DIRECTION,
    UART_CFG_RX_TX_DIRECTION,
};

and added this to modbus_serial_rx_on, modbus_serial_tx_on and modbus_serial_disable (UART_CFG_RX_TX_DIRECTION)

I don't know if it's necessary or possible to create a pull request to extend the standard API, but this solved the problem

@dcpleung @jfischer-no do you think this solution is worth pushing ?

mkolchurin commented 2 years ago

https://github.com/FRASTM/zephyr/commit/190c3f881a5546d9b78ca90eedec13729cbdc005

Thanks for the answer! I can't test right now but will definitely do so tomorrow. I think it will work in this case, but uart_stm32_poll_in will not work after calling uart_stm32_irq_rx_disable (but I don't know if there is a scenario when it is necessary to use polling and interrupts at the same time)

mkolchurin commented 2 years ago

Here are the changes that I think should be made: uart: https://github.com/mkolchurin/zephyr/commit/bb9f3f92e2fd0fe0e137e51a6e9a79040ccaf008 modbus: https://github.com/mkolchurin/zephyr/commit/ca0c3064a5c9e82dc6244de57ad5736b235bd490

jfischer-no commented 2 years ago

@dcpleung @jfischer-no do you think this solution is worth pushing ?

What is "Selects direction of UART", does it change TX and RX lines? That is probably RS-485 specific and should be prefixed / abstracted accordingly. NACK for MODBUS RTU changes, that should be solved differently, there is already bus transceiver control over GPIO lines.

erwango commented 2 years ago

Changing this from 'Bug' to 'Enhancement' and removing STM32 tag is this is likely generic .

romkell commented 2 years ago

Hi we experienced a similar problem with the uart driver (v2.7.x) in order integrate a third party IO-Link core stack. which requires direct control over the different irqs rx, tx-empty, tx-complete at rather high speed, low latency (IO-Link COM2 @ 230kBaud Allowed gaps: 1 TBit @ 230kBaud approx. 4.3 us rx byte gap: max 1 TBit (which is not so much "our" Zepyhr OS firmware side) rx - tx response gap : 1 - 10 Tbit tx byte gap: max. 3Tbit

We have written a STM32 specific extension in our company internal framework, but would be happy to see that or similar implemented in Zephyr OS directly and not for STM32 only.

uart_ext.zip