From 64418d14a2eba3b843c2a2a0a5b94ff474fe8d4b Mon Sep 17 00:00:00 2001 From: Scott Leonard Date: Sun, 23 Nov 2025 21:32:56 -0800 Subject: [PATCH] Basic Framework --- FreeRTOSConfig.h | 158 +++++++++++++++++++++++++++++++++++++ app_tasks.c | 201 +++++++++++++++++++++++++++++++++++++++++++++++ app_tasks.h | 48 +++++++++++ freertos.c | 59 ++++++++++++++ 4 files changed, 466 insertions(+) create mode 100644 FreeRTOSConfig.h create mode 100644 app_tasks.c create mode 100644 app_tasks.h create mode 100644 freertos.c diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h new file mode 100644 index 0000000..bd1ea83 --- /dev/null +++ b/FreeRTOSConfig.h @@ -0,0 +1,158 @@ +/* USER CODE BEGIN Header */ +/* + * FreeRTOS Kernel V10.2.1 + * Portion Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Portion Copyright (C) 2019 StMicroelectronics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ +/* USER CODE END Header */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * These parameters and more are described within the 'configuration' section of the + * FreeRTOS API documentation available on the FreeRTOS.org web site. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* USER CODE BEGIN Includes */ +/* Section where include file can be added */ +/* USER CODE END Includes */ + +/* Ensure definitions are only used by the compiler, and not by the assembler. */ +#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) + #include + extern uint32_t SystemCoreClock; +#endif +#define configENABLE_FPU 0 +#define configENABLE_MPU 0 + +#define configUSE_PREEMPTION 1 +#define configSUPPORT_STATIC_ALLOCATION 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ( SystemCoreClock ) +#define configTICK_RATE_HZ ((TickType_t)1000) +#define configMAX_PRIORITIES ( 56 ) +#define configMINIMAL_STACK_SIZE ((uint16_t)256) +#define configTOTAL_HEAP_SIZE ((size_t)15360) +#define configMAX_TASK_NAME_LEN ( 16 ) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +/* USER CODE BEGIN MESSAGE_BUFFER_LENGTH_TYPE */ +/* Defaults to size_t for backward compatibility, but can be changed + if lengths will always be less than the number of bytes in a size_t. */ +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t +/* USER CODE END MESSAGE_BUFFER_LENGTH_TYPE */ + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( 2 ) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 512 + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_eTaskGetState 1 + +/* + * The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used + * by the application thus the correct define need to be enabled below + */ +#define USE_FreeRTOS_HEAP_4 + +/* Cortex-M specific definitions. */ +#ifdef __NVIC_PRIO_BITS + /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ + #define configPRIO_BITS __NVIC_PRIO_BITS +#else + #define configPRIO_BITS 4 +#endif + +/* The lowest interrupt priority that can be used in a call to a "set priority" +function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 + +/* The highest interrupt priority that can be used by any interrupt service +routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL +INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER +PRIORITY THAN THIS! (higher priorities are lower numeric values. */ +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + +/* Interrupt priorities used by the kernel port layer itself. These are generic +to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! +See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) + +/* Normal assert() semantics without relying on the provision of an assert.h +header file. */ +/* USER CODE BEGIN 1 */ +#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );} +/* USER CODE END 1 */ + +/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS +standard names. */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler + +/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick, + to prevent overwriting SysTick_Handler defined within STM32Cube HAL */ + +#define xPortSysTickHandler SysTick_Handler + +/* USER CODE BEGIN Defines */ +/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */ +/* USER CODE END Defines */ + +#endif /* FREERTOS_CONFIG_H */ diff --git a/app_tasks.c b/app_tasks.c new file mode 100644 index 0000000..35ebafd --- /dev/null +++ b/app_tasks.c @@ -0,0 +1,201 @@ +/* USER CODE BEGIN Header */ +/** + * Begin Comments + * @brief Lightweight Modbus RTU slave driver for STM32 + FreeRTOS + * + * This module implements a simple, real-time friendly Modbus RTU slave + * using UART idle-line detection and RS485 GPIO control. It supports + * function codes 0x03 and 0x10 and is designed to run as a cooperative + * background task in embedded systems using STM32 HAL and FreeRTOS. + * + * Author: Scott Leonard + * License: Unlicense + */ + +/* USER CODE END Header */ + +#include "main.h" +#include "app_tasks.h" + +#include +#include + +#include "FreeRTOS.h" +#include "task.h" +#include "cmsis_os.h" + + +#define RX_FRAME_LENGTH 256 +#define TX_FRAME_LENGTH 256 +#define TABLE_SIZE 128 +#define SLAVE_ID 0x01 + +enum { + READ_HOLDING_REGISTERS = 0x03, + WRITE_HOLDING_REGISTERS = 0x10 +}; + +#define ILLEGAL_FUNCTION 0x01 +#define ILLEGAL_DATA_ADDRESS 0x02 +#define ILLEGAL_DATA_VALUE 0x03 + + +extern UART_HandleTypeDef huart2; + +uint8_t request_frame[RX_FRAME_LENGTH]; +uint8_t response_frame[TX_FRAME_LENGTH]; +uint16_t modbus_table[TABLE_SIZE] = {0}; + +void rs485_dir_tx(void) { + HAL_GPIO_WritePin(GPIOF, PF0_DE_NRE_Pin, GPIO_PIN_SET); // HIGH = TX + __NOP(); __NOP(); __NOP(); __NOP(); // Delay for direction switch +} + +void rs485_dir_rx(void) { + HAL_GPIO_WritePin(GPIOF, PF0_DE_NRE_Pin, GPIO_PIN_RESET); // LOW = RX +} + +void app_task_Modbus(void) +{ + static uint8_t modbus_initialized = 0; + if (!modbus_initialized) { + init_modbus_system(); + HAL_UARTEx_ReceiveToIdle_IT(&huart2, request_frame, RX_FRAME_LENGTH); + modbus_initialized = 1; + } + osDelay(10); +} + +uint16_t calculate_modbus_crc(uint8_t *data, uint16_t length) { + uint16_t crc = 0xFFFF; + for (uint16_t pos = 0; pos < length; pos++) { + crc ^= (uint16_t)data[pos]; + for (uint8_t i = 8; i != 0; i--) { + if ((crc & 0x0001) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else { + crc >>= 1; + } + } + } + return crc; +} + +void send_modbus_data(uint8_t *data, int size) { + uint16_t crc = calculate_modbus_crc(data, size); + data[size] = crc & 0xFF; + data[size+1] = (crc>>8) & 0xFF; + rs485_dir_tx(); + HAL_UART_Transmit(&huart2, data, size+2, 100); + rs485_dir_rx(); +} + +void modbus_exception(uint8_t exceptionCode) { + response_frame[0] = request_frame[0]; + response_frame[1] = request_frame[1] | 0x80; + response_frame[2] = exceptionCode; + send_modbus_data(response_frame, 3); +} + +uint8_t table_read(void) { + uint16_t startAddr = ((request_frame[2]<<8) | request_frame[3]); + uint16_t numRegs = ((request_frame[4]<<8) | request_frame[5]); + + if ((numRegs < 1) || (numRegs > 125)) { + modbus_exception(ILLEGAL_DATA_VALUE); + return 0; + } + + uint16_t endAddr = startAddr + numRegs - 1; + if (endAddr > TABLE_SIZE - 1) { + modbus_exception(ILLEGAL_DATA_ADDRESS); + return 0; + } + + response_frame[0] = request_frame[0]; + response_frame[1] = request_frame[1]; + response_frame[2] = numRegs * 2; + int indx = 3; + + for (int i = 0; i < numRegs; i++) { + response_frame[indx++] = (modbus_table[startAddr] >> 8) & 0xFF; + response_frame[indx++] = modbus_table[startAddr] & 0xFF; + startAddr++; + } + + send_modbus_data(response_frame, indx); + return 1; +} + +uint8_t table_write(void) { + uint16_t startAddr = ((request_frame[2]<<8) | request_frame[3]); + uint16_t numRegs = ((request_frame[4]<<8) | request_frame[5]); + uint8_t byteCount = request_frame[6]; + + if ((numRegs < 1) || (numRegs > 123)) { + modbus_exception(ILLEGAL_DATA_VALUE); + return 0; + } + + if (byteCount != (numRegs * 2)) { + modbus_exception(ILLEGAL_DATA_VALUE); + return 0; + } + + uint16_t endAddr = startAddr + numRegs - 1; + if (endAddr > TABLE_SIZE - 1) { + modbus_exception(ILLEGAL_DATA_ADDRESS); + return 0; + } + + int indx = 7; + for (int i = 0; i < numRegs; i++) { + uint8_t high_byte = request_frame[indx++]; + uint8_t low_byte = request_frame[indx++]; + modbus_table[startAddr++] = (high_byte << 8) | low_byte; + } + + response_frame[0] = request_frame[0]; + memcpy(&response_frame[1], &request_frame[1], 5); + + + send_modbus_data(response_frame, 6); + return 1; +} + +void process_modbus_message(uint16_t Size) { + if (Size < 8) return; + if (request_frame[0] != SLAVE_ID) return; + + uint16_t receivedCRC = request_frame[Size-2] | (request_frame[Size-1] << 8); + uint16_t calculatedCRC = calculate_modbus_crc(request_frame, Size-2); + if (receivedCRC != calculatedCRC) return; + + switch (request_frame[1]) { + case READ_HOLDING_REGISTERS: + table_read(); + break; + case WRITE_HOLDING_REGISTERS: + table_write(); + break; + default: + modbus_exception(ILLEGAL_FUNCTION); + break; + } +} + +void init_modbus_system(void) { + + memset(request_frame, 0, sizeof(request_frame)); + memset(response_frame, 0, sizeof(response_frame)); + rs485_dir_rx(); // Start in RX mode +} + +void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) +{ + if (huart->Instance == USART2) { + __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_PEF | UART_CLEAR_FEF | UART_CLEAR_NEF | UART_CLEAR_OREF); + HAL_UARTEx_ReceiveToIdle_IT(&huart2, request_frame, RX_FRAME_LENGTH); + } +} diff --git a/app_tasks.h b/app_tasks.h new file mode 100644 index 0000000..0049348 --- /dev/null +++ b/app_tasks.h @@ -0,0 +1,48 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : app_tasks.h + * @brief : Header for ModTOS (Modbus RTU slave task). + * + * Declares core symbols for the Modbus driver running in FreeRTOS context. + * Author: Scott Leonard + * License: Unlicense + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifndef __APP_TASKS_H +#define __APP_TASKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "main.h" +#include "cmsis_os.h" +#include + +/* Shared buffers and register table */ +extern uint8_t request_frame[]; +extern uint8_t response_frame[]; +extern uint16_t modbus_table[]; + +/* Task entry point */ +void app_task_Modbus(void); + +/* Core Modbus driver functions */ +void rs485_dir_tx(void); +void rs485_dir_rx(void); +uint16_t calculate_modbus_crc(uint8_t *data, uint16_t length); +void send_modbus_data(uint8_t *data, int size); +void modbus_exception(uint8_t exception_code); +uint8_t table_read(void); +uint8_t table_write(void); +void process_modbus_message(uint16_t size); +void init_modbus_system(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __APP_TASKS_H */ diff --git a/freertos.c b/freertos.c new file mode 100644 index 0000000..ac67769 --- /dev/null +++ b/freertos.c @@ -0,0 +1,59 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * File Name : freertos.c + * Description : Code for freertos applications + ****************************************************************************** + * @attention + * + * Copyright (c) 2025 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "FreeRTOS.h" +#include "task.h" +#include "main.h" + +/* Private includes ----------------------------------------------------------*/ +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +/* Private typedef -----------------------------------------------------------*/ +/* USER CODE BEGIN PTD */ + +/* USER CODE END PTD */ + +/* Private define ------------------------------------------------------------*/ +/* USER CODE BEGIN PD */ + +/* USER CODE END PD */ + +/* Private macro -------------------------------------------------------------*/ +/* USER CODE BEGIN PM */ + +/* USER CODE END PM */ + +/* Private variables ---------------------------------------------------------*/ +/* USER CODE BEGIN Variables */ + +/* USER CODE END Variables */ + +/* Private function prototypes -----------------------------------------------*/ +/* USER CODE BEGIN FunctionPrototypes */ + +/* USER CODE END FunctionPrototypes */ + +/* Private application code --------------------------------------------------*/ +/* USER CODE BEGIN Application */ + +/* USER CODE END Application */ +