STM32H723 + DP83848 + LWIP + RT-Thread(FreeRTOS) + STM32CubeMX + Keil MDK 超详细

2023-05-16

工程环境:

MCU:STM32H723ZGT

ETH PHY :DP83848

RT-Thread:RT-Thread nano 3.1.5

Software Pack:STM32CubeH7 Firmware Package V1.10.0 / 11-February-2022

参考文章:

STM32H723配置以太网+Freertos注意事项

STM32H723+Lwip+ETH+CUBE 完整配置(排了巨多坑!)

Cube配置STM32H743+DP83848以太网工程

STM32H743+CubeMX-梳理MPU的设置

前言:

首先使用CubeMX配置相关外设和软件代码,导出Keil MDK工程,然后在Keil中修改相关代码。

内存规划:

D2域SRAM1(0x30000000-0x30000200 , 512B)存放ETH DMA的Rx描述符

D2域SRAM1(0x30000200-0x30000400 , 512B)存放ETH DMA的Tx描述符

D2域SRAM1(0x30000400-0x30004000 , 15KB)存放LWIP的RX_POOL内存池

D2域SRAM2(0x30004000-0x30008000 , 16KB)存放LWIP的内存堆

这里LWIP的RX_POOL内存池和内存堆不只局限于D2域,也可以放在D1域的AXI SRAM,AXI SRAM默认有320KB,大很多,但是不能放在DTCM中,因为ETH DMA无法访问DTCM。

注意:ETH DMA的Rx描述符和Tx描述符只能放在D2域。

我们这里把内存池和内存堆独立配置,LWIP还支持将内存池由内存堆实现,将内存堆由内存池实现。

一、CubeMX配置:

ETH外设配置:

选择ETH外设;

我这里使用的是MII接口,请根据实际情况选择;

配置ETH外设的MAC地址和ETH DMA描述符;

Ethernet MAC Address  :配置MAC地址;

Tx Descriptor Length  :ETH DMA Tx描述符长度,这里默认4个,后期可以在代码中修改;

First Tx Descriptor Address  :首个ETH DMA Tx描述符地址,这个地址必须是D2域的SRAM地址,这里配置为0x30000200

Rx Descriptor Length  :ETH DMA Rx描述符长度,这里默认4个,后期可以在代码中修改;

First Rx Descriptor Address  :首个ETH DMA Rx描述符地址,这个地址必须是D2域的SRAM地址,这里配置为0x30000000

Rx Buffers Length:Rx缓冲区长度,配置为1528

ETH DMA的描述符1个是24字节,这里分别把TX和RX配置为4个,也就是分别用了96字节,而我们分配的空间分别是512字节,最多可以配置512/4=21个,实际用不了这么多,剩余的空间就预留后期拓展吧,在配置MPU的时候要把这1KB(0x30000000-0x30000400 )单独保护起来。

开启ETH全局中断 

 根据实际情况配置以太网外设的GPIO,并将所有使用的GPIO配置为高速;

添加PHY芯片的复位引脚,我这里用的是PF13,将PF13配置为上拉推挽输出模式,默认输出高电平。

 MPU配置

D2域SRAM主要用于LWIP的RX_POOL内存池和内存堆,存放以太网接收和发送的数据,这里不缓存这个区域;

 D2域的0x30000000-0x30000400 这1KB用于存放ETH DMA的描述符,这里配置为共享设备模式。

 RTOS配置

STM32CubeMX也支持添加RT-Thread,需要手动安装软件包,但是STM32CubeMX不会生成LWIP与操作系统相关的代码,主要就是线程、信号量、邮箱、互斥锁等相关的代码,在这里我们先启用STM32CubeMX里的FreeRTOS操作系统,导出工程文件后在Keil里移除FreeRTOS,然后添加RT-Thread,并将LWIP与操作系统相关的代码替换成RT-Thread的代码。

LWIP配置

这里把LWIP的内存堆放在D2域的SRAM2中(0x30004000-0x30008000),LWIP的可用内存堆大小MEM_SIZE配置为14KB。

LWIP的内存堆也可以放在D1域的AXI SRAM中,但是不能放在DTCM中,因为ETH DMA无法访问DTCM。

配置TCPIP线程:

 将TCPIP线程的优先级设高点,在RT-Thread中数字越小优先级越高。

 配置IP地址:

选择PHY芯片:

这里只能选择LAN8742,由于我们用的是DP83848,后面我们在代码中修改一下PHY的地址。

串口配置

这里开启USART1用于输出调试信息,根据实际情况配置。

HAL库时钟配置

默认使用系统定时器SysTick

RCC时钟配置

启用外部HSE时钟

 时钟树根据实际使用情况配置

导出Keil工程

这里选择仅拷贝使用的库代码,将每个外设初始化代码放在单个文件中,开启修改备份。

二、 Keil代码修改

修改PHY地址寄存器:

DP83848的PHY地址是在PHYCR(0x19)这个寄存器中,打开lan8742.c文件,找到int32_t LAN8742_Init(lan8742_Object_t *pObj)函数,将图中第107行代码这里改成0x19。程序中是通过循环读取PHYCR(0x19)寄存器,直到读出的地址与addr匹配为止。

修改获取链路状态函数

LAN8742的链路状态是在地址为31的寄存器中,而DP83848在PHYSTS寄存器(地址0x10)中,

所以需要修改 LAN8742_GetLinkState() 函数,

 修改后如下:

/**
  * @brief  Get the link state of LAN8742 device.
  * @param  pObj: Pointer to device object. 
  * @param  pLinkState: Pointer to link state
  * @retval LAN8742_STATUS_LINK_DOWN  if link is down
  *         LAN8742_STATUS_AUTONEGO_NOTDONE if Auto nego not completed 
  *         LAN8742_STATUS_100MBITS_FULLDUPLEX if 100Mb/s FD
  *         LAN8742_STATUS_100MBITS_HALFDUPLEX if 100Mb/s HD
  *         LAN8742_STATUS_10MBITS_FULLDUPLEX  if 10Mb/s FD
  *         LAN8742_STATUS_10MBITS_HALFDUPLEX  if 10Mb/s HD       
  *         LAN8742_STATUS_READ_ERROR if connot read register
  *         LAN8742_STATUS_WRITE_ERROR if connot write to register
  */
int32_t LAN8742_GetLinkState(lan8742_Object_t *pObj)
{
  uint32_t readval = 0;
  
  /* Read Status register */
  if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
  {
    return LAN8742_STATUS_READ_ERROR;
  }
  
  /* Read Status register again */
  if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
  {
    return LAN8742_STATUS_READ_ERROR;
  }
  
  if((readval & LAN8742_BSR_LINK_STATUS) == 0)
  {
    /* Return Link Down status */
    return LAN8742_STATUS_LINK_DOWN;    
  }
  
  /* Check Auto negotiaition */
  if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) < 0)//读取基本控制寄存器0x00
  {
    return LAN8742_STATUS_READ_ERROR;
  }
  
  if((readval & LAN8742_BCR_AUTONEGO_EN) != LAN8742_BCR_AUTONEGO_EN) //未启用自动协商
  {
    if(((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) && ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE)) 
    {
      return LAN8742_STATUS_100MBITS_FULLDUPLEX;
    }
    else if ((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT)
    {
      return LAN8742_STATUS_100MBITS_HALFDUPLEX;
    }        
    else if ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE)
    {
      return LAN8742_STATUS_10MBITS_FULLDUPLEX;
    }
    else
    {
      return LAN8742_STATUS_10MBITS_HALFDUPLEX;
    }  		
  }
  else /* Auto Nego enabled */
  {
    //DP83848
    if(pObj->IO.ReadReg(pObj->DevAddr, 0x10, &readval) < 0) //DP83848,PHY状态寄存器(PHYSTS) 地址0x10
    {
      return LAN8742_STATUS_READ_ERROR;
    }
      
    /* Check if auto nego not done */
    if((readval & 0x0010) == 0) //bit4, Auto-Negotiation Complete
    {
      return LAN8742_STATUS_AUTONEGO_NOTDONE;//自动协商未完成
    }
    
    if((readval & 0x0006) == 0x04)//bit2,bit1,  100 Mb/s mode,Full duplex mode
    {
      return LAN8742_STATUS_100MBITS_FULLDUPLEX;
    }
    else if ((readval & 0x0006) == 0x00)//bit2,bit1,  100 Mb/s mode,Half duplex mode.
    {
      return LAN8742_STATUS_100MBITS_HALFDUPLEX;
    }
    else if ((readval & 0x0006) == 0x06)//bit2,bit1,  10 Mb/s mode,Half duplex mode.
    {
      return LAN8742_STATUS_10MBITS_FULLDUPLEX;
    }
    else
    {
      return LAN8742_STATUS_10MBITS_HALFDUPLEX; //10 Mb/s mode,Half duplex mode.
    }				
  }
}

添加PHY芯片复位代码:

打开ethernetif.c文件,找到static void low_level_init(struct netif *netif)函数,在这里添加PHY复位代码,我这里用的是PF13,根据实际情况修改。

添加LWIP的RX_POOL内存池定位代码

打开cc.h文件,

注释这行代码,防止编译时报错。

在文件末尾添加如下代码,将memp_memory_RX_POOL_base这个数组定位到D2域SRAM1中,由于前1KB用于存放ETH DMA描述符了,所以从0x30000400开始,一共15KB。

/* USER CODE BEGIN 0 */                                    
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
#pragma location = 0x30000400
extern unsigned char memp_memory_RX_POOL_base[];

#elif defined ( __CC_ARM )  /* MDK ARM Compiler */
__attribute__((at(0x30000400))) extern unsigned char memp_memory_RX_POOL_base[];

#elif defined ( __GNUC__ ) /* GNU Compiler */
extern unsigned char memp_memory_RX_POOL_base[] __attribute__((section(".Rx_PoolSection")));
#endif 
/* USER CODE END 0 */

如果想要把RX_POOL内存池放到别的地方,如果用的是AC5编译器,则修改上图中第94行代码,如果用的是AC6编译器,则需要修改分散加载文件。

AC6编译器修改方式如下:

 

 分散加载文件内容如下:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x24000000 0x00050000  {
   .ANY (+RW +ZI)
  }
  
  RW_IRAM3 0x30000000 0x00000200  {  ; 512B - SRAM1 存放ETH DMA RX描述符
   .ANY(.RxDecripSection)
  }
  
  RW_IRAM4 0x30000200 0x00000200  {  ; 512B - SRAM1 存放ETH DMA TX描述符
   .ANY(.TxDecripSection)
  }
  
  RW_IRAM5 0x30000400 0x00003C00  {  ; 15KB - SRAM1 存放RX_POOL内存池
   .ANY(.Rx_PoolSection)
  }
}

由于这里配置的RX_POOL内存池的大小只有15KB,还需要修改 ethernetif.c 文件中ETH_RX_BUFFER_CNT 宏的大小,15KB大约能分配9个ETH_RX_BUFFER 。

#define ETH_RX_BUFFER_CNT             9U

 

此时编译整个工程,下载程序到板子就能ping通了。

到此整个系统可以工作了,下面是将FreeRTOS替换成RT-Thread


三、移除FreeRTOS代码

 将FreeRTOS的文件包含目录删除

Ctrl+F 查找 cmsis_os.h,凡是包含了cmsis_os.h这个头文件的文件,全部注释掉

 

四、添加RT-Thread

 为了省事,这里直接使用Keil的包管理器(需要先安装RT-Thread Pack),添加RT-Thread,

 

打开board.c,添加头文件和外部函数声明,

 

#include "main.h"

extern HAL_StatusTypeDef HAL_Init(void);
extern void SystemClock_Config(void);

 在void rt_hw_board_init(void)函数中添加HAL库初始化和时钟配置的函数,这里使用SysTick定时器为RT-Thread提供心跳,

void rt_hw_board_init(void)
{
//#error "TODO 1: OS Tick Configuration."
    /* 
     * TODO 1: OS Tick Configuration
     * Enable the hardware timer and call the rt_os_tick_callback function
     * periodically with the frequency RT_TICK_PER_SECOND. 
     */
    HAL_Init();
    SystemClock_Config();
    SystemCoreClockUpdate();
    
    HAL_SYSTICK_Config(SystemCoreClock / RT_TICK_PER_SECOND);
    
    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

 添加SysTick系统定时器中断函数,用于操作系统

/**
 * This is the timer interrupt service routine.
 *
 */
void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    HAL_IncTick();
    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

修改 stm32h7xx_it.c 文件,注释图中的代码

 HardFault_Handler(void)和MemManage_Handler(void)函数RT-Thread已经实现了,这里注释掉

注释系统定时器中断函数,(已经在board.c文件中了)

 

打开 rt_config.h 文件,使能信号量、互斥锁、邮箱

把main线程的栈改大点

 

 新建一个 board.h 文件,放到工程目录里,这里面主要是芯片存储有关的内容

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-5      SummerGift   first version
 */

#ifndef __BOARD_H__
#define __BOARD_H__

#include <rtthread.h>
#include <stm32h7xx.h>
//#include "drv_common.h"
//#include "drv_gpio.h"

#ifdef __cplusplus
extern "C" {
#endif

#define STM32_FLASH_START_ADRESS     ((uint32_t)0x08000000)
#define STM32_FLASH_SIZE             (1024 * 1024)
#define STM32_FLASH_END_ADDRESS      ((uint32_t)(STM32_FLASH_START_ADRESS + STM32_FLASH_SIZE))

#define STM32_SRAM_SIZE           (256) 
#define STM32_SRAM_END            (0x24000000 + STM32_SRAM_SIZE * 1024) /*AXI SRAM, 256KB*/

#if defined(__CC_ARM) || defined(__CLANG_ARM)
extern int Image$$RW_IRAM1$$ZI$$Limit;
#define HEAP_BEGIN      (&Image$$RW_IRAM1$$ZI$$Limit)
#elif __ICCARM__
#pragma section="CSTACK"
#define HEAP_BEGIN      (__segment_end("CSTACK"))
#else
extern int __bss_end;
#define HEAP_BEGIN      (&__bss_end)
#endif

#define HEAP_END        STM32_SRAM_END

void SystemClock_Config(void);

#ifdef __cplusplus
}
#endif

#endif

五、替换sys_arch.c、sys_arch.h、ethernetif.c中有关操作系统的代码

修改 sys_arch.h 文件,如下:

#ifndef __SYS_ARCH_H__
#define __SYS_ARCH_H__

#include "lwip/opt.h"

#include "arch/cc.h"
#include <rtthread.h>

#if (NO_SYS != 0)
#error "NO_SYS need to be set to 0 to use threaded API"
#endif

#ifdef  __cplusplus
extern "C" {
#endif

#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif

#define SYS_MBOX_NULL RT_NULL
#define SYS_SEM_NULL  RT_NULL

//typedef uint32_t sys_prot_t;

#define SYS_MBOX_SIZE 10
#define SYS_LWIP_TIMER_NAME "lwip_timer"
#define SYS_LWIP_MBOX_NAME "lwip_mbox"
#define SYS_LWIP_SEM_NAME "lwip_sem"
#define SYS_LWIP_MUTEX_NAME "lwip_mu"

typedef rt_sem_t sys_sem_t;
typedef rt_mutex_t sys_mutex_t;
typedef rt_mailbox_t  sys_mbox_t;
typedef rt_thread_t sys_thread_t;

#ifdef  __cplusplus
}
#endif

#endif /* __SYS_ARCH_H__ */

修改 sys_arch.c 文件,这个文件里面是有关线程、信号量等的操作,修改成RT-Thread的实现方式:

/* lwIP includes. */
#include "lwip/debug.h"
#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/stats.h"

#if !NO_SYS

#include <rthw.h>
#include <rtthread.h>
#include "sys_arch.h"
/* ====================== Mailbox ====================== */

/*
 * Create an empty mailbox for maximum "size" elements
 *
 * @return the operation status, ERR_OK on OK; others on error
 */
err_t sys_mbox_new(sys_mbox_t *mbox, int size)
{
    static unsigned short counter = 0;
    char tname[RT_NAME_MAX];
    sys_mbox_t tmpmbox;

    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MBOX_NAME, counter);
    counter ++;

    tmpmbox = rt_mb_create(tname, size, RT_IPC_FLAG_FIFO);
    if (tmpmbox != RT_NULL)
    {
        *mbox = tmpmbox;

        return ERR_OK;
    }

    return ERR_MEM;
}

/*
 * Deallocate a mailbox
 */
void sys_mbox_free(sys_mbox_t *mbox)
{
    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_mb_delete(*mbox);

    return;
}

/** Post a message to an mbox - may not fail
 * -> blocks if full, only used from tasks not from ISR
 * @param mbox mbox to posts the message
 * @param msg message to post (ATTENTION: can be NULL)
 */
void sys_mbox_post(sys_mbox_t *mbox, void *msg)
{
    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_mb_send_wait(*mbox, (rt_uint32_t)msg, RT_WAITING_FOREVER);

    return;
}

/*
 * Try to post the "msg" to the mailbox
 *
 * @return return ERR_OK if the "msg" is posted, ERR_MEM if the mailbox is full
 */
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
    if (rt_mb_send(*mbox, (rt_uint32_t)msg) == RT_EOK)
        return ERR_OK;

    return ERR_MEM;
}

err_t
sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
{
  return sys_mbox_trypost(q, msg);
}

/** Wait for a new message to arrive in the mbox
 * @param mbox mbox to get a message from
 * @param msg pointer where the message is stored
 * @param timeout maximum time (in milliseconds) to wait for a message
 * @return time (in milliseconds) waited for a message, may be 0 if not waited
           or SYS_ARCH_TIMEOUT on timeout
 *         The returned time has to be accurate to prevent timer jitter!
 */
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout)
{
    rt_err_t ret;
    s32_t t;
    u32_t tick;

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* get the begin tick */
    tick = rt_tick_get();

    if(timeout == 0)
        t = RT_WAITING_FOREVER;
    else
    {
        /* convirt msecond to os tick */
        if (timeout < (1000/RT_TICK_PER_SECOND))
            t = 1;
        else
            t = timeout / (1000/RT_TICK_PER_SECOND);
    }

    ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, t);
    if(ret != RT_EOK)
    {
        return SYS_ARCH_TIMEOUT;
    }

    /* get elapse msecond */
    tick = rt_tick_get() - tick;

    /* convert tick to msecond */
    tick = tick * (1000 / RT_TICK_PER_SECOND);
    if (tick == 0)
        tick = 1;

    return tick;
}

/** Wait for a new message to arrive in the mbox
 * @param mbox mbox to get a message from
 * @param msg pointer where the message is stored
 * @param timeout maximum time (in milliseconds) to wait for a message
 * @return 0 (milliseconds) if a message has been received
 *         or SYS_MBOX_EMPTY if the mailbox is empty
 */
u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg)
{
    int ret;

    ret = rt_mb_recv(*mbox, (rt_ubase_t *)msg, 0);

    if(ret == -RT_ETIMEOUT)
        return SYS_ARCH_TIMEOUT;
    else
    {
        if (ret == RT_EOK)
            ret = 1;
    }

    return ret;
}

#ifndef sys_mbox_valid
/** Check if an mbox is valid/allocated:
 *  return 1 for valid, 0 for invalid
 */
int sys_mbox_valid(sys_mbox_t *mbox)
{
    return (int)(*mbox);
}
#endif

#ifndef sys_mbox_set_invalid
/** Set an mbox invalid so that sys_mbox_valid returns 0
 */
void sys_mbox_set_invalid(sys_mbox_t *mbox)
{
    *mbox = RT_NULL;
}
#endif
/*-----------------------------------------------------------------------------------*/
/*
 * Create a new semaphore
 *
 * @return the operation status, ERR_OK on OK; others on error
 */
err_t sys_sem_new(sys_sem_t *sem, u8_t count)
{
    static unsigned short counter = 0;
    char tname[RT_NAME_MAX];
    sys_sem_t tmpsem;

    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_SEM_NAME, counter);
    counter ++;

    tmpsem = rt_sem_create(tname, count, RT_IPC_FLAG_FIFO);
    if (tmpsem == RT_NULL)
        return ERR_MEM;
    else
    {
        *sem = tmpsem;

        return ERR_OK;
    }
}
/*-----------------------------------------------------------------------------------*/
/*
 * Block the thread while waiting for the semaphore to be signaled
 *
 * @return If the timeout argument is non-zero, it will return the number of milliseconds
 *         spent waiting for the semaphore to be signaled; If the semaphore isn't signaled
 *         within the specified time, it will return SYS_ARCH_TIMEOUT; If the thread doesn't
 *         wait for the semaphore, it will return zero
 */
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
    rt_err_t ret;
    s32_t t;
    u32_t tick;

    RT_DEBUG_NOT_IN_INTERRUPT;

    /* get the begin tick */
    tick = rt_tick_get();
    if (timeout == 0)
        t = RT_WAITING_FOREVER;
    else
    {
        /* convert msecond to os tick */
        if (timeout < (1000/RT_TICK_PER_SECOND))
            t = 1;
        else
            t = timeout / (1000/RT_TICK_PER_SECOND);
    }

    ret = rt_sem_take(*sem, t);

    if (ret == -RT_ETIMEOUT)
        return SYS_ARCH_TIMEOUT;
    else
    {
        if (ret == RT_EOK)
            ret = 1;
    }

    /* get elapse msecond */
    tick = rt_tick_get() - tick;

    /* convert tick to msecond */
    tick = tick * (1000 / RT_TICK_PER_SECOND);
    if (tick == 0)
        tick = 1;

    return tick;
}

/*
 * Signal a semaphore
 */
void sys_sem_signal(sys_sem_t *sem)
{
    rt_sem_release(*sem);
}

/*
 * Deallocate a semaphore
 */
void sys_sem_free(sys_sem_t *sem)
{
    RT_DEBUG_NOT_IN_INTERRUPT;
    rt_sem_delete(*sem);
}

#ifndef sys_sem_valid
/** Check if a semaphore is valid/allocated:
 *  return 1 for valid, 0 for invalid
 */
int sys_sem_valid(sys_sem_t *sem)
{
    return (int)(*sem);
}
#endif

#ifndef sys_sem_set_invalid
/** Set a semaphore invalid so that sys_sem_valid returns 0
 */
void sys_sem_set_invalid(sys_sem_t *sem)
{
    *sem = RT_NULL;
}
#endif

/* ====================== Mutex ====================== */

/** Create a new mutex
 * @param mutex pointer to the mutex to create
 * @return a new mutex
 */
err_t sys_mutex_new(sys_mutex_t *mutex)
{
    static unsigned short counter = 0;
    char tname[RT_NAME_MAX];
    sys_mutex_t tmpmutex;

    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_snprintf(tname, RT_NAME_MAX, "%s%d", SYS_LWIP_MUTEX_NAME, counter);
    counter ++;

    tmpmutex = rt_mutex_create(tname, RT_IPC_FLAG_FIFO);
    if (tmpmutex == RT_NULL)
        return ERR_MEM;
    else
    {
        *mutex = tmpmutex;

        return ERR_OK;
    }
}

/** Lock a mutex
 * @param mutex the mutex to lock
 */
void sys_mutex_lock(sys_mutex_t *mutex)
{
    RT_DEBUG_NOT_IN_INTERRUPT;
    rt_mutex_take(*mutex, RT_WAITING_FOREVER);

    return;
}

/** Unlock a mutex
 * @param mutex the mutex to unlock
 */
void sys_mutex_unlock(sys_mutex_t *mutex)
{
    rt_mutex_release(*mutex);
}

/** Delete a semaphore
 * @param mutex the mutex to delete
 */
void sys_mutex_free(sys_mutex_t *mutex)
{
    RT_DEBUG_NOT_IN_INTERRUPT;

    rt_mutex_delete(*mutex);
}

#ifndef sys_mutex_valid
/** Check if a mutex is valid/allocated:
 *  return 1 for valid, 0 for invalid
 */
int sys_mutex_valid(sys_mutex_t *mutex)
{
    return (int)(*mutex);
}
#endif

#ifndef sys_mutex_set_invalid
/** Set a mutex invalid so that sys_mutex_valid returns 0
 */
void sys_mutex_set_invalid(sys_mutex_t *mutex)
{
    *mutex = RT_NULL;
}
#endif

// Initialize sys arch
void sys_init(void)
{

}

/* ====================== System ====================== */

/*
 * Start a new thread named "name" with priority "prio" that will begin
 * its execution in the function "thread()". The "arg" argument will be
 * passed as an argument to the thread() function
 */
sys_thread_t sys_thread_new(const char    *name,
                            lwip_thread_fn thread,
                            void          *arg,
                            int            stacksize,
                            int            prio)
{
    rt_thread_t t;

    RT_DEBUG_NOT_IN_INTERRUPT;
    
    /* create thread */
    t = rt_thread_create(name, thread, arg, stacksize, prio, 20);
    RT_ASSERT(t != RT_NULL);
    
    /* startup thread */
    rt_thread_startup(t);
    
    return t;
}

sys_prot_t sys_arch_protect(void)
{
    rt_base_t level;

    /* disable interrupt */
    level = rt_hw_interrupt_disable();
    return level;
}

void sys_arch_unprotect(sys_prot_t pval)
{
    /* enable interrupt */
    rt_hw_interrupt_enable(pval);

    return;
}

void sys_arch_assert(const char *file, int line)
{
    rt_kprintf("\nAssertion: %d in %s, thread %s\n",
               line, file, rt_thread_self()->name);
    RT_ASSERT(0);
}

u32_t sys_jiffies(void)
{
    return rt_tick_get();
}

#endif /* !NO_SYS */

将 ethernetif.c 文件中有关信号量、线程等操作的部分,全部改成RT-Thread的实现方式

 

 

在  low_level_output() 函数中添加SCB_CleanInvalidateDCache();

如下图所示:

low_level_input() 函数中添加 SCB_CleanInvalidateDCache();

如下图所示:

 

 修改 lwip.c 文件

 

 六、修改 main.c 添加LWIP初始化函数

 若使用 AC6编译器,暂时不让 slipif.c 这个文件参与编译

 

 

 完。

注意重点

1.  在  low_level_output()  low_level_input() 函数中添加SCB_CleanInvalidateDCache();

2.  DP83848的PHY地址是在PHYCR(0x19)这个寄存器中,与LAN8742不一样,在LAN8742_Init()函数中改成读取PHYCR(0x19)寄存器。

当然我们也可以在程序手动赋值PHY地址,免去循环读取的步骤。

3.  由于CubeMX只能生成LAN8742的phy代码, 里面涉及到一些扩展寄存器,

一定要修改 LAN8742_GetLinkState(),这个函数是获取当前的以太网链路状态,由于DP83848和LAN8742的扩展寄存器不一样,所以必须修改成DP84848的寄存器(PHYSTS,0x10),不然当网线热插拔时LWIP就会卡死。

凡是涉及到扩展寄存器的,LAN8742和DP83848有所不同,可以自己修改相关的函数。

当网线热插拔时,可以在ethernet_link_status_updated()函数里添加一些自己想要的功能。

4.  LWIP的内存池和内存堆不能放在DTCM中,因为ETH DMA无法访问DTCM。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32H723 + DP83848 + LWIP + RT-Thread(FreeRTOS) + STM32CubeMX + Keil MDK 超详细 的相关文章

  • 【FreeRTOS(三)】任务状态

    文章目录 任务状态 任务挂起 vTaskSuspend 取消任务挂起 vTaskResume 挂起任务调度器 vTaskSuspendAll 取消挂起任务调度器 xTaskResumeAll 代码示例 任务挂起 取消任务挂起 代码示例 挂起
  • freeRTOS使用uxTaskGetStackHighWaterMark函数查看任务堆栈空间的使用情况

    摘要 每个任务都有自己的堆栈 堆栈的总大小在创建任务的时候就确定了 此函数用于检查任务从创建好到现在的历史剩余最小值 这个值越小说明任务堆栈溢出的可能性就越大 FreeRTOS 把这个历史剩余最小值叫做 高水位线 此函数相对来说会多耗费一点
  • FreeRTOS_中断

    传送门 博客汇总帖 传送门 Cortex M3 中断 异常 传送门 Cortex M3笔记 基础 笔记内容参考 正点原子的FreeRTOS开发手册 cortex m3权威指南 Cortex M3和Cortex M4权威指南等 文中stm32
  • Error: L6218E: Undefined symbol vApplicationGetIdleTaskMemory (referred from tasks.o).

    我用的是F103ZET6的板子 移植成功后 编译出现两个错误是关于stm32f10x it c 里 void SVC Handler void void PendSV Handler void 两个函数的占用问题 随后编译出现以下两个问题
  • surfaceDestroyed什么时候被调用

    今天看别人的代码 突然有个疑问 surfaceDestroyed这个函数什么时候被调用呢 上网搜了一番 基本都说是surface被销毁的时候 才会调用surfaceDestroyed 问题又来了surface什么时候被销毁呢 大家都知道su
  • 【LWIP】stm32用CubeMX(6.4版)配置LwIP+Ping+TCPclient+TCPserver发送信息到PC(操作部分)

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 零 更新 2022 08 07 一 实验平台 二 手把手全程配置步骤 1 配置电脑环境 2 配置cubeMX 3 配置MDK Keil5 4 配置TCPcli
  • FreeRTOS:中断配置

    目录 一 Cortex M 中断 1 1中断简介 1 2中断管理简介 1 3优先级分组定义 1 4优先级设置 1 5用于中断屏蔽的特殊寄存器 1 5 1PRIMASK 和 FAULTMASK 寄存器 1 5 2BASEPRI 寄存器 二 F
  • 关于RT-Thread中优先级翻转问题的简记

    最近在学习RT Thread的相关知识 记录一下心得 优先级翻转 是指当一个高优先级线程试图通过信号量机制访问共享资源时 如果该信号量已被低优先级线程持有 而这个低优先级线程在运行过程中可能又被其他一些中等优先级的线程抢占 从而造成高优先级
  • 实时系统RTX之理解一

    文献来源 http wzhyblog yo2 cn articles e5 ae 9e e6 97 b6 e7 b3 bb e7 bb 9frtx e5 ae 98 e6 96 b9 e6 96 87 e6 a1 a3 e4 b8 ad e
  • Qt之QThread详解

    一 线程管理 1 线程启动 void start Priority priority InheritPriority 调用后会执行run 函数 但在run 函数执行前会发射信号started 操作系统将根据优先级参数调度线程 如果线程已经在
  • stm32f103zet6移植标准库的sdio驱动

    sdio移植 st官网给的标准库有给一个用于st出的评估板的sdio外设实现 但一是文件结构有点复杂 二是相比于国内正点原子和野火的板子也有点不同 因此还是需要移植下才能使用 当然也可以直接使用正点原子或野火提供的实例 但为了熟悉下sdio
  • FreeRTOS学习笔记(8)---- 软件定时器

    使用FreeRTOS软件定时器需要在文件FreeRTOSConfig h先做如下配置 1 configUSE TIMERS 使能软件定时器 2 configTIMER TASK PRIORITY 定时器任务优先级 3 configTIMER
  • 多核编程学习笔记之OpenMP(一)

    多核编程学习笔记之OpenMP 一 I 配置及简介 1 1 在VC 2008 VC9 0 中 如果没有任何设置 在代码中使用编译指导语句将不会报错 但是也不起作用 1 2 OpenMP发展与优势 1 2 1 OpemMP的规范由SGI发起
  • freeRTOS出现任务卡死的情况。

    最近在做一个产品二代升级的项目 代码是上一任工程师留下的 很多BUG 而且融合了HAL库和LL库 以及github上下载的GSM源码 很不好用 我这边是将2G模块换成了4G 且添加了单独的BLE模块 因此只在源码的基础上 去除2G和BLE代
  • FreeRTOS轻量级同步--任务通知

    1 简介 在FreeRTOS的配置参数中的configUSE TASK NOTIFICATIONS宏打开 一般RTOS会默认打开 如图1所示 图1 notify宏开关 RTOS在创建任务时 会创建一个32位的通知值ulNotifiedVal
  • thread_Timer(线程中定时器)

    package com gzhs zsd thread import java util Date import java util Timer import java util TimerTask Timer定时器运用 author 谢泽
  • FreeRTOS之系统配置

    1 FreeRTOS的系统配置文件为FreeRTOSConfig h 在此配置文件中可以完成FreeRTOS的裁剪和配置 在官方的demo中 每个工程都有一个该文件 2 先说一下 INCLUDE 开始的宏 使用 INCLUDE 开头的宏用来
  • QT实现多线程,以及子线程调用主线程方法与变量

    实现思路 第一步需要将子线程声明为主线程的友元类 第二步是将主线程类对象的地址通过信号槽传递给子线程中创建的对象 使得子线程能访问主线程的数据的 1 子线程 displayresult h 头文件 伪代码 include tabwindow
  • C++ freeRTOS任务,非静态成员函数的无效使用

    哪里有问题 void MyClass task void pvParameter while 1 this gt update void MyClass startTask xTaskCreate this gt task Task 204
  • NTP请求包

    我试图弄清楚我需要在 NTP 请求包中发送 客户端 什么才能从服务器检索 NTP 包 我正在 Cortex M3 Stellaris LM3S6965 上使用 LWIP 据我了解 我将收到 UDP 标头 然后收到具有不同时间戳的 NTP 协

随机推荐