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.
dlms_comms.cpp
- Committer:
- creatron
- Date:
- 2014-11-23
- Revision:
- 11:ca1e0ca3a673
- Parent:
- 10:e104a1b24165
File content as of revision 11:ca1e0ca3a673:
#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 rs485.attach(NULL, Serial::TxIrq); // when characters received rs485.attach(this, &dlms_comms::Rx_interrupt, Serial::RxIrq); timer = NULL; debug_uart = NULL; tx_irq_count = 0l; rx_irq_count = 0l; } /** --------------------------------------------------------------------------- * @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; tx_irq_count = 0l; rx_irq_count = 0l; // 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); // dummy send packet to rs485 send_packet ("\r\nHello cruel world\r\n",12); # endif if ( rs485.readable()) rs485.getc(); } /** --------------------------------------------------------------------------- * @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) { tx_irq_count++; // disable the transmit interrupt rs485.attach(NULL, Serial::TxIrq); // enable the receiver, we are finito with the transmission of characters // this changes the direction on the transceiver buffer 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; 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++]); } /** --------------------------------------------------------------------------- * @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); } UINT_64 dlms_comms::ret_rx_irq_count (void) { return rx_irq_count; } UINT_64 dlms_comms::ret_tx_irq_count (void) { return tx_irq_count; } bool dlms_comms::ret_dir_485 (void) { return dir_485; } //----[ the end ]--------------------------------------------------------------