refactor: 精简R8裸机工程并补强调试链路
This commit is contained in:
@@ -1,370 +0,0 @@
|
||||
/* USER CODE BEGIN Header */
|
||||
/**
|
||||
******************************************************************************
|
||||
* File Name : freertos.c
|
||||
* Description : Code for freertos applications
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2026 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"
|
||||
#include "cmsis_os.h"
|
||||
|
||||
/* Private includes ----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Includes */
|
||||
#include "semphr.h"
|
||||
#include "stream_buffer.h"
|
||||
|
||||
/* Application modules */
|
||||
#include "tcp_server.h"
|
||||
#include "tcp_client.h"
|
||||
#include "uart_trans.h"
|
||||
#include "config.h"
|
||||
#include "ethernetif.h"
|
||||
|
||||
/* LwIP includes */
|
||||
#include "lwip/tcpip.h"
|
||||
#include "lwip/ip4_addr.h"
|
||||
#include "lwip/dhcp.h"
|
||||
#include "lwip/timeouts.h"
|
||||
/* USER CODE END Includes */
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PTD */
|
||||
|
||||
/* USER CODE END PTD */
|
||||
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PD */
|
||||
/* Task stack sizes (words, not bytes) */
|
||||
#define LWIP_TASK_STACK_SIZE 384
|
||||
#define TCP_SERVER_TASK_STACK_SIZE 320
|
||||
#define TCP_CLIENT_TASK_STACK_SIZE 320
|
||||
#define UART_TRANS_TASK_STACK_SIZE 192
|
||||
#define CONFIG_TASK_STACK_SIZE 256
|
||||
#define DEFAULT_TASK_STACK_SIZE 128
|
||||
|
||||
/* Task priorities */
|
||||
#define LWIP_TASK_PRIORITY (osPriorityAboveNormal)
|
||||
#define TCP_SERVER_TASK_PRIORITY (osPriorityNormal)
|
||||
#define TCP_CLIENT_TASK_PRIORITY (osPriorityNormal)
|
||||
#define UART_TRANS_TASK_PRIORITY (osPriorityNormal)
|
||||
#define CONFIG_TASK_PRIORITY (osPriorityBelowNormal)
|
||||
#define DEFAULT_TASK_PRIORITY (osPriorityLow)
|
||||
/* USER CODE END PD */
|
||||
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* USER CODE BEGIN PM */
|
||||
|
||||
/* USER CODE END PM */
|
||||
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* USER CODE BEGIN Variables */
|
||||
/* Task handles */
|
||||
TaskHandle_t lwipTaskHandle = NULL;
|
||||
TaskHandle_t tcpServerTaskHandle = NULL;
|
||||
TaskHandle_t tcpClientTaskHandle = NULL;
|
||||
TaskHandle_t uartServerTransTaskHandle = NULL;
|
||||
TaskHandle_t uartClientTransTaskHandle = NULL;
|
||||
TaskHandle_t configTaskHandle = NULL;
|
||||
|
||||
/* SPI mutex for CH390 access */
|
||||
SemaphoreHandle_t ch390SpiMutex = NULL;
|
||||
|
||||
/* CH390 interrupt notification semaphore */
|
||||
SemaphoreHandle_t ch390IntSemaphore = NULL;
|
||||
/* USER CODE END Variables */
|
||||
|
||||
/* Definitions for defaultTask */
|
||||
osThreadId_t defaultTaskHandle;
|
||||
const osThreadAttr_t defaultTask_attributes = {
|
||||
.name = "defaultTask",
|
||||
.stack_size = DEFAULT_TASK_STACK_SIZE * 4,
|
||||
.priority = (osPriority_t) DEFAULT_TASK_PRIORITY,
|
||||
};
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* USER CODE BEGIN FunctionPrototypes */
|
||||
void LwIPTask(void *argument);
|
||||
/* USER CODE END FunctionPrototypes */
|
||||
|
||||
void StartDefaultTask(void *argument);
|
||||
|
||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
||||
|
||||
/**
|
||||
* @brief FreeRTOS initialization
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void MX_FREERTOS_Init(void) {
|
||||
/* USER CODE BEGIN Init */
|
||||
|
||||
/* USER CODE END Init */
|
||||
|
||||
/* USER CODE BEGIN RTOS_MUTEX */
|
||||
/* Create SPI mutex for CH390 access */
|
||||
ch390SpiMutex = xSemaphoreCreateMutex();
|
||||
configASSERT(ch390SpiMutex != NULL);
|
||||
/* USER CODE END RTOS_MUTEX */
|
||||
|
||||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
||||
/* Create CH390 interrupt notification semaphore */
|
||||
ch390IntSemaphore = xSemaphoreCreateBinary();
|
||||
configASSERT(ch390IntSemaphore != NULL);
|
||||
/* USER CODE END RTOS_SEMAPHORES */
|
||||
|
||||
/* USER CODE BEGIN RTOS_TIMERS */
|
||||
/* start timers, add new ones, ... */
|
||||
/* USER CODE END RTOS_TIMERS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_QUEUES */
|
||||
/* add queues, ... */
|
||||
/* USER CODE END RTOS_QUEUES */
|
||||
|
||||
/* Create the thread(s) */
|
||||
/* creation of defaultTask */
|
||||
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
||||
|
||||
/* USER CODE BEGIN RTOS_THREADS */
|
||||
BaseType_t task_created;
|
||||
|
||||
/* Create LwIP task (handles network stack + TCP server/client) */
|
||||
task_created = xTaskCreate(LwIPTask, "LwIP", LWIP_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 3, &lwipTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
|
||||
/* Create TCP Server task */
|
||||
task_created = xTaskCreate(tcp_server_task, "TCPServer", TCP_SERVER_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 2, &tcpServerTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
|
||||
/* Create TCP Client task */
|
||||
task_created = xTaskCreate(tcp_client_task, "TCPClient", TCP_CLIENT_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 2, &tcpClientTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
|
||||
/* Create UART Server transparent transmission task */
|
||||
task_created = xTaskCreate(uart_server_trans_task, "UartSrvTx", UART_TRANS_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 2, &uartServerTransTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
|
||||
/* Create UART Client transparent transmission task */
|
||||
task_created = xTaskCreate(uart_client_trans_task, "UartCliTx", UART_TRANS_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 2, &uartClientTransTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
|
||||
/* Create Configuration task (AT commands via UART1) */
|
||||
task_created = xTaskCreate(config_task, "Config", CONFIG_TASK_STACK_SIZE, NULL,
|
||||
tskIDLE_PRIORITY + 1, &configTaskHandle);
|
||||
configASSERT(task_created == pdPASS);
|
||||
/* USER CODE END RTOS_THREADS */
|
||||
|
||||
/* USER CODE BEGIN RTOS_EVENTS */
|
||||
/* add events, ... */
|
||||
/* USER CODE END RTOS_EVENTS */
|
||||
|
||||
}
|
||||
|
||||
/* USER CODE BEGIN Header_StartDefaultTask */
|
||||
/**
|
||||
* @brief Function implementing the defaultTask thread.
|
||||
* LED blinking for system status indication.
|
||||
* @param argument: Not used
|
||||
* @retval None
|
||||
*/
|
||||
/* USER CODE END Header_StartDefaultTask */
|
||||
void StartDefaultTask(void *argument)
|
||||
{
|
||||
/* USER CODE BEGIN StartDefaultTask */
|
||||
(void)argument;
|
||||
|
||||
/* Infinite loop - LED heartbeat */
|
||||
for(;;)
|
||||
{
|
||||
/* Toggle LED (PC13, active low) */
|
||||
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
|
||||
|
||||
/* 500ms toggle = 1Hz blink rate */
|
||||
osDelay(500);
|
||||
}
|
||||
/* USER CODE END StartDefaultTask */
|
||||
}
|
||||
|
||||
/* Private application code --------------------------------------------------*/
|
||||
/* USER CODE BEGIN Application */
|
||||
|
||||
/**
|
||||
* @brief LwIP task - handles network stack initialization and processing
|
||||
*/
|
||||
void LwIPTask(void *argument)
|
||||
{
|
||||
(void)argument;
|
||||
|
||||
ip4_addr_t ipaddr, netmask, gw;
|
||||
uart_config_t uart_server_cfg;
|
||||
uart_config_t uart_client_cfg;
|
||||
uint8_t use_dhcp = 0;
|
||||
const device_config_t *cfg;
|
||||
|
||||
/* Wait for configuration to be loaded */
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
/* Get device configuration */
|
||||
cfg = config_get();
|
||||
|
||||
/* Initialize UART transparent transmission module */
|
||||
uart_trans_init();
|
||||
|
||||
/* Apply UART settings from configuration */
|
||||
uart_server_cfg.baudrate = UART_DEFAULT_BAUDRATE;
|
||||
uart_server_cfg.data_bits = UART_DEFAULT_DATA_BITS;
|
||||
uart_server_cfg.stop_bits = UART_DEFAULT_STOP_BITS;
|
||||
uart_server_cfg.parity = UART_DEFAULT_PARITY;
|
||||
|
||||
uart_client_cfg.baudrate = UART_DEFAULT_BAUDRATE;
|
||||
uart_client_cfg.data_bits = UART_DEFAULT_DATA_BITS;
|
||||
uart_client_cfg.stop_bits = UART_DEFAULT_STOP_BITS;
|
||||
uart_client_cfg.parity = UART_DEFAULT_PARITY;
|
||||
|
||||
if (cfg != NULL)
|
||||
{
|
||||
uart_server_cfg.baudrate = cfg->uart2_baudrate;
|
||||
uart_server_cfg.data_bits = cfg->uart2_databits;
|
||||
uart_server_cfg.stop_bits = cfg->uart2_stopbits;
|
||||
uart_server_cfg.parity = cfg->uart2_parity;
|
||||
|
||||
uart_client_cfg.baudrate = cfg->uart3_baudrate;
|
||||
uart_client_cfg.data_bits = cfg->uart3_databits;
|
||||
uart_client_cfg.stop_bits = cfg->uart3_stopbits;
|
||||
uart_client_cfg.parity = cfg->uart3_parity;
|
||||
}
|
||||
|
||||
(void)uart_trans_config(UART_CHANNEL_SERVER, &uart_server_cfg);
|
||||
(void)uart_trans_config(UART_CHANNEL_CLIENT, &uart_client_cfg);
|
||||
|
||||
/* Wait for TCP tasks to initialize their StreamBuffers */
|
||||
/* TCP Server and TCP Client tasks call tcp_server_init() / tcp_client_init() */
|
||||
/* which create the StreamBuffers. We need to wait until they're ready. */
|
||||
while (tcp_server_get_rx_stream() == NULL ||
|
||||
tcp_server_get_tx_stream() == NULL ||
|
||||
tcp_client_get_rx_stream() == NULL ||
|
||||
tcp_client_get_tx_stream() == NULL)
|
||||
{
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
|
||||
/* Connect StreamBuffers between TCP and UART modules */
|
||||
/* Server: TCP Server RX -> UART2 TX, UART2 RX -> TCP Server TX */
|
||||
uart_trans_set_streams(UART_CHANNEL_SERVER,
|
||||
tcp_server_get_rx_stream(), /* TCP RX -> UART TX */
|
||||
tcp_server_get_tx_stream()); /* UART RX -> TCP TX */
|
||||
|
||||
/* Client: TCP Client RX -> UART3 TX, UART3 RX -> TCP Client TX */
|
||||
uart_trans_set_streams(UART_CHANNEL_CLIENT,
|
||||
tcp_client_get_rx_stream(), /* TCP RX -> UART TX */
|
||||
tcp_client_get_tx_stream()); /* UART RX -> TCP TX */
|
||||
|
||||
/* Initialize LwIP stack (calls tcpip_init internally) */
|
||||
tcpip_init(NULL, NULL);
|
||||
|
||||
/* Wait for tcpip thread to initialize */
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
/* Set IP addresses */
|
||||
if (cfg != NULL && cfg->dhcp_enable != 0)
|
||||
{
|
||||
use_dhcp = 1;
|
||||
IP4_ADDR(&ipaddr, 0, 0, 0, 0);
|
||||
IP4_ADDR(&netmask, 0, 0, 0, 0);
|
||||
IP4_ADDR(&gw, 0, 0, 0, 0);
|
||||
}
|
||||
else if (cfg != NULL)
|
||||
{
|
||||
IP4_ADDR(&ipaddr, cfg->ip[0], cfg->ip[1], cfg->ip[2], cfg->ip[3]);
|
||||
IP4_ADDR(&netmask, cfg->mask[0], cfg->mask[1], cfg->mask[2], cfg->mask[3]);
|
||||
IP4_ADDR(&gw, cfg->gw[0], cfg->gw[1], cfg->gw[2], cfg->gw[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Default IP if no config */
|
||||
IP4_ADDR(&ipaddr, 192, 168, 1, 200);
|
||||
IP4_ADDR(&netmask, 255, 255, 255, 0);
|
||||
IP4_ADDR(&gw, 192, 168, 1, 1);
|
||||
}
|
||||
|
||||
/* Initialize network interface */
|
||||
lwip_netif_init(&ipaddr, &netmask, &gw);
|
||||
|
||||
if (use_dhcp)
|
||||
{
|
||||
dhcp_start(&ch390_netif);
|
||||
}
|
||||
|
||||
/* Main loop - handle network events */
|
||||
for (;;)
|
||||
{
|
||||
/* Wait for CH390 interrupt or timeout */
|
||||
if (xSemaphoreTake(ch390IntSemaphore, pdMS_TO_TICKS(10)) == pdTRUE)
|
||||
{
|
||||
/* Process network input - poll all pending packets */
|
||||
ethernetif_poll();
|
||||
}
|
||||
|
||||
/* Check link status periodically */
|
||||
ethernetif_check_link();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get SPI mutex handle for CH390 driver
|
||||
*/
|
||||
SemaphoreHandle_t get_ch390_spi_mutex(void)
|
||||
{
|
||||
return ch390SpiMutex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Notify LwIP task of CH390 interrupt (call from ISR)
|
||||
*/
|
||||
void notify_ch390_interrupt_from_isr(void)
|
||||
{
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
if (ch390IntSemaphore != NULL)
|
||||
{
|
||||
xSemaphoreGiveFromISR(ch390IntSemaphore, &xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get UART Server transparent task handle
|
||||
*/
|
||||
TaskHandle_t get_uart_server_task_handle(void)
|
||||
{
|
||||
return uartServerTransTaskHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get UART Client transparent task handle
|
||||
*/
|
||||
TaskHandle_t get_uart_client_task_handle(void)
|
||||
{
|
||||
return uartClientTransTaskHandle;
|
||||
}
|
||||
|
||||
/* USER CODE END Application */
|
||||
+1
-1
@@ -65,7 +65,7 @@ void MX_GPIO_Init(void)
|
||||
|
||||
/*Configure GPIO pin : PB0 */
|
||||
GPIO_InitStruct.Pin = GPIO_PIN_0;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
|
||||
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
|
||||
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
||||
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
||||
|
||||
|
||||
@@ -364,7 +364,9 @@ void Error_Handler(void)
|
||||
{
|
||||
/* USER CODE BEGIN Error_Handler_Debug */
|
||||
/* User can add his own implementation to report the HAL error return state */
|
||||
SEGGER_RTT_WriteString(0, "\r\nError_Handler hit\r\n");
|
||||
__disable_irq();
|
||||
__BKPT(0);
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,8 +71,6 @@ void HAL_MspInit(void)
|
||||
__HAL_RCC_PWR_CLK_ENABLE();
|
||||
|
||||
/* System interrupt init*/
|
||||
/* PendSV_IRQn interrupt configuration */
|
||||
HAL_NVIC_SetPriority(PendSV_IRQn, 15, 0);
|
||||
|
||||
/** NOJTAG: JTAG-DP Disabled and SW-DP Enabled
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file syscalls.c
|
||||
* @author Auto-generated by STM32CubeMX
|
||||
* @brief Minimal System calls file
|
||||
*
|
||||
* For more information about which c-functions
|
||||
* need which of these lowlevel functions
|
||||
* please consult the Newlib or Picolibc libc-manual
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* Copyright (c) 2020-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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes */
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
|
||||
|
||||
/* Variables */
|
||||
extern int __io_putchar(int ch) __attribute__((weak));
|
||||
extern int __io_getchar(void) __attribute__((weak));
|
||||
|
||||
|
||||
char *__env[1] = { 0 };
|
||||
char **environ = __env;
|
||||
|
||||
|
||||
/* Functions */
|
||||
void initialise_monitor_handles()
|
||||
{
|
||||
}
|
||||
|
||||
int _getpid(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _kill(int pid, int sig)
|
||||
{
|
||||
(void)pid;
|
||||
(void)sig;
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void _exit (int status)
|
||||
{
|
||||
_kill(status, -1);
|
||||
while (1) {} /* Make sure we hang here */
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _read(int file, char *ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
int DataIdx;
|
||||
|
||||
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||
{
|
||||
*ptr++ = __io_getchar();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _write(int file, char *ptr, int len)
|
||||
{
|
||||
(void)file;
|
||||
int DataIdx;
|
||||
|
||||
for (DataIdx = 0; DataIdx < len; DataIdx++)
|
||||
{
|
||||
__io_putchar(*ptr++);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int _close(int file)
|
||||
{
|
||||
(void)file;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int _fstat(int file, struct stat *st)
|
||||
{
|
||||
(void)file;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _isatty(int file)
|
||||
{
|
||||
(void)file;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _lseek(int file, int ptr, int dir)
|
||||
{
|
||||
(void)file;
|
||||
(void)ptr;
|
||||
(void)dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _open(char *path, int flags, ...)
|
||||
{
|
||||
(void)path;
|
||||
(void)flags;
|
||||
/* Pretend like we always fail */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _wait(int *status)
|
||||
{
|
||||
(void)status;
|
||||
errno = ECHILD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _unlink(char *name)
|
||||
{
|
||||
(void)name;
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
clock_t _times(struct tms *buf)
|
||||
{
|
||||
(void)buf;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _stat(const char *file, struct stat *st)
|
||||
{
|
||||
(void)file;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _link(char *old, char *new)
|
||||
{
|
||||
(void)old;
|
||||
(void)new;
|
||||
errno = EMLINK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _fork(void)
|
||||
{
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _execve(char *name, char **argv, char **env)
|
||||
{
|
||||
(void)name;
|
||||
(void)argv;
|
||||
(void)env;
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// --- Picolibc Specific Section ---
|
||||
#if defined(__PICOLIBC__)
|
||||
|
||||
/**
|
||||
* @brief Picolibc helper function to output a character to a FILE stream.
|
||||
* This redirects the output to the low-level __io_putchar function.
|
||||
* @param c Character to write.
|
||||
* @param file FILE stream pointer (ignored).
|
||||
* @retval int The character written.
|
||||
*/
|
||||
static int starm_putc(char c, FILE *file)
|
||||
{
|
||||
(void) file;
|
||||
__io_putchar(c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Picolibc helper function to input a character from a FILE stream.
|
||||
* This redirects the input from the low-level __io_getchar function.
|
||||
* @param file FILE stream pointer (ignored).
|
||||
* @retval int The character read, cast to an unsigned char then int.
|
||||
*/
|
||||
static int starm_getc(FILE *file)
|
||||
{
|
||||
unsigned char c;
|
||||
(void) file;
|
||||
c = __io_getchar();
|
||||
return c;
|
||||
}
|
||||
|
||||
// Define and initialize the standard I/O streams for Picolibc.
|
||||
// FDEV_SETUP_STREAM connects the starm_putc and starm_getc helper functions to a FILE structure.
|
||||
// _FDEV_SETUP_RW indicates the stream is for reading and writing.
|
||||
static FILE __stdio = FDEV_SETUP_STREAM(starm_putc,
|
||||
starm_getc,
|
||||
NULL,
|
||||
_FDEV_SETUP_RW);
|
||||
|
||||
// Assign the standard stream pointers (stdin, stdout, stderr) to the initialized stream.
|
||||
// Picolibc uses these pointers for standard I/O operations (printf, scanf, etc.).
|
||||
FILE *const stdin = &__stdio;
|
||||
__strong_reference(stdin, stdout);
|
||||
__strong_reference(stdin, stderr);
|
||||
|
||||
// Create strong aliases mapping standard C library function names (without underscore)
|
||||
// to the implemented system call stubs (with underscore). Picolibc uses these
|
||||
// standard names internally, so this linking is required.
|
||||
__strong_reference(_read, read);
|
||||
__strong_reference(_write, write);
|
||||
__strong_reference(_times, times);
|
||||
__strong_reference(_execve, execve);
|
||||
__strong_reference(_fork, fork);
|
||||
__strong_reference(_link, link);
|
||||
__strong_reference(_unlink, unlink);
|
||||
__strong_reference(_stat, stat);
|
||||
__strong_reference(_wait, wait);
|
||||
__strong_reference(_open, open);
|
||||
__strong_reference(_close, close);
|
||||
__strong_reference(_lseek, lseek);
|
||||
__strong_reference(_isatty, isatty);
|
||||
__strong_reference(_fstat, fstat);
|
||||
__strong_reference(_exit, exit);
|
||||
__strong_reference(_kill, kill);
|
||||
__strong_reference(_getpid, getpid);
|
||||
|
||||
#endif //__PICOLIBC__
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file sysmem.c
|
||||
* @author Generated by STM32CubeMX
|
||||
* @brief System Memory calls file
|
||||
*
|
||||
* For more information about which C functions
|
||||
* need which of these lowlevel functions
|
||||
* please consult the Newlib or Picolibc libc manual
|
||||
******************************************************************************
|
||||
* @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.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Includes */
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Pointer to the current high watermark of the heap usage
|
||||
*/
|
||||
static uint8_t *__sbrk_heap_end = NULL;
|
||||
|
||||
/**
|
||||
* @brief _sbrk() allocates memory to the newlib heap and is used by malloc
|
||||
* and others from the C library
|
||||
*
|
||||
* @verbatim
|
||||
* ############################################################################
|
||||
* # .data # .bss # newlib heap # MSP stack #
|
||||
* # # # # Reserved by _Min_Stack_Size #
|
||||
* ############################################################################
|
||||
* ^-- RAM start ^-- _end _estack, RAM end --^
|
||||
* @endverbatim
|
||||
*
|
||||
* This implementation starts allocating at the '_end' linker symbol
|
||||
* The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack
|
||||
* The implementation considers '_estack' linker symbol to be RAM end
|
||||
* NOTE: If the MSP stack, at any point during execution, grows larger than the
|
||||
* reserved size, please increase the '_Min_Stack_Size'.
|
||||
*
|
||||
* @param incr Memory size
|
||||
* @return Pointer to allocated memory
|
||||
*/
|
||||
void *_sbrk(ptrdiff_t incr)
|
||||
{
|
||||
extern uint8_t _end; /* Symbol defined in the linker script */
|
||||
extern uint8_t _estack; /* Symbol defined in the linker script */
|
||||
extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */
|
||||
const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;
|
||||
const uint8_t *max_heap = (uint8_t *)stack_limit;
|
||||
uint8_t *prev_heap_end;
|
||||
|
||||
/* Initialize heap end at first call */
|
||||
if (NULL == __sbrk_heap_end)
|
||||
{
|
||||
__sbrk_heap_end = &_end;
|
||||
}
|
||||
|
||||
/* Protect heap from growing into the reserved MSP stack */
|
||||
if (__sbrk_heap_end + incr > max_heap)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return (void *)-1;
|
||||
}
|
||||
|
||||
prev_heap_end = __sbrk_heap_end;
|
||||
__sbrk_heap_end += incr;
|
||||
|
||||
return (void *)prev_heap_end;
|
||||
}
|
||||
|
||||
#if defined(__PICOLIBC__)
|
||||
// Picolibc expects syscalls without the leading underscore.
|
||||
// This creates a strong alias so that
|
||||
// calls to `sbrk()` are resolved to our `_sbrk()` implementation.
|
||||
__strong_reference(_sbrk, sbrk);
|
||||
#endif
|
||||
Reference in New Issue
Block a user