1.先从[https://www.oryx-embedded.com/download/](https://)官网下载最新版本的CycloneTCP网络协议栈,这里可以选用Open Source Version,反正也只是做评估测试使用;

2.下载完毕后,发现demo里有之前apm32f407的移植示例,这里在demo/geehy文件夹下,新建一个apm32f427zg_tiny_board文件夹,从apm32f407ig_tiny_board中拷贝一个ftp工程进来,然后开始替换极海F4标准库版本;

3.之前的F407移植使用的是较旧的库版本,这里替换成最新的SDK V1.5.0,然后替换MDK工程中的 system_apm32f4xx.c 、startup_apm32f427xx.s,以及../third_party/geehy/devices下的文件,标准库同步替换;



4.这里我们计划实现一个ftp客户端的自动文件下载功能,即在PC端搭建一个FTP server,然后将开发板与PC通过网线进行连接,配置开发板为静态IP(192.168.10.100),PC端IP为(192.168.10.200),开发板会每间隔30秒自动从FTP Server上下载一个约1.8M的文件,然后通过串口进行下载过程的打印,以此来评估开发板网络外设的性能及可靠性;
5.移植说明,上面描述了替换文件,接着打开apm32f4xx_eth_driver.c,找到apm32f4xxEthInitGpio函数,将网络外设使用的IO进行替换,见下文:
`__weak_func void apm32f4xxEthInitGpio(NetInterface *interface)
{
//APM32F427ZG Tiny Board?
GPIO_Config_T gpioConfig;
//Enable SYSCFG clock
RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
//Enable GPIO clocks
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOC);
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOG);
//Select RMII interface mode
SYSCFG_ConfigMediaInterface(SYSCFG_INTERFACE_RMII);
//Configure RMII pins
gpioConfig.mode = GPIO_MODE_AF;
gpioConfig.otype = GPIO_OTYPE_PP;
gpioConfig.pupd = GPIO_PUPD_NOPULL;
gpioConfig.speed = GPIO_SPEED_100MHz;
//Configure ETH_RMII_REF_CLK (PA1), ETH_MDIO (PA2) and ETH_RMII_CRS_DV (PA7)
gpioConfig.pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
GPIO_Config(GPIOA, &gpioConfig);
//Configure ETH_MDC (PC1), ETH_RMII_RXD0 (PC4) and ETH_RMII_RXD1 (PC5)
gpioConfig.pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
GPIO_Config(GPIOC, &gpioConfig);
//Configure ETH_RMII_TX_EN (PG11), ETH_RMII_TXD0 (PG13) and
//ETH_RMII_TXD1 (PG14)
gpioConfig.pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
GPIO_Config(GPIOG, &gpioConfig);
//Remap Ethernet pins
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_1, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_2, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_7, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_1, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_4, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_5, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_11, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_13, GPIO_AF_ETH);
GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_14, GPIO_AF_ETH);
}`
6.修改main.c
`//Ethernet interface configuration
#define APP_IF_NAME “eth0”
#define APP_HOST_NAME “ftp-client-demo”
#define APP_MAC_ADDR “00-AB-CD-EF-04-27”
#define APP_USE_DHCP_CLIENT DISABLED
#define APP_IPV4_HOST_ADDR “192.168.10.100”
#define APP_IPV4_SUBNET_MASK “255.255.255.0”
#define APP_IPV4_DEFAULT_GATEWAY “192.168.10.1”
#define APP_IPV4_PRIMARY_DNS “8.8.8.8”
#define APP_IPV4_SECONDARY_DNS “8.8.4.4”
#define APP_USE_SLAAC DISABLED
#define APP_IPV6_LINK_LOCAL_ADDR “fe80::427”
#define APP_IPV6_PREFIX “2001:db8::”
#define APP_IPV6_PREFIX_LENGTH 64
#define APP_IPV6_GLOBAL_ADDR “2001:db8::427”
#define APP_IPV6_ROUTER “fe80::1”
#define APP_IPV6_PRIMARY_DNS “2001:4860:4860::8888”
#define APP_IPV6_SECONDARY_DNS “2001:4860:4860::8844”
//Application configuration
#define APP_FTP_SERVER_NAME “192.168.10.200”
#define APP_FTP_SERVER_PORT 21
#define APP_FTP_LOGIN “admin”
#define APP_FTP_PASSWORD “password”
#define APP_FTP_FILENAME “ASW.vbf”//“CycloneTCP.txt”
//Global variables
FtpClientContext ftpClientContext;
/**
- @brief I/O initialization
**/
void ioInit(void)
{
GPIO_Config_T gpioConfig;
//Enable GPIO clocks
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOF);
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOD);
//Configure LED2
gpioConfig.pin = GPIO_PIN_0;
gpioConfig.mode = GPIO_MODE_OUT;
gpioConfig.otype = GPIO_OTYPE_PP;
gpioConfig.pupd = GPIO_PUPD_NOPULL;
gpioConfig.speed = GPIO_SPEED_2MHz;
GPIO_Config(GPIOF, &gpioConfig);
//Configure LED3
gpioConfig.pin = GPIO_PIN_1;
gpioConfig.mode = GPIO_MODE_OUT;
gpioConfig.otype = GPIO_OTYPE_PP;
gpioConfig.pupd = GPIO_PUPD_NOPULL;
gpioConfig.speed = GPIO_SPEED_2MHz;
GPIO_Config(GPIOF, &gpioConfig);
//Clear LEDs
GPIO_SetBit(GPIOF, GPIO_PIN_0);
GPIO_SetBit(GPIOF, GPIO_PIN_1);
//Configure KEY1 button
gpioConfig.pin = GPIO_PIN_9;
gpioConfig.mode = GPIO_MODE_IN;
gpioConfig.pupd = GPIO_PUPD_UP;
gpioConfig.speed = GPIO_SPEED_2MHz;
GPIO_Config(GPIOD, &gpioConfig);
//Configure KEY2 button
gpioConfig.pin = GPIO_PIN_10;
gpioConfig.mode = GPIO_MODE_IN;
gpioConfig.pupd = GPIO_PUPD_UP;
gpioConfig.speed = GPIO_SPEED_2MHz;
GPIO_Config(GPIOD, &gpioConfig);
}
/**
- @brief FTP client test routine
- @return Error code
**/
error_t ftpClientTest(void)
{
error_t error;
size_t n;
IpAddr ipAddr;
char_t buffer[1024+1];
uint32_t file_sz = 0;
static uint32_t ftp_download_times = 0;
//Initialize FTP client context
ftpClientInit(&ftpClientContext);
//Start of exception handling block
do
{
//Debug message
TRACE_INFO("\r\n\r\nResolving server name...\r\n");
//Resolve FTP server name
error = getHostByName(NULL, APP_FTP_SERVER_NAME, &ipAddr, 0);
//Any error to report?
if(error)
{
//Debug message
TRACE_INFO("Failed to resolve server name!\r\n");
break;
}
//Set timeout value for blocking operations
error = ftpClientSetTimeout(&ftpClientContext, 20000);
//Any error to report?
if(error)
break;
//Debug message
TRACE_INFO("Connecting to FTP server %s...\r\n",
ipAddrToString(&ipAddr, NULL));
//Connect to the FTP server
error = ftpClientConnect(&ftpClientContext, &ipAddr, APP_FTP_SERVER_PORT,
FTP_MODE_PLAINTEXT | FTP_MODE_PASSIVE);
//Any error to report?
if(error)
{
//Debug message
TRACE_INFO("Failed to connect to FTP server!\r\n");
break;
}
//Login to the FTP server using the provided username and password
error = ftpClientLogin(&ftpClientContext, APP_FTP_LOGIN, APP_FTP_PASSWORD);
//Any error to report?
if(error)
break;
//Open the specified file for reading
error = ftpClientOpenFile(&ftpClientContext, APP_FTP_FILENAME,
FTP_FILE_MODE_READ | FTP_FILE_MODE_BINARY /*FTP_FILE_MODE_TEXT*/);
//Any error to report?
if(error)
break;
//Read the contents of the file
while(!error)
{
//Read data
error = ftpClientReadFile(&ftpClientContext, buffer, sizeof(buffer) - 1, &n, 0);
//Check status code
if(!error)
{
#if 0
//Properly terminate the string with a NULL character
buffer[n] = '\0';
//Dump the contents of the file
TRACE_INFO("%s", buffer);
#else
file_sz += n;
TRACE_INFO("current file size:%u\r\n", file_sz);
osDelayTask(2);
#endif
//toggle LED3
GPIO_ToggleBit(GPIOF, GPIO_PIN_1);
}
}
//Terminate the string with a line feed
TRACE_INFO("\r\n");
//Any error to report?
if(error != ERROR_END_OF_STREAM)
break;
file_sz = 0;
ftp_download_times++;
TRACE_INFO("FTP Download Times:%d\r\n", ftp_download_times);
GPIO_SetBit(GPIOF, GPIO_PIN_1);
//Close file
error = ftpClientCloseFile(&ftpClientContext);
//Any error to report?
if(error)
break;
//Gracefully disconnect from the FTP server
ftpClientDisconnect(&ftpClientContext);
//Debug message
TRACE_INFO("Connection closed\r\n");
//End of exception handling block
} while(0);
//Release FTP client context
ftpClientDeinit(&ftpClientContext);
//Return status code
return error;
}
/**
- @brief User task
- @param[in] param Unused parameter
**/
void userTask(void *param)
{
#define FTP_TEST_PERIOD 30000
uint32_t ftp_tick_count = 0;
NetInterface *interface;
interface = &netInterface[0];
//Endless loop
while(1)
{
if (((xTaskGetTickCount() - ftp_tick_count) >= FTP_TEST_PERIOD) && (netGetLinkState(interface)))
{
//FTP client test routine
ftpClientTest();
ftp_tick_count = xTaskGetTickCount();
}
//Loop delay
osDelayTask(100);
}
}
/**
- @brief LED task
- @param[in] param Unused parameter
**/
void ledTask(void *param)
{
//Endless loop
while(1)
{
GPIO_ResetBit(GPIOF, GPIO_PIN_0);
osDelayTask(100);
GPIO_SetBit(GPIOF, GPIO_PIN_0);
osDelayTask(900);
}
}
/**
- @brief Main entry point
- @return Unused value
**/
int_t main(void)
{
error_t error;
OsTaskId taskId;
OsTaskParameters taskParams;
NetInterface *interface;
MacAddr macAddr;
#if (APP_USE_DHCP_CLIENT == DISABLED)
Ipv4Addr ipv4Addr;
#endif
#if (APP_USE_SLAAC == DISABLED)
Ipv6Addr ipv6Addr;
#endif
// 必须重新调用该函数更新系统主频,否则系统主频SystemCoreClock会恢复到默认16M,导致RTOS调度时间异常 2026-01-11
SystemInit();
//Initialize kernel
osInitKernel();
//Configure debug UART
debugInit(115200);
//Start-up message
TRACE_INFO(“\r\n”);
TRACE_INFO(“*******************************\r\n”);
TRACE_INFO(“ CycloneTCP FTP Client Demo \r\n”);
TRACE_INFO(“*******************************\r\n”);
TRACE_INFO(“Copyright: 2010-2025 Oryx Embedded SARL\r\n”);
TRACE_INFO(“Compiled: %s %s\r\n”, DATE, TIME);
TRACE_INFO(“Target: APM32F427\r\n”);
TRACE_INFO(“\r\n”);
//Configure I/Os
ioInit();
//TCP/IP stack initialization
error = netInit();
//Any error to report?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to initialize TCP/IP stack!\r\n”);
}
//Configure the first Ethernet interface
interface = &netInterface[0];
//Set interface name
netSetInterfaceName(interface, APP_IF_NAME);
//Set host name
netSetHostname(interface, APP_HOST_NAME);
//Set host MAC address
macStringToAddr(APP_MAC_ADDR, &macAddr);
netSetMacAddr(interface, &macAddr);
//Select the relevant network adapter
netSetDriver(interface, &apm32f4xxEthDriver);
netSetPhyDriver(interface, &lan8720PhyDriver);
//Initialize network interface
error = netConfigInterface(interface);
//Any error to report?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to configure interface %s!\r\n”, interface->name);
}
#if (IPV4_SUPPORT == ENABLED)
#if (APP_USE_DHCP_CLIENT == ENABLED)
//Get default settings
dhcpClientGetDefaultSettings(&dhcpClientSettings);
//Set the network interface to be configured by DHCP
dhcpClientSettings.interface = interface;
//Disable rapid commit option
dhcpClientSettings.rapidCommit = FALSE;
//DHCP client initialization
error = dhcpClientInit(&dhcpClientContext, &dhcpClientSettings);
//Failed to initialize DHCP client?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to initialize DHCP client!\r\n”);
}
//Start DHCP client
error = dhcpClientStart(&dhcpClientContext);
//Failed to start DHCP client?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to start DHCP client!\r\n”);
}
#else
//Set IPv4 host address
ipv4StringToAddr(APP_IPV4_HOST_ADDR, &ipv4Addr);
ipv4SetHostAddr(interface, ipv4Addr);
//Set subnet mask
ipv4StringToAddr(APP_IPV4_SUBNET_MASK, &ipv4Addr);
ipv4SetSubnetMask(interface, ipv4Addr);
//Set default gateway
ipv4StringToAddr(APP_IPV4_DEFAULT_GATEWAY, &ipv4Addr);
ipv4SetDefaultGateway(interface, ipv4Addr);
//Set primary and secondary DNS servers
ipv4StringToAddr(APP_IPV4_PRIMARY_DNS, &ipv4Addr);
ipv4SetDnsServer(interface, 0, ipv4Addr);
ipv4StringToAddr(APP_IPV4_SECONDARY_DNS, &ipv4Addr);
ipv4SetDnsServer(interface, 1, ipv4Addr);
#endif
#endif
#if (IPV6_SUPPORT == ENABLED)
#if (APP_USE_SLAAC == ENABLED)
//Get default settings
slaacGetDefaultSettings(&slaacSettings);
//Set the network interface to be configured
slaacSettings.interface = interface;
//SLAAC initialization
error = slaacInit(&slaacContext, &slaacSettings);
//Failed to initialize SLAAC?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to initialize SLAAC!\r\n”);
}
//Start IPv6 address autoconfiguration process
error = slaacStart(&slaacContext);
//Failed to start SLAAC process?
if(error)
{
//Debug message
TRACE_ERROR(“Failed to start SLAAC!\r\n”);
}
#else
//Set link-local address
ipv6StringToAddr(APP_IPV6_LINK_LOCAL_ADDR, &ipv6Addr);
ipv6SetLinkLocalAddr(interface, &ipv6Addr);
//Set IPv6 prefix
ipv6StringToAddr(APP_IPV6_PREFIX, &ipv6Addr);
ipv6SetPrefix(interface, 0, &ipv6Addr, APP_IPV6_PREFIX_LENGTH);
//Set global address
ipv6StringToAddr(APP_IPV6_GLOBAL_ADDR, &ipv6Addr);
ipv6SetGlobalAddr(interface, 0, &ipv6Addr);
//Set default router
ipv6StringToAddr(APP_IPV6_ROUTER, &ipv6Addr);
ipv6SetDefaultRouter(interface, 0, &ipv6Addr);
//Set primary and secondary DNS servers
ipv6StringToAddr(APP_IPV6_PRIMARY_DNS, &ipv6Addr);
ipv6SetDnsServer(interface, 0, &ipv6Addr);
ipv6StringToAddr(APP_IPV6_SECONDARY_DNS, &ipv6Addr);
ipv6SetDnsServer(interface, 1, &ipv6Addr);
#endif
#endif
//Set task parameters
taskParams = OS_TASK_DEFAULT_PARAMS;
taskParams.stackSize = 2048;
taskParams.priority = OS_TASK_PRIORITY_NORMAL;
//Create user task
taskId = osCreateTask(“User”, userTask, NULL, &taskParams);
//Failed to create the task?
if(taskId == OS_INVALID_TASK_ID)
{
//Debug message
TRACE_ERROR(“Failed to create task!\r\n”);
}
//Set task parameters
taskParams = OS_TASK_DEFAULT_PARAMS;
taskParams.stackSize = 1024;
taskParams.priority = OS_TASK_PRIORITY_NORMAL;
//Create a task to blink the LED
taskId = osCreateTask(“LED”, ledTask, NULL, &taskParams);
//Failed to create the task?
if(taskId == OS_INVALID_TASK_ID)
{
//Debug message
TRACE_ERROR(“Failed to create task!\r\n”);
}
//Start the execution of tasks
osStartKernel();
//This function should never return
return 0;
}
`
7、运行效果
LED3间歇性闪烁一次,每间隔30秒开发板开始执行一次文件下载,并输出下载文件大小及下载次数;

8.问题点
在main函数的头部,必须重新调用SystemInit();来更新SystemCoreClock的值为240000000,否则系统会按照16000000的主频进行systick的初始化(仿真发现其实在启动文件内会先调用SystemInit(),SystemCoreClock的值会更新为240000000,但是进入main函数后,这个数值又恢复成16000000),导致调度周期异常,该现象在APM32F4xx_SDK_V1.5.0\Examples\Board_APM32F427_Tiny\RTOS\CMSIS_FreeRTOS工程中也是如此,希望官方可以研究解答一下疑问;
