要加的功能
- 设备识别码:SGMW001、SGMW002递增。服务器连接时发送。串口发送
0R电阻/选择性焊接电阻
| 电阻位号 | 功能 | 是否焊接 |
|---|---|---|
| R33 | SBC_3.3V给整版供电 | ✅ |
| R71 | CAN收发器电源 | ✅ |
| R125 | MP4462给WIFI供电 | ❌ |
| R110 | 变压器中心抽头电压 | ❌ |
| R105 | WIFI调试贴片灯 | ❌ |
| R53 | SBC一个电阻 | ✅ |
| R122 | 串口供电 | ✅ |
| R10、R22、R39、R40 | WIFI侧变压器电阻 | ➖ |
| R34 | MP4462是否12V供电3.3V | ❌ |
| R126 | SBC_3.3V给WIFI供电 | ❌ |
| R14 | 是否同时复位SBC | ❌ |
HF-A21相关引脚
| 引脚号 | 作用 | |
|---|---|---|
| PG6 | nReady | |
| PG7 | nLink | |
| PG3 | Green | |
| 74HC595_Q6 | Blue | |
| 74HC595_Q7 | Red | |
| 74HC595_Q4 | LAN8720_NRST | |
| PC6 | UART6_TX | 波特率 |
| PC7 | UART6_RX | 57600 |
EHT/PHY相关引脚
| 引脚号 | 引脚名 | 引脚号 | 引脚名 |
|---|---|---|---|
| PA1 | REFCLK | PE3 | 595_SER |
| PA2 | MDIO | PC14 | 595_RCK |
| PA7 | CRS_DV | PC15 | 595_SCK |
| PB11 | TX_EN | 74HC595_Q3 | SPI_EN(隔离芯片使能) |
| PB11-EHT_TXEN | SPI_EN | ||
| PC1 | MDC | ||
| PC4 | RXD0 | ||
| PC5 | RXD1 | ||
| PG12 | TXD1 | ||
| PG13 | TXD0 | ||
| 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

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:控制数据的访问权限(访问许可)
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


⚠️硬汉哥的图 很直观

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的流程是这样的

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的寄存器来控制的
cache支持4种策略:(M7 内核只要开启了 Cache,read allocate 就是开启的)
- none-cacheable
- write back、 write allocate、 read allocate
- write through、no write allocate、read allocate
- write back、 no write alloca、 read allocate
none-cacheable
不开启cache,对应C = 0的情况
Write back,read allocate,write allocate
写回(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)来进行访问控制。
- 低优先级任务获取资源:低优先级任务
Task_L先运行,并成功获取了互斥信号量,开始访问共享资源。 - 高优先级任务抢占:此时,高优先级任务
Task_H就绪并抢占 CPU 开始运行。但当Task_H也需要访问同一共享资源时,由于互斥信号量已被Task_L持有,Task_H只能进入阻塞状态,等待Task_L释放信号量。 - 中等优先级任务抢占:
Task_L继续运行,在其还未释放互斥信号量时,中等优先级任务Task_M就绪。由于Task_M优先级高于Task_L,Task_M抢占Task_L的 CPU 使用权开始运行。这样就导致Task_L无法及时释放互斥信号量,进而使得高优先级的Task_H一直处于阻塞等待状态,出现了高优先级任务被中等优先级任务间接阻塞的情况,即优先级翻转。
- 优先级继承:当高优先级任务因等待低优先级任务持有的互斥信号量而阻塞时,低优先级任务的优先级会被临时提升到与高优先级任务相同(或根据具体实现提升到一个合适的较高优先级)。这样,低优先级任务就能在不被中等优先级任务抢占的情况下,尽快执行完并释放互斥信号量,高优先级任务也能及时获得信号量并运行。当低优先级任务释放信号量后,其优先级再恢复到原来的水平。FreeRTOS 中的互斥信号量默认支持优先级继承机制。
TCPIP
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 占用的空间值中包含了stack和heap的大小 (经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把heap空间计算在内) | ||||||
| 程序的存储和运行: | ||||||
![]() |
选择完芯片后,会有芯片的Flash和SRAM,编写完的程序不能超过这些存储器的大小,如果超出了,编译器会提示错误,我们就可以根据不同数据存储的位置,进行代码裁剪
| 程序的状态 | 组成 | |
|---|---|---|
| 程序静止存储时 | Code+RO data+RW data | ⇐Flash |
| 程序执行的RW区域 | RW data + ZI data | ⇐RAM |
| 程序执行的RO区域 | RO data + code |
| 优先级等级 | 优先级宏定义 | 任务名称 | 说明(任务功能) |
|---|---|---|---|
| 1(最高) | osPriorityRealtime | FatfsInit | 文件系统初始化(实时优先级,最紧急) |
| 2 | osPriorityAboveNormal | CAN_LED | CAN 状态 LED 指示(高于正常优先级) |
| 3 | osPriorityNormal | UsartDataProces | USART 数据处理(正常优先级) |
| 3 | osPriorityNormal | CanDisp | CAN 数据显示(正常优先级) |
| 3 | osPriorityNormal | PowerDown_Autosave | 掉电自动保存(正常优先级) |
| 3 | osPriorityNormal | WiFi_Init | WiFi 初始化(正常优先级) |
| 4 | osPriorityLow | Key_Rename | 特殊按键改名(低优先级) |
| 4 | osPriorityLow | Time_Autosave | 时间自动保存(低优先级) |
| 4 | osPriorityLow | Channel_Down | 通道减少蜂鸣器控制(低优先级) |
| 5 | osPriorityBelowNormal | Loop_Record | 循环录制任务(低于低优先级) |
SDMMC
| 引脚号 | 引脚功能 |
|---|---|
| PC8 | SD_D0 |
| PC9 | SD_D1 |
| PC10 | SD_D2 |
| PC11 | SD_D3 |
| PC12 | SD_CLK |
| PD2 | SD_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芯片
V1.4.2版本WIFI尝试验证
- MCU串口1和PC通信:PA9_TX PA10_RX
- MCU串口6和HF通信:PC6_TX PC7_RX
- LAN8720A相关引脚(ETH):
- 尤其注意ETH_TX_EN的PB11是接在隔离芯片上的。需要拉低SPI_EN才能使隔离芯片连通。SPI_EN的拉低,需要靠74HC595芯片驱动
-
- 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 (实际长度)
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(ðernet_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任务、信号量等

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