diff --git a/.mxproject b/.mxproject index 150afdf..4d90f56 100644 --- a/.mxproject +++ b/.mxproject @@ -8,21 +8,21 @@ CDefines=USE_HAL_DRIVER;STM32F103xB;USE_HAL_DRIVER;USE_HAL_DRIVER; [PreviousUsedKeilFiles] SourceFiles=..\Core\Src\main.c;..\Core\Src\gpio.c;..\Core\Src\freertos.c;..\Core\Src\dma.c;..\Core\Src\iwdg.c;..\Core\Src\spi.c;..\Core\Src\usart.c;..\Core\Src\stm32f1xx_it.c;..\Core\Src\stm32f1xx_hal_msp.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_dma.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_cortex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_pwr.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_exti.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_iwdg.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_spi.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_uart.c;..\Middlewares\Third_Party\FreeRTOS\Source\croutine.c;..\Middlewares\Third_Party\FreeRTOS\Source\event_groups.c;..\Middlewares\Third_Party\FreeRTOS\Source\list.c;..\Middlewares\Third_Party\FreeRTOS\Source\queue.c;..\Middlewares\Third_Party\FreeRTOS\Source\stream_buffer.c;..\Middlewares\Third_Party\FreeRTOS\Source\tasks.c;..\Middlewares\Third_Party\FreeRTOS\Source\timers.c;..\Middlewares\Third_Party\FreeRTOS\Source\CMSIS_RTOS_V2\cmsis_os2.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang\heap_4.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c;..\Drivers\CMSIS\Device\ST\STM32F1xx\Source\Templates\system_stm32f1xx.c;..\Core\Src\system_stm32f1xx.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_rcc_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_gpio.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_dma.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_cortex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_pwr.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_flash_ex.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_exti.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_iwdg.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_spi.c;..\Drivers\STM32F1xx_HAL_Driver\Src\stm32f1xx_hal_uart.c;..\Middlewares\Third_Party\FreeRTOS\Source\croutine.c;..\Middlewares\Third_Party\FreeRTOS\Source\event_groups.c;..\Middlewares\Third_Party\FreeRTOS\Source\list.c;..\Middlewares\Third_Party\FreeRTOS\Source\queue.c;..\Middlewares\Third_Party\FreeRTOS\Source\stream_buffer.c;..\Middlewares\Third_Party\FreeRTOS\Source\tasks.c;..\Middlewares\Third_Party\FreeRTOS\Source\timers.c;..\Middlewares\Third_Party\FreeRTOS\Source\CMSIS_RTOS_V2\cmsis_os2.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang\heap_4.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c;..\Drivers\CMSIS\Device\ST\STM32F1xx\Source\Templates\system_stm32f1xx.c;..\Core\Src\system_stm32f1xx.c;;;..\Middlewares\Third_Party\FreeRTOS\Source\croutine.c;..\Middlewares\Third_Party\FreeRTOS\Source\event_groups.c;..\Middlewares\Third_Party\FreeRTOS\Source\list.c;..\Middlewares\Third_Party\FreeRTOS\Source\queue.c;..\Middlewares\Third_Party\FreeRTOS\Source\stream_buffer.c;..\Middlewares\Third_Party\FreeRTOS\Source\tasks.c;..\Middlewares\Third_Party\FreeRTOS\Source\timers.c;..\Middlewares\Third_Party\FreeRTOS\Source\CMSIS_RTOS_V2\cmsis_os2.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\MemMang\heap_4.c;..\Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c; -HeaderPath=..\Drivers\STM32F1xx_HAL_Driver\Inc\Legacy;..\Drivers\STM32F1xx_HAL_Driver\Inc;..\Middlewares\Third_Party\FreeRTOS\Source\include;..\Middlewares\Third_Party\FreeRTOS\Source\CMSIS_RTOS_V2;..\Middlewares\Third_Party\FreeRTOS\Source\portable\RVDS\ARM_CM3;..\Drivers\CMSIS\Device\ST\STM32F1xx\Include;..\Drivers\CMSIS\Include;..\Core\Inc; +HeaderPath=..\Drivers\STM32F1xx_HAL_Driver\Inc\Legacy;..\Drivers\STM32F1xx_HAL_Driver\Inc;..\Drivers\CMSIS\Device\ST\STM32F1xx\Include;..\Drivers\CMSIS\Include;..\Core\Inc; CDefines=USE_HAL_DRIVER;STM32F103xB;USE_HAL_DRIVER;USE_HAL_DRIVER; [PreviousGenFiles] AdvancedFolderStructure=true HeaderFileListSize=9 HeaderFiles#0=..\Core\Inc\gpio.h -HeaderFiles#1=..\Core\Inc\FreeRTOSConfig.h -HeaderFiles#2=..\Core\Inc\dma.h -HeaderFiles#3=..\Core\Inc\iwdg.h -HeaderFiles#4=..\Core\Inc\spi.h -HeaderFiles#5=..\Core\Inc\usart.h -HeaderFiles#6=..\Core\Inc\stm32f1xx_it.h -HeaderFiles#7=..\Core\Inc\stm32f1xx_hal_conf.h -HeaderFiles#8=..\Core\Inc\main.h +HeaderFiles#1=..\Core\Inc\dma.h +HeaderFiles#2=..\Core\Inc\iwdg.h +HeaderFiles#3=..\Core\Inc\spi.h +HeaderFiles#4=..\Core\Inc\usart.h +HeaderFiles#5=..\Core\Inc\stm32f1xx_it.h +HeaderFiles#6=..\Core\Inc\stm32f1xx_hal_conf.h +HeaderFiles#7=..\Core\Inc\main.h +HeaderFiles#8= HeaderFolderListSize=1 HeaderPath#0=..\Core\Inc HeaderFiles=; diff --git a/TCP2UART.ioc b/TCP2UART.ioc index c7bd422..678ee15 100644 --- a/TCP2UART.ioc +++ b/TCP2UART.ioc @@ -31,7 +31,7 @@ Dma.USART2_RX.0.Direction=DMA_PERIPH_TO_MEMORY Dma.USART2_RX.0.Instance=DMA1_Channel6 Dma.USART2_RX.0.MemDataAlignment=DMA_MDATAALIGN_BYTE Dma.USART2_RX.0.MemInc=DMA_MINC_ENABLE -Dma.USART2_RX.0.Mode=DMA_NORMAL +Dma.USART2_RX.0.Mode=DMA_CIRCULAR Dma.USART2_RX.0.PeriphDataAlignment=DMA_PDATAALIGN_BYTE Dma.USART2_RX.0.PeriphInc=DMA_PINC_DISABLE Dma.USART2_RX.0.Priority=DMA_PRIORITY_LOW @@ -49,7 +49,7 @@ Dma.USART3_RX.4.Direction=DMA_PERIPH_TO_MEMORY Dma.USART3_RX.4.Instance=DMA1_Channel3 Dma.USART3_RX.4.MemDataAlignment=DMA_MDATAALIGN_BYTE Dma.USART3_RX.4.MemInc=DMA_MINC_ENABLE -Dma.USART3_RX.4.Mode=DMA_NORMAL +Dma.USART3_RX.4.Mode=DMA_CIRCULAR Dma.USART3_RX.4.PeriphDataAlignment=DMA_PDATAALIGN_BYTE Dma.USART3_RX.4.PeriphInc=DMA_PINC_DISABLE Dma.USART3_RX.4.Priority=DMA_PRIORITY_LOW @@ -63,24 +63,21 @@ Dma.USART3_TX.5.PeriphDataAlignment=DMA_PDATAALIGN_BYTE Dma.USART3_TX.5.PeriphInc=DMA_PINC_DISABLE Dma.USART3_TX.5.Priority=DMA_PRIORITY_LOW Dma.USART3_TX.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority -FREERTOS.IPParameters=Tasks01 -FREERTOS.Tasks01=defaultTask,24,128,StartDefaultTask,Default,NULL,Dynamic,NULL,NULL File.Version=6 GPIO.groupedBy=Group By Peripherals KeepUserPlacement=false Mcu.CPN=STM32F103R8T6 Mcu.Family=STM32F1 Mcu.IP0=DMA -Mcu.IP1=FREERTOS -Mcu.IP2=IWDG -Mcu.IP3=NVIC -Mcu.IP4=RCC -Mcu.IP5=SPI1 -Mcu.IP6=SYS -Mcu.IP7=USART1 -Mcu.IP8=USART2 -Mcu.IP9=USART3 -Mcu.IPNb=10 +Mcu.IP1=IWDG +Mcu.IP2=NVIC +Mcu.IP3=RCC +Mcu.IP4=SPI1 +Mcu.IP5=SYS +Mcu.IP6=USART1 +Mcu.IP7=USART2 +Mcu.IP8=USART3 +Mcu.IPNb=9 Mcu.Name=STM32F103R(8-B)Tx Mcu.Package=LQFP64 Mcu.Pin0=PC13-TAMPER-RTC @@ -92,9 +89,8 @@ Mcu.Pin13=PA9 Mcu.Pin14=PA10 Mcu.Pin15=PA13 Mcu.Pin16=PA14 -Mcu.Pin17=VP_FREERTOS_VS_CMSIS_V2 -Mcu.Pin18=VP_IWDG_VS_IWDG -Mcu.Pin19=VP_SYS_VS_Systick +Mcu.Pin17=VP_IWDG_VS_IWDG +Mcu.Pin18=VP_SYS_VS_Systick Mcu.Pin2=PD1-OSC_OUT Mcu.Pin3=PA2 Mcu.Pin4=PA3 @@ -103,36 +99,32 @@ Mcu.Pin6=PA5 Mcu.Pin7=PA6 Mcu.Pin8=PA7 Mcu.Pin9=PB0 -Mcu.PinsNb=20 +Mcu.PinsNb=19 Mcu.ThirdPartyNb=0 Mcu.UserConstants= Mcu.UserName=STM32F103R8Tx MxCube.Version=6.16.1 MxDb.Version=DB.6.0.161 -NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false -NVIC.DMA1_Channel2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DMA1_Channel3_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DMA1_Channel4_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DMA1_Channel5_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DMA1_Channel6_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DMA1_Channel7_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false +NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.DMA1_Channel2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DMA1_Channel3_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DMA1_Channel4_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DMA1_Channel5_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DMA1_Channel6_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DMA1_Channel7_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.EXTI0_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true NVIC.ForceEnableDMAVector=true -NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false -NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false -NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false -NVIC.PendSV_IRQn=true\:15\:0\:false\:false\:false\:true\:false\:false\:false +NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4 -NVIC.SPI1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true -NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:false\:false\:false\:false\:false -NVIC.SavedPendsvIrqHandlerGenerated=true -NVIC.SavedSvcallIrqHandlerGenerated=true -NVIC.SavedSystickIrqHandlerGenerated=true -NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:true\:false\:true\:false -NVIC.USART1_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.USART2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true\:true -NVIC.USART3_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true\:true -NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false\:false +NVIC.SPI1_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true +NVIC.SysTick_IRQn=true\:15\:0\:false\:false\:true\:true\:false\:false +NVIC.USART1_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.USART2_IRQn=true\:5\:0\:false\:false\:true\:true\:false\:true +NVIC.USART3_IRQn=true\:5\:0\:false\:false\:true\:true\:true\:true +NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false PA10.Mode=Asynchronous PA10.Signal=USART1_RX PA13.Mode=Serial_Wire @@ -143,8 +135,8 @@ PA2.Mode=Asynchronous PA2.Signal=USART2_TX PA3.Mode=Asynchronous PA3.Signal=USART2_RX -PA4.Mode=NSS_Signal_Hard_Output -PA4.Signal=SPI1_NSS +PA4.Locked=true +PA4.Signal=GPIO_Output PA5.Mode=Full_Duplex_Master PA5.Signal=SPI1_SCK PA6.Mode=Full_Duplex_Master @@ -189,7 +181,7 @@ ProjectManager.FirmwarePackage=STM32Cube FW_F1 V1.8.7 ProjectManager.FreePins=false ProjectManager.FreePinsContext= ProjectManager.HalAssertFull=false -ProjectManager.HeapSize=0x1000 +ProjectManager.HeapSize=0x0 ProjectManager.KeepUserCode=true ProjectManager.LastFirmware=true ProjectManager.LibraryCopy=0 @@ -201,7 +193,7 @@ ProjectManager.ProjectFileName=TCP2UART.ioc ProjectManager.ProjectName=TCP2UART ProjectManager.ProjectStructure= ProjectManager.RegisterCallBack= -ProjectManager.StackSize=0x800 +ProjectManager.StackSize=0x400 ProjectManager.TargetToolchain=MDK-ARM V5.32 ProjectManager.ToolChainLocation= ProjectManager.UAScriptAfterPath= @@ -235,12 +227,14 @@ RCC.USBFreq_Value=72000000 RCC.VCOOutput2Freq_Value=8000000 SH.GPXTI0.0=GPIO_EXTI0 SH.GPXTI0.ConfNb=1 -SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_4 -SPI1.CalculateBaudRate=18.0 MBits/s +SPI1.BaudRatePrescaler=SPI_BAUDRATEPRESCALER_8 +SPI1.CLKPhase=SPI_PHASE_2EDGE +SPI1.CLKPolarity=SPI_POLARITY_HIGH +SPI1.CalculateBaudRate=9.0 MBits/s SPI1.Direction=SPI_DIRECTION_2LINES -SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler,VirtualNSS +SPI1.IPParameters=VirtualType,Mode,Direction,CalculateBaudRate,BaudRatePrescaler,VirtualNSS,CLKPolarity,CLKPhase SPI1.Mode=SPI_MODE_MASTER -SPI1.VirtualNSS=VM_NSSHARD +SPI1.VirtualNSS=VM_NSSSOFT SPI1.VirtualType=VM_MASTER USART1.IPParameters=VirtualMode USART1.VirtualMode=VM_ASYNC @@ -248,11 +242,8 @@ USART2.IPParameters=VirtualMode USART2.VirtualMode=VM_ASYNC USART3.IPParameters=VirtualMode USART3.VirtualMode=VM_ASYNC -VP_FREERTOS_VS_CMSIS_V2.Mode=CMSIS_V2 -VP_FREERTOS_VS_CMSIS_V2.Signal=FREERTOS_VS_CMSIS_V2 VP_IWDG_VS_IWDG.Mode=IWDG_Activate VP_IWDG_VS_IWDG.Signal=IWDG_VS_IWDG VP_SYS_VS_Systick.Mode=SysTick VP_SYS_VS_Systick.Signal=SYS_VS_Systick board=custom -rtos.0.ip=FREERTOS diff --git a/项目技术实现.md b/项目技术实现.md index fec017c..2b4ad37 100644 --- a/项目技术实现.md +++ b/项目技术实现.md @@ -1,937 +1,375 @@ -# TCP2UART 项目技术实现 +# TCP2UART 项目技术实现(裸机迁移基线) -## 一、系统架构 +## 一、目标 -``` -+-------------------+ +-------------------+ -| TCP Server | | TCP Client | -| (监听端口) | | (连接远程服务器) | -+--------+----------+ +--------+----------+ - | | - | LwIP TCP/IP Stack | - +------------------------------+ - | - +------v------+ - | CH390D | - | (SPI接口) | - +------+------+ - | - +------v------+ - | STM32 | - | F103R8T6 | - +------+------+ - | - +-------------+-------------+ - | | | - +---v---+ +---v---+ +---v---+ - | UART1 | | UART2 | | UART3 | - | 配置口 | | 透传口 | | 透传口 | - +-------+ +-------+ +-------+ -``` +当前分支 `baremetal-r8` 的目标不是一次性完成全部业务逻辑重写,而是先把工程基线切换到适合 `STM32F103R8T6` 的裸机方向,为后续继续开发提供统一入口。 -> **架构说明**:基于官方 EVT 示例代码,采用 LwIP 轻量级 TCP/IP 协议栈,而非直接操作 CH390D 的硬件 Socket。CH390D 作为以太网 MAC+PHY 芯片,通过 SPI 接口与 STM32 通信。 +本阶段已经完成或约束如下: -## 二、硬件配置 +1. MCU 目标统一为 `STM32F103R8T6 / STM32F103xB` +2. `CubeMX IOC` 中已移除 `FreeRTOS` 中间件声明 +3. 工程规划转为裸机轮询 + 中断驱动模型 +4. 暂不在本阶段重写 TCP/串口业务逻辑 +5. 保留现有源码作为迁移参考,后续由其他 Agent/开发者继续接力实现 -### 2.1 MCU 型号 +## 二、硬件与资源约束 -STM32F103R8T6(LQFP64,64KB Flash,20KB RAM) +### 2.1 MCU -### 2.2 引脚分配 +- 型号:`STM32F103R8T6` +- Flash:`64 KB` +- SRAM:`20 KB` +- 主频:`72 MHz` + +### 2.2 主要外设 + +- `SPI1`:连接 `CH390D` +- `USART1`:配置串口 +- `USART2`:Server 透传串口 +- `USART3`:Client 透传串口 +- `DMA1`:3 路 UART 收发 DMA +- `EXTI0`:CH390 中断输入 +- `IWDG`:独立看门狗 + +### 2.3 当前引脚分配 | 引脚 | 功能 | 用途 | |------|------|------| | PA2 | USART2_TX | Server 透传串口 | | PA3 | USART2_RX | Server 透传串口 | -| PA4 | SPI1_NSS | CH390D 片选(硬件自动管理) | +| PA4 | SPI1_NSS | CH390D 片选 | | PA5 | SPI1_SCK | CH390D SPI 时钟 | | PA6 | SPI1_MISO | CH390D SPI 数据输入 | | PA7 | SPI1_MOSI | CH390D SPI 数据输出 | | PA9 | USART1_TX | 配置串口 | | PA10 | USART1_RX | 配置串口 | -| PA13 | SWDIO | SWD 调试接口 | -| PA14 | SWCLK | SWD 调试接口 | -| PB0 | EXTI0 | CH390D 中断输入 | -| PB1 | GPIO_Output | CH390D 复位 | +| PB0 | EXTI0 | CH390D INT | +| PB1 | GPIO_Output | CH390D RESET | | PB10 | USART3_TX | Client 透传串口 | | PB11 | USART3_RX | Client 透传串口 | -| PC13 | GPIO_Output | 板载 LED(灌电流,系统状态指示) | +| PC13 | GPIO_Output | 状态 LED | | PD0/PD1 | HSE | 8MHz 外部晶振 | -### 2.3 DMA 通道分配 +## 三、为何从 FreeRTOS 迁移到裸机 -| DMA 通道 | 外设 | 方向 | -|----------|------|------| -| DMA1_Ch2 | USART3_TX | 内存→外设 | -| DMA1_Ch3 | USART3_RX | 外设→内存 | -| DMA1_Ch4 | USART1_TX | 内存→外设 | -| DMA1_Ch5 | USART1_RX | 外设→内存 | -| DMA1_Ch6 | USART2_RX | 外设→内存 | -| DMA1_Ch7 | USART2_TX | 内存→外设 | +`STM32F103R8T6` 的 RAM 只有 `20KB`。当前工程在引入如下组件后,链接空间明显不足: -### 2.4 中断优先级分配 +1. `FreeRTOS` 内核 +2. `CMSIS-RTOS V2` 包装层 +3. 多任务栈 +4. `lwIP + socket/netconn` OS 抽象层 +5. 多路 `StreamBuffer / Semaphore / Mutex` -基于 FreeRTOS 的 NVIC 优先级分组配置(使用 4 位抢占优先级): +此前编译已经证明: -| 外设 | 抢占优先级 | 子优先级 | 说明 | -|------|------------|----------|------| -| EXTI0(CH390D INT) | 5 | 0 | 网络数据接收,需高于 FreeRTOS 临界区 | -| DMA1_Ch2/3 (USART3) | 6 | 0 | 透传串口 DMA | -| DMA1_Ch6/7 (USART2) | 6 | 1 | 透传串口 DMA | -| DMA1_Ch4/5 (USART1) | 7 | 0 | 配置口 DMA | -| SysTick | 15 | 0 | FreeRTOS 系统节拍(configKERNEL_INTERRUPT_PRIORITY) | +1. 源码层报错可修复 +2. 统一 `R8` 型号后仍然在链接阶段失败 +3. 主要瓶颈是 `RAM + Flash` 同时偏紧 -> **注意**:FreeRTOS 要求 `configMAX_SYSCALL_INTERRUPT_PRIORITY` 设为 5(0x50),所有使用 FreeRTOS API 的 ISR 优先级数值必须 >= 5。 +因此本项目后续建议的主方向为: -### 2.5 时钟配置 +1. 去掉 `FreeRTOS` +2. 去掉 `CMSIS-RTOS V2` +3. 避免依赖 `lwIP socket/netconn` +4. 采用裸机主循环 + DMA/IDLE/EXTI 中断驱动 +5. 网络侧改为更贴近资源受限场景的实现模型 -| 参数 | 值 | -|------|-----| -| SYSCLK | 72MHz(PLL × 9) | -| HCLK | 72MHz | -| APB1 | 36MHz(分频 2) | -| APB2 | 72MHz | -| SPI1 | 18MHz(预分频 4) | +## 四、裸机架构目标 -### 2.6 内存配置 +### 4.1 总体分层 -| 参数 | 值 | -|------|-----| -| Heap | 0x1000(4KB) | -| Stack | 0x800(2KB) | - -> **注意**:STM32F103R8T6 仅有 20KB RAM,需合理分配 FreeRTOS 堆和任务栈。 - -## 三、软件架构分层 - -### 3.1 软件分层结构 - -``` -+----------------------------------------------------------+ -| Application Layer | -| (ConfigTask, ServerTransTask, ClientTransTask) | -+----------------------------------------------------------+ -| TCP/IP Stack (LwIP) | -| tcp_server / tcp_client / netif / pbuf / timeouts | -+----------------------------------------------------------+ -| Network Interface Layer | -| ethernetif (low_level_input / low_level_output) | -+----------------------------------------------------------+ -| CH390D Driver Layer | -| CH390.c (协议层) + CH390_Interface.c (硬件抽象层) | -+----------------------------------------------------------+ -| HAL/LL Layer | -| SPI + GPIO + DMA + UART | -+----------------------------------------------------------+ +```text ++--------------------------------------------------+ +| Application State Machine | +| config / server link / client link / watchdog | ++--------------------------------------------------+ +| Transport Scheduler | +| main loop polling + event flags + timeout scan | ++--------------------------------------------------+ +| Network Interface | +| CH390 event polling / packet rx-tx dispatch | ++--------------------------------------------------+ +| Peripheral Drivers | +| UART DMA+IDLE / SPI / GPIO / EXTI / Flash | ++--------------------------------------------------+ +| STM32 HAL / CMSIS | ++--------------------------------------------------+ ``` -### 3.2 目录结构 +### 4.2 执行模型 -``` -Core/ -├── Inc/ -│ ├── main.h -│ ├── FreeRTOSConfig.h -│ └── lwipopts.h # LwIP 配置 -├── Src/ -│ ├── main.c -│ ├── freertos.c # FreeRTOS 任务创建 -│ └── stm32f1xx_it.c # 中断处理 -│ -Drivers/ -├── CH390/ # CH390 驱动(从 EVT 移植) -│ ├── CH390.c # 协议层:收发包、PHY 配置等 -│ ├── CH390.h -│ ├── CH390_Interface.c # 硬件抽象层:SPI 读写 -│ └── CH390_Interface.h -│ -├── LwIP/ # LwIP 协议栈(从 EVT 移植) -│ ├── src/ -│ │ ├── core/ # TCP/IP 核心 -│ │ ├── netif/ # 网络接口 -│ │ │ ├── ethernetif.c # CH390 网卡驱动适配 -│ │ │ └── ethernetif.h -│ │ └── include/ -│ └── apps/ -│ ├── tcp_server.c # TCP Server 透传 -│ └── tcp_client.c # TCP Client 透传 -│ -App/ -├── config.c # AT 命令解析 -├── config.h -├── uart_trans.c # UART 透传管理 -├── uart_trans.h -├── flash_param.c # Flash 参数存储 -└── flash_param.h -``` +不再使用任务调度器,改为以下模型: -## 四、FreeRTOS 任务设计 +1. `ISR` 只做最小事件置位与 DMA 状态更新 +2. 主循环统一处理事件、超时、状态机推进 +3. UART RX 继续依赖 `DMA + IDLE` +4. UART TX 可保留 `DMA` +5. CH390 中断只置位 `netif_pending` +6. 网络协议处理在主循环中推进 -### 4.1 任务划分 +### 4.3 事件源 -| 任务名 | 优先级 | 功能 | 栈大小 | -|--------|--------|------|--------| -| LwIPTask | osPriorityHigh | LwIP 协议栈处理(定时器 + 网卡输入) | 512 words | -| ServerTransTask | osPriorityAboveNormal | TCP Server ↔ UART2 双向透传 | 384 words | -| ClientTransTask | osPriorityAboveNormal | TCP Client ↔ UART3 双向透传 | 384 words | -| ConfigTask | osPriorityNormal | UART1 AT 命令解析 | 256 words | - -> **说明**:相比原设计减少任务数,将网络任务合并为 LwIPTask 统一处理,透传任务专注数据搬运。 - -### 4.2 任务间通信 - -``` - +-------------+ - | LwIPTask | - | (协议栈处理) | - +------+------+ - | - +------------------+------------------+ - | | -+-------v-------+ +-------v-------+ -| tcp_server_pcb| | tcp_client_pcb| -| (LwIP PCB) | | (LwIP PCB) | -+-------+-------+ +-------+-------+ - | | -+-------v-------+ +-------v-------+ -|ServerTransTask| |ClientTransTask| -| StreamBuffer1 | | StreamBuffer2 | -+-------+-------+ +-------+-------+ - | | - +---v---+ +---v---+ - | UART2 | | UART3 | - +-------+ +-------+ -``` - -### 4.3 同步机制 - -| 机制 | 用途 | 说明 | -|------|------|------| -| xStreamBuffer | UART DMA → TCP 发送 | 零拷贝流式缓冲,适合变长数据 | -| xSemaphore | TCP 接收 → UART 发送 | 通知信号量,TCP 收到数据后释放 | -| xMutex | CH390 SPI 访问保护 | LwIP 输出 + 中断输入共享 SPI | - -### 4.4 数据流向 - -``` -Server 链路(UART2 ↔ TCP Server): - [外部TCP Client] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ServerTransTask → UART2_TX(DMA) - UART2_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [外部TCP Client] - -Client 链路(UART3 ↔ TCP Client): - [远程服务器] → CH390 → LwIPTask(tcp_recv) → StreamBuffer → ClientTransTask → UART3_TX(DMA) - UART3_RX(DMA+IDLE) → StreamBuffer → LwIPTask(tcp_write) → CH390 → [远程服务器] -``` - -## 五、CH390D 驱动层(基于官方 EVT) - -### 5.1 驱动文件结构 - -驱动代码直接移植自 `Reference/EVT/EXAM/PUB/`,需修改接口宏定义: +建议保留以下裸机事件位: ```c -/* CH390.h 中启用 SPI 接口 */ -#define CH390_INTERFACE_SPI // CH390H/CH390D 使用 SPI -// #define CH390_INTERFACE_8_BIT // CH390L/CH390F 8-bit mode -// #define CH390_INTERFACE_16_BIT // CH390L 16-bit mode +typedef enum { + EVENT_NONE = 0x00000000u, + EVENT_CH390_INT = 0x00000001u, + EVENT_UART1_RX_IDLE = 0x00000002u, + EVENT_UART2_RX_IDLE = 0x00000004u, + EVENT_UART3_RX_IDLE = 0x00000008u, + EVENT_UART2_TX_DONE = 0x00000010u, + EVENT_UART3_TX_DONE = 0x00000020u, + EVENT_NET_TIMER_1MS = 0x00000040u, + EVENT_LINK_RETRY = 0x00000080u, + EVENT_CONFIG_PENDING = 0x00000100u, +} app_event_t; ``` -### 5.2 SPI 接口层(CH390_Interface.c 适配 HAL) +这些事件位建议通过 `volatile uint32_t g_app_events` 或一个轻量原子位图维护。 + +## 五、建议的软件模块拆分 + +### 5.1 保留模块 + +以下模块可继续保留,但需要去除 RTOS 依赖: + +1. `App/config.c` +2. `App/flash_param.c` +3. `App/tcp_server.*` +4. `App/tcp_client.*` +5. `App/uart_trans.*` +6. `Drivers/CH390/*` +7. `Drivers/LwIP/*` 或后续替代网络栈 + +### 5.2 需要改造的核心模块 + +1. `Core/Src/main.c` +2. `Core/Src/stm32f1xx_it.c` +3. `Core/Src/usart.c` +4. `Core/Src/dma.c` +5. `Core/Src/freertos.c`:后续应移除出构建或清空为兼容壳 +6. `Core/Inc/FreeRTOSConfig.h`:后续可移除出工程 +7. `Drivers/LwIP/port/sys_arch.c`:若完全去 OS,应停止使用该移植层 +8. `Drivers/LwIP/src/include/arch/sys_arch.h` + +### 5.3 建议新增的裸机模块 + +建议后续新增: + +1. `App/app_scheduler.c/.h` + - 统一处理事件位 + - 驱动周期任务 + - 执行状态机推进 + +2. `App/app_net.c/.h` + - 网络初始化 + - CH390 事件分发 + - 链路保活/重连 + +3. `App/app_uart.c/.h` + - UART DMA/IDLE 收发整合 + - 与网络方向的缓冲协调 + +4. `App/app_time.c/.h` + - 基于 `SysTick` 的毫秒计时 + - 软件超时管理 + +## 六、裸机下的关键技术决策 + +### 6.1 延时与时间基准 + +建议统一使用: + +1. `SysTick 1ms` 全局时基 +2. 禁止在主业务路径使用长阻塞 `HAL_Delay` +3. 超时逻辑统一基于 `tick_now - tick_start` + +示例: ```c -/* 引脚定义适配本项目 */ -#define CH390_SPI_HANDLE hspi1 -#define CH390_CS_PORT GPIOA -#define CH390_CS_PIN GPIO_PIN_4 -#define CH390_RST_PORT GPIOB -#define CH390_RST_PIN GPIO_PIN_1 -#define CH390_INT_PORT GPIOB -#define CH390_INT_PIN GPIO_PIN_0 - -/* HAL SPI 适配 */ -static uint8_t ch390_spi_exchange_byte(uint8_t byte) -{ - uint8_t rx; - HAL_SPI_TransmitReceive(&CH390_SPI_HANDLE, &byte, &rx, 1, HAL_MAX_DELAY); - return rx; -} - -/* FreeRTOS 延时适配 */ -void ch390_delay_us(uint32_t time) -{ - /* 微秒级延时,可用 DWT 或简单循环 */ - uint32_t cycles = (SystemCoreClock / 1000000) * time; - while(cycles--) __NOP(); -} - -void ch390_delay_ms(uint32_t time) -{ - /* FreeRTOS 环境下使用 vTaskDelay */ - if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) { - vTaskDelay(pdMS_TO_TICKS(time)); - } else { - HAL_Delay(time); - } -} +uint32_t app_now_ms(void); +bool app_is_timeout(uint32_t start, uint32_t timeout_ms); ``` -### 5.3 协议层主要 API +### 6.2 UART 接收模型 + +保持 `DMA + IDLE` 是合理的,原因: + +1. 可降低 CPU 占用 +2. 适合串口透传场景 +3. 便于在裸机下维持较高吞吐 + +建议 UART RX 流程: + +1. DMA 持续接收至环形或双缓冲 +2. IDLE 中断触发“本帧结束” +3. ISR 只记录长度与事件位 +4. 主循环消费数据并决定转发方向 + +### 6.3 UART 发送模型 + +建议继续使用 `DMA TX`: + +1. 发送启动在主循环中执行 +2. DMA 完成中断只清 busy 标志并置 `TX_DONE` 事件 +3. 发送缓冲统一由应用层管理 + +### 6.4 CH390 访问模型 + +裸机下不再需要 `mutex`,但仍需保证上下文一致性: + +1. ISR 内不要执行复杂 SPI 事务 +2. EXTI 仅置位 `EVENT_CH390_INT` +3. 所有 CH390 SPI 读写都在主循环中完成 +4. 若必须与 DMA 回调共享状态,使用短临界区保护 + +### 6.5 网络栈路线 + +这里需要后续 Agent 决定最终实现路线,建议二选一: + +1. `lwIP RAW API + NO_SYS=1` + - 优点:仍保留成熟 TCP/IP 栈 + - 缺点:迁移复杂度较高,需要重写当前 `socket/netconn` 依赖 + +2. 基于现有 CH390 资料,评估是否存在更轻的直接 socket/简化协议接口 + - 优点:可能进一步减小资源占用 + - 缺点:功能边界和维护成本需重新评估 + +当前更推荐的长期路线是: + +`lwIP RAW API + NO_SYS=1` + +因为这条路线与现有 CH390 + 以太网驱动结构更连续。 + +## 七、主循环设计建议 + +建议采用固定骨架: ```c -/* 从 CH390.h 提取的关键函数 */ - -/* 初始化与配置 */ -void ch390_hardware_reset(void); // 硬件复位(拉低 RST 引脚) -void ch390_software_reset(void); // 软件复位(写 NCR 寄存器) -void ch390_default_config(void); // 默认配置:LED模式、校验和、使能RX/中断 -void ch390_set_phy_mode(enum ch390_phy_mode mode); // CH390_AUTO / CH390_100MFD / CH390_10MFD -void ch390_set_mac_address(uint8_t *mac_addr); // 设置 MAC 地址 - -/* 数据收发 */ -uint32_t ch390_receive_packet(uint8_t *buff, uint8_t *rx_status); // 接收以太网帧 -void ch390_send_packet(uint8_t *buff, uint16_t length); // 发送以太网帧 -void ch390_drop_packet(uint16_t len); // 丢弃当前包 - -/* 状态查询 */ -int ch390_get_link_status(void); // 0: 断开, 1: 连接 -int ch390_get_phy_speed(void); // 0: 100Mbps, 1: 10Mbps -uint8_t ch390_get_int_status(void); // 获取并清除中断状态 - -/* 中断配置 */ -void ch390_interrupt_config(uint8_t mask); // IMR_PRI | IMR_PTI | IMR_LNKCHGI 等 -uint16_t ch390_get_int_pin(void); // 读取 INT 引脚电平 -``` - -### 5.4 初始化流程 - -```c -void CH390_Init(void) +int main(void) { - /* 1. GPIO 初始化(由 CubeMX 完成) */ - - /* 2. SPI 初始化(由 CubeMX 完成) - * Mode: Master, Full-Duplex - * CPOL: High, CPHA: 2Edge (Mode 3) - * NSS: Software - * Prescaler: 4 (18MHz @ 72MHz PCLK) - */ - - /* 3. 硬件复位 */ - ch390_hardware_reset(); - ch390_delay_ms(10); // 上电后等待 10ms - - /* 4. 默认配置 */ - ch390_default_config(); - - /* 5. 读取内置 MAC 或设置自定义 MAC */ - uint8_t mac[6]; - ch390_get_mac(mac); // CH390 内置 MAC - // ch390_set_mac_address(custom_mac); // 或使用自定义 MAC - - /* 6. 等待 PHY 链路建立 */ - while (!ch390_get_link_status()) { - ch390_delay_ms(100); + HAL_Init(); + SystemClock_Config(); + + MX_GPIO_Init(); + MX_DMA_Init(); + MX_IWDG_Init(); + MX_USART1_UART_Init(); + MX_USART2_UART_Init(); + MX_USART3_UART_Init(); + MX_SPI1_Init(); + + app_time_init(); + app_uart_init(); + app_net_init(); + app_config_init(); + + while (1) + { + app_poll_events(); + app_process_config(); + app_process_uart_links(); + app_process_network(); + app_process_timeouts(); + app_feed_watchdog(); } } ``` -## 六、LwIP 协议栈集成 +设计原则: -### 6.1 LwIP 配置(lwipopts.h) +1. 所有步骤可重入或幂等 +2. 每轮主循环不可长时间阻塞 +3. 网络与串口处理都要支持“分段推进” -```c -/* 基础配置 */ -#define NO_SYS 0 /* 使用 OS */ -#define LWIP_NETCONN 0 /* 不使用 Netconn API */ -#define LWIP_SOCKET 0 /* 不使用 Socket API */ +## 八、建议的状态机拆分 -/* 内存配置(适配 20KB RAM) */ -#define MEM_SIZE (4*1024) /* 堆大小 4KB */ -#define MEMP_NUM_PBUF 8 -#define MEMP_NUM_TCP_PCB 4 /* 同时 TCP 连接数 */ -#define MEMP_NUM_TCP_PCB_LISTEN 2 /* Server + Client */ -#define PBUF_POOL_SIZE 8 -#define PBUF_POOL_BUFSIZE 512 +### 8.1 TCP Server 链路 -/* TCP 配置 */ -#define LWIP_TCP 1 -#define TCP_MSS (1460) -#define TCP_SND_BUF (2*TCP_MSS) -#define TCP_WND (2*TCP_MSS) -#define TCP_SND_QUEUELEN (4*TCP_SND_BUF/TCP_MSS) - -/* DHCP/静态 IP */ -#define LWIP_DHCP 1 - -/* 校验和由 CH390 硬件计算 */ -#define CHECKSUM_GEN_IP 0 -#define CHECKSUM_GEN_TCP 0 -#define CHECKSUM_GEN_UDP 0 -#define CHECKSUM_CHECK_IP 0 -#define CHECKSUM_CHECK_TCP 0 -#define CHECKSUM_CHECK_UDP 0 +```text +IDLE -> LISTEN -> CONNECTED -> CLOSING -> LISTEN ``` -### 6.2 网卡驱动适配(ethernetif.c) +### 8.2 TCP Client 链路 -基于 `Reference/EVT/EXAM/LwIP_Example/lwip-2_2_0/src/netif/ethernetif.c` 修改: - -```c -/* 发送以太网帧 */ -static err_t low_level_output(struct netif *netif, struct pbuf *p) -{ - struct pbuf *q; - - /* 获取 SPI 互斥锁(FreeRTOS 环境) */ - xSemaphoreTake(ch390_spi_mutex, portMAX_DELAY); - - for (q = p; q != NULL; q = q->next) { - ch390_write_mem(q->payload, q->len); - } - - /* 等待上次发送完成 */ - while(ch390_read_reg(CH390_TCR) & TCR_TXREQ); - - /* 设置包长度并发起发送请求 */ - ch390_write_reg(CH390_TXPLL, p->tot_len & 0xff); - ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xff); - ch390_send_request(); - - xSemaphoreGive(ch390_spi_mutex); - - LINK_STATS_INC(link.xmit); - return ERR_OK; -} - -/* 接收以太网帧 */ -static struct pbuf *low_level_input(struct netif *netif) -{ - struct ethernetif *ethernetif = netif->state; - struct pbuf *p, *q; - uint8_t rx_ready; - uint8_t header[4]; - uint16_t len; - - /* 检查是否有数据包 */ - ch390_read_reg(CH390_MRCMDX); - rx_ready = ch390_read_reg(CH390_MRCMDX); - - if (rx_ready & CH390_PKT_ERR) { - /* 复位 RX FIFO */ - ch390_write_reg(CH390_RCR, 0); - ch390_write_reg(CH390_MPTRCR, 0x01); - ch390_delay_ms(1); - ch390_write_reg(CH390_RCR, RCR_RXEN | RCR_DIS_CRC); - ethernetif->rx_len = 0; - return NULL; - } - - if (!(rx_ready & CH390_PKT_RDY)) { - ethernetif->rx_len = 0; - return NULL; - } - - /* 读取头部:状态 + 长度 */ - ch390_read_mem(header, 4); - ethernetif->rx_status = header[1]; - len = (header[2] | (header[3] << 8)) - 4; // 去掉 CRC - ethernetif->rx_len = len; - - /* 分配 pbuf 并读取数据 */ - p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); - if (p != NULL) { - for (q = p; q != NULL; q = q->next) { - ch390_read_mem(q->payload, q->len); - } - ch390_drop_packet(4); // 跳过 CRC - LINK_STATS_INC(link.recv); - } else { - ch390_drop_packet(len + 4); - LINK_STATS_INC(link.memerr); - } - - return p; -} +```text +IDLE -> RESOLVE/CONFIG -> CONNECTING -> CONNECTED -> RETRY_WAIT -> CONNECTING ``` -### 6.3 中断处理 +### 8.3 配置口 -```c -/* EXTI0 中断服务程序(PB0 连接 CH390D INT) */ -void EXTI0_IRQHandler(void) -{ - if (__HAL_GPIO_EXTI_GET_IT(CH390_INT_PIN) != RESET) { - __HAL_GPIO_EXTI_CLEAR_IT(CH390_INT_PIN); - - /* 通知 LwIP 任务处理 */ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(lwipTaskHandle, &xHigherPriorityTaskWoken); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } -} - -/* LwIP 任务中的中断处理 */ -void ch390_int_handler(void) -{ - uint8_t int_status = ch390_get_int_status(); - - /* 链路状态变化 */ - if (int_status & ISR_LNKCHG) { - vTaskDelay(pdMS_TO_TICKS(65)); // 等待 PHY 稳定 - if (ch390_get_link_status()) { - netif_set_link_up(&ch390_netif); - } else { - netif_set_link_down(&ch390_netif); - } - } - - /* 接收溢出 */ - if (int_status & ISR_ROS) { - struct ethernetif *ethernetif = ch390_netif.state; - do { - ethernetif_input(&ch390_netif); - } while (ethernetif->rx_len != 0); - } - - /* 收到数据包 */ - if (int_status & ISR_PR) { - struct ethernetif *ethernetif = ch390_netif.state; - do { - ethernetif_input(&ch390_netif); - } while (ethernetif->rx_len != 0); - } -} +```text +IDLE -> RX_FRAME_READY -> PARSE -> EXECUTE -> RESPOND -> IDLE ``` -## 七、TCP 链路管理(基于 LwIP RAW API) +### 8.4 CH390 网口 -### 7.1 Server 链路(监听端口) - -```c -static struct tcp_pcb *tcp_server_pcb; -static struct tcp_pcb *tcp_server_conn; // 当前连接的客户端 - -/* 接收回调:TCP 数据 → StreamBuffer → UART2 */ -static err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) -{ - if (p != NULL) { - tcp_recved(tpcb, p->tot_len); - - /* 写入 StreamBuffer,通知 ServerTransTask */ - xStreamBufferSend(uart2_tx_stream, p->payload, p->tot_len, 0); - - pbuf_free(p); - return ERR_OK; - } else { - /* 对端关闭连接 */ - tcp_close(tpcb); - tcp_server_conn = NULL; - return ERR_OK; - } -} - -/* 连接接受回调 */ -static err_t tcp_server_accept(void *arg, struct tcp_pcb *newpcb, err_t err) -{ - /* 只接受一个连接 */ - if (tcp_server_conn != NULL) { - tcp_abort(newpcb); - return ERR_ABRT; - } - - tcp_server_conn = newpcb; - tcp_recv(newpcb, tcp_server_recv); - tcp_err(newpcb, tcp_server_error); - return ERR_OK; -} - -/* 初始化 TCP Server */ -void tcp_server_init(uint16_t port) -{ - tcp_server_pcb = tcp_new(); - tcp_bind(tcp_server_pcb, IP_ADDR_ANY, port); - tcp_server_pcb = tcp_listen(tcp_server_pcb); - tcp_accept(tcp_server_pcb, tcp_server_accept); -} - -/* UART2 数据发送到 TCP */ -void tcp_server_send_data(uint8_t *data, uint16_t len) -{ - if (tcp_server_conn != NULL && tcp_sndbuf(tcp_server_conn) >= len) { - tcp_write(tcp_server_conn, data, len, TCP_WRITE_FLAG_COPY); - tcp_output(tcp_server_conn); - } -} +```text +RESET -> INIT -> LINK_CHECK -> RUNNING -> ERROR_RECOVER -> INIT ``` -### 7.2 Client 链路(主动连接) +## 九、内存预算建议 -```c -static struct tcp_pcb *tcp_client_pcb; -static uint8_t client_connected = 0; +以 `STM32F103R8T6` 为目标,后续实现应尽量遵循: -/* 接收回调:TCP 数据 → StreamBuffer → UART3 */ -static err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) -{ - if (p != NULL) { - tcp_recved(tpcb, p->tot_len); - xStreamBufferSend(uart3_tx_stream, p->payload, p->tot_len, 0); - pbuf_free(p); - } else if (err == ERR_OK) { - /* 服务器断开,延时重连 */ - tcp_close(tpcb); - client_connected = 0; - sys_timeout(3000, tcp_client_reconnect, NULL); - } - return ERR_OK; -} +### 9.1 推荐 RAM 预算 -/* 连接错误回调 */ -static void tcp_client_error(void *arg, err_t err) -{ - client_connected = 0; - sys_timeout(3000, tcp_client_reconnect, NULL); -} +| 项目 | 建议值 | 说明 | +|------|--------|------| +| 启动栈 | 1 KB | `startup` 保留 | +| C Heap | 0 | 默认关闭 | +| UART1 RX/TX | 384 B | 配置口 | +| UART2 RX/TX | 1 KB | 透传链路 | +| UART3 RX/TX | 1 KB | 透传链路 | +| 网络收发缓存 | 2-4 KB | 视协议栈路线而定 | +| 参数/状态结构 | < 2 KB | 控制状态 | -/* 连接成功回调 */ -static err_t tcp_client_connected(void *arg, struct tcp_pcb *pcb, err_t err) -{ - client_connected = 1; - tcp_recv(pcb, tcp_client_recv); - return ERR_OK; -} +### 9.2 原则 -/* 初始化 TCP Client */ -void tcp_client_init(ip_addr_t *server_ip, uint16_t server_port) -{ - tcp_client_pcb = tcp_new(); - tcp_err(tcp_client_pcb, tcp_client_error); - tcp_connect(tcp_client_pcb, server_ip, server_port, tcp_client_connected); -} +1. 避免动态分配 +2. 优先静态缓冲 + 明确大小 +3. 单向链路缓冲优先复用 +4. 所有大缓冲区要在文档中登记 -/* 重连定时器回调 */ -static void tcp_client_reconnect(void *arg) -{ - if (!client_connected) { - tcp_client_init(&config.remote_ip, config.remote_port); - } -} -``` +## 十、当前已完成的工程侧修改 -## 八、串口透传层 +本分支当前已经完成: -### 8.1 UART DMA + 空闲中断配置 +1. `R8/xB` 型号统一 +2. MDK 启动文件切换到 `startup_stm32f103xb.s` +3. `IOC` 中移除 `FREERTOS` 中间件声明 +4. `PendSV/SVCall` 不再作为 RTOS 中断入口保留 +5. `Heap/Stack` 的工程默认值收缩到更适合 `R8` -```c -/* CubeMX 配置: - * USART2/3: 115200, 8N1, DMA TX/RX, 空闲中断 - */ +## 十一、后续 Agent 接手时应优先处理的事项 -#define UART_RX_BUF_SIZE 256 +### 11.1 工程配置层 -uint8_t uart2_rx_buf[UART_RX_BUF_SIZE]; -uint8_t uart3_rx_buf[UART_RX_BUF_SIZE]; +1. 从 `MDK-ARM/TCP2UART.uvprojx` 中移除 `FreeRTOS` 源文件组 +2. 从包含路径中移除 `CMSIS_RTOS_V2` 和 `FreeRTOS` 路径 +3. 视需要移除 `Core/Src/freertos.c` +4. 视需要移除 `Core/Inc/FreeRTOSConfig.h` -void UART_Trans_Init(void) -{ - /* 启动 DMA 接收 */ - HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx_buf, UART_RX_BUF_SIZE); - HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx_buf, UART_RX_BUF_SIZE); - - /* 禁用半传输中断(减少中断次数) */ - __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); - __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT); -} -``` +### 11.2 代码层 -### 8.2 空闲中断回调(接收完成) +1. 将所有 `osThreadNew`、`vTaskDelay`、`xStreamBuffer*`、`xSemaphore*`、`xMutex*` 调用替换为裸机实现 +2. 将 `lwIP` 从 `NO_SYS=0` 路线迁移到裸机可用路线 +3. 重写 `CH390` 事件处理为主循环驱动 +4. 重写 UART 透传调度逻辑为状态机 -```c -/* HAL 空闲中断回调 */ -void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) -{ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - if (huart == &huart2) { - /* UART2 数据 → Server StreamBuffer */ - xStreamBufferSendFromISR(server_rx_stream, uart2_rx_buf, Size, &xHigherPriorityTaskWoken); - /* 重启 DMA 接收 */ - HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx_buf, UART_RX_BUF_SIZE); - __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); - } - else if (huart == &huart3) { - /* UART3 数据 → Client StreamBuffer */ - xStreamBufferSendFromISR(client_rx_stream, uart3_rx_buf, Size, &xHigherPriorityTaskWoken); - HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx_buf, UART_RX_BUF_SIZE); - __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT); - } - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} -``` +### 11.3 风险点 -### 8.3 透传任务 +1. 当前 `socket/netconn` 方案不能直接脱离 OS 使用 +2. `sys_arch` 相关移植层最终应被清退 +3. 原来依赖任务切换“自然解耦”的路径,迁移到裸机后必须明确状态和时序边界 -```c -/* Server 透传任务:UART2 ↔ TCP Server */ -void ServerTransTask(void *param) -{ - uint8_t buf[256]; - size_t len; - - for (;;) { - /* TCP → UART2:从 StreamBuffer 读取并 DMA 发送 */ - len = xStreamBufferReceive(uart2_tx_stream, buf, sizeof(buf), pdMS_TO_TICKS(10)); - if (len > 0) { - HAL_UART_Transmit_DMA(&huart2, buf, len); - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // 等待 DMA 完成 - } - - /* UART2 → TCP:从 StreamBuffer 读取并 TCP 发送 */ - len = xStreamBufferReceive(server_rx_stream, buf, sizeof(buf), 0); - if (len > 0) { - tcp_server_send_data(buf, len); - } - } -} +## 十二、交接说明 -/* DMA 发送完成回调 */ -void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) -{ - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - if (huart == &huart2) { - vTaskNotifyGiveFromISR(serverTransTaskHandle, &xHigherPriorityTaskWoken); - } - else if (huart == &huart3) { - vTaskNotifyGiveFromISR(clientTransTaskHandle, &xHigherPriorityTaskWoken); - } - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} -``` +当前分支适合作为“裸机迁移起点”,但不是最终可编译成品。它的价值在于: -## 九、LwIP 主任务 +1. 目标器件与工程元数据已经统一 +2. `CubeMX` 方向已经从 `FreeRTOS` 转向裸机 +3. 技术实现文档已明确后续重构路线 -```c -/* LwIP 任务:协议栈处理 + 定时器 */ -void LwIPTask(void *param) -{ - /* 初始化网卡 */ - ip4_addr_t ipaddr, netmask, gateway; - IP4_ADDR(&ipaddr, config.ip[0], config.ip[1], config.ip[2], config.ip[3]); - IP4_ADDR(&netmask, config.mask[0], config.mask[1], config.mask[2], config.mask[3]); - IP4_ADDR(&gateway, config.gw[0], config.gw[1], config.gw[2], config.gw[3]); - - init_lwip_netif(&ipaddr, &netmask, &gateway); - - /* 等待 PHY 链路建立 */ - while (!ch390_get_link_status()) { - vTaskDelay(pdMS_TO_TICKS(100)); - } - netif_set_link_up(&ch390_netif); - - /* 初始化 TCP Server/Client */ - tcp_server_init(config.server_port); - - ip_addr_t remote_ip; - IP4_ADDR(&remote_ip, config.remote_ip[0], config.remote_ip[1], - config.remote_ip[2], config.remote_ip[3]); - tcp_client_init(&remote_ip, config.remote_port); - - /* 主循环 */ - for (;;) { - /* 等待中断通知或超时 */ - uint32_t notify = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10)); - - if (notify > 0 || ch390_get_int_pin()) { - xSemaphoreTake(ch390_spi_mutex, portMAX_DELAY); - ch390_int_handler(); - xSemaphoreGive(ch390_spi_mutex); - } - - /* LwIP 定时器处理 */ - sys_check_timeouts(); - } -} -``` - -## 十、参数配置 - -### 7.1 配置命令格式(UART1) - -``` -AT+IP=192.168.1.100\r\n // 设置设备IP -AT+MASK=255.255.255.0\r\n // 设置子网掩码 -AT+GW=192.168.1.1\r\n // 设置网关 -AT+PORT=8080\r\n // 设置Server监听端口 -AT+RIP=192.168.1.200\r\n // 设置Client连接的远程IP -AT+RPORT=9000\r\n // 设置Client连接的远程端口 -AT+BAUD1=115200\r\n // 设置UART2波特率 -AT+BAUD2=115200\r\n // 设置UART3波特率 -AT+SAVE\r\n // 保存参数到Flash -AT+RESET\r\n // 重启设备 -AT+?\r\n // 查询当前配置 -``` - -### 7.2 参数存储结构 - -```c -typedef struct { - uint32_t magic; // 0x54435055 "TCPU" - uint8_t ip[4]; - uint8_t mask[4]; - uint8_t gw[4]; - uint16_t server_port; // Server 监听端口 - uint8_t remote_ip[4]; // Client 远程IP - uint16_t remote_port; // Client 远程端口 - uint32_t uart2_baud; // UART2 波特率 - uint32_t uart3_baud; // UART3 波特率 - uint32_t crc; // CRC32校验 -} ConfigTypeDef; -``` - -### 7.3 Flash 存储 - -```c -/* 使用 STM32F103R8 内部 Flash 最后一页存储配置 */ -#define CONFIG_FLASH_ADDR 0x0800FC00 // 64KB Flash最后1KB - -void Config_Save(ConfigTypeDef *cfg) { - cfg->crc = CRC32_Calculate((uint8_t*)cfg, sizeof(ConfigTypeDef)-4); - FLASH_ErasePage(CONFIG_FLASH_ADDR); - FLASH_Program(CONFIG_FLASH_ADDR, cfg, sizeof(ConfigTypeDef)); -} - -void Config_Load(ConfigTypeDef *cfg) { - memcpy(cfg, (void*)CONFIG_FLASH_ADDR, sizeof(ConfigTypeDef)); - if(cfg->magic != 0x54435055 || CRC32_Check(cfg) != HAL_OK) { - Config_Default(cfg); // 加载默认配置 - } -} -``` - -## 十一、关键配置 - -### 11.1 FreeRTOS 配置(FreeRTOSConfig.h) - -```c -/* 基础配置 */ -#define configUSE_PREEMPTION 1 -#define configCPU_CLOCK_HZ 72000000 -#define configTICK_RATE_HZ 1000 -#define configMAX_PRIORITIES 7 -#define configMINIMAL_STACK_SIZE 128 -#define configTOTAL_HEAP_SIZE (8*1024) /* 8KB,LwIP 需要更多堆 */ - -/* 中断优先级配置 */ -#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 -#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 -#define configKERNEL_INTERRUPT_PRIORITY (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << 4) -#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << 4) - -/* 功能启用 */ -#define configUSE_MUTEXES 1 -#define configUSE_COUNTING_SEMAPHORES 1 -#define configUSE_TASK_NOTIFICATIONS 1 -#define configSUPPORT_STATIC_ALLOCATION 0 -#define configSUPPORT_DYNAMIC_ALLOCATION 1 - -/* Stream Buffer 支持 */ -#define configUSE_STREAM_BUFFERS 1 -``` - -### 11.2 SPI 配置(CubeMX) - -| 参数 | 值 | 说明 | -|------|-----|------| -| Mode | Full-Duplex Master | | -| NSS | Software | 软件控制 CS | -| Data Size | 8 Bits | | -| First Bit | MSB First | | -| Prescaler | 4 | 72MHz/4 = 18MHz | -| CPOL | High | 空闲时 CLK 高电平 | -| CPHA | 2 Edge | 第二个边沿采样 | -| CRC | Disabled | | - -### 11.3 缓冲区配置 - -```c -/* 串口缓冲区 */ -#define UART_RX_BUF_SIZE 256 /* DMA 接收缓冲区 */ - -/* StreamBuffer 配置 */ -#define STREAM_BUFFER_SIZE 512 /* 每个方向的流缓冲区大小 */ -#define STREAM_TRIGGER_LEVEL 1 /* 触发等待任务的最小字节数 */ - -/* LwIP 缓冲区(见 lwipopts.h) */ -#define PBUF_POOL_SIZE 8 -#define PBUF_POOL_BUFSIZE 512 -``` - -### 11.4 内存使用估算(20KB RAM) - -| 组件 | 大小 | 说明 | -|------|------|------| -| FreeRTOS Heap | 8KB | 任务栈 + 队列 + 信号量等 | -| LwIP MEM_SIZE | 4KB | pbuf/TCP 缓冲区 | -| UART DMA 缓冲区 | 512B | 2 × 256B | -| StreamBuffer | 2KB | 4 × 512B | -| 全局变量 | ~1KB | 配置结构 + 状态变量 | -| 栈(main) | 2KB | 启动前使用 | -| **总计** | ~18KB | 预留 2KB 安全余量 | - -> **优化建议**: -> - 如内存紧张,可减小 `PBUF_POOL_SIZE` 和 `StreamBuffer` 大小 -> - 使用 `configSUPPORT_STATIC_ALLOCATION` 静态分配任务栈 - -## 十二、丢包测试方案 - -### 9.1 测试方法 - -1. **TCP 发送端**:连续发送递增序号数据包(10000包 × 100字节) -2. **串口接收端**:统计接收数据包数量及序号连续性 -3. **反向测试**:串口发送 → TCP 接收 - -### 9.2 测试指标 - -| 指标 | 要求 | -|------|------| -| 丢包率 | < 0.01% | -| 延迟 | < 10ms | -| 吞吐量 | > 90% 理论值 | - -### 9.3 测试脚本(PC端) - -```python -import socket -import time - -def send_test(host, port, count=10000): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.connect((host, port)) - - for i in range(count): - data = f"PKT{i:06d}".encode() + b'\x00' * 92 # 100字节 - sock.send(data) - time.sleep(0.001) # 1ms间隔 - - sock.close() - print(f"Sent {count} packets") - -def recv_test(port, timeout=30): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.bind(('0.0.0.0', port)) - sock.listen(1) - conn, addr = sock.accept() - - recv_count = 0 - start = time.time() - while time.time() - start < timeout: - data = conn.recv(1024) - if data: - recv_count += len(data) // 100 - - conn.close() - print(f"Received {recv_count} packets") -``` +接下来的工作重点应由后续 Agent 在此基线上继续完成逻辑代码迁移。