Skip to content

quanghona/Freemodbus-CMSIS-RTOS2-STM32

Repository files navigation

Awesome

Translation: cn

Freemodbus-CMSIS-RTOS2-STM32

A port of Freemodbus for CMSIS-RTOS2

Other ports (a tiny awesome Freemodbus dual modes):

Repository Repository owner OS Comment
FreeModbus_Slave-Master-RTT-STM32 Armink RT-Thread The original Freemodbus master mode
FreeModbus_Slave-Master-RTT-STM32 Armink RT-Thread Same repository but hosted on gitee
STM32-FreeModbus-Example ADElectronics None A simple example for both master and slave mode of modbus RTU
HAL_FreeRTOS_Modbus Alidong FreeRTOS A FreeRTOS port
chibi-modbus-master AndruPol chibiOS A ChibiOS port
freemodbus_master_slave connictro ESP32 RTOS A FreeRTOS port for ESP32

Rules

This port is base on some rules:

  • Independent: Each file in porting layer use it own variables. Except for portserial.c and portserial_m.c, which need to use UART instance from HAL library. But other variable in those 2 files still remain static inside the file.
  • Minimize time and execution in ISR: A message (send or receive) is handle in one call. Specifically, the serialport send a whole message in one go. And when receiving character, it save data to a buffer. Until a RX line idle or full message received, a message handler is delegated to a thread to process the message.
  • Message timeout: The port wait for response until timeout. After that, it will skip the current request and return control to system.

Porting steps

Let's make Armink's repository as starting point.

  1. In Freemodbus middleware, changes the port/rtt folder to our OS, in this case cmsis_rtos2.
  2. Inside port/cmsis_rtos2 folder, changes port.c to portcritical.c as the build, the system is misunderstand with the port.c in RTOS (at least it happened in my project).
  3. Implements EnterCriticalSection and ExitCriticalSection functions using CMSIS-RTOS2 kernel control API.
  4. Implement portevent.c and portevent_m.c using CMSIS-RTOS2 Event flags.
  5. Implement porttimer.c and porttimer_m.c using CMSIS-RTOS2 Timer or hardware timer (see ADElectronic's repository to see how to use hardware timer).
  6. Implements portserial.c and portserial_m.c using target HAL UART (or LL UART). We also implement UART ISRs in this file.
  7. Use ADElectronics's repository idea to transfer message in one call. In mbport.h, add xMBPortSerialPutBytes and xMBMasterPortSerialPutBytes prototypes and implement it in portserial.c and portserial_m.c respectively.
  8. In mbrtu.c and mbrtu_m.c, calls the xMBMasterPortSerialPutBytes function inside xMBMasterRTUTransmitFSM at state STATE_M_TX_XMIT. Replaces the xMBMasterPortSerialPutByte or uses the below snippet:
/* Defines the MASTER_SEND_ALL_BYTES_IN_ONE_CALL macro yourself */
#if MASTER_SEND_ALL_BYTES_IN_ONE_CALL > 0
			xMBMasterPortSerialPutBytes(pucMasterSndBufferCur, usMasterSndBufferCount);
			usMasterSndBufferCount = 0;
#else
            xMBMasterPortSerialPutByte( ( CHAR )*pucMasterSndBufferCur );
            pucMasterSndBufferCur++;  /* next byte in send buffer. */
            usMasterSndBufferCount--;
#endif
  1. Implement interrupt handler modbus(es). The handler function is in portserial.c and portserial_m.c, which its prototypes are in port.h (stated in 6.). We focus on 3 interrupt flags:
    • RXNE - receive buffer not empty or new character is received, we save it in a buffer;
    • IDLE - idle flags, indicating a whole message is received;
    • TC - transfer complete, a whole message is sent.
  • Note that we do not handle TXE interrupt and leave it to the HAL library.
  1. Create thread(s) for modbus polling following a simple code snippet:
void MBMasterPollTask(void *argument)
{
    eMBMasterInit(MB_RTU, 6, 9600, MB_PAR_NONE);
    eMBMasterEnable();
    for(;;)
    {
        eMBMasterPoll();
    }
}
  1. Update Makefile (or other toolchain/IDE) to include the modbus library.

Project configuration

  • Build tools: CubeMX + Makefile
  • MCU: STM32F407ZET6 (See Porting steps for other MCU)
  • Modbus serial ports: USART6 (master) and USART3 (slave). To change to other port, for each mode (portserial.c and portserial_m.c), change 2 places:
extern UART_HandleTypeDef huart6;
// ...
#define huart                                   huart6

Replace the correct UART port for your application.

Languages