8 years, 8 months ago.

BufferedSerial: no stop bits sent?

Hi!

I am trying to use BufferedSerial to send 8 bytes via the USART1 (ultimately through an RS485 transceiver, to a ModBus device) of my Nucleo F411RE board (TX=PA_7; RX=PA_10) using the following code: (only relevant lines inserted)

code snippet

#include "mbed.h"
#include "BufferedSerial.h"

#define USART_INT_BUFFER_SIZE 255
#define RS485_TRANSMIT 1
#define RS485_RECEIVE 0

Serial pc(USBTX, USBRX);
BufferedSerial usart_Int(PA_9, PA_10, 256, 4);  // tx, rx, buffer size, number of printf()s present on the ring buffer at a time
DigitalOut rs485_DE(PA_15);

char tx_buffer_Int[USART_INT_BUFFER_SIZE];

int main()
{
    rs485_DE = RS485_RECEIVE;
    pc.baud(115200);
    pc.format(8,Serial::None,1);
    usart_Int.baud(19200);
    usart_Int.format(8,Serial::Even,1);
    usart_Int.attach(&Rx_interrupt_Int, Serial::RxIrq);

    tx_buffer_Int[0] = 0x01;
    tx_buffer_Int[1] = 0x03;
    tx_buffer_Int[2] = 0x03;
    tx_buffer_Int[3] = 0xE8;
    tx_buffer_Int[4] = 0x00;
    tx_buffer_Int[5] = 0x01;
    tx_buffer_Int[6] = 0x04;
    tx_buffer_Int[7] = 0x7A;
    
    rs485_DE = RS485_TRANSMIT;
    wait_ms(1);
    usart_Int.write(tx_buffer_Int, 8);
    wait_ms(5);
    rs485_DE = RS485_RECEIVE;
    pc.printf("\ndone");
}

This nicely transmits the 8 bytes, but at the end of the last byte, it fails to issue a Stop bit in my opinion. What you can see in the following oscilloscope capture:

/media/uploads/vectorcontrols/modbus_packet_captured.jpg

The green trace shows how the same package was transmitted from a PC program. The blue trace show the captured package when the Nucleo board sends it.

Both times the traces were captured at the receiving ModBus device, after its RS485 transceiver (there are 3V3 logic levels there). The byte decoding was done by the PicoScope program, and I have verified that separately to be correct.

After the package is sent to the ModBus end device, it should respond with a package (that is what one can see following the green trace, when it goes to low level for prolonged time on the receiving line during transmitting its response package). The PC program always gets a response, the Nucleo board never.

Any ideas?

Thanks! Best Regards: Balazs

2 Answers

8 years, 8 months ago.

There is a problem with the nucleo libs sending parity. The parity and the last databit seem to be interfering. See discussion here. The problem was raised as issue on github. A quick fix that seems to work is setting the number of databits to 9 rather than 8 when you want the parity bit.

Accepted Answer

Hi Wim,

I have tried what you have suggested, and it worked for me too. My ModBus device responds now, as expected.

Thank you for your help, Best Regards: Balazs

posted by Vector Controls 17 Jul 2015
8 years, 8 months ago.

Ignore most of this - see update below.....

The stop bit is there. Your last byte is FA. Serial data is sent LSB first so the data should be

0101 1111

The start bit is always 0 The stop bit is always 1

Even parity, 6 1's in the data so the parity bit is 1.

This gives a data sequence of 0 0101 1111 1, exactly what the blue trace on your scope shows. The green trace seems to then have an extra byte of value FF transmitted followed after a pause by a break signal.

UPDATE--

Sorry, I just looked at the code, it's trying to send 7A not FA isn't it, that's what that last low pulse is on the green trace.

So it's not the stop bit that's missing, it's the final 0 in the data byte. No idea how that could be happening.

Could you be switching the bus direction too soon?

You write the data and then 5ms later you change the bus direction. 8 bytes of 11 bits (8 data, 1 start, 1 stop, 1 parity) at 19200 = 4.6ms

0.4ms to start transmitting or lost between characters seems like it should be enough of a margin but it's not massive and could explain what you see. Maybe try increasing it to 6ms and see if things start working.

posted by Andy A 16 Jul 2015

Hi Andy,

Thanks for your response, What you say makes sense, but my original last byte is 0x7A (0101 1110). This is then changed to achieve even parity: 0101 1111 (it becomes 0xFA).

Your response made me think about the original purpose of this packet, namely it shall be a ModBus RTU packet. As such, I think the RTU protocol definitions shall be applied: The RTU format is: 1 Start bit, 8 data bits, 1 parity bit and one Stop bit. So 11 bits in total. The rule of the RTU octet structure is that if no parity is used then it is 1 Start bit, 8 data bits, no parity bit and two Stop bits, to keep it always 11 bits long. Now this seems to be a problem, that I am not able to set the serial port data bit length to 8 bits + the parity bit.

posted by Vector Controls 16 Jul 2015

Try usart_Int.format(9,Serial::Even,1)

The ST documentation all seems to indicate that data bits is including the parity bit.

posted by Andy A 16 Jul 2015

Hi Andy,

Thank you for your feedback and thoughts.

First this code switched the drive enable pin of the RS485 transceiver only after 10ms to receive mode, then I have decreased it to the point where it didn't work anymore (4ms), and then increased it again a little bit. Then I also made the calculations to verify this which aligns with your calculation of 4.6ms.

Later I wanted to make this code either "transmit interrupt-driven" or any other way that could have worked for optimal bus driving timing, and then I have encountered this problem of a missing bit.

I believe Wim's conclusion of a bug in the Nucleo libs makes full sense to me.

The only reason I did not try the 9 bit data mode so far was that in the docs of Serial lib it was written that "Data length - Data transferred can be either 7 or 8 bits long. The default setting for a Serial connection on the mbed Microcontroller is 8 bits."

I will keep you posted if the quick fix worked!

Once again, thank you for your help!

Best Regards: Balazs

posted by Vector Controls 17 Jul 2015