本帖最后由 xiaoqi976633690 于 2023-7-12 12:37 编辑
声明:本例程基于极海官方APM32F4xx_SDK_v1.3内的ETH_TCP_client例程修改,目标路径\**\APM32F4xx_SDK_v1.3\Examples\ETH\ETH_TCP_client
一、准备硬件:APM32F407IG Tiny Board,RJ45网线,路由或笔记本电脑或交换机,usb串口线
软件:APM32F4xx_SDK_v1.3,lwip基础,mdk,网络调试助手。
二、实验目的
tiny开发板通过网络连接到server端,server端发送指令控制开发板LED3点亮和熄灭。
二、实验过程
由于官方已经将底层的驱动实现,这里不细讲lwip移植和lwip内核实现原理,感兴趣的可以看下正点原子或野火的lwip专题教程。
LWIP有三种API,在本例程中,使用Raw API(无操作系统)
a. mdk安装apm32f407的pack,打开官方例程
b.主要实现函数
c. min.c 主要函数说明
LwIP协议栈初始化函数
LwIP_Init函数用于初始化LwIP协议栈,一般在main函数中调用。首先是内存相关初始化,mem_init函数是动态内存堆初始化,memp_init函数是存储池初始化, LwIP是实现内存的高效利用,内部需要不同形式的内存管理模式。 接下来为ipaddr、netmask和gw结构体变量赋值,设置本地IP地址、子网掩码和网关,如果使用DHCP功能直接赋值为0即可。netif_add是以太网设备添加函数, 即向LwIP协议栈申请添加一个网卡设备,函数有7个形参,第一个为netif结构体类型变量指针,这里赋值为gnetif地址,该网卡设备属性就存放在gnetif变量中; 第二个为ip_addr结构体类型变量指针,用于设置网卡IP地址;第三个ip_addr结构体类型变量指针,用于设置子网掩码;第四个为ip_addr结构体类型变量指针, 用于设置网关;第五个为void变量,用户自定义字段,一般不用直接赋值NULL;第六个为netif_init_fn类型函数指针,用于指向网卡设备初始化函数, 这里赋值为指向ethernetif_init函数,该函数在ethernetif.c文件定义,初始化LwIP与ETH外设连接函数;最后一个参数为netif_input_fn类型函数指针, 用于指向以太网帧接收函数,这里赋值为指向ethernet_input函数,该函数定义在etharp.c文件中。
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] This function initializes the lwIP stack
*
* @param None
*
* @retval None
*/
void LwIP_Init(void) //lwip初始化函数
{
struct ip_addr ipaddr;
struct ip_addr netmask;
struct ip_addr gw;
/** Initializes the dynamic memory heap */
mem_init();
/** Initializes the memory pools */
memp_init();
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
/** Config MAC Address */
ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);
/** Add a network interface to the list of lwIP netifs */
netif_add(&UserNetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/** Registers the default network interface */
netif_set_default(&UserNetif);
/** When the netif is fully configured this function must be called */
netif_set_up(&UserNetif);
}
LwIP_Pkt_Handle函数用于从以太网存储器读取一个以太网帧并将其发送给LwIP,它在接收到以太网帧时被调用, 它是直接调用ethernetif_input函数实现的,该函数定义在ethernetif.c文件中。
/*!
* [url=home.php?mod=space&uid=247401]@brief[/url] This function received ethernet packet
*
* @param None
*
* @retval None
*/
void LwIP_Pkt_Handle(void)
{
/** Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
ethernetif_input(&UserNetif);
}
LwIP_Periodic_Handle函数是一个必须被无限循环调用的LwIP支持函数,一般在main函数的无限循环中调用,主要功能是为LwIP各个模块提供时间并查询链路状态, 该函数有一个形参,用于指示当前时间,单位为ms。
/*!
* @brief This function LwIP periodic tasks
*
* @param ETHTimer the current Ethernet Timer value
*
* @retval None
*/
void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
{
static uint8_t flagToggle = 0;
#if LWIP_TCP
/** TCP periodic process every 250 ms */
if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer = ETHTimer;
tcp_tmr();
}
#endif
/** ARP periodic process every 5s */
if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
{
ARPTimer = ETHTimer;
etharp_tmr();
}
/** Check link status */
if ((ETHTimer - LinkTimer) >= 1000)
{
if ((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0))
{
/** link goes up */
netif_set_link_up(&UserNetif);
flagToggle = 1;
}
if ((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1))
{
EthLinkStatus = 1;
/** link goes down */
netif_set_link_down(&UserNetif);
flagToggle = 0;
}
}
}
d. echo_cc.c函数讲解
该文件主要实现tcp—client
LWIP 中 RAW API 编程接口中与 TCP 相关的函数:
TCP 的 RAW API 编程函数
tcp连接初始化函数
/*!
* @brief tcp client init
*
* @param tcpc_port: client port
*
* @param ipaddr: point to the client ip address
*
* @retval None
*/
void tcpc_echo_init(ip_addr_t *ipaddr, uint16_t tcpc_port)//tcp连接初始化函数
{
tcpc_echo_pcb = tcp_new();
if (tcpc_echo_pcb != NULL)
{
/* connect to server address:port */
tcp_connect(tcpc_echo_pcb, ipaddr, tcpc_port, tcpc_echo_accept);///* 连接服务器 */
}
else
{
/* abort? output diagnostic? */
}
}
pbuf接收处理函数指令处理主要在该函数内实现/*!
* @brief Printf TCP Receive Data
*
* @param p: pointer on the tcp pcb received pbuf
*
* @retval None
*/
void PrintfRecData(struct pbuf *p)//pbuf接收处理函数
{
char recbuf[100] = {0};
char LED_ON[]={"LED3ON"};
char LED_OFF[]={"LED3OFF"};
memcpy(recbuf, p->payload, p->len);
printf("received message:%s\n\r", recbuf);
if(strcmp(recbuf,LED_ON)==0)
{
printf("LED3 on\n\r");
APM_TINY_LEDOn(LED3);//打开LED
}
if(strcmp(recbuf,LED_OFF)==0)
{
printf("LED3 off\n\r");
APM_TINY_LEDOff(LED3);//关闭LED
}
}
/*!
* @brief LwIP tcp_recv callback function
*
* @param None
*
* @retval None
*/
err_t tcpc_echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
struct tcpc_echo_state *es;
err_t ret_err;
LWIP_ASSERT("arg != NULL", arg != NULL);
es = (struct tcpc_echo_state *)arg;
if (p == NULL)
{
/* remote host closed connection */
es->state = ES_CLOSING;
if (es->p == NULL)
{
/* we're done sending, close it */
tcpc_echo_close(tpcb, es);
}
else
{
/* we're not done yet */
tcp_sent(tpcb, tcpc_echo_sent);
tcpc_echo_send(tpcb, es);
}
ret_err = ERR_OK;
}
else if (err != ERR_OK)
{
/* cleanup, for unkown reason */
if (p != NULL)
{
es->p = NULL;
pbuf_free(p);
}
ret_err = err;
}
else if (es->state == ES_ACCEPTED)//接收缓存处理
{
tcp_recved(tpcb, p->tot_len);
/** Printf TCP Receive Data */
PrintfRecData(p);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
else
{
/* unkown es->state, trash data */
tcp_recved(tpcb, p->tot_len);
es->p = NULL;
pbuf_free(p);
ret_err = ERR_OK;
}
return ret_err;
}
/*!
* @brief LwIP tcp_err callback function
*
* @param None
*
* @retval None
*/
void tcpc_echo_error(void *arg, err_t err)
{
struct tcpc_echo_state *es;
LWIP_UNUSED_ARG(err);
es = (struct tcpc_echo_state *)arg;
if (es != NULL)
{
mem_free(es);
}
}
e.main.c
main函数实现
int main(void)
{
char LCDDisplayBuf[100] = {0};
struct ip_addr DestIPaddr;
uint8_t flag = 0;
USART_Config_T usartConfig;
/** User config the different system Clock */
UserRCMClockConfig();
/** Configure SysTick */
ConfigSysTick();
/** Configure USART */
usartConfig.baudRate = 115200;
usartConfig.wordLength = USART_WORD_LEN_8B;
usartConfig.stopBits = USART_STOP_BIT_1;
usartConfig.parity = USART_PARITY_NONE ;
usartConfig.mode = USART_MODE_TX_RX;
usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;
APM_BOARD_COMInit(COM1,&usartConfig);
/** Configures LED2 and LED3 */
APM_BOARD_LEDInit(LED2);
APM_BOARD_LEDInit(LED3);
/** KEY init*/
APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
printf("This is a ETH TCP Client Demo! \r\n请稍侯,正在启动lwip\n\r");
APM_TINY_LEDOn(LED3);
/** Configure ethernet (GPIOs, clocks, MAC, DMA) */
ConfigEthernet();
/** Initilaize the LwIP stack */
LwIP_Init();
APM_TINY_LEDOff(LED3);
/** Use Com printf static IP address*/
sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
IP_ADDR0,
IP_ADDR1,
IP_ADDR2,
IP_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
NETMASK_ADDR0,
NETMASK_ADDR1,
NETMASK_ADDR2,
NETMASK_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
GW_ADDR0,
GW_ADDR1,
GW_ADDR2,
GW_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d \r\n",
COMP_IP_ADDR0,
COMP_IP_ADDR1,
COMP_IP_ADDR2,
COMP_IP_ADDR3,
COMP_PORT);
printf("%s",LCDDisplayBuf);
printf("\n\rKEY1: Connect TCP server \r\n");
printf("KEY2: Disconnect TCP server \r\n");
while(1)
{
if ((APM_TINY_PBGetState(BUTTON_KEY1)==0)&&(flag==0))
{
APM_TINY_LEDOn(LED2);
if (EthLinkStatus == 0)
{
/** connect to tcp server */
printf("\n\rConnect TCP server \r\n");
IP4_ADDR( &DestIPaddr, COMP_IP_ADDR0, COMP_IP_ADDR1, COMP_IP_ADDR2, COMP_IP_ADDR3 );
tcpc_echo_init(&DestIPaddr,COMP_PORT);
flag=1;
}
}
if ((APM_TINY_PBGetState(BUTTON_KEY2)==0)&&(flag==1))
{
APM_TINY_LEDOff(LED2);
printf("\n\rDisconnect TCP server \r\n");
tcpc_echo_disable();
flag=0;
}
/** check if any packet received */
if (ETH_CheckReceivedFrame())
{
/** process received ethernet packet */
LwIP_Pkt_Handle();
}
/** handle periodic timers for LwIP */
LwIP_Periodic_Handle(ETHTimer);
}
}
f.烧录-实验现象
将开发板连接和server端同网段到网络。
等待串口出现ip提示信息,按key1连接到server端
发送对应指令实现了led的点亮和熄灭
程序源码附件
ETH_TCP_client.zip
(7.72 MB)
|