UnbufferedSerial sending with Parity Even does not work

I’m using the library mbed-modbus with Mbed-CE, the target is a board with STM32F407VE (custom target).

The code works in general, but in portserial.cpp the xMBPortSerialInit did not pass the parity to UnbufferedSerial object. I added this, but pc.write() is still sending with parity none, although the underlying HAL_init for the uart is called with the correct parameters like 9 bits for 8 data + parity and parity setting is also even.
For testing I added a pc.write() loop into the init function. The string is sent, but with parity none. What can be the problem?

This is a part from portserial.cpp for the init function with my testcode, it is called with MB_PAR_EVEN:

/* ----------------------- System Variables ---------------------------------*/
// UnbufferedSerial pc(USART3_TX, USART3_RX);    // Cam - mbed USB serial port
UnbufferedSerial pc(PD_5, PD_6);
DigitalOut	rs485TxEn(PD_7, 0);

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
    pc.enable_input( xRxEnable );
    pc.enable_output( xTxEnable );
    rs485TxEn = xTxEnable ? 1 : 0;
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    pc.enable_output( false );
    pc.enable_input( false );
    pc.set_blocking( false );
    pc.baud( ulBaudRate );

    // convert MBParity to Mbed Parity
    SerialBase::Parity p = SerialBase::Parity::None;
    if (eParity == eMBParity::MB_PAR_EVEN) {
        p = SerialBase::Parity::Even;
    }  else if (eParity == eMBParity::MB_PAR_ODD) {
        p = SerialBase::Parity::Odd;
    } else {
        return FALSE;
    }
    pc.format(ucDataBits, p);
    
    //pc.attach( &prvvUARTTxReadyISR, SerialBase::TxIrq );
    //pc.attach( &prvvUARTRxISR, SerialBase::RxIrq ); 
    
    // testcode
    pc.enable_output( true );
    rs485TxEn = 1;
    while(true) {
        pc.write( "0123456789", 10 );
        ThisThread::sleep_for(10ms);
    }
    return TRUE;
}

damned, it seems to be a clock init problem. I’ve stepped thru the SetSysClock() and see that the HSE is wrong, it does not fit for my custom target. HSE fails and the fallback HSI is used.
In my testcodes I have printf for Mbed Version and a hello message, usually this fails already when the clock is not configured correctly.

edit:
but the problem is still there, even with fixed HSE clock :frowning:
There should be 8 data + 1 parity bit, but the analyzer is telling me there are 8 data bits.

got it. This enable_output(true) runs the initialization again and restores only the baudrate, but not the format settings, they where reset to default. I think that is not ok and some Mbed problem.

This looks like a bug, feel free to submit a patch to Mbed CE!

yes, but it took already long time to find the reason for this problem.
I think it came with static pin-map: patch for SerialBase class by mprse · Pull Request #12033 · ARMmbed/mbed-os · GitHub
There is the call for _init_func() when both Tx and Rx were disabled. Maybe this can be avoided in the Modbus port, as all the inits are also not necessary.

edit:
the serial_init_direct is called in the enable functions and clears the settings. I don’t know why this is init is necessary.

after further testing, I see that the porting of the modbus lib to Mbed6 can be improved. Calling the enable functions is not necessary, it is enough to attach the ISR or detach by calling attach with nullptr.

A remaining problem is the transmit control for the half duplex driver. STM has an AppNote AN3070 for this. The solution is to use DMA or to use the transmit complete interrupt. This interrupt is not implemented in Mbed, but I will try to do it. Has someone done this already?

1 Like