feat(cproject): 重构项目文件结构并添加EtherCAT通信支持
- 将设备相关文件重新组织为device/spi和comm分组 - 添加EtherCAT从站通信模块(esc_exmc_bus、esc_lan9252、ethercat_slave等) - 添加EXMC外设组件支持 - 统一文件命名规范为小写 refactor(canfd): 优化CANFD模块中断安全性和配置方式 - 替换自定义临界区函数为RT_CRITICAL_SAVE/RESTORE宏 - 使用配置常量CONFIG_IRQ_PREEMPT_CAN替代硬编码值 - 简化邮箱满时的处理逻辑,移除不必要的SysTick等待 - 移除废弃的中断保存头文件引用 refactor(timer): 更新定时器配置以使用配置常量 - 修复头文件包含路径大小写问题 - 使用CONFIG_NVIC_PRIORITY_GROUP配置项替代硬编码值
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @file esc_exmc_bus.cpp
|
||||
* @brief LAN9252 ESC 的 EXMC 16-bit 并行总线驱动实现。
|
||||
*/
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
|
||||
extern "C" {
|
||||
#include "gd32e50x_rcu.h"
|
||||
#include "gd32e50x_gpio.h"
|
||||
#ifdef EXMC_NORSRAM_REGION1
|
||||
#include "gd32e50x_exmc.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 内存映射访问宏:基址 + 偏移(按 16-bit 半字对齐,LAN9252 HBI 地址左移 1 位)。
|
||||
* 使用 volatile uint16_t* 转换避免依赖 CMSIS 的 __IO 宏。 */
|
||||
#define ESC_EXMC_REG16(offset) (*(volatile uint16_t*)(CONFIG_ESC_EXMC_BASE_ADDR + (offset)))
|
||||
|
||||
void EscExmcBus::init()
|
||||
{
|
||||
/* 使能 GPIO 与 EXMC 时钟 */
|
||||
rcu_periph_clock_enable(RCU_GPIOD);
|
||||
rcu_periph_clock_enable(RCU_GPIOE);
|
||||
rcu_periph_clock_enable(RCU_AF);
|
||||
#ifdef EXMC_NORSRAM_REGION1
|
||||
rcu_periph_clock_enable(RCU_EXMC);
|
||||
|
||||
/* 配置 16-bit 数据线 D0-D15 为 AF 推挽 */
|
||||
gpio_init(CONFIG_EXMC_D0_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D0_PIN);
|
||||
gpio_init(CONFIG_EXMC_D1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D1_PIN);
|
||||
gpio_init(CONFIG_EXMC_D2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D2_PIN);
|
||||
gpio_init(CONFIG_EXMC_D3_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D3_PIN);
|
||||
gpio_init(CONFIG_EXMC_D4_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D4_PIN);
|
||||
gpio_init(CONFIG_EXMC_D5_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D5_PIN);
|
||||
gpio_init(CONFIG_EXMC_D6_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D6_PIN);
|
||||
gpio_init(CONFIG_EXMC_D7_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D7_PIN);
|
||||
gpio_init(CONFIG_EXMC_D8_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D8_PIN);
|
||||
gpio_init(CONFIG_EXMC_D9_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D9_PIN);
|
||||
gpio_init(CONFIG_EXMC_D10_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D10_PIN);
|
||||
gpio_init(CONFIG_EXMC_D11_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D11_PIN);
|
||||
gpio_init(CONFIG_EXMC_D12_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D12_PIN);
|
||||
gpio_init(CONFIG_EXMC_D13_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D13_PIN);
|
||||
gpio_init(CONFIG_EXMC_D14_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D14_PIN);
|
||||
gpio_init(CONFIG_EXMC_D15_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_D15_PIN);
|
||||
|
||||
/* 控制线:A16 / NOE / NWE / NE1 */
|
||||
gpio_init(CONFIG_EXMC_A16_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_A16_PIN);
|
||||
gpio_init(CONFIG_EXMC_NOE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NOE_PIN);
|
||||
gpio_init(CONFIG_EXMC_NWE_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NWE_PIN);
|
||||
gpio_init(CONFIG_EXMC_NE1_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, CONFIG_EXMC_NE1_PIN);
|
||||
|
||||
/* EXMC NOR/SRAM Region1 时序配置(16-bit,非复用,异步) */
|
||||
exmc_norsram_parameter_struct norsram_init;
|
||||
exmc_norsram_struct_para_init(&norsram_init);
|
||||
norsram_init.norsram_region = CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1;
|
||||
norsram_init.write_mode = EXMC_ASYN_WRITE;
|
||||
norsram_init.read_mode = EXMC_ASYN_READ;
|
||||
norsram_init.nwait_mode = EXMC_NWAIT_DISABLE;
|
||||
norsram_init.asyn_wait = DISABLE;
|
||||
norsram_init.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
|
||||
norsram_init.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
|
||||
/* 时序:按 LAN9252 HBI 要求配置 read/write wait state */
|
||||
norsram_init.read_latency = EXMC_DEN_ALE_TR_6_HCLK; // 需按 LAN9252 HBI 时序实测调整
|
||||
norsram_init.write_latency = EXMC_DEN_ALE_TR_6_HCLK; // 需按 LAN9252 HBI 时序实测调整
|
||||
exmc_norsram_init(&norsram_init);
|
||||
exmc_norsram_enable(CONFIG_ESC_EXMC_BANK_NOR_SRAM_REGION1);
|
||||
#else
|
||||
/* EXMC 外设库未安装:仅占位,CONFIG_ETHERCAT_ENABLE=1 时需先安装 EXMC 组件。 */
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t EscExmcBus::read16(uint32_t offset)
|
||||
{
|
||||
return ESC_EXMC_REG16(offset);
|
||||
}
|
||||
|
||||
void EscExmcBus::write16(uint32_t offset, uint16_t data)
|
||||
{
|
||||
ESC_EXMC_REG16(offset) = data;
|
||||
}
|
||||
|
||||
void EscExmcBus::readBlock(uint32_t offset, uint16_t *buf, size_t count)
|
||||
{
|
||||
for (size_t i = 0U; i < count; ++i) {
|
||||
buf[i] = ESC_EXMC_REG16(offset + (uint32_t)(i * 2U));
|
||||
}
|
||||
}
|
||||
|
||||
void EscExmcBus::writeBlock(uint32_t offset, const uint16_t *buf, size_t count)
|
||||
{
|
||||
for (size_t i = 0U; i < count; ++i) {
|
||||
ESC_EXMC_REG16(offset + (uint32_t)(i * 2U)) = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ETHERCAT_ENABLE */
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file esc_exmc_bus.h
|
||||
* @brief LAN9252 ESC 的 EXMC 16-bit 并行总线抽象接口。
|
||||
* @note 通过 EXMC NOR/SRAM Region1(NE1,基址 0x60000000)以内存映射方式访问 LAN9252。
|
||||
* 16-bit 数据总线 + A16 地址线 + NOE/NWE/NE1 控制线。
|
||||
*/
|
||||
#ifndef COMM_ESC_EXMC_BUS_H
|
||||
#define COMM_ESC_EXMC_BUS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* @brief LAN9252 ESC 的 EXMC 并行总线驱动。
|
||||
* @note 提供 16-bit 读写与块读写接口,内部通过内存映射地址直接访问 ESC 寄存器空间。
|
||||
*/
|
||||
class EscExmcBus {
|
||||
public:
|
||||
/** @brief 初始化 EXMC NOR/SRAM Region1 与全部 GPIO(AF 复用)。 */
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief 从指定偏移读取 16-bit 数据。
|
||||
* @param[in] offset 地址偏移(字节)。
|
||||
* @return uint16_t 读取的 16-bit 数据。
|
||||
*/
|
||||
static uint16_t read16(uint32_t offset);
|
||||
|
||||
/**
|
||||
* @brief 向指定偏移写入 16-bit 数据。
|
||||
* @param[in] offset 地址偏移(字节)。
|
||||
* @param[in] data 待写入的 16-bit 数据。
|
||||
*/
|
||||
static void write16(uint32_t offset, uint16_t data);
|
||||
|
||||
/**
|
||||
* @brief 从指定偏移连续读取一块 16-bit 数据。
|
||||
* @param[in] offset 地址偏移(字节)。
|
||||
* @param[out] buf 输出缓冲(以 uint16_t 为单位)。
|
||||
* @param[in] count 读取的 16-bit 半字数量。
|
||||
*/
|
||||
static void readBlock(uint32_t offset, uint16_t *buf, size_t count);
|
||||
|
||||
/**
|
||||
* @brief 向指定偏移连续写入一块 16-bit 数据。
|
||||
* @param[in] offset 地址偏移(字节)。
|
||||
* @param[in] buf 数据缓冲(以 uint16_t 为单位)。
|
||||
* @param[in] count 写入的 16-bit 半字数量。
|
||||
*/
|
||||
static void writeBlock(uint32_t offset, const uint16_t *buf, size_t count);
|
||||
};
|
||||
|
||||
#endif /* COMM_ESC_EXMC_BUS_H */
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @file esc_lan9252.cpp
|
||||
* @brief LAN9252 EtherCAT ESC 驱动实现。
|
||||
*/
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
|
||||
namespace {
|
||||
/* LAN9252 ESC 关键寄存器地址(字节偏移,EXMC 访问) */
|
||||
constexpr uint16_t LAN9252_REG_CHIP_ID_L = 0x0000U; /**< 芯片 ID 低 16 位(=0x4321)。 */
|
||||
constexpr uint16_t LAN9252_REG_CHIP_ID_H = 0x0002U; /**< 芯片 ID 高 16 位(=0x8765)。 */
|
||||
constexpr uint32_t LAN9252_CHIP_ID_VALUE = 0x87654321U; /**< 期望的 32-bit 芯片 ID。 */
|
||||
|
||||
/* PDI 内存基址偏移(实际取决于 LAN9252 PDI 配置,这里为示意值) */
|
||||
constexpr uint16_t LAN9252_PDI_BASE_OFFSET = 0x1000U;
|
||||
}
|
||||
|
||||
void EscLan9252::init()
|
||||
{
|
||||
EscExmcBus::init();
|
||||
/* TODO: 配置 ESC 中断使能、AL 控制等基本寄存器(留待协议栈集成阶段实现) */
|
||||
}
|
||||
|
||||
bool EscLan9252::selfTest()
|
||||
{
|
||||
const uint16_t id_low = readReg(LAN9252_REG_CHIP_ID_L);
|
||||
const uint16_t id_high = readReg(LAN9252_REG_CHIP_ID_H);
|
||||
const uint32_t chip_id = ((uint32_t)id_high << 16) | id_low;
|
||||
return (chip_id == LAN9252_CHIP_ID_VALUE);
|
||||
}
|
||||
|
||||
uint16_t EscLan9252::readReg(uint16_t addr)
|
||||
{
|
||||
/* ESC 寄存器地址按字节寻址,EXMC 16-bit 访问需要地址左移 1 位 */
|
||||
return EscExmcBus::read16((uint32_t)addr << 1U);
|
||||
}
|
||||
|
||||
void EscLan9252::writeReg(uint16_t addr, uint16_t val)
|
||||
{
|
||||
EscExmcBus::write16((uint32_t)addr << 1U, val);
|
||||
}
|
||||
|
||||
void EscLan9252::readPdi(uint16_t offset, uint8_t *buf, size_t len)
|
||||
{
|
||||
if (buf == nullptr || len == 0U) {
|
||||
return;
|
||||
}
|
||||
/* PDI 按 16-bit 读取,非对齐字节需特殊处理(此处简化为按半字读取) */
|
||||
const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U;
|
||||
const size_t half_count = len / 2U;
|
||||
for (size_t i = 0U; i < half_count; ++i) {
|
||||
uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(i * 2U));
|
||||
buf[i * 2U] = (uint8_t)(w & 0xFFU);
|
||||
buf[i * 2U + 1] = (uint8_t)((w >> 8) & 0xFFU);
|
||||
}
|
||||
/* 处理奇数字节 */
|
||||
if ((len & 1U) != 0U) {
|
||||
uint16_t w = EscExmcBus::read16(exmc_offset + (uint32_t)(half_count * 2U));
|
||||
buf[half_count * 2U] = (uint8_t)(w & 0xFFU);
|
||||
}
|
||||
}
|
||||
|
||||
void EscLan9252::writePdi(uint16_t offset, const uint8_t *buf, size_t len)
|
||||
{
|
||||
if (buf == nullptr || len == 0U) {
|
||||
return;
|
||||
}
|
||||
const uint32_t exmc_offset = (uint32_t)(LAN9252_PDI_BASE_OFFSET + offset) << 1U;
|
||||
const size_t half_count = len / 2U;
|
||||
for (size_t i = 0U; i < half_count; ++i) {
|
||||
uint16_t w = (uint16_t)buf[i * 2U] | ((uint16_t)buf[i * 2U + 1] << 8);
|
||||
EscExmcBus::write16(exmc_offset + (uint32_t)(i * 2U), w);
|
||||
}
|
||||
if ((len & 1U) != 0U) {
|
||||
EscExmcBus::write16(exmc_offset + (uint32_t)(half_count * 2U), (uint16_t)buf[half_count * 2U]);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ETHERCAT_ENABLE */
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file esc_lan9252.h
|
||||
* @brief LAN9252 EtherCAT 从站控制器(ESC)驱动接口。
|
||||
* @note 通过 EscExmcBus 16-bit 并行总线访问 LAN9252 寄存器与 PDI 内存。
|
||||
*/
|
||||
#ifndef COMM_ESC_LAN9252_H
|
||||
#define COMM_ESC_LAN9252_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/**
|
||||
* @brief LAN9252 ESC 驱动。
|
||||
* @note 封装 ESC 寄存器读写与 PDI(Process Data Interface)内存块访问。
|
||||
*/
|
||||
class EscLan9252 {
|
||||
public:
|
||||
/** @brief 初始化 EXMC 总线与 ESC 基本寄存器。 */
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief ESC 自检:读取芯片 ID 寄存器校验。
|
||||
* @return bool true 表示 ID 匹配(0x87654321),false 表示失败。
|
||||
*/
|
||||
static bool selfTest();
|
||||
|
||||
/**
|
||||
* @brief 读取 ESC 寄存器(16-bit)。
|
||||
* @param[in] addr 寄存器地址(ESC 寄存器空间偏移)。
|
||||
* @return uint16_t 寄存器值。
|
||||
*/
|
||||
static uint16_t readReg(uint16_t addr);
|
||||
|
||||
/**
|
||||
* @brief 写入 ESC 寄存器(16-bit)。
|
||||
* @param[in] addr 寄存器地址。
|
||||
* @param[in] val 待写入值。
|
||||
*/
|
||||
static void writeReg(uint16_t addr, uint16_t val);
|
||||
|
||||
/**
|
||||
* @brief 从 PDI 内存读取一块数据。
|
||||
* @param[in] offset PDI 偏移(字节)。
|
||||
* @param[out] buf 输出缓冲。
|
||||
* @param[in] len 读取字节数。
|
||||
*/
|
||||
static void readPdi(uint16_t offset, uint8_t *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @brief 向 PDI 内存写入一块数据。
|
||||
* @param[in] offset PDI 偏移(字节)。
|
||||
* @param[in] buf 数据缓冲。
|
||||
* @param[in] len 写入字节数。
|
||||
*/
|
||||
static void writePdi(uint16_t offset, const uint8_t *buf, size_t len);
|
||||
};
|
||||
|
||||
#endif /* COMM_ESC_LAN9252_H */
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file ethercat_slave.cpp
|
||||
* @brief EtherCAT 从站周期处理实现。
|
||||
*/
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "main.h"
|
||||
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
|
||||
PdoBuffer EthercatSlave::s_inputs[2] = {};
|
||||
PdoBuffer EthercatSlave::s_outputs[2] = {};
|
||||
volatile uint8_t EthercatSlave::s_inputReadIndex = 0U;
|
||||
volatile uint8_t EthercatSlave::s_outputWriteIndex = 0U;
|
||||
|
||||
void EthercatSlave::init()
|
||||
{
|
||||
EscLan9252::init();
|
||||
if (EscLan9252::selfTest()) {
|
||||
/* ESC 自检通过:TODO 启用周期同步 */
|
||||
}
|
||||
s_inputReadIndex = 0U;
|
||||
s_outputWriteIndex = 0U;
|
||||
}
|
||||
|
||||
__attribute__((section(".ramfunc")))
|
||||
void EthercatSlave::periodicIsr()
|
||||
{
|
||||
/* 1. 读取输入 PDO 到另一块缓冲(当前可读缓冲的配对) */
|
||||
const uint8_t write_idx = s_inputReadIndex ^ 1U;
|
||||
EscLan9252::readPdi(0x0000U, s_inputs[write_idx].data, CONFIG_ETHERCAT_PDO_SIZE);
|
||||
s_inputs[write_idx].len = CONFIG_ETHERCAT_PDO_SIZE;
|
||||
|
||||
/* 2. 写入输出 PDO(从应用层未写入的那个缓冲读取) */
|
||||
const uint8_t out_read_idx = s_outputWriteIndex ^ 1U;
|
||||
EscLan9252::writePdi(0x0000U, s_outputs[out_read_idx].data, s_outputs[out_read_idx].len);
|
||||
|
||||
/* 3. 翻转输入缓冲指针,使新完成的缓冲成为可读缓冲 */
|
||||
s_inputReadIndex = write_idx;
|
||||
|
||||
/* 4. 置位非实时任务,通知主循环处理应用层 PDO 映射 */
|
||||
TaskManager::set(1UL << TASK_ETHERCAT_PROCESS);
|
||||
}
|
||||
|
||||
const PdoBuffer* EthercatSlave::getLatestInputs()
|
||||
{
|
||||
/* s_inputReadIndex 始终指向最新完成的输入缓冲 */
|
||||
return &s_inputs[s_inputReadIndex];
|
||||
}
|
||||
|
||||
void EthercatSlave::setOutputs(const PdoBuffer *outputs)
|
||||
{
|
||||
if (outputs == nullptr) {
|
||||
return;
|
||||
}
|
||||
/* 写入应用层缓冲(ISR 当前不读取的那个) */
|
||||
const uint8_t write_idx = s_outputWriteIndex ^ 1U;
|
||||
s_outputs[write_idx] = *outputs;
|
||||
s_outputWriteIndex = write_idx;
|
||||
}
|
||||
|
||||
void EthercatSlave::process()
|
||||
{
|
||||
/* 应用层 PDO 映射入口(非实时,由主循环 TASK_ETHERCAT_PROCESS 调用)。
|
||||
* TODO: 将 getLatestInputs() 数据映射到应用对象字典;将应用输出填入 setOutputs()。 */
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ETHERCAT_ENABLE */
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file ethercat_slave.h
|
||||
* @brief EtherCAT 从站周期处理接口与 PDO 双缓冲(ping-pong)结构。
|
||||
* @note 周期 ISR 调用 periodicIsr() 完成输入 PDO 读取 + 输出 PDO 写入;
|
||||
* 应用层通过 getLatestInputs()/setOutputs() 与周期路径无锁交换数据。
|
||||
*/
|
||||
#ifndef COMM_ETHERCAT_SLAVE_H
|
||||
#define COMM_ETHERCAT_SLAVE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
/** @brief 默认 PDO 大小(可按需调整)。 */
|
||||
constexpr size_t CONFIG_ETHERCAT_PDO_SIZE = 32U;
|
||||
|
||||
/**
|
||||
* @brief PDO 数据缓冲(输入/输出共用结构)。
|
||||
* @note 周期 ISR 与应用层通过双缓冲无锁交换。
|
||||
*/
|
||||
struct PdoBuffer {
|
||||
uint8_t data[CONFIG_ETHERCAT_PDO_SIZE] = {0}; /**< 数据载荷。 */
|
||||
size_t len = 0U; /**< 有效数据长度。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief EtherCAT 从站周期处理。
|
||||
* @note 周期路径(periodicIsr)必须极简:读输入→写输出→翻转指针→置位任务。
|
||||
*/
|
||||
class EthercatSlave {
|
||||
public:
|
||||
/** @brief 初始化 ESC 与双缓冲。 */
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief 周期中断处理(由 SYNC0 EXTI 触发)。
|
||||
* @note 放入 RAM 执行段(.ramfunc)以降低 Flash 等待带来的抖动。
|
||||
* 流程:读输入 PDO → 写输出 PDO → 翻转输入缓冲指针 → 置位 TASK_ETHERCAT_PROCESS。
|
||||
*/
|
||||
static void periodicIsr();
|
||||
|
||||
/**
|
||||
* @brief 获取最新输入 PDO(主循环调用,无锁)。
|
||||
* @return const PdoBuffer* 最新输入缓冲只读指针;周期 ISR 不会修改该缓冲。
|
||||
*/
|
||||
static const PdoBuffer* getLatestInputs();
|
||||
|
||||
/**
|
||||
* @brief 设置下次周期要发送的输出 PDO(主循环调用)。
|
||||
* @param[in] outputs 输出数据指针(内容会被拷贝到内部输出缓冲)。
|
||||
* @note 调用方在 ISR 不持有期间调用即可,内部使用乒乓缓冲避免竞争。
|
||||
*/
|
||||
static void setOutputs(const PdoBuffer *outputs);
|
||||
|
||||
/**
|
||||
* @brief 应用层 PDO 映射入口(非实时,由 TASK_ETHERCAT_PROCESS 调用)。
|
||||
* @note 留接口,具体映射逻辑由协议栈后续实现。
|
||||
*/
|
||||
static void process();
|
||||
|
||||
private:
|
||||
static PdoBuffer s_inputs[2]; /**< 输入双缓冲(pong-pong)。 */
|
||||
static PdoBuffer s_outputs[2]; /**< 输出双缓冲(ping-pong)。 */
|
||||
static volatile uint8_t s_inputReadIndex; /**< ISR 当前写入索引;getLatestInputs 读另一个。 */
|
||||
static volatile uint8_t s_outputWriteIndex; /**< 应用层写入索引;ISR 读另一个。 */
|
||||
};
|
||||
|
||||
#endif /* COMM_ETHERCAT_SLAVE_H */
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @file ethercat_sync.cpp
|
||||
* @brief EtherCAT SYNC0 周期同步实现。
|
||||
*/
|
||||
#include "comm/ethercat_sync.h"
|
||||
#include "comm/ethercat_slave.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "main.h"
|
||||
|
||||
#if CONFIG_ETHERCAT_ENABLE
|
||||
|
||||
extern "C" {
|
||||
#include "gd32e50x_rcu.h"
|
||||
#include "gd32e50x_gpio.h"
|
||||
#include "gd32e50x_exti.h"
|
||||
#include "gd32e50x_misc.h"
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 1)
|
||||
#include "gd32e50x_timer.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
void EthercatSync::init()
|
||||
{
|
||||
/* 使能 GPIOB 时钟(SYNC0=PB0 / IRQ=PB1 / SYNC1=PB2) */
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
rcu_periph_clock_enable(RCU_AF);
|
||||
|
||||
/* 配置三个引脚为上拉输入(EXTI 下降沿触发:LAN9252 输出低有效脉冲) */
|
||||
gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ,
|
||||
CONFIG_ESC_SYNC0_PIN | CONFIG_ESC_IRQ_PIN | CONFIG_ESC_SYNC1_PIN);
|
||||
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 0)
|
||||
/* ---- 默认:EXTI 外部触发模式 ---- */
|
||||
/* SYNC0:PB0 -> EXTI0,下降沿,preempt=0 */
|
||||
gpio_exti_source_select(CONFIG_ESC_SYNC0_EXTI_SOURCE_PORT, CONFIG_ESC_SYNC0_EXTI_SOURCE_PIN);
|
||||
exti_init(CONFIG_ESC_SYNC0_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
|
||||
exti_interrupt_enable(CONFIG_ESC_SYNC0_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC0_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_SYNC0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
|
||||
|
||||
/* ESC IRQ:PB1 -> EXTI1,下降沿,preempt=1 */
|
||||
gpio_exti_source_select(CONFIG_ESC_IRQ_EXTI_SOURCE_PORT, CONFIG_ESC_IRQ_EXTI_SOURCE_PIN);
|
||||
exti_init(CONFIG_ESC_IRQ_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
|
||||
exti_interrupt_enable(CONFIG_ESC_IRQ_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_IRQ_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_ESC, 0U);
|
||||
|
||||
/* SYNC1:PB2 -> EXTI2,下降沿,preempt=1(备用) */
|
||||
gpio_exti_source_select(CONFIG_ESC_SYNC1_EXTI_SOURCE_PORT, CONFIG_ESC_SYNC1_EXTI_SOURCE_PIN);
|
||||
exti_init(CONFIG_ESC_SYNC1_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
|
||||
exti_interrupt_enable(CONFIG_ESC_SYNC1_EXTI);
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
|
||||
nvic_irq_enable(CONFIG_ESC_SYNC1_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC1, 0U);
|
||||
#else
|
||||
/* ---- 降级模式:TIMER0 内部定时 ---- */
|
||||
rcu_periph_clock_enable(RCU_TIMER0);
|
||||
timer_parameter_struct timer_initpara;
|
||||
timer_struct_para_init(&timer_initpara);
|
||||
/* 100us 周期:180MHz / (179+1) / (99+1) = 10kHz = 100us */
|
||||
timer_initpara.prescaler = 179U;
|
||||
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
|
||||
timer_initpara.counterdirection = TIMER_COUNTER_UP;
|
||||
timer_initpara.period = 99U;
|
||||
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
|
||||
timer_init(TIMER0, &timer_initpara);
|
||||
timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);
|
||||
timer_interrupt_enable(TIMER0, TIMER_INT_UP);
|
||||
nvic_irq_enable(TIMER0_IRQn, CONFIG_IRQ_PREEMPT_ETHERCAT_SYNC, 0U);
|
||||
timer_enable(TIMER0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EthercatSync::onSync0Exti()
|
||||
{
|
||||
if (exti_interrupt_flag_get(CONFIG_ESC_SYNC0_EXTI) != RESET) {
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC0_EXTI);
|
||||
EthercatSlave::periodicIsr();
|
||||
}
|
||||
}
|
||||
|
||||
void EthercatSync::onEscIrqExti()
|
||||
{
|
||||
if (exti_interrupt_flag_get(CONFIG_ESC_IRQ_EXTI) != RESET) {
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_IRQ_EXTI);
|
||||
/* 极简:仅置位任务,事件处理移主循环 */
|
||||
TaskManager::set(1UL << TASK_ETHERCAT_PROCESS);
|
||||
}
|
||||
}
|
||||
|
||||
void EthercatSync::onSync1Exti()
|
||||
{
|
||||
if (exti_interrupt_flag_get(CONFIG_ESC_SYNC1_EXTI) != RESET) {
|
||||
exti_interrupt_flag_clear(CONFIG_ESC_SYNC1_EXTI);
|
||||
/* SYNC1 备用,暂不处理 */
|
||||
}
|
||||
}
|
||||
|
||||
void EthercatSync::onTimerIrq()
|
||||
{
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 1)
|
||||
if (timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP) != RESET) {
|
||||
timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);
|
||||
EthercatSlave::periodicIsr();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ---- 中断入口 C 包装 ---- */
|
||||
extern "C" {
|
||||
|
||||
#if (CONFIG_ETHERCAT_SYNC_MODE == 0)
|
||||
void EXTI0_IRQHandler(void) { EthercatSync::onSync0Exti(); }
|
||||
void EXTI1_IRQHandler(void) { EthercatSync::onEscIrqExti(); }
|
||||
void EXTI2_IRQHandler(void) { EthercatSync::onSync1Exti(); }
|
||||
#else
|
||||
void TIMER0_IRQHandler(void) { EthercatSync::onTimerIrq(); }
|
||||
#endif
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
#endif /* CONFIG_ETHERCAT_ENABLE */
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @file ethercat_sync.h
|
||||
* @brief EtherCAT SYNC0 周期同步接口(EXTI 独立向量 / TIMER0 降级)。
|
||||
* @note 默认由 LAN9252 SYNC0 引脚(PB0/EXTI0)硬件触发周期中断,
|
||||
* preempt=0 独占向量保证抖动 <1us;CONFIG_ETHERCAT_SYNC_MODE=1 时用 TIMER0 降级。
|
||||
* ESC IRQ(PB1/EXTI1,preempt=1)与 SYNC1(PB2/EXTI2,preempt=1)独立向量。
|
||||
*/
|
||||
#ifndef COMM_ETHERCAT_SYNC_H
|
||||
#define COMM_ETHERCAT_SYNC_H
|
||||
|
||||
class EthercatSync {
|
||||
public:
|
||||
/** @brief 初始化 SYNC0/IRQ/SYNC1 EXTI 引脚与 NVIC(或降级模式 TIMER0)。 */
|
||||
static void init();
|
||||
|
||||
/** @brief SYNC0 EXTI 中断处理(EXTI0)。 */
|
||||
static void onSync0Exti();
|
||||
/** @brief ESC IRQ EXTI 中断处理(EXTI1)。 */
|
||||
static void onEscIrqExti();
|
||||
/** @brief SYNC1 EXTI 中断处理(EXTI2,备用)。 */
|
||||
static void onSync1Exti();
|
||||
/** @brief TIMER0 降级模式中断处理。 */
|
||||
static void onTimerIrq();
|
||||
};
|
||||
|
||||
#endif /* COMM_ETHERCAT_SYNC_H */
|
||||
Reference in New Issue
Block a user