工程环境:
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()函数里添加一些自己想要的功能。文章来源:https://www.toymoban.com/news/detail-640376.html
4. LWIP的内存池和内存堆不能放在DTCM中,因为ETH DMA无法访问DTCM。文章来源地址https://www.toymoban.com/news/detail-640376.html
到了这里,关于STM32H723 + DP83848 + LWIP + RT-Thread(FreeRTOS) + STM32CubeMX + Keil MDK 超详细的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!