复制代码
#include "stm32f4xx_hal.h"
#include "lan9252.h"
#include "simplecat.h"
// 运动控制参数定义
typedef struct {
int32_t target_pos; // 目标位置(来自主站PDO)
int32_t actual_pos; // 实际位置(反馈给主站)
int16_t target_vel; // 目标速度
int16_t actual_vel; // 实际速度
uint8_t enable; // 使能信号
} MotionData_t;
MotionData_t g_motion_data = {0}; // 全局运动数据
EC_SLAVE_HANDLE g_ec_handle; // EtherCAT 从站句柄
// 1. 初始化 LAN9252 + SPI
void LAN9252_Init(void) {
SPI_HandleTypeDef hspi1;
// SPI 初始化(模式0,8位,时钟分频如4分频,即18MHz@72MHz主频)
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
HAL_SPI_Init(&hspi1);
// LAN9252 硬件复位
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_Delay(100);
// LAN9252 寄存器初始化(配置为EtherCAT从站模式)
LAN9252_WriteReg(LAN9252_REG_EC_CFG, 0x01); // 使能EtherCAT模式
LAN9252_WriteReg(LAN9252_REG_IRQ_EN, 0x02); // 使能帧接收中断
}
// 2. EtherCAT 从站初始化(PDO映射 + 状态机)
void EC_Slave_Init(void) {
// 初始化从站句柄
SimpleCAT_Init(&g_ec_handle, LAN9252_ReadReg, LAN9252_WriteReg);
// 配置PDO映射(TxPDO:反馈数据;RxPDO:指令数据)
// RxPDO(主站→从站):使能(1B) + 目标位置(4B) + 目标速度(2B)
SimpleCAT_AddRxPDO(&g_ec_handle, 0x1600, 3); // RxPDO索引0x1600,3个对象
SimpleCAT_AddRxPDOEntry(&g_ec_handle, 0x1600, 0x0001, 0x00, 1, &g_motion_data.enable);
SimpleCAT_AddRxPDOEntry(&g_ec_handle, 0x1600, 0x0002, 0x00, 4, &g_motion_data.target_pos);
SimpleCAT_AddRxPDOEntry(&g_ec_handle, 0x1600, 0x0003, 0x00, 2, &g_motion_data.target_vel);
// TxPDO(从站→主站):实际位置(4B) + 实际速度(2B)
SimpleCAT_AddTxPDO(&g_ec_handle, 0x1A00, 2); // TxPDO索引0x1A00,2个对象
SimpleCAT_AddTxPDOEntry(&g_ec_handle, 0x1A00, 0x0001, 0x00, 4, &g_motion_data.actual_pos);
SimpleCAT_AddTxPDOEntry(&g_ec_handle, 0x1A00, 0x0002, 0x00, 2, &g_motion_data.actual_vel);
// 配置同步管理(SM):SM2=RxPDO,SM3=TxPDO
SimpleCAT_SetSyncManager(&g_ec_handle, 2, SM_TYPE_RX, 0x1600);
SimpleCAT_SetSyncManager(&g_ec_handle, 3, SM_TYPE_TX, 0x1A00);
// 启动EtherCAT从站状态机
SimpleCAT_Start(&g_ec_handle);
}
// 3. 运动控制核心算法(位置环PID)
void Motion_Control(void) {
static int32_t pos_err, last_pos_err;
static float kp = 1.0, ki = 0.1, kd = 0.05;
static float integral = 0;
int16_t pwm_out;
if (g_motion_data.enable == 0) { // 未使能时复位
g_motion_data.actual_pos = 0;
g_motion_data.actual_vel = 0;
integral = 0;
last_pos_err = 0;
return;
}
// 计算位置误差
pos_err = g_motion_data.target_pos - g_motion_data.actual_pos;
// PID计算
integral += pos_err * 0.001; // 采样周期1ms
pwm_out = kp * pos_err + ki * integral + kd * (pos_err - last_pos_err) / 0.001;
last_pos_err = pos_err;
// 限幅PWM输出(假设PWM范围±1000)
pwm_out = (pwm_out > 1000) ? 1000 : (pwm_out < -1000) ? -1000 : pwm_out;
// 输出PWM到电机驱动(示例:TIM1_CH1)
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 500 + pwm_out); // 中点500
// 更新实际位置(模拟编码器反馈,实际需接编码器)
g_motion_data.actual_pos += (int32_t)(g_motion_data.actual_vel * 0.001);
g_motion_data.actual_vel = (int16_t)(pwm_out * 0.1); // 模拟速度反馈
}
// 4. 主循环(1ms中断触发)
void TIM2_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET) {
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
// 1. 处理EtherCAT通信(接收主站指令,发送反馈)
SimpleCAT_Process(&g_ec_handle);
// 2. 执行运动控制
Motion_Control();
}
}
// 主函数
int main(void) {
// 初始化HAL库
HAL_Init();
// 配置系统时钟(如STM32F407→168MHz)
SystemClock_Config();
// 初始化GPIO(SPI/片选/中断/PWM)
MX_GPIO_Init();
// 初始化SPI/LAN9252
LAN9252_Init();
// 初始化定时器(TIM2:1ms中断,TIM1:PWM输出)
MX_TIM1_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2); // 启动1ms定时器中断
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 启动PWM输出
// 初始化EtherCAT从站
EC_Slave_Init();
// 死循环
while (1) {
// 处理LAN9252中断(可选,也可轮询)
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET) {
LAN9252_ClearIRQ(); // 清除中断标志
SimpleCAT_HandleIRQ(&g_ec_handle); // 处理EtherCAT帧
}
}
}