From: Martin Mares Date: Mon, 8 Jul 2019 17:05:48 +0000 (+0200) Subject: ModBus: Parametrization X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=36f9f87fe6a65c2bbedbd09ef91011d1553c9d3e;p=home-hw.git ModBus: Parametrization --- diff --git a/test-modbus/config.h b/test-modbus/config.h new file mode 100644 index 0000000..1b116d6 --- /dev/null +++ b/test-modbus/config.h @@ -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 diff --git a/test-modbus/modbus.c b/test-modbus/modbus.c index 69fa980..39bd039 100644 --- a/test-modbus/modbus.c +++ b/test-modbus/modbus.c @@ -1,3 +1,10 @@ +/* + * Generic MODBUS Library for STM32 + * + * (c) 2019 Martin Mareš + */ + +#include "config.h" #include "util.h" #include "modbus.h" @@ -9,6 +16,59 @@ #include #include +/*** 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(); diff --git a/test-modbus/modbus.h b/test-modbus/modbus.h index 010fb3c..913a149 100644 --- a/test-modbus/modbus.h +++ b/test-modbus/modbus.h @@ -1,16 +1,15 @@ +/* + * Generic MODBUS Library for STM32 + * + * (c) 2019 Martin Mareš + */ + +#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 diff --git a/test-modbus/test.c b/test-modbus/test.c index 96da014..381b2ed 100644 --- a/test-modbus/test.c +++ b/test-modbus/test.c @@ -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;