9 years, 6 months ago.

Nucleo F030R8 RS485 on second uart

I need a RS485 on the second UART so I configure the 1st UART with

   Serial control(SERIAL_TX, SERIAL_RX);   //this transmits and receive on the standard Rx/Tx pins

This works and I can print to control ( resistor mod done, else it is available on the virtual com port).

According to the data sheet the second UART is available on D8 and D2. I thus

   // Instantiate the raw serial (2nd uart) Tx then Rx
   RawSerial  rs485(D8, D2);
   // pin to switch the rs485 buffer direction
   // enable the transmission or receiption of data
   DigitalOut dir_485 (A3); 

    // attach the receiver input to 'run' a method when characters received
    rs485.attach(this,   &dlms_comms::Rx_interrupt,  Serial::RxIrq);

    // transmit empty flag .. need to set buffer in receive mode again
    rs485.attach(this,  &dlms_comms::Tx_interrupt,  Serial::TxIrq);

    // default comms parameters
    SerialBase::Parity my_parity = SerialBase::None;

    // set the class baudrate
    rs485.baud(baudrate);

    // enable the receiver on the transceiver buffer chip
    dir_485 = 0;

    // clear the ring buffer
    rx_head_ptr = 0;
    rx_tail_ptr = 0;
    memset (rx_buffer, 0, sizeof(rx_buffer));

    // set the enum parity as required by class
    switch (parity)
    {
    default:
    case 'n' :
        my_parity = SerialBase::None;
        break;
    case 'o':
        my_parity = SerialBase::Odd;
        break;
    case 'e':
        my_parity = SerialBase::Even;
        break;
    }

    // lets doit .. config the rs485 now
    rs485.format((int) bits,
                 my_parity,
                 (int) stop_bits);

This seem to be ok until I do the format, then the first UART stops to operate, and I do not have a clue whats happening after that, as my debugger is a 'gonner'

Can I use the serial and raw serial class simultaneously ?, why does the raw serial breaks the normal serial comms. Any help will be appreciated. Thanks

1 Answer

9 years, 6 months ago.

I will answer my own question: RawSerial does not use streams and is more 'safe'. and should work even in interrupt functions, so the safe option is to rather use RawSerial if you want to printfs in interrupts functions.

You cannot enable the transmit interrupts and leave it active, it will just generate zillions of interrupts, you should disable the transmit empty when you receive it by

/** ---------------------------------------------------------------------------
 * @brief dlms_comms::Tx_interrupt
 */
void dlms_comms::Tx_interrupt(void)
{
    // enable the receiver, we are finito with the transmission of characters
    // this changes the direction on the transceiver buffer on the rRS485 buffer
    dir_485 = 0;       
    // disable the transmit empty interrupt, to prevent it just to happen again when we exit
    rs485.attach(NULL, Serial::TxIrq);
}

Their also seem to be a bug in mbed , that when you use the first uart printf the receive interrupts on the second uart got deactivated, the fix is just to enable the receive interrupts again on a regular basis,

    // due to a bug in mbed , the debug display printf disable the second uart receive interrupts  
    // this will enable the interrupts on a regular basis

    // when characters received, run the Rx_interrupt method
    rs485.attach(this,
                 &dlms_comms::Rx_interrupt,
                 Serial::RxIrq); 

The transmitter now looks like this

/** ---------------------------------------------------------------------------
 * @brief dlms_comms::send_packet This method sends a serial packet data to the
 * rs485 bus, the directoion of the tranceiver chip is changed to transmit and
 * the packet is tranmitted.
 * @param ptr     Pointer address for start of the packet
 * @param length  Packet length
 * @note          The direction is changed with a interrupt when the transmitter
 *                is empty
 */
void dlms_comms::send_packet (const UINT_8  *ptr,
                              const UINT_8   length)
{
    // local stack variable
    UINT_8 * tmp_ptr =(UINT_8 *) ptr;

    // change the tranceiver direction to output
    dir_485 = 1;
    for (UINT_8 i= 0; i < length; i++)
    {
        rs485.putc (*tmp_ptr++);
    }
    rs485.attach(this,
                 &dlms_comms::Tx_interrupt,
                 Serial::TxIrq);
}

and the receiver . Note I have a 256 byte soft ring buffer, so just inc a unsigned char for the indexes

/** ---------------------------------------------------------------------------
 * @brief dlms_comms::Rx_interrupt . This method received all characters from
 * the uart and saved it in a ring buffer, 256 bytes long. The CPU only has 16
 * byte fifo, this makes it a 256 bit fifo
 */
void dlms_comms::Rx_interrupt(void)
{
    UINT_8 value;
    rx_irq_count++;
    // read the uart and place character in ring buffer
    value = rs485.getc();
    rx_buffer[rx_head_ptr++] = value;
}
/** ---------------------------------------------------------------------------
 * @brief dlms_comms::available This method return if we have any characters
 * available (received on the rs485 port)
 * @return true if characters received on the rs485
 */
bool dlms_comms::char_available (void)
{
    if (rx_head_ptr == rx_tail_ptr)
        // nope
        return false;
    else
        return true;
}
/** ---------------------------------------------------------------------------
 * @brief dlms_comms::get_char This method return the last character received
 * on the rs485 port
 * @return Unsigned byte
 * @note The user must make sure that characters are available before, else
 * this method shall wait for the next character, no timeouts yet
 */
UINT_8 dlms_comms::get_char (void)
{
    // we have to wait for at least on charcter in the ring buffer
    while (char_available() == false);
    // return the character
    return (rx_buffer[rx_tail_ptr++]);
}

I now have a nice RS485 class that works on the Nucleo F030R8

One problem I still have is that the baudrate seem to be to 'out'. I get top bit sets on the receiver when it is not suppose to be set, if I however set the baudrate a bit higher, it does work. This is dependant on the temperature, if I switch it on in the morning the topbit is set, but if I leave it on for a while it the top bit eventually fades, and it works.. This was tested with the SiLabs 210x chipset, and I have hundreds of these in the field without problems,

This issue is more noticeable on 115200, than on 9600. The internal osc seem to drift a bit on the Nucleo.

Can ytou provide some details "You cannot enable the transmit interrupts and leave it active, it will just generate zillions of interrupts, you should disable the transmit empty when you receive it by"

That should not happen if the flags are properly cleared adn the buffer emptied ? or?

Regarding to the bug, please look at sources, and report on github if you see there an issue.

Thanks

posted by Martin Kojtal 12 Nov 2014