#endif
#endif
+// Debugging
+// #define MODBUS_DEBUG
+
+#ifdef MODBUS_DEBUG
+#define DEBUG debug_printf
+#else
+#define DEBUG(xxx, ...) do { } while (0)
+#endif
/*** State ***/
enum mb_state {
static byte tx_buf[MODBUS_TX_BUFSIZE];
static u16 tx_size;
static u16 tx_pos;
+static byte pending_error;
static bool check_frame(void);
static void process_frame(void);
rx_bad = 0;
usart_set_mode(MODBUS_USART, USART_MODE_RX);
usart_enable_rx_interrupt(MODBUS_USART);
+ modbus_ready_hook();
}
static void rx_done(void)
void modbus_init(void)
{
+ DEBUG("MODBUS: Init\n");
+
timer_set_prescaler(MODBUS_TIMER, CPU_CLOCK_MHZ-1); // 1 tick = 1 μs
timer_set_mode(MODBUS_TIMER, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_DOWN);
timer_update_on_overflow(MODBUS_TIMER);
if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) {
rx_bad = 1;
} else if (rx_size < MODBUS_RX_BUFSIZE) {
+ if (!rx_size)
+ modbus_frame_start_hook();
rx_buf[rx_size++] = ch;
} else {
// Frame too long
return;
}
+ DEBUG("MODBUS: < dest=%02x func=%02x len=%u\n", rx_buf[0], rx_buf[1], rx_size);
+
if (rx_buf[0] == MODBUS_OUR_ADDRESS) {
// Frame addressed to us: process and reply
process_frame();
+ DEBUG("MODBUS: > status=%02x len=%u\n", tx_buf[1], tx_size);
tx_init();
} else if (rx_buf[0] == 0x00) {
// Broadcast frame: process, but do not reply
{
if (rx_bad) {
// FIXME: Error counters?
+ DEBUG("MODBUS: RX bad\n");
return false;
}
if (rx_size < 4) {
// FIXME: Error counters?
+ DEBUG("MODBUS: RX undersize\n");
return false;
}
u16 rx_crc = (rx_buf[rx_size-2] << 8) | rx_buf[rx_size-1];
if (crc != rx_crc) {
// FIXME: Error counters?
+ DEBUG("MODBUS: Bad CRC\n");
return false;
}
ERR_ILLEGAL_FUNCTION = 0x01,
ERR_ILLEGAL_DATA_ADDRESS = 0x02,
ERR_ILLEGAL_DATA_VALUE = 0x03,
+ ERR_SLAVE_DEVICE_FAILURE = 0x04,
};
static uint read_remains(void)
return report_error(ERR_ILLEGAL_DATA_VALUE);
modbus_set_coil(addr, value);
+
+ write_u16(addr);
+ write_u16(value);
}
static void func_write_single_register(void)
return report_error(ERR_ILLEGAL_DATA_ADDRESS);
modbus_set_holding_register(addr, value);
+
+ write_u16(addr);
+ write_u16(value);
}
static void func_write_multiple_coils(void)
for (u16 i = 0; i < count; i++)
modbus_set_coil(start + i, rx_frame[i/8] & (1U << (i%8)));
+
+ write_u16(start);
+ write_u16(count);
}
static void func_write_multiple_registers(void)
for (u16 i = 0; i < count; i++)
modbus_set_holding_register(start + i, read_u16());
+
+ write_u16(start);
+ write_u16(count);
}
static void func_mask_write_register(void)
u16 reg = modbus_get_holding_register(addr);
reg = (reg & and_mask) | (or_mask & ~and_mask);
modbus_set_holding_register(addr, reg);
+
+ write_u16(addr);
+ write_u16(and_mask);
+ write_u16(or_mask);
}
static void func_read_write_multiple_registers(void)
tx_buf[0] = MODBUS_OUR_ADDRESS;
tx_buf[1] = rx_buf[1];
tx_size = 2;
+ pending_error = 0;
switch (func) {
case FUNC_READ_COILS:
report_error(ERR_ILLEGAL_FUNCTION);
}
+ // Is there a deferred error pending?
+ if (pending_error)
+ report_error(pending_error);
+
// Finish reply frame
write_u16(crc16(tx_buf, tx_size));
}
+
+void modbus_slave_error(void)
+{
+ pending_error = ERR_SLAVE_DEVICE_FAILURE;
+}