fix(esc_lan9252): 修复LAN9252 HBI访问模型和IRQ输出使能问题
- 修正HBI访问模型:将ESC核心CSR(0x0000-0x0FFF)改为通过ECAT_CSR_CMD/DATA 间接端口访问,解决此前直接寻址导致核心寄存器恒读0/写丢弃的问题 - 新增csrRead/csrWrite/pramRead/pramWrite接口,正确实现LAN9252访问端口操作 - 添加PDI就绪检测:等待DL_STATUS bit0置位确认EEPROM加载完成和EtherCAT核心使能 - 实现IRQ引脚输出使能:配置IRQ_CFG和INT_EN寄存器,使EtherCAT事件能到达PB1, 解决PDI_Isr未触发导致SAFEOP->OP无限挂起的问题 - 为ESC_EXMC硬件诊断添加CONFIG_ESC_EXMC_DIAG门控,默认关闭避免影响正常运行 - 修正system_gd32e50x.c中HXTAL故障诊断的寄存器偏移和位设置错
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "dc_app.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stddef.h> /* offsetof:下方布局守卫断言使用 */
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* el9800appl.h 桩 include (SSC 内部需要)
|
||||
@@ -49,6 +50,13 @@ TOBJ6000;
|
||||
static TOBJ7000 s_outputObj = {4, {0, 0, 0, 0}};
|
||||
static TOBJ6000 s_inputObj = {4, {0, 0, 0, 0}};
|
||||
|
||||
/* 布局守卫(1.5.42):SSC 的 SDO 单条目访问按"si0 占 16bit,其后按 BitLength 紧排"
|
||||
* 计算偏移(OBJ_GetEntryOffset),故 UINT64 数组必须紧跟在 UINT16 之后落在字节 2。
|
||||
* OBJ_STRUCT_PACKED_START 曾被留空导致其落在字节 8、SDO 读写错位,加断言防回归。 */
|
||||
_Static_assert(offsetof(TOBJ7000, u64Data) == 2, "TOBJ7000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
_Static_assert(offsetof(TOBJ6000, u64Data) == 2, "TOBJ6000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
_Static_assert(sizeof(TOBJ7000) == 34, "TOBJ7000 尺寸应为 2+32");
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* SYNC 诊断对象 0x2000 (只读, 主站 SDO 轮询)
|
||||
* sub1=SYNC0 计数 sub2=SYNC1 计数 sub3=ESC IRQ 计数 sub4=uptime 秒
|
||||
@@ -66,6 +74,9 @@ TOBJ2000;
|
||||
|
||||
static TOBJ2000 s_syncDiag = {4, 0, 0, 0, 0};
|
||||
|
||||
/* 同上:UINT32 首条目必须落在字节 2(未紧排时为 4)。 */
|
||||
_Static_assert(offsetof(TOBJ2000, u32Sync0) == 2, "TOBJ2000 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
|
||||
/* EthercatSync 计数器 getter (C++ 侧 extern "C" 导出) */
|
||||
extern UINT32 EcatDbg_GetSync0(void);
|
||||
extern UINT32 EcatDbg_GetSync1(void);
|
||||
@@ -113,29 +124,57 @@ static TOBJ_PDO_MAPPING sTxPDOMap = {4, {
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Entry Descriptions
|
||||
*
|
||||
* 重要(1.5.42 修正): OBJ_GetEntryDesc()(objdef.c:554) 只对 OBJCODE_ARR 复用
|
||||
* pEntryDesc[1] 描述所有子索引; 对 OBJCODE_REC 是逐下标索引 pEntryDesc[Subindex]。
|
||||
* 因此 REC 对象的描述数组必须给满(Sub0..SubN 共 N+1 项), 否则 SDO Info 越界读到
|
||||
* 相邻常量数组的垃圾, 主站 config_map 报 "Unsupported access to an object" /
|
||||
* "Attempt to read to a write only object"(实测 0x1600/0x1A00 的 sub0/3/4 报错
|
||||
* 而 sub1/2 侥幸通过, 正是越界特征)。
|
||||
* 另: 可被 PDO 映射的数据子条目必须带 OBJACCESS_RxPDOMAPPING/TxPDOMAPPING,
|
||||
* 否则主站校验 0x1600/0x1A00 的映射目标时会拒绝(官方 sampleappl.h 同此)。
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/* PDO Assign (0x1C12/0x1C13): Sub0=UINT8 count, Sub1..n=UINT16 index */
|
||||
/* PDO Assign (0x1C12/0x1C13): ARR, Sub0=UINT8 count, Sub1..n=UINT16 index。
|
||||
* ARR 只需 2 项(Sub0 + 通用项), 由 OBJ_GetEntryDesc 复用 [1]。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOAssignEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}
|
||||
};
|
||||
|
||||
/* PDO Mapping (0x1600/0x1A00): Sub0=UINT8 count, Sub1..n=UINT32 mapping */
|
||||
/* PDO Mapping (0x1600/0x1A00): REC 含 4 个映射条目 -> 必须给 Sub0..Sub4 共 5 项。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asPDOMappingEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}, /* Sub0: 条目数 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub1 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub2 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* Sub3 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ} /* Sub4 */
|
||||
};
|
||||
|
||||
/* Data object 0x7000/0x6000: Sub0=UINT8 count, Sub1..4=UINT64 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asDataEntryDesc[] = {
|
||||
/* 0x7000 Output Data: REC, Sub0..Sub4 共 5 项; 子条目可被 RxPDO 映射。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asOutputEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE}
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READWRITE | OBJACCESS_RXPDOMAPPING}
|
||||
};
|
||||
|
||||
/* SYNC 诊断 0x2000: Sub0=UINT8 count, Sub1..4=UINT32 (只读) */
|
||||
/* 0x6000 Input Data: REC, Sub0..Sub4 共 5 项; 子条目可被 TxPDO 映射, 输入侧只读。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asInputEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING},
|
||||
{DEFTYPE_UNSIGNED64, 0x40, ACCESS_READ | OBJACCESS_TXPDOMAPPING}
|
||||
};
|
||||
|
||||
/* SYNC 诊断 0x2000: REC, Sub0..Sub4 共 5 项, 纯只读不参与 PDO 映射。 */
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asSyncDiagEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}
|
||||
};
|
||||
|
||||
@@ -156,29 +195,29 @@ OBJCONST UCHAR OBJMEM aName0x2000[] = "SYNC Diag";
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
TOBJECT OBJMEM ApplicationObjDicList[] = {
|
||||
/* 0x1C12: RxPDO Assign */
|
||||
/* 0x1C12: RxPDO Assign (ARR of UINT16; DEFTYPE 为元素类型) */
|
||||
{NULL, NULL, 0x1C12, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)},
|
||||
asPDOAssignEntryDesc, aName0x1C12, &sRxPDOassign, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1600: RxPDO Mapping */
|
||||
/* 0x1600: RxPDO Mapping (REC, 4 条目 -> 描述数组必须 5 项) */
|
||||
{NULL, NULL, 0x1600, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)},
|
||||
asPDOMappingEntryDesc, aName0x1600, &sRxPDOMap, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x7000: Output Data (主站→从站) */
|
||||
{NULL, NULL, 0x7000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
asDataEntryDesc, aName0x7000, &s_outputObj, NULL, NULL, 0x0000},
|
||||
asOutputEntryDesc, aName0x7000, &s_outputObj, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1C13: TxPDO Assign */
|
||||
{NULL, NULL, 0x1C13, {DEFTYPE_UNSIGNED16, 1 | (OBJCODE_ARR << 8)},
|
||||
asPDOAssignEntryDesc, aName0x1C13, &sTxPDOassign, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x1A00: TxPDO Mapping */
|
||||
/* 0x1A00: TxPDO Mapping (REC, 4 条目 -> 描述数组必须 5 项) */
|
||||
{NULL, NULL, 0x1A00, {DEFTYPE_PDOMAPPING, 4 | (OBJCODE_REC << 8)},
|
||||
asPDOMappingEntryDesc, aName0x1A00, &sTxPDOMap, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x6000: Input Data (从站→主站) */
|
||||
{NULL, NULL, 0x6000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
asDataEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000},
|
||||
asInputEntryDesc, aName0x6000, &s_inputObj, NULL, NULL, 0x0000},
|
||||
|
||||
/* 0x2000: SYNC 诊断 (只读, 主站 SDO 轮询验证 DC 通路) */
|
||||
{NULL, NULL, 0x2000, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
|
||||
@@ -517,12 +517,21 @@
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Struct Packing (ARM AC6: __attribute__((packed)) 不需要前后宏,留空即可)
|
||||
------ Struct Packing
|
||||
------
|
||||
------ 1.5.42 修正:此前三对宏全部留空,注释误以为"AC6 不需要"。实际这些宏就是 SSC
|
||||
------ 用来紧排结构体的唯一手段——SSC 的 SDO 读写按"si0 占 16bit,其后各条目按
|
||||
------ BitLength 紧排"计算字节偏移(OBJ_GetEntryOffset, objdef.c:609),留空则结构体
|
||||
------ 按编译器自然对齐,与该假设不符。armclang 实测:{UINT16; UINT64[4]} 的 UINT64
|
||||
------ 落在字节 8 而非 2(编译期断言验证),{UINT16; UINT32 x4} 落在字节 4 而非 2,
|
||||
------ 于是 0x7000/0x6000/0x2000 的 SDO 单条目访问全部读写错位内存;SSC 内部
|
||||
------ 0x1C3x(TSYNCMANPAR)/0x10F1 等同样受影响。
|
||||
------ AC6/armclang 支持 GCC 风格 __attribute__((packed)),置于 struct 关键字之后
|
||||
------ (即 START 位置)对整个结构体生效。
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef STRUCT_PACKED_START
|
||||
#define STRUCT_PACKED_START
|
||||
#define STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef STRUCT_PACKED_END
|
||||
@@ -530,7 +539,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_START
|
||||
#define MBX_STRUCT_PACKED_START
|
||||
#define MBX_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_END
|
||||
@@ -538,7 +547,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_START
|
||||
#define OBJ_STRUCT_PACKED_START
|
||||
#define OBJ_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_END
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
* @file hw_gd32exmc.cpp
|
||||
* @brief SSC V5.11 硬件 port 层 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode)
|
||||
*
|
||||
* 实现 el9800hw.h 中声明的全部 HW_* 函数,将 SSC 的 ESC 访问请求
|
||||
* 转发到 EscExmcBus (EXMC NOR/SRAM Region0, 16-bit 内存映射)。
|
||||
* 实现 el9800hw.h 中声明的全部 HW_* 函数,将 SSC 的 ESC 访问请求
|
||||
* 转发到 EscLan9252 的间接访问端口(1.5.39 按 DS 12.11 修正访问模型):
|
||||
* - EtherCAT 核心 CSR(0x0000-0x0FFF:AL Status/Event、SyncManager、LED 等)
|
||||
* → ECAT_CSR_CMD(0x304)/ECAT_CSR_DATA(0x300) 间接端口;
|
||||
* - 过程 RAM(0x1000-0x1FFF:邮箱 0x1000+、PDO 0x1400+)
|
||||
* → PRAM FIFO(0x000-0x03C 数据口 + 0x308-0x314 地址/命令)。
|
||||
* 1.5.37 及之前直读/直写 HBI 索引(误以为索引空间统一可寻址),核心 CSR 全部
|
||||
* 读 0/写丢弃——SSC 对主站 AL Control 完全失聪,主站恒读 AL=INIT。
|
||||
*
|
||||
* HBI Indexed 寻址协议(A2=LAN9252 Pin29←PD12=EXMC_A17, A1=Pin25←PD11=EXMC_A16;
|
||||
* EXMC 16-bit 模式地址左移 1 位,见 esc_lan9252.cpp 头注释):
|
||||
* 1. 写 HBI_IDX_0 (EXMC offset 0x00000) = ESC 寄存器字节地址
|
||||
* 2. 读/写 HBI_DATA_0 低 word (EXMC offset 0x40000, A2=1/A1=0)
|
||||
* 3. 读/写 HBI_DATA_0 高 word (EXMC offset 0x60000, A2=1/A1=1)
|
||||
*
|
||||
* 并发保护:
|
||||
* - HW_EscRead/HW_EscWrite 等主循环函数:内部经 EscExmcBus::maskEnter/Exit
|
||||
* 屏蔽 EXTI0+EXTI1(HBI Indexed 是两步事务,不可被 ISR 打断)。
|
||||
* - HW_EscReadIsr/HW_EscWriteIsr (ISR):在 preempt=0 的 SYNC0 ISR 内调用,
|
||||
* 天然不会被抢占;PDI_Isr/Sync1_Isr(preempt=1) 由 ethercat_sync.cpp 包装器
|
||||
* 并发保护(不变):
|
||||
* - HW_EscRead/HW_EscWrite 等主循环函数:内部经 EscExmcBus::maskEnter/Exit
|
||||
* 屏蔽 EXTI0+EXTI1(访问端口是多步事务,不可被 ISR 打断)。
|
||||
* - HW_EscReadIsr/HW_EscWriteIsr (ISR):在 preempt=0 的 SYNC0 ISR 内调用,
|
||||
* 天然不会被抢占;PDI_Isr/Sync1_Isr(preempt=1) 由 ethercat_sync.cpp 包装器
|
||||
* 屏蔽 EXTI0 后调用。
|
||||
*/
|
||||
|
||||
@@ -28,126 +28,76 @@
|
||||
|
||||
/* 项目硬件驱动 */
|
||||
#include "comm/esc_exmc_bus.h"
|
||||
#include "comm/esc_lan9252.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* HBI Indexed Mode EXMC 偏移 (与 esc_lan9252.cpp 一致)
|
||||
*---------------------------------------------------------------------------*/
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0, A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1(EXMC_A17), A1=0(EXMC_A16) */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1(EXMC_A17), A1=1(EXMC_A16) */
|
||||
|
||||
/* AL Event Mask 寄存器影子 (ESC 0x0204) */
|
||||
volatile uint16_t s_alEventMask = 0xFFFFU;
|
||||
|
||||
/* SSC 看门狗软件定时器 (DWT CYCCNT 基准) */
|
||||
volatile uint32_t s_timerBase = 0U;
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* 内部 HBI 访问原语 (无锁,调用者负责并发保护)
|
||||
*---------------------------------------------------------------------------*/
|
||||
constexpr uint16_t ESC_PRAM_BASE = 0x1000U; /**< 过程 RAM 起点(DS 12.11):≥此地址走 PRAM FIFO */
|
||||
|
||||
inline void hbiSetIndex(uint16_t addr)
|
||||
{
|
||||
EscExmcBus::write16(HBI_IDX0_OFFSET, addr);
|
||||
}
|
||||
|
||||
inline uint16_t hbiReadLow(void)
|
||||
{
|
||||
return EscExmcBus::read16(HBI_DATA0_LOW_OFF);
|
||||
}
|
||||
|
||||
inline uint16_t hbiReadHigh(void)
|
||||
{
|
||||
return EscExmcBus::read16(HBI_DATA0_HIGH_OFF);
|
||||
}
|
||||
|
||||
inline void hbiWriteLow(uint16_t v)
|
||||
{
|
||||
EscExmcBus::write16(HBI_DATA0_LOW_OFF, v);
|
||||
}
|
||||
|
||||
inline void hbiWriteHigh(uint16_t v)
|
||||
{
|
||||
EscExmcBus::write16(HBI_DATA0_HIGH_OFF, v);
|
||||
}
|
||||
|
||||
/** 从 ESC 地址读取 Len 字节到 pData (按 16-bit word 对齐处理) */
|
||||
void hbiRead(void *pData, uint16_t address, uint16_t len)
|
||||
/** 从 ESC 地址读取 Len 字节到 pData(按地址自动路由 CSR 端口 / PRAM FIFO) */
|
||||
void escRead(MEM_ADDR *pData, uint16_t address, uint16_t len)
|
||||
{
|
||||
uint8_t *dst = (uint8_t *)pData;
|
||||
if (address >= ESC_PRAM_BASE) {
|
||||
EscLan9252::pramRead(address, dst, len);
|
||||
return;
|
||||
}
|
||||
/* CSR 端口:size 1 任意地址,size 2 需偶地址(DS 表 12-14) */
|
||||
uint16_t addr = address;
|
||||
uint16_t remaining = len;
|
||||
|
||||
/* 奇数起始地址:读低 byte */
|
||||
if ((addr & 1U) != 0U && remaining > 0U) {
|
||||
hbiSetIndex(addr & 0xFFFEU);
|
||||
uint16_t w = hbiReadLow();
|
||||
*dst++ = (uint8_t)((w >> 8) & 0xFFU);
|
||||
*dst++ = (uint8_t)EscLan9252::csrRead(addr, 1U);
|
||||
addr++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
/* 对齐的 16-bit word 读取 */
|
||||
while (remaining >= 2U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
const uint16_t w = (uint16_t)EscLan9252::csrRead(addr, 2U);
|
||||
dst[0] = (uint8_t)(w & 0xFFU);
|
||||
dst[1] = (uint8_t)((w >> 8) & 0xFFU);
|
||||
dst[1] = (uint8_t)(w >> 8);
|
||||
dst += 2;
|
||||
addr += 2;
|
||||
remaining -= 2;
|
||||
}
|
||||
|
||||
/* 尾部奇数字节 */
|
||||
if (remaining > 0U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
*dst = (uint8_t)(w & 0xFFU);
|
||||
*dst = (uint8_t)EscLan9252::csrRead(addr, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
/** 向 ESC 地址写入 Len 字节 (按 16-bit word 对齐处理) */
|
||||
void hbiWrite(const void *pData, uint16_t address, uint16_t len)
|
||||
/** 向 ESC 地址写入 Len 字节(按地址自动路由 CSR 端口 / PRAM FIFO) */
|
||||
void escWrite(const MEM_ADDR *pData, uint16_t address, uint16_t len)
|
||||
{
|
||||
const uint8_t *src = (const uint8_t *)pData;
|
||||
if (address >= ESC_PRAM_BASE) {
|
||||
EscLan9252::pramWrite(address, src, len);
|
||||
return;
|
||||
}
|
||||
uint16_t addr = address;
|
||||
uint16_t remaining = len;
|
||||
|
||||
/* 奇数起始地址:RMW 高 byte */
|
||||
if ((addr & 1U) != 0U && remaining > 0U) {
|
||||
uint16_t aligned = addr & 0xFFFEU;
|
||||
hbiSetIndex(aligned);
|
||||
uint16_t w = hbiReadLow();
|
||||
w = (w & 0x00FFU) | ((uint16_t)(*src++) << 8);
|
||||
hbiSetIndex(aligned);
|
||||
hbiWriteLow(w);
|
||||
EscLan9252::csrWrite(addr, *src, 1U);
|
||||
src++;
|
||||
addr++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
/* 对齐的 16-bit word 写入 */
|
||||
while (remaining >= 2U) {
|
||||
uint16_t w = (uint16_t)src[0] | ((uint16_t)src[1] << 8);
|
||||
hbiSetIndex(addr);
|
||||
hbiWriteLow(w);
|
||||
const uint16_t w = (uint16_t)src[0] | ((uint16_t)src[1] << 8);
|
||||
EscLan9252::csrWrite(addr, w, 2U);
|
||||
src += 2;
|
||||
addr += 2;
|
||||
remaining -= 2;
|
||||
}
|
||||
|
||||
/* 尾部奇数字节:RMW 低 byte */
|
||||
if (remaining > 0U) {
|
||||
hbiSetIndex(addr);
|
||||
uint16_t w = hbiReadLow();
|
||||
w = (w & 0xFF00U) | (uint16_t)(*src);
|
||||
hbiSetIndex(addr);
|
||||
hbiWriteLow(w);
|
||||
EscLan9252::csrWrite(addr, *src, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,8 +123,7 @@ UINT8 HW_Init(void)
|
||||
s_timerBase = DWT->CYCCNT;
|
||||
|
||||
/* 读取当前 AL Event Mask */
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
s_alEventMask = hbiReadLow();
|
||||
s_alEventMask = (uint16_t)EscLan9252::csrRead(ESC_AL_EVENTMASK_OFFSET, 2U);
|
||||
|
||||
return 0; /* 0 = success */
|
||||
}
|
||||
@@ -186,13 +135,12 @@ void HW_Release(void)
|
||||
|
||||
/**
|
||||
* @brief 读取 AL Event 寄存器 (0x0220),主循环上下文。
|
||||
* @note 内部屏蔽 EXTI0+EXTI1 保证 HBI 事务完整。
|
||||
* @note 内部屏蔽 EXTI0+EXTI1 保证访问端口事务完整。
|
||||
*/
|
||||
UINT16 HW_GetALEventRegister(void)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(ESC_AL_EVENT_OFFSET);
|
||||
const UINT16 v = hbiReadLow();
|
||||
const UINT16 v = (UINT16)EscLan9252::csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
return v;
|
||||
}
|
||||
@@ -202,8 +150,7 @@ UINT16 HW_GetALEventRegister(void)
|
||||
*/
|
||||
UINT16 HW_GetALEventRegister_Isr(void)
|
||||
{
|
||||
hbiSetIndex(ESC_AL_EVENT_OFFSET);
|
||||
return hbiReadLow();
|
||||
return (UINT16)EscLan9252::csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,8 +160,7 @@ void HW_ResetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
s_alEventMask &= ~intMask;
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
hbiWriteLow(s_alEventMask);
|
||||
EscLan9252::csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -225,8 +171,7 @@ void HW_SetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
s_alEventMask |= intMask;
|
||||
hbiSetIndex(ESC_AL_EVENTMASK_OFFSET);
|
||||
hbiWriteLow(s_alEventMask);
|
||||
EscLan9252::csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -239,12 +184,9 @@ void HW_SetLed(UINT8 RunLed, UINT8 ErrLed)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
/* Run LED override (0x0138): bit0 = LED on */
|
||||
hbiSetIndex(ESC_RUN_LED_OVERRIDE);
|
||||
hbiWriteLow(RunLed ? 0x0001U : 0x0000U);
|
||||
|
||||
EscLan9252::csrWrite(ESC_RUN_LED_OVERRIDE, RunLed ? 0x0001U : 0x0000U, 2U);
|
||||
/* Error LED override (0x0139): bit0 = LED on */
|
||||
hbiSetIndex(ESC_ERROR_LED_OVERRIDE);
|
||||
hbiWriteLow(ErrLed ? 0x0001U : 0x0000U);
|
||||
EscLan9252::csrWrite(ESC_ERROR_LED_OVERRIDE, ErrLed ? 0x0001U : 0x0000U, 2U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -255,14 +197,14 @@ void HW_SetLed(UINT8 RunLed, UINT8 ErrLed)
|
||||
void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiRead(pData, Address, Len);
|
||||
escRead(pData, Address, Len);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiWrite(pData, Address, Len);
|
||||
escWrite(pData, Address, Len);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
@@ -272,12 +214,12 @@ void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
|
||||
void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
hbiRead(pData, Address, Len);
|
||||
escRead(pData, Address, Len);
|
||||
}
|
||||
|
||||
void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
{
|
||||
hbiWrite(pData, Address, Len);
|
||||
escWrite(pData, Address, Len);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
@@ -285,59 +227,49 @@ void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len)
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief 禁用指定 SM 通道 (清 SM Activate 寄存器 bit0)。
|
||||
* @brief 禁用指定 SM 通道 (清 SM Activate 寄存器 0x0806+8n bit0)。
|
||||
* @param channel SM 通道号 (0-15)
|
||||
* @note 1.5.39:偏移由 +4 改为 +6——+4 是 SM Control(操作模式字段),
|
||||
* Activate(bit0=SM Enable)在 +6;旧值从未真正生效过(访问层全空)。
|
||||
*/
|
||||
void HW_DisableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
/* SM 寄存器基址 0x0800,每个 SM 占 8 字节,Activate 在 offset +4 (bit0) */
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
uint16_t activateAddr = smBase + 4U;
|
||||
|
||||
const uint16_t activateAddr = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(activateAddr);
|
||||
uint16_t val = hbiReadLow();
|
||||
val &= ~0x0001U; /* 清 bit0 = deactivate */
|
||||
hbiSetIndex(activateAddr);
|
||||
hbiWriteLow(val);
|
||||
uint8_t val = (uint8_t)EscLan9252::csrRead(activateAddr, 1U);
|
||||
val &= (uint8_t)~0x01U; /* 清 bit0 = deactivate */
|
||||
EscLan9252::csrWrite(activateAddr, val, 1U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 使能指定 SM 通道 (置 SM Activate 寄存器 bit0)。
|
||||
* @brief 使能指定 SM 通道 (置 SM Activate 寄存器 0x0806+8n bit0)。
|
||||
*/
|
||||
void HW_EnableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
uint16_t activateAddr = smBase + 4U;
|
||||
|
||||
const uint16_t activateAddr = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiSetIndex(activateAddr);
|
||||
uint16_t val = hbiReadLow();
|
||||
val |= 0x0001U;
|
||||
hbiSetIndex(activateAddr);
|
||||
hbiWriteLow(val);
|
||||
uint8_t val = (uint8_t)EscLan9252::csrRead(activateAddr, 1U);
|
||||
val |= 0x01U;
|
||||
EscLan9252::csrWrite(activateAddr, val, 1U);
|
||||
EscExmcBus::maskExit(prev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定 SM 通道的寄存器块指针。
|
||||
* @note SSC 用此函数读取 SM 配置 (StartAddr, Length, Control, Status, Activate)。
|
||||
* 由于 HBI 是间接访问,无法返回真实指针。SSC 在 EL9800_HW 模式下
|
||||
* 实际通过 HW_EscRead 读取 SM 寄存器,此函数返回 NULL 并由调用者
|
||||
* 使用 HW_EscRead 替代。
|
||||
*
|
||||
* 检查 ecatslv.c 中 HW_GetSyncMan 的使用方式:
|
||||
* 如果 SSC 直接解引用返回的指针,则需要提供一个本地缓冲。
|
||||
* 这里提供一个静态缓冲,每次调用时从 ESC 读入。
|
||||
* 访问是间接端口,无法返回真实指针;提供静态缓冲,每次调用时从 ESC 读入。
|
||||
*/
|
||||
static TSYNCMAN s_smBuf;
|
||||
|
||||
TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel)
|
||||
{
|
||||
uint16_t smBase = 0x0800U + (uint16_t)channel * 8U;
|
||||
const uint16_t smBase = (uint16_t)(ESC_SYNCMAN_REG_OFFSET
|
||||
+ (uint16_t)channel * SIZEOF_SM_REGISTER);
|
||||
const uint32_t prev = EscExmcBus::maskEnter();
|
||||
hbiRead(&s_smBuf, smBase, SIZEOF_SM_REGISTER);
|
||||
escRead((MEM_ADDR *)&s_smBuf, smBase, SIZEOF_SM_REGISTER);
|
||||
EscExmcBus::maskExit(prev);
|
||||
return &s_smBuf;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user