发新帖本帖赏金 30.00元(功能说明)我要提问
返回列表
打印
[APM32F4]

【极海APM32F407IG Tiny Board开发板测评】TCP_client点灯

[复制链接]
929|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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)

使用特权

评论回复

打赏榜单

Gfan 打赏了 30.00 元 2023-08-17
理由:APM32F407 TINY测评精选文章

沙发
xiaoqi976633690|  楼主 | 2023-7-12 16:42 | 只看该作者
演示视频哔哩哔哩https://www.bilibili.com/video/BV1XM4y1j7FE/

使用特权

评论回复
板凳
tpgf| | 2023-8-7 13:18 | 只看该作者
client是指的客户端还是服务器端啊

使用特权

评论回复
评论
xiaoqi976633690 2023-8-7 15:01 回复TA
客户端 
地板
nawu| | 2023-8-7 13:40 | 只看该作者
以太网的连接必须要有心跳信号吗

使用特权

评论回复
5
aoyi| | 2023-8-7 14:03 | 只看该作者
tcp好像是应该有心跳信号的  但是udp好像是不需要的

使用特权

评论回复
6
zljiu| | 2023-8-7 16:06 | 只看该作者
楼主有没有实测连接是否稳定呀

使用特权

评论回复
7
gwsan| | 2023-8-7 16:35 | 只看该作者
实测通讯速率最大能达到多少了吗?

使用特权

评论回复
8
tfqi| | 2023-8-7 17:12 | 只看该作者
以太网这边可以通过软件更改客户端或者是服务器端吗

使用特权

评论回复
发新帖 本帖赏金 30.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

28

主题

167

帖子

2

粉丝