]> mj.ucw.cz Git - home-hw.git/commitdiff
ModBus: Parametrization
authorMartin Mares <mj@ucw.cz>
Mon, 8 Jul 2019 17:05:48 +0000 (19:05 +0200)
committerMartin Mares <mj@ucw.cz>
Mon, 8 Jul 2019 17:05:48 +0000 (19:05 +0200)
test-modbus/config.h [new file with mode: 0644]
test-modbus/modbus.c
test-modbus/modbus.h
test-modbus/test.c

diff --git a/test-modbus/config.h b/test-modbus/config.h
new file mode 100644 (file)
index 0000000..1b116d6
--- /dev/null
@@ -0,0 +1,20 @@
+// Processor clock
+
+#define CPU_CLOCK_MHZ 72
+
+// MODBUS library parameters
+
+#define MODBUS_USART USART2
+#define MODBUS_NVIC_USART_IRQ NVIC_USART2_IRQ
+#define MODBUS_USART_ISR usart2_isr
+
+#define MODBUS_TXEN_GPIO_PORT GPIOA
+#define MODBUS_TXEN_GPIO_PIN GPIO1
+
+#define MODBUS_TIMER TIM2
+#define MODBUS_NVIC_TIMER_IRQ NVIC_TIM2_IRQ
+#define MODBUS_TIMER_ISR tim2_isr
+
+#define MODBUS_OUR_ADDRESS 42
+
+#define MODBUS_BAUD_RATE 19200
index 69fa9802b7d6c77558e84570e80f90babade7a6c..39bd0399467fb9311db8f9eae88d9899e99abe24 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ *     Generic MODBUS Library for STM32
+ *
+ *     (c) 2019 Martin Mareš <mj@ucw.cz>
+ */
+
+#include "config.h"
 #include "util.h"
 #include "modbus.h"
 
@@ -9,6 +16,59 @@
 #include <libopencm3/stm32/usart.h>
 #include <libopencm3/stm32/timer.h>
 
+/*** Configuration ***/
+
+// You should set the following parameters in config.h
+
+// USART (pins are expected to be configured by the caller)
+// #define MODBUS_USART USART2
+// #define MODBUS_NVIC_USART_IRQ NVIC_USART2_IRQ
+// #define MODBUS_USART_ISR usart2_isr
+
+// GPIO pin for transmitter enable (pins is expected to be configured by the caller)
+// #define MODBUS_TXEN_GPIO_PORT GPIOA
+// #define MODBUS_TXEN_GPIO_PIN GPIO1
+
+// Timer
+// #define MODBUS_TIMER TIM2
+// #define MODBUS_NVIC_TIMER_IRQ NVIC_TIM2_IRQ
+// #define MODBUS_TIMER_ISR tim2_isr
+
+// Slave address we are responding at
+// #define MODBUS_OUR_ADDRESS 42
+
+// Baud rate
+#ifndef MODBUS_BAUD_RATE
+#define MODBUS_BAUD_RATE 19200
+#endif
+
+// CPU clock frequency
+// #define CPU_CLOCK_MHZ 72
+
+// Receive buffer size (standard specifies 256 bytes, you can make it shorter if necessary)
+#ifndef MODBUS_RX_BUFSIZE
+#define MODBUS_RX_BUFSIZE 256
+#endif
+
+// Transmit buffer size (standard specifies 256 bytes, you can make it shorter if necessary)
+#ifndef MODBUS_TX_BUFSIZE
+#define MODBUS_TX_BUFSIZE 256
+#endif
+
+// Receive timeout in microseconds
+#ifndef MODBUS_RX_TIMEOUT
+#if MODBUS_BAUD_RATE <= 19200
+// For low baud rates, the standard specifies timeout of 1.5 character times
+// (1 character = start bit + 8 data bits + parity bit + stop bit = 11 bits)
+#define MODBUS_RX_TIMEOUT (1000000*11*3/2/MODBUS_BAUD_RATE)
+#else
+// For high rates, the timeout is fixed to 750 μs
+#define MODBUS_RX_TIMEOUT 750
+#endif
+#endif
+
+/*** State ***/
+
 enum mb_state {
        STATE_RX,
        STATE_RX_DONE,
@@ -18,10 +78,7 @@ enum mb_state {
        STATE_TX_DONE,
 };
 
-#define RX_BUFSIZE 256
-#define TX_BUFSIZE 256
-
-static byte rx_buf[RX_BUFSIZE];
+static byte rx_buf[MODBUS_RX_BUFSIZE];
 static u16 rx_size;
 static byte rx_bad;
 static byte state;             // STATE_xxx
@@ -29,119 +86,112 @@ static byte state;                // STATE_xxx
 static byte *rx_frame;
 static byte *rx_frame_end;
 
-static byte tx_buf[TX_BUFSIZE];
+static byte tx_buf[MODBUS_TX_BUFSIZE];
 static u16 tx_size;
 static u16 tx_pos;
 
-#define MB_OUR_ADDRESS 42
-
 static void rx_init(void)
 {
        state = STATE_RX;
        rx_size = 0;
        rx_bad = 0;
-       usart_set_mode(USART2, USART_MODE_RX);
-       usart_enable_rx_interrupt(USART2);
+       usart_set_mode(MODBUS_USART, USART_MODE_RX);
+       usart_enable_rx_interrupt(MODBUS_USART);
 }
 
 static void rx_done(void)
 {
        state = STATE_RX_DONE;
-       usart_disable_rx_interrupt(USART2);
+       usart_disable_rx_interrupt(MODBUS_USART);
 }
 
 static void tx_init(void)
 {
        state = STATE_TX;
        tx_pos = 0;
-       gpio_set(GPIOA, GPIO1);
-       usart_set_mode(USART2, USART_MODE_TX);
-       usart_enable_tx_interrupt(USART2);
+       gpio_set(MODBUS_TXEN_GPIO_PORT, MODBUS_TXEN_GPIO_PIN);
+       usart_set_mode(MODBUS_USART, USART_MODE_TX);
+       usart_enable_tx_interrupt(MODBUS_USART);
 }
 
 static void tx_done(void)
 {
        state = STATE_TX_DONE;
-       // usart_disable_tx_interrupt(USART2);          // Already done by irq handler
-       gpio_clear(GPIOA, GPIO1);
+       // usart_disable_tx_interrupt(MODBUS_USART);            // Already done by irq handler
+       gpio_clear(MODBUS_TXEN_GPIO_PORT, MODBUS_TXEN_GPIO_PIN);
 }
 
 void modbus_init(void)
 {
-       timer_set_prescaler(TIM2, 71);          // 1 tick = 1 μs
-       timer_set_mode(TIM2, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_DOWN);
-       timer_update_on_overflow(TIM2);
-       timer_disable_preload(TIM2);
-       timer_one_shot_mode(TIM2);
-       timer_enable_irq(TIM2, TIM_DIER_UIE);
-       nvic_enable_irq(NVIC_TIM2_IRQ);
-
-       gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_USART2_RX);
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX);
+       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);
+       timer_disable_preload(MODBUS_TIMER);
+       timer_one_shot_mode(MODBUS_TIMER);
+       timer_enable_irq(MODBUS_TIMER, TIM_DIER_UIE);
+       nvic_enable_irq(MODBUS_NVIC_TIMER_IRQ);
 
-       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
-       gpio_clear(GPIOA, GPIO1);
+       gpio_clear(MODBUS_TXEN_GPIO_PORT, MODBUS_TXEN_GPIO_PIN);
 
-       usart_set_baudrate(USART2, 19200);
-       usart_set_databits(USART2, 9);
-       usart_set_stopbits(USART2, USART_STOPBITS_1);
-       // usart_set_parity(USART2, USART_PARITY_NONE);
-       usart_set_parity(USART2, USART_PARITY_EVEN);
-       usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
+       usart_set_baudrate(MODBUS_USART, MODBUS_BAUD_RATE);
+       usart_set_databits(MODBUS_USART, 9);
+       usart_set_stopbits(MODBUS_USART, USART_STOPBITS_1);
+       usart_set_parity(MODBUS_USART, USART_PARITY_EVEN);
+       usart_set_flow_control(MODBUS_USART, USART_FLOWCONTROL_NONE);
 
        rx_init();
 
-       nvic_enable_irq(NVIC_USART2_IRQ);
-       usart_enable(USART2);
+       nvic_enable_irq(MODBUS_NVIC_USART_IRQ);
+       usart_enable(MODBUS_USART);
 }
 
-void usart2_isr(void)
+void MODBUS_USART_ISR(void)
 {
-       u32 status = USART_SR(USART2);
+       u32 status = USART_SR(MODBUS_USART);
 
        if (status & USART_SR_RXNE) {
-               uint ch = usart_recv(USART2);
+               uint ch = usart_recv(MODBUS_USART);
                if (state == STATE_RX) {
                        if (status & (USART_SR_FE | USART_SR_ORE | USART_SR_NE)) {
                                rx_bad = 1;
-                       } else if (rx_size < RX_BUFSIZE) {
+                       } else if (rx_size < MODBUS_RX_BUFSIZE) {
                                rx_buf[rx_size++] = ch;
                        } else {
                                // Frame too long
                                rx_bad = 2;
                        }
-                       timer_set_period(TIM2, 7500);   // 0.75 ms timeout for end of frame (FIXME: right value?)
-                       timer_generate_event(TIM2, TIM_EGR_UG);
-                       timer_enable_counter(TIM2);
+                       timer_set_period(MODBUS_TIMER, MODBUS_RX_TIMEOUT);
+                       timer_generate_event(MODBUS_TIMER, TIM_EGR_UG);
+                       timer_enable_counter(MODBUS_TIMER);
                }
        }
 
        if (state == STATE_TX) {
                if (status & USART_SR_TXE) {
                        if (tx_pos < tx_size) {
-                               usart_send(USART2, tx_buf[tx_pos++]);
+                               usart_send(MODBUS_USART, tx_buf[tx_pos++]);
                        } else {
                                // The transmitter is double-buffered, so at this moment, it is transmitting
                                // the last byte of the frame. Wait until transfer is completed.
-                               usart_disable_tx_interrupt(USART2);
-                               USART_CR1(USART2) |= USART_CR1_TCIE;
+                               usart_disable_tx_interrupt(MODBUS_USART);
+                               USART_CR1(MODBUS_USART) |= USART_CR1_TCIE;
                                state = STATE_TX_LAST;
                        }
                }
        } else if (state == STATE_TX_LAST) {
                if (status & USART_SR_TC) {
                        // Transfer of the last byte is complete. Release the bus.
-                       USART_CR1(USART2) &= ~USART_CR1_TCIE;
+                       USART_CR1(MODBUS_USART) &= ~USART_CR1_TCIE;
                        tx_done();
                        rx_init();
                }
        }
 }
 
-void tim2_isr(void)
+void MODBUS_TIMER_ISR(void)
 {
-       if (TIM_SR(TIM2) & TIM_SR_UIF) {
-               TIM_SR(TIM2) &= ~TIM_SR_UIF;
+       if (TIM_SR(MODBUS_TIMER) & TIM_SR_UIF) {
+               TIM_SR(MODBUS_TIMER) &= ~TIM_SR_UIF;
                if (state == STATE_RX)
                        rx_done();
        }
@@ -303,7 +353,7 @@ static void write_u16(u16 v)
 static bool body_fits(uint body_len)
 {
        // body_len excludes slave address, function code, and CRC
-       return (2 + body_len + 2 <= TX_BUFSIZE);
+       return (2 + body_len + 2 <= MODBUS_TX_BUFSIZE);
 }
 
 static void report_error(byte code)
@@ -540,7 +590,7 @@ static void func_encapsulated_interface_transport(void)
        for (id = range_min; id <= range_max; id++) {
                if (modbus_id_strings[id]) {
                        byte len = strlen(modbus_id_strings[id]);
-                       byte remains = TX_BUFSIZE - 4 - tx_size;        // 2 for CRC, 2 for object header
+                       byte remains = MODBUS_TX_BUFSIZE - 4 - tx_size; // 2 for CRC, 2 for object header
                        if (len > remains) {
                                // If it is the only object, cut it
                                if (!tx_buf[more_follows_at + 2])
@@ -566,7 +616,7 @@ static void process_frame(void)
        byte func = read_byte();
 
        // Prepare reply frame
-       tx_buf[0] = MB_OUR_ADDRESS;
+       tx_buf[0] = MODBUS_OUR_ADDRESS;
        tx_buf[1] = rx_buf[1];
        tx_size = 2;
 
@@ -623,7 +673,7 @@ void modbus_loop(void)
                return;
        }
 
-       if (rx_buf[0] == MB_OUR_ADDRESS) {
+       if (rx_buf[0] == MODBUS_OUR_ADDRESS) {
                // Frame addressed to us: process and reply
                process_frame();
                tx_init();
index 010fb3cb8a5eb8579cab41b51221cb8e7e100007..913a149c350ae62f1d67167150388708f71dbe8f 100644 (file)
@@ -1,16 +1,15 @@
+/*
+ *     Generic MODBUS Library for STM32
+ *
+ *     (c) 2019 Martin Mareš <mj@ucw.cz>
+ */
+
+#ifndef _MODBUS_H
+#define _MODBUS_H
+
 void modbus_init(void);
 void modbus_loop(void);
 
-enum modbus_id_object {
-       MODBUS_ID_VENDOR_NAME,          // first three must be always defined
-       MODBUS_ID_PRODUCT_CODE,
-       MODBUS_ID_MAJOR_MINOR_REVISION,
-       MODBUS_ID_VENDOR_URL,           // the rest may be NULL
-       MODBUS_ID_PRODUCT_NAME,
-       MODBUS_ID_USER_APP_NAME,
-       MODBUS_ID_MAX,
-};
-
 // Callbacks
 
 bool modbus_check_discrete_input(u16 addr);
@@ -27,4 +26,16 @@ bool modbus_check_holding_register(u16 addr);
 u16 modbus_get_holding_register(u16 addr);
 void modbus_set_holding_register(u16 addr, u16 value);
 
+enum modbus_id_object {
+       MODBUS_ID_VENDOR_NAME,          // first three must be always defined
+       MODBUS_ID_PRODUCT_CODE,
+       MODBUS_ID_MAJOR_MINOR_REVISION,
+       MODBUS_ID_VENDOR_URL,           // the rest may be NULL
+       MODBUS_ID_PRODUCT_NAME,
+       MODBUS_ID_USER_APP_NAME,
+       MODBUS_ID_MAX,
+};
+
 extern const char * const modbus_id_strings[MODBUS_ID_MAX];
+
+#endif
index 96da0141e381b38189ae582974d764a589850551..381b2ed58aec7a34f4e05e665a6ea12b4a0eec22 100644 (file)
@@ -33,6 +33,11 @@ static void gpio_setup(void)
        // PC13 = BluePill LED
        gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
        gpio_clear(GPIOC, GPIO13);
+
+       // Pins for MODBUS USART
+       gpio_set_mode(GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_USART2_RX);
+       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX);
+       gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
 }
 
 static volatile u32 ms_ticks;