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@11:ca1e0ca3a673, 2014-11-23 (annotated)
- Committer:
- creatron
- Date:
- Sun Nov 23 08:13:26 2014 +0000
- Revision:
- 11:ca1e0ca3a673
- Parent:
- 10:e104a1b24165
Commit to be publised
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
creatron | 0:044dfba47660 | 1 | #include "mbed.h" |
creatron | 0:044dfba47660 | 2 | |
creatron | 0:044dfba47660 | 3 | #include "RawSerial.h" |
creatron | 0:044dfba47660 | 4 | #include "dlms_comms.h" |
creatron | 0:044dfba47660 | 5 | |
creatron | 0:044dfba47660 | 6 | //#define DEBUG_DLMS_COMMS |
creatron | 0:044dfba47660 | 7 | |
creatron | 11:ca1e0ca3a673 | 8 | // Instantiate the raw serial (2nd uart) Tx then Rx |
creatron | 0:044dfba47660 | 9 | RawSerial rs485(PA_9, PA_10); |
creatron | 11:ca1e0ca3a673 | 10 | |
creatron | 11:ca1e0ca3a673 | 11 | // pin to switch the rs485 buffer direction |
creatron | 10:e104a1b24165 | 12 | DigitalOut dir_485 (PB_0); |
creatron | 8:595258a79939 | 13 | |
creatron | 0:044dfba47660 | 14 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 15 | * @brief C O N S T R U C T O R |
creatron | 0:044dfba47660 | 16 | */ |
creatron | 0:044dfba47660 | 17 | dlms_comms::dlms_comms (void) |
creatron | 0:044dfba47660 | 18 | { |
creatron | 0:044dfba47660 | 19 | // enable the rs485 buffer receiver, |
creatron | 0:044dfba47660 | 20 | dir_485 = 0; |
creatron | 0:044dfba47660 | 21 | |
creatron | 0:044dfba47660 | 22 | // clear the pointers and ring buffer |
creatron | 0:044dfba47660 | 23 | rx_head_ptr = 0; |
creatron | 0:044dfba47660 | 24 | rx_tail_ptr = 0; |
creatron | 0:044dfba47660 | 25 | memset (rx_buffer, 0, sizeof(rx_buffer)); |
creatron | 0:044dfba47660 | 26 | |
creatron | 8:595258a79939 | 27 | // attach the receiver input to 'run' a method |
creatron | 9:d49cdc77f867 | 28 | rs485.attach(NULL, Serial::TxIrq); |
creatron | 8:595258a79939 | 29 | |
creatron | 7:cfe1e0eafb7e | 30 | // when characters received |
creatron | 7:cfe1e0eafb7e | 31 | rs485.attach(this, |
creatron | 7:cfe1e0eafb7e | 32 | &dlms_comms::Rx_interrupt, |
creatron | 7:cfe1e0eafb7e | 33 | Serial::RxIrq); |
creatron | 7:cfe1e0eafb7e | 34 | timer = NULL; |
creatron | 0:044dfba47660 | 35 | debug_uart = NULL; |
creatron | 3:29454cac7930 | 36 | tx_irq_count = 0l; |
creatron | 3:29454cac7930 | 37 | rx_irq_count = 0l; |
creatron | 0:044dfba47660 | 38 | } |
creatron | 0:044dfba47660 | 39 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 40 | * @brief bq34z110::init_port_expander Put all ports in INPUT mode |
creatron | 0:044dfba47660 | 41 | * @param parent_uart The debug uart class pionter |
creatron | 0:044dfba47660 | 42 | * @param parent_timer The main timer (system tick) class pointer |
creatron | 0:044dfba47660 | 43 | */ |
creatron | 0:044dfba47660 | 44 | void dlms_comms::init (RawSerial * parent_uart, |
creatron | 0:044dfba47660 | 45 | Timer * parent_timer) |
creatron | 0:044dfba47660 | 46 | { |
creatron | 0:044dfba47660 | 47 | // we use the parent timer and debug port |
creatron | 0:044dfba47660 | 48 | debug_uart = parent_uart; |
creatron | 0:044dfba47660 | 49 | timer = parent_timer; |
creatron | 11:ca1e0ca3a673 | 50 | #ifdef DEBUG_DLMS_COMMS |
creatron | 11:ca1e0ca3a673 | 51 | debug_uart->printf (" init_dlms_comms..\r\n"); |
creatron | 11:ca1e0ca3a673 | 52 | #endif |
creatron | 0:044dfba47660 | 53 | } |
creatron | 0:044dfba47660 | 54 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 55 | * @brief dlms_comms::initialise This method initialise the rs485 comms port |
creatron | 0:044dfba47660 | 56 | * |
creatron | 0:044dfba47660 | 57 | * @param baudrate The RS485 baudrate |
creatron | 0:044dfba47660 | 58 | * @param bits The RS485 number of bits (8) |
creatron | 0:044dfba47660 | 59 | * @param parity The parity required 'n','o', 'e' |
creatron | 0:044dfba47660 | 60 | * @param stop_bits The number of stop bits 1 or 2 |
creatron | 0:044dfba47660 | 61 | */ |
creatron | 0:044dfba47660 | 62 | void dlms_comms::initialise (INT_16 baudrate, |
creatron | 0:044dfba47660 | 63 | UINT_8 bits, |
creatron | 0:044dfba47660 | 64 | UINT_8 parity, |
creatron | 0:044dfba47660 | 65 | UINT_8 stop_bits ) |
creatron | 0:044dfba47660 | 66 | { |
creatron | 0:044dfba47660 | 67 | // default comms parameters |
creatron | 0:044dfba47660 | 68 | SerialBase::Parity my_parity = SerialBase::None; |
creatron | 3:29454cac7930 | 69 | tx_irq_count = 0l; |
creatron | 3:29454cac7930 | 70 | rx_irq_count = 0l; |
creatron | 0:044dfba47660 | 71 | // set the class baudrate |
creatron | 0:044dfba47660 | 72 | rs485.baud(baudrate); |
creatron | 0:044dfba47660 | 73 | // enable the receiver on the tranceiver buffer chip |
creatron | 0:044dfba47660 | 74 | dir_485 = 0; |
creatron | 0:044dfba47660 | 75 | // clear the ring buffer |
creatron | 0:044dfba47660 | 76 | rx_head_ptr = 0; |
creatron | 0:044dfba47660 | 77 | rx_tail_ptr = 0; |
creatron | 0:044dfba47660 | 78 | memset (rx_buffer, 0, sizeof(rx_buffer)); |
creatron | 0:044dfba47660 | 79 | // set the enum parity as required by class |
creatron | 0:044dfba47660 | 80 | switch (parity) |
creatron | 0:044dfba47660 | 81 | { |
creatron | 0:044dfba47660 | 82 | default: |
creatron | 0:044dfba47660 | 83 | case 'n' : |
creatron | 0:044dfba47660 | 84 | my_parity = SerialBase::None; |
creatron | 0:044dfba47660 | 85 | break; |
creatron | 0:044dfba47660 | 86 | case 'o': |
creatron | 0:044dfba47660 | 87 | my_parity = SerialBase::Odd; |
creatron | 0:044dfba47660 | 88 | break; |
creatron | 0:044dfba47660 | 89 | case 'e': |
creatron | 0:044dfba47660 | 90 | my_parity = SerialBase::Even; |
creatron | 0:044dfba47660 | 91 | break; |
creatron | 0:044dfba47660 | 92 | } |
creatron | 0:044dfba47660 | 93 | // lets doit .. config the rs485 now |
creatron | 0:044dfba47660 | 94 | rs485.format((int) bits, |
creatron | 0:044dfba47660 | 95 | my_parity, |
creatron | 0:044dfba47660 | 96 | (int) stop_bits); |
creatron | 11:ca1e0ca3a673 | 97 | # ifdef DEBUG_DLMS_COMMS |
creatron | 11:ca1e0ca3a673 | 98 | debug_uart->printf (" init_dlms_comms..Baudrate (%d) %d bits Parity=%c\r\n", |
creatron | 11:ca1e0ca3a673 | 99 | baudrate, bits, parity); |
creatron | 0:044dfba47660 | 100 | // dummy send packet to rs485 |
creatron | 11:ca1e0ca3a673 | 101 | send_packet ("\r\nHello cruel world\r\n",12); |
creatron | 11:ca1e0ca3a673 | 102 | # endif |
creatron | 6:70460dcbc43c | 103 | if ( rs485.readable()) |
creatron | 6:70460dcbc43c | 104 | rs485.getc(); |
creatron | 0:044dfba47660 | 105 | } |
creatron | 0:044dfba47660 | 106 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 107 | * @brief dlms_comms::get_dir485 . This method returns the state of the rs485 |
creatron | 0:044dfba47660 | 108 | * direction |
creatron | 0:044dfba47660 | 109 | */ |
creatron | 0:044dfba47660 | 110 | bool dlms_comms::get_dir485 (void) |
creatron | 0:044dfba47660 | 111 | { |
creatron | 0:044dfba47660 | 112 | return dir_485; |
creatron | 0:044dfba47660 | 113 | } |
creatron | 0:044dfba47660 | 114 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 115 | * @brief dlms_comms::Tx_interrupt |
creatron | 0:044dfba47660 | 116 | */ |
creatron | 0:044dfba47660 | 117 | void dlms_comms::Tx_interrupt(void) |
creatron | 0:044dfba47660 | 118 | { |
creatron | 3:29454cac7930 | 119 | tx_irq_count++; |
creatron | 11:ca1e0ca3a673 | 120 | |
creatron | 11:ca1e0ca3a673 | 121 | // disable the transmit interrupt |
creatron | 11:ca1e0ca3a673 | 122 | rs485.attach(NULL, Serial::TxIrq); |
creatron | 0:044dfba47660 | 123 | // enable the receiver, we are finito with the transmission of characters |
creatron | 0:044dfba47660 | 124 | // this changes the direction on the transceiver buffer |
creatron | 9:d49cdc77f867 | 125 | dir_485 = 0; |
creatron | 0:044dfba47660 | 126 | } |
creatron | 0:044dfba47660 | 127 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 128 | * @brief dlms_comms::Rx_interrupt . This method received all characters from |
creatron | 0:044dfba47660 | 129 | * the uart and saved it in a ring buffer, 256 bytes long. The CPU only has 16 |
creatron | 0:044dfba47660 | 130 | * byte fifo, this makes it a 256 bit fifo |
creatron | 0:044dfba47660 | 131 | */ |
creatron | 0:044dfba47660 | 132 | void dlms_comms::Rx_interrupt(void) |
creatron | 0:044dfba47660 | 133 | { |
creatron | 0:044dfba47660 | 134 | UINT_8 value; |
creatron | 3:29454cac7930 | 135 | rx_irq_count++; |
creatron | 0:044dfba47660 | 136 | // read the uart and place character in ring buffer |
creatron | 0:044dfba47660 | 137 | value = rs485.getc(); |
creatron | 0:044dfba47660 | 138 | rx_buffer[rx_head_ptr++] = value; |
creatron | 0:044dfba47660 | 139 | } |
creatron | 0:044dfba47660 | 140 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 141 | * @brief dlms_comms::available This method return if we have any characters |
creatron | 0:044dfba47660 | 142 | * available (received on the rs485 port) |
creatron | 0:044dfba47660 | 143 | * @return true if characters received on the rs485 |
creatron | 0:044dfba47660 | 144 | */ |
creatron | 0:044dfba47660 | 145 | bool dlms_comms::char_available (void) |
creatron | 0:044dfba47660 | 146 | { |
creatron | 0:044dfba47660 | 147 | if (rx_head_ptr == rx_tail_ptr) |
creatron | 0:044dfba47660 | 148 | // nope |
creatron | 0:044dfba47660 | 149 | return false; |
creatron | 0:044dfba47660 | 150 | else |
creatron | 0:044dfba47660 | 151 | return true; |
creatron | 0:044dfba47660 | 152 | } |
creatron | 0:044dfba47660 | 153 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 154 | * @brief dlms_comms::get_char This method return the last character received |
creatron | 0:044dfba47660 | 155 | * on the rs485 port |
creatron | 0:044dfba47660 | 156 | * @return Unsigned byte |
creatron | 0:044dfba47660 | 157 | * @note The user must make sure that characters are available before, else |
creatron | 0:044dfba47660 | 158 | * this method shall wait for the next character, no timeouts yet |
creatron | 0:044dfba47660 | 159 | */ |
creatron | 0:044dfba47660 | 160 | UINT_8 dlms_comms::get_char (void) |
creatron | 0:044dfba47660 | 161 | { |
creatron | 0:044dfba47660 | 162 | // we have to wait for at least on charcter in the ring buffer |
creatron | 0:044dfba47660 | 163 | while (char_available() == false); |
creatron | 0:044dfba47660 | 164 | // return the character |
creatron | 0:044dfba47660 | 165 | return (rx_buffer[rx_tail_ptr++]); |
creatron | 0:044dfba47660 | 166 | } |
creatron | 0:044dfba47660 | 167 | /** --------------------------------------------------------------------------- |
creatron | 0:044dfba47660 | 168 | * @brief dlms_comms::send_packet This method sends a serial packet data to the |
creatron | 0:044dfba47660 | 169 | * rs485 bus, the directoion of the tranceiver chip is changed to transmit and |
creatron | 0:044dfba47660 | 170 | * the packet is tranmitted. |
creatron | 0:044dfba47660 | 171 | * @param ptr Pointer address for start of the packet |
creatron | 0:044dfba47660 | 172 | * @param length Packet length |
creatron | 0:044dfba47660 | 173 | * @note The direction is changed with a interrupt when the transmitter |
creatron | 0:044dfba47660 | 174 | * is empty |
creatron | 0:044dfba47660 | 175 | */ |
creatron | 0:044dfba47660 | 176 | void dlms_comms::send_packet (const UINT_8 *ptr, |
creatron | 0:044dfba47660 | 177 | const UINT_8 length) |
creatron | 0:044dfba47660 | 178 | { |
creatron | 0:044dfba47660 | 179 | // local stack variable |
creatron | 0:044dfba47660 | 180 | UINT_8 * tmp_ptr =(UINT_8 *) ptr; |
creatron | 0:044dfba47660 | 181 | |
creatron | 0:044dfba47660 | 182 | // change the tranceiver direction to output |
creatron | 0:044dfba47660 | 183 | dir_485 = 1; |
creatron | 0:044dfba47660 | 184 | for (UINT_8 i= 0; i < length; i++) |
creatron | 0:044dfba47660 | 185 | { |
creatron | 0:044dfba47660 | 186 | rs485.putc (*tmp_ptr++); |
creatron | 0:044dfba47660 | 187 | } |
creatron | 7:cfe1e0eafb7e | 188 | rs485.attach(this, |
creatron | 7:cfe1e0eafb7e | 189 | &dlms_comms::Tx_interrupt, |
creatron | 9:d49cdc77f867 | 190 | Serial::TxIrq); |
creatron | 0:044dfba47660 | 191 | } |
creatron | 3:29454cac7930 | 192 | UINT_64 dlms_comms::ret_rx_irq_count (void) |
creatron | 0:044dfba47660 | 193 | { |
creatron | 3:29454cac7930 | 194 | return rx_irq_count; |
creatron | 3:29454cac7930 | 195 | } |
creatron | 3:29454cac7930 | 196 | UINT_64 dlms_comms::ret_tx_irq_count (void) |
creatron | 3:29454cac7930 | 197 | { |
creatron | 3:29454cac7930 | 198 | return tx_irq_count; |
creatron | 0:044dfba47660 | 199 | } |
creatron | 6:70460dcbc43c | 200 | bool dlms_comms::ret_dir_485 (void) |
creatron | 6:70460dcbc43c | 201 | { |
creatron | 6:70460dcbc43c | 202 | return dir_485; |
creatron | 6:70460dcbc43c | 203 | } |
creatron | 0:044dfba47660 | 204 | //----[ the end ]-------------------------------------------------------------- |