stm32h743vit6+lwip+freertos+lan8720/yt8521+sai+uart1、2、7+AC6编译
本文总结了STM32H7系列基于LWIP和YT8521/YT8512C以太网PHY芯片的开发经验,主要包括以下关键内容: 问题排查与优化 发现Ping响应延迟10ms的问题,定位为LWIP调试日志未关闭导致打印占用时间 正常Ping响应应控制在1ms以内 MPU配置关键点 内存区域划分与Cache策略直接影响系统稳定性 提供了详细的MPU配置方案,确保不出现Hardfault 强调SRAM3区域不
下面是mpu的配置,如果和cubemx有出入按照代码走,代码已经跑通了1.先记一下发现的bug,ping响应时间很长,大概在10ms,原因是打印太多了,lwip的icmp或者其他的debug日志开启后没有关,在ping的时候会造成打印占用回复时间,导致延迟大于10ms,正常应该是1ms。
2.mpu配置会直接影响到你的运行是否hardfault、串口接收的数据和ethnet是否正常。下面我会贴上sct文件的分配写法,以及mpu的配置文件。
3.yt8521和lan8720并不一样,寄存器配置我也会在下面贴出来。
首先是cubemx配置
中断优先级我这里忘记改了,你们自己按工程需要改吧
串口正常和别的板子没区别,图不贴了,注意一个问题,如果晶振频率不同需要改,之前用在开发板验证的程序,晶振频率差1Mhz导致串口数据发送正确,但是接收有时候正确有时候出错,查了一下开发板晶振和画的板子不通导致这个问题。
sram1、2、3都是D2区,那个都可以我这里放到了SRAM3中,SRAM3中mpu配置也不能使能cache访问,否则会出错
接收缓冲区cubemx配置是在0x30040200,但是他这个生成出来之后是ac5的编译器配置,使用ac6时是需要自己重新分配的,否则会从0x24000000开始,这个你们可以从生成的map中搜一下,这个rxbuffers确实在0x24000000 512kb这个区。
sai我是双声道,16bit的i2s,256khz,8k的采样率,连了一个模块,我stm32h7做从设备,sai的配置有个坑是在代码里,采样的数量那块在我另外一个帖子写了,不懂可以看那个帖子
我是直连电脑,电脑的ip给了188.3,板子是188.6,这个不能相同
没贴出的都是默认的
时钟我是24m的,按自己的配置
这是sct文件,我是和mpu的区域配置是相符的,里面的注释里,区域是对的,注释的内容可能有误,后续我也是边实验边改,注释可能忘了改了,按照代码的为准,
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00100000 { ; 1MB Flash (加载区域)
ER_IROM1 0x08000000 0x00100000 { ; 执行地址 = 加载地址
*.o(RESET, +First) ; 中断向量表优先
*(InRoot$$Sections) ; ARM 库的特殊段
.ANY(+RO) ; 所有只读数据
.ANY(+XO) ; 所有可执行代码
}
;-------------------- DTCM (128KB) ----------------------
RW_IRAM1 0x20000000 0x00020000 { ; DTCM - 高速数据内存
.ANY(+RW +ZI) ; 全部RW/ZI数据(默认)
;*(.DTCM_Section) ; 手动分配的关键数据
}
;-------------------- AXI SRAM (512 KB) ------------------
RW_IRAM2 0x24000000 0x00040000 { ; AXI RAM - 主内存区1 D1区,cache使能,dma关闭
.ANY(+RW +ZI) ; 其余RW/ZI数据
}
RW_IRAM3 0x24040000 0x00040000 { ; AXI RAM - 主内存区2 D1区,cache关闭,dma使能
.ANY(+RW +ZI) ; 其余RW/ZI数据
}
;-------------------- SRAM1 (128KB) ----------------------
RW_DMARxDscrTab 0x30000000 0x20000 {; AHB RAM - 主内存区3 D2区 ,cache使能,dma关闭
.ANY(+RW +ZI) ; 其余RW/ZI数据
}
;-------------------- SRAM2 (128KB) ----------------------
RW_DMATxDscrTab 0x30020000 0x20000{ ;AHB RAM - 主内存区3 D2区 ,cache使能,dma关闭
.ANY(+RW +ZI) ; 其余RW/ZI数据
ethernetif.o(.bss.memp_memory_RX_POOL_base); /* RX缓冲区 */
}
;-------------------- SRAM3 (32KB) ----------------------
RW_IRAM4 0x30040000 0x8000 {;AHB RAM - 主内存区3 D2区 用于以太网和USB缓冲
;. = ALIGN(32); /* 32字节对齐 */
/* LWIP 内存池 */
*(.RxDecripSection) /* 接收描述符 */
*(.TxDecripSection) /* 发送描述符 */
}
;-------------------- SRAM4 (64KB) ----------------------
RW_IRAM6 0x38000000 0x10000 {;AHB RAM - 主内存区3 D3区
.ANY(+RW +ZI)
}
;-------------------- 添加备份SRAM (4KB) -----------------
RW_BKPSRAM 0x38800000 0x00001000 { ; AHB RAM - 主内存区3 D3区,备份域内存
.ANY(+RW +ZI)
}
}
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
#include "dma.h"
#include "lwip.h"
#include "sai.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MPU_Config(void);
void MX_FREERTOS_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* �ض���fputc���� */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void) {
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* Enable the CPU Cache */
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick.
*/
HAL_Init();
/* USER CODE BEGIN Init */
/* Enable D2 domain SRAM3 Clock (0x30040000 AXI)*/
/* 手动启用 D2 域 SRAM 时钟 */
__HAL_RCC_D2SRAM3_CLK_ENABLE(); // SRAM3 (0x30040000)
__HAL_RCC_D2SRAM1_CLK_ENABLE(); // SRAM1 (0x30000000)
__HAL_RCC_D2SRAM2_CLK_ENABLE(); // SRAM2 (0x30020000)
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
MX_SAI1_Init();
MX_UART7_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Init scheduler */
osKernelInitialize(); /* Call init function for freertos objects (in
cmsis_os2.c) */
MX_FREERTOS_Init();
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 2;
RCC_OscInitStruct.PLL.PLLN = 80;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 7;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2 |
RCC_CLOCKTYPE_D3PCLK1 | RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
// MPU_TEX_LEVEL1时,更高性能但需要显式缓存维护,DMA 访问必须手动清理/失效缓存
//开了MPU_ACCESS_SHAREABLE,MPU_ACCESS_CACHEABLE,MPU_ACCESS_BUFFERABLE之后,串口接收到的是最新值
//关了MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_NOT_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE之后,串口接收到的是最新值
// MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE之后,直接hardfault
// MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_NOT_CACHEABLE,MPU_ACCESS_BUFFERABLE,直接hardfault
// MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_CACHEABLE,MPU_ACCESS_BUFFERABLE,只有第一次能接收到值,之后就是0,但是长度可以接收到,中断可以触发
// MPU_ACCESS_SHAREABLE,MPU_ACCESS_NOT_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE,串口接收到的是最新值,
//开了MPU_ACCESS_SHAREABLE,MPU_ACCESS_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE之后,直接hardfault
//
// MPU_TEX_LEVEL0时,写直达,即写cache又写内存,适合简单应用,硬件自动维护缓存一致性DMA,访问无需软件干预
//开了MPU_ACCESS_SHAREABLE,MPU_ACCESS_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE之后,可以收到最新数据
//关了MPU_ACCESS_NOT_SHAREABLE,MPU_ACCESS_NOT_CACHEABLE,MPU_ACCESS_NOT_BUFFERABLE之后,hardfault
/* USER CODE END 4 */
/* MPU Configuration */
void MPU_Config(void) {
MPU_Region_InitTypeDef MPU_InitStruct = {0};
/* Disables the MPU */
HAL_MPU_Disable();
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Enable = MPU_REGION_ENABLE;
MPU_InitStruct.Number = MPU_REGION_NUMBER0;
MPU_InitStruct.BaseAddress = 0x24000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_512KB;
MPU_InitStruct.SubRegionDisable = 0x00;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER1;
MPU_InitStruct.BaseAddress = 0x30000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
MPU_InitStruct.SubRegionDisable = 0x0;
MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER2;
MPU_InitStruct.BaseAddress = 0x30040000;
MPU_InitStruct.Size = MPU_REGION_SIZE_32KB;
MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
// MPU_InitStruct.Number = MPU_REGION_NUMBER3;
// MPU_InitStruct.BaseAddress = 0x30044000;
// MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
// MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
// MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
// MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
// MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
// HAL_MPU_ConfigRegion(&MPU_InitStruct);
/** Initializes and configures the Region and the memory to be protected
*/
MPU_InitStruct.Number = MPU_REGION_NUMBER4;
MPU_InitStruct.BaseAddress = 0x38000000;
MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;
MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE;
MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_InitStruct);
/* Enables the MPU */
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM1 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void) {
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1) {
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line) {
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line
number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
main.c 是mpu的配置,如果和cubemx有出入按照代码走,代码已经跑通了,注意需要加上这几个时钟的开启
下面是yt8512h的驱动代码,用的是yt8512c的寄存器,不影响,都通用的
/* Includes ------------------------------------------------------------------*/
#include "yt8521h.h"
#include "user_log.h"
#define YT8512C_SW_RESET_TO ((uint32_t)500U) /* 软件复位等待时间 */
#define YT8512C_INIT_TO ((uint32_t)2000U) /* 初始化等待时间 */
#define YT8512C_MAX_DEV_ADDR ((uint32_t)31U) /* PHY地址的最大值 */
#define YT8512C_AND_RTL8201BL_PHYREGISTER2 0x0000
/**
* @brief 将IO函数注册到组件对象
* @param pobj:设备对象
* @param ioctx:保存设备IO功能
* @retval YT8512C_STATUS_OK:OK
* YT8512C_STATUS_ERROR:缺少功能
*/
int32_t yt8512c_regster_bus_io(yt8512c_object_t *pobj,
yt8512c_ioc_tx_t *ioctx) {
if (!pobj || !ioctx->readreg || !ioctx->writereg || !ioctx->gettick) {
return YT8512C_STATUS_ERROR;
}
pobj->io.init = ioctx->init;
pobj->io.deinit = ioctx->deinit;
pobj->io.readreg = ioctx->readreg;
pobj->io.writereg = ioctx->writereg;
pobj->io.gettick = ioctx->gettick;
return YT8512C_STATUS_OK;
}
/**
* @brief 初始化YT8512C并配置所需的硬件资源
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:初始化YT8512C并配置所需的硬件资源成功
YT8512C_STATUS_ADDRESS_ERROR:找不到设备地址
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写入寄存器
YT8512C_STATUS_RESET_TIMEOUT:无法执行软件复位
*/
int32_t yt8512c_init(yt8512c_object_t *pobj) {
uint32_t tickstart = 0, regvalue = 0, addr = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->is_initialized == 0) {
if (pobj->io.init != 0) {
/* MDC时钟 */
pobj->io.init();
}
/* 设置PHY地址为32 */
pobj->devaddr = YT8512C_MAX_DEV_ADDR + 1;
/* 主要为了查找PHY地址 */
for (addr = 0; addr <= YT8512C_MAX_DEV_ADDR; addr++) {
if (pobj->io.readreg(addr, YT8512C_PHYSCSR, ®value) < 0) {
status = YT8512C_STATUS_READ_ERROR;
/* 无法读取这个设备地址继续下一个地址 */
continue;
}
/* 已经找到PHY地址了 */
if ((regvalue & YT8512C_PHY_COUNT) == addr) {
pobj->devaddr = addr;
status = YT8512C_STATUS_OK;
break;
}
}
/* 判断这个PHY地址是否大于32(2^5)*/
if (pobj->devaddr > YT8512C_MAX_DEV_ADDR) {
status = YT8512C_STATUS_ADDRESS_ERROR;
}
/* 如果PHY地址有效 */
if (status == YT8512C_STATUS_OK) {
/* 设置软件复位 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR,
YT8512C_BCR_SOFT_RESET) >= 0) {
/* 获取软件重置状态 */
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, ®value) >= 0) {
tickstart = pobj->io.gettick();
/* 等待软件复位完成或超时 */
while (regvalue & YT8512C_BCR_SOFT_RESET) {
if ((pobj->io.gettick() - tickstart) <= YT8512C_SW_RESET_TO) {
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, ®value) < 0) {
status = YT8512C_STATUS_READ_ERROR;
break;
}
} else {
status = YT8512C_STATUS_RESET_TIMEOUT;
break;
}
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
} else {
status = YT8512C_STATUS_WRITE_ERROR;
}
}
}
/* 到了这里,初始化完成!!! */
if (status == YT8512C_STATUS_OK) {
tickstart = pobj->io.gettick();
/* 等待2s进行初始化 */
while ((pobj->io.gettick() - tickstart) <= YT8512C_INIT_TO) {
}
pobj->is_initialized = 1;
}
return status;
}
/**
* @brief 反初始化YT8512C及其硬件资源
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:反初始化失败成功
YT8512C_STATUS_ERROR:反初始化失败
*/
int32_t yt8512c_deinit(yt8512c_object_t *pobj) {
if (pobj->is_initialized) {
if (pobj->io.deinit != 0) {
if (pobj->io.deinit() < 0) {
return YT8512C_STATUS_ERROR;
}
}
pobj->is_initialized = 0;
}
return YT8512C_STATUS_OK;
}
/**
* @brief 关闭YT8512C的下电模式
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_disable_power_down_mode(yt8512c_object_t *pobj) {
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0) {
readval &= ~YT8512C_BCR_POWER_DOWN;
/* 清除下电模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 使能YT8512C的下电模式
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_enable_power_down_mode(yt8512c_object_t *pobj) {
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0) {
readval |= YT8512C_BCR_POWER_DOWN;
/* 使能下电模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 启动自动协商过程
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_start_auto_nego(yt8512c_object_t *pobj) {
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0) {
readval |= YT8512C_BCR_AUTONEGO_EN;
/* 启动自动协商 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 获取YT8512C设备的链路状态
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_100MBITS_FULLDUPLEX:100M,全双工
YT8512C_STATUS_100MBITS_HALFDUPLEX :100M,半双工
YT8512C_STATUS_10MBITS_FULLDUPLEX:10M,全双工
YT8512C_STATUS_10MBITS_HALFDUPLEX :10M,半双工
YT8512C_STATUS_READ_ERROR:不能读取寄存器
*/
int32_t yt8512c_get_link_state(yt8512c_object_t *pobj) {
uint32_t readval = 0;
/* 检测特殊功能寄存器链接值 */
if (pobj->io.readreg(pobj->devaddr, YT8512C_PHYSCSR, &readval) < 0) {
log_error("Failed to read YT8512C_PHYSCSR register\r\n");
return YT8512C_STATUS_READ_ERROR;
}
//log_debug("YT8512C_PHYSCSR = %08x\n", readval);
/* 1. 检查链路状态 (Bit 10) */
if (!(readval & (1 << 10))) {
log_debug("Link is DOWN\r\n");
return YT8512C_STATUS_LINK_DOWN;
}
//log_debug("Link is UP");
/* 2. 检查状态是否已解析 (Bit 11) */
if (!(readval & (1 << 11))) {
log_debug("Speed/Duplex not resolved");
return YT8512C_STATUS_AUTONEGO_NOTDONE;
}
/* 3. 提取速度模式 (Bits 15:14) */
uint8_t speed_mode = (readval >> 14) & 0x03;
/* 4. 提取双工模式 (Bit 13) */
uint8_t duplex_mode = (readval >> 13) & 0x01;
//log_debug("Speed mode: %d, Duplex mode: %d", speed_mode, duplex_mode);
/* 5. 判断具体链路状态 */
switch (speed_mode) {
case 0x00: // 10 Mbps
if (duplex_mode) {
return YT8512C_STATUS_10MBITS_FULLDUPLEX;
} else {
return YT8512C_STATUS_10MBITS_HALFDUPLEX;
}
case 0x01: // 100 Mbps
if (duplex_mode) {
return YT8512C_STATUS_100MBITS_FULLDUPLEX;
} else {
return YT8512C_STATUS_100MBITS_HALFDUPLEX;
}
case 0x02: // 1000 Mbps
// 根据您的状态宏定义,这里可能需要扩展
log_warning("1000Mbps mode detected, returning as 1000M Full");
return YT8512C_STATUS_1000MBITS_FULLDUPLEX;
case 0x03: // Reserved
default:
log_error("Invalid speed mode: 0x%02X", speed_mode);
return YT8512C_STATUS_ERROR;
}
}
/**
* @brief 设置YT8512C设备的链路状态
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_ERROR :设置失败
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_set_link_state(yt8512c_object_t *pobj, uint32_t linkstate) {
uint32_t bcrvalue = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &bcrvalue) >= 0) {
/* 禁用链路配置(自动协商,速度和双工) */
bcrvalue &= ~(YT8512C_BCR_AUTONEGO_EN | YT8512C_BCR_SPEED_SELECT |
YT8512C_BCR_DUPLEX_MODE);
if (linkstate == YT8512C_STATUS_100MBITS_FULLDUPLEX) {
bcrvalue |= (YT8512C_BCR_SPEED_SELECT | YT8512C_BCR_DUPLEX_MODE);
} else if (linkstate == YT8512C_STATUS_100MBITS_HALFDUPLEX) {
bcrvalue |= YT8512C_BCR_SPEED_SELECT;
} else if (linkstate == YT8512C_STATUS_10MBITS_FULLDUPLEX) {
bcrvalue |= YT8512C_BCR_DUPLEX_MODE;
} else {
/* 错误的链路状态参数 */
status = YT8512C_STATUS_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
if (status == YT8512C_STATUS_OK) {
/* 写入链路状态 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, bcrvalue) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
}
return status;
}
/**
* @brief 启用环回模式
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_enable_loop_back_mode(yt8512c_object_t *pobj) {
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0) {
readval |= YT8512C_BCR_LOOPBACK;
/* 启用环回模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 禁用环回模式
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_disable_loop_back_mode(yt8512c_object_t *pobj) {
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0) {
readval &= ~YT8512C_BCR_LOOPBACK;
/* 禁用环回模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0) {
status = YT8512C_STATUS_WRITE_ERROR;
}
} else {
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef YT8512C_H
#define YT8512C_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include "global.h"
/* PHY芯片寄存器映射表 */
#define YT8512C_BCR ((uint16_t)0x0000U)
#define YT8512C_BSR ((uint16_t)0x0001U)
#define PHY_REGISTER2 ((uint16_t)0x0002U)
#define PHY_REGISTER3 ((uint16_t)0x0003U)
#define YT8512C_PHYI1R ((uint16_t)0x0002U)
#define YT8512C_PHYI2R ((uint16_t)0x0003U)
/* 操作SCR寄存器的值(一般不需要修改) */
#define YT8512C_BCR_SOFT_RESET ((uint16_t)0x8000U)
#define YT8512C_BCR_LOOPBACK ((uint16_t)0x4000U)
#define YT8512C_BCR_SPEED_SELECT ((uint16_t)0x2000U)
#define YT8512C_BCR_AUTONEGO_EN ((uint16_t)0x1000U)
#define YT8512C_BCR_POWER_DOWN ((uint16_t)0x0800U)
#define YT8512C_BCR_ISOLATE ((uint16_t)0x0400U)
#define YT8512C_BCR_RESTART_AUTONEGO ((uint16_t)0x0200U)
#define YT8512C_BCR_DUPLEX_MODE ((uint16_t)0x0100U)
/* 操作BSR寄存器的值(一般不需要修改) */
#define YT8512C_BSR_100BASE_T4 ((uint16_t)0x8000U)
#define YT8512C_BSR_100BASE_TX_FD ((uint16_t)0x4000U)
#define YT8512C_BSR_100BASE_TX_HD ((uint16_t)0x2000U)
#define YT8512C_BSR_10BASE_T_FD ((uint16_t)0x1000U)
#define YT8512C_BSR_10BASE_T_HD ((uint16_t)0x0800U)
#define YT8512C_BSR_100BASE_T2_FD ((uint16_t)0x0400U)
#define YT8512C_BSR_100BASE_T2_HD ((uint16_t)0x0200U)
#define YT8512C_BSR_EXTENDED_STATUS ((uint16_t)0x0100U)
#define YT8512C_BSR_AUTONEGO_CPLT ((uint16_t)0x0020U)
#define YT8512C_BSR_REMOTE_FAULT ((uint16_t)0x0010U)
#define YT8512C_BSR_AUTONEGO_ABILITY ((uint16_t)0x0008U)
#define YT8512C_BSR_LINK_STATUS ((uint16_t)0x0004U)
#define YT8512C_BSR_JABBER_DETECT ((uint16_t)0x0002U)
#define YT8512C_BSR_EXTENDED_CAP ((uint16_t)0x0001U)
/* PHY芯片进程状态 */
#define YT8512C_STATUS_READ_ERROR ((int32_t)-5)
#define YT8512C_STATUS_WRITE_ERROR ((int32_t)-4)
#define YT8512C_STATUS_ADDRESS_ERROR ((int32_t)-3)
#define YT8512C_STATUS_RESET_TIMEOUT ((int32_t)-2)
#define YT8512C_STATUS_ERROR ((int32_t)-1)
#define YT8512C_STATUS_OK ((int32_t) 0)
#define YT8512C_STATUS_LINK_DOWN ((int32_t) 1)
#define YT8512C_STATUS_100MBITS_FULLDUPLEX ((int32_t) 2)
#define YT8512C_STATUS_100MBITS_HALFDUPLEX ((int32_t) 3)
#define YT8512C_STATUS_10MBITS_FULLDUPLEX ((int32_t) 4)
#define YT8512C_STATUS_10MBITS_HALFDUPLEX ((int32_t) 5)
#define YT8512C_STATUS_AUTONEGO_NOTDONE ((int32_t) 6)
#define YT8512C_STATUS_1000MBITS_FULLDUPLEX ((int32_t) 7)
/* PHY地址 ---- 由用户设置 */
#define YT8512C_ADDR ((uint16_t)0x0000U)
/* PHY寄存器的数量 */
#define YT8512C_PHY_COUNT ((uint16_t)0x001FU)
#define YT8512C_PHYSCSR ((uint16_t)0x11) /*!< tranceiver status register */
#define YT8512C_SPEED_STATUS ((uint16_t)0x4010) /*!< configured information of speed: 100Mbit/s */
#define YT8512C_DUPLEX_STATUS ((uint16_t)0x2000) /*!< configured information of duplex: full-duplex */
#define YT8512C_PHY_COUNT ((uint16_t)0x001FU)
#define YT8512C_PHYSCSR ((uint16_t)0x11) /*!< tranceiver status register */
#define YT8512C_SPEED_STATUS ((uint16_t)0x4010) /*!< configured information of speed: 100Mbit/s */
#define YT8512C_DUPLEX_STATUS ((uint16_t)0x2000) /*!< configured information of duplex: full-duplex */
/* 定义函数指针 */
typedef int32_t (*yt8512c_init_func) (void);
typedef int32_t (*yt8512c_deinit_func) (void);
typedef int32_t (*yt8512c_readreg_func) (uint32_t, uint32_t, uint32_t *);
typedef int32_t (*yt8512c_writereg_func) (uint32_t, uint32_t, uint32_t);
typedef int32_t (*yt8512c_gettick_func) (void);
/* PHY共用函数结构体 */
typedef struct
{
yt8512c_init_func init; /* 指向PHY初始化函数 */
yt8512c_deinit_func deinit; /* 指向PHY反初始化函数 */
yt8512c_writereg_func writereg; /* 指向PHY写寄存器函数 */
yt8512c_readreg_func readreg; /* 指向PHY读寄存器函数 */
yt8512c_gettick_func gettick; /* 指向节拍函数 */
} yt8512c_ioc_tx_t;
/* 注册到组件对象结构体 */
typedef struct
{
uint32_t devaddr; /* PHY地址 */
uint32_t is_initialized; /* 描述该设备是否初始化 */
yt8512c_ioc_tx_t io; /* 设备调用的函数入口 */
void *pdata; /* 传入的形参 */
}yt8512c_object_t;
int32_t yt8512c_regster_bus_io(yt8512c_object_t *pobj, yt8512c_ioc_tx_t *ioctx); /* 将IO函数注册到组件对象 */
int32_t yt8512c_init(yt8512c_object_t *pobj); /* 初始化YT8512C并配置所需的硬件资源 */
int32_t yt8512c_deinit(yt8512c_object_t *pobj); /* 反初始化YT8512C及其硬件资源 */
int32_t yt8512c_disable_power_down_mode(yt8512c_object_t *pobj); /* 关闭YT8512C的下电模式 */
int32_t yt8512c_enable_power_down_mode(yt8512c_object_t *pobj); /* 使能YT8512C的下电模式 */
int32_t yt8512c_start_auto_nego(yt8512c_object_t *pobj); /* 启动自动协商过程 */
int32_t yt8512c_get_link_state(yt8512c_object_t *pobj); /* 获取YT8512C设备的链路状态 */
int32_t yt8512c_set_link_state(yt8512c_object_t *pobj, uint32_t linkstate); /* 设置YT8512C设备的链路状态 */
int32_t yt8512c_enable_loop_back_mode(yt8512c_object_t *pobj); /* 启用环回模式 */
int32_t yt8512c_disable_loop_back_mode(yt8512c_object_t *pobj); /* 禁用环回模式 */
#ifdef __cplusplus
}
#endif
#endif /* YT8512C_H */
然后如果用ac6的话,需要修改这ethernetif.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : ethernetif.c
* Description : This file provides code for the configuration
* of the ethernetif.c MiddleWare.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/opt.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "ethernetif.h"
#include "lan8742.h"
#include <string.h>
#include "cmsis_os.h"
#include "lwip/tcpip.h"
/* Within 'USER CODE' section, code will be kept by default at each generation
*/
/* USER CODE BEGIN 0 */
#include "yt8521h.h"
#include "stdio.h"
#include "user_udp.h"
#include "user_log.h"
/* ETH_MACMDIOAR 寄存器位定义 */
#define ETH_MACMDIOAR_GB_Pos (0U)
#define ETH_MACMDIOAR_GB_Msk (0x1UL << ETH_MACMDIOAR_GB_Pos) /*!< 0x00000001 \
*/
#define ETH_MACMDIOAR_GB ETH_MACMDIOAR_GB_Msk /*!< MII Busy */
/* 其他自定义定义 */
#define PHY_YT8521H_ADDR 0x01 // YT8521H PHY地址
/* USER CODE END 0 */
/* Private define ------------------------------------------------------------*/
/* The time to block waiting for input. */
#define TIME_WAITING_FOR_INPUT (portMAX_DELAY)
/* Time to block waiting for transmissions to finish */
#define ETHIF_TX_TIMEOUT (2000U)
/* USER CODE BEGIN OS_THREAD_STACK_SIZE_WITH_RTOS */
/* Stack size of the interface thread */
#define INTERFACE_THREAD_STACK_SIZE (1024)
/* USER CODE END OS_THREAD_STACK_SIZE_WITH_RTOS */
/* Network interface name */
#define IFNAME0 's'
#define IFNAME1 't'
/* ETH Setting */
#define ETH_DMA_TRANSMIT_TIMEOUT (20U)
#define ETH_TX_BUFFER_MAX ((ETH_TX_DESC_CNT)*2U)
/* ETH_RX_BUFFER_SIZE parameter is defined in lwipopts.h */
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Private variables ---------------------------------------------------------*/
/*
@Note: This interface is implemented to operate in zero-copy mode only:
- Rx Buffers will be allocated from LwIP stack Rx memory pool,
then passed to ETH HAL driver.
- Tx Buffers will be allocated from LwIP stack memory heap,
then passed to ETH HAL driver.
@Notes:
1.a. ETH DMA Rx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_RX_DESC_CNT in ETH GUI (Rx Descriptor
Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
1.b. ETH DMA Tx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_TX_DESC_CNT in ETH GUI (Tx Descriptor
Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
2.a. Rx Buffers number must be between ETH_RX_DESC_CNT and 2*ETH_RX_DESC_CNT
2.b. Rx Buffers must have the same size: ETH_RX_BUFFER_SIZE, this value must
passed to ETH DMA in the init field (heth.Init.RxBuffLen)
2.c The RX Ruffers addresses and sizes must be properly defined to be aligned
to L1-CACHE line size (32 bytes).
*/
/* Data Type Definitions */
typedef enum { RX_ALLOC_OK = 0x00, RX_ALLOC_ERROR = 0x01 } RxAllocStatusTypeDef;
typedef struct {
struct pbuf_custom pbuf_custom;
uint8_t buff[(ETH_RX_BUFFER_SIZE + 31) & ~31] __ALIGNED(32);
} RxBuff_t;
/* Memory Pool Declaration */
#define ETH_RX_BUFFER_CNT 12U
LWIP_MEMPOOL_DECLARE(RX_POOL, ETH_RX_BUFFER_CNT, sizeof(RxBuff_t),
"Zero-copy RX PBUF pool");
/* Variable Definitions */
static uint8_t RxAllocStatus;
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma location = 0x30040000
ETH_DMADescTypeDef
DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
#pragma location = 0x30040060
ETH_DMADescTypeDef
DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
#elif defined(__CC_ARM) /* MDK ARM Compiler */
__attribute__((at(0x30040000))) ETH_DMADescTypeDef
DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
__attribute__((at(0x30040060))) ETH_DMADescTypeDef
DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
#elif defined(__GNUC__) /* GNU Compiler */
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((
section(".RxDecripSection"))); /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((
section(".TxDecripSection"))); /* Ethernet Tx DMA Descriptors */
// ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]
// __attribute__((section(".bss.ARM.__at_0x30040000"))); /* Ethernet Rx DMA
// Descriptors */
// ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]
// __attribute__((section(".bss.ARM.__at_0x30040060"))); /* Ethernet Tx DMA
// Descriptors */
#endif
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma location = 0x30040200
extern u8_t memp_memory_RX_POOL_base[];
#elif defined(__CC_ARM) /* MDK ARM Compiler */
__attribute__((
section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
#elif defined(__GNUC__) /* GNU */
extern u8_t memp_memory_RX_POOL_base[];
#endif
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
osSemaphoreId RxPktSemaphore = NULL; /* Semaphore to signal incoming packets */
osSemaphoreId TxPktSemaphore =
NULL; /* Semaphore to signal transmit packet complete */
/* Global Ethernet handle */
ETH_HandleTypeDef heth;
ETH_TxPacketConfig TxConfig;
/* Private function prototypes -----------------------------------------------*/
int32_t ETH_PHY_IO_Init(void);
int32_t ETH_PHY_IO_DeInit(void);
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr,
uint32_t *pRegVal);
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr,
uint32_t RegVal);
int32_t ETH_PHY_IO_GetTick(void);
lan8742_Object_t LAN8742;
lan8742_IOCtx_t LAN8742_IOCtx = {ETH_PHY_IO_Init, ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg, ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick};
/* USER CODE BEGIN 3 */
yt8512c_object_t YT8521H;
yt8512c_ioc_tx_t YT8521H_IOCtx = {ETH_PHY_IO_Init, ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg, ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick};
/* USER CODE END 3 */
/* Private functions ---------------------------------------------------------*/
void pbuf_free_custom(struct pbuf *p);
/**
* @brief Ethernet Rx Transfer completed callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *handlerEth) {
osSemaphoreRelease(RxPktSemaphore);
}
/**
* @brief Ethernet Tx Transfer completed callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_TxCpltCallback(ETH_HandleTypeDef *handlerEth) {
osSemaphoreRelease(TxPktSemaphore);
}
/**
* @brief Ethernet DMA transfer error callback
* @param handlerEth: ETH handler
* @retval None
*/
void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *handlerEth) {
if ((HAL_ETH_GetDMAError(handlerEth) & ETH_DMACSR_RBU) == ETH_DMACSR_RBU) {
osSemaphoreRelease(RxPktSemaphore);
}
}
/* USER CODE BEGIN 4 */
void ETH_PHY_Init(void);
uint8_t Find_PHY_Address(void);
/* USER CODE END 4 */
/*******************************************************************************
LL Driver Interface ( LwIP stack --> ETH)
*******************************************************************************/
/**
* @brief In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif *netif) {
HAL_StatusTypeDef hal_eth_init_status = HAL_OK;
/* USER CODE BEGIN OS_THREAD_ATTR_CMSIS_RTOS_V2 */
osThreadAttr_t attributes;
/* USER CODE END OS_THREAD_ATTR_CMSIS_RTOS_V2 */
uint32_t duplex, speed = 0;
int32_t PHYLinkState = 0;
ETH_MACConfigTypeDef MACConf = {0};
/* Start ETH HAL Init */
uint8_t MACAddr[6];
heth.Instance = ETH;
MACAddr[0] = 0x00;
MACAddr[1] = 0x80;
MACAddr[2] = 0xE1;
MACAddr[3] = 0x00;
MACAddr[4] = 0x00;
MACAddr[5] = 0x00;
heth.Init.MACAddr = &MACAddr[0];
heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
heth.Init.TxDesc = DMATxDscrTab;
heth.Init.RxDesc = DMARxDscrTab;
heth.Init.RxBuffLen = 1536;
/* USER CODE BEGIN MACADDRESS */
HAL_GPIO_WritePin(ETH_RST_GPIO_Port, ETH_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(150);
HAL_GPIO_WritePin(ETH_RST_GPIO_Port, ETH_RST_Pin, GPIO_PIN_SET);
HAL_Delay(150);
// Clean DCache
SCB_CleanInvalidateDCache();
/* USER CODE END MACADDRESS */
hal_eth_init_status = HAL_ETH_Init(&heth);
memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig));
TxConfig.Attributes =
ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
/* End ETH HAL Init */
/* Initialize the RX POOL */
LWIP_MEMPOOL_INIT(RX_POOL);
#if LWIP_ARP || LWIP_ETHERNET
/* set MAC hardware address length */
netif->hwaddr_len = ETH_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = heth.Init.MACAddr[0];
netif->hwaddr[1] = heth.Init.MACAddr[1];
netif->hwaddr[2] = heth.Init.MACAddr[2];
netif->hwaddr[3] = heth.Init.MACAddr[3];
netif->hwaddr[4] = heth.Init.MACAddr[4];
netif->hwaddr[5] = heth.Init.MACAddr[5];
/* maximum transfer unit */
netif->mtu = ETH_MAX_PAYLOAD;
/* Accept broadcast address and ARP traffic */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
#if LWIP_ARP
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */
/* create a binary semaphore used for informing ethernetif of frame reception
*/
RxPktSemaphore = osSemaphoreNew(1, 0, NULL);
/* create a binary semaphore used for informing ethernetif of frame
* transmission */
TxPktSemaphore = osSemaphoreNew(1, 0, NULL);
/* create the task that handles the ETH_MAC */
/* USER CODE BEGIN OS_THREAD_NEW_CMSIS_RTOS_V2 */
memset(&attributes, 0x0, sizeof(osThreadAttr_t));
attributes.name = "EthIf";
attributes.stack_size = INTERFACE_THREAD_STACK_SIZE;
attributes.priority = osPriorityRealtime;
osThreadNew(ethernetif_input, netif, &attributes);
/* USER CODE END OS_THREAD_NEW_CMSIS_RTOS_V2 */
/* USER CODE BEGIN PHY_PRE_CONFIG */
/* USER CODE END PHY_PRE_CONFIG */
/* Set PHY IO functions */
#if 0
LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx);
/* Initialize the LAN8742 ETH PHY */
if(LAN8742_Init(&LAN8742) != LAN8742_STATUS_OK)
{
netif_set_link_down(netif);
netif_set_down(netif);
return;
}
if (hal_eth_init_status == HAL_OK)
{
PHYLinkState = LAN8742_GetLinkState(&LAN8742);
/* Get link state */
if(PHYLinkState <= LAN8742_STATUS_LINK_DOWN)
{
netif_set_link_down(netif);
netif_set_down(netif);
}
else
{
switch (PHYLinkState)
{
case LAN8742_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case LAN8742_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case LAN8742_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
case LAN8742_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
default:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
}
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
HAL_ETH_Start_IT(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
#endif
/* USER CODE BEGIN PHY_POST_CONFIG */
/* 设置PHY IO功能 */
yt8512c_regster_bus_io(&YT8521H, &YT8521H_IOCtx);
/* 初始化ETH PHY */
yt8512c_init(&YT8521H);
/* 必须开启自动协商功能 */
yt8512c_start_auto_nego(&YT8521H);
osDelay(1000); /* 必须等待初始化 */
PHYLinkState = yt8512c_get_link_state(&YT8521H);
if (PHYLinkState == YT8512C_STATUS_READ_ERROR) {
netif_set_link_down(netif);
netif_set_down(netif);
log_debug("YT8512C PHY Read Error\r\n");
return;
} else {
if (hal_eth_init_status == HAL_OK) {
switch (PHYLinkState) {
case YT8512C_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case YT8512C_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
case YT8512C_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
case YT8512C_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
break;
default:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
break;
}
} else {
log_debug("YT8512C PHY Init Error\r\n");
Error_Handler();
}
}
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
HAL_ETH_Start_IT(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
/* USER CODE END PHY_POST_CONFIG */
#endif /* LWIP_ARP || LWIP_ETHERNET */
/* USER CODE BEGIN LOW_LEVEL_INIT */
/* USER CODE END LOW_LEVEL_INIT */
}
/**
* @brief This function should do the actual transmission of the packet. The
* packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and
* type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif *netif, struct pbuf *p) {
uint32_t i = 0U;
struct pbuf *q = NULL;
err_t errval = ERR_OK;
ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT] = {0};
memset(Txbuffer, 0, ETH_TX_DESC_CNT * sizeof(ETH_BufferTypeDef));
for (q = p; q != NULL; q = q->next) {
if (i >= ETH_TX_DESC_CNT)
return ERR_IF;
Txbuffer[i].buffer = q->payload;
Txbuffer[i].len = q->len;
if (i > 0) {
Txbuffer[i - 1].next = &Txbuffer[i];
}
if (q->next == NULL) {
Txbuffer[i].next = NULL;
}
i++;
}
TxConfig.Length = p->tot_len;
TxConfig.TxBuffer = Txbuffer;
TxConfig.pData = p;
pbuf_ref(p);
do {
if (HAL_ETH_Transmit_IT(&heth, &TxConfig) == HAL_OK) {
errval = ERR_OK;
} else {
if (HAL_ETH_GetError(&heth) & HAL_ETH_ERROR_BUSY) {
/* Wait for descriptors to become available */
osSemaphoreAcquire(TxPktSemaphore, ETHIF_TX_TIMEOUT);
HAL_ETH_ReleaseTxPacket(&heth);
errval = ERR_BUF;
} else {
/* Other error */
pbuf_free(p);
errval = ERR_IF;
}
}
} while (errval == ERR_BUF);
return errval;
}
/**
* @brief Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf *low_level_input(struct netif *netif) {
struct pbuf *p = NULL;
if (RxAllocStatus == RX_ALLOC_OK) {
HAL_ETH_ReadData(&heth, (void **)&p);
}
return p;
}
/**
* @brief This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input(void *argument) {
struct pbuf *p = NULL;
struct netif *netif = (struct netif *)argument;
for (;;) {
if (osSemaphoreAcquire(RxPktSemaphore, TIME_WAITING_FOR_INPUT) == osOK) {
do {
p = low_level_input(netif);
if (p != NULL) {
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
}
} while (p != NULL);
}
}
}
#if !LWIP_ARP
/**
* This function has to be completed by user in case of ARP OFF.
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if ...
*/
static err_t low_level_output_arp_off(struct netif *netif, struct pbuf *q,
const ip4_addr_t *ipaddr) {
err_t errval;
errval = ERR_OK;
/* USER CODE BEGIN 5 */
/* USER CODE END 5 */
return errval;
}
#endif /* LWIP_ARP */
/**
* @brief Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif *netif) {
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
// MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd,
// LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
#if LWIP_ARP
netif->output = etharp_output;
#else
/* The user should write its own code in low_level_output_arp_off function */
netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
/**
* @brief Custom Rx pbuf free callback
* @param pbuf: pbuf to be freed
* @retval None
*/
void pbuf_free_custom(struct pbuf *p) {
struct pbuf_custom *custom_pbuf = (struct pbuf_custom *)p;
LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf);
/* If the Rx Buffer Pool was exhausted, signal the ethernetif_input task to
* call HAL_ETH_GetRxDataBuffer to rebuild the Rx descriptors. */
if (RxAllocStatus == RX_ALLOC_ERROR) {
RxAllocStatus = RX_ALLOC_OK;
osSemaphoreRelease(RxPktSemaphore);
}
}
/* USER CODE BEGIN 6 */
/**
* @brief Returns the current time in milliseconds
* when LWIP_TIMERS == 1 and NO_SYS == 1
* @param None
* @retval Current Time value
*/
u32_t sys_now(void) { return HAL_GetTick(); }
/* USER CODE END 6 */
/**
* @brief Initializes the ETH MSP.
* @param ethHandle: ETH handle
* @retval None
*/
void HAL_ETH_MspInit(ETH_HandleTypeDef *ethHandle) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (ethHandle->Instance == ETH) {
/* USER CODE BEGIN ETH_MspInit 0 */
/* USER CODE END ETH_MspInit 0 */
/* Enable Peripheral clock */
__HAL_RCC_ETH1MAC_CLK_ENABLE();
__HAL_RCC_ETH1TX_CLK_ENABLE();
__HAL_RCC_ETH1RX_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PB12 ------> ETH_TXD0
PB13 ------> ETH_TXD1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(ETH_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
/* USER CODE BEGIN ETH_MspInit 1 */
/* USER CODE END ETH_MspInit 1 */
}
}
void HAL_ETH_MspDeInit(ETH_HandleTypeDef *ethHandle) {
if (ethHandle->Instance == ETH) {
/* USER CODE BEGIN ETH_MspDeInit 0 */
/* USER CODE END ETH_MspDeInit 0 */
/* Disable Peripheral clock */
__HAL_RCC_ETH1MAC_CLK_DISABLE();
__HAL_RCC_ETH1TX_CLK_DISABLE();
__HAL_RCC_ETH1RX_CLK_DISABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PB11 ------> ETH_TX_EN
PB12 ------> ETH_TXD0
PB13 ------> ETH_TXD1
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(ETH_IRQn);
/* USER CODE BEGIN ETH_MspDeInit 1 */
/* USER CODE END ETH_MspDeInit 1 */
}
}
/*******************************************************************************
PHI IO Functions
*******************************************************************************/
/**
* @brief Initializes the MDIO interface GPIO and clocks.
* @param None
* @retval 0 if OK, -1 if ERROR
*/
int32_t ETH_PHY_IO_Init(void) {
/* We assume that MDIO GPIO configuration is already done
in the ETH_MspInit() else it should be done here
*/
/* Configure the MDIO Clock */
HAL_ETH_SetMDIOClockRange(&heth);
return 0;
}
/**
* @brief De-Initializes the MDIO interface .
* @param None
* @retval 0 if OK, -1 if ERROR
*/
int32_t ETH_PHY_IO_DeInit(void) { return 0; }
/**
* @brief Read a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param pRegVal: pointer to hold the register value
* @retval 0 if OK -1 if Error
*/
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr,
uint32_t *pRegVal) {
if (HAL_ETH_ReadPHYRegister(&heth, DevAddr, RegAddr, pRegVal) != HAL_OK) {
return -1;
}
return 0;
}
/**
* @brief Write a value to a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param RegVal: Value to be written
* @retval 0 if OK -1 if Error
*/
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr,
uint32_t RegVal) {
if (HAL_ETH_WritePHYRegister(&heth, DevAddr, RegAddr, RegVal) != HAL_OK) {
return -1;
}
return 0;
}
/**
* @brief Get the time in millisecons used for internal PHY driver process.
* @retval Time value
*/
int32_t ETH_PHY_IO_GetTick(void) { return HAL_GetTick(); }
/**
* @brief Check the ETH link state then update ETH driver and netif link
* accordingly.
* @retval None
*/
void ethernet_link_thread(void *argument) {
ETH_MACConfigTypeDef MACConf = {0};
int32_t PHYLinkState = 0;
uint32_t linkchanged = 0U, speed = 0U, duplex = 0U;
struct netif *netif = (struct netif *)argument;
/* USER CODE BEGIN ETH link init */
/* USER CODE END ETH link init */
#if 0
for(;;)
{
PHYLinkState = LAN8742_GetLinkState(&LAN8742);
if(netif_is_link_up(netif) && (PHYLinkState <= LAN8742_STATUS_LINK_DOWN))
{
HAL_ETH_Stop_IT(&heth);
netif_set_down(netif);
netif_set_link_down(netif);
}
else if(!netif_is_link_up(netif) && (PHYLinkState > LAN8742_STATUS_LINK_DOWN))
{
switch (PHYLinkState)
{
case LAN8742_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
default:
break;
}
if(linkchanged)
{
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
HAL_ETH_Start_IT(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
}
}
#endif
/* USER CODE BEGIN ETH link Thread core code for User BSP */
bool lwip_initialized = false;
for (;;) {
PHYLinkState = yt8512c_get_link_state(&YT8521H);
// log_debug("[PHY] State: %d", PHYLinkState);
if (netif_is_link_up(netif) && (PHYLinkState <= LAN8742_STATUS_LINK_DOWN)) {
HAL_ETH_Stop_IT(&heth);
netif_set_down(netif);
netif_set_link_down(netif);
/* 打印详细状态 */
log_debug("[ETH] MACCR: 0x%08X\n", ETH->MACCR);
log_debug("[ETH] DMACSR: 0x%08X\n", ETH->DMACSR);
} else if (!netif_is_link_up(netif) &&
(PHYLinkState > LAN8742_STATUS_LINK_DOWN)) {
printf("[NET] Link up detected! Bringing interface up...\n");
switch (PHYLinkState) {
case YT8512C_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
printf("[PHY] 100Mbps Full Duplex\n");
break;
case YT8512C_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
printf("[PHY] 100Mbps Half Duplex\n");
break;
case YT8512C_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
printf("[PHY] 10Mbps Full Duplex\n");
break;
case YT8512C_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
printf("[PHY] 10Mbps Half Duplex\n");
break;
default:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
printf("[PHY] Unknown state! Defaulting to 100Mbps Full Duplex\n");
break;
}
if (linkchanged) {
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
printf("[ETH] Current MAC config: Speed=%s, Duplex=%s\n",
MACConf.Speed == ETH_SPEED_100M ? "100M" : "10M",
MACConf.DuplexMode == ETH_FULLDUPLEX_MODE ? "Full" : "Half");
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
printf("[ETH] New MAC config: Speed=%s, Duplex=%s\n",
speed == ETH_SPEED_100M ? "100M" : "10M",
duplex == ETH_FULLDUPLEX_MODE ? "Full" : "Half");
int status = HAL_ETH_Start_IT(&heth);
printf("[ETH] HAL_ETH_Start_IT status: %d\n", status);
if (status != HAL_OK) {
printf("[ETH] Error starting ETH! Code: %d\n", heth.ErrorCode);
}
netif_set_up(netif);
netif_set_link_up(netif);
}
}
if (lwip_initialized == false) {
/*lwip 初始化完成 发送任务通知*/
printf("[LWIP] Initialization complete\n");
lwip_initialized = true;
}
/* USER CODE END ETH link Thread core code for User BSP */
osDelay(100);
}
}
void HAL_ETH_RxAllocateCallback(uint8_t **buff) {
/* USER CODE BEGIN HAL ETH RxAllocateCallback */
struct pbuf_custom *p = LWIP_MEMPOOL_ALLOC(RX_POOL);
if (p) {
/* Get the buff from the struct pbuf address. */
*buff = (uint8_t *)p + offsetof(RxBuff_t, buff);
p->custom_free_function = pbuf_free_custom;
/* Initialize the struct pbuf.
* This must be performed whenever a buffer's allocated because it may be
* changed by lwIP or the app, e.g., pbuf_free decrements ref. */
pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, p, *buff, ETH_RX_BUFFER_SIZE);
} else {
RxAllocStatus = RX_ALLOC_ERROR;
*buff = NULL;
}
/* USER CODE END HAL ETH RxAllocateCallback */
}
void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff,
uint16_t Length) {
/* USER CODE BEGIN HAL ETH RxLinkCallback */
struct pbuf **ppStart = (struct pbuf **)pStart;
struct pbuf **ppEnd = (struct pbuf **)pEnd;
struct pbuf *p = NULL;
/* Get the struct pbuf from the buff address. */
p = (struct pbuf *)(buff - offsetof(RxBuff_t, buff));
p->next = NULL;
p->tot_len = 0;
p->len = Length;
/* Chain the buffer. */
if (!*ppStart) {
/* The first buffer of the packet. */
*ppStart = p;
} else {
/* Chain the buffer to the end of the packet. */
(*ppEnd)->next = p;
}
*ppEnd = p;
/* Update the total length of all the buffers of the chain. Each pbuf in the
* chain should have its tot_len
* set to its own length, plus the length of all the following pbufs in the
* chain. */
for (p = *ppStart; p != NULL; p = p->next) {
p->tot_len += Length;
}
/* Invalidate data cache because Rx DMA's writing to physical memory makes it
* stale. */
SCB_InvalidateDCache_by_Addr((uint32_t *)buff, Length);
/* USER CODE END HAL ETH RxLinkCallback */
}
void HAL_ETH_TxFreeCallback(uint32_t *buff) {
/* USER CODE BEGIN HAL ETH TxFreeCallback */
pbuf_free((struct pbuf *)buff);
/* USER CODE END HAL ETH TxFreeCallback */
}
/* USER CODE BEGIN 8 */
/* USER CODE END 8 */
然后是low_level_init初始化函数和ethernet_link_thread这个线程的修改,把之前的LAN8742的改成yt8512的
在cc.h中增加
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
#ifndef __CC_H__
#define __CC_H__
#include "cpu.h"
#include <stdlib.h>
#include <stdio.h>
typedef int sys_prot_t;
#define LWIP_PROVIDE_ERRNO
#if defined (__GNUC__) & !defined (__CC_ARM)
//#define LWIP_TIMEVAL_PRIVATE 0
//#include <sys/time.h>
#endif
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__TASKING__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
x, __LINE__, __FILE__); } while(0)
/* Define random number generator function */
#define LWIP_RAND() ((u32_t)rand())
#endif /* __CC_H__ */
然后是udp的socket,有些我业务里的东西,你们删掉就行,定义的帧头啥的没啥用,
#include "user_udp.h"
#include "FreeRTOS.h"
#include "cmsis_os2.h"
#include "lwip/err.h"
#include "lwip/inet.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/sockets.h"
#include "lwip/tcpip.h"
#include "semphr.h"
#include "task.h"
//#include "user_i2s.h"
#include "user_task.h"
#include "user_uart.h"
#include <stdbool.h>
#include <string.h>
#include "stream_buffer.h"
#include "user_info.h"
#include "htdm.h"
#include "user_uart.h"
#include "user_log.h"
//#include "i2s.h"
/********************udp任务相关*******************/
#define UDP_VOICE_PORT 5000 // 接收端口
#define UDP_MAX_CLIENTS 5 // 最大支持客户端连接数
#define UDP_CLIENT_TIMEOUT 5 * 60 * 1000 // 毫秒
#define STREAM_BUFFER_SIZE (10 * 1280)
#define SEND_THRESHOLD (5 * 1280)
#define BROADCAST_IP (0XFF78a8c0) //广播ip
uint8_t stream_discard_buf[1280]; //流缓冲丢弃区
//#define BROADCAST_IP 0xC0A878FF
// udp语音流缓冲区
StreamBufferHandle_t xStreamBuffer;
StreamBufferHandle_t xStreamBuffer_2;
//使用天通业务电话短信结构体
extern sms_phone_info_t phone_info;
udp_center_state_t udp_center_state; //中心服务器状态
uint8_t udp_recvbuf[CLIENT_BUFFER_SIZE]; // 接收缓冲区,用流缓冲区替代
udp_client_t
udp_voice_clients; //语音客户端结构体,在收到电话相关帧后记录ip和端口,在收到挂断后释放
udp_client_t
udp_state_clients; //状态查询客户端结构体,在收到状态查询帧后记录ip和端口,在发送状态后释放
udp_client_t
udp_sms_clients; //短信客户端结构体,在收到短信相关帧后记录ip和端口,在发送短信后释放
udp_client_t
udp_contact_clients; //联系人客户端结构体,收到查询/设置联系人帧后记录ip和端口,在发送联系人后释放
udp_client_t
udp_sms_template_clients; //联系人客户端结构体,收到查询/设置联系人帧后记录ip和端口,在发送联系人后释放
uint8_t cur_active_index = 0; // 当前活动IP
//// 用于存储客户端信息
typedef struct {
ip_addr_t ip; // 客户端 IP
uint16_t port; // 客户端端口
uint32_t last_time; // 最后活跃时间(tick)
uint8_t active; // 是否已登记
uint8_t *buffer; // 指向动态缓冲区
uint16_t buffer_size; // 缓冲区大小
client_type_flag_t client_flag; //指定客户端标记默认为常规客户端
} UdpClientInfo;
UdpClientInfo g_udp_clients[UDP_MAX_CLIENTS];
static int udp_sock = -1;
static SemaphoreHandle_t udp_mutex = NULL;
extern uint8_t udp_recvbuf[CLIENT_BUFFER_SIZE]; // 接收缓冲区,用流缓冲区替代
// extern uint8_t i2s_rx_2_udp_buffer[I2S_DMA_BUFFER_SIZE+7];
// extern I2S_DMA_Buffers_t i2s_tx_buffers;
// extern I2S_DMA_Buffers_t i2s_rx_buffers;
uint8_t udp_send_buf_i2s[SEND_THRESHOLD]; //语音流缓冲读出的发送缓冲区
osThreadId_t udpsThread_t;
osThreadId_t voicesThread_t;
void StartudpsdTask(void *argument);
void init_stream_buffer(void) {
xStreamBuffer = xStreamBufferCreate(STREAM_BUFFER_SIZE, 1);
if (xStreamBuffer == NULL) {
// 处理流缓冲区创建失败的情况
log_debug("xStreamBuffer is null\r\n");
} else {
// 设置流缓冲区的触发等级
// xStreamBufferSetTriggerLevel(xStreamBuffer_2, 4);
}
}
const osThreadAttr_t udpsTask_attributes = {
.name = "udpsTask",
.stack_size = 1024 * 6,
.priority = (osPriority_t)osPriorityNormal,
};
void user_udp_init(void) {
struct sockaddr_in local_addr;
udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_sock < 0) {
log_info("UDP socket create failed\n");
return;
}
// log_debug("udp_sock[%i]\r\n",udp_sock);
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(UDP_VOICE_PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(udp_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
log_info("UDP socket bind failed\n");
closesocket(udp_sock);
udp_sock = -1;
return;
}
// log_debug("udp_sock[%i]\r\n",udp_sock);
struct timeval timeout = {0, 5000}; // 5ms recv timeout
setsockopt(udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// log_debug("udp_sock[%i]\r\n",udp_sock);
udp_mutex = xSemaphoreCreateMutex();
udpsThread_t = osThreadNew(StartudpsdTask, NULL, &udpsTask_attributes);
// voicesThread_t = osThreadNew(StartVoiceSendTask, NULL,
// &voiceTask_attributes);
}
void Udp_SetSocketNonBlocking(int sock) {
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
log_info("fcntl(F_GETFL) failed\n");
return;
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
log_info("fcntl(F_SETFL) failed\n");
} else {
log_info("UDP socket set to non-blocking\n");
}
}
/**
* @brief UDP 接收语音数据任务
*/
void StartudpsdTask(void *arg) {
struct sockaddr_in from;
socklen_t addr_len = sizeof(from);
if (udp_sock < 0) {
log_info("UDP socket not initialized!\n");
vTaskDelete(NULL);
}
// 设置非阻塞
// Udp_SetSocketNonBlocking(udp_sock);
log_debug("UDP recv task started (port %d)\n", UDP_VOICE_PORT);
init_stream_buffer();
while (1) {
int len = recvfrom(udp_sock, udp_recvbuf, CLIENT_BUFFER_SIZE, 0,
(struct sockaddr *)&from, &addr_len);
if (len > 0) {
ip_addr_t from_ip;
from_ip.addr = from.sin_addr.s_addr;
uint16_t from_port = ntohs(from.sin_port);
log_debug("UDP data from %s:%d, len: %d\n", ipaddr_ntoa(&from_ip),
from_port, len);
// for(int i = 0; i < len; i++)
// {
// log_info("%02x ", udp_recvbuf[i]);
// }
// log_info("\r\n");
// UdpVoiceClient_Update(from_ip, from_port, udp_recvbuf, len);
// udp_message_handle(udp_recvbuf, len);
#if 1
// 回环验证
sendto(udp_sock, udp_recvbuf, len, 0, (struct sockaddr *)&from,
sizeof(from));
log_debug("udp_sock:%x\r\nudp_recvbuf:%x\r\n", udp_sock, udp_recvbuf);
#elif 0
// 回环验证
Udp_SendToTarget(udp_state_clients.ip_str, udp_state_clients.port,
udp_recvbuf, len);
#endif
// Audio_PushToPlayBuffer((uint8_t *)recv_buf, len);
}
// UdpVoiceClient_Cleanup();
vTaskDelay(pdMS_TO_TICKS(1)); // 防止空转
}
}
void user_network_up_or_down(uint8_t sta) {
if (sta == 1) {
// user_tcp_init();
log_info("Network interface is up.\r\n");
user_udp_init();
} else {
log_info("Network interface is down.\r\n");
}
}
void udp_thread_notify_vaule(bool irq, uint32_t nofity_bit) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (irq == true) {
xTaskNotifyFromISR((TaskHandle_t)udpsThread_t, nofity_bit, eSetBits,
&xHigherPriorityTaskWoken); //
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} else {
xTaskNotify((TaskHandle_t)udpsThread_t, nofity_bit, eSetBits);
}
}
#ifndef __USER_UDP_H
#define __USER_UDP_H
//_F表示功能码
//_A表示操作类型
//_R表示应答类型
#include <stdint.h>
#include <stdbool.h>
#define UDP_I2S_TX_HALF_FINISH 0x01
#define UDP_I2S_TX_FINISHED UDP_I2S_TX_HALF_FINISH << 1
#define CLIENT_BUFFER_SIZE 1500 // 每个客户端的缓冲区大小
/*************udp任务通知*************/
#define UDP_TASK_NOTIFY_LWIP_READY 0X01
typedef enum {
UDP_CLIENT_NORMAL = 0,//常规客户端
UDP_CLIENT_CALL,//电话客户端 = 1
UDP_CLIENT_SMS,//短信客户端
UDP_CLIENT_SOS,//SOS客户端
STATUS_QUERY_CLIENT,//状态查询客户端
CONTACT_QUERY_CLIENT,//联系人查询/设置联系人客户端
SMS_TEMPLATE_CLIENT,//设置短信模板
}client_type_flag_t;
typedef struct{
uint8_t center_state_info[128];//中心节点状态信息
uint8_t lens;
}udp_center_state_t;
#define UDP_FRAME_HEAD 0xfd //默认帧头
#define UDP_FRAME_END 0Xdf //默认帧尾
/**
* @brief udp协议功能码
*
*/
typedef enum {
UDP_LOG_F = 0x01, //用户ip登录/登出
UDP_LOG_REP_F = 0x02, //用户ip登录/登出应答
UDP_COMMUNICATION_TEST_F = 0x03, //通讯测试
UDP_COMMUNICATION_TEST_REP_F = 0x04, //通讯测试应答
UDP_DIAL_F = 0x05, //拨打电话
UDP_DIAL_REP_F = 0x06, //拨打电话应答帧
UDP_HANG_F = 0x07, //挂断电话
UDP_HANG_REP_F = 0x08, //挂断电话应答帧
UDP_SMS_F = 0x09, //发送短信
UDP_SMS_REP_F = 0x0A, //发送短信应答帧
UDP_SET_PHONE_F = 0x0B, //设置电话/紧急短信模板
UDP_SET_PHONE_REP_F = 0x0C, //设置电话/紧急短信模板应答帧
UDP_SOS_F = 0x0E, // SOS请求帧
UDP_SOS_REP_F = 0x0F, // SOS请求应答帧
UDP_STATUS_F = 0x10, //状态查询
UDP_STATUS_REP_F = 0x11, //状态查询应答帧
} udp_frame_funcode_t;
// 登录/登出操作类型枚举
typedef enum {
LOGIN_ACTION_A = 0x01, // 登录操作
LOGOUT_ACTION_A = 0x02 // 登出操作
} udp_login_action_t;
// 登录/登出应答状态枚举
typedef enum {
ACCOUNT_LOGIN_SUCCESS_R = 0x01, //登录成功
ACCOUNT_LOGIN_FAILED_R, //登录失败
ACCOUNT_LOGOUT_SUCCESS_R, //登出成功
ACCOUNT_LOGOUT_FAILED_R, //登出失败
} udp_login_state_rep_t;
// 保活操作类型枚举
typedef enum {
UDP_ALIVE_A = 0x01, //请求保活
}udp_keep_rep_t;
// 保活应答状态枚举
typedef enum {
UDP_KEEP_ALIVE_R = 0x01, //保活成功
}udp_keep_alive_rep_t;
// 拨打电话操作类型枚举
typedef enum {
UDP_CALL_A = 0x01, //拨打电话
}udp_call_t;
// 拨打电话应答状态枚举
typedef enum {
UDP_CALLING_R = 0x01, //正在拨打电话
UDP_CALL_CONNECT_OK_R, //对方接通
UDP_CALL_NET_BUSY_R, //卫星网络已被占用,电话拨打失败 //卫星网络已被占用,电话拨打失败
UDP_CALL_NET_DISCONNECT_R,//网络连接中断,请重新连接
}udp_call_rep_t;
// 挂断电话操作类型枚举
typedef enum {
UDP_HANGUP_A = 0x01, //挂断电话
}udp_hangup_t;
// 挂断电话应答状态枚举
typedef enum {
UDP_HANGUP_OK_R =0x01, //挂断成功
}udp_hangup_rep_t;
// 发送短信操作类型枚举
typedef enum {
UDP_SMS_SEND_A = 0x01, //发送短信
}udp_sms_t;
// 发送短信应答状态枚举
typedef enum {
UDP_SMS_SEND_OK_R = 0x01, // 发送成功
UDP_SMS_SEND_NO_SIGNAL_R, // 发送失败:天通无信号
UDP_SMS_SEND_SATELLITE_FAIL_R, // 发送失败:收到天通回复发送失败
UDP_SMS_SEND_PDU_FAIL_R, // 发送失败:生成PDU短信失败
UDP_SMS_SENDING_R, // 正在发送短信
UDP_SMS_SEND_WAITING_R // 卫星网络占用
} udp_sms_rep_t;
// 设置电话模板操作类型枚举
typedef enum {
UDP_SET_PHONE_A = 0x01, //设置紧急短信模板
}udp_set_phone_t;
// 设置短信模板操作类型枚举
typedef enum {
UDP_SET_PHONE_SMS_A = 0x01, // 设置紧急短信中心号码
UDP_SET_EMERGENCY_CONTACT1_A, // 设置紧急联系人1
UDP_SET_EMERGENCY_CONTACT2_A, // 设置紧急联系人2
UDP_SET_EMERGENCY_CONTACT3_A, // 设置紧急联系人3
UDP_SET_SATELLITE_PHONE_A // 本机卫星电话卡号
} udp_set_phone_sms_t;
// 设置电话/短信模板应答状态枚举
typedef enum {
UDP_SET_OK_R =1,//设置成功
UDP_SET_FAIL_R,//设置失败
}udp_set_phone_sms_rep_t;
// SOS操作类型枚举
typedef enum {
UDP_SOS_A = 0x01, //SOS请求
}udp_sos_t;
// SOS应答状态枚举
typedef enum {
UDP_SOS_OK_R = 1, // 发起sos流程成功
UDP_SOS_CONTACT_INVALID_R, // 发起sos流程失败:联系人无效
UDP_SOS_NET_BUSY_R // 发起sos流程失败:网络被占用
} udp_sos_rep_t;
// 状态查询应答状态枚举
typedef enum {
UDP_STATUS_R = 0x01, //状态查询
}udp_status_t;
/**
* @brief UDP状态查询应答状态枚举
*
* 包含系统各模块的状态标识,用于UDP通信中的状态报告
*/
typedef enum {
SATELLITE_CONNECTION_STATUS_R = 0x01, // 卫星连接状态 (0: 断开, 1: 连接)
SATELLITE_SIGNAL_QUALITY_R, // 卫星信号质量+SNR (0-100表示质量百分比)
CENTER_NODE_POWER_STATUS_R, // 中心节点电源状态 (0: 关闭, 1: 开启)
MOTION_STATUS_R, // 运动状态 (0: 静止, 1: 运动中)
SATELLITE_NETWORK_STATUS_R, // 卫星网络状态 (0: 断开, 1: 连接)
CENTER_NODE_BATTERY_LEVEL_R, // 中心节点电源电量 (0-100表示电量百分比)
CENTER_NODE_WORK_STATUS_R, // 中心节点工作状态 (0: 待机, 1: 工作中)
SIM_CARD_STATUS_R, // SIM卡状态 (0: 异常, 1: 正常)
SOS_EMERGENCY_STATUS_R, // SOS状态 (0: 正常, 1: 紧急状态)
CONNECTED_DEVICES_COUNT_R // 当前连入中心节点设备数 (0-255)
} udp_status_rep_t;
typedef void (*udp_data_parser_hook_t)(uint8_t *buffer, uint16_t len);
/*功能码和回调函数*/
typedef struct {
uint8_t udp_msg_func_code;
udp_data_parser_hook_t hook;
} udp_dispatcher_t;
/**
* @brief UDP语音客户端信息
*
*/
typedef struct{
char ip_str[16];
uint16_t port;
uint8_t type;
}udp_client_t;
/**
* @brief UDP初始化,该初始化在网线插入以后,LWIP初始化后调用
*
*/
void user_udp_init(void);
/**
* @brief UDP广播
*
* @param data
* @param len
* @return int
*/
int UdpVoice_SendToAll(const void *data, uint16_t len);
/**
* @brief 向指定IP和端口发送UDP数据
*
* @param ip_str
* @param port
* @param data
* @param len
* @return int
*/
int Udp_SendToTarget(const char *ip_str, uint16_t port, const void *data,
uint16_t len);
/**
* @brief 发送udp数据时分为语音数据和业务数据两种
*
* @param data
* @param len
* @param client_flag
*/
void user_send_udp_data_to_target(const void *data, uint16_t len,client_type_flag_t client_flag);
/**
* @brief 接收到的整帧数据解析和执行
*
* @param udp_recvbuf 接收到的数据
* @param len 数据长度
*/
void udp_message_handle(uint8_t *udp_recvbuf, uint16_t len);
/**
* @brief udp数据帧解析和分发处理
*
* @param func_code 功能码
* @param buffer 数据
* @param len 数据长度
*/
void udp_frame_parser(uint8_t func_code, uint8_t *buffer, uint16_t len);
/**
* @brief 发送数据到第一个活跃的ip
*
* @param buffer 数据
* @param len 数据长度
*/
int UdpVoice_SendToFirst(const void *data, uint16_t len);
/**
* @brief 语音发送完成中断触发的任务通知,用于udp中发送语音时的状态同步
*
* @param irq
* @param nofity_bit
*/
void VoiceSend_thread_notify_vaule(bool irq, uint32_t nofity_bit);
void user_network_up_or_down(uint8_t sta);
void udp_thread_notify_vaule(bool irq, uint32_t nofity_bit);
#if 0
/**
* @brief ip登录操作处理函数
*
* @param buffer
* @param len
*/
void udp_login_hook(uint8_t *buffer, uint16_t len);
/**
* @brief ip登录操作应答处理函数
*
* @param buffer
* @param len
*/
void udp_login_rep_hook(uint8_t login_ruslt);
/**
* @brief 保活测试
*
* @param buffer
* @param len
*/
void udp_communication_test_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_communication_test_rep_hook();
/**
* @brief 拨打电话回调
*
* @param buffer
* @param len
*/
void udp_dial_hook(uint8_t *buffer, uint16_t len);
/**
* @brief 拨打电话应答处理函数
*
* @param call_result
*/
void udp_dial_rep_hook( udp_call_rep_t call_result);
/**
* @brief 挂断电话回调
*
* @param buffer
* @param len
*/
void udp_hang_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_hang_rep_hook();
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_sms_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_sms_rep_hook(udp_sms_rep_t sms_send_sta);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_set_phone_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_set_phone_rep_hook();
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_sos_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_sos_rep_hook();
/**
* @brief
*
* @param buffer
* @param len
*/
void udp_status_hook(uint8_t *buffer, uint16_t len);
/**
* @brief
*
* @param status
*/
void udp_status_rep_hook(udp_status_t status);
#endif
#endif
udp的部分主要看这里,这里就是收发和socket的精髓,其他不用管
void StartudpsdTask(void *argument);
void init_stream_buffer(void) {
xStreamBuffer = xStreamBufferCreate(STREAM_BUFFER_SIZE, 1);
if (xStreamBuffer == NULL) {
// 处理流缓冲区创建失败的情况
log_debug("xStreamBuffer is null\r\n");
} else {
// 设置流缓冲区的触发等级
// xStreamBufferSetTriggerLevel(xStreamBuffer_2, 4);
}
}
const osThreadAttr_t udpsTask_attributes = {
.name = "udpsTask",
.stack_size = 1024 * 6,
.priority = (osPriority_t)osPriorityNormal,
};
void user_udp_init(void) {
struct sockaddr_in local_addr;
udp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (udp_sock < 0) {
log_info("UDP socket create failed\n");
return;
}
// log_debug("udp_sock[%i]\r\n",udp_sock);
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(UDP_VOICE_PORT);
local_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(udp_sock, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
log_info("UDP socket bind failed\n");
closesocket(udp_sock);
udp_sock = -1;
return;
}
// log_debug("udp_sock[%i]\r\n",udp_sock);
struct timeval timeout = {0, 5000}; // 5ms recv timeout
setsockopt(udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// log_debug("udp_sock[%i]\r\n",udp_sock);
udp_mutex = xSemaphoreCreateMutex();
udpsThread_t = osThreadNew(StartudpsdTask, NULL, &udpsTask_attributes);
// voicesThread_t = osThreadNew(StartVoiceSendTask, NULL,
// &voiceTask_attributes);
}
void Udp_SetSocketNonBlocking(int sock) {
int flags = fcntl(sock, F_GETFL, 0);
if (flags < 0) {
log_info("fcntl(F_GETFL) failed\n");
return;
}
if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
log_info("fcntl(F_SETFL) failed\n");
} else {
log_info("UDP socket set to non-blocking\n");
}
}
/**
* @brief UDP 接收语音数据任务
*/
void StartudpsdTask(void *arg) {
struct sockaddr_in from;
socklen_t addr_len = sizeof(from);
if (udp_sock < 0) {
log_info("UDP socket not initialized!\n");
vTaskDelete(NULL);
}
// 设置非阻塞
// Udp_SetSocketNonBlocking(udp_sock);
log_debug("UDP recv task started (port %d)\n", UDP_VOICE_PORT);
init_stream_buffer();
while (1) {
int len = recvfrom(udp_sock, udp_recvbuf, CLIENT_BUFFER_SIZE, 0,
(struct sockaddr *)&from, &addr_len);
if (len > 0) {
ip_addr_t from_ip;
from_ip.addr = from.sin_addr.s_addr;
uint16_t from_port = ntohs(from.sin_port);
log_debug("UDP data from %s:%d, len: %d\n", ipaddr_ntoa(&from_ip),
from_port, len);
// for(int i = 0; i < len; i++)
// {
// log_info("%02x ", udp_recvbuf[i]);
// }
// log_info("\r\n");
// UdpVoiceClient_Update(from_ip, from_port, udp_recvbuf, len);
// udp_message_handle(udp_recvbuf, len);
#if 1
// 回环验证
sendto(udp_sock, udp_recvbuf, len, 0, (struct sockaddr *)&from,
sizeof(from));
log_debug("udp_sock:%x\r\nudp_recvbuf:%x\r\n", udp_sock, udp_recvbuf);
#elif 0
// 回环验证
Udp_SendToTarget(udp_state_clients.ip_str, udp_state_clients.port,
udp_recvbuf, len);
#endif
// Audio_PushToPlayBuffer((uint8_t *)recv_buf, len);
}
// UdpVoiceClient_Cleanup();
vTaskDelay(pdMS_TO_TICKS(1)); // 防止空转
}
}
void user_network_up_or_down(uint8_t sta) {
if (sta == 1) {
// user_tcp_init();
log_info("Network interface is up.\r\n");
user_udp_init();
} else {
log_info("Network interface is down.\r\n");
}
}
void udp_thread_notify_vaule(bool irq, uint32_t nofity_bit) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (irq == true) {
xTaskNotifyFromISR((TaskHandle_t)udpsThread_t, nofity_bit, eSetBits,
&xHigherPriorityTaskWoken); //
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
} else {
xTaskNotify((TaskHandle_t)udpsThread_t, nofity_bit, eSetBits);
}
}
下面是lwip.c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : LWIP.c
* Description : This file provides initialization code for LWIP
* middleWare.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "lwip.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#if defined ( __CC_ARM ) /* MDK ARM Compiler */
#include "lwip/sio.h"
#endif /* MDK ARM Compiler */
#include "ethernetif.h"
#include <string.h>
/* USER CODE BEGIN 0 */
#include "user_udp.h"
/* USER CODE END 0 */
/* Private function prototypes -----------------------------------------------*/
static void ethernet_link_status_updated(struct netif *netif);
/* ETH Variables initialization ----------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Variables Initialization */
struct netif gnetif;
ip4_addr_t ipaddr;
ip4_addr_t netmask;
ip4_addr_t gw;
uint8_t IP_ADDRESS[4];
uint8_t NETMASK_ADDRESS[4];
uint8_t GATEWAY_ADDRESS[4];
/* USER CODE BEGIN OS_THREAD_ATTR_CMSIS_RTOS_V2 */
#define INTERFACE_THREAD_STACK_SIZE ( 4*1024 )
osThreadAttr_t attributes;
/* USER CODE END OS_THREAD_ATTR_CMSIS_RTOS_V2 */
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/**
* LwIP initialization function
*/
void MX_LWIP_Init(void)
{
/* IP addresses initialization */
IP_ADDRESS[0] = 192;
IP_ADDRESS[1] = 168;
IP_ADDRESS[2] = 188;
IP_ADDRESS[3] = 6;
NETMASK_ADDRESS[0] = 255;
NETMASK_ADDRESS[1] = 255;
NETMASK_ADDRESS[2] = 255;
NETMASK_ADDRESS[3] = 0;
GATEWAY_ADDRESS[0] = 192;
GATEWAY_ADDRESS[1] = 168;
GATEWAY_ADDRESS[2] = 188;
GATEWAY_ADDRESS[3] = 1;
/* USER CODE BEGIN IP_ADDRESSES */
IP_ADDRESS[0] = 192;
IP_ADDRESS[1] = 168;
IP_ADDRESS[2] = 188;
IP_ADDRESS[3] = 6;
NETMASK_ADDRESS[0] = 255;
NETMASK_ADDRESS[1] = 255;
NETMASK_ADDRESS[2] = 255;
NETMASK_ADDRESS[3] = 0;
GATEWAY_ADDRESS[0] = 192;
GATEWAY_ADDRESS[1] = 168;
GATEWAY_ADDRESS[2] = 188;
GATEWAY_ADDRESS[3] = 1;
/* USER CODE END IP_ADDRESSES */
/* Initialize the LwIP stack with RTOS */
tcpip_init( NULL, NULL );
/* IP addresses initialization without DHCP (IPv4) */
IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
/* add the network interface (IPv4/IPv6) with RTOS */
netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
/* Registers the default network interface */
netif_set_default(&gnetif);
/* We must always bring the network interface up connection or not... */
netif_set_up(&gnetif);
/* Set the link callback function, this function is called on change of link status*/
netif_set_link_callback(&gnetif, ethernet_link_status_updated);
/* Create the Ethernet link handler thread */
/* USER CODE BEGIN H7_OS_THREAD_NEW_CMSIS_RTOS_V2 */
memset(&attributes, 0x0, sizeof(osThreadAttr_t));
attributes.name = "EthLink";
attributes.stack_size = INTERFACE_THREAD_STACK_SIZE;
attributes.priority = osPriorityBelowNormal;
osThreadNew(ethernet_link_thread, &gnetif, &attributes);
/* USER CODE END H7_OS_THREAD_NEW_CMSIS_RTOS_V2 */
/* USER CODE BEGIN 3 */
/* USER CODE END 3 */
}
#ifdef USE_OBSOLETE_USER_CODE_SECTION_4
/* Kept to help code migration. (See new 4_1, 4_2... sections) */
/* Avoid to use this user section which will become obsolete. */
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
#endif
/**
* @brief Notify the User about the network interface config status
* @param netif: the network interface
* @retval None
*/
static void ethernet_link_status_updated(struct netif *netif)
{
if (netif_is_up(netif))
{
/* USER CODE BEGIN 5 */
user_network_up_or_down(1);
/* USER CODE END 5 */
}
else /* netif is down */
{
/* USER CODE BEGIN 6 */
user_network_up_or_down(0);
/* USER CODE END 6 */
}
}
#if defined ( __CC_ARM ) /* MDK ARM Compiler */
/**
* Opens a serial device for communication.
*
* @param devnum device number
* @return handle to serial device if successful, NULL otherwise
*/
sio_fd_t sio_open(u8_t devnum)
{
sio_fd_t sd;
/* USER CODE BEGIN 7 */
sd = 0; // dummy code
/* USER CODE END 7 */
return sd;
}
/**
* Sends a single character to the serial device.
*
* @param c character to send
* @param fd serial device handle
*
* @note This function will block until the character can be sent.
*/
void sio_send(u8_t c, sio_fd_t fd)
{
/* USER CODE BEGIN 8 */
/* USER CODE END 8 */
}
/**
* Reads from the serial device.
*
* @param fd serial device handle
* @param data pointer to data buffer for receiving
* @param len maximum length (in bytes) of data to receive
* @return number of bytes actually received - may be 0 if aborted by sio_read_abort
*
* @note This function will block until data can be received. The blocking
* can be cancelled by calling sio_read_abort().
*/
u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
{
u32_t recved_bytes;
/* USER CODE BEGIN 9 */
recved_bytes = 0; // dummy code
/* USER CODE END 9 */
return recved_bytes;
}
/**
* Tries to read from the serial device. Same as sio_read but returns
* immediately if no data is available and never blocks.
*
* @param fd serial device handle
* @param data pointer to data buffer for receiving
* @param len maximum length (in bytes) of data to receive
* @return number of bytes actually received
*/
u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len)
{
u32_t recved_bytes;
/* USER CODE BEGIN 10 */
recved_bytes = 0; // dummy code
/* USER CODE END 10 */
return recved_bytes;
}
#endif /* MDK ARM Compiler */
lwip的所有协议都有一套原生的debug方式在debug.h和opt.h中开启,大家有问题可以查查,这里我就不贴了
/*
* @file: .c
* @brief:
* @author: YangWentao
* @version:
* @date:
* @Copyright: 1.0.0
* @par Change Logs: 2024-10-23
*/
/*
* @file: .c
* @brief:
* @author: YangWentao
* @version:
* @date:
* @Copyright: 1.0.0
* @par Change Logs: 2024-10-23
*/
#include "user_sai.h"
#include "user_uart.h"
#include "sai.h"
#include "usart.h"
#include <stdbool.h>
#include <string.h>
#include "user_log.h"
#include "user_task.h"
#define USE_OPUS_ENC 1
static uint8_t app_rec_buf[APP_REC_SIZE];
/**
* @brief 当前从BLE接收到的语音数据长度
*
*/
static uint32_t ble_sound_lens = 0;
/**
* @brief 语音录音缓冲区
*
*/
SAI_DEF sai_rx_t;
SAI_DEF sai_tx_t;
/**语音启动播放标志位
* 在语音接收到一定长度后即可播放语音
*/
static uint8_t user_playflag = 0;
/**
* @brief 0: 板子与卫星、APP测试语音,双通测试
* 1:板子与卫星语音回环测试
* 3:板子与卫星,APP测试语音,单通。APP说话,卫星电话发起者收听
*/
static uint8_t opus_enable = 0;
//
static void sai_irq_set(bool enable);
static HAL_StatusTypeDef User_SAI_Disable(SAI_HandleTypeDef *hsai);
HAL_StatusTypeDef User_HAL_SAI_DeInit(SAI_HandleTypeDef *hsai);
HAL_StatusTypeDef User_HAL_SAI_Transmit_DMA(SAI_HandleTypeDef *hsai,
uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef User_HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai,
uint8_t *pData, uint16_t Size);
// 8000次/s,0.125ms/次,4B/次,40ms = 1帧 = 320次,4B * 320 = 1280字节/帧,半满
// = 1280字节/帧,全满 = 2帧
void bsp_sai_init(void) {
// 获取DMA半满的起始地址
sai_tx_t.dma_buf2 = &sai_tx_t.dma_buf[WAV_SAI_TX_DMA_HALF_SIZE*2];
sai_rx_t.dma_buf2 = &sai_rx_t.dma_buf[WAV_SAI_TX_DMA_HALF_SIZE*2];
HAL_SAI_Transmit_DMA(&USER_SAI_TX, sai_tx_t.dma_buf,WAV_SAI_TX_DMA_ALL_SIZE);
//__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_HT);
//__HAL_DMA_DISABLE(USER_SAI_TX.hdmatx);
__HAL_SAI_ENABLE(&USER_SAI_TX);
HAL_SAI_Receive_DMA(&USER_SAI_RX, sai_rx_t.dma_buf,WAV_SAI_TX_DMA_ALL_SIZE);
//__HAL_DMA_DISABLE(USER_SAI_RX.hdmarx);
__HAL_SAI_ENABLE(&USER_SAI_RX);
}
#if 0
void u_record_ready(uint8_t flag) {
opus_enable = flag;
if ((USER_SAI_RX.hdmarx->Instance->CCR & DMA_CCR_EN) == true) {
log_debug("sai alyread enable\r\n");
return;
}
log_debug("ready record voice\r\n");
#if 0
HAL_SAI_Receive_DMA(&USER_SAI_RX, sai_rx_t.dma_buf, WAV_SAI_TX_DMA_HALF_SIZE);
__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_TC | DMA_IT_HT);
HAL_SAI_Transmit_DMA(&USER_SAI_TX, app_rec_buf, APP_REC_SIZE / 2);
#else
User_HAL_SAI_Transmit_DMA(&USER_SAI_TX, app_rec_buf, APP_REC_SIZE / 2);
__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_TC | DMA_IT_HT);
__HAL_SAI_ENABLE(&USER_SAI_TX);
if (opus_enable != 3) {
User_HAL_SAI_Receive_DMA(&USER_SAI_RX, sai_rx_t.dma_buf,
WAV_SAI_TX_DMA_HALF_SIZE);
__HAL_SAI_ENABLE(&USER_SAI_RX);
}
#endif
}
void u_sai_ready(void) {
if ((USER_SAI_RX.hdmarx->Instance->CCR & DMA_CCR_EN) == true) {
log_debug("sai alyread enable\r\n");
return;
}
log_debug("ready record voice\r\n");
#if 0
HAL_SAI_Receive_DMA(&USER_SAI_RX, sai_rx_t.dma_buf, WAV_SAI_TX_DMA_HALF_SIZE);
__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_TC | DMA_IT_HT);
HAL_SAI_Transmit_DMA(&USER_SAI_TX, app_rec_buf, APP_REC_SIZE / 2);
#endif
sai_irq_set(true);
// app->htdm
__HAL_DMA_ENABLE(USER_SAI_TX.hdmatx);
// htdm->app
__HAL_DMA_ENABLE(USER_SAI_RX.hdmarx);
}
void user_sai_stop(void) {
log_debug("sai stop\r\n");
sai_irq_set(false);
__HAL_DMA_DISABLE(USER_SAI_RX.hdmarx);
__HAL_DMA_DISABLE(USER_SAI_TX.hdmatx);
if ((USER_SAI_RX.hdmarx->Instance->CCR & DMA_CCR_EN) == true) {
log_debug("sai alyread enable\r\n");
}
if ((USER_SAI_TX.hdmatx->Instance->CCR & DMA_CCR_EN) == true) {
log_debug("sai alyread enable\r\n");
}
user_tx_buf_init();
}
#endif
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == USER_SAI_TX.Instance) {
sai_tx_t.half_flag = true;
// log_debug("sHt\r\n");
}
}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == USER_SAI_TX.Instance) {
sai_tx_t.done_flag = true;
// log_debug("sCt\r\n");
}
}
void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == USER_SAI_RX.Instance) {
sai_rx_t.half_flag = true;
// log_debug("rHt\r\n");
debug_thread_notify_vaule(true, I2S_TASK_NOTIFY_RX_HALF);
}
}
void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == USER_SAI_RX.Instance) {
sai_rx_t.done_flag = true;
// log_debug("rCt\r\n");
debug_thread_notify_vaule(true, I2S_TASK_NOTIFY_RX_FULL);
}
}
void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai) {
if (hsai->Instance == USER_SAI_RX.Instance) {
log_debug("saiB error:%d,,,,,,,,,,,,", hsai->ErrorCode);
User_HAL_SAI_Receive_DMA(&USER_SAI_RX, sai_rx_t.dma_buf,
WAV_SAI_TX_DMA_HALF_SIZE);
__HAL_SAI_ENABLE(&USER_SAI_RX);
} else {
log_debug("saiA error:%d,,,,,,,,,,,,,,,", hsai->ErrorCode);
User_HAL_SAI_Transmit_DMA(&USER_SAI_TX, app_rec_buf, APP_REC_SIZE / 2);
__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_HT | DMA_IT_TC);
__HAL_DMA_DISABLE_IT(USER_SAI_TX.hdmatx, DMA_IT_TC);
__HAL_SAI_ENABLE(&USER_SAI_TX);
}
}
void user_tx_buf_init(void) {
memset(app_rec_buf, 0, APP_REC_SIZE);
ble_sound_lens = 0;
memset(sai_rx_t.dma_buf, 0, WAV_SAI_TX_DMA_ALL_SIZE);
}
void bsp_cpoy_sound(uint8_t *buf, uint16_t lens) {
uint32_t ramin_buf_lens = APP_REC_SIZE - ble_sound_lens; // 当前缓冲区剩余空间
uint32_t re_write_lens = lens - ramin_buf_lens; // 重头写入的数据量
// 如果剩余缓冲区的地址能够写入当前的全部数据
// log_debug("%d\r\n", ble_sound_lens);
if (ramin_buf_lens >= lens) {
memcpy(&app_rec_buf[ble_sound_lens], buf, lens);
ble_sound_lens += lens;
if (ble_sound_lens == APP_REC_SIZE)
ble_sound_lens = 0;
} else {
memcpy(&app_rec_buf[ble_sound_lens], buf, ramin_buf_lens);
memcpy(app_rec_buf, &buf[ramin_buf_lens], re_write_lens);
ble_sound_lens = re_write_lens;
}
// if (user_playflag == false && ble_sound_lens >= (512 * 4)) {
if (user_playflag == false) {
user_playflag = true;
// TODO:
log_debug("....................\r\n");
//__HAL_DMA_ENABLE(USER_SAI_TX.hdmatx);
}
}
#if 0
void user_sai_process(void) {
extern uint8_t uart5_debug_flag;
if (sai_rx_t.half_flag == true) {
//
sai_rx_t.half_flag = false;
if (opus_enable == 1) {
log_debug("rec buf 1\r\n");
bsp_cpoy_sound(user_dec_msg_t.Data, user_dec_msg_t.Len);
} else {
Handle_Enc_data(sai_rx_t.dma_buf, &user_enc_msg_t);
HAL_UART_Transmit_DMA(&huart5, user_enc_msg_t.Data, user_enc_msg_t.Len);
if (uart5_debug_flag == 1) {
log_info("mcu->ble:");
for (uint8_t i = 0; i < user_enc_msg_t.Len; i++) {
log_info("%02x-", user_enc_msg_t.Data[i]);
}
log_info("\r\n");
}
}
}
if (sai_rx_t.done_flag == true) {
sai_rx_t.done_flag = false;
//
if (opus_enable == 1) {
log_debug("rec buf 2\r\n");
Handle_Enc_data(sai_rx_t.dma_buf2, &user_enc_msg_t);
Handle_Dec_data(user_enc_msg_t.Data, user_enc_msg_t.Len, &user_dec_msg_t);
bsp_cpoy_sound(user_dec_msg_t.Data, user_dec_msg_t.Len);
} else {
Handle_Enc_data(sai_rx_t.dma_buf2, &user_enc_msg_t);
HAL_UART_Transmit_DMA(&huart5, user_enc_msg_t.Data, user_enc_msg_t.Len);
if (uart5_debug_flag == 1) {
log_info("mcu->ble:");
for (uint8_t i = 0; i < user_enc_msg_t.Len; i++) {
log_info("%02x-", user_enc_msg_t.Data[i]);
}
log_info("\r\n");
}
}
}
if (sai_tx_t.half_flag == true) {
sai_tx_t.half_flag = false;
memset(user_dec_msg_t.Data, 0, 640);
} else if (sai_tx_t.done_flag == true) {
sai_tx_t.done_flag = false;
memset(&user_dec_msg_t.Data[640], 0, 640);
}
}
void user_dec_uart_data(uint8_t *data, uint16_t lens) {
#if 1
Handle_Dec_data(data, lens, &user_dec_msg_t);
bsp_cpoy_sound(user_dec_msg_t.Data, user_dec_msg_t.Len);
#else
HAL_UART_Transmit_DMA(&huart5, data, lens);
__HAL_DMA_DISABLE_IT(huart5.hdmatx, DMA_IT_HT);
#endif
}
#endif
// SAi DMA API
//
//
//
//
//
#define User_SAI_DEFAULT_TIMEOUT 4U /* 4ms */
#define User_SAI_LONG_TIMEOUT 1000U
typedef enum { User_SAI_MODE_DMA, User_SAI_MODE_IT } User_SAI_ModeTypedef;
HAL_StatusTypeDef User_HAL_SAI_DeInit(SAI_HandleTypeDef *hsai) {
/* Check the SAI handle allocation */
if (hsai == NULL) {
return HAL_ERROR;
}
hsai->State = HAL_SAI_STATE_BUSY;
/* Disabled All interrupt and clear all the flag */
hsai->Instance->IMR = 0;
hsai->Instance->CLRFR = 0xFFFFFFFFU;
/* Disable the SAI */
if (User_SAI_Disable(hsai) != HAL_OK) {
/* Reset SAI state to ready */
hsai->State = HAL_SAI_STATE_READY;
/* Release Lock */
__HAL_UNLOCK(hsai);
return HAL_ERROR;
}
/* Flush the fifo */
SET_BIT(hsai->Instance->CR2, SAI_xCR2_FFLUSH);
/* DeInit the low level hardware: GPIO, CLOCK, NVIC and DMA */
HAL_SAI_MspDeInit(hsai);
/* Initialize the error code */
hsai->ErrorCode = HAL_SAI_ERROR_NONE;
/* Initialize the SAI state */
hsai->State = HAL_SAI_STATE_RESET;
/* Release Lock */
__HAL_UNLOCK(hsai);
return HAL_OK;
}
static uint32_t User_SAI_InterruptFlag(const SAI_HandleTypeDef *hsai,
uint32_t mode) {
uint32_t tmpIT = SAI_IT_OVRUDR;
if (mode == User_SAI_MODE_IT) {
tmpIT |= SAI_IT_FREQ;
}
if ((hsai->Init.Protocol == SAI_AC97_PROTOCOL) &&
((hsai->Init.AudioMode == SAI_MODESLAVE_RX) ||
(hsai->Init.AudioMode == SAI_MODEMASTER_RX))) {
tmpIT |= SAI_IT_CNRDY;
}
if ((hsai->Init.AudioMode == SAI_MODESLAVE_RX) ||
(hsai->Init.AudioMode == SAI_MODESLAVE_TX)) {
tmpIT |= SAI_IT_AFSDET | SAI_IT_LFSDET;
} else {
/* hsai has been configured in master mode */
tmpIT |= SAI_IT_WCKCFG;
}
return tmpIT;
}
static HAL_StatusTypeDef User_SAI_Disable(SAI_HandleTypeDef *hsai) {
uint32_t count = User_SAI_DEFAULT_TIMEOUT * (SystemCoreClock / 7 / 1000);
HAL_StatusTypeDef status = HAL_OK;
/* Disable the SAI instance */
__HAL_SAI_DISABLE(hsai);
do {
/* Check for the Timeout */
if (count-- == 0) {
/* Update error code */
hsai->ErrorCode |= HAL_SAI_ERROR_TIMEOUT;
status = HAL_TIMEOUT;
break;
}
} while ((hsai->Instance->CR1 & SAI_xCR1_SAIEN) != RESET);
return status;
}
static void User_SAI_DMATxHalfCplt(DMA_HandleTypeDef *hdma) {
SAI_HandleTypeDef *hsai =
(SAI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
#if (USE_HAL_SAI_REGISTER_CALLBACKS == 1)
hsai->TxHalfCpltCallback(hsai);
#else
HAL_SAI_TxHalfCpltCallback(hsai);
#endif /* USE_HAL_SAI_REGISTER_CALLBACKS */
}
static void User_SAI_DMATxCplt(DMA_HandleTypeDef *hdma) {
SAI_HandleTypeDef *hsai =
(SAI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
if (hdma->Init.Mode != DMA_CIRCULAR) {
hsai->XferCount = 0;
/* Disable SAI Tx DMA Request */
hsai->Instance->CR1 &= (uint32_t)(~SAI_xCR1_DMAEN);
/* Stop the interrupts error handling */
__HAL_SAI_DISABLE_IT(hsai, User_SAI_InterruptFlag(hsai, User_SAI_MODE_DMA));
hsai->State = HAL_SAI_STATE_READY;
}
HAL_SAI_TxCpltCallback(hsai);
}
static void User_SAI_DMAError(DMA_HandleTypeDef *hdma) {
SAI_HandleTypeDef *hsai =
(SAI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
/* Set SAI error code */
hsai->ErrorCode |= HAL_SAI_ERROR_DMA;
if ((hsai->hdmatx->ErrorCode == HAL_DMA_ERROR_TE) ||
(hsai->hdmarx->ErrorCode == HAL_DMA_ERROR_TE)) {
/* Disable the SAI DMA request */
hsai->Instance->CR1 &= ~SAI_xCR1_DMAEN;
/* Disable SAI peripheral */
User_SAI_Disable(hsai);
/* Set the SAI state ready to be able to start again the process */
hsai->State = HAL_SAI_STATE_READY;
/* Initialize XferCount */
hsai->XferCount = 0U;
}
/* SAI error Callback */
HAL_SAI_ErrorCallback(hsai);
}
/**
* @brief 不开启sai外设,只使能Dma传输和中断
*
* @param hsai
* @param pData
* @param Size
* @return HAL_StatusTypeDef
*/
HAL_StatusTypeDef User_HAL_SAI_Transmit_DMA(SAI_HandleTypeDef *hsai,
uint8_t *pData, uint16_t Size) {
uint32_t tickstart = HAL_GetTick();
if ((pData == NULL) || (Size == 0)) {
return HAL_ERROR;
}
if (hsai->State == HAL_SAI_STATE_READY) {
/* Process Locked */
__HAL_LOCK(hsai);
hsai->pBuffPtr = pData;
hsai->XferSize = Size;
hsai->XferCount = Size;
hsai->ErrorCode = HAL_SAI_ERROR_NONE;
hsai->State = HAL_SAI_STATE_BUSY_TX;
/* Set the SAI Tx DMA Half transfer complete callback */
hsai->hdmatx->XferHalfCpltCallback = User_SAI_DMATxHalfCplt;
/* Set the SAI TxDMA transfer complete callback */
hsai->hdmatx->XferCpltCallback = User_SAI_DMATxCplt;
/* Set the DMA error callback */
hsai->hdmatx->XferErrorCallback = User_SAI_DMAError;
/* Set the DMA Tx abort callback */
hsai->hdmatx->XferAbortCallback = NULL;
/* Enable the Tx DMA Stream */
if (HAL_DMA_Start_IT(hsai->hdmatx, (uint32_t)hsai->pBuffPtr,
(uint32_t)&hsai->Instance->DR,
hsai->XferSize) != HAL_OK) {
__HAL_UNLOCK(hsai);
return HAL_ERROR;
}
/* Enable the interrupts for error handling */
__HAL_SAI_ENABLE_IT(hsai, User_SAI_InterruptFlag(hsai, User_SAI_MODE_DMA));
/* Enable SAI Tx DMA Request */
hsai->Instance->CR1 |= SAI_xCR1_DMAEN;
/* Wait until FIFO is not empty */
while ((hsai->Instance->SR & SAI_xSR_FLVL) == SAI_FIFOSTATUS_EMPTY) {
/* Check for the Timeout */
if ((HAL_GetTick() - tickstart) > User_SAI_LONG_TIMEOUT) {
/* Update error code */
hsai->ErrorCode |= HAL_SAI_ERROR_TIMEOUT;
/* Process Unlocked */
__HAL_UNLOCK(hsai);
return HAL_TIMEOUT;
}
}
/* Process Unlocked */
__HAL_UNLOCK(hsai);
return HAL_OK;
} else {
return HAL_BUSY;
}
}
static void User_SAI_DMARxHalfCplt(DMA_HandleTypeDef *hdma) {
SAI_HandleTypeDef *hsai =
(SAI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
HAL_SAI_RxHalfCpltCallback(hsai);
}
static void User_SAI_DMARxCplt(DMA_HandleTypeDef *hdma) {
SAI_HandleTypeDef *hsai =
(SAI_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
if (hdma->Init.Mode != DMA_CIRCULAR) {
/* Disable Rx DMA Request */
hsai->Instance->CR1 &= (uint32_t)(~SAI_xCR1_DMAEN);
hsai->XferCount = 0;
/* Stop the interrupts error handling */
__HAL_SAI_DISABLE_IT(hsai, User_SAI_InterruptFlag(hsai, User_SAI_MODE_DMA));
hsai->State = HAL_SAI_STATE_READY;
}
HAL_SAI_RxCpltCallback(hsai);
}
/**
* @brief 不开启sai外设,只使能Dma传输和中断
*
* @param hsai
* @param pData
* @param Size
* @return HAL_StatusTypeDef
*/
HAL_StatusTypeDef User_HAL_SAI_Receive_DMA(SAI_HandleTypeDef *hsai,
uint8_t *pData, uint16_t Size) {
if ((pData == NULL) || (Size == 0)) {
return HAL_ERROR;
}
if (hsai->State == HAL_SAI_STATE_READY) {
/* Process Locked */
__HAL_LOCK(hsai);
hsai->pBuffPtr = pData;
hsai->XferSize = Size;
hsai->XferCount = Size;
hsai->ErrorCode = HAL_SAI_ERROR_NONE;
hsai->State = HAL_SAI_STATE_BUSY_RX;
/* Set the SAI Rx DMA Half transfer complete callback */
hsai->hdmarx->XferHalfCpltCallback = User_SAI_DMARxHalfCplt;
/* Set the SAI Rx DMA transfer complete callback */
hsai->hdmarx->XferCpltCallback = User_SAI_DMARxCplt;
/* Set the DMA error callback */
hsai->hdmarx->XferErrorCallback = User_SAI_DMAError;
/* Set the DMA Rx abort callback */
hsai->hdmarx->XferAbortCallback = NULL;
/* Enable the Rx DMA Stream */
if (HAL_DMA_Start_IT(hsai->hdmarx, (uint32_t)&hsai->Instance->DR,
(uint32_t)hsai->pBuffPtr, hsai->XferSize) != HAL_OK) {
__HAL_UNLOCK(hsai);
return HAL_ERROR;
}
/* Enable the interrupts for error handling */
__HAL_SAI_ENABLE_IT(hsai, User_SAI_InterruptFlag(hsai, User_SAI_MODE_DMA));
/* Enable SAI Rx DMA Request */
hsai->Instance->CR1 |= SAI_xCR1_DMAEN;
//对比hal库,删减了以下代码
/* Check if the SAI is already enabled */
// if ((hsai->Instance->CR1 & SAI_xCR1_SAIEN) == RESET) {
// /* Enable SAI peripheral */
// __HAL_SAI_ENABLE(hsai);
// }
/* Process Unlocked */
__HAL_UNLOCK(hsai);
return HAL_OK;
} else {
return HAL_BUSY;
}
}
#if 0
static void sai_irq_set(bool enable) {
if (enable == true) {
__HAL_DMA_CLEAR_FLAG(
USER_SAI_RX.hdmarx,
(DMA_ISR_GIF1 << (USER_SAI_RX.hdmarx->ChannelIndex & 0x1CU)));
__HAL_SAI_CLEAR_FLAG(&USER_SAI_TX, SAI_FLAG_OVRUDR | SAI_FLAG_MUTEDET |
SAI_FLAG_WCKCFG | SAI_FLAG_FREQ |
SAI_FLAG_CNRDY | SAI_FLAG_AFSDET |
SAI_FLAG_LFSDET);
__HAL_SAI_CLEAR_FLAG(&USER_SAI_RX, SAI_FLAG_OVRUDR | SAI_FLAG_MUTEDET |
SAI_FLAG_WCKCFG | SAI_FLAG_FREQ |
SAI_FLAG_CNRDY | SAI_FLAG_AFSDET |
SAI_FLAG_LFSDET);
__HAL_SAI_ENABLE_IT(
&USER_SAI_TX, User_SAI_InterruptFlag(&USER_SAI_TX, User_SAI_MODE_DMA));
__HAL_SAI_ENABLE_IT(
&USER_SAI_RX, User_SAI_InterruptFlag(&USER_SAI_RX, User_SAI_MODE_DMA));
} else { //
__HAL_SAI_DISABLE_IT(
&USER_SAI_TX, User_SAI_InterruptFlag(&USER_SAI_TX, User_SAI_MODE_DMA));
__HAL_SAI_DISABLE_IT(
&USER_SAI_RX, User_SAI_InterruptFlag(&USER_SAI_RX, User_SAI_MODE_DMA));
__HAL_DMA_CLEAR_FLAG(
USER_SAI_RX.hdmarx,
(DMA_ISR_GIF1 << (USER_SAI_RX.hdmarx->ChannelIndex & 0x1CU)));
__HAL_SAI_CLEAR_FLAG(&USER_SAI_TX, SAI_FLAG_OVRUDR | SAI_FLAG_MUTEDET |
SAI_FLAG_WCKCFG | SAI_FLAG_FREQ |
SAI_FLAG_CNRDY | SAI_FLAG_AFSDET |
SAI_FLAG_LFSDET);
__HAL_SAI_CLEAR_FLAG(&USER_SAI_RX, SAI_FLAG_OVRUDR | SAI_FLAG_MUTEDET |
SAI_FLAG_WCKCFG | SAI_FLAG_FREQ |
SAI_FLAG_CNRDY | SAI_FLAG_AFSDET |
SAI_FLAG_LFSDET);
}
}
#endif
void user_get_i2s_buf(SAI_DEF *i2s_tx_buffers, SAI_DEF *i2s_rx_buffers) {
i2s_tx_buffers = &sai_tx_t;
i2s_rx_buffers = &sai_rx_t;
}
/*
* @file: .c
* @brief:
* @author: YangWentao
* @version:
* @date:
* @Copyright: 1.0.0
* @par Change Logs: 2024-10-23
*/
#ifndef INC_BSP_SAI_H_
#define INC_BSP_SAI_H_
#include <stdint.h>
#define WAV_SAI_TX_DMA_HALF_SIZE 640
#define WAV_SAI_TX_DMA_ALL_SIZE WAV_SAI_TX_DMA_HALF_SIZE * 2
#define APP_REC_SIZE WAV_SAI_TX_DMA_ALL_SIZE
#define USER_SAI_TX hsai_BlockA1
#define USER_SAI_RX hsai_BlockB1
typedef struct {
uint8_t dma_buf[WAV_SAI_TX_DMA_ALL_SIZE * 2]; // 语音发送缓冲区2560字节
uint8_t *dma_buf2;
uint8_t half_flag; // 语音DMA发送一半
uint8_t done_flag; // 语音DMA发送完成
uint8_t all_done_flag; // 语音全部发送完成
} SAI_DEF;
void u_sai_ready(void);
/**
* @brief 语音缓冲区初始化
*
*/
void bsp_sai_init(void);
/**
* @brief 开启语音发送和接收DMA链路
*
* @param flag
* flag=3时仅开启发送,方便测试APP单项语音
*/
void u_record_ready(uint8_t flag);
/**
* @brief 音频数据处理
*
*/
void user_sai_process(void);
/**
* @brief 停止语音dma
* 已废弃,从模式下无法关闭
*
*/
void user_sai_stop(void);
/**
* @brief 将接收到的语音拷贝纸缓冲区进行播放
*
* @param buf
* @param lens
*/
void bsp_cpoy_sound(uint8_t *buf, uint16_t lens);
/**
* @brief 语音数据流关闭后,清空缓冲区
* 否则下次语音通话建立会影响音质
*
*/
void user_tx_buf_init(void);
/**
* @brief 将蓝牙发送的语音数据进行解码,
* 解码后送到SAI缓冲区,发送至天通进行播放
*
* @param data
* @param lens
*/
void user_dec_uart_data(uint8_t *data, uint16_t lens);
/**
* @brief 获取语音传输缓冲区
*
* @param i2s_tx_buffers
* @param i2s_rx_buffers
*/
void user_get_i2s_buf(SAI_DEF *i2s_tx_buffers, SAI_DEF *i2s_rx_buffers);
#endif
上面的是sai的配置
以上就是全部的总结了,也可以参考【开发】2.1、H7/H743/H750 - lwIP移植(裸机/操作系统)这篇文档,但是这篇文章mpu的配置可能不能满足后续工程的开发,我这个mpu也是研究4天配出来的,目前ping通,串口也可以正常使用后续有问题我在往上加,目前先这样用吧
更多推荐
所有评论(0)