手把手教你用STM32cube调通STM32H743以太网通讯并实现TCP客户端

STM32H7内核是Cortex-M7,目前接触到与M4最大的区别就是CACHE,M7有两个CACHE,DCACHE和ICACHE。由于这个CACHE踩了不少坑。html

本例程软硬件配置为:FreeRTOS+LwIP+LAN8720,在正点原子阿波罗开发板上测试api

1. Cube配置

RCC都使用外部晶振,由于后面要用到FreeRTOS,cube会建议使用TIM1做为SYS时钟源服务器

系统网络

不考虑功耗时钟频率直接拉到最高480MHzsocket

ETH模块使能,配置为RMII模式,MAC地址随便填一个,这里能看到一个warning,说ETH模块只能运行在0x24000000地址起始的RAM中,网上查了一下这个地址是AXI RAM,AXI是一种总线结构,这边先不用管,能够等进入工程后配置。而后就是确认ETH与物理层芯片相连的引脚是否正确,因为使用的是阿波罗开发板,原来默认的发送接口是B十二、B13,须要改成G1三、G14,最后使能ETH全局中断。这里会发现如今版本的Cube没有配置物理层芯片地址(PHY ADDR)的选项,不用担忧,在程序中它会本身遍历0-31的物理地址,找到有应答的地址做为物理层芯片地址。除此以外,将ETH的GPIO口频率都设为最高。tcp

启用FreeRTOS,可根据实际需求修改配置函数

启动LWIP发现是灰的,移到LWIP上会发现cortex-m7须要启用DCache才能使用LWIP测试

启用DCache后能正常启用LWIPui

设置物理层芯片,这里只有LAN8742能够选,不过不要紧,LAN8720也能够用,在通用设置里面能够选择启用哪些操做,这里使能了DNS.net

最后物理层芯片须要一个硬件复位信号引脚,因为阿波罗开发板中该引脚是由PCF8574芯片控制的,PCF8574芯片须要经过IIC控制,嫌麻烦就看了下正点原子给的原理图,直接把红框中的两个电阻焊掉用跳线链接LAN8720的复位引脚和H743的GPIO引脚,这个GPIO口随便选,这里用了A5。配置输出电平为High

网络部分的配置就这些,其余模块根据本身需求配置,好比串口、定时器等。这里使能了一个串口做为调试用,波特率直接给到了921600Bit/s,引脚改成A9A10

最后一步设置项目名称等,编译器使用了IAR8.3(编译比keil快不少),设置完了就生成代码

2. 工程配置

main.c中重定向printf用于打印消息,另外提供一个MPU配置函数

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
    //HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1 , 0xffff);
	while ((USART1->ISR & 0X40) == 0);
    USART1->TDR = (uint8_t) ch;
    return ch;
}

/**
  * @brief  Configure the MPU attributes 
  * @param  None
  * @retval None
  */
static void MPU_Config(void)
{
  MPU_Region_InitTypeDef MPU_InitStruct;

  /* Disable the MPU */
  HAL_MPU_Disable();

  /* Configure the MPU attributes as Device not cacheable 
     for ETH DMA descriptors */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0x30040000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256B;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  /* Configure the MPU attributes as Cacheable write through 
     for LwIP RAM heap which contains the Tx buffers */
  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = 0x30044000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER1;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);

  /* Enable the MPU */
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/* USER CODE END 0 */

main()函数中调用MPU_config,并使能SRAM时钟

在ethernetif.c文件的low_level_init函数中添加物理层重启语句

在ethernetif.c文件的low_level_output函数中添加DCache语句

在ethernetif.c文件的low_level_input函数中添加DCache语句

在lwipopts.h文件末尾添加宏定义

#define LWIP_RAM_HEAP_POINTER    (0x30044000)

在中断文件stm32h7xx_it.c中先引入外部信号量,再在ETH中断中添加信号量释放函数

最后打开IAR的Project->Options->Linker->Config->Edit修改RAM起始地址

到这里就软件就配置完了,接下来是TCP客户端的实现

3. TCP客户端

建立TCP客户端的.c文件

加入代码

#include "lwip.h"
#include "lwip/api.h"
#include "stm32h7xx_hal.h"
#include "cmsis_os.h"
#include "socket.h"
#include "dns.h"

void DNS_found_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg)
{
    uint32_t* ip = (uint32_t*)ipaddr;
    printf("DNS found: IP %d:%d:%d:%d\n",((*ip)&0xff),(((*ip)>>8)&0xff),(((*ip)>>16)&0xff),(((*ip)>>24)&0xff));
}

void network_init(struct netconn *conn)
{
    int sock = -1;
    conn = netconn_new(NETCONN_TCP);
    if (conn == NULL){
        printf("Set new connection failed...\r\n");
        return;
    }
    sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
    if(sock < 0){
        printf("Get socket failed!... Try again...\r\n");
        vTaskDelay(1000);
    }
    conn->socket = sock;
}

int network_connect(struct netconn *conn, char *hostname, int port)
{
    int err;
    struct sockaddr_in sAddr;
    ip_addr_t addr;
    printf("Socket is:%d\r\n",conn->socket);
    err = dns_gethostbyname(hostname, &addr, DNS_found_cb, NULL);
    if(err != 0){
        printf("DNS failed to get host by name : %d \n",err);
        return -1;
    }
    //addr.addr = htonl(addr.addr);
    sAddr.sin_addr.s_addr = *((in_addr_t*)(&addr));
    sAddr.sin_family = AF_INET;
    sAddr.sin_port = htons(port);

    //netconn_bind(conn,IP_ADDR_ANY,port); 
    err = lwip_connect(conn->socket, (struct sockaddr*)(&sAddr), sizeof(struct sockaddr));
    if(err != 0){
        printf("LwIP connect failed :%d \n", err);
        return -2;
    }
    return 0;

}

void tcp_client_task(void const * argument)
{
//    struct netbuf *buf;
//    void *data;
//    u16_t len;
    err_t err;
    struct netconn conn;

    LWIP_UNUSED_ARG(argument);

    char ip4[] = "192.168.0.100";
    uint16_t port = 30001;

    printf("Connecting to server: %s :%d ...\r\n",ip4,port);
    network_init(&conn);

CONNECT:
    err = network_connect(&conn, ip4, port);
    if (err != ERR_OK){
        printf("Connect to server failed:%d \tReconnecting...\r\n",err);
        vTaskDelay(3000);
        goto CONNECT;
    }
    printf("Connect to server successed!\r\n");

    while(1)
    {
        vTaskDelay(2000);
    }
}

其中IP地址与端口须要根据本身服务器设置

在main.c文件中添加TCP任务函数

extern void tcp_client_task(void const * argument);

定义全局变量的TCP任务句柄

osThreadId TCPTaskHandle;

在StartDefaultTask中添加TCP任务

osThreadDef(TCPClient, tcp_client_task, osPriorityNormal, 0, 2048);
TCPTaskHandle = osThreadCreate(osThread(TCPClient), NULL);

若是须要debug能够打开lwip的debug功能,在debug.h中一下位置添加debug的宏定义

而后在opt.h文件中打开对应的Debug开关,如打开DHCP_DEBUG等等

大功告成,接下来就是烧录,并运行程序。观察串口打印的debug内容,能够看到lwip经过DHCP得到到了IP地址、网关及子网掩码

进入路由器配置页面,能够看到路由器中多了一个Uknown设备,地址为192.168.0.103

服务器端显示,有一个192.168.0.103的TCP客户端链接

ping也能够直接ping通

 

4. 工程文件

STM32H743以太网通讯TCP客户端实现

5. Reference

http://www.stmcu.org.cn/module/forum/thread-615089-1-3.html