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-11
- Revision:
- 8:595258a79939
- Parent:
- 7:cfe1e0eafb7e
- Child:
- 9:d49cdc77f867
File content as of revision 8:595258a79939:
#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); DigitalOut dir_485 (D7); /** --------------------------------------------------------------------------- * @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)); // 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 // Serial::TxIrq); // attach the receiver input to 'run' a method // when characters received rs485.attach(this, &dlms_comms::Rx_interrupt, //RawSerial::RxIrq Serial::RxIrq); timer = NULL; debug_uart = NULL; tx_irq_count = 0l; rx_irq_count = 0l; // /** Set the flow control type on the serial port // * // * @param type the flow control type (Disabled, RTS, CTS, RTSCTS) // * @param flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for CTS) // * @param flow2 the second flow control pin (CTS for RTSCTS) // */ // rs485.set_flow_control(RTSCTS , //Flow type, // dir_485, //PB_0, //PinName flow1=NC, // dir_485 //PB_0 //PinName flow2=NC // ); } /** --------------------------------------------------------------------------- * @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); //# endif // dummy send packet to rs485 //send_packet ("\r\nHello cruel world\r\n",12); 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++; // 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; 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 ]--------------------------------------------------------------