要加的功能

  • 设备识别码:SGMW001、SGMW002递增。服务器连接时发送。串口发送

0R电阻/选择性焊接电阻

电阻位号功能是否焊接
R33SBC_3.3V给整版供电
R71CAN收发器电源
R125MP4462给WIFI供电
R110变压器中心抽头电压
R105WIFI调试贴片灯
R53SBC一个电阻
R122串口供电
R10、R22、R39、R40WIFI侧变压器电阻
R34MP4462是否12V供电3.3V
R126SBC_3.3V给WIFI供电
R14是否同时复位SBC

HF-A21相关引脚

引脚号作用
PG6nReady
PG7nLink
PG3Green
74HC595_Q6Blue
74HC595_Q7Red
74HC595_Q4LAN8720_NRST
PC6UART6_TX波特率
PC7UART6_RX57600

EHT/PHY相关引脚

引脚号引脚名引脚号引脚名
PA1REFCLKPE3595_SER
PA2MDIOPC14595_RCK
PA7CRS_DVPC15595_SCK
PB11TX_EN74HC595_Q3SPI_EN(隔离芯片使能)
PB11-EHT_TXENSPI_EN
PC1MDC
PC4RXD0
PC5RXD1
PG12TXD1
PG13TXD0
WIFI按键:PC13
事件按键:PB1

WIFI不通 :CUBEMX是真坑人

原因

6.12版本的CUBEMX,配置ETH时,Rx内存池的地址,即使你设置好了,导出工程后,并不是你设置的地址(可以在.map文件中查看memp_memory_RX_POOL_base的地址,不是我们设置的0x3000 0200)。此处的空间是ETH的DMA无法访问的区域!, ETH的DMA一直没搬运到数据,所以才会一直ping不通 参考文章:cubemx升级之后导致H7芯片lwip无法ping通

解决方法

修改.sct文件强制把接收数据空间的地址放置到RAM_D2区域(针对H723来说,是0x3000 0000 - 0x3000 800) 在魔术棒里,Linker-取消勾选use memory layout from target dialog,然后就可以点击edit去编辑scatter file了

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
 
LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00020000  {  ; RW data
   .ANY (+RW +ZI)
  }
  RW_IRAM2 0x24000000 0x00020000  {
   .ANY (+RW +ZI)
  }
  
  memory_RX_POOL_base 0x30000200 0x5000{
   *(.Rx_PoolSection)
  }
  
}

自己添加进去的是memory_RX_POOL_base这段代码,0x30000200是基地址,0x5000是分配的大小。(这个大小不太确定,填小了会报错,填多了可以ping通,不过不知道会不会有问题)

opt.h中,开启LWIP_RAW,才能ping外部 在lwipopts.h有 LWIP_RAM_HEAP_POINTER 相关的配置

notes:Lwip使用DMA传递信息,对应的DMA内存定义在sram中。H7的sram分为好几段,高速段为cpu独享,通俗点说就是这一段允许用户编写的程序使用,但是不允许DMA使用。所以为DMA定义的内存或者数组要避开这一段。另外Lwip使用DMA时存在交互存取问题,避开这一段后,也不能让cpu像使用普通cache那样乱序使用,否则将可能出现严重问题。很多人用F7、H7和Lwip协议栈都出现ping不通的现象,都是内存管理问题。怎样管理?需要使用内存守护单元MPU。

notes:ping指令处理过程:PC输入ping指令 传输到物理层phy芯片 phy把数据转化为RMII电平传输给STM32 MAC芯片(stm32h7内置) MAC芯片接收到ping指令并处理 (没有细究ping指令是否要stm32h7内的程序进行处理后才能回复还是直接由内置的MAC芯片直接进行回复) stm32h7通过RMII接口回复 phy芯片的RMII接口接收到数据之后通过网线传输到上位机 上位机接收到回复打印相应信息

STM32H723ZGT6+ETH+Lwip裸机配置

CUBEMX版本:6.12.1 一定要注意版本!老版本和新版本有些地方的配置不同(不过可能影响不大,但不同版本之间bug不同,这个影响很大!)

MPU配置

这个level一定要是上面0下面1,要不然会进硬件错误 下面的两个enable不变,上面四个都可以enable。 但上面四个全enable后,下面四个不能都enable 目前不太知道具体的原因,就按照这么配置就行,不要变了

ETH配置

根据自己的硬件设计选择MII还是RMII ETH中断打开 根据自己硬件连接,选择对应的引脚,并设置高速 还要设置LAN8720A的硬件复位引脚,我这里是用74HC595控制的 我这里还额外设置了WIFI桥接模块相关的引脚

USART

根据自己需求配置 我这里配置的串口1和PC通讯、串口6和桥接模块通讯,波特率统一57600

LWIP

软件配置

__HAL_RCC_D2SRAM1_CLK_ENABLE();

建议把LWIP_Init放到串口初始化之后。加入LAN8720A硬件复位的代码

SCB_CleanInvalidateDCache();

在while中加入LWIP执行的代码

MX_LWIP_Process();

串口重定向

#include "stdio.h"
int fputc(int ch, FILE * f)
{
	HAL_UART_Transmit(&huart1, (uint8_t *) &ch, 1, HAL_MAX_DELAY);
	return ch;
}

🔴🔴🔴 重点来了!!!!! 🔴🔴🔴 魔术棒option-Linker 先勾选Use Memory…编译一遍,让keil生成.sct文件。 然后取消勾选,点击edit,手动添加红框内的代码。编译下载就可以了

memory_RX_POOL_base 0x30000200 0x5000{
   *(.Rx_PoolSection)
  }

因为这个版本的cubemx,虽然在eth.c中有这种编译指令

#elif defined ( __CC_ARM ) /* MDK ARM Compiler */
__attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];

但是,并没有给出extern u8_t memp_memory_RX_POOL_base[];这个定义(应该为0x3000 0200),所以导致 .Rx_PoolSection的地址实际为 0x2000 0000,根本就不是我们在cubemx中设置的0x3000 0200这个地址

H723总线架构学习

总线

在DS(data sheet) 第2章Description有图表,介绍了H723的block框图、外设大小数量 在RM(reference manual)第2章Memory and bus architecture也有

Flash

DS 3.3章Memories有一部分对flash的简略介绍,RM第4章详细介绍了Embedded Flash memory

SDRAM

DS 3.3章Memories有一部分对SRAM的简略介绍 RM 2.4章有更详细的介绍。 如何快速查看SRAM的地址:其实在RM 2.3章有所有的内存映射、寄存器地址边界。不过我们可以在工程中,打开文件stm32h723xx.h,搜索Peripheral_memory_map,也能看到对应的地址

关于TCM的介绍: TCM = tightly coupled memory 紧密耦合内存,他也是SRAM,且是CortexM7内核专用dedicated的,也就是说D1域除了cortexM7内核以外的master,都不能访问,且D2、D3域的master也不能通过总线访问 DTCM是放数据的,地址0x2000 0000 ITCM是放指令的,地址0x0000 0000 cubemx的一个坑:6.12版本,不管在ETH中的Rx Buffer Addr设置的多少,cubemx生成后的项目编译后,查看.map文件,发现这个地址是在DTCM的起始地址,从而导致ETH无法访问这里的数据。 阅读RM 第63章,ETH内嵌了一个专用的MDA,The DMA block exchanges data between the peripheral and the system memory。再看RM的系统架构图,ETH在D2域,他能直接访问的是SRAM1和2。 所以我们应该手动修改配置,使memp_memory_RX_POOL_base指向正确的地址

// .map文件
memp_memory_RX_POOL_base                 0x20000000   Data       18819  ethernetif.o(.Rx_PoolSection)

Cortex-M7内核Cache和MPU

MPU

参考正点原子的教程

可以设置16个region,每个区域还可以设置sub region子域。 可以启用背景区域,即没被MPU设置的其他所有地址,背景区域只允许特权访问 region区域可以重叠,重叠部分,遵守region区域号大的规则 几个重要的寄存器:CTRL、RNR、RBAR 和 RASR |1000

CTRL控制 寄存器

只有低3位有效 PRIV DEF ENA:是否开启背景region。 1:未开启任何region,并使能MPU时,允许特权级程序访问所有地址,用户级程序卡死;开启了region并使能MPU时,背景region与普通region重合区域,受到规则限制 0:未配置region会被视为无访问权限,需手动为应用内存添加 MPU 区域定义 HFNMIENA:是否在 NMI 和硬件fault 中断服务例程中禁止 MPU 一般设置为0 ENABLE:是否使能MPU

RNR 区域 编号 寄存器

选择要配置哪个region

RBAR 区域 基地址 寄存器

ADDR:region的基地址。基地址在数值上要能被region容量整除 VALID: 当 VALID=1 时,RBAR 中的REGION 字段会覆盖MPU_RNR 寄存器中预先设置的区域编号;
当 VALID=0 时,RBAR 中的 REGION 字段无效,使用 MPU_RNR 中设置的区域编号通常VALID = 0,通过RNR寄存器来配置region号

RASR区域 属性 和 容量 寄存器

XN:用于控制是否允许从此区域取指令 XN=1,禁止从区域取指令,如果强行取指令,将产生一个 MemManage 异常 XN=0,允许取指令,一般 ENABLE 对应CUBEMX里的MPU Instruction Access AP:控制数据的访问权限(访问许可) |1000 TEX、S、C 、 B:这几位比较重要,通过不同的排列组合,来控制cache的功能。通常只关注TEX = 0b000、0b001时的组合方式 C:该区域是否开启cache S: S=1,则二级存储器不可以缓存(Cache) S=0,则可以缓存(Cache),一般我们设置该位为 0 即可 B: B=1,则二级存储器可以缓冲,即写回模式 write back B=0,则二级存储器不可以缓冲,即写通模式 write through |1000 |1000

⚠️硬汉哥的图 很直观

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 = 0x00;
  MPU_InitStruct.Size = MPU_REGION_SIZE_4GB;
  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_NOT_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  /** 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.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE;
  /** Initializes and configures the Region and the memory to be protected  */
  MPU_InitStruct.Number = MPU_REGION_NUMBER2;
  MPU_InitStruct.BaseAddress = 0x30004000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_16KB;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
 
}

Cache

cache基础

cache作用:将数据提前加载到Cache中,当CPU需要时,再将该数据提供给CPU,提高STM32访问内存的速度 因为在F7、H7高级的芯片上,内核的工作频率,已经高于片内SRAM的工作频率了。(AXI SRAM、SRAM1、SRAM2的频率一般在240MHz,而H7芯片的频率已经达到了480MHz,Cache的工作频率也是480MHz) 类似于我们的电脑上,L1、L2、L3这三级缓存,但M7内核只有1级缓存,即L1-cache

没有cache时,CPU读写SRAM的流程是这样的

存在cache时,CPU读写SRAM的流程是这样的 |500

https://blog.csdn.net/weixin_42501188/article/details/146292864

使能cache的SRAM区域的读写操作类型

读操作:如果CPU要读的数据在cache中已经加载好,就叫做读命中cache hit,若没有,就是cache miss

  • cache hit:直接走线路3即可读到数据
  • cache miss:分两种情况
    • read allocate:走路线2,数据从SRAM加载到cache,再线路3从cache读取数据。在Cache中开辟对应的数据空间,以后直接从Cache中拿数据就行
    • read through:不读cache,走线路1,直接从SRAM中直接读取数据

写操作:如果CPU要写的 SRAM 区数据在 Cache 中已经开辟了对应的区域,就叫写命中 cache hit,没有就是cache miss

  • cache hit
    • write through:同时走线路1和3,cache和SRAM同时更新数据
    • write back:只走线路3,数据只写到cache中。当数据从cache中清除时,再走路线2,写入SRAM中
  • cache miss
    • write allocate:线路2,把数据从SRAM加载到cache中,再线路3,对cache中的数据进行写入,再线路2,把数据写入SRAM
    • no write allocate:线路1,直接写入SRAM

cache配置

cache是通过MPU的寄存器来控制的 |1000 cache支持4种策略:(M7 内核只要开启了 Cache,read allocate 就是开启的)

  1. none-cacheable
  2. write back、 write allocate、 read allocate
  3. write through、no write allocate、read allocate
  4. write back、 no write alloca、 read allocate

none-cacheable

不开启cache,对应C = 0的情况

Write back,read allocate,write allocate

|1000 写回(hit):先写到cache里,等cache数据清空时,再写到SRAM中(安全隐患:若此时DMA读取该数据,而cache的数据还没更新到SRAM中,则读取的数据是错的) 写分配(miss):把数据从SRAM加载到cache,再更新cache 读(hit):直接从cache中读取已经加载好的需要的数据(安全隐患:若hit时,DMA更新了SRAM中的数据,则读取的数据是错的) 读分配(miss):在cache中开辟空间,把数据从SRAM中加载进来

报文收发存储逻辑

CAN收发器:电平逻辑转换 CAN控制器:

MCP2515后五路

收发器转换CAN电平,给控制器,控制器收到一帧完整的CAN报文,中断引脚产生中断给MCU,进入中断程序。 在中断里面,给报文的结构体,给时间戳、通过SPI读MCP2515控制器的报文内容(拉低片选,传输函数,拉高片选),判断通道、ID转换,写入fifo 前三路基本一样,只是走了SBC芯片

时间戳的RTC是IIC协议

存储是从FIFO里取出来,add到blf文件里

优先级翻转

以一个简单的示例场景来解释:
假设有三个任务,分别为高优先级任务Task_H、中等优先级任务Task_M和低优先级任务Task_L,且系统采用基于优先级的抢占式调度策略。同时,系统中存在一个共享资源,通过互斥信号量(Mutex)来进行访问控制。

  1. 低优先级任务获取资源:低优先级任务Task_L先运行,并成功获取了互斥信号量,开始访问共享资源。
  2. 高优先级任务抢占:此时,高优先级任务Task_H就绪并抢占 CPU 开始运行。但当Task_H也需要访问同一共享资源时,由于互斥信号量已被Task_L持有,Task_H只能进入阻塞状态,等待Task_L释放信号量。
  3. 中等优先级任务抢占Task_L继续运行,在其还未释放互斥信号量时,中等优先级任务Task_M就绪。由于Task_M优先级高于Task_LTask_M抢占Task_L的 CPU 使用权开始运行。这样就导致Task_L无法及时释放互斥信号量,进而使得高优先级的Task_H一直处于阻塞等待状态,出现了高优先级任务被中等优先级任务间接阻塞的情况,即优先级翻转。
  • 优先级继承:当高优先级任务因等待低优先级任务持有的互斥信号量而阻塞时,低优先级任务的优先级会被临时提升到与高优先级任务相同(或根据具体实现提升到一个合适的较高优先级)。这样,低优先级任务就能在不被中等优先级任务抢占的情况下,尽快执行完并释放互斥信号量,高优先级任务也能及时获得信号量并运行。当低优先级任务释放信号量后,其优先级再恢复到原来的水平。FreeRTOS 中的互斥信号量默认支持优先级继承机制。

TCPIP

IP协议架构

MDK的编译过程和产生的文件类型讲解

Microcontroller Development kit

编译过程

机器码就是01组合而成的二进制数据,直接与硬件交互,CPU直接执行,但是大多数人基本看不懂,也不会写。 为了更好的编程,人们发明了汇编语言(ADD R1, R2这样的),用汇编语言写完后,由汇编器转成机器码,方便了人们编程。

buid时,先把.c文件和.s文件通过工具,编成.o文件,每个.o文件都对应.c或. s文件,.o文件是分散的,没什么地址的关联。 通过链接器,将这些分散的.o文件和库文件,链接成映像文件.axf 最后通过格式转换器,转换成hex或bin文件,可以下载到单片机中 如果用cubemx生成的工程,在ARM-MDK/工程名 文件夹中可以看到这些文件,除了.o .axf这些文件外,还有一些其他的文件

程序的组成、存储、运行

编译‍程序后输出的信息:Program Size:Code=xx RO-data=xx RW-data=xx ZI-data=xx 编译后,应用程序中所有具有同一性质的数据、代码会被归到一个域 .text:相当于 RO code .constdata:相当于 RO data .bss:相当于 ZI data .data:相当于 RW data

区域名称含义存储内容是否占用 Flash是否占用 RAM特性也可能称为
Code代码段程序代码(指令)只读、固定大小.text
RO-data只读数据段常量(如const变量、字符串字面量“hello”)只读、固定大小.constdata
RW-data读写数据段初始化为非 0 的全局 / 静态变量✅(初始值)✅(运行时)可读写、需初始化.data
ZI-data零初始化数据段(BSS)未初始化或初始化为 0 的全局 / 静态变量自动清零、不占 Flash.bss
Stack局部变量、函数调用上下文自动管理、后进先出
Heap动态分配的内存(如malloc手动管理、灵活分配
编译器给出的 ZI-data 占用的空间值中包含了stackheap的大小 (经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把heap空间计算在内)
程序的存储和运行

选择完芯片后,会有芯片的Flash和SRAM,编写完的程序不能超过这些存储器的大小,如果超出了,编译器会提示错误,我们就可以根据不同数据存储的位置,进行代码裁剪

程序的状态组成
程序静止存储时Code+RO data+RW dataFlash
程序执行的RW区域RW data + ZI dataRAM
程序执行的RO区域RO data + code
优先级等级优先级宏定义任务名称说明(任务功能)
1(最高)osPriorityRealtimeFatfsInit文件系统初始化(实时优先级,最紧急)
2osPriorityAboveNormalCAN_LEDCAN 状态 LED 指示(高于正常优先级)
3osPriorityNormalUsartDataProcesUSART 数据处理(正常优先级)
3osPriorityNormalCanDispCAN 数据显示(正常优先级)
3osPriorityNormalPowerDown_Autosave掉电自动保存(正常优先级)
3osPriorityNormalWiFi_InitWiFi 初始化(正常优先级)
4osPriorityLowKey_Rename特殊按键改名(低优先级)
4osPriorityLowTime_Autosave时间自动保存(低优先级)
4osPriorityLowChannel_Down通道减少蜂鸣器控制(低优先级)
5osPriorityBelowNormalLoop_Record循环录制任务(低于低优先级)

SDMMC

引脚号引脚功能
PC8SD_D0
PC9SD_D1
PC10SD_D2
PC11SD_D3
PC12SD_CLK
PD2SD_CMD

WIFI灯的策略

1、红灯常亮 WiFi模块异常
2、绿灯常亮 模块正常且已与其他建立连接但处于“待机状态”没有数据传输
3、绿灯快闪 模块正常且处于数据可传输状态(数据正在传输)
4、绿色慢闪 设备处于等待中(与服务器连接)
5、蓝灯常亮 设备正常,且无连接

  • 分析
    • 绿色:已经与服务器建立TCP连接。 常亮:建立TCP连接,快闪:数据传输,慢闪:正在连接
    • 蓝色:模块正常,但未建立TCP连接
    • 红色:模块异常

PCB修改

  • 侧边串口的typeC外移:改不了
    • 找有没有延长的c口 C6332278
  • wifi模块改变压器方案
    • 五零那边小助手用的就是变压器方案,说明另一个方案肯定有问题
    • 偏置电压AMS1117要留着
    • WIFI Reload 可酌情删除
  • LAN8720复位问题
  • 串口供电:改成2电阻分压,默认低电平,拨码联通高电平
    • 是否改成默认高,按下低?
  • 丝印:led灯、几个拨码开关都写清楚

PHY 芯片接口直连(不使用变压器)的设计

blog.csdn.net/qq_21794157/article/details/124494243

基础知识:网口 PHY 芯片对 TX 和 RX 信号有两种驱动方式:电压驱动和电流驱动。不 同的驱动方式决定了 PHY 在与变压器连接的时候,变压器的中心抽头的接法。

电压驱动型的 PHY,变压器的中心抽头接电容到地

电流驱动型的 PHY,变压器的中心抽头接电源,电源大小即为 PHY 芯片的 UTP 端口电压;

从LAN8720A的手册中,看变压器连接方式,变压器中心抽头接VCC,所以是电流型驱动的PHY芯片

image

V1.4.2版本WIFI尝试验证

  • MCU串口1和PC通信:PA9_TX PA10_RX
  • MCU串口6和HF通信:PC6_TX PC7_RX
  • LAN8720A相关引脚(ETH):image
    • 尤其注意ETH_TX_EN的PB11是接在隔离芯片上的。需要拉低SPI_EN才能使隔离芯片连通。SPI_EN的拉低,需要靠74HC595芯片驱动
    • imageimage
  • 74HC595芯片相关:
    • PE3_595_SER PC14_595_RCK PC15_595_SCK
  • WIFI模块相关: PG7_nLink
  • MCU的ip设置在lwip.c中

以太网转发

mcu的IP、掩码、网关在lwip.c中

要连接的服务器ip在clientFTP.h中

  • 他有些指令有的时候会回复好几个➕ok,就直接delay当接收到回应了。 回头我改成串口收到+ok
  • 你先把其他要做的继续往前做了,能用有线模拟的先用有线,包括从以太网打包的CAN报文的转发和接收
  • 几天抓紧通过有线的板子把诊断报文收发的协议实现了啊,硬件这块我搞好了告诉你怎么改.从以太网数据收发 到 CAN收发都需要关联好的

服务器的 发can

服务端发出的数据:
通道号(1B) 请求ID(2B) 响应ID(2B) DLC(2B) DATA (实际长度)
返回数据一样:
通道号(1B) 请求ID(2B) 响应ID(2B) DLC(2B) DATA (实际长度)

image

VCI:
WIFi模块的TXN、TXP、RXN、RXP直连变压器
PHY芯片是KSZ8041NLI,手册上没看到连变压器的电路,看板子TX+-和RX+-走线被那个变压器挡住了。好像是直接连到变压器上了

我要做的: CAN_Read_FIFO(&CAN_Data_msg1); 就读可以读取设备收到的CAN报文

接收服务端报文socket_read
通过CAN发送出去MCP2515_Tx or CAN_SendPacket
接收CAN报文CAN_Read_FIFO()
发回服务端socket_write

前三路是can_sendpacket发送出去,后五路是MCP2515_Tx发出去

在主控芯片为STM32H723ZG的PCB板上,使用HAL库,实现MCU接收以太网报文,将其转换为CAN报文,发送到8路CAN中的具体某一路CAN通道上;CAN通道收到报文后经过处理,返回相应的报文,MCU接收到CAN通道的报文后,将其转换为以太网报文,通过以太网发送回去,

具体要求如下: 1.以太网发送的报文数据格式如下:通道号(1B) 请求ID(2B) 响应ID(2B) DLC(2B) DATA (实际长度) 2.返回以太网的报文格式如下:通道号(1B) 请求ID(2B) 响应ID(2B) DLC(2B) DATA (实际长度) 3.CAN报文发送函数使用这个 //CAN报文发送函数 //CAN_ID:发送的CAN ID //len:发送的数据字节数 //pdata:要发送的数据所存放的位置 void MCP2515_TxMessage(SPI_HandleTypeDef hspi, uint32_t CAN_ID,uint8_t len,uint8_t * pdata) { MCP2515_Single_Write(hspi, 0x30, 0x03); //设置发送最高优先级 MCP2515_Single_Write(hspi, 0x31, (uint8_t)(CAN_ID>>3)); //ID高位 MCP2515_Single_Write(hspi, 0x32, (uint8_t)(CAN_ID & 0x0007)<<5); //ID低位 MCP2515_Single_Write(hspi, 0x35, len); //RTR和DLC MCP2515_Write_TxBuff(hspi, TXB0D0, 8, pdata); MCP2515_RTS_Req(hspi, TXB0_RTS); } 4.CAN报文接收读取函数使用这个 // 读软件FIFO uint8_t CAN_Read_FIFO(CAN_Data_msg_obj * CAN_Data_msg1) { memcpy(CAN_Data_msg1,CAN_Data_Read,CAN_Data_struct_size); CAN_Data_Read += (CAN_Data_struct_size); if(CAN_Data_Read >= (uint32_t * )CAN_BUFF_END_ADDR) { CAN_Data_Read = (uint32_t * )CAN_BUFF_BEGIN_ADDR; } msg_num—;

return 0; } 5.CAN_Data_msg_obj结构体: typedef struct { uint16_t Ext_ID; //扩展ID,若为扩展帧则为ID高两个字节,标准帧则为0 uint16_t Std_ID; //标准ID uint16_t error_type; //错误帧类型 uint8_t Data_Flag; //标识符 uint8_t dlc; uint8_t CAN_Data[8]; }CAN_Data_msg_obj;

#include "stm32h7xx_hal.h"
#include <string.h>
 
// 假设的SPI句柄,实际使用时需要正确初始化
extern SPI_HandleTypeDef hspi;
 
// 以太网接收缓冲区大小
#define ETHERNET_RX_BUFFER_SIZE 256
// CAN报文接收缓冲区大小
#define CAN_RX_BUFFER_SIZE 256
 
// 以太网接收缓冲区
uint8_t ethernet_rx_buffer[ETHERNET_RX_BUFFER_SIZE];
// CAN报文接收缓冲区
CAN_Data_msg_obj can_rx_buffer[CAN_RX_BUFFER_SIZE];
 
// CAN报文发送函数
// CAN_ID:发送的CAN ID
// len:发送的数据字节数
// pdata:要发送的数据所存放的位置
void MCP2515_TxMessage(SPI_HandleTypeDef hspi, uint32_t CAN_ID, uint8_t len, uint8_t * pdata)
{
    MCP2515_Single_Write(hspi, 0x30, 0x03);              // 设置发送最高优先级
    MCP2515_Single_Write(hspi, 0x31, (uint8_t)(CAN_ID>>3));       // ID高位
    MCP2515_Single_Write(hspi, 0x32, (uint8_t)(CAN_ID & 0x0007)<<5); // ID低位
    MCP2515_Single_Write(hspi, 0x35, len);               // RTR和DLC
    MCP2515_Write_TxBuff(hspi, TXB0D0, 8, pdata);
    MCP2515_RTS_Req(hspi, TXB0_RTS);
}
 
// 读软件FIFO
uint8_t CAN_Read_FIFO(CAN_Data_msg_obj * CAN_Data_msg1)
{
    memcpy(CAN_Data_msg1, CAN_Data_Read, CAN_Data_struct_size);
    CAN_Data_Read += (CAN_Data_struct_size);
    if(CAN_Data_Read >= (uint32_t * )CAN_BUFF_END_ADDR)
    {
        CAN_Data_Read = (uint32_t * )CAN_BUFF_BEGIN_ADDR;
    }
    msg_num--;
 
    return 0;
}
 
// CAN_Data_msg_obj结构体
typedef struct 
{
    uint16_t Ext_ID; // 扩展ID,若为扩展帧则为ID高两个字节,标准帧则为0
    uint16_t Std_ID; // 标准ID
    uint16_t error_type; // 错误帧类型
    uint8_t Data_Flag; // 标识符
    uint8_t dlc;
    uint8_t CAN_Data[8];
} CAN_Data_msg_obj;
 
// 以太网接收处理函数
void Ethernet_Receive_Handler(uint8_t *rx_buffer, uint16_t rx_length)
{
    if (rx_length < 7) return; // 至少需要通道号、请求ID、响应ID、DLC
 
    uint8_t channel = rx_buffer[0];
    uint16_t request_id = (rx_buffer[1] << 8) | rx_buffer[2];
    uint16_t response_id = (rx_buffer[3] << 8) | rx_buffer[4];
    uint16_t dlc = (rx_buffer[5] << 8) | rx_buffer[6];
 
    uint32_t can_id = (uint32_t)request_id; // 简单示例,使用请求ID作为CAN ID
    uint8_t *data = &rx_buffer[7];
 
    MCP2515_TxMessage(hspi, can_id, dlc, data);
}
 
// CAN接收处理函数
void CAN_Receive_Handler()
{
    CAN_Data_msg_obj can_msg;
    if (CAN_Read_FIFO(&can_msg) == 0) // Read正常
    {
        uint8_t channel = 1; // 假设通道号为1,可根据实际情况修改
        uint16_t request_id = can_msg.Std_ID;
        uint16_t response_id = 0; // 可根据实际情况修改
        uint16_t dlc = can_msg.dlc;
 
        uint8_t ethernet_tx_buffer[ETHERNET_RX_BUFFER_SIZE];
        ethernet_tx_buffer[0] = channel;
        ethernet_tx_buffer[1] = (request_id >> 8) & 0xFF;
        ethernet_tx_buffer[2] = request_id & 0xFF;
        ethernet_tx_buffer[3] = (response_id >> 8) & 0xFF;
        ethernet_tx_buffer[4] = response_id & 0xFF;
        ethernet_tx_buffer[5] = (dlc >> 8) & 0xFF;
        ethernet_tx_buffer[6] = dlc & 0xFF;
        memcpy(&ethernet_tx_buffer[7], can_msg.CAN_Data, dlc);
 
        // 调用以太网发送函数发送转换后的以太网报文
        Ethernet_Send(ethernet_tx_buffer, 7 + dlc);
    }
}
 
// 主函数
int main(void)
{
    HAL_Init();
    // 初始化以太网和CAN等外设
    // MX_ETH_Init();
    // MX_CAN_Init();
 
    while (1)
    {
        // 检查以太网是否有数据接收
         if (Ethernet_Is_Data_Received())
         {
             uint16_t rx_length = Ethernet_Receive(ethernet_rx_buffer);
             Ethernet_Receive_Handler(ethernet_rx_buffer, rx_length);
         }
 
        // 检查CAN是否有数据接收
         if (CAN_Is_Data_Received())
         {
             CAN_Receive_Handler();
         }
    }
}    

代码移植

使用cubemx生成工程后, 仿照其风格,手动添加FreeRTOS_V1任务、信号量等

|500

 
1.创建任务
// 任务句柄
osThreadId WiFi_InitHandle;
// 声明任务函数
void Task_WiFi_Init(void const * argument);
// 关联任务句柄和任务函数
void MX_FREERTOS_Init(void) 
{
  osThreadDef(WiFi_Init, Task_WiFi_Init, osPriorityIdle, 0, 128); // 关联 任务名 任务函数
  WiFi_InitHandle = osThreadCreate(osThread(WiFi_Init), NULL); // 关联 任务句柄 任务名进程
}

 
2. 创建二值信号量
// 声明 信号量句柄
osSemaphoreId WifiHandle;
// 关联 信号量句柄 和 信号量
void MX_FREERTOS_Init(void) 
{
  osSemaphoreDef(Wifi);
  WifiHandle = osSemaphoreCreate(osSemaphore(Wifi), 1); // 这个1表示可以被take
}

ETH寄存器

Channel status register (ETH_DMACSR)

位号寄存器名作用
0TI: Transmit Interruptpacket transmission is complete
1TPS: Transmit Process Stopped发送停止时set
2TBU: Transmit Buffer Unavailable发送列表中有下一个描述符,但是DMA无法获取。Transmission is suspended
6RI: Receive Interruptpacket reception is complete
7RBU: Receive Buffer Unavailable接收列表中有下一个描述符,但是DMA无法获取。Receive is suspended
8RPS: Receive Process Stopped接收程序停止时asserted
9RWT: Receive Watchdog Timeout接收包超过2048byte时asserted
10ETI: Early Transmit Interrupt要发送的数据全都进到 MTL Tx FIFO
11ERI: Early Receive InterruptDMA已填充数据包的第一个数据缓冲区。该寄存器的RI位会自动清除该位
12FBE: Fatal Bus Error发生总线错误(如EB字段所述TEB/REB)。set后,相应的DMA通道引擎将禁用所有总线访问
13CDE: Context Descriptor ErrorDMA Tx/Rx引擎接收到描述符错误
14AIS: Abnormal Interrupt Summary暂时不考虑
15NIS: Normal Interrupt Summary暂时不考虑
16-18TEB[2:0]: Tx DMA Error Bits总线错误类型。
16:读传输时出错1,写传输时出错0
17:描述符访问时出错1,数据缓冲区访问时出错0
18:Tx DMA在数据传输过程中出错1,Tx DMA在数据传输期间无错误0
仅在FEB置位时有效。不会产生中断
19-21REB[2:0]: Rx DMA Error Bits19:读传输时出错1,写传输时出错0
20:描述符访问时出错1,数据缓冲区访问时出错0
21:Rx DMA在数据传输过程中出错1,Rx DMA在数据传输期间无错误0
仅在FEB置位时有效。不会产生中断

诊断集成

CANFD发送

BitRateSwitch FDFormat 这两个参数要修改

ECU节点诊断测试

1001诊断会话控制 040724072C00021001 发:02 10 01 AA AA AA AA AA 收:06 50 01 00 32 01 F4 AA

1003诊断控制会话1 040724072C00021003 发:02 10 03 AA AA AA AA AA 收:06 50 03 00 32 01 F4 AA 根据标识符读取数据 040724072C000322F191 发:03 22 F1 91 AA AA AA AA 收:07 62 F1 91 01 6A 39 68 根据标识符读取数据1 040724072C000322F190 发:03 22 F1 90 AA AA AA AA 收:10 14 62 F1 90 4C 5A 57 发:30 00 00 AA AA AA AA AA 收:21 31 32 33 34 35 36 37 收:22 38 39 30 31 32 31 32

新供电版本板子

DC插头:C17701683,外径6.5,内径5.5,柱直径2。实际只能做2.1的线