STM32 + LAN9252 EtherCAT 从站实现传感器数据采集
本文提供完整可落地的工程方案,以 STM32F407(通用款)+ LAN9252 为例,实现传感器数据采集并通过 EtherCAT 上报,包含硬件接线、核心代码、TwinCAT 配置全流程,新手可直接复刻。
一、方案整体设计
1. 核心架构

2. 关键选型(通用适配)
- STM32:F407VET6(SPI 接口、ADC/DMA、足够 GPIO),其他型号(F1/F7/H7)仅需修改底层驱动
- LAN9252:核心 ESC,SPI 通信(最高 30MHz),双 PHY 冗余
- 传感器类型:支持模拟量(4-20mA/0-10V)、数字量(DI/DO)、串口传感器(如 Modbus)
- EtherCAT 协议:基于 Beckhoff SSC(Standard Slave Controller)标准,兼容所有主站
二、硬件接线(最简必接)
| STM32 引脚 | LAN9252 引脚 | 功能说明 |
|---|---|---|
| PA4 | SCS# | SPI 片选(低有效) |
| PA5 | SCK | SPI 时钟 |
| PA6 | SO/SIO1 | SPI 主机输入(从机输出) |
| PA7 | SI/SIO0 | SPI 主机输出(从机输入) |
| PB0 | IRQ | 中断(可选,下降沿触发) |
| NRST | RST# | 硬件复位(低有效) |
| 3.3V | VDD33 | 供电(需稳压,纹波 < 100mV) |
| GND | GND | 共地 |
| (可选) | LED[0/1] | 状态指示灯 |
传感器接线示例
- 4-20mA 模拟量:传感器→电流转电压模块(如 ACS712)→STM32 ADC 引脚(PA0)
- 数字量传感器:传感器→光耦隔离→STM32 GPIO(PB1)
三、软件实现(完整代码)
1. 前置准备
- 安装 STM32CubeMX(生成底层代码)
- 下载 Beckhoff SSC Tool(生成 EtherCAT 从站代码)
- 安装 TwinCAT3(主站调试)
- 依赖库:STM32 HAL 库、LAN9252 SPI 驱动、SSC 从站协议栈
2. 核心代码(分模块)
模块 1:LAN9252 SPI 底层驱动(lan9252_spi.c)
#include "lan9252_spi.h"
#include "stm32f4xx_hal.h"
#define LAN9252_SPI hspi1
#define LAN9252_CS_PIN GPIO_PIN_4
#define LAN9252_CS_PORT GPIOA
// SPI片选控制
static void LAN9252_CS_LOW(void) {
HAL_GPIO_WritePin(LAN9252_CS_PORT, LAN9252_CS_PIN, GPIO_PIN_RESET);
}
static void LAN9252_CS_HIGH(void) {
HAL_GPIO_WritePin(LAN9252_CS_PORT, LAN9252_CS_PIN, GPIO_PIN_SET);
}
// 读LAN9252 ESC寄存器(32位)
uint32_t LAN9252_ReadReg(uint16_t addr) {
uint8_t tx_buf[4] = {0x03, (addr >> 8) & 0xFF, addr & 0xFF, 0x00};
uint8_t rx_buf[4] = {0};
LAN9252_CS_LOW();
HAL_SPI_TransmitReceive(&LAN9252_SPI, tx_buf, rx_buf, 4, 100);
LAN9252_CS_HIGH();
return (rx_buf[1] << 24) | (rx_buf[2] << 16) | (rx_buf[3] << 8) | rx_buf[0];
}
// 写LAN9252 ESC寄存器(32位)
void LAN9252_WriteReg(uint16_t addr, uint32_t data) {
uint8_t tx_buf[4] = {0x02, (addr >> 8) & 0xFF, addr & 0xFF, 0x00};
tx_buf[3] = (data >> 24) & 0xFF;
tx_buf[2] = (data >> 16) & 0xFF;
tx_buf[1] = (data >> 8) & 0xFF;
tx_buf[0] = data & 0xFF;
LAN9252_CS_LOW();
HAL_SPI_Transmit(&LAN9252_SPI, tx_buf, 4, 100);
LAN9252_CS_HIGH();
}
// LAN9252初始化
void LAN9252_Init(void) {
// 硬件复位(拉低RST# 100us)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_Delay(1);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_Delay(10);
// 检查READY位(确认LAN9252启动完成)
while((LAN9252_ReadReg(0x0084) & 0x01) == 0);
// 初始化SyncManager(SM2=TxPDO,SM3=RxPDO)
LAN9252_WriteReg(0x0800, 0x00000001); // SM0: 邮箱输出
LAN9252_WriteReg(0x0810, 0x00000002); // SM1: 邮箱输入
LAN9252_WriteReg(0x0820, 0x00010003); // SM2: TxPDO(传感器数据上报)
LAN9252_WriteReg(0x0830, 0x00010004); // SM3: RxPDO(主站控制指令)
}
模块 2:传感器数据采集(sensor.c)
#include "sensor.h"
#include "stm32f4xx_hal.h"
ADC_HandleTypeDef hadc1;
// ADC初始化(采集4-20mA传感器,对应0-3.3V)
void Sensor_ADC_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
sConfig.Channel = ADC_CHANNEL_0; // PA0
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_480CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
// 启动ADC连续采集
HAL_ADC_Start(&hadc1);
}
// 读取模拟量传感器数据(4-20mA转实际值,示例:温度0-100℃)
float Sensor_Read_Analog(void) {
uint32_t adc_value = HAL_ADC_GetValue(&hadc1);
// 4-20mA对应ADC值:524(4mA)~2620(20mA),转0-100℃
float current = (adc_value - 524) / (2620 - 524) * 16 + 4; // 计算电流
float temp = (current - 4) / 16 * 100; // 电流转温度
return temp;
}
// 读取数字量传感器(如接近开关)
uint8_t Sensor_Read_Digital(void) {
return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1); // PB1
}
模块 3:EtherCAT PDO 数据交互(ecat_slave.c)
#include "ecat_slave.h"
#include "lan9252_spi.h"
#include "sensor.h"
// PDO数据结构体(与TwinCAT配置一致)
typedef struct {
float temp; // 温度值(TxPDO,从站→主站)
uint8_t di_state; // 数字量状态(TxPDO)
uint8_t do_ctrl; // 数字量控制(RxPDO,主站→从站)
} PDO_Data_t;
PDO_Data_t pdo_data = {0};
// 更新TxPDO(传感器数据写入LAN9252 PDO缓存)
void ECAT_Update_TxPDO(void) {
// 读取传感器数据
pdo_data.temp = Sensor_Read_Analog();
pdo_data.di_state = Sensor_Read_Digital();
// 将数据写入LAN9252的TxPDO缓存(地址0x1000)
uint32_t temp_int = *(uint32_t*)&pdo_data.temp; // 浮点转32位整数
LAN9252_WriteReg(0x1000, temp_int); // 温度值
LAN9252_WriteReg(0x1004, pdo_data.di_state); // 数字量状态
}
// 读取RxPDO(主站控制指令)
void ECAT_Read_RxPDO(void) {
pdo_data.do_ctrl = LAN9252_ReadReg(0x1008) & 0xFF;
// 根据主站指令控制DO(示例:PB2)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, pdo_data.do_ctrl ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
// EtherCAT主循环
void ECAT_Main_Loop(void) {
// 检查AL状态(0x0130,OP状态=0x08)
uint32_t al_state = LAN9252_ReadReg(0x0130);
if(al_state == 0x08) { // OP状态才交互数据
ECAT_Update_TxPDO();
ECAT_Read_RxPDO();
}
}
模块 4:主函数(main.c)
#include "main.h"
#include "lan9252_spi.h"
#include "sensor.h"
#include "ecat_slave.h"
SPI_HandleTypeDef hspi1;
int main(void) {
// 初始化HAL库
HAL_Init();
// 系统时钟配置(168MHz)
SystemClock_Config();
// 初始化GPIO、SPI、ADC
MX_GPIO_Init();
MX_SPI1_Init();
Sensor_ADC_Init();
// 初始化LAN9252
LAN9252_Init();
// 主循环
while (1) {
ECAT_Main_Loop(); // EtherCAT数据交互
HAL_Delay(1); // 1ms周期(可根据传感器调整)
}
}
3. SSC Tool 配置(生成 ESI 文件)
- 打开 SSC Tool,新建工程,选择 LAN9252 模板;
- 配置 PDO 映射:
- TxPDO(从站→主站):
0x1A00,包含温度(32 位浮点)、DI 状态(8 位); - RxPDO(主站→从站):
0x1600,包含 DO 控制(8 位);
- TxPDO(从站→主站):
- 生成 ESI 文件(
LAN9252_Sensor.xml)和从站代码,集成到 STM32 工程。
四、TwinCAT3 主站配置(调试)
1. 导入 ESI 文件
- 打开 TwinCAT3 → 右键 "EtherCAT" → "Import ESI" → 选择生成的
LAN9252_Sensor.xml;
2. 扫描从站
- 连接 EtherCAT 网线(LAN9252 的 ETH0/ETH1)到 PC/TwinCAT 主站;
- 右键 "EtherCAT Master" → "Scan Devices" → 识别到 LAN9252 从站;
3. 配置 PDO 映射
- 双击从站 → "Process Data" → 勾选 TxPDO/RxPDO;
- 映射变量:
- TxPDO:
temp(REAL)、di_state(BOOL); - RxPDO:
do_ctrl(BOOL);
- TxPDO:
4. 运行调试
- 切换到 "Run" 模式,将从站状态从 Init→PreOp→SafeOp→OP;
- 打开 "Watch" 窗口,实时监控传感器数据(温度、DI 状态),下发 DO 控制指令。
五、关键注意事项
- SPI 通信稳定性 :
- SPI 时钟频率建议≤10MHz(新手),避免数据丢包;
- CS 引脚需硬件上拉,减少干扰;
- 传感器校准 :
- 4-20mA 传感器需校准零点(4mA 对应 ADC 值)和满量程(20mA 对应 ADC 值);
- EtherCAT 状态机 :
- 确保从站能正常进入 OP 状态,若卡在 PreOp,检查 ESI 文件或 SM 配置;
- 电源纹波 :
- LAN9252 对电源纹波敏感,需加 100nF+10uF 去耦电容。
六、总结
- 核心流程:STM32 采集传感器数据 → 写入 LAN9252 的 TxPDO 缓存 → EtherCAT 主站(TwinCAT)读取数据,同时主站可下发控制指令到 RxPDO;
- 关键代码:LAN9252 的 SPI 读写是基础,PDO 数据映射需与 TwinCAT 配置严格一致;
- 调试重点:先确保 LAN9252 能被 TwinCAT 识别,再逐步验证传感器采集和 PDO 交互