STM32+LAN9252 实现 EtherCAT 从站 IO 控制(完整落地方案)
EtherCAT 是工业以太网中高性能的实时总线,LAN9252 是 Microchip 推出的EtherCAT 从站控制器(ESC),可快速实现 STM32 与 EtherCAT 主站(如倍福 TwinCAT)的通信,进而完成 IO 的实时控制。以下是从硬件选型、连接、软件实现到调试的全流程方案,新手也能落地。
一、方案整体设计
1. 核心硬件选型
| 组件 | 选型建议 | 核心作用 |
|---|---|---|
| STM32 主控 | STM32F407ZGT6(推荐) | 运行 EtherCAT 逻辑、IO 控制 |
| EtherCAT 从站 | LAN9252(SPI 接口版) | 处理 EtherCAT 协议、物理层通信 |
| 扩展 IO | 光耦隔离 IO 模块(可选) | 数字输入(DI)/ 输出(DO)控制 |
| 电源 | 5V/3.3V 双电源 | 给 STM32 和 LAN9252 供电 |
2. 硬件连接(关键)
LAN9252 与 STM32 通过SPI 接口通信(LAN9252 的 ESC 核心仅支持 SPI/I2C,SPI 速率更高),硬件连接如下(Mermaid 可视化):
graph LR
A[STM32F407] -->|SPI_SCK| B[LAN9252]
A -->|SPI_MOSI| B
A -->|SPI_MISO| B
A -->|SPI_NSS/CS(PA4)| B
A -->|INT(PA0,中断)| B // LAN9252中断输出,通知STM32数据更新
A -->|GPIO(PB0-PB7)| C[DI/DO模块] // 控制扩展IO
B -->|ETH_TX/RX| D[RJ45网口] // 连接EtherCAT主站
E[5V电源] -->|转3.3V| A
E --> B // LAN9252支持5V/3.3V供电
关键电气注意事项:
- LAN9252 的 SPI 电平为 3.3V,需和 STM32 SPI 引脚电平匹配;
- DI/DO 建议做光耦隔离,避免工业现场干扰;
- LAN9252 的 RJ45 需接终端电阻(多数模块已集成)。
3. 软件架构
采用分层设计,降低耦合,新手易理解:
├── 底层驱动
│ ├── LAN9252 SPI驱动(ESC寄存器读写)
│ ├── STM32 HAL SPI/IO驱动
│ └── 中断驱动(LAN9252 INT引脚)
├── EtherCAT协议层
│ ├── ESC状态机(INIT→PREOP→SAFEOP→OP)
│ ├── PDO映射(过程数据对象,实时IO数据)
│ └── SDO配置(服务数据对象,参数配置)
└── 应用层
├── IO读写逻辑
└── 主站数据交互
二、核心代码实现(基于 STM32 HAL 库)
以下是关键代码模块,完整可运行(需适配你的 STM32 工程):
1. 第一步:LAN9252 ESC 寄存器读写驱动(SPI)
LAN9252 的 ESC 核心寄存器是 EtherCAT 通信的核心,需通过 SPI 读写:
#include "stm32f4xx_hal.h"
#include "lan9252.h"
// SPI句柄(需在CubeMX中初始化SPI1,速率≤18MHz)
extern SPI_HandleTypeDef hspi1;
#define LAN9252_CS_PIN GPIO_PIN_4
#define LAN9252_CS_PORT GPIOA
// CS引脚拉低(选中LAN9252)
static void LAN9252_CS_LOW(void) {
HAL_GPIO_WritePin(LAN9252_CS_PORT, LAN9252_CS_PIN, GPIO_PIN_RESET);
}
// CS引脚拉高(取消选中)
static void LAN9252_CS_HIGH(void) {
HAL_GPIO_WritePin(LAN9252_CS_PORT, LAN9252_CS_PIN, GPIO_PIN_SET);
}
/**
* @brief 读取LAN9252 ESC寄存器
* @param addr: 寄存器地址(ESC地址空间,32位)
* @param data: 读取的数据缓冲区
* @param len: 读取长度(字节)
*/
void LAN9252_ESC_Read(uint32_t addr, uint8_t *data, uint16_t len) {
LAN9252_CS_LOW();
// 发送读命令+地址(LAN9252 SPI格式:位7=1(读),位6-0=地址高7位;后续3字节=地址低24位)
uint8_t cmd[4] = {0x80 | ((addr >> 24) & 0x7F), (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF};
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
// 读取数据
HAL_SPI_Receive(&hspi1, data, len, 100);
LAN9252_CS_HIGH();
}
/**
* @brief 写入LAN9252 ESC寄存器
* @param addr: 寄存器地址
* @param data: 要写入的数据
* @param len: 写入长度
*/
void LAN9252_ESC_Write(uint32_t addr, uint8_t *data, uint16_t len) {
LAN9252_CS_LOW();
// 发送写命令+地址(位7=0(写))
uint8_t cmd[4] = {((addr >> 24) & 0x7F), (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF};
HAL_SPI_Transmit(&hspi1, cmd, 4, 100);
// 写入数据
HAL_SPI_Transmit(&hspi1, data, len, 100);
LAN9252_CS_HIGH();
}
2. 第二步:EtherCAT 从站状态机初始化
EtherCAT 从站需经历INIT→PREOP→SAFEOP→OP四个状态,主站(TwinCAT)会控制状态切换,STM32 需响应状态变化:
// ESC核心寄存器地址定义(LAN9252标准地址)
#define ESC_AL_STATUS_CODE 0x0120 // 从站状态寄存器
#define ESC_AL_CONTROL 0x0128 // 状态控制寄存器
#define ESC_SM0_ACTIVE 0x0800 // SM0(同步管理器0)激活位
#define ESC_PDO_RX_ADDR 0x1000 // PDO接收缓冲区(主站→从站,控制DO)
#define ESC_PDO_TX_ADDR 0x1100 // PDO发送缓冲区(从站→主站,上传DI)
// EtherCAT从站状态枚举
typedef enum {
EC_STATE_INIT = 0, // 初始化
EC_STATE_PREOP = 1, // 预操作
EC_STATE_SAFEOP = 2, // 安全操作
EC_STATE_OP = 3 // 运行
} EC_StateTypeDef;
/**
* @brief 获取当前EtherCAT从站状态
*/
EC_StateTypeDef LAN9252_Get_EC_State(void) {
uint8_t status = 0;
LAN9252_ESC_Read(ESC_AL_STATUS_CODE, &status, 1);
return (EC_StateTypeDef)(status & 0x0F);
}
/**
* @brief LAN9252初始化(ESC配置)
*/
void LAN9252_Init(void) {
uint8_t data = 0;
// 1. 复位ESC
data = 0x80;
LAN9252_ESC_Write(ESC_AL_CONTROL, &data, 1);
HAL_Delay(100);
// 2. 配置同步管理器SM0/SM1(PDO通信)
// SM0:接收主站数据(DO控制),地址0x1000,长度8字节
uint8_t sm0_config[8] = {0x00, 0x10, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00};
LAN9252_ESC_Write(0x0800, sm0_config, 8);
// SM1:发送从站数据(DI上传),地址0x1100,长度8字节
uint8_t sm1_config[8] = {0x00, 0x11, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00};
LAN9252_ESC_Write(0x0808, sm1_config, 8);
// 3. 激活SM0/SM1
data = 0x01;
LAN9252_ESC_Write(ESC_SM0_ACTIVE, &data, 1);
HAL_Delay(50);
}
3. 第三步:IO 控制核心逻辑(DI/DO)
定义 8 路 DI(输入)和 8 路 DO(输出),通过 PDO 与主站交互:
// IO端口定义(示例:PB0-PB7为DO,PC0-PC7为DI)
#define DO_PORT GPIOB
#define DI_PORT GPIOC
// 读取DI数据(8路)
uint8_t Read_DI_Data(void) {
uint8_t di_data = 0;
// 逐位读取PC0-PC7
for (int i = 0; i < 8; i++) {
if (HAL_GPIO_ReadPin(DI_PORT, GPIO_PIN_0 << i) == GPIO_PIN_SET) {
di_data |= (1 << i);
}
}
return di_data;
}
// 写入DO数据(8路)
void Write_DO_Data(uint8_t do_data) {
// 逐位写入PB0-PB7
for (int i = 0; i < 8; i++) {
if (do_data & (1 << i)) {
HAL_GPIO_WritePin(DO_PORT, GPIO_PIN_0 << i, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(DO_PORT, GPIO_PIN_0 << i, GPIO_PIN_RESET);
}
}
}
/**
* @brief EtherCAT IO主循环(需在main中循环调用)
*/
void EC_IO_Loop(void) {
EC_StateTypeDef ec_state = LAN9252_Get_EC_State();
uint8_t pdo_rx_data[1] = {0}; // 主站→从站:DO数据(1字节=8路)
uint8_t pdo_tx_data[1] = {0}; // 从站→主站:DI数据(1字节=8路)
switch (ec_state) {
case EC_STATE_OP: // 运行状态:实时交互IO数据
// 1. 读取主站下发的DO数据(从PDO_RX缓冲区)
LAN9252_ESC_Read(ESC_PDO_RX_ADDR, pdo_rx_data, 1);
Write_DO_Data(pdo_rx_data[0]); // 更新DO输出
// 2. 读取DI数据,上传到主站(写入PDO_TX缓冲区)
pdo_tx_data[0] = Read_DI_Data();
LAN9252_ESC_Write(ESC_PDO_TX_ADDR, pdo_tx_data, 1);
break;
case EC_STATE_SAFEOP: // 安全操作:DO置0
Write_DO_Data(0x00);
break;
case EC_STATE_PREOP: // 预操作:仅状态响应,无IO交互
case EC_STATE_INIT: // 初始化:复位IO
Write_DO_Data(0x00);
break;
default:
break;
}
}
-
第四步:main 函数整合
int main(void) {
// 1. STM32初始化(HAL库初始化、时钟、GPIO、SPI)
HAL_Init();
SystemClock_Config(); // 需配置STM32F407时钟(168MHz)
MX_GPIO_Init(); // 初始化DI/DO/GPIO(CS/INT)
MX_SPI1_Init(); // 初始化SPI1(LAN9252通信)// 2. LAN9252初始化 LAN9252_Init(); // 3. 主循环 while (1) { EC_IO_Loop(); // EtherCAT IO交互 HAL_Delay(1); // 1ms循环,保证实时性 }}
三、调试与验证(关键步骤)
1. 硬件调试
- 用万用表检查 STM32 与 LAN9252 的 SPI 引脚(SCK/MOSI/MISO/CS)电平,确保 3.3V 正常;
- 检查 LAN9252 的 RJ45 网口指示灯:连接主站后,LINK 灯常亮,DATA 灯闪烁表示通信正常。
2. 软件烧录
- 用 ST-Link 将代码烧录到 STM32;
- 确保无硬件报错(如 SPI 通信失败、GPIO 初始化失败)。
3. 主站测试(倍福 TwinCAT3)
- 安装 TwinCAT3,将电脑网口连接到 LAN9252 的 RJ45;
- 打开 TwinCAT3,扫描 EtherCAT 从站(
PLC→EtherCAT→Scan),会识别到 LAN9252 从站; - 配置 PDO 映射:
- 从站→主站:映射 DI 数据(1 字节,地址 0x1100);
- 主站→从站:映射 DO 数据(1 字节,地址 0x1000);
- 将从站状态切换到
OP(运行); - 测试:
- 在 TwinCAT 中修改 DO 输出值,STM32 的 DO 引脚电平应同步变化;
- 短接 STM32 的 DI 引脚到高电平,TwinCAT 中应能看到 DI 值更新。
四、常见问题与解决
| 问题现象 | 原因分析 | 解决方法 |
|---|---|---|
| 主站扫描不到从站 | SPI 通信失败 / LAN9252 未复位 | 检查 SPI 引脚连接;重新初始化 LAN9252 |
| 从站卡在 PREOP 状态 | SM(同步管理器)配置错误 | 核对 SM0/SM1 的地址和长度配置 |
| IO 数据无交互(OP 状态) | PDO 映射地址不匹配 | 确保 TwinCAT 的 PDO 地址和 STM32 的缓冲区一致 |
| DO 输出乱码 | 电平不匹配 / 干扰 | 增加光耦隔离;检查接地和电源滤波 |
总结
- 核心硬件:STM32 通过 SPI 与 LAN9252 通信,LAN9252 负责 EtherCAT 协议解析,STM32 负责 IO 控制;
- 软件关键:实现 ESC 寄存器读写、EtherCAT 状态机、PDO 数据交互,OP 状态下实时更新 IO;
- 调试重点:先确保 SPI 通信正常,再配置 PDO 映射,最后通过 TwinCAT 验证 IO 交互。