diff --git a/gefran_gtf.c b/gefran_gtf.c new file mode 100644 index 0000000..f1b8a9e --- /dev/null +++ b/gefran_gtf.c @@ -0,0 +1,16 @@ +#include "modbus-master.h" +#include + +#define FIRMWARE_VERSION 120 + +uint16_t gtf_firmware_version(uint8_t address){ + uint16_t val = 0; + readReg(address, FIRMWARE_VERSION, 1); + int8_t res = wait_receive(1, &val, 250); + + if(!res) + return val; + else + return res; +} + diff --git a/gefran_gtf.h b/gefran_gtf.h new file mode 100644 index 0000000..1b1a1ab --- /dev/null +++ b/gefran_gtf.h @@ -0,0 +1,2 @@ + +uint16_t gtf_firmware_version(uint8_t address); diff --git a/modbus-master.c b/modbus-master.c new file mode 100644 index 0000000..13f5bed --- /dev/null +++ b/modbus-master.c @@ -0,0 +1,126 @@ +#include +#include +#include +#include "modbus.h" + +#define receiveOkay (modbusGetBusState() & (1< 8 bytes). You might want to check this at this point. + dest[x]=(rxbuffer[3+x*2]<<8)+rxbuffer[4+x*2]; //do sth with the acquired data. + } + } + + } + return 0; +} + +int8_t wait_write(uint8_t timeout){ + uint8_t breaker = timeout; + while(!receiveOkay && breaker) { //wait for client response, time out after 1s + breaker--; + _delay_ms(1); + if(breaker==0) + return -1; + } + + if(receiveOkay) { //if this fails, there was either no response or a crc error + if(rxbuffer[1]&0x80) { //client responded with an error code + return rxbuffer[1]&0x80; + } + } + return 0; +} + +void readReg(uint8_t slaveid, uint16_t address, uint8_t amount) { + _delay_ms(2); + rxbuffer[0]=slaveid; + modbusSetAddress(slaveid); + rxbuffer[1]=0x03; + rxbuffer[2]=(address>>8)&0xFF; + rxbuffer[3]=address&0xFF; + rxbuffer[4]=0x00; + rxbuffer[5]=amount; + modbusSendMessage(5); +} + +void writeReg(uint8_t slaveid, uint16_t address, uint16_t value) { + _delay_ms(2); + rxbuffer[0]=slaveid; + modbusSetAddress(slaveid); + rxbuffer[1]=0x06; + rxbuffer[2]=(address>>8)&0xFF; + rxbuffer[3]=address&0xFF; + rxbuffer[4]=(value>>8)&0xFF;; + rxbuffer[5]=value&0xFF; + modbusSendMessage(5); +} + +void readInputReg(uint8_t slaveid, uint16_t address, uint16_t amount) { + _delay_ms(2); + rxbuffer[0]=slaveid; + modbusSetAddress(slaveid); + rxbuffer[1]=0x04; + rxbuffer[2]=(address>>8)&0xFF; + rxbuffer[3]=address&0xFF; + rxbuffer[4]=0; + rxbuffer[5]=amount; + modbusSendMessage(5); +} + +void readInputState(uint8_t slaveid, uint16_t address, uint16_t amount) { + _delay_ms(2); + rxbuffer[0]=slaveid; + modbusSetAddress(slaveid); + rxbuffer[1]=0x02; + rxbuffer[2]=(address>>8)&0xFF; + rxbuffer[3]=address&0xFF; + rxbuffer[4]=0; + rxbuffer[5]=amount; + modbusSendMessage(5); +} + +void writeCoil(uint8_t slaveid, uint16_t address, uint16_t value) { + _delay_ms(2); + rxbuffer[0]=slaveid; + modbusSetAddress(slaveid); + rxbuffer[1]=0x05; + rxbuffer[2]=(address>>8)&0xFF; + rxbuffer[3]=address&0xFF; + rxbuffer[4]=value?0xFF:0x00; + rxbuffer[5]=0; + modbusSendMessage(5); +} + +ISR(TIMER0_COMPA_vect) { //this ISR is called 9765.625 times per second + modbusTickTimer(); +} diff --git a/modbus-master.h b/modbus-master.h new file mode 100644 index 0000000..11bb5d0 --- /dev/null +++ b/modbus-master.h @@ -0,0 +1,11 @@ +#include + +void timer2_init(void); +void modbus_master_init(void); +int8_t wait_receive(uint8_t len, uint16_t dest[], uint8_t timeout); +int8_t wait_write(uint8_t timeout); +void readReg(uint8_t slaveid, uint16_t address, uint8_t amount); +void writeReg(uint8_t slaveid, uint16_t address, uint16_t value); +void readInputReg(uint8_t slaveid, uint16_t address, uint16_t amount); +void readInputState(uint8_t slaveid, uint16_t address, uint16_t amount); +void writeCoil(uint8_t slaveid, uint16_t address, uint16_t value); diff --git a/modbus.c b/modbus.c new file mode 100644 index 0000000..424e6c3 --- /dev/null +++ b/modbus.c @@ -0,0 +1,436 @@ +/************************************************************************* +Title: Yet another (small) modbus (server) implementation for the avr. +Author: Max Brueggemann +Hardware: any AVR with hardware UART, tested on Atmega 88/168 at 20Mhz +License: BSD-3-Clause + +DESCRIPTION: + Refer to the header file yaMBSiavr.h. + +USAGE: + Refer to the header file yaMBSiavr.h. + +LICENSE: + +Copyright 2017 Max Brueggemann, www.maxbrueggemann.de + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +*************************************************************************/ + +#include +#include "modbus.h" +#include + +volatile unsigned char BusState = 0; +volatile uint16_t modbusTimer = 0; +volatile unsigned char rxbuffer[MaxFrameIndex+1]; +volatile uint16_t DataPos = 0; +volatile unsigned char PacketTopIndex = 7; +volatile unsigned char modBusStaMaStates = 0; +volatile uint16_t modbusDataAmount = 0; +volatile uint16_t modbusDataLocation = 0; + +/* @brief: save address and amount +* +*/ +void modbusSaveLocation(void) +{ + modbusDataLocation=(rxbuffer[3]|(rxbuffer[2]<<8)); + if (rxbuffer[1]==fcPresetSingleRegister || rxbuffer[1]==fcForceSingleCoil) modbusDataAmount=1; + else modbusDataAmount=(rxbuffer[5]|(rxbuffer[4]<<8)); +} + +/* @brief: returns 1 if data location adr is touched by current command +* +* Arguments: - adr: address of the data object +* +*/ +uint8_t modbusIsInRange(uint16_t adr) +{ + if((modbusDataLocation <= adr) && (adr<(modbusDataLocation+modbusDataAmount))) + return 1; + return 0; +} + +/* @brief: returns 1 if range of data locations is touched by current command +* +* Arguments: - startAdr: address of first data object in range +* - lastAdr: address of last data object in range +* +*/ +uint8_t modbusIsRangeInRange(uint16_t startAdr, uint16_t lastAdr) +{ + if(modbusIsInRange(startAdr) && modbusIsInRange(lastAdr)) + return 1; + return 0; +} + +uint8_t modbusGetBusState(void) +{ + return BusState; +} + +#if ADDRESS_MODE == SINGLE_ADR +volatile unsigned char Address = 0x00; +uint8_t modbusGetAddress(void) +{ + return Address; +} + +void modbusSetAddress(unsigned char newadr) +{ + Address = newadr; +} +#endif + +#if PHYSICAL_TYPE == 485 +void transceiver_txen(void) +{ + TRANSCEIVER_ENABLE_PORT|=(1<>= 1; + if (carry) out ^= 0xA001; + } + } + //out=0x1234; + if ((ptrToArray[inputSize]==out%256) && (ptrToArray[inputSize+1]==out/256)) //check + { + return 1; + } else { + ptrToArray[inputSize]=out%256; //append Lo + ptrToArray[inputSize+1]=out/256; //append Hi + return 0; + } +} + +/* @brief: copies a single bit from one char to another char (or arrays thereof) +* +* +*/ +void listBitCopy(volatile uint8_t *source, uint16_t sourceNr,volatile uint8_t *target, uint16_t targetNr) +{ + if(*(source+(sourceNr/8))&(1<<(sourceNr-((sourceNr/8)*8)))) + { + *(target+(targetNr/8))|=(1<<(targetNr-((targetNr/8)*8))); + } else *(target+(targetNr/8))&=~(1<<(targetNr-((targetNr/8)*8))); +} + +/* @brief: Back to receiving state. +* +*/ +void modbusReset(void) +{ + BusState=(1< perform crc check + modbusSaveLocation(); + BusState=(1<MaxFrameIndex) modbusReset(); + else + { + rxbuffer[DataPos]=data; + DataPos++; //TODO: maybe prevent this from exceeding 255? + } + } else + // Bus state is Timed out.. + if (!(BusState & (1<> 8); + UBRRL = (unsigned char) _UBRR; + UART_STATUS = (1<> 8); + *(outreg+1+c*2) = (uint8_t)(*(inreg+c)); + } +} + +/* @brief: copies a single or multiple 16-bit-words from one array of integers to an array of bytes +* +*/ +void modbusRegisterToInt(volatile uint8_t *inreg, volatile uint16_t *outreg, uint8_t amount) +{ + for (uint8_t c=0; c=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation))) { + + if ((rxbuffer[1]==fcReadHoldingRegisters) || (rxbuffer[1]==fcReadInputRegisters) ) + { + if ((modbusDataAmount*2)<=(MaxFrameIndex-4)) //message buffer big enough? + { + rxbuffer[2]=(unsigned char)(modbusDataAmount*2); + intToModbusRegister(ptrToInArray+(modbusDataLocation-startAddress),rxbuffer+3,modbusDataAmount); + modbusSendMessage(2+rxbuffer[2]); + return 1; + } else modbusSendException(ecIllegalDataValue); + } + else if (rxbuffer[1]==fcPresetMultipleRegisters) + { + if (((rxbuffer[6])>=modbusDataAmount*2) && ((DataPos-9)>=rxbuffer[6])) //enough data received? + { + modbusRegisterToInt(rxbuffer+7,ptrToInArray+(modbusDataLocation-startAddress),(unsigned char)(modbusDataAmount)); + modbusSendMessage(5); + return 1; + } else modbusSendException(ecIllegalDataValue);//too few data bytes received + } + else if (rxbuffer[1]==fcPresetSingleRegister) + { + modbusRegisterToInt(rxbuffer+4,ptrToInArray+(modbusDataLocation-startAddress),1); + modbusSendMessage(5); + return 1; + } + //modbusSendException(ecSlaveDeviceFailure); //inapropriate call of modbusExchangeRegisters + return 0; + } else { + modbusSendException(ecIllegalDataValue); + return 0; + } +} + +/* @brief: Handles single/multiple input/coil reading and single/multiple coil writing. +* +* Arguments: - ptrToInArray: pointer to the user's data array containing bits +* - startAddress: address of the first bit in the supplied array +* - size: input array size in the requested format (bits) +* +*/ +uint8_t modbusExchangeBits(volatile uint8_t *ptrToInArray, uint16_t startAddress, uint16_t size) +{ + if ((modbusDataLocation>=startAddress) && ((startAddress+size)>=(modbusDataAmount+modbusDataLocation))) + { + if ((rxbuffer[1]==fcReadInputStatus) || (rxbuffer[1]==fcReadCoilStatus)) + { + if (modbusDataAmount<=((MaxFrameIndex-4)*8)) //message buffer big enough? + { + rxbuffer[2]=(modbusDataAmount/8); + if (modbusDataAmount%8>0) + { + rxbuffer[(uint8_t)(modbusDataAmount/8)+3]=0x00; //fill last data byte with zeros + rxbuffer[2]++; + } + for (uint16_t c = 0; c=modbusDataAmount) && ((DataPos-9)>=rxbuffer[6])) //enough data received? + { + for (uint16_t c = 0; c +/** + * @code #include @endcode + * + * @brief Interrupt-based Modbus implementation for small avr microcontrollers. + * The Modbus implementation guidelines at modbus.org call for response + * timeouts in the range of several seconds , hence only timing critical + * parts have been implemented within ISRs. The actual handling of the Modbus + * frame can easily be done in the main while loop. + * + * @author Max Brueggemann www.maxbrueggemann.de + */ + +/* define baudrate of modbus */ +#ifndef BAUD +#define BAUD 19200L +#endif + +/* +* Definitions for transceiver enable pin. +*/ +#ifndef TRANSCEIVER_ENABLE_PORT +#define TRANSCEIVER_ENABLE_PORT PORTJ +#endif + +#ifndef TRANSCEIVER_ENABLE_PIN +#define TRANSCEIVER_ENABLE_PIN 6 +#define TRANSCEIVER_ENABLE_PIN_2 5 +#endif + +#ifndef TRANSCEIVER_ENABLE_PORT_DDR +#define TRANSCEIVER_ENABLE_PORT_DDR DDRJ +#endif + +/** + * @brief + * At the moment the user has to set the value for Baudrate and + * speed mode manually. The values depend on the operating frequency + * of your AVR and can be found in its datasheet. + */ +#if defined(__AVR_ATtiny2313__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect +#define UART_RECEIVE_INTERRUPT USART_RX_vect +#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART_STATUS UCSRA +#define UART_CONTROL UCSRB +#define UART_DATA UDR +#define UART_UDRIE UDRIE + +#elif defined(__AVR_ATmega164P__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART1_TX_vect +#define UART_RECEIVE_INTERRUPT USART1_RX_vect +#define UART_TRANSMIT_INTERRUPT USART1_UDRE_vect +#define UART_STATUS UCSR1A +#define UART_CONTROL UCSR1B +#define UART_DATA UDR1 +#define UART_UDRIE UDRIE1 +#define UCSRC UCSR1C +#define RXCIE RXCIE1 +#define TXCIE TXCIE1 +#define RXEN RXEN1 +#define TXEN TXEN1 +#define UCSZ0 UCSZ10 +#define U2X U2X1 +#define UBRRH UBRR1H +#define UBRRL UBRR1L + +#elif defined(__AVR_ATmega168PA__)|(__AVR_ATmega88PA__)|(__AVR_ATmega328P__)|(__AVR_ATmega168P__)|(__AVR_ATmega88P__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect +#define UART_RECEIVE_INTERRUPT USART_RX_vect +#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART_STATUS UCSR0A +#define UART_CONTROL UCSR0B +#define UART_DATA UDR0 +#define UART_UDRIE UDRIE0 +#define UCSRC UCSR0C +#define RXCIE RXCIE0 +#define TXCIE TXCIE0 +#define RXEN RXEN0 +#define TXEN TXEN0 +#define UCSZ0 UCSZ00 +#define U2X U2X0 +#define UBRRH UBRR0H +#define UBRRL UBRR0L + +#elif defined(__AVR_ATmega328PB__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect +#define UART_RECEIVE_INTERRUPT USART0_RX_vect +#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART_STATUS UCSR0A +#define UART_CONTROL UCSR0B +#define UART_DATA UDR0 +#define UART_UDRIE UDRIE0 +#define UCSRC UCSR0C +#define RXCIE RXCIE0 +#define TXCIE TXCIE0 +#define RXEN RXEN0 +#define TXEN TXEN0 +#define UCSZ0 UCSZ00 +#define U2X U2X0 +#define UBRRH UBRR0H +#define UBRRL UBRR0L + +#elif defined(__AVR_ATtiny441__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect +#define UART_RECEIVE_INTERRUPT USART0_RX_vect +#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART_STATUS UCSR0A +#define UART_CONTROL UCSR0B +#define UART_DATA UDR0 +#define UART_UDRIE UDRIE0 +#define UCSRC UCSR0C +#define RXCIE RXCIE0 +#define TXCIE TXCIE0 +#define RXEN RXEN0 +#define TXEN TXEN0 +#define UCSZ0 UCSZ00 +#define U2X U2X0 +#define UBRRH UBRR0H +#define UBRRL UBRR0L + +#elif defined(__AVR_ATmega8__)|| defined(__AVR_ATmega16__) || defined(__AVR_ATmega32__) || defined(__AVR_ATmega323__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TXC_vect +#define UART_RECEIVE_INTERRUPT USART_RXC_vect +#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART_STATUS UCSRA +#define UART_CONTROL UCSRB +#define UART_DATA UDR +#define UART_UDRIE UDRIE + +#elif defined(__AVR_AT90PWM3B__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART_TX_vect +#define UART_RECEIVE_INTERRUPT USART_RX_vect +#define UART_TRANSMIT_INTERRUPT USART_UDRE_vect +#define UART_STATUS UCSRA +#define UART_CONTROL UCSRB +#define UART_DATA UDR +#define UART_UDRIE UDRIE + +#elif defined(__AVR_ATmega1284P__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART0_TX_vect +#define UART_RECEIVE_INTERRUPT USART0_RX_vect +#define UART_TRANSMIT_INTERRUPT USART0_UDRE_vect +#define UART_STATUS UCSR0A +#define UART_CONTROL UCSR0B +#define UART_DATA UDR0 +#define UART_UDRIE UDRIE0 +#define UCSRC UCSR0C +#define RXCIE RXCIE0 +#define TXCIE TXCIE0 +#define RXEN RXEN0 +#define TXEN TXEN0 +#define UCSZ0 UCSZ00 +#define U2X U2X0 +#define UBRRH UBRR0H +#define UBRRL UBRR0L + +#elif defined(__AVR_ATmega2560__) +#define UART_TRANSMIT_COMPLETE_INTERRUPT USART3_TX_vect +#define UART_RECEIVE_INTERRUPT USART3_RX_vect +#define UART_TRANSMIT_INTERRUPT USART3_UDRE_vect +#define UART_STATUS UCSR3A +#define UART_CONTROL UCSR3B +#define UART_DATA UDR3 +#define UART_UDRIE UDRIE3 +#define UCSRC UCSR3C +#define RXCIE RXCIE3 +#define TXCIE TXCIE3 +#define RXEN RXEN3 +#define TXEN TXEN3 +#define UCSZ0 UCSZ30 +#define U2X U2X0 +#define UBRRH UBRR3H +#define UBRRL UBRR3L + +#else +#error "no definition available" +#endif + +#ifndef F_CPU +#error " F_CPU not defined " +#else + #define _UBRR (F_CPU / 8 / BAUD ) -1 +#endif /* F_CPU */ +/* + * Available address modes. +*/ +#define MULTIPLE_ADR 2 +#define SINGLE_ADR 1 + +/* +* Use SINGLE_ADR or MULTIPLE_ADR, default: SINGLE_ADR +* This is useful for building gateways, routers or clients that for whatever reason need multiple addresses. +*/ +#define ADDRESS_MODE SINGLE_ADR + +/* +* Use 485 or 232, default: 485 +* Use 232 for testing purposes or very simple applications that do not require RS485 and bus topology. +*/ +#define PHYSICAL_TYPE 485 //possible values: 485, 232 + +#if BAUD>=19200 +#define modbusInterFrameDelayReceiveStart 16 +#define modbusInterFrameDelayReceiveEnd 18 +#define modbusInterCharTimeout 7 +#else +#define modbusBlocksize 10 +#define modbusBlockTime ((float)modbusBlocksize*1000000)/((float) BAUD) //is 260 for 38400 +#define timerISROccurenceTime 100 //time in microseconds between two calls of modbusTickTimer +#define modbusInterFrameDelayReceiveStart (uint16_t)(modbusBlockTime*3.5/(float)timerISROccurenceTime) +#define modbusInterFrameDelayReceiveEnd (uint16_t)(modbusBlockTime*4/(float)timerISROccurenceTime) +#define modbusInterCharTimeout (uint16_t)(modbusBlockTime*1.5/(float)timerISROccurenceTime) +#endif + + +/** + * @brief Defines the maximum Modbus frame size accepted by the device. 255 is the default + * and also the maximum value. However, it might be useful to set this to lower + * values, with 8 being the lowest possible value, in order to save on ram space. + */ +#define MaxFrameIndex 255 + +/** + * @brief Modbus Function Codes + * Refer to modbus.org for further information. + * It's good practice to return exception code 01 in case you receive a function code + * that you haven't implemented in your application. + */ +#define fcReadCoilStatus 1 //read single/multiple coils +#define fcReadInputStatus 2 //read single/multiple inputs +#define fcReadHoldingRegisters 3 //read analog output registers +#define fcReadInputRegisters 4 //read analog input registers (2 Bytes per register) +#define fcForceSingleCoil 5 //write single bit +#define fcPresetSingleRegister 6 //write analog output register (2 Bytes) +#define fcForceMultipleCoils 15 //write multiple bits +#define fcPresetMultipleRegisters 16 //write multiple analog output registers (2 Bytes each) +#define fcReportSlaveID 17 //read device description, run status and other device specific information + +/** + * @brief Modbus Exception Codes + * Refer to modbus.org for further information. + * It's good practice to return exception code 01 in case you receive a function code + * that you haven't implemented in your application. + */ +#define ecIllegalFunction 1 +#define ecIllegalDataAddress 2 +#define ecIllegalDataValue 3 +#define ecSlaveDeviceFailure 4 +#define ecAcknowledge 5 +#define ecSlaveDeviceBusy 6 +#define ecNegativeAcknowledge 7 +#define ecMemoryParityError 8 + +/** + * @brief Internal bit definitions + */ +#define BusTimedOut 0 +#define Receiving 1 +#define Transmitting 2 +#define ReceiveCompleted 3 +#define TransmitRequested 4 +#define TimerActive 5 +#define GapDetected 6 + +/** +* @brief Configures the UART. Call this function only once. +*/ +extern void modbusInit(void); + +/** +* @brief receive/transmit data array +*/ +extern volatile unsigned char rxbuffer[MaxFrameIndex+1]; + +/** +* @brief Current receive/transmit position +*/ +extern volatile uint16_t DataPos; + +/** + * This only applies to single address mode. + */ +#if ADDRESS_MODE == SINGLE_ADR + /** + * @brief: Read the device address + */ + extern uint8_t modbusGetAddress(void); + + /** + * @brief: Set the device address + * Arguments: - newadr: the new device address + */ + extern void modbusSetAddress(unsigned char newadr); +#endif + +/* @brief: Sends a response. +* +* Arguments: - packtop, index of the last byte in rxbuffer +* that contains payload. Maximum value is +* MaxFrameIndex-2. +*/ +extern void modbusSendMessage(unsigned char packtop); + +/* @brief: Sends a Modbus exception. +* +* Arguments: - exceptionCode +*/ +extern void modbusSendException(unsigned char exceptionCode); + +/* @brief: Discards the current transaction. For MULTIPLE_ADR-mode and general +* testing purposes. Call this function if you don't want to reply at all. +*/ +void modbusReset(void); + +/** + * @brief Call this function whenever possible and check if its return value has the ReceiveCompleted Bit set. + * Preferably do this in the main while. I do not recommend calling this function within ISRs. + * @example if (modbusGetBusState() & (1<