📜 ⬆️ ⬇️

Porting FreeModbus 1.5 under STM32 HAL rs485 without RTOS

Recently, I started working on embedded systems and has come to programming microcontrollers, namely, STM32F373. One of the tasks was to deploy a Modbus Slave RTU over an rs485 interface.

As the deadline was pressed, it was decided to take something ready, which I did. A short googling led me to the FreeModbus library, and here the pain started, which I hope you will not encounter.

0. Preparing a project


For a quick start, I use STM32CubeMX , it allows you to quickly generate the controller initialization code without pain. In my case, this is STM32F373VCTx LQFP100.

Periphery:
')

I only do this to generate the HAL_TIM_Base_MspInit() and HAL_UART_MspInit() functions, this will make your life easier if you connect other timers and usart'y.

peripheral

Clocking:

We need the TIM6 timer or the one you have chosen to operate at a frequency of at least 20kHz. After rustling through the initialization code, I realized that TIM6 works on PCLK1 (Peripheral CLocK). By default, it (the timer) is clocked from HSI (High Speed ​​Internal resonator), just like everything else, but it seemed to me that this is not enough, so I drove the whole thing through the PLL (Phase-Locked Loop) and set the multiplier value to x8 to raise to 32 MHz, so the MK works smoother and more pleasant.

clock config

Additional configuration:

We will need to work with the interrupt timer, so we will enable it on the Configuration tab of the System NVIC section "TIM6 global interrupt and DAC1 underrun error interrupts". We also need USART1 "USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25" interrupts.

image

I use a lot of additional interrupts to indicate errors, so don’t pay attention to a bunch of checkboxes.

On it we finish, we save the project, the code.

I generate code for SW4STM32 (System Workbench for STM32, registration required). I prefer to generate separate .h / .c files for the periphery, turn on full_assert and turn off the generation of the call to the functions of the peripheral initialization.

imageimageimage

1. Download & install


Everything is simple here: download the archive , unpack, copy the files from the archive / modbus / into the project, do not forget about the archive / modbus / include , add to the includes.
We will also need a port layer, with which we will work, take it from the archive / demo / BARE , copy, ready.

2. Porting


We do everything based on the official documentation .

port.h

Let's start with include'ov: I remove assert.h , because I throw assert() on assert_param() , and add stm32f3xx_hal.h , if you use a processor of another series, then instead of this, use the appropriate library.

Next we are interested in the ENTER_CRITICAL_SECTION( ) and EXIT_CRITICAL_SECTION( ) macros. We replace them with __disable_irq() and __enable_irq() respectively.
Immediately, it should be noted that this is not the best implementation of critical sections: if a situation occurs in which a function with such a critical section causes another function with such a critical section from its critical section, we will get a premature interrupt activation (example in the spoiler), but if you port it described in the article way, then this situation does not happen. A solution to the problem can be found here .

Problem demonstration
 void foo() { ENTER_CRITICAL_SECTION(); //      //       EXIT_CRITICAL_SECTION(); //  ,    ,        bar() } void bar() { ENTER_CRITICAL_SECTION(); //      foo(); //      ,    EXIT_CRITICAL_SECTION(); //  =( } 



I also add the prototype UART_IRQ_Handler() , which will be used for a bit of a crutch implementation of the interrupt response.

port.h
 /* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: $Id: port.h,v 1.1 2006/08/22 21:35:13 wolti Exp $ */ #ifndef _PORT_H #define _PORT_H #include "stm32f3xx_hal.h" #include <inttypes.h> #define INLINE inline #define PR_BEGIN_EXTERN_C extern "C" { #define PR_END_EXTERN_C } //   #define ENTER_CRITICAL_SECTION( ) __disable_irq() #define EXIT_CRITICAL_SECTION( ) __enable_irq() //      #define assert(val) assert_param(val) typedef uint8_t BOOL; typedef unsigned char UCHAR; typedef char CHAR; typedef uint16_t USHORT; typedef int16_t SHORT; typedef uint32_t ULONG; typedef int32_t LONG; #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif // callback  uart BOOL UART_IRQ_Handler(USART_TypeDef * usart); #endif 


porttimer.c

Again, let's start with the includes: in the platform includes section, add stm32f3xx_hal_tim.h , from which you need the TIM_COUNTERMODE_UP constant.

In the static functions section, I add a handler for the timer and 2 variables for storing the timeout, and the current value of the counter.
The xMBPortTimersInit() function is xMBPortTimersInit() timer initialization. We expect that it will tick every 50 ÎĽs.

htim - our handler, type - static TIM_HandleTypeDef , global
timeout - static uint16_t , global
downcounter - static uint16_t , global

  htim.Instance = TIM6; // ,     6  htim.Init.CounterMode = TIM_COUNTERMODE_UP; //    ( 0  ) /*    ,       32 HAL_RCC_GetPCLK1Freq() -   PCLK1,     32000000   1000000,         1  -1 -   :   ,  0 -    1, 1 -   2  ..*/ htim.Init.Prescaler = (HAL_RCC_GetPCLK1Freq() / 1000000) - 1; //  50 ,  -1, ,   htim.Init.Period = 50 - 1; //   ,      timeout = usTim1Timerout50us; // ,   ,    HAL return HAL_OK == HAL_TIM_Base_Init(&htim) ? TRUE : FALSE; 

The vMBPortTimersEnable() function. There are 2 simple steps:

  1. Reset timer.
  2. Start the timer asynchronously with interrupt feedback.

  downcounter = timeout; HAL_TIM_Base_Start_IT(&htim); 

The vMBPortTimersDisable() function. Then just turn off the timer.

  HAL_TIM_Base_Stop_IT(&htim); 

Also add a function to respond to the HAL_TIM_Base_Stop_IT() timer:

  // ,    ,       if(__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE) != RESET && __HAL_TIM_GET_IT_SOURCE(&htim, TIM_IT_UPDATE) !=RESET) { __HAL_TIM_CLEAR_IT(&htim, TIM_IT_UPDATE); //    if (!--downcounter) //  ,      prvvTIMERExpiredISR(); //  callback  } 

porttimer.c
 /* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: $Id: porttimer.c,v 1.1 2006/08/22 21:35:13 wolti Exp $ */ /* ----------------------- Platform includes --------------------------------*/ #include "port.h" #include "stm32f3xx_hal_tim.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ static void prvvTIMERExpiredISR( void ); static TIM_HandleTypeDef htim; static uint16_t timeout = 0; static uint16_t downcounter = 0; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { htim.Instance = TIM6; htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Prescaler = (HAL_RCC_GetPCLK1Freq() / 1000000) - 1; htim.Init.Period = 50 - 1; timeout = usTim1Timerout50us; return HAL_OK == HAL_TIM_Base_Init(&htim) ? TRUE : FALSE; } inline void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ downcounter = timeout; HAL_TIM_Base_Start_IT(&htim); } inline void vMBPortTimersDisable( ) { /* Disable any pending timers. */ HAL_TIM_Base_Stop_IT(&htim); } /* Create an ISR which is called whenever the timer has expired. This function * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that * the timer has expired. */ static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void TIM6_DAC1_IRQHandler(void) { /* TIM Update event */ if(__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE) != RESET && __HAL_TIM_GET_IT_SOURCE(&htim, TIM_IT_UPDATE) !=RESET) { __HAL_TIM_CLEAR_IT(&htim, TIM_IT_UPDATE); if (!--downcounter) prvvTIMERExpiredISR(); } } 


portserial.c

In general, the whole article was written precisely because of this file, because I could not port the UART-layer to STM32 under the “pure” HAL and had to go deeper. In the description of the library it is written that interrupts are needed, signaling that the transmission buffer is empty and that the reception buffer is not empty. And HAL does not support such callbacks, so we will show off.

Thesaurus:

huart - handler for UART, static UART_HandleTypeDef
DE_Port - port of the channel direction control legs, static GPIO_TypeDef *
DE_Pin - pin to control the channel direction, static uint16_t

Let's start with the vMBPortSerialEnable() function. Here we turn to the "hidden opportunities" HAL.

  if (xRxEnable) { //      //      (.   rs485) HAL_GPIO_WritePin(DE_Port, DE_Pin, GPIO_PIN_RESET); __HAL_UART_ENABLE_IT(&huart, UART_IT_RXNE); //  ,       } else { __HAL_UART_DISABLE_IT(&huart, UART_IT_RXNE); //   } if (xTxEnable) { //      //      (.   rs485) HAL_GPIO_WritePin(DE_Port, DE_Pin, GPIO_PIN_SET); __HAL_UART_ENABLE_IT(&huart, UART_IT_TXE); //  ,      } else { __HAL_UART_DISABLE_IT(&huart, UART_IT_TXE); //   } 

Further, the initialization function of the port xMBPortSerialInit() . Made it more or less universal, it initializes the specified UART itself, but do not forget that the part with MSP initialization should be generated: initialization of pins, interrupts, etc.

xMBPortSerialInit ()
  huart.Init.Mode = UART_MODE_TX_RX; //      huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; //    (   rs485) // ,    ,        huart.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED; huart.Init.OverSampling = UART_OVERSAMPLING_16; //  - huart.Init.StopBits = UART_STOPBITS_1; //  - huart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; //  : UART,     switch (ucPORT) { case 0: huart.Instance = USART1; DE_Port = GPIOA; DE_Pin = GPIO_PIN_12; break; case 1: huart.Instance = USART2; DE_Port = GPIOA; DE_Pin = GPIO_PIN_1; break; case 2: huart.Instance = USART3; DE_Port = GPIOB; DE_Pin = GPIO_PIN_14; break; default: //  ,      ,     ,   - return FALSE; } //  huart.Init.BaudRate = ulBaudRate; //   switch (ucDataBits) { case 8: huart.Init.WordLength = UART_WORDLENGTH_8B; break; case 9: huart.Init.WordLength = UART_WORDLENGTH_9B; break; default: //  ,      ,     ,   - return FALSE; } //    switch (eParity) { case MB_PAR_NONE: huart.Init.Parity = UART_PARITY_NONE; break; case MB_PAR_EVEN: huart.Init.Parity = UART_PARITY_EVEN; break; case MB_PAR_ODD: huart.Init.Parity = UART_PARITY_ODD; break; default: //  ,      ,     ,   - return FALSE; } //  ,  rs485 return HAL_OK == HAL_RS485Ex_Init(&huart, UART_DE_POLARITY_HIGH, 0, 0) ? TRUE : FALSE; 


Now I / O functions xMBPortSerialPutByte() and xMBPortSerialGetByte() . Here we will do hardcore low-level (for HAL) IO, using UART's registers.

To read the byte from the port: *pucByte = huart.Instance->RDR
To write a byte to the port: huart.Instance->TDR = ucByte

And finally, we add the UART_IRQ_Handler() function, which we described in port.h. It will be responsible for intercepting I / O interrupts. The basic idea: if the interruption is “our”, i.e. from our port, and what we are waiting for, we return TRUE - this means that we intercepted it.

  if (usart == huart.Instance) { // ,     if((__HAL_UART_GET_IT(&huart, UART_IT_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(&huart, UART_IT_RXNE) != RESET)) { // ,      pxMBFrameCBByteReceived(); //     __HAL_UART_SEND_REQ(&huart, UART_RXDATA_FLUSH_REQUEST); //    return TRUE; // ,   } if((__HAL_UART_GET_IT(&huart, UART_IT_TXE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(&huart, UART_IT_TXE) != RESET)) { // ,      pxMBFrameCBTransmitterEmpty(); //     return TRUE; // ,   } } return FALSE; // ,    ,    

portserial.c
 /* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: $Id: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $ */ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ static UART_HandleTypeDef huart; static GPIO_TypeDef * DE_Port; static uint16_t DE_Pin; /* ----------------------- Start implementation -----------------------------*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ if (xRxEnable) { HAL_GPIO_WritePin(DE_Port, DE_Pin, GPIO_PIN_RESET); __HAL_UART_ENABLE_IT(&huart, UART_IT_RXNE); } else { __HAL_UART_DISABLE_IT(&huart, UART_IT_RXNE); } if (xTxEnable) { HAL_GPIO_WritePin(DE_Port, DE_Pin, GPIO_PIN_SET); __HAL_UART_ENABLE_IT(&huart, UART_IT_TXE); } else { __HAL_UART_DISABLE_IT(&huart, UART_IT_TXE); } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { huart.Init.Mode = UART_MODE_TX_RX; huart.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart.Init.OneBitSampling = UART_ONEBIT_SAMPLING_DISABLED; huart.Init.OverSampling = UART_OVERSAMPLING_16; huart.Init.StopBits = UART_STOPBITS_1; huart.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; switch (ucPORT) { case 0: huart.Instance = USART1; DE_Port = GPIOA; DE_Pin = GPIO_PIN_4; break; case 1: huart.Instance = USART2; DE_Port = GPIOA; DE_Pin = GPIO_PIN_1; break; case 2: huart.Instance = USART3; DE_Port = GPIOB; DE_Pin = GPIO_PIN_14; break; default: return FALSE; } huart.Init.BaudRate = ulBaudRate; switch (ucDataBits) { case 8: huart.Init.WordLength = UART_WORDLENGTH_8B; break; case 9: huart.Init.WordLength = UART_WORDLENGTH_9B; break; default: return FALSE; } switch (eParity) { case MB_PAR_NONE: huart.Init.Parity = UART_PARITY_NONE; break; case MB_PAR_EVEN: huart.Init.Parity = UART_PARITY_EVEN; break; case MB_PAR_ODD: huart.Init.Parity = UART_PARITY_ODD; break; default: return FALSE; } return HAL_OK == HAL_RS485Ex_Init(&huart, UART_DE_POLARITY_HIGH, 0, 0); } BOOL xMBPortSerialPutByte( CHAR ucByte ) { /* Put a byte in the UARTs transmit buffer. This function is called * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been * called. */ huart.Instance->TDR = ucByte; return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { /* Return the byte in the UARTs receive buffer. This function is called * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. */ *pucByte = huart.Instance->RDR; return TRUE; } BOOL UART_IRQ_Handler(USART_TypeDef * usart) { if (usart == huart.Instance) { if((__HAL_UART_GET_IT(&huart, UART_IT_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(&huart, UART_IT_RXNE) != RESET)) { pxMBFrameCBByteReceived(); __HAL_UART_SEND_REQ(&huart, UART_RXDATA_FLUSH_REQUEST); return TRUE; } if((__HAL_UART_GET_IT(&huart, UART_IT_TXE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(&huart, UART_IT_TXE) != RESET)) { pxMBFrameCBTransmitterEmpty(); return TRUE; } } return FALSE; } 


It would seem that this is all, but there are a couple more points:

As you can see, nobody calls the UART_IRQ_Handler() function. To fix this, you need to visit the stm32f3xx_it.c file. There we add include for port.h. The following lines must be added to all USARTx_IRQ_Handler's (in our case, USART1_IRQ_Handle() ).

  //   ,       if (FALSE != UART_IRQ_Handler(USART1)) return; 

stm32f3xx_it.h
 /** ****************************************************************************** * @file stm32f3xx_it.c * @brief Interrupt Service Routines. ****************************************************************************** * * COPYRIGHT(c) 2016 STMicroelectronics * * 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 STMicroelectronics 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. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32f3xx_hal.h" #include "stm32f3xx.h" #include "stm32f3xx_it.h" /* USER CODE BEGIN 0 */ #include "port.h" /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ extern UART_HandleTypeDef huart1; /******************************************************************************/ /* Cortex-M4 Processor Interruption and Exception Handlers */ /******************************************************************************/ /** * @brief This function handles System tick timer. */ void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); HAL_SYSTICK_IRQHandler(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ } /******************************************************************************/ /* STM32F3xx Peripheral Interrupt Handlers */ /* Add here the Interrupt Handlers for the used peripherals. */ /* For the available peripheral interrupt handler names, */ /* please refer to the startup file (startup_stm32f3xx.s). */ /******************************************************************************/ /** * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ if (FALSE != UART_IRQ_Handler(USART1)) return; /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 


And the strangest thing is that I still could not decide: the library, in response, gave all the bytes except the last. It killed me, and when calculating everything was OK, this is probably a question of UART or hand curves, but the following decision saved: just add another one to the byte counter (the modbus / rtu / mbrtu.c file is eMBRTUSend() ).

modbus / rtu / mbrtu.c eMBRTUSend () function
 eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) { eMBErrorCode eStatus = MB_ENOERR; USHORT usCRC16; ENTER_CRITICAL_SECTION( ); /* Check if the receiver is still in idle state. If not we where to * slow with processing the received frame and the master sent another * frame on the network. We have to abort sending the frame. */ if( eRcvState == STATE_RX_IDLE ) { /* First byte before the Modbus-PDU is the slave address. */ pucSndBufferCur = ( UCHAR * ) pucFrame - 1; usSndBufferCount = 1; /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */ pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress; usSndBufferCount += usLength; /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */ usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF ); ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 ); usSndBufferCount++; //      /* Activate the transmitter. */ eSndState = STATE_TX_XMIT; vMBPortSerialEnable( FALSE, TRUE ); } else { eStatus = MB_EIO; } EXIT_CRITICAL_SECTION( ); return eStatus; } 


3. Usage


Already almost done.

Customization

Visit mbconfig.h . Look for it in include'ah. I have SW4STM32, so Ctrl + Click saves me. First, we configure the library to work only with ModbusRTU:

 /*! \brief If Modbus ASCII support is enabled. */ #define MB_ASCII_ENABLED ( 0 ) /*! \brief If Modbus RTU support is enabled. */ #define MB_RTU_ENABLED ( 1 ) /*! \brief If Modbus TCP support is enabled. */ #define MB_TCP_ENABLED ( 0 ) 

You can also turn off features that you don’t use, this will ease the library and allow you not to implement callbacks.

Using

  1. #include "mb.h
  2. eMBInit ()
  3. eMBEnable ()
  4. eMBPoll ()

And don't forget to implement callbacks.

On this I think that I fulfilled my mission, go to the off-site for examples and documentation.

Usage: Modules / Modbus
Callbacks: Modules / Modbus Registers

Source: https://habr.com/ru/post/279747/


All Articles