This is a RS485 class that uses the second UART and was tested on a Nucleo F030R8. A main demo program howto use the class is included. This class control the direction pin on the transceiver buffer automatically, and used transmit and receive interrupts. Ring buffers (256 bytes) are implemented on both transmission and reception. It assumes a ADM3485 'type' buffer where pins 2 ans 3 are connected and seen as direction. This test program could easily be adapted as base for other programs.

Dependencies:   mbed

Revision:
0:044dfba47660
Child:
1:7091401482c5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dlms_comms.cpp	Tue Nov 11 16:01:12 2014 +0000
@@ -0,0 +1,197 @@
+#include "mbed.h"
+
+#include "RawSerial.h"
+#include "dlms_comms.h"
+
+//#define DEBUG_DLMS_COMMS 
+
+   // Instantiate the raw serial (2nd uart) Tx then Rx
+   RawSerial  rs485(PA_9, PA_10);
+  // pin to switch the rs485 buffer direction
+   DigitalOut dir_485 (PB_0);
+
+/** ---------------------------------------------------------------------------
+ * @brief  C O N S T R U C T O R
+ */
+dlms_comms::dlms_comms (void)
+{
+    // enable the rs485 buffer receiver, 
+    dir_485 = 0;
+
+    // clear the pointers and ring buffer
+    rx_head_ptr = 0;
+    rx_tail_ptr = 0;
+    memset (rx_buffer, 0, sizeof(rx_buffer));
+
+//    // attach the receiver input to 'run' a method
+//    // when characters received
+//    rs485.attach(this,
+//                 &dlms_comms::Rx_interrupt,
+//                 RawSerial::RxIrq);
+    // transmit empty flag .. need to set buffer
+    // in receive mode again after we have transmitted
+    // all data characters
+    rs485.attach(this,
+                 &dlms_comms::Tx_interrupt,
+                 RawSerial::TxIrq);
+    timer = NULL;
+    debug_uart = NULL;
+}
+/** ---------------------------------------------------------------------------
+ * @brief bq34z110::init_port_expander Put all ports in INPUT mode
+ * @param parent_uart  The debug uart class pionter
+ * @param parent_timer The main timer (system tick) class pointer
+ */
+void dlms_comms::init  (RawSerial * parent_uart,
+                        Timer     * parent_timer)
+{  
+    // we use the parent timer and debug port
+    debug_uart  = parent_uart; 
+    timer       = parent_timer;
+//#ifdef DEBUG_DLMS_COMMS    
+    debug_uart->printf ("   init_dlms_comms..\r\n");
+//#endif      
+}
+/** ---------------------------------------------------------------------------
+ * @brief dlms_comms::initialise This method initialise the rs485 comms port
+ *
+ * @param baudrate   The RS485 baudrate
+ * @param bits       The RS485 number of bits (8)
+ * @param parity     The parity required 'n','o', 'e'
+ * @param stop_bits  The number of stop bits 1 or 2
+ */
+void dlms_comms::initialise (INT_16      baudrate,
+                             UINT_8      bits,
+                             UINT_8      parity,
+                             UINT_8      stop_bits )
+{
+    // default comms parameters
+    SerialBase::Parity my_parity = SerialBase::None;
+    irq_count = 0;
+    // set the class baudrate
+    rs485.baud(baudrate);
+    // enable the receiver on the tranceiver 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);
+#  ifdef DEBUG_DLMS_COMMS    
+     debug_uart->printf ("   init_dlms_comms..Baudrate (%d) %d bits Parity=%c\r\n",
+                          baudrate, bits, parity);
+#  endif                          
+     // dummy send packet to rs485 
+     //send_packet ("\r\nHello cruel world\r\n",12);       
+}
+/** ---------------------------------------------------------------------------
+ * @brief dlms_comms::get_dir485 . This method  returns the state of the rs485
+ * direction
+ */
+bool dlms_comms::get_dir485 (void)
+{
+    return dir_485;
+}
+/** ---------------------------------------------------------------------------
+ * @brief dlms_comms::Tx_interrupt
+ */
+void dlms_comms::Tx_interrupt(void)
+{
+    irq_count++;
+    // enable the receiver, we are finito with the transmission of characters
+    // this changes the direction on the transceiver buffer
+    if (dir_485 == 1)
+        dir_485 = 0;       
+}
+/** ---------------------------------------------------------------------------
+ * @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;
+    // 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++]);
+}
+/** ---------------------------------------------------------------------------
+ * @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++);
+    }
+}
+UINT_64 dlms_comms::ret_irq_count (void)
+{
+    return irq_count;
+}
+bool dlms_comms::poll_rs485(void)
+{
+    if (rs485.readable ())
+    {
+       Rx_interrupt();
+       return true;
+    }
+    return false;
+}
+//----[ the end ]--------------------------------------------------------------