diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml index efa6ee5..38940f3 100644 --- a/.settings/language.settings.xml +++ b/.settings/language.settings.xml @@ -5,7 +5,7 @@ - + @@ -16,7 +16,7 @@ - + diff --git a/src/it_handlers.c b/src/it_handlers.c index a79f9f9..d3688ec 100644 --- a/src/it_handlers.c +++ b/src/it_handlers.c @@ -87,6 +87,8 @@ void SysTick_Handler(void) { srl_keep_timeout(main_kiss_srl_ctx_ptr); srl_keep_timeout(main_wx_srl_ctx_ptr); + srl_keep_tx_delay(main_wx_srl_ctx_ptr); + i2cKeepTimeout(); delay_decrement_counter(); diff --git a/src/main.c b/src/main.c index 8572f5d..9dfdf75 100644 --- a/src/main.c +++ b/src/main.c @@ -349,6 +349,7 @@ int main(int argc, char* argv[]){ srl_init(main_kiss_srl_ctx_ptr, USART1, srl_usart1_rx_buffer, RX_BUFFER_1_LN, srl_usart1_tx_buffer, TX_BUFFER_1_LN, main_target_kiss_baudrate, 1); srl_init(main_wx_srl_ctx_ptr, USART2, srl_usart2_rx_buffer, RX_BUFFER_2_LN, srl_usart2_tx_buffer, TX_BUFFER_2_LN, main_target_wx_baudrate, _RTU_SLAVE_STOP_BITS); + srl_switch_tx_delay(main_wx_srl_ctx_ptr, 1); main_modbus_rtu_master_enabled = 1; diff --git a/system/include/drivers/serial.h b/system/include/drivers/serial.h index 0c7be8e..144e6b9 100644 --- a/system/include/drivers/serial.h +++ b/system/include/drivers/serial.h @@ -14,6 +14,7 @@ #define SEPARATE_RX_BUFF #define SEPARATE_TX_BUFF +#define SRL_TX_DELAY_IN_MS 10 #define SRL_DEFAULT_RX_TIMEOUT_IN_MS 400 #define SRL_TIMEOUT_ENABLE 1 @@ -33,10 +34,17 @@ typedef enum srlRxState { typedef enum srlTxState { SRL_TX_NOT_CONFIG, SRL_TX_IDLE, + SRL_TX_WAITING, SRL_TXING, SRL_TX_ERROR }srl_tx_state_t; +typedef enum srl_timeout_mode { + SRL_TIMEOUT_OFF, + SRL_TIMEOUT_INSTANT, + SRL_TIMEOUT_TRIGGERED +}srl_timeout_mode_t; + typedef struct srl_context_t { USART_TypeDef *port; @@ -58,9 +66,11 @@ typedef struct srl_context_t { uint8_t srl_triggered_start; uint8_t srl_triggered_stop; - uint8_t srl_start_trigger; // znak oznaczaj�cy pocz�tek istotnych danych do odbebrania - uint8_t srl_stop_trigger; // znak oznaczaj�cy koniec istotnych danych do odebrania + uint8_t srl_start_trigger; + uint8_t srl_stop_trigger; + // temporary storage for reading the 'DR' register during waiting for the first byte + // of the protocol data to be received volatile uint8_t srl_garbage_storage; uint8_t srl_rx_timeout_enable; @@ -71,6 +81,15 @@ typedef struct srl_context_t { uint32_t srl_rx_start_time; uint32_t srl_rx_waiting_start_time; + // this is a time when srl_start_tx or srl_send_data initiated the transfer. If this is set to 0xFFFFFFFF + // the transmission will start immediately. If it is set to any other value the transmission + // (first and any consecutive byte) will be delayed by value defined by 'SRL_TX_DELAY_IN_MS'. + // + // This is a workaround of garbage occurring on RS485 bus when TE/RE pin of MAX485 is switched. + // A delay will separate this artificial 0xFF (when TE/RE are switched) from the rest of data + // what should make Modbus-RTU working again -> this 0xFF will be ignored. + uint32_t srl_tx_start_time; + srl_rx_state_t srl_rx_state; srl_tx_state_t srl_tx_state; @@ -129,6 +148,8 @@ uint8_t srl_receive_data_with_instant_timeout(srl_context_t *ctx, int num, char uint8_t srl_receive_data_with_callback(srl_context_t *ctx, srl_rx_termination_callback_t cbk); uint16_t srl_get_num_bytes_rxed(srl_context_t *ctx); uint8_t* srl_get_rx_buffer(srl_context_t *ctx); +void srl_keep_tx_delay(srl_context_t *ctx); +void srl_switch_tx_delay(srl_context_t *ctx, uint8_t disable_enable); void srl_keep_timeout(srl_context_t *ctx); void srl_switch_timeout(srl_context_t *ctx, uint8_t disable_enable, uint32_t value); void srl_switch_timeout_for_waiting(srl_context_t *ctx, uint8_t disable_enable); diff --git a/system/include/modbus_rtu/rtu_crc.h b/system/include/modbus_rtu/rtu_crc.h index 547f2d5..6864fbb 100644 --- a/system/include/modbus_rtu/rtu_crc.h +++ b/system/include/modbus_rtu/rtu_crc.h @@ -15,8 +15,10 @@ inline uint16_t rtu_crc_stream(uint16_t previous_crc, uint8_t current_data) { previous_crc ^= (uint16_t)current_data; for (i = 0; i < 8; ++i) { - if (previous_crc & 1) + if (previous_crc & 1) { + previous_crc = (previous_crc >> 1); previous_crc = (previous_crc) ^ 0xA001; + } else previous_crc = (previous_crc >> 1); } diff --git a/system/src/drivers/serial.c b/system/src/drivers/serial.c index 3ff6d33..0c6717b 100644 --- a/system/src/drivers/serial.c +++ b/system/src/drivers/serial.c @@ -8,23 +8,21 @@ #include -int srlBRRegValue = 0x09C4 ; // dla symulacji ---- baudrate 9600bps -//int SrlBRRegValue = 0x0209; // dla realnego uk��du #ifdef SEPARATE_TX_BUFF -uint8_t srl_usart1_tx_buffer[TX_BUFFER_1_LN] = {'\0'}; // dane do wys�ania do zdalnego urz�dzenia +uint8_t srl_usart1_tx_buffer[TX_BUFFER_1_LN] = {'\0'}; #endif #ifdef SEPARATE_RX_BUFF -uint8_t srl_usart1_rx_buffer[RX_BUFFER_1_LN] = {'\0'}; // dane odebrane od zdalnego urz�dzenia +uint8_t srl_usart1_rx_buffer[RX_BUFFER_1_LN] = {'\0'}; #endif #ifdef SEPARATE_TX_BUFF -uint8_t srl_usart2_tx_buffer[TX_BUFFER_2_LN] = {'\0'}; // dane do wys�ania do zdalnego urz�dzenia +uint8_t srl_usart2_tx_buffer[TX_BUFFER_2_LN] = {'\0'}; #endif #ifdef SEPARATE_RX_BUFF -uint8_t srl_usart2_rx_buffer[RX_BUFFER_2_LN] = {'\0'}; // dane odebrane od zdalnego urz�dzenia +uint8_t srl_usart2_rx_buffer[RX_BUFFER_2_LN] = {'\0'}; #endif @@ -56,6 +54,8 @@ void srl_init( ctx->te_port = 0; ctx->te_pin = 0; + ctx->srl_tx_start_time = 0xFFFFFFFFu; + USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = baudrate; @@ -219,20 +219,27 @@ uint8_t srl_start_tx(srl_context_t *ctx, short leng) { ctx->srl_tx_bytes_counter = 1; // setting a pointer to transmit buffer to the internal buffer inside the driver - ctx->srl_tx_buf_pointer = srl_usart1_tx_buffer; + //ctx->srl_tx_buf_pointer = srl_usart1_tx_buffer; if (ctx->te_port != 0) GPIO_SetBits(ctx->te_port, ctx->te_pin); - ctx->port->CR1 |= USART_CR1_TE; - ctx->port->SR &= (0xFFFFFFFF ^ USART_SR_TC); - ctx->port->DR = ctx->srl_tx_buf_pointer[0]; + // check if delay should be applied to transmission + if (ctx->srl_tx_start_time == 0xFFFFFFFFu) { + ctx->port->CR1 |= USART_CR1_TE; + ctx->port->SR &= (0xFFFFFFFF ^ USART_SR_TC); + ctx->port->DR = ctx->srl_tx_buf_pointer[0]; - ctx->srl_tx_state = SRL_TXING; + ctx->srl_tx_state = SRL_TXING; - ctx->port->CR1 |= USART_CR1_TXEIE; // przerwanie zg�aszane kiedy rejsetr DR jest pusty - ctx->port->CR1 |= USART_CR1_TCIE; // przerwanie zg�aszane po transmisji bajtu - // je�eli rejestr DR jest nadal pusty + ctx->port->CR1 |= USART_CR1_TXEIE; + ctx->port->CR1 |= USART_CR1_TCIE; + + } + else { + ctx->srl_tx_state = SRL_TX_WAITING; + ctx->srl_tx_start_time = main_get_master_time(); + } return SRL_OK; } @@ -479,6 +486,24 @@ void srl_irq_handler(srl_context_t *ctx) { // storing received byte into buffer ctx->srl_rx_buf_pointer[ctx->srl_rx_bytes_counter] = (uint8_t)ctx->port->DR; + // check if termination callback pointer has been set + if (ctx->srl_rx_term != NULL) { + // if yes call it + stop_rxing = ctx->srl_rx_term( ctx->srl_rx_buf_pointer[ctx->srl_rx_bytes_counter], + ctx->srl_rx_buf_pointer, + ctx->srl_rx_bytes_counter); + + // and check the return value + if (stop_rxing == 1) { + // if this was the last byte of transmission switch the state + // of receiving part to done + ctx->srl_rx_state = SRL_RX_DONE; + + ctx->srl_triggered_stop = 0; + } + + } + // checking if this byte in stream holds the protocol information about // how many bytes needs to be received. if (ctx->srl_rx_lenght_param_addres == ctx->srl_rx_bytes_counter) { @@ -500,23 +525,6 @@ void srl_irq_handler(srl_context_t *ctx) { // moving buffer pointer forward ctx->srl_rx_bytes_counter++; - // check if termination callback pointer has been set - if (ctx->srl_rx_term != NULL) { - // if yes call it - stop_rxing = ctx->srl_rx_term( ctx->srl_rx_buf_pointer[ctx->srl_rx_bytes_counter], - ctx->srl_rx_buf_pointer, - ctx->srl_rx_bytes_counter); - - // and check the return value - if (stop_rxing == 1) { - // if this was the last byte of transmission switch the state - // of receiving part to done - ctx->srl_rx_state = SRL_RX_DONE; - - ctx->srl_triggered_stop = 0; - } - - } } // if the user want the driver to stop receiving after certain is received @@ -632,6 +640,46 @@ uint8_t* srl_get_rx_buffer(srl_context_t *ctx) { return ctx->srl_rx_buf_pointer; } +void srl_keep_tx_delay(srl_context_t *ctx) { + if (ctx != 0) { + + // check if pre tx delay is enabled by an user + if (ctx->srl_tx_start_time != 0xFFFFFFFFu) { + + // if it is enabled then check if the serial port is currently set to waiting state + if (ctx->srl_tx_state == SRL_TX_WAITING) { + + // check if a delay has expired + if (main_get_master_time() - ctx->srl_tx_start_time >= SRL_TX_DELAY_IN_MS) { + + // if yes start the transmission + ctx->port->CR1 |= USART_CR1_TE; + ctx->port->SR &= (0xFFFFFFFF ^ USART_SR_TC); + ctx->port->DR = ctx->srl_tx_buf_pointer[0]; + + ctx->srl_tx_state = SRL_TXING; + + ctx->port->CR1 |= USART_CR1_TXEIE; + ctx->port->CR1 |= USART_CR1_TCIE; + } + } + + } + } +} + +void srl_switch_tx_delay(srl_context_t *ctx, uint8_t disable_enable) { + if (ctx != 0) { + + if (disable_enable == 1) { + ctx->srl_tx_start_time = 0x0u; + } + else { + ctx->srl_tx_start_time = 0xFFFFFFFFu; + } + } +} + void srl_switch_timeout(srl_context_t *ctx, uint8_t disable_enable, uint32_t value) { if (disable_enable == 1) ctx->srl_rx_timeout_enable = 1; diff --git a/system/src/modbus_rtu/rtu_parser.c b/system/src/modbus_rtu/rtu_parser.c index 2f977cc..c88f79b 100644 --- a/system/src/modbus_rtu/rtu_parser.c +++ b/system/src/modbus_rtu/rtu_parser.c @@ -16,7 +16,7 @@ int32_t rtu_parser_03_04_registers(uint8_t* input, uint16_t input_ln, rtu_regist uint16_t data = 0; // iterator through input table and registers table - int i = 0, j = 6; + int i = 0, j = 4; // 7 bytes is the shortest meaningful Modbus RTU frame // with a value of single register @@ -31,12 +31,12 @@ int32_t rtu_parser_03_04_registers(uint8_t* input, uint16_t input_ln, rtu_regist output->slave_address = data; // fetch function code - data = *(input + 2) << 8 | *(input + 3); + data = *(input + 2); // check if the function code is correct or not if (data == 0x03 || data == 0x04) { // fetch the function result lenght in bytes - data = *(input + 4) << 8 | *(input + 5); + data = *(input + 3); // store amount of registers in this response output->number_of_registers = data / 2; @@ -49,6 +49,8 @@ int32_t rtu_parser_03_04_registers(uint8_t* input, uint16_t input_ln, rtu_regist j += 2; } + retval = MODBUS_RET_OK; + } else { // if not exit with an error as this isn't diff --git a/system/src/modbus_rtu/rtu_serial_io.c b/system/src/modbus_rtu/rtu_serial_io.c index 52024e3..94b20c5 100644 --- a/system/src/modbus_rtu/rtu_serial_io.c +++ b/system/src/modbus_rtu/rtu_serial_io.c @@ -34,6 +34,9 @@ typedef enum rtu_pool_state { rtu_pool_state_t rtu_pool = RTU_POOL_STOP; +/** + * Timestamp of last received modbus RTU response with good CRC + */ uint32_t rtu_time_of_last_receive = 0; /** @@ -42,7 +45,14 @@ uint32_t rtu_time_of_last_receive = 0; uint16_t rtu_serial_previous_crc = 0xFFFF; /** - * A callback for stream CRC calculation + * Cleared by 'rtu_serial_callback' when first byte from range 0x1..0xF7 is received + */ +uint8_t rtu_waiting_for_slave_addr = 0x1; + + + +/** + * The callback for stream CRC calculation */ uint8_t rtu_serial_callback(uint8_t current_data, const uint8_t * const rx_buffer, uint16_t rx_bytes_counter) { @@ -50,17 +60,36 @@ uint8_t rtu_serial_callback(uint8_t current_data, const uint8_t * const rx_buffe uint16_t new_crc = 0; - // calculate new crc - new_crc = rtu_crc_stream(rtu_serial_previous_crc, current_data); + // check if the callback still waits for first 'valid' byte to be received from RTU slave + if (rtu_waiting_for_slave_addr == 0x1) { - // if the new CRC value equals 0x0000 it means that this was MSB - // of CRC from correctly received Modbus-RTU frame - if (new_crc == 0) { - // return '1' to terminate the transmission - retval = 1; + // check if the byte which was received from the slave is valid address + if (current_data >= 0x01 && current_data <= 0xF7) { + // clear this flag to start CRC calculation and data receiving + rtu_waiting_for_slave_addr = 0; + } + else { + // RTU slave cannot respond with the broadcast address (0x00), also 0xF8..0xFF + // are not valid RTU address + ; + } } - else { - rtu_serial_previous_crc = new_crc; + + // the second 'if' clause checks if the slave response has began + if (rtu_waiting_for_slave_addr == 0x0) { + + // calculate new crc + new_crc = rtu_crc_stream(rtu_serial_previous_crc, current_data); + + // if the new CRC value equals 0x0000 it means that this was MSB + // of CRC from correctly received Modbus-RTU frame + if (new_crc == 0) { + // return '1' to terminate the transmission + retval = 1; + } + else { + rtu_serial_previous_crc = new_crc; + } } return retval; @@ -179,6 +208,8 @@ int32_t rtu_serial_pool(rtu_pool_queue_t* queue, srl_context_t* serial_context) rtu_serial_previous_crc = 0xFFFF; + rtu_waiting_for_slave_addr = 1; + // if serial transmission has been starter if (result == SRL_OK) { // proceed to the next state (transmitting) @@ -200,13 +231,14 @@ int32_t rtu_serial_pool(rtu_pool_queue_t* queue, srl_context_t* serial_context) case RTU_POOL_TRANSMITTING: { // if transmission is still pending - if (serial_context->srl_tx_state == SRL_TXING) { + if (serial_context->srl_tx_state == SRL_TXING || serial_context->srl_tx_state == SRL_TX_WAITING) { // wait until it will finish ; } else { // trigger reception srl_receive_data_with_callback(serial_context, rtu_serial_callback); + //srl_receive_data(serial_context, 8, 0, 0, 0, 0, 0); // switch the state rtu_pool = RTU_POOL_RECEIVING;