feat(boot): BOOT 常驻原生 EtherCAT 刷 APP,跳转前按 HBI 使用情况补发 ESC 复位
把 BOOT 侧已上板跑通的 SSC/HBI 移植层一次性入库(comm/ecat + gd32e50x_exmc), 并修掉「刷完首启 APP 报 EXMC 总线未就绪、断电重上即好」这条实测链: - v1.1.0.13:jumpToApplication 前补一发 LAN9252 RST#。BOOT 侧 HBI 全是索引两步事务, bootEcatDeinit 末尾的 writeReg32 把 HBI_IDX_0 留在 0x0054,而 APP 起板探针判 IDX_0==0x1234(DS 9.5.4.2 复位默认值),带着残值跳进去就直接报总线未就绪。 - v1.1.0.14:那一发不能无条件下发。path=jump_latest_now(上电 3ms 直跳、这轮 ESC 从没 起过)也在电源刚稳的窗口里拉了 RST#,释放点距上电仅约 15ms,板上表现为每次上电都停 在 BOOT 里(串口停在 [J5-HEX];microlib + putChar 逐字符阻塞等 TBE,故日志停在半截 = CPU 真停在那儿,不是尾巴没刷出来)。现由 s_escBusUp 门控(bootEcatBringup 里 exmcInit 成功才置位);脉宽从 delay_1us(按 4cycle/迭代标定,实测 30ms 只走出 8ms) 换成 DWT CYCCNT 真 30ms + 释放后 2ms;跳转前打 [J6] boot v<ver> handoff。 delay_1ms 在这里不可用:本函数跑在 __disable_irq() 之后,SysTick 中断不递减就是永久 死等,而 BOOT 没开 IWDG —— 那会变成刷完直接静默变砖。 v1.1.0.12 的两条一并入库:10s OTA 窗口的活性判据并上 ecatSessionActive(防半截灌写被 当合法 APP 跳进去、HardFault→复位→留守 10s 形成活锁);0x100A 上报真版本串 (DEVICE_SW_VERSION_LEN 0x3→0x10,acSoftwareversion 显式给 LEN+1 防越界读)。 板上实测:v1.1.0.14 上电直跳恢复正常,APP v1.6.34 起板,自检 ethercat 不再降级。
This commit is contained in:
@@ -25,7 +25,7 @@ solution:
|
||||
target-set:
|
||||
- set:
|
||||
images:
|
||||
- project-context: GD32E50X_BOOT.Debug
|
||||
- project-context: GD32E50X_BOOT.Release
|
||||
debugger:
|
||||
name: ST-Link@pyOCD
|
||||
clock: 4000000
|
||||
|
||||
@@ -26,6 +26,34 @@ project:
|
||||
- group: spi_link
|
||||
files:
|
||||
- file: ../../spi_link/spi_link_v2.cpp
|
||||
- group: device_exmc
|
||||
files:
|
||||
- file: ./RTE/Device/GD32E508VE/gd32e50x_exmc.h
|
||||
- file: ./RTE/Device/GD32E508VE/gd32e50x_exmc.c
|
||||
- group: comm/ecat
|
||||
files:
|
||||
- file: ./comm/ecat/ecat_def.h
|
||||
- file: ./comm/ecat/esc.h
|
||||
- file: ./comm/ecat/el9800hw.h
|
||||
- file: ./comm/ecat/el9800appl.h
|
||||
- file: ./comm/ecat/applInterface.h
|
||||
- file: ./comm/ecat/ecatslv.h
|
||||
- file: ./comm/ecat/ecatappl.h
|
||||
- file: ./comm/ecat/mailbox.h
|
||||
- file: ./comm/ecat/ecatcoe.h
|
||||
- file: ./comm/ecat/sdoserv.h
|
||||
- file: ./comm/ecat/objdef.h
|
||||
- file: ./comm/ecat/coeappl.h
|
||||
- file: ./comm/ecat/dc_app.h
|
||||
- file: ./comm/ecat/ecatslv.c
|
||||
- file: ./comm/ecat/ecatappl.c
|
||||
- file: ./comm/ecat/mailbox.c
|
||||
- file: ./comm/ecat/ecatcoe.c
|
||||
- file: ./comm/ecat/sdoserv.c
|
||||
- file: ./comm/ecat/objdef.c
|
||||
- file: ./comm/ecat/coeappl.c
|
||||
- file: ./comm/ecat/boot_ecat_hw.cpp
|
||||
- file: ./comm/ecat/boot_ecat_app.cpp
|
||||
|
||||
output:
|
||||
type:
|
||||
@@ -66,6 +94,8 @@ project:
|
||||
- -fshort-wchar
|
||||
- -Wno-register
|
||||
- -IG:/Robot_v2/DCU/spi_link
|
||||
# SSC 头与源同目录平铺(comm/ecat),Phase 0 尺寸探针
|
||||
- -Icomm/ecat
|
||||
C:
|
||||
- -std=gnu11
|
||||
CPP:
|
||||
|
||||
@@ -0,0 +1,708 @@
|
||||
/*!
|
||||
\file gd32e50x_exmc.c
|
||||
\brief EXMC driver
|
||||
|
||||
\version 2026-02-09, V1.7.0, firmware for GD32E50x
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "gd32e50x_exmc.h"
|
||||
|
||||
/* EXMC bank0 register reset value */
|
||||
#define BANK0_SNCTL_REGION0_RESET ((uint32_t)0x000030DBU)
|
||||
#define BANK0_SNCTL_REGION1_2_3_RESET ((uint32_t)0x000030D2U)
|
||||
#define BANK0_SNTCFG_RESET ((uint32_t)0x0FFFFFFFU)
|
||||
#define BANK0_SNWTCFG_RESET ((uint32_t)0x0FFFFFFFU)
|
||||
|
||||
/* EXMC bank1/2 register reset mask */
|
||||
#define BANK1_2_NPCTL_RESET ((uint32_t)0x00000018U)
|
||||
#define BANK1_2_NPINTEN_RESET ((uint32_t)0x00000042U)
|
||||
#define BANK1_2_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU)
|
||||
#define BANK1_2_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU)
|
||||
|
||||
/* EXMC bank3 register reset mask */
|
||||
#define BANK3_NPCTL_RESET ((uint32_t)0x00000018U)
|
||||
#define BANK3_NPINTEN_RESET ((uint32_t)0x00000043U)
|
||||
#define BANK3_NPCTCFG_RESET ((uint32_t)0xFCFCFCFCU)
|
||||
#define BANK3_NPATCFG_RESET ((uint32_t)0xFCFCFCFCU)
|
||||
#define BANK3_PIOTCFG3_RESET ((uint32_t)0xFCFCFCFCU)
|
||||
|
||||
/* EXMC register bit offset */
|
||||
#define SNCTL_NRMUX_OFFSET ((uint32_t)1U)
|
||||
#define SNCTL_SBRSTEN_OFFSET ((uint32_t)8U)
|
||||
#define SNCTL_WRAPEN_OFFSET ((uint32_t)10U)
|
||||
#define SNCTL_WREN_OFFSET ((uint32_t)12U)
|
||||
#define SNCTL_NRWTEN_OFFSET ((uint32_t)13U)
|
||||
#define SNCTL_EXMODEN_OFFSET ((uint32_t)14U)
|
||||
#define SNCTL_ASYNCWAIT_OFFSET ((uint32_t)15U)
|
||||
|
||||
#define SNTCFG_AHLD_OFFSET ((uint32_t)4U)
|
||||
#define SNTCFG_DSET_OFFSET ((uint32_t)8U)
|
||||
#define SNTCFG_BUSLAT_OFFSET ((uint32_t)16U)
|
||||
|
||||
#define SNWTCFG_WAHLD_OFFSET ((uint32_t)4U)
|
||||
#define SNWTCFG_WDSET_OFFSET ((uint32_t)8U)
|
||||
#define SNWTCFG_WBUSLAT_OFFSET ((uint32_t)16U)
|
||||
|
||||
#define NPCTL_NDWTEN_OFFSET ((uint32_t)1U)
|
||||
#define NPCTL_ECCEN_OFFSET ((uint32_t)6U)
|
||||
|
||||
#define NPCTCFG_COMWAIT_OFFSET ((uint32_t)8U)
|
||||
#define NPCTCFG_COMHLD_OFFSET ((uint32_t)16U)
|
||||
#define NPCTCFG_COMHIZ_OFFSET ((uint32_t)24U)
|
||||
|
||||
#define NPATCFG_ATTWAIT_OFFSET ((uint32_t)8U)
|
||||
#define NPATCFG_ATTHLD_OFFSET ((uint32_t)16U)
|
||||
#define NPATCFG_ATTHIZ_OFFSET ((uint32_t)24U)
|
||||
|
||||
#define PIOTCFG_IOWAIT_OFFSET ((uint32_t)8U)
|
||||
#define PIOTCFG_IOHLD_OFFSET ((uint32_t)16U)
|
||||
#define PIOTCFG_IOHIZ_OFFSET ((uint32_t)24U)
|
||||
|
||||
#define INTEN_INTS_OFFSET ((uint32_t)3U)
|
||||
|
||||
/*!
|
||||
\brief deinitialize EXMC NOR/SRAM region
|
||||
\param[in] exmc_norsram_region: select the region of bank0
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_deinit(uint32_t exmc_norsram_region)
|
||||
{
|
||||
/* reset the registers */
|
||||
if(EXMC_BANK0_NORSRAM_REGION0 == exmc_norsram_region){
|
||||
EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION0_RESET;
|
||||
}else{
|
||||
EXMC_SNCTL(exmc_norsram_region) = BANK0_SNCTL_REGION1_2_3_RESET;
|
||||
}
|
||||
EXMC_SNTCFG(exmc_norsram_region) = BANK0_SNTCFG_RESET;
|
||||
EXMC_SNWTCFG(exmc_norsram_region) = BANK0_SNWTCFG_RESET;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize exmc_norsram_parameter_struct with the default values
|
||||
\param[in] none
|
||||
\param[out] exmc_norsram_init_struct: the initialized struct exmc_norsram_parameter_struct pointer
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct)
|
||||
{
|
||||
/* configure the structure with default values */
|
||||
exmc_norsram_init_struct->norsram_region = EXMC_BANK0_NORSRAM_REGION0;
|
||||
exmc_norsram_init_struct->address_data_mux = ENABLE;
|
||||
exmc_norsram_init_struct->memory_type = EXMC_MEMORY_TYPE_SRAM;
|
||||
exmc_norsram_init_struct->databus_width = EXMC_NOR_DATABUS_WIDTH_8B;
|
||||
exmc_norsram_init_struct->burst_mode = DISABLE;
|
||||
exmc_norsram_init_struct->nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
|
||||
exmc_norsram_init_struct->wrap_burst_mode = DISABLE;
|
||||
exmc_norsram_init_struct->nwait_config = EXMC_NWAIT_CONFIG_BEFORE;
|
||||
exmc_norsram_init_struct->memory_write = ENABLE;
|
||||
exmc_norsram_init_struct->nwait_signal = ENABLE;
|
||||
exmc_norsram_init_struct->extended_mode = DISABLE;
|
||||
exmc_norsram_init_struct->asyn_wait = DISABLE;
|
||||
exmc_norsram_init_struct->write_mode = EXMC_ASYN_WRITE;
|
||||
|
||||
/* read/write timing configure */
|
||||
exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime = 0xFU;
|
||||
exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime = 0xFU;
|
||||
exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime = 0xFFU;
|
||||
exmc_norsram_init_struct->read_write_timing->bus_latency = 0xFU;
|
||||
exmc_norsram_init_struct->read_write_timing->syn_clk_division = EXMC_SYN_CLOCK_RATIO_16_CLK;
|
||||
exmc_norsram_init_struct->read_write_timing->syn_data_latency = EXMC_DATALAT_17_CLK;
|
||||
exmc_norsram_init_struct->read_write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A;
|
||||
|
||||
/* write timing configure, when extended mode is used */
|
||||
exmc_norsram_init_struct->write_timing->asyn_address_setuptime = 0xFU;
|
||||
exmc_norsram_init_struct->write_timing->asyn_address_holdtime = 0xFU;
|
||||
exmc_norsram_init_struct->write_timing->asyn_data_setuptime = 0xFFU;
|
||||
exmc_norsram_init_struct->write_timing->bus_latency = 0xFU;
|
||||
exmc_norsram_init_struct->write_timing->asyn_access_mode = EXMC_ACCESS_MODE_A;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize EXMC NOR/SRAM region
|
||||
\param[in] exmc_norsram_parameter_struct: configure the EXMC NOR/SRAM parameter
|
||||
norsram_region: EXMC_BANK0_NORSRAM_REGIONx,x=0..3
|
||||
write_mode: EXMC_ASYN_WRITE,EXMC_SYN_WRITE
|
||||
extended_mode: ENABLE or DISABLE
|
||||
asyn_wait: ENABLE or DISABLE
|
||||
nwait_signal: ENABLE or DISABLE
|
||||
memory_write: ENABLE or DISABLE
|
||||
nwait_config: EXMC_NWAIT_CONFIG_BEFORE,EXMC_NWAIT_CONFIG_DURING
|
||||
wrap_burst_mode: ENABLE or DISABLE
|
||||
nwait_polarity: EXMC_NWAIT_POLARITY_LOW,EXMC_NWAIT_POLARITY_HIGH
|
||||
burst_mode: ENABLE or DISABLE
|
||||
databus_width: EXMC_NOR_DATABUS_WIDTH_8B,EXMC_NOR_DATABUS_WIDTH_16B
|
||||
memory_type: EXMC_MEMORY_TYPE_SRAM,EXMC_MEMORY_TYPE_PSRAM,EXMC_MEMORY_TYPE_NOR
|
||||
address_data_mux: ENABLE or DISABLE
|
||||
read_write_timing: struct exmc_norsram_timing_parameter_struct set the time
|
||||
asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D
|
||||
syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17)
|
||||
syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16)
|
||||
bus_latency: 1,..,16
|
||||
asyn_data_setuptime: 2,..,256
|
||||
asyn_address_holdtime: 2,..,16
|
||||
asyn_address_setuptime: 1,..,16
|
||||
write_timing: struct exmc_norsram_timing_parameter_struct set the time
|
||||
asyn_access_mode: EXMC_ACCESS_MODE_A,EXMC_ACCESS_MODE_B,EXMC_ACCESS_MODE_C,EXMC_ACCESS_MODE_D
|
||||
syn_data_latency: EXMC_DATALAT_n_CLK,(n=2,..,17)
|
||||
syn_clk_division: EXMC_SYN_CLOCK_RATIO_DISABLE, EXMC_SYN_CLOCK_RATIO_n_CLK,(n=2,..,16)
|
||||
bus_latency: 1,..,16
|
||||
asyn_data_setuptime: 2,..,256
|
||||
asyn_address_holdtime: 2,..,16
|
||||
asyn_address_setuptime: 1,..,16
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct)
|
||||
{
|
||||
uint32_t snctl = 0x00000000U,sntcfg = 0x00000000U,snwtcfg = 0x00000000U;
|
||||
|
||||
/* get the register value */
|
||||
snctl = EXMC_SNCTL(exmc_norsram_init_struct->norsram_region);
|
||||
|
||||
/* clear relative bits */
|
||||
snctl &= ((uint32_t)~(EXMC_SNCTL_NRMUX | EXMC_SNCTL_NRTP | EXMC_SNCTL_NRW | EXMC_SNCTL_SBRSTEN |
|
||||
EXMC_SNCTL_NREN | EXMC_SNCTL_NRWTPOL | EXMC_SNCTL_WRAPEN | EXMC_SNCTL_NRWTCFG |
|
||||
EXMC_SNCTL_WREN | EXMC_SNCTL_NRWTEN | EXMC_SNCTL_EXMODEN | EXMC_SNCTL_ASYNCWAIT |
|
||||
EXMC_SNCTL_SYNCWR ));
|
||||
|
||||
snctl |= (uint32_t)(exmc_norsram_init_struct->address_data_mux << SNCTL_NRMUX_OFFSET) |
|
||||
exmc_norsram_init_struct->memory_type |
|
||||
exmc_norsram_init_struct->databus_width |
|
||||
(exmc_norsram_init_struct->burst_mode << SNCTL_SBRSTEN_OFFSET) |
|
||||
exmc_norsram_init_struct->nwait_polarity |
|
||||
(exmc_norsram_init_struct->wrap_burst_mode << SNCTL_WRAPEN_OFFSET) |
|
||||
exmc_norsram_init_struct->nwait_config |
|
||||
(exmc_norsram_init_struct->memory_write << SNCTL_WREN_OFFSET) |
|
||||
(exmc_norsram_init_struct->nwait_signal << SNCTL_NRWTEN_OFFSET) |
|
||||
(exmc_norsram_init_struct->extended_mode << SNCTL_EXMODEN_OFFSET) |
|
||||
(exmc_norsram_init_struct->asyn_wait << SNCTL_ASYNCWAIT_OFFSET) |
|
||||
exmc_norsram_init_struct->write_mode;
|
||||
|
||||
sntcfg = (uint32_t)((exmc_norsram_init_struct->read_write_timing->asyn_address_setuptime - 1U ) & EXMC_SNTCFG_ASET )|
|
||||
(((exmc_norsram_init_struct->read_write_timing->asyn_address_holdtime - 1U ) << SNTCFG_AHLD_OFFSET ) & EXMC_SNTCFG_AHLD ) |
|
||||
(((exmc_norsram_init_struct->read_write_timing->asyn_data_setuptime - 1U ) << SNTCFG_DSET_OFFSET ) & EXMC_SNTCFG_DSET ) |
|
||||
(((exmc_norsram_init_struct->read_write_timing->bus_latency - 1U ) << SNTCFG_BUSLAT_OFFSET ) & EXMC_SNTCFG_BUSLAT )|
|
||||
exmc_norsram_init_struct->read_write_timing->syn_clk_division |
|
||||
exmc_norsram_init_struct->read_write_timing->syn_data_latency |
|
||||
exmc_norsram_init_struct->read_write_timing->asyn_access_mode;
|
||||
|
||||
/* nor flash access enable */
|
||||
if(EXMC_MEMORY_TYPE_NOR == exmc_norsram_init_struct->memory_type){
|
||||
snctl |= (uint32_t)EXMC_SNCTL_NREN;
|
||||
}
|
||||
|
||||
/* extended mode configure */
|
||||
if(ENABLE == exmc_norsram_init_struct->extended_mode){
|
||||
snwtcfg = (uint32_t)((exmc_norsram_init_struct->write_timing->asyn_address_setuptime - 1U) & EXMC_SNWTCFG_WASET ) |
|
||||
(((exmc_norsram_init_struct->write_timing->asyn_address_holdtime -1U ) << SNWTCFG_WAHLD_OFFSET ) & EXMC_SNWTCFG_WAHLD )|
|
||||
(((exmc_norsram_init_struct->write_timing->asyn_data_setuptime -1U ) << SNWTCFG_WDSET_OFFSET ) & EXMC_SNWTCFG_WDSET )|
|
||||
(((exmc_norsram_init_struct->write_timing->bus_latency - 1U ) << SNWTCFG_WBUSLAT_OFFSET ) & EXMC_SNWTCFG_WBUSLAT ) |
|
||||
exmc_norsram_init_struct->write_timing->asyn_access_mode;
|
||||
}else{
|
||||
snwtcfg = BANK0_SNWTCFG_RESET;
|
||||
}
|
||||
|
||||
/* configure the registers */
|
||||
EXMC_SNCTL(exmc_norsram_init_struct->norsram_region) = snctl;
|
||||
EXMC_SNTCFG(exmc_norsram_init_struct->norsram_region) = sntcfg;
|
||||
EXMC_SNWTCFG(exmc_norsram_init_struct->norsram_region) = snwtcfg;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief enable EXMC NOR/PSRAM bank region
|
||||
\param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_enable(uint32_t exmc_norsram_region)
|
||||
{
|
||||
EXMC_SNCTL(exmc_norsram_region) |= (uint32_t)EXMC_SNCTL_NRBKEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief disable EXMC NOR/PSRAM bank region
|
||||
\param[in] exmc_norsram_region: specifie the region of NOR/PSRAM Bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_disable(uint32_t exmc_norsram_region)
|
||||
{
|
||||
EXMC_SNCTL(exmc_norsram_region) &= ~(uint32_t)EXMC_SNCTL_NRBKEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief configure CRAM page size
|
||||
\param[in] exmc_norsram_region: specifie the region of NOR/PSRAM bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK0_NORSRAM_REGIONx(x=0..3)
|
||||
\param[in] page_size: CRAM page size
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_CRAM_AUTO_SPLIT: the clock is generated only during synchronous access
|
||||
\arg EXMC_CRAM_PAGE_SIZE_128_BYTES: page size is 128 bytes
|
||||
\arg EXMC_CRAM_PAGE_SIZE_256_BYTES: page size is 256 bytes
|
||||
\arg EXMC_CRAM_PAGE_SIZE_512_BYTES: page size is 512 bytes
|
||||
\arg EXMC_CRAM_PAGE_SIZE_1024_BYTES: page size is 1024 bytes
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_norsram_page_size_config(uint32_t exmc_norsram_region, uint32_t page_size)
|
||||
{
|
||||
/* reset the bits */
|
||||
EXMC_SNCTL(exmc_norsram_region) &= ~EXMC_SNCTL_CPS;
|
||||
|
||||
/* set the CPS bits */
|
||||
EXMC_SNCTL(exmc_norsram_region) |= page_size;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief deinitialize EXMC NAND bank
|
||||
\param[in] exmc_nand_bank: select the bank of NAND
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANKx_NAND(x=1..2)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_deinit(uint32_t exmc_nand_bank)
|
||||
{
|
||||
/* EXMC_BANK1_NAND or EXMC_BANK2_NAND */
|
||||
EXMC_NPCTL(exmc_nand_bank) = BANK1_2_NPCTL_RESET;
|
||||
EXMC_NPINTEN(exmc_nand_bank) = BANK1_2_NPINTEN_RESET;
|
||||
EXMC_NPCTCFG(exmc_nand_bank) = BANK1_2_NPCTCFG_RESET;
|
||||
EXMC_NPATCFG(exmc_nand_bank) = BANK1_2_NPATCFG_RESET;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize exmc_nand_parameter_struct with the default values
|
||||
\param[in] none
|
||||
\param[out] the initialized struct exmc_nand_parameter_struct pointer
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_struct_para_init(exmc_nand_parameter_struct* exmc_nand_init_struct)
|
||||
{
|
||||
/* configure the structure with default values */
|
||||
exmc_nand_init_struct->nand_bank = EXMC_BANK1_NAND;
|
||||
exmc_nand_init_struct->wait_feature = DISABLE;
|
||||
exmc_nand_init_struct->databus_width = EXMC_NAND_DATABUS_WIDTH_8B;
|
||||
exmc_nand_init_struct->ecc_logic = DISABLE;
|
||||
exmc_nand_init_struct->ecc_size = EXMC_ECC_SIZE_256BYTES;
|
||||
exmc_nand_init_struct->ctr_latency = 0x0U;
|
||||
exmc_nand_init_struct->atr_latency = 0x0U;
|
||||
exmc_nand_init_struct->common_space_timing->setuptime = 0xFCU;
|
||||
exmc_nand_init_struct->common_space_timing->waittime = 0xFCU;
|
||||
exmc_nand_init_struct->common_space_timing->holdtime = 0xFCU;
|
||||
exmc_nand_init_struct->common_space_timing->databus_hiztime = 0xFCU;
|
||||
exmc_nand_init_struct->attribute_space_timing->setuptime = 0xFCU;
|
||||
exmc_nand_init_struct->attribute_space_timing->waittime = 0xFCU;
|
||||
exmc_nand_init_struct->attribute_space_timing->holdtime = 0xFCU;
|
||||
exmc_nand_init_struct->attribute_space_timing->databus_hiztime = 0xFCU;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize EXMC NAND bank
|
||||
\param[in] exmc_nand_parameter_struct: configure the EXMC NAND parameter
|
||||
nand_bank: EXMC_BANK1_NAND,EXMC_BANK2_NAND
|
||||
ecc_size: EXMC_ECC_SIZE_xBYTES,x=256,512,1024,2048,4096
|
||||
atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16
|
||||
ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16
|
||||
ecc_logic: ENABLE or DISABLE
|
||||
databus_width: EXMC_NAND_DATABUS_WIDTH_8B,EXMC_NAND_DATABUS_WIDTH_16B
|
||||
wait_feature: ENABLE or DISABLE
|
||||
common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time
|
||||
databus_hiztime: 1,..,255
|
||||
holdtime: 1,..,254
|
||||
waittime: 2,..,255
|
||||
setuptime: 1,..,255
|
||||
attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time
|
||||
databus_hiztime: 1,..,255
|
||||
holdtime: 1,..,254
|
||||
waittime: 2,..,255
|
||||
setuptime: 1,..,255
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_init(exmc_nand_parameter_struct* exmc_nand_init_struct)
|
||||
{
|
||||
uint32_t npctl = 0x00000000U, npctcfg = 0x00000000U, npatcfg = 0x00000000U;
|
||||
|
||||
npctl = (uint32_t)(exmc_nand_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET)|
|
||||
EXMC_NPCTL_NDTP |
|
||||
exmc_nand_init_struct->databus_width |
|
||||
(exmc_nand_init_struct->ecc_logic << NPCTL_ECCEN_OFFSET)|
|
||||
exmc_nand_init_struct->ecc_size |
|
||||
exmc_nand_init_struct->ctr_latency |
|
||||
exmc_nand_init_struct->atr_latency;
|
||||
|
||||
npctcfg = (uint32_t)((exmc_nand_init_struct->common_space_timing->setuptime - 1U) & EXMC_NPCTCFG_COMSET ) |
|
||||
(((exmc_nand_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) |
|
||||
((exmc_nand_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) |
|
||||
(((exmc_nand_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ );
|
||||
|
||||
npatcfg = (uint32_t)((exmc_nand_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) |
|
||||
(((exmc_nand_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) |
|
||||
((exmc_nand_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD ) |
|
||||
(((exmc_nand_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ );
|
||||
|
||||
/* EXMC_BANK1_NAND or EXMC_BANK2_NAND initialize */
|
||||
EXMC_NPCTL(exmc_nand_init_struct->nand_bank) = npctl;
|
||||
EXMC_NPCTCFG(exmc_nand_init_struct->nand_bank) = npctcfg;
|
||||
EXMC_NPATCFG(exmc_nand_init_struct->nand_bank) = npatcfg;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief enable NAND bank
|
||||
\param[in] exmc_nand_bank: specifie the NAND bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANKx_NAND(x=1,2)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_enable(uint32_t exmc_nand_bank)
|
||||
{
|
||||
EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_NDBKEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief disable NAND bank
|
||||
\param[in] exmc_nand_bank: specifie the NAND bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANKx_NAND(x=1,2)
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_disable(uint32_t exmc_nand_bank)
|
||||
{
|
||||
EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_NDBKEN);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief enable or disable the EXMC NAND ECC function
|
||||
\param[in] exmc_nand_bank: specifie the NAND bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANKx_NAND(x=1,2)
|
||||
\param[in] newvalue: ENABLE or DISABLE
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_nand_ecc_config(uint32_t exmc_nand_bank, ControlStatus newvalue)
|
||||
{
|
||||
if (ENABLE == newvalue){
|
||||
/* enable the selected NAND bank ECC function */
|
||||
EXMC_NPCTL(exmc_nand_bank) |= EXMC_NPCTL_ECCEN;
|
||||
}else{
|
||||
/* disable the selected NAND bank ECC function */
|
||||
EXMC_NPCTL(exmc_nand_bank) &= (~EXMC_NPCTL_ECCEN);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get the EXMC ECC value
|
||||
\param[in] exmc_nand_bank: specifie the NAND bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANKx_NAND(x=1,2)
|
||||
\param[out] none
|
||||
\retval the error correction code(ECC) value
|
||||
*/
|
||||
uint32_t exmc_ecc_get(uint32_t exmc_nand_bank)
|
||||
{
|
||||
return (EXMC_NECC(exmc_nand_bank));
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief deinitialize EXMC PC card bank
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_pccard_deinit(void)
|
||||
{
|
||||
/* EXMC_BANK3_PCCARD */
|
||||
EXMC_NPCTL3 = BANK3_NPCTL_RESET;
|
||||
EXMC_NPINTEN3 = BANK3_NPINTEN_RESET;
|
||||
EXMC_NPCTCFG3 = BANK3_NPCTCFG_RESET;
|
||||
EXMC_NPATCFG3 = BANK3_NPATCFG_RESET;
|
||||
EXMC_PIOTCFG3 = BANK3_PIOTCFG3_RESET;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize exmc_pccard_parameter_struct parameter with the default values
|
||||
\param[in] none
|
||||
\param[out] the initialized struct exmc_pccard_parameter_struct pointer
|
||||
\retval none
|
||||
*/
|
||||
void exmc_pccard_struct_para_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct)
|
||||
{
|
||||
/* configure the structure with default values */
|
||||
exmc_pccard_init_struct->wait_feature = DISABLE;
|
||||
exmc_pccard_init_struct->ctr_latency = 0x0U;
|
||||
exmc_pccard_init_struct->atr_latency = 0x0U;
|
||||
exmc_pccard_init_struct->common_space_timing->setuptime = 0xFCU;
|
||||
exmc_pccard_init_struct->common_space_timing->waittime = 0xFCU;
|
||||
exmc_pccard_init_struct->common_space_timing->holdtime = 0xFCU;
|
||||
exmc_pccard_init_struct->common_space_timing->databus_hiztime = 0xFCU;
|
||||
exmc_pccard_init_struct->attribute_space_timing->setuptime = 0xFCU;
|
||||
exmc_pccard_init_struct->attribute_space_timing->waittime = 0xFCU;
|
||||
exmc_pccard_init_struct->attribute_space_timing->holdtime = 0xFCU;
|
||||
exmc_pccard_init_struct->attribute_space_timing->databus_hiztime = 0xFCU;
|
||||
exmc_pccard_init_struct->io_space_timing->setuptime = 0xFCU;
|
||||
exmc_pccard_init_struct->io_space_timing->waittime = 0xFCU;
|
||||
exmc_pccard_init_struct->io_space_timing->holdtime = 0xFCU;
|
||||
exmc_pccard_init_struct->io_space_timing->databus_hiztime = 0xFCU;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief initialize EXMC PC card bank
|
||||
\param[in] exmc_pccard_parameter_struct: configure the EXMC NAND parameter
|
||||
atr_latency: EXMC_ALE_RE_DELAY_x_HCLK,x=1..16
|
||||
ctr_latency: EXMC_CLE_RE_DELAY_x_HCLK,x=1..16
|
||||
wait_feature: ENABLE or DISABLE
|
||||
common_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time
|
||||
databus_hiztime: 1,..,255
|
||||
holdtime: 1,..,254
|
||||
waittime: 2,..,255
|
||||
setuptime: 1,..,255
|
||||
attribute_space_timing: struct exmc_nand_pccard_timing_parameter_struct set the time
|
||||
databus_hiztime: 1,..,255
|
||||
holdtime: 1,..,254
|
||||
waittime: 2,..,255
|
||||
setuptime: 1,..,255
|
||||
io_space_timing: exmc_nand_pccard_timing_parameter_struct set the time
|
||||
databus_hiztime: 0,..,255
|
||||
holdtime: 1,..,255
|
||||
waittime: 2,..,256
|
||||
setuptime: 1,..,256
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_pccard_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct)
|
||||
{
|
||||
/* configure the EXMC bank3 PC card control register */
|
||||
EXMC_NPCTL3 = (uint32_t)(exmc_pccard_init_struct->wait_feature << NPCTL_NDWTEN_OFFSET) |
|
||||
EXMC_NAND_DATABUS_WIDTH_16B |
|
||||
exmc_pccard_init_struct->ctr_latency |
|
||||
exmc_pccard_init_struct->atr_latency ;
|
||||
|
||||
/* configure the EXMC bank3 PC card common space timing configuration register */
|
||||
EXMC_NPCTCFG3 = (uint32_t)((exmc_pccard_init_struct->common_space_timing->setuptime - 1U)& EXMC_NPCTCFG_COMSET ) |
|
||||
(((exmc_pccard_init_struct->common_space_timing->waittime - 1U) << NPCTCFG_COMWAIT_OFFSET) & EXMC_NPCTCFG_COMWAIT ) |
|
||||
((exmc_pccard_init_struct->common_space_timing->holdtime << NPCTCFG_COMHLD_OFFSET) & EXMC_NPCTCFG_COMHLD ) |
|
||||
(((exmc_pccard_init_struct->common_space_timing->databus_hiztime - 1U) << NPCTCFG_COMHIZ_OFFSET) & EXMC_NPCTCFG_COMHIZ );
|
||||
|
||||
/* configure the EXMC bank3 PC card attribute space timing configuration register */
|
||||
EXMC_NPATCFG3 = (uint32_t)((exmc_pccard_init_struct->attribute_space_timing->setuptime - 1U) & EXMC_NPATCFG_ATTSET ) |
|
||||
(((exmc_pccard_init_struct->attribute_space_timing->waittime - 1U) << NPATCFG_ATTWAIT_OFFSET) & EXMC_NPATCFG_ATTWAIT ) |
|
||||
((exmc_pccard_init_struct->attribute_space_timing->holdtime << NPATCFG_ATTHLD_OFFSET) & EXMC_NPATCFG_ATTHLD )|
|
||||
(((exmc_pccard_init_struct->attribute_space_timing->databus_hiztime -1U) << NPATCFG_ATTHIZ_OFFSET) & EXMC_NPATCFG_ATTHIZ );
|
||||
|
||||
/* configure the EXMC bank3 PC card io space timing configuration register */
|
||||
EXMC_PIOTCFG3 = (uint32_t)((exmc_pccard_init_struct->io_space_timing->setuptime - 1U) & EXMC_PIOTCFG3_IOSET ) |
|
||||
(((exmc_pccard_init_struct->io_space_timing->waittime - 1U) << PIOTCFG_IOWAIT_OFFSET) & EXMC_PIOTCFG3_IOWAIT ) |
|
||||
((exmc_pccard_init_struct->io_space_timing->holdtime << PIOTCFG_IOHLD_OFFSET) & EXMC_PIOTCFG3_IOHLD )|
|
||||
((exmc_pccard_init_struct->io_space_timing->databus_hiztime << PIOTCFG_IOHIZ_OFFSET) & EXMC_PIOTCFG3_IOHIZ );
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief enable PC Card Bank
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_pccard_enable(void)
|
||||
{
|
||||
EXMC_NPCTL3 |= EXMC_NPCTL_NDBKEN;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief disable PC Card Bank
|
||||
\param[in] none
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_pccard_disable(void)
|
||||
{
|
||||
EXMC_NPCTL3 &= (~EXMC_NPCTL_NDBKEN);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief enable EXMC interrupt
|
||||
\param[in] exmc_bank: specifies the NAND bank,PC card bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC card bank
|
||||
\param[in] interrupt: EXMC interrupt flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_interrupt_enable(uint32_t exmc_bank,uint32_t interrupt)
|
||||
{
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
EXMC_NPINTEN(exmc_bank) |= interrupt;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief disable EXMC interrupt
|
||||
\param[in] exmc_bank: specifies the NAND bank , PC card bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC card bank
|
||||
\param[in] interrupt: EXMC interrupt flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_interrupt_disable(uint32_t exmc_bank,uint32_t interrupt)
|
||||
{
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
EXMC_NPINTEN(exmc_bank) &= (~interrupt);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get EXMC flag status
|
||||
\param[in] exmc_bank: specifies the NAND bank , PC card bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC Card bank
|
||||
\param[in] flag: EXMC status and flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag
|
||||
\param[out] none
|
||||
\retval FlagStatus: SET or RESET
|
||||
*/
|
||||
FlagStatus exmc_flag_get(uint32_t exmc_bank,uint32_t flag)
|
||||
{
|
||||
uint32_t status = 0x00000000U;
|
||||
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
status = EXMC_NPINTEN(exmc_bank);
|
||||
|
||||
if ((status & flag) != (uint32_t)flag ){
|
||||
/* flag is reset */
|
||||
return RESET;
|
||||
}else{
|
||||
/* flag is set */
|
||||
return SET;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief clear EXMC flag status
|
||||
\param[in] exmc_bank: specifie the NAND bank , PCCARD bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC card bank
|
||||
\param[in] flag: EXMC status and flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_FLAG_RISE: interrupt rising edge status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_LEVEL: interrupt high-level status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_FALL: interrupt falling edge status
|
||||
\arg EXMC_NAND_PCCARD_FLAG_FIFOE: FIFO empty flag
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_flag_clear(uint32_t exmc_bank,uint32_t flag)
|
||||
{
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
EXMC_NPINTEN(exmc_bank) &= (~flag);
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief get EXMC interrupt flag
|
||||
\param[in] exmc_bank: specifies the NAND bank , PC card bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC card bank
|
||||
\param[in] interrupt: EXMC interrupt flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag
|
||||
\param[out] none
|
||||
\retval FlagStatus: SET or RESET
|
||||
*/
|
||||
FlagStatus exmc_interrupt_flag_get(uint32_t exmc_bank,uint32_t interrupt)
|
||||
{
|
||||
uint32_t status = 0x00000000U,interrupt_enable = 0x00000000U,interrupt_state = 0x00000000U;
|
||||
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
status = EXMC_NPINTEN(exmc_bank);
|
||||
interrupt_state = (status & (interrupt >> INTEN_INTS_OFFSET));
|
||||
|
||||
interrupt_enable = (status & interrupt);
|
||||
|
||||
if ((interrupt_enable) && (interrupt_state)){
|
||||
/* interrupt flag is set */
|
||||
return SET;
|
||||
}else{
|
||||
/* interrupt flag is reset */
|
||||
return RESET;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief clear EXMC interrupt flag
|
||||
\param[in] exmc_bank: specifies the NAND bank , PC card bank
|
||||
only one parameter can be selected which is shown as below:
|
||||
\arg EXMC_BANK1_NAND: the NAND bank1
|
||||
\arg EXMC_BANK2_NAND: the NAND bank2
|
||||
\arg EXMC_BANK3_PCCARD: the PC card bank
|
||||
\param[in] interrupt: EXMC interrupt flag
|
||||
only one parameter can be selected which are shown as below:
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_RISE: rising edge interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_LEVEL: high-level interrupt and corresponding flag
|
||||
\arg EXMC_NAND_PCCARD_INT_FLAG_FALL: falling edge interrupt and corresponding flag
|
||||
\param[out] none
|
||||
\retval none
|
||||
*/
|
||||
void exmc_interrupt_flag_clear(uint32_t exmc_bank,uint32_t interrupt)
|
||||
{
|
||||
/* NAND bank1,bank2 or PC card bank3 */
|
||||
EXMC_NPINTEN(exmc_bank) &= ~(interrupt >> INTEN_INTS_OFFSET);
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*!
|
||||
\file gd32e50x_exmc.h
|
||||
\brief definitions for the EXMC
|
||||
|
||||
\version 2026-02-09, V1.7.0, firmware for GD32E50x
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright (c) 2025, GigaDevice Semiconductor Inc.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef GD32E50X_EXMC_H
|
||||
#define GD32E50X_EXMC_H
|
||||
|
||||
#include "gd32e50x.h"
|
||||
|
||||
/* EXMC definitions */
|
||||
#define EXMC (EXMC_BASE) /*!< EXMC register base address */
|
||||
|
||||
/* registers definitions */
|
||||
/* NOR/PSRAM */
|
||||
#define EXMC_SNCTL0 REG32(EXMC + 0x00U) /*!< EXMC SRAM/NOR flash control register0 */
|
||||
#define EXMC_SNTCFG0 REG32(EXMC + 0x04U) /*!< EXMC SRAM/NOR flash timing configuration register0 */
|
||||
#define EXMC_SNWTCFG0 REG32(EXMC + 0x104U) /*!< EXMC SRAM/NOR flash write timing configuration register0 */
|
||||
|
||||
#define EXMC_SNCTL1 REG32(EXMC + 0x08U) /*!< EXMC SRAM/NOR flash control register1 */
|
||||
#define EXMC_SNTCFG1 REG32(EXMC + 0x0CU) /*!< EXMC SRAM/NOR flash timing configuration register1 */
|
||||
#define EXMC_SNWTCFG1 REG32(EXMC + 0x10CU) /*!< EXMC SRAM/NOR flash write timing configuration register1 */
|
||||
|
||||
#define EXMC_SNCTL2 REG32(EXMC + 0x10U) /*!< EXMC SRAM/NOR flash control register2 */
|
||||
#define EXMC_SNTCFG2 REG32(EXMC + 0x14U) /*!< EXMC SRAM/NOR flash timing configuration register2 */
|
||||
#define EXMC_SNWTCFG2 REG32(EXMC + 0x114U) /*!< EXMC SRAM/NOR flash write timing configuration register2 */
|
||||
|
||||
#define EXMC_SNCTL3 REG32(EXMC + 0x18U) /*!< EXMC SRAM/NOR flash control register3 */
|
||||
#define EXMC_SNTCFG3 REG32(EXMC + 0x1CU) /*!< EXMC SRAM/NOR flash timing configuration register3 */
|
||||
#define EXMC_SNWTCFG3 REG32(EXMC + 0x11CU) /*!< EXMC SRAM/NOR flash write timing configuration register3 */
|
||||
|
||||
/* NAND/PC card */
|
||||
#define EXMC_NPCTL1 REG32(EXMC + 0x60U) /*!< EXMC NAND/PC card control register1 */
|
||||
#define EXMC_NPINTEN1 REG32(EXMC + 0x64U) /*!< EXMC NAND/PC card interrupt enable register1 */
|
||||
#define EXMC_NPCTCFG1 REG32(EXMC + 0x68U) /*!< EXMC NAND/PC card common space timing configuration register1 */
|
||||
#define EXMC_NPATCFG1 REG32(EXMC + 0x6CU) /*!< EXMC NAND/PC card attribute space timing configuration register1 */
|
||||
#define EXMC_NECC1 REG32(EXMC + 0x74U) /*!< EXMC NAND ECC register1 */
|
||||
|
||||
#define EXMC_NPCTL2 REG32(EXMC + 0x80U) /*!< EXMC NAND/PC card control register2 */
|
||||
#define EXMC_NPINTEN2 REG32(EXMC + 0x84U) /*!< EXMC NAND/PC card interrupt enable register2 */
|
||||
#define EXMC_NPCTCFG2 REG32(EXMC + 0x88U) /*!< EXMC NAND/PC card common space timing configuration register2 */
|
||||
#define EXMC_NPATCFG2 REG32(EXMC + 0x8CU) /*!< EXMC NAND/PC card attribute space timing configuration register2 */
|
||||
#define EXMC_NECC2 REG32(EXMC + 0x94U) /*!< EXMC NAND ECC register2 */
|
||||
|
||||
#define EXMC_NPCTL3 REG32(EXMC + 0xA0U) /*!< EXMC NAND/PC card control register3 */
|
||||
#define EXMC_NPINTEN3 REG32(EXMC + 0xA4U) /*!< EXMC NAND/PC card interrupt enable register3 */
|
||||
#define EXMC_NPCTCFG3 REG32(EXMC + 0xA8U) /*!< EXMC NAND/PC card common space timing configuration register3 */
|
||||
#define EXMC_NPATCFG3 REG32(EXMC + 0xACU) /*!< EXMC NAND/PC card attribute space timing configuration register3 */
|
||||
#define EXMC_PIOTCFG3 REG32(EXMC + 0xB0U) /*!< EXMC PC card I/O space timing configuration register */
|
||||
|
||||
/* bits definitions */
|
||||
/* EXMC_SNCTLx,x=0..3 */
|
||||
#define EXMC_SNCTL_NRBKEN BIT(0) /*!< NOR bank enable */
|
||||
#define EXMC_SNCTL_NRMUX BIT(1) /*!< NOR bank memory address/data multiplexing enable */
|
||||
#define EXMC_SNCTL_NRTP BITS(2,3) /*!< NOR bank memory type */
|
||||
#define EXMC_SNCTL_NRW BITS(4,5) /*!< NOR bank memory data bus width */
|
||||
#define EXMC_SNCTL_NREN BIT(6) /*!< NOR flash access enable */
|
||||
#define EXMC_SNCTL_SBRSTEN BIT(8) /*!< synchronous burst enable */
|
||||
#define EXMC_SNCTL_NRWTPOL BIT(9) /*!< NWAIT signal polarity */
|
||||
#define EXMC_SNCTL_WRAPEN BIT(10) /*!< wrapped burst mode enable */
|
||||
#define EXMC_SNCTL_NRWTCFG BIT(11) /*!< NWAIT signal configuration, only work in synchronous mode */
|
||||
#define EXMC_SNCTL_WREN BIT(12) /*!< write enable */
|
||||
#define EXMC_SNCTL_NRWTEN BIT(13) /*!< NWAIT signal enable */
|
||||
#define EXMC_SNCTL_EXMODEN BIT(14) /*!< extended mode enable */
|
||||
#define EXMC_SNCTL_ASYNCWAIT BIT(15) /*!< asynchronous wait enable */
|
||||
#define EXMC_SNCTL_CPS BITS(16,18) /*!< CRAM page size */
|
||||
#define EXMC_SNCTL_SYNCWR BIT(19) /*!< synchronous write config */
|
||||
|
||||
/* EXMC_SNTCFGx,x=0..3 */
|
||||
#define EXMC_SNTCFG_ASET BITS(0,3) /*!< asynchronous address setup time */
|
||||
#define EXMC_SNTCFG_AHLD BITS(4,7) /*!< asynchronous address hold time */
|
||||
#define EXMC_SNTCFG_DSET BITS(8,15) /*!< asynchronous data setup time */
|
||||
#define EXMC_SNTCFG_BUSLAT BITS(16,19) /*!< bus latency */
|
||||
#define EXMC_SNTCFG_CKDIV BITS(20,23) /*!< synchronous clock divide ratio */
|
||||
#define EXMC_SNTCFG_DLAT BITS(24,27) /*!< synchronous data latency for NOR flash */
|
||||
#define EXMC_SNTCFG_ASYNCMOD BITS(28,29) /*!< asynchronous access mode */
|
||||
|
||||
/* EXMC_SNWTCFGx,x=0..3 */
|
||||
#define EXMC_SNWTCFG_WASET BITS(0,3) /*!< asynchronous address setup time */
|
||||
#define EXMC_SNWTCFG_WAHLD BITS(4,7) /*!< asynchronous address hold time */
|
||||
#define EXMC_SNWTCFG_WDSET BITS(8,15) /*!< asynchronous data setup time */
|
||||
#define EXMC_SNWTCFG_WBUSLAT BITS(16,19) /*!< bus latency */
|
||||
#define EXMC_SNWTCFG_WASYNCMOD BITS(28,29) /*!< asynchronous access mode */
|
||||
|
||||
/* EXMC_NPCTLx,x=1..3 */
|
||||
#define EXMC_NPCTL_NDWTEN BIT(1) /*!< wait feature enable */
|
||||
#define EXMC_NPCTL_NDBKEN BIT(2) /*!< NAND bank enable */
|
||||
#define EXMC_NPCTL_NDTP BIT(3) /*!< NAND bank memory type */
|
||||
#define EXMC_NPCTL_NDW BITS(4,5) /*!< NAND bank memory data bus width */
|
||||
#define EXMC_NPCTL_ECCEN BIT(6) /*!< ECC enable */
|
||||
#define EXMC_NPCTL_CTR BITS(9,12) /*!< CLE to RE delay */
|
||||
#define EXMC_NPCTL_ATR BITS(13,16) /*!< ALE to RE delay */
|
||||
#define EXMC_NPCTL_ECCSZ BITS(17,19) /*!< ECC size */
|
||||
|
||||
/* EXMC_NPINTENx,x=1..3 */
|
||||
#define EXMC_NPINTEN_INTRS BIT(0) /*!< interrupt rising edge status */
|
||||
#define EXMC_NPINTEN_INTHS BIT(1) /*!< interrupt high-level status */
|
||||
#define EXMC_NPINTEN_INTFS BIT(2) /*!< interrupt falling edge status */
|
||||
#define EXMC_NPINTEN_INTREN BIT(3) /*!< interrupt rising edge detection enable */
|
||||
#define EXMC_NPINTEN_INTHEN BIT(4) /*!< interrupt high-level detection enable */
|
||||
#define EXMC_NPINTEN_INTFEN BIT(5) /*!< interrupt falling edge detection enable */
|
||||
#define EXMC_NPINTEN_FFEPT BIT(6) /*!< FIFO empty flag */
|
||||
|
||||
/* EXMC_NPCTCFGx,x=1..3 */
|
||||
#define EXMC_NPCTCFG_COMSET BITS(0,7) /*!< common memory setup time */
|
||||
#define EXMC_NPCTCFG_COMWAIT BITS(8,15) /*!< common memory wait time */
|
||||
#define EXMC_NPCTCFG_COMHLD BITS(16,23) /*!< common memory hold time */
|
||||
#define EXMC_NPCTCFG_COMHIZ BITS(24,31) /*!< common memory data bus HiZ time */
|
||||
|
||||
/* EXMC_NPATCFGx,x=1..3 */
|
||||
#define EXMC_NPATCFG_ATTSET BITS(0,7) /*!< attribute memory setup time */
|
||||
#define EXMC_NPATCFG_ATTWAIT BITS(8,15) /*!< attribute memory wait time */
|
||||
#define EXMC_NPATCFG_ATTHLD BITS(16,23) /*!< attribute memory hold time */
|
||||
#define EXMC_NPATCFG_ATTHIZ BITS(24,31) /*!< attribute memory data bus HiZ time */
|
||||
|
||||
/* EXMC_PIOTCFG3 */
|
||||
#define EXMC_PIOTCFG3_IOSET BITS(0,7) /*!< IO space setup time */
|
||||
#define EXMC_PIOTCFG3_IOWAIT BITS(8,15) /*!< IO space wait time */
|
||||
#define EXMC_PIOTCFG3_IOHLD BITS(16,23) /*!< IO space hold time */
|
||||
#define EXMC_PIOTCFG3_IOHIZ BITS(24,31) /*!< IO space data bus HiZ time */
|
||||
|
||||
/* EXMC_NECCx,x=1,2 */
|
||||
#define EXMC_NECC_ECC BITS(0,31) /*!< ECC result */
|
||||
|
||||
/* constants definitions */
|
||||
/* EXMC NOR/SRAM timing initialize struct */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t asyn_access_mode; /*!< asynchronous access mode */
|
||||
uint32_t syn_data_latency; /*!< configure the data latency, synchronous access mode valid */
|
||||
uint32_t syn_clk_division; /*!< configure the clock divide ratio, synchronous access mode valid */
|
||||
uint32_t bus_latency; /*!< configure the bus latency */
|
||||
uint32_t asyn_data_setuptime; /*!< configure the data setup time, asynchronous access mode valid */
|
||||
uint32_t asyn_address_holdtime; /*!< configure the address hold time, asynchronous access mode valid */
|
||||
uint32_t asyn_address_setuptime; /*!< configure the data setup time, asynchronous access mode valid */
|
||||
}exmc_norsram_timing_parameter_struct;
|
||||
|
||||
/* EXMC NOR/SRAM initialize struct */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t norsram_region; /*!< select the region of EXMC NOR/SRAM bank */
|
||||
uint32_t write_mode; /*!< the write mode, synchronous mode or asynchronous mode */
|
||||
uint32_t extended_mode; /*!< enable or disable the extended mode */
|
||||
uint32_t asyn_wait; /*!< enable or disable the asynchronous wait function */
|
||||
uint32_t nwait_signal; /*!< enable or disable the NWAIT signal while in synchronous bust mode */
|
||||
uint32_t memory_write; /*!< enable or disable the write operation */
|
||||
uint32_t nwait_config; /*!< NWAIT signal configuration, only work in synchronous mode */
|
||||
uint32_t wrap_burst_mode; /*!< enable or disable the wrap burst mode */
|
||||
uint32_t nwait_polarity; /*!< specifies the polarity of NWAIT signal from memory */
|
||||
uint32_t burst_mode; /*!< enable or disable the burst mode */
|
||||
uint32_t databus_width; /*!< specifies the databus width of external memory */
|
||||
uint32_t memory_type; /*!< specifies the type of external memory */
|
||||
uint32_t address_data_mux; /*!< specifies whether the data bus and address bus are multiplexed */
|
||||
exmc_norsram_timing_parameter_struct* read_write_timing; /*!< timing parameters for read and write if the extended mode is not used or the timing
|
||||
parameters for read if the extended mode is used */
|
||||
exmc_norsram_timing_parameter_struct* write_timing; /*!< timing parameters for write when the extended mode is used */
|
||||
}exmc_norsram_parameter_struct;
|
||||
|
||||
/* EXMC NAND/PC card timing initialize struct */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t databus_hiztime; /*!< configure the dadtabus HiZ time for write operation */
|
||||
uint32_t holdtime; /*!< configure the address hold time(or the data hold time for write operation) */
|
||||
uint32_t waittime; /*!< configure the minimum wait time */
|
||||
uint32_t setuptime; /*!< configure the address setup time */
|
||||
}exmc_nand_pccard_timing_parameter_struct;
|
||||
|
||||
/* EXMC NAND initialize struct */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t nand_bank; /*!< select the bank of NAND */
|
||||
uint32_t ecc_size; /*!< the page size for the ECC calculation */
|
||||
uint32_t atr_latency; /*!< configure the latency of ALE low to RB low */
|
||||
uint32_t ctr_latency; /*!< configure the latency of CLE low to RB low */
|
||||
uint32_t ecc_logic; /*!< enable or disable the ECC calculation logic */
|
||||
uint32_t databus_width; /*!< the NAND flash databus width */
|
||||
uint32_t wait_feature; /*!< enable or disable the wait feature */
|
||||
exmc_nand_pccard_timing_parameter_struct* common_space_timing; /*!< the timing parameters for NAND flash common space */
|
||||
exmc_nand_pccard_timing_parameter_struct* attribute_space_timing; /*!< the timing parameters for NAND flash attribute space */
|
||||
}exmc_nand_parameter_struct;
|
||||
|
||||
/* EXMC PC card initialize struct */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t atr_latency; /*!< configure the latency of ALE low to RB low */
|
||||
uint32_t ctr_latency; /*!< configure the latency of CLE low to RB low */
|
||||
uint32_t wait_feature; /*!< enable or disable the wait feature */
|
||||
exmc_nand_pccard_timing_parameter_struct* common_space_timing; /*!< the timing parameters for PC card common space */
|
||||
exmc_nand_pccard_timing_parameter_struct* attribute_space_timing; /*!< the timing parameters for PC card attribute space */
|
||||
exmc_nand_pccard_timing_parameter_struct* io_space_timing; /*!< the timing parameters for PC card IO space */
|
||||
}exmc_pccard_parameter_struct;
|
||||
|
||||
/* EXMC_register address */
|
||||
#define EXMC_SNCTL(region) REG32(EXMC + 0x08U * (region)) /*!< EXMC SRAM/NOR flash control registers, region = 0,1,2,3 */
|
||||
#define EXMC_SNTCFG(region) REG32(EXMC + 0x04U + 0x08U * (region)) /*!< EXMC SRAM/NOR flash timing configuration registers, region = 0,1,2,3 */
|
||||
#define EXMC_SNWTCFG(region) REG32(EXMC + 0x104U + 0x08U * (region)) /*!< EXMC SRAM/NOR flash write timing configuration registers, region = 0,1,2,3 */
|
||||
|
||||
#define EXMC_NPCTL(bank) REG32(EXMC + 0x40U + 0x20U * (bank)) /*!< EXMC NAND/PC card control registers, bank = 1,2,3 */
|
||||
#define EXMC_NPINTEN(bank) REG32(EXMC + 0x44U + 0x20U * (bank)) /*!< EXMC NAND/PC card interrupt enable registers, bank = 1,2,3 */
|
||||
#define EXMC_NPCTCFG(bank) REG32(EXMC + 0x48U + 0x20U * (bank)) /*!< EXMC NAND/PC card common space timing configuration registers, bank = 1,2,3 */
|
||||
#define EXMC_NPATCFG(bank) REG32(EXMC + 0x4CU + 0x20U * (bank)) /*!< EXMC NAND/PC card attribute space timing configuration registers, bank = 1,2,3 */
|
||||
#define EXMC_NECC(bank) REG32(EXMC + 0x54U + 0x20U * (bank)) /*!< EXMC NAND ECC registers, bank = 1,2 */
|
||||
|
||||
/* CRAM page size */
|
||||
#define SNCTL_CPS(regval) (BITS(16,18) & ((uint32_t)(regval) << 16))
|
||||
#define EXMC_CRAM_AUTO_SPLIT SNCTL_CPS(0) /*!< automatic burst split on page boundary crossing */
|
||||
#define EXMC_CRAM_PAGE_SIZE_128_BYTES SNCTL_CPS(1) /*!< page size is 128 bytes */
|
||||
#define EXMC_CRAM_PAGE_SIZE_256_BYTES SNCTL_CPS(2) /*!< page size is 256 bytes */
|
||||
#define EXMC_CRAM_PAGE_SIZE_512_BYTES SNCTL_CPS(3) /*!< page size is 512 bytes */
|
||||
#define EXMC_CRAM_PAGE_SIZE_1024_BYTES SNCTL_CPS(4) /*!< page size is 1024 bytes */
|
||||
|
||||
/* NOR bank memory data bus width */
|
||||
#define SNCTL_NRW(regval) (BITS(4,5) & ((uint32_t)(regval) << 4))
|
||||
#define EXMC_NOR_DATABUS_WIDTH_8B SNCTL_NRW(0) /*!< NOR data width is 8 bits */
|
||||
#define EXMC_NOR_DATABUS_WIDTH_16B SNCTL_NRW(1) /*!< NOR data width is 16 bits */
|
||||
|
||||
/* NOR bank memory type */
|
||||
#define SNCTL_NRTP(regval) (BITS(2,3) & ((uint32_t)(regval) << 2))
|
||||
#define EXMC_MEMORY_TYPE_SRAM SNCTL_NRTP(0) /*!< SRAM,ROM */
|
||||
#define EXMC_MEMORY_TYPE_PSRAM SNCTL_NRTP(1) /*!< PSRAM,CRAM */
|
||||
#define EXMC_MEMORY_TYPE_NOR SNCTL_NRTP(2) /*!< NOR flash */
|
||||
|
||||
/* asynchronous access mode */
|
||||
#define SNTCFG_ASYNCMOD(regval) (BITS(28,29) & ((uint32_t)(regval) << 28))
|
||||
#define EXMC_ACCESS_MODE_A SNTCFG_ASYNCMOD(0) /*!< mode A access */
|
||||
#define EXMC_ACCESS_MODE_B SNTCFG_ASYNCMOD(1) /*!< mode B access */
|
||||
#define EXMC_ACCESS_MODE_C SNTCFG_ASYNCMOD(2) /*!< mode C access */
|
||||
#define EXMC_ACCESS_MODE_D SNTCFG_ASYNCMOD(3) /*!< mode D access */
|
||||
|
||||
/* data latency for NOR flash */
|
||||
#define SNTCFG_DLAT(regval) (BITS(24,27) & ((uint32_t)(regval) << 24))
|
||||
#define EXMC_DATALAT_2_CLK SNTCFG_DLAT(0) /*!< data latency of first burst access is 2 EXMC_CLK */
|
||||
#define EXMC_DATALAT_3_CLK SNTCFG_DLAT(1) /*!< data latency of first burst access is 3 EXMC_CLK */
|
||||
#define EXMC_DATALAT_4_CLK SNTCFG_DLAT(2) /*!< data latency of first burst access is 4 EXMC_CLK */
|
||||
#define EXMC_DATALAT_5_CLK SNTCFG_DLAT(3) /*!< data latency of first burst access is 5 EXMC_CLK */
|
||||
#define EXMC_DATALAT_6_CLK SNTCFG_DLAT(4) /*!< data latency of first burst access is 6 EXMC_CLK */
|
||||
#define EXMC_DATALAT_7_CLK SNTCFG_DLAT(5) /*!< data latency of first burst access is 7 EXMC_CLK */
|
||||
#define EXMC_DATALAT_8_CLK SNTCFG_DLAT(6) /*!< data latency of first burst access is 8 EXMC_CLK */
|
||||
#define EXMC_DATALAT_9_CLK SNTCFG_DLAT(7) /*!< data latency of first burst access is 9 EXMC_CLK */
|
||||
#define EXMC_DATALAT_10_CLK SNTCFG_DLAT(8) /*!< data latency of first burst access is 10 EXMC_CLK */
|
||||
#define EXMC_DATALAT_11_CLK SNTCFG_DLAT(9) /*!< data latency of first burst access is 11 EXMC_CLK */
|
||||
#define EXMC_DATALAT_12_CLK SNTCFG_DLAT(10) /*!< data latency of first burst access is 12 EXMC_CLK */
|
||||
#define EXMC_DATALAT_13_CLK SNTCFG_DLAT(11) /*!< data latency of first burst access is 13 EXMC_CLK */
|
||||
#define EXMC_DATALAT_14_CLK SNTCFG_DLAT(12) /*!< data latency of first burst access is 14 EXMC_CLK */
|
||||
#define EXMC_DATALAT_15_CLK SNTCFG_DLAT(13) /*!< data latency of first burst access is 15 EXMC_CLK */
|
||||
#define EXMC_DATALAT_16_CLK SNTCFG_DLAT(14) /*!< data latency of first burst access is 16 EXMC_CLK */
|
||||
#define EXMC_DATALAT_17_CLK SNTCFG_DLAT(15) /*!< data latency of first burst access is 17 EXMC_CLK */
|
||||
|
||||
/* synchronous clock divide ratio */
|
||||
#define SNTCFG_CKDIV(regval) (BITS(20,23) & ((uint32_t)(regval) << 20))
|
||||
#define EXMC_SYN_CLOCK_RATIO_DISABLE SNTCFG_CKDIV(0) /*!< EXMC_CLK disable */
|
||||
#define EXMC_SYN_CLOCK_RATIO_2_CLK SNTCFG_CKDIV(1) /*!< frequency EXMC_CLK = HCLK/2 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_3_CLK SNTCFG_CKDIV(2) /*!< frequency EXMC_CLK = HCLK/3 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_4_CLK SNTCFG_CKDIV(3) /*!< frequency EXMC_CLK = HCLK/4 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_5_CLK SNTCFG_CKDIV(4) /*!< frequency EXMC_CLK = HCLK/5 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_6_CLK SNTCFG_CKDIV(5) /*!< frequency EXMC_CLK = HCLK/6 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_7_CLK SNTCFG_CKDIV(6) /*!< frequency EXMC_CLK = HCLK/7 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_8_CLK SNTCFG_CKDIV(7) /*!< frequency EXMC_CLK = HCLK/8 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_9_CLK SNTCFG_CKDIV(8) /*!< frequency EXMC_CLK = HCLK/9 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_10_CLK SNTCFG_CKDIV(9) /*!< frequency EXMC_CLK = HCLK/10 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_11_CLK SNTCFG_CKDIV(10) /*!< frequency EXMC_CLK = HCLK/11 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_12_CLK SNTCFG_CKDIV(11) /*!< frequency EXMC_CLK = HCLK/12 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_13_CLK SNTCFG_CKDIV(12) /*!< frequency EXMC_CLK = HCLK/13 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_14_CLK SNTCFG_CKDIV(13) /*!< frequency EXMC_CLK = HCLK/14 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_15_CLK SNTCFG_CKDIV(14) /*!< frequency EXMC_CLK = HCLK/15 */
|
||||
#define EXMC_SYN_CLOCK_RATIO_16_CLK SNTCFG_CKDIV(15) /*!< frequency EXMC_CLK = HCLK/16 */
|
||||
|
||||
/* ECC size */
|
||||
#define NPCTL_ECCSZ(regval) (BITS(17,19) & ((uint32_t)(regval) << 17))
|
||||
#define EXMC_ECC_SIZE_256BYTES NPCTL_ECCSZ(0) /* ECC size is 256 bytes */
|
||||
#define EXMC_ECC_SIZE_512BYTES NPCTL_ECCSZ(1) /* ECC size is 512 bytes */
|
||||
#define EXMC_ECC_SIZE_1024BYTES NPCTL_ECCSZ(2) /* ECC size is 1024 bytes */
|
||||
#define EXMC_ECC_SIZE_2048BYTES NPCTL_ECCSZ(3) /* ECC size is 2048 bytes */
|
||||
#define EXMC_ECC_SIZE_4096BYTES NPCTL_ECCSZ(4) /* ECC size is 4096 bytes */
|
||||
#define EXMC_ECC_SIZE_8192BYTES NPCTL_ECCSZ(5) /* ECC size is 8192 bytes */
|
||||
|
||||
/* ALE to RE delay */
|
||||
#define NPCTL_ATR(regval) (BITS(13,16) & ((uint32_t)(regval) << 13))
|
||||
#define EXMC_ALE_RE_DELAY_1_HCLK NPCTL_ATR(0) /* ALE to RE delay = 1*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_2_HCLK NPCTL_ATR(1) /* ALE to RE delay = 2*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_3_HCLK NPCTL_ATR(2) /* ALE to RE delay = 3*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_4_HCLK NPCTL_ATR(3) /* ALE to RE delay = 4*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_5_HCLK NPCTL_ATR(4) /* ALE to RE delay = 5*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_6_HCLK NPCTL_ATR(5) /* ALE to RE delay = 6*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_7_HCLK NPCTL_ATR(6) /* ALE to RE delay = 7*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_8_HCLK NPCTL_ATR(7) /* ALE to RE delay = 8*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_9_HCLK NPCTL_ATR(8) /* ALE to RE delay = 9*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_10_HCLK NPCTL_ATR(9) /* ALE to RE delay = 10*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_11_HCLK NPCTL_ATR(10) /* ALE to RE delay = 11*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_12_HCLK NPCTL_ATR(11) /* ALE to RE delay = 12*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_13_HCLK NPCTL_ATR(12) /* ALE to RE delay = 13*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_14_HCLK NPCTL_ATR(13) /* ALE to RE delay = 14*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_15_HCLK NPCTL_ATR(14) /* ALE to RE delay = 15*HCLK */
|
||||
#define EXMC_ALE_RE_DELAY_16_HCLK NPCTL_ATR(15) /* ALE to RE delay = 16*HCLK */
|
||||
|
||||
/* CLE to RE delay */
|
||||
#define NPCTL_CTR(regval) (BITS(9,12) & ((uint32_t)(regval) << 9))
|
||||
#define EXMC_CLE_RE_DELAY_1_HCLK NPCTL_CTR(0) /* CLE to RE delay = 1*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_2_HCLK NPCTL_CTR(1) /* CLE to RE delay = 2*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_3_HCLK NPCTL_CTR(2) /* CLE to RE delay = 3*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_4_HCLK NPCTL_CTR(3) /* CLE to RE delay = 4*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_5_HCLK NPCTL_CTR(4) /* CLE to RE delay = 5*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_6_HCLK NPCTL_CTR(5) /* CLE to RE delay = 6*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_7_HCLK NPCTL_CTR(6) /* CLE to RE delay = 7*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_8_HCLK NPCTL_CTR(7) /* CLE to RE delay = 8*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_9_HCLK NPCTL_CTR(8) /* CLE to RE delay = 9*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_10_HCLK NPCTL_CTR(9) /* CLE to RE delay = 10*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_11_HCLK NPCTL_CTR(10) /* CLE to RE delay = 11*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_12_HCLK NPCTL_CTR(11) /* CLE to RE delay = 12*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_13_HCLK NPCTL_CTR(12) /* CLE to RE delay = 13*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_14_HCLK NPCTL_CTR(13) /* CLE to RE delay = 14*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_15_HCLK NPCTL_CTR(14) /* CLE to RE delay = 15*HCLK */
|
||||
#define EXMC_CLE_RE_DELAY_16_HCLK NPCTL_CTR(15) /* CLE to RE delay = 16*HCLK */
|
||||
|
||||
/* NAND bank memory data bus width */
|
||||
#define NPCTL_NDW(regval) (BITS(4,5) & ((uint32_t)(regval) << 4))
|
||||
#define EXMC_NAND_DATABUS_WIDTH_8B NPCTL_NDW(0) /*!< NAND data width is 8 bits */
|
||||
#define EXMC_NAND_DATABUS_WIDTH_16B NPCTL_NDW(1) /*!< NAND data width is 16 bits */
|
||||
|
||||
/* EXMC NOR/SRAM bank region definition */
|
||||
#define EXMC_BANK0_NORSRAM_REGION0 ((uint32_t)0x00000000U) /*!< bank0 NOR/SRAM region0 */
|
||||
#define EXMC_BANK0_NORSRAM_REGION1 ((uint32_t)0x00000001U) /*!< bank0 NOR/SRAM region1 */
|
||||
#define EXMC_BANK0_NORSRAM_REGION2 ((uint32_t)0x00000002U) /*!< bank0 NOR/SRAM region2 */
|
||||
#define EXMC_BANK0_NORSRAM_REGION3 ((uint32_t)0x00000003U) /*!< bank0 NOR/SRAM region3 */
|
||||
|
||||
/* EXMC NOR/SRAM write mode */
|
||||
#define EXMC_ASYN_WRITE ((uint32_t)0x00000000U) /*!< asynchronous write mode */
|
||||
#define EXMC_SYN_WRITE EXMC_SNCTL_SYNCWR /*!< synchronous write mode */
|
||||
|
||||
/* EXMC NWAIT signal configuration */
|
||||
#define EXMC_NWAIT_CONFIG_BEFORE ((uint32_t)0x00000000U) /*!< NWAIT signal is active one data cycle before wait state */
|
||||
#define EXMC_NWAIT_CONFIG_DURING EXMC_SNCTL_NRWTCFG /*!< NWAIT signal is active during wait state */
|
||||
|
||||
/* EXMC NWAIT signal polarity configuration */
|
||||
#define EXMC_NWAIT_POLARITY_LOW ((uint32_t)0x00000000U) /*!< low level is active of NWAIT */
|
||||
#define EXMC_NWAIT_POLARITY_HIGH EXMC_SNCTL_NRWTPOL /*!< high level is active of NWAIT */
|
||||
|
||||
/* EXMC NAND/PC card bank definition */
|
||||
#define EXMC_BANK1_NAND ((uint32_t)0x00000001U) /*!< bank1 NAND flash */
|
||||
#define EXMC_BANK2_NAND ((uint32_t)0x00000002U) /*!< bank2 NAND flash */
|
||||
#define EXMC_BANK3_PCCARD ((uint32_t)0x00000003U) /*!< bank3 PC card */
|
||||
|
||||
/* EXMC flag bits */
|
||||
#define EXMC_NAND_PCCARD_FLAG_RISE EXMC_NPINTEN_INTRS /*!< interrupt rising edge status */
|
||||
#define EXMC_NAND_PCCARD_FLAG_LEVEL EXMC_NPINTEN_INTHS /*!< interrupt high-level status */
|
||||
#define EXMC_NAND_PCCARD_FLAG_FALL EXMC_NPINTEN_INTFS /*!< interrupt falling edge status */
|
||||
#define EXMC_NAND_PCCARD_FLAG_FIFOE EXMC_NPINTEN_FFEPT /*!< FIFO empty flag */
|
||||
|
||||
/* EXMC interrupt flag bits */
|
||||
#define EXMC_NAND_PCCARD_INT_FLAG_RISE EXMC_NPINTEN_INTREN /*!< rising edge interrupt and corresponding flag */
|
||||
#define EXMC_NAND_PCCARD_INT_FLAG_LEVEL EXMC_NPINTEN_INTHEN /*!< high-level interrupt and corresponding flag */
|
||||
#define EXMC_NAND_PCCARD_INT_FLAG_FALL EXMC_NPINTEN_INTFEN /*!< falling edge interrupt and corresponding flag */
|
||||
|
||||
/* function declarations */
|
||||
/* NOR/SRAM */
|
||||
/* deinitialize EXMC NOR/SRAM region */
|
||||
void exmc_norsram_deinit(uint32_t exmc_norsram_region);
|
||||
/* initialize exmc_norsram_parameter_struct with the default values */
|
||||
void exmc_norsram_struct_para_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct);
|
||||
/* initialize EXMC NOR/SRAM region */
|
||||
void exmc_norsram_init(exmc_norsram_parameter_struct* exmc_norsram_init_struct);
|
||||
/* enable EXMC NOR/SRAM region */
|
||||
void exmc_norsram_enable(uint32_t exmc_norsram_region);
|
||||
/* disable EXMC NOR/SRAM region */
|
||||
void exmc_norsram_disable(uint32_t exmc_norsram_region);
|
||||
/* configure CRAM page size */
|
||||
void exmc_norsram_page_size_config(uint32_t exmc_norsram_region, uint32_t page_size);
|
||||
|
||||
/* NAND */
|
||||
/* deinitialize EXMC NAND bank */
|
||||
void exmc_nand_deinit(uint32_t exmc_nand_bank);
|
||||
/* initialize exmc_nand_parameter_struct with the default values */
|
||||
void exmc_nand_struct_para_init(exmc_nand_parameter_struct* exmc_nand_init_struct);
|
||||
/* initialize EXMC NAND bank */
|
||||
void exmc_nand_init(exmc_nand_parameter_struct* exmc_nand_init_struct);
|
||||
/* enable EXMC NAND bank */
|
||||
void exmc_nand_enable(uint32_t exmc_nand_bank);
|
||||
/* disable EXMC NAND bank */
|
||||
void exmc_nand_disable(uint32_t exmc_nand_bank);
|
||||
/* enable or disable the EXMC NAND ECC function */
|
||||
void exmc_nand_ecc_config(uint32_t exmc_nand_bank, ControlStatus newvalue);
|
||||
/* get the EXMC ECC value */
|
||||
uint32_t exmc_ecc_get(uint32_t exmc_nand_bank);
|
||||
|
||||
/* PC card */
|
||||
/* deinitialize EXMC PC card bank */
|
||||
void exmc_pccard_deinit(void);
|
||||
/* initialize exmc_pccard_parameter_struct parameter with the default values */
|
||||
void exmc_pccard_struct_para_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct);
|
||||
/* initialize EXMC PC card bank */
|
||||
void exmc_pccard_init(exmc_pccard_parameter_struct* exmc_pccard_init_struct);
|
||||
/* enable EXMC PC card bank */
|
||||
void exmc_pccard_enable(void);
|
||||
/* disable EXMC PC card bank */
|
||||
void exmc_pccard_disable(void);
|
||||
|
||||
/* interrupt & flag functions */
|
||||
/* enable EXMC interrupt */
|
||||
void exmc_interrupt_enable(uint32_t exmc_bank,uint32_t interrupt);
|
||||
/* disable EXMC interrupt */
|
||||
void exmc_interrupt_disable(uint32_t exmc_bank,uint32_t interrupt);
|
||||
/* get EXMC flag status */
|
||||
FlagStatus exmc_flag_get(uint32_t exmc_bank,uint32_t flag);
|
||||
/* clear EXMC flag status */
|
||||
void exmc_flag_clear(uint32_t exmc_bank,uint32_t flag);
|
||||
/* get EXMC interrupt flag */
|
||||
FlagStatus exmc_interrupt_flag_get(uint32_t exmc_bank,uint32_t interrupt);
|
||||
/* clear EXMC interrupt flag */
|
||||
void exmc_interrupt_flag_clear(uint32_t exmc_bank,uint32_t interrupt);
|
||||
|
||||
#endif /* GD32E50X_EXMC_H */
|
||||
@@ -94,7 +94,7 @@
|
||||
// <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
|
||||
// <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
|
||||
#define __STACK_SIZE 0x00000400
|
||||
#define __HEAP_SIZE 0x00000C00
|
||||
#define __HEAP_SIZE 0x00004000
|
||||
// </h>
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
\addtogroup EcatAppl EtherCAT application
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file applInterface.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Definition of the application interface functions
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version - :<br>
|
||||
V5.10.1 : Start file change log
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef _APPL_INTERFACE_H_
|
||||
#define _APPL_INTERFACE_H_
|
||||
|
||||
#include "ecat_def.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esc.h"
|
||||
#include "ecatslv.h"
|
||||
#include "objdef.h"
|
||||
#include "ecatappl.h"
|
||||
|
||||
|
||||
|
||||
#endif /*#ifndef _APPL_INTERFACE_H_*/
|
||||
|
||||
#if defined(_APPL_INTERFACE_) && (_APPL_INTERFACE_ == 1)
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Function pointer which are used to call optional functions from the application
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 if successful or greater 0 in case of an error
|
||||
|
||||
\param wordaddr Word address of the EEPROM data to be read
|
||||
|
||||
\brief This function shall copy EEPROM data to the ESC EEPROM data register (0x508:0x50F/0x50B).
|
||||
\brief The EEPROM data starting at the specified word address and the length specified with "EEPROM_READ_SIZE".
|
||||
\brief The data shall be copied to the ESC EEPROM buffer (ESC offset 0x508)
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO UINT16 (* pAPPL_EEPROM_Read)(UINT32 wordaddr);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 if successful or greater 0 in case of an error
|
||||
|
||||
\param wordaddr Word address of the EEPROM data to be written
|
||||
|
||||
\brief This function shall copy data from the ESC EEPROM data register (0x508:0x50F/0x50B) to the EEPROM memory.
|
||||
\brief The EEPROM data starting at the specified word address and the length specified with "EEPROM_WRITE_SIZE".
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO UINT16(*pAPPL_EEPROM_Write)(UINT32 wordaddr);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 if successful or greater 0 in case of an error
|
||||
|
||||
\brief This function shall copy the EEPROM reload information to the ESC EEPROM data register (0x508:0x50F/0x50B).
|
||||
\brief Read the ESC data sheet for the reload information (e.g. Beckhoff IPCore ESC Datasheet section II, clause 3.45.1)
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO UINT16(*pAPPL_EEPROM_Reload)(void);
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Functions provided by the SSC to be called from the application
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 if successful or greater 0 in case of an error
|
||||
|
||||
\brief Initialize the generic slave stack.
|
||||
\brief This function should be called after the platform including operating system and
|
||||
\brief ESC is ready to use.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO UINT16 MainInit(void);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function handles the low priority function like EtherCAT state machine
|
||||
\brief handling, mailbox protocols and if no synchronization is enabled also the
|
||||
\brief application.
|
||||
\brief This function shall be called cyclically from the application.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO void MainLoop(void);
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param alStatus requested AL Status
|
||||
\param alStatusCode requested AL Status Code
|
||||
|
||||
\brief This function shall be called by the application to trigger state transition in case
|
||||
\brief of an application error or to complete a pending transition.
|
||||
\brief If the function was called due to an error it shall be again if the error is gone.
|
||||
\brief NOTE: state requests to a higher state than the current state are not allowed.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
PROTO void ECAT_StateChange(UINT8 alStatus, UINT16 alStatusCode);
|
||||
|
||||
#undef PROTO
|
||||
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* @file boot_ecat_app.cpp
|
||||
* @brief BOOT 邮箱-only SSC 的应用层:最小对象字典 + EtherCAT 下载会话入口。
|
||||
*
|
||||
* 角色分工(刻意这么切的):
|
||||
* - 硬件/寄存器 → boot_ecat_hw.cpp
|
||||
* - flash 与提交 → sys/OTA.cpp(BOOT 唯一的 flash writer,EtherCAT 不另起写者)
|
||||
* - 本文件 → 对象字典 + APPL 钩子 + 把 SDO 写转成对 OTAHandler 的调用
|
||||
*
|
||||
* 为什么不需要 FoE:comm/ecat 这份 SSC 的 CoE/SDO 服务是完整的(mailbox.c:453 的
|
||||
* case MBX_TYPE_COE 原样可用),而 SOEM 的 ecx_SDOwrite 会自动分段。SSC 把整段
|
||||
* 分段下载先攒进 pSdoSegData(sdoserv.c:214-216 注明是 SDOS_SdoInd 里分配的),
|
||||
* 最后一个分段到达时才调 OBJ_Write,并在 objdef.c:1352 早分发给对象的 Write 回调
|
||||
* —— 所以**一次 SDO 传输 = 回调一次、拿到整块**,从站侧不需要 begin/data/commit
|
||||
* 状态机,也不需要自己拼包。
|
||||
*
|
||||
* 对象:
|
||||
* 0x1000/0x1008/0x1009/0x100A/0x1018 由 coeappl.c 的标准表自动挂上;
|
||||
* BOOT 与 APP 共用同一份 SII(vendor/product 完全相同,本轮刻意不动 SII),
|
||||
* 所以主站**只能靠 0x1008 的 "GD32E508 DCU BOOT" 字符串**辨认这是引导程序。
|
||||
* 0x2500 会话控制(REC of UINT32)
|
||||
* 0x2501 数据通道(ARR of UINT8,BitSize = 4096*8,写回调落 flash)
|
||||
*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
#include "esc.h"
|
||||
#include "el9800appl.h"
|
||||
#include "objdef.h"
|
||||
#include "sdoserv.h"
|
||||
#include "ecatslv.h"
|
||||
#include "dc_app.h"
|
||||
|
||||
#include "sys/OTA.h"
|
||||
#include "sys/config.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/** 全局实例定义在 sys/OTA.cpp:18(main.h 也这么声明)。 */
|
||||
extern OTAHandler ota;
|
||||
|
||||
/*============================================================================
|
||||
* 下载参数
|
||||
*==========================================================================*/
|
||||
|
||||
/** @brief 一次 SDO 传输的镜像块大小。
|
||||
* @note 上限来自 TSDOINFOENTRYDESC.BitSize 是 UINT16 —— 单个条目最大
|
||||
* 65535 bit = 8191 字节。取 4096:实测 DCU_APP 的 .hex 转出来 124,992 B
|
||||
* ⇒ 31 块。每块 = 1 个 initiate(带 240 B,SOEM maxdata = mbx_l-0x10)
|
||||
* + 16 个段(3856 = 15×247 + 151;SSC 侧段上限 = 256-6-3 = 247,取小者)
|
||||
* = **17 个邮箱帧搬 4096 B**,即每帧净落 flash 241 B。
|
||||
* 从站侧只需 4KB 攒包缓冲(sdoserv.c 在最后一帧才调 OBJ_Write)。 */
|
||||
#define ECAT_DL_CHUNK 4096U
|
||||
|
||||
/*============================================================================
|
||||
* 0x2500 会话控制(REC,条目全用 UINT32,偏移才和 SSC 的
|
||||
* "si0 占 16bit + 按 BitLength 紧排" 计算一致;见 APP dc_app.c:72-77 的同款断言)
|
||||
*==========================================================================*/
|
||||
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 bSubIndex; /**< sub0:条目数,SSC 按 UINT16 读 */
|
||||
UINT32 u32Cmd; /**< sub1:写 1=begin 2=commit 3=abort;读回最近一次结果 */
|
||||
UINT32 u32Expected; /**< sub2:镜像总字节数(begin 时写) */
|
||||
UINT32 u32Received; /**< sub3:已落 flash 字节数(只读,进度) */
|
||||
UINT32 u32Phase; /**< sub4:bit0=已 arm,bit1=会话活跃,高 16 位=错误码 */
|
||||
}
|
||||
OBJ_STRUCT_PACKED_END
|
||||
TOBJ2500;
|
||||
|
||||
_Static_assert(offsetof(TOBJ2500, u32Cmd) == 2, "TOBJ2500 未紧排:检查 OBJ_STRUCT_PACKED_START");
|
||||
_Static_assert(sizeof(TOBJ2500) == 2 + 4 * 4, "TOBJ2500 尺寸应为 2+16");
|
||||
|
||||
static TOBJ2500 s_dlCtl = {4, 0U, 0U, 0U, 0U};
|
||||
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asDlCtlEntryDesc[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
/* sub1 cmd / sub2 expected 必须带写权限位:SSC 的 OBJ_Write 在 objdef.c:1325 就做
|
||||
* 通用写权限检查,**只有过了它才会在 1352 分发到本对象的 ObjFoeDlCtlWrite**。
|
||||
* 之前两条都写成 ACCESS_READ,主站声明总长那一步直接吃到
|
||||
* abort=0x06010002 "Attempt to write to a read only object"(实测),回调一次没进过,
|
||||
* 整个 SDO 写半边等于不存在。sub3/sub4 是真只读,保持不动。 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, /* sub1 cmd: R(结果)/ W(触发,见 Write) */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, /* sub2 expected(begin 前声明总长) */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* sub3 received: 只读 */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* sub4 phase: 只读 */
|
||||
};
|
||||
static OBJCONST UCHAR aName0x2500[] = "GD32 self-update control";
|
||||
|
||||
/*============================================================================
|
||||
* 0x2501 数据通道
|
||||
*
|
||||
* 建成 VAR(不是 ARR):VAR 没有 sub0 计数字段,pVarPtr 就是值本身,主站按
|
||||
* `0x2501:00` 普通访问写一整块即可。ARR 的话 ObjDesc 的元素类型会和条目类型
|
||||
* 打架,还要在缓冲前面多塞一个没用的 sub0。
|
||||
*
|
||||
* 两条主站必须守的约束(objdef.c 实测,改协议前先回来看):
|
||||
* - VAR 的 maxSubindex 恒为 0(objdef.c:1278 只对非 VAR 才从 sub0 取),
|
||||
* 所以只能写 `:00`,写 `:1` 直接 ABORTIDX_SUBINDEX_NOT_EXISTING;
|
||||
* - complete access 对 VAR 一律 UNSUPPORTED_ACCESS(objdef.c:1295),
|
||||
* 所以 SOEM 那侧必须传 CA=FALSE —— ecat_native 就是这样调的。
|
||||
* 另外 OBJ_Write 在 objdef.c:1352 就早分发给 Write 回调,前面没有任何通用**长度**
|
||||
* 校验(sdoserv.c:714 注明「由对象自己的函数查」),所以最后一块短收能原样传进来。
|
||||
* ⚠ 但**写权限门在前面**(objdef.c:1325):条目描述符没给写位就到不了回调,且报的是
|
||||
* SSC 通用 abort 码而不是本对象的错误 —— 0x2500 那次就这么静默躺平的,见上面注。
|
||||
*==========================================================================*/
|
||||
|
||||
static UINT8 s_dlChunk[ECAT_DL_CHUNK];
|
||||
|
||||
/** @brief 落 flash 前的对齐缓冲(文件级 static,不能开在栈上:BOOT 栈仅 1KB)。
|
||||
* 回调只在 MainLoop 里跑,无重入。 */
|
||||
static UINT8 s_dlStage[ECAT_DL_CHUNK];
|
||||
|
||||
/** 单条目 BitSize 是 UINT16 ⇒ 8191 字节是天花板,这就是块长取 4096 的原因。 */
|
||||
_Static_assert(ECAT_DL_CHUNK * 8U <= 0xFFFFU, "0x2501 块长超出 BitSize 上限");
|
||||
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asDlChunkEntryDesc[] = {
|
||||
/* 唯一条目 = 整块镜像。写回调在 objdef.c:1352 早分发,默认拷贝路径不会执行。 */
|
||||
{DEFTYPE_OCTETSTRING, (UINT16)(ECAT_DL_CHUNK * 8U), ACCESS_READWRITE},
|
||||
};
|
||||
static OBJCONST UCHAR aName0x2501[] = "GD32 firmware image chunk";
|
||||
|
||||
/*============================================================================
|
||||
* SDO 回调
|
||||
*==========================================================================*/
|
||||
|
||||
/** @brief 0x2501 写入 = 收到一块镜像。
|
||||
*
|
||||
* SSC 已把整段分段下载攒完才调这里,dataSize 就是本块字节数(最后一块可短)。
|
||||
* 落 flash 走 OTAHandler 现成的那条路,本文件不碰 fmc_*。
|
||||
*/
|
||||
static UINT8 ObjFoeDlWrite(UINT16 index, UINT8 subindex, UINT32 dataSize,
|
||||
UINT16 MBXMEM *pData, UINT8 bCompleteAccess)
|
||||
{
|
||||
(void)index;
|
||||
(void)bCompleteAccess;
|
||||
|
||||
if (subindex != 0U) {
|
||||
return ABORTIDX_SUBINDEX_NOT_EXISTING;
|
||||
}
|
||||
if (dataSize == 0U || dataSize > ECAT_DL_CHUNK) {
|
||||
return ABORTIDX_PARAM_LENGTH_TOO_LONG;
|
||||
}
|
||||
|
||||
/* 邮箱载荷按原样字节搬出即可。字节序已从两端源码对过(**还没上板实测**):
|
||||
* - SOEM `ecx_SDOwrite`:`memcpy(&SDOp->ldata[1], hp, framedatasize)`,
|
||||
* 镜像字节原样上线,总长单独放 CompleteSize(不占载荷);
|
||||
* - SSC `SdoDownloadSegmentInd`:攒包时把线节拍重建成长字节序内存字
|
||||
* (sdoserv.c:282/287 `(Data[i-1]>>8)&~MASK | (Data[i]<<8)&MASK`),
|
||||
* 即 `pSdoSegData` 里的字节顺序 == 线上顺序。
|
||||
* ⇒ 主站镜像字节 N 落到 flash 的 offset N。上板第一件事仍是拿现成
|
||||
* /api/sdo 往 0x2501 写 00 01 02 ... 0F 再看回读/串口,不对就只在
|
||||
* 这一处加转换,绝不在 ecatFeed 之后补偿。 */
|
||||
/* 必须拷一份再落 flash:writeApplicationData 按 `*(u32*)&data[i]` 整字写,
|
||||
* 邮箱载荷不保证 4 字节对齐,直接喂进去在 Cortex-M33 上是未对齐 32bit 读。
|
||||
* ⚠ 但不能开成局部数组 —— BOOT 栈只有 1KB(regions_GD32E508VE.h:96),
|
||||
* 4KB 局部必爆栈,所以是文件级 static。回调运行在 MainLoop 里,
|
||||
* 不存在重入。 */
|
||||
memcpy(s_dlStage, (const void *)pData, dataSize);
|
||||
|
||||
if (!ota.ecatFeed(s_dlStage, dataSize)) {
|
||||
/* 会话没 arm / 超界 / 与 SPI OTA 抢写:报 SDO abort,主站侧能看到 */
|
||||
return ABORTIDX_GENERAL_ERROR;
|
||||
}
|
||||
s_dlCtl.u32Received = ota.ecatReceived();
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/** @brief 从 SDO 载荷里取一个 UINT32。
|
||||
*
|
||||
* 和 0x2501 一样按原样字节取,不做手工字序拼装。
|
||||
* ⚠ 字节序与载荷布局是上板第一件要验的事:拿现成 /api/sdo 往 0x2500:02 写
|
||||
* 0x11223344 再读回,不对就在这一处加转换,别在下游各处补偿。
|
||||
*/
|
||||
static inline UINT32 dlLoadU32(const UINT16 MBXMEM *p)
|
||||
{
|
||||
UINT32 v = 0U;
|
||||
memcpy(&v, (const void *)p, sizeof(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
/** @brief 0x2500 写入:sub1 = 命令,sub2 = 镜像总长;sub3/sub4 只读。 */
|
||||
static UINT8 ObjFoeDlCtlWrite(UINT16 index, UINT8 subindex, UINT32 dataSize,
|
||||
UINT16 MBXMEM *pData, UINT8 bCompleteAccess)
|
||||
{
|
||||
(void)index;
|
||||
(void)dataSize;
|
||||
(void)bCompleteAccess;
|
||||
|
||||
if (subindex == 2U) {
|
||||
/* 直接存:不能再"转交 OBJ_Write",那会绕回本回调无限递归。 */
|
||||
s_dlCtl.u32Expected = dlLoadU32(pData);
|
||||
return 0U;
|
||||
}
|
||||
if (subindex != 1U) {
|
||||
/* sub0 是条目数,sub3/sub4 只读 */
|
||||
return ABORTIDX_WRITE_ONLY_ENTRY;
|
||||
}
|
||||
|
||||
const UINT32 cmd = dlLoadU32(pData);
|
||||
|
||||
switch (cmd) {
|
||||
case 1U: {
|
||||
const UINT32 total = s_dlCtl.u32Expected;
|
||||
if (!ota.ecatBegin(total)) {
|
||||
s_dlCtl.u32Phase |= 0x00010000U; /* 高 16 位错误码:拒绝 */
|
||||
return ABORTIDX_GENERAL_ERROR;
|
||||
}
|
||||
s_dlCtl.u32Phase = (s_dlCtl.u32Phase & ~0xFFFF0000U) | 0x0002U; /* 会话活跃 */
|
||||
s_dlCtl.u32Received = 0U;
|
||||
break;
|
||||
}
|
||||
case 2U:
|
||||
if (!ota.ecatCommit()) {
|
||||
s_dlCtl.u32Phase = (s_dlCtl.u32Phase & ~0xFFFF0000U) | 0x00030000U;
|
||||
return ABORTIDX_GENERAL_ERROR;
|
||||
}
|
||||
s_dlCtl.u32Phase = (s_dlCtl.u32Phase & ~0xFFFF0000U) & ~0x0002U;
|
||||
break;
|
||||
case 3U:
|
||||
ota.ecatAbort();
|
||||
s_dlCtl.u32Phase &= ~0x0002U;
|
||||
s_dlCtl.u32Received = 0U;
|
||||
break;
|
||||
default:
|
||||
return ABORTIDX_GENERAL_ERROR;
|
||||
}
|
||||
s_dlCtl.u32Cmd = cmd;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/** @brief 把 UINT32 原样字节写回 SDO 载荷(与 dlLoadU32 对称)。 */
|
||||
static inline void dlStoreU32(UINT16 MBXMEM *p, UINT32 v)
|
||||
{
|
||||
memcpy((void *)p, &v, sizeof(v));
|
||||
}
|
||||
|
||||
/** @brief 0x2500 读取:把实时状态并进去,主站轮询进度不必区分对象。
|
||||
*
|
||||
* 自己搬字节,不调 objdef.c 的 OBJ_Read —— 那个头的 PROTO 声明没有 extern "C"
|
||||
* 包裹(这套 SSC 里只有 el9800hw.h 有),从 C++ 调会拿到修饰名变未定义符号。
|
||||
* 0x2500 是我们自己的定长 record,逐 subindex 取值比重造 SSC 的偏移计算更直白。
|
||||
*/
|
||||
static UINT8 ObjFoeDlCtlRead(UINT16 index, UINT8 subindex, UINT32 objSize,
|
||||
UINT16 MBXMEM *pData, UINT8 bCompleteAccess)
|
||||
{
|
||||
(void)index;
|
||||
(void)objSize;
|
||||
(void)bCompleteAccess;
|
||||
|
||||
s_dlCtl.u32Received = ota.ecatReceived();
|
||||
s_dlCtl.u32Phase = (s_dlCtl.u32Phase & 0xFFFF0000U)
|
||||
| (ota.ecatArmed() ? 0x0001U : 0x0000U)
|
||||
| (ota.ecatSessionActive() ? 0x0002U : 0x0000U);
|
||||
|
||||
UINT32 v;
|
||||
switch (subindex) {
|
||||
case 0U: v = s_dlCtl.bSubIndex; break;
|
||||
case 1U: v = s_dlCtl.u32Cmd; break;
|
||||
case 2U: v = s_dlCtl.u32Expected; break;
|
||||
case 3U: v = s_dlCtl.u32Received; break;
|
||||
case 4U: v = s_dlCtl.u32Phase; break;
|
||||
default: return ABORTIDX_SUBINDEX_NOT_EXISTING;
|
||||
}
|
||||
|
||||
if (subindex == 0U) {
|
||||
/* sub0 是 UINT8(条目数),只占载荷一个字节 */
|
||||
*(UINT8 *)pData = (UINT8)v;
|
||||
} else {
|
||||
dlStoreU32(pData, v);
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/*============================================================================
|
||||
* 对象字典
|
||||
*==========================================================================*/
|
||||
|
||||
TOBJECT OBJMEM ApplicationObjDicList[] = {
|
||||
{NULL, NULL, 0x2500, {DEFTYPE_RECORD, 4 | (OBJCODE_REC << 8)},
|
||||
asDlCtlEntryDesc, aName0x2500, &s_dlCtl,
|
||||
ObjFoeDlCtlRead, ObjFoeDlCtlWrite, 0x0000},
|
||||
|
||||
{NULL, NULL, 0x2501, {DEFTYPE_OCTETSTRING, 0 | (OBJCODE_VAR << 8)},
|
||||
asDlChunkEntryDesc, aName0x2501, &s_dlChunk,
|
||||
NULL, ObjFoeDlWrite, 0x0000},
|
||||
|
||||
/* 终止条目 */
|
||||
{NULL, NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000}
|
||||
};
|
||||
|
||||
TOBJECT OBJMEM * VARMEM ApplicationObjDic = ApplicationObjDicList;
|
||||
|
||||
/*============================================================================
|
||||
* APP 埋点/PDO 计划符号的 BOOT 本地实现
|
||||
*
|
||||
* comm/ecat/ecatappl.c 与 APP 那份逐字节相同,因此仍引用 1.6.0~1.6.25 期间加的
|
||||
* EcatDbg_Dwt / EcatPdo_OutputProbe / EcatPdo_SparseEscPlan / EcatPdo_EscWritePlan
|
||||
* (实体在 APP 的 comm/ethercat_sync.cpp、comm/ethercat_pdo.cpp 里)。
|
||||
* 这里补齐而**不改 ecatappl.c**,以保持两份 SSC 可 diff。
|
||||
* BOOT 无过程数据,取中性实现:分段计划恒 0(⇒ 整包写)、输出探针恒 1(⇒ 全量读)。
|
||||
*==========================================================================*/
|
||||
|
||||
extern "C" UINT32 EcatDbg_Dwt(void) { return DWT->CYCCNT; }
|
||||
|
||||
extern "C" UINT8 EcatPdo_OutputProbe(UINT8 const *head) { (void)head; return 1U; }
|
||||
|
||||
extern "C" UINT8 EcatPdo_SparseEscPlan(UINT16 *o0, UINT16 *l0, UINT16 *o1, UINT16 *l1)
|
||||
{
|
||||
(void)o0; (void)l0; (void)o1; (void)l1;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
extern "C" UINT8 EcatPdo_EscWritePlan(UINT16 *offs, UINT16 *lens, UINT8 maxSegs)
|
||||
{
|
||||
(void)offs; (void)lens; (void)maxSegs;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/*============================================================================
|
||||
* APPL_* —— ecatslv.c / ecatappl.c 调用的 11 个钩子
|
||||
*
|
||||
* BOOT 无过程数据:Input/Output handler 一律拒绝,SAFEOP 进不去,
|
||||
* 从站稳定停在 PREOP —— 正是 SDO 下载需要的状态,也让 0x0420 过程数据
|
||||
* 看门狗天然不适用(秒级上传不会被踢出 OP)。
|
||||
*==========================================================================*/
|
||||
|
||||
UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize)
|
||||
{
|
||||
*pInputSize = 0U;
|
||||
*pOutputSize = 0U;
|
||||
return 0U;
|
||||
}
|
||||
|
||||
UINT16 APPL_StartMailboxHandler(void) { return 0U; }
|
||||
UINT16 APPL_StopMailboxHandler(void) { return 0U; }
|
||||
|
||||
UINT16 APPL_StartInputHandler(UINT16 *pIntMask)
|
||||
{
|
||||
(void)pIntMask;
|
||||
return ALSTATUSCODE_INVALIDALCONTROL; /* 非 0 = 拒绝该迁移 */
|
||||
}
|
||||
|
||||
UINT16 APPL_StopInputHandler(void) { return 0U; }
|
||||
|
||||
UINT16 APPL_StartOutputHandler(void) { return ALSTATUSCODE_INVALIDALCONTROL; }
|
||||
UINT16 APPL_StopOutputHandler(void) { return 0U; }
|
||||
|
||||
void APPL_AckErrorInd(UINT16 stateTrans) { (void)stateTrans; }
|
||||
void APPL_InputMapping(UINT16 *pInputData) { (void)pInputData; }
|
||||
void APPL_OutputMapping(UINT16 *pOutputData) { (void)pOutputData; }
|
||||
void APPL_Application(void) { }
|
||||
|
||||
/*============================================================================
|
||||
* EcatTask
|
||||
*==========================================================================*/
|
||||
|
||||
extern "C" UINT16 MainInit(void);
|
||||
extern "C" void MainLoop(void);
|
||||
extern "C" bool bootEcatBringup(void);
|
||||
|
||||
static bool s_started = false;
|
||||
static bool s_hwOk = false;
|
||||
|
||||
namespace {
|
||||
/** @brief 与 main.cpp 的 1ms 主循环节拍对齐的时基判定(ECAT_TIMER_INT=0)。 */
|
||||
constexpr uint32_t kTickCycles = ECAT_TIMER_INC_P_MS; /* 180000 @180MHz = 1ms */
|
||||
uint32_t s_lastCheck = 0U;
|
||||
}
|
||||
|
||||
/** @brief 上电调一次。硬件不通就整个 EtherCAT 栈不启,SPI OTA 照旧。 */
|
||||
extern "C" void ecatTaskStart(void)
|
||||
{
|
||||
if (s_started) {
|
||||
return;
|
||||
}
|
||||
s_started = true;
|
||||
|
||||
s_hwOk = bootEcatBringup();
|
||||
if (!s_hwOk) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* HW_Init() 必须显式调:MainInit() 不调它(见 ecatappl.c:571 注释),
|
||||
* APP 侧就是因为零调用者才让 s_alEventMask 影子停在 0xFFFF。 */
|
||||
(void)HW_Init();
|
||||
(void)MainInit();
|
||||
s_lastCheck = DWT->CYCCNT;
|
||||
}
|
||||
|
||||
/** @brief 主循环每圈调。邮箱服务、AL 状态机、SDO 分段都在这条路上推进。 */
|
||||
extern "C" void ecatTaskPoll(void)
|
||||
{
|
||||
if (!s_started || !s_hwOk) {
|
||||
return;
|
||||
}
|
||||
MainLoop();
|
||||
|
||||
/* ECAT_TIMER_INT=0:SSC 的超时计数靠 MainLoop 内部自己按 HW_GetTimerValue()
|
||||
* 判 1ms,这里不需要额外节拍。保留函数以便 Phase 2 挂进度上报。 */
|
||||
(void)kTickCycles;
|
||||
(void)s_lastCheck;
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
/**
|
||||
* @file boot_ecat_hw.cpp
|
||||
* @brief BOOT 侧 LAN9252 / EXMC 硬件移植层(SSC 的 HW_* 实现)。
|
||||
*
|
||||
* 提炼自 DCU_APP 的 comm/esc_exmc_bus.cpp + comm/esc_lan9252.cpp +
|
||||
* comm/ssc/port/hw_gd32exmc.cpp,只留 BOOT 邮箱-only 从站需要的部分。
|
||||
* 剔除:过程数据看门狗(PREOP 无 PD,0x0420 不适用)、电源管理 D1-D3、
|
||||
* bit-bang 通断诊断、SPI 0xA2 诊断口、SYNC0/SYNC1/TIMER0。
|
||||
*
|
||||
* 与 APP 刻意为之的两处差异:
|
||||
* 1) 不加 `.ramfunc`。APP 那批属性在 cproject/链接脚本里没有任何放置规则,
|
||||
* 实际靠 --diag_suppress=L6314W 兜着,是否真在 RAM 里未证实;BOOT 无周期约束。
|
||||
* 2) HW_Init() 真的被调用。APP 侧它零调用者(MainInit 注释要求应用层自己调),
|
||||
* 导致 s_alEventMask 影子停在 0xFFFF、s_timerBase 停在 0。
|
||||
*
|
||||
* 引脚常量与 DCU_APP/sys/config.h:388-441 同源,改板子必须同时改两处。
|
||||
* BOOT 自身占用 USART0 PA9/PA10、CAN0 部分重映射 PB12/PB13、SPI0 重映射
|
||||
* PB3/4/5/6+PA15、SWD PA13/14、PC9 —— 与 EXMC 的 PD/PE 全集、PB1、PB10 无冲突。
|
||||
*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
#include "esc.h"
|
||||
#include "el9800hw.h"
|
||||
#include "ecatslv.h"
|
||||
|
||||
#include "gd32e50x.h"
|
||||
#include "sys/systick.h"
|
||||
/* DEBUG_PRINT 只在 sys/config.h 里定义。漏了这一行时 `#if DEBUG_PRINT` 按 0 处理,
|
||||
* 下面所有 BOOT_ECAT_LOG 会被静默编译掉 —— 产物里连 "RST#"/"PDI ok" 这些串都搜不到,
|
||||
* 表现为「起板一句日志都没有」。改完请拿 `grep -ac 'PDI ok' *.bin` 复核,别只看编译通过。 */
|
||||
#include "sys/config.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gd32e50x_exmc.h"
|
||||
#include "gd32e50x_gpio.h"
|
||||
#include "gd32e50x_rcu.h"
|
||||
#include "gd32e50x_exti.h"
|
||||
#include "gd32e50x_misc.h"
|
||||
}
|
||||
|
||||
/* ecatappl.h 的 PROTO 声明没有 extern "C" 包裹(只有 el9800hw.h 有),
|
||||
* 从 C++ include 会拿到 C++ 修饰名,而 ecatslv/ecatappl 是按 C 编译的。
|
||||
* 这里只用 PDI_Isr,直接自己声明。 */
|
||||
extern "C" void PDI_Isr(void);
|
||||
|
||||
#if DEBUG_PRINT
|
||||
#include <cstdio>
|
||||
#define BOOT_ECAT_LOG(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define BOOT_ECAT_LOG(...)
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t ESC_EXMC_BASE_ADDR = 0x60000000U;
|
||||
/* 16-bit 模式 HADDR[25:1]→EXMC_A[24:0]:LAN9252 A1(PD11=A16)=bit17、A2(PD12=A17)=bit18 */
|
||||
constexpr uint32_t HBI_IDX0_OFFSET = 0x00000U; /* A2=0,A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_LOW_OFF = 0x40000U; /* A2=1,A1=0 */
|
||||
constexpr uint32_t HBI_DATA0_HIGH_OFF = 0x60000U; /* A2=1,A1=1 */
|
||||
|
||||
#define BOOT_ESC_REG16(offset) (*(volatile uint16_t *)(ESC_EXMC_BASE_ADDR + (offset)))
|
||||
|
||||
/* LAN9252 系统 CSR(HBI 直接索引可达,不走 EtherCAT 核心端口) */
|
||||
constexpr uint16_t LAN9252_REG_BYTE_TEST = 0x0064U;
|
||||
constexpr uint32_t LAN9252_BYTE_TEST_VALUE = 0x87654321U;
|
||||
constexpr uint16_t LAN9252_REG_IRQ_CFG = 0x0054U;
|
||||
constexpr uint16_t LAN9252_REG_INT_STS = 0x0058U;
|
||||
constexpr uint16_t LAN9252_REG_INT_EN = 0x005CU;
|
||||
constexpr uint32_t IRQ_CFG_IRQ_EN = 1UL << 8;
|
||||
constexpr uint32_t IRQ_CFG_IRQ_TYPE = 1UL << 0; /* 1=推挽 */
|
||||
constexpr uint32_t INT_EN_ECAT_INT = 1UL << 0;
|
||||
|
||||
/* EtherCAT 核心寄存器 */
|
||||
constexpr uint16_t ESC_REG_DL_STATUS = 0x0110U; /**< bit0 = PDI operational */
|
||||
constexpr uint16_t ESC_REG_EEP_CSR = 0x0502U;
|
||||
constexpr uint16_t ECAT_PRAM_RD_DATA = 0x0000U;
|
||||
constexpr uint16_t ECAT_PRAM_WR_DATA = 0x0020U;
|
||||
constexpr uint16_t ECAT_CSR_DATA_REG = 0x0300U;
|
||||
constexpr uint16_t ECAT_CSR_CMD_REG = 0x0304U;
|
||||
constexpr uint16_t ECAT_PRAM_RD_ADDR_LEN = 0x0308U;
|
||||
constexpr uint16_t ECAT_PRAM_RD_CMD = 0x030CU;
|
||||
constexpr uint16_t ECAT_PRAM_WR_ADDR_LEN = 0x0310U;
|
||||
constexpr uint16_t ECAT_PRAM_WR_CMD = 0x0314U;
|
||||
constexpr uint32_t CSR_BUSY = 1UL << 31;
|
||||
constexpr uint32_t CSR_RNW = 1UL << 30;
|
||||
constexpr uint32_t PRAM_BUSY = 1UL << 31;
|
||||
constexpr uint32_t PRAM_AVAIL = 1UL << 0;
|
||||
constexpr uint32_t POLL_MAX = 2000UL;
|
||||
constexpr uint16_t ESC_PRAM_BASE = 0x1000U;
|
||||
|
||||
/* RST# / 探活:与 APP config.h:407-411 同值 */
|
||||
constexpr uint32_t ESC_NRST_PORT = GPIOB;
|
||||
constexpr uint32_t ESC_NRST_PIN = GPIO_PIN_10;
|
||||
constexpr uint32_t ESC_NRST_ASSERT_MS = 30U; /* 覆盖 tpurstd≥25ms 与 trstia≥200µs */
|
||||
constexpr uint32_t ESC_HBI_POLL_MS = 300U;
|
||||
constexpr uint32_t ESC_HBI_POLL_STEP_MS = 5U;
|
||||
|
||||
/* ESC IRQ:PB1 → EXTI1 下降沿(LAN9252 侧配推挽低有效) */
|
||||
constexpr uint32_t ESC_IRQ_PORT = GPIOB;
|
||||
constexpr uint32_t ESC_IRQ_PIN = GPIO_PIN_1;
|
||||
constexpr exti_line_enum ESC_IRQ_EXTI = EXTI_1;
|
||||
constexpr IRQn_Type ESC_IRQ_IRQn = EXTI1_IRQn;
|
||||
constexpr uint32_t ESC_IRQ_SRC_PORT = GPIO_PORT_SOURCE_GPIOB;
|
||||
constexpr uint32_t ESC_IRQ_SRC_PIN = GPIO_PIN_SOURCE_1;
|
||||
|
||||
struct PinSpec { uint32_t port; uint32_t pin; };
|
||||
constexpr PinSpec kExmcDataPins[16] = {
|
||||
{GPIOD, GPIO_PIN_14}, {GPIOD, GPIO_PIN_15},
|
||||
{GPIOD, GPIO_PIN_0}, {GPIOD, GPIO_PIN_1},
|
||||
{GPIOE, GPIO_PIN_7}, {GPIOE, GPIO_PIN_8},
|
||||
{GPIOE, GPIO_PIN_9}, {GPIOE, GPIO_PIN_10},
|
||||
{GPIOE, GPIO_PIN_11}, {GPIOE, GPIO_PIN_12},
|
||||
{GPIOE, GPIO_PIN_13}, {GPIOE, GPIO_PIN_14},
|
||||
{GPIOE, GPIO_PIN_15}, {GPIOD, GPIO_PIN_8},
|
||||
{GPIOD, GPIO_PIN_9}, {GPIOD, GPIO_PIN_10},
|
||||
};
|
||||
constexpr PinSpec kExmcCtrlPins[5] = {
|
||||
{GPIOD, GPIO_PIN_11}, /* A16 → A1(Pin25) */
|
||||
{GPIOD, GPIO_PIN_12}, /* A17 → A2(Pin29) */
|
||||
{GPIOD, GPIO_PIN_4}, /* NOE */
|
||||
{GPIOD, GPIO_PIN_5}, /* NWE */
|
||||
{GPIOD, GPIO_PIN_7}, /* NE0 */
|
||||
};
|
||||
|
||||
inline void setHbiIndex(uint16_t a) { BOOT_ESC_REG16(HBI_IDX0_OFFSET) = a; }
|
||||
inline uint32_t readReg32(uint16_t a)
|
||||
{
|
||||
setHbiIndex(a);
|
||||
return ((uint32_t)BOOT_ESC_REG16(HBI_DATA0_HIGH_OFF) << 16)
|
||||
| BOOT_ESC_REG16(HBI_DATA0_LOW_OFF);
|
||||
}
|
||||
inline void writeReg32(uint16_t a, uint32_t v)
|
||||
{
|
||||
setHbiIndex(a);
|
||||
BOOT_ESC_REG16(HBI_DATA0_LOW_OFF) = (uint16_t)(v & 0xFFFFU);
|
||||
BOOT_ESC_REG16(HBI_DATA0_HIGH_OFF) = (uint16_t)(v >> 16);
|
||||
}
|
||||
|
||||
/** @brief HBI 事务互斥:屏蔽 ESC IRQ。
|
||||
*
|
||||
* HBI Indexed 是两步事务(写 IDX 再读写 DATA),主循环整段 escRead 若被 PDI_Isr
|
||||
* 插入一次 HBI 事务,索引被改写就会错址。返回先前使能态,不破坏外层
|
||||
* (如 SSC DISABLE_ESC_INT)已有的屏蔽。 */
|
||||
inline uint32_t hbiEnter()
|
||||
{
|
||||
uint32_t prev = 0U;
|
||||
if (NVIC_GetEnableIRQ(ESC_IRQ_IRQn) != 0U) {
|
||||
prev |= 1U;
|
||||
NVIC_DisableIRQ(ESC_IRQ_IRQn);
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
inline void hbiExit(uint32_t prev)
|
||||
{
|
||||
if ((prev & 1U) != 0U) {
|
||||
NVIC_EnableIRQ(ESC_IRQ_IRQn);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief 读 ESC 核心寄存器。
|
||||
* @param verify CSR_DATA 是锁存寄存器,命令不变时连读两次必一致,不一致即本拍
|
||||
* EXMC 毛刺(1.5.59)——这类错读曾让 GetSyncMan 把邮箱几何缓存成
|
||||
* 0x1204/0x1208 触发 SSC 邮箱楔死。邮箱/SM/AL 必须双读,init 轮询可单读。 */
|
||||
uint32_t csrRead(uint16_t addr, uint8_t size, bool verify = true)
|
||||
{
|
||||
for (uint8_t attempt = 0U; attempt < (verify ? 2U : 1U); ++attempt) {
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | CSR_RNW | ((uint32_t)size << 16) | addr);
|
||||
n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
const uint32_t d1 = readReg32(ECAT_CSR_DATA_REG);
|
||||
if (!verify) {
|
||||
return d1;
|
||||
}
|
||||
if (d1 == readReg32(ECAT_CSR_DATA_REG)) {
|
||||
return d1;
|
||||
}
|
||||
}
|
||||
return readReg32(ECAT_CSR_DATA_REG);
|
||||
}
|
||||
|
||||
void csrWrite(uint16_t addr, uint32_t data, uint8_t size)
|
||||
{
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
writeReg32(ECAT_CSR_DATA_REG, data);
|
||||
writeReg32(ECAT_CSR_CMD_REG, CSR_BUSY | ((uint32_t)size << 16) | addr);
|
||||
n = POLL_MAX;
|
||||
while ((readReg32(ECAT_CSR_CMD_REG) & CSR_BUSY) != 0U && --n != 0U) {}
|
||||
}
|
||||
|
||||
/** @brief PRAM 读 FIFO(邮箱 SM0/SM1 在 0x1000/0x1200,全走这里)。
|
||||
* @note 逐 DWORD、弹前必查 AVAIL。1.6.0 曾改用 AVAIL_CNT 批量弹,实测 12B 短读
|
||||
* 首 DWORD 新鲜、后续取到 FIFO 陈旧项导致窗口基址冻结,1.6.2 已回退;
|
||||
* 邮箱小 PDU 同类风险,弄清该计数器对短读的精确语义前不要恢复批量。 */
|
||||
void pramRead(uint16_t addr, uint8_t *buf, uint16_t len)
|
||||
{
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
writeReg32(ECAT_PRAM_RD_ADDR_LEN, ((uint32_t)len << 16) | addr);
|
||||
writeReg32(ECAT_PRAM_RD_CMD, PRAM_BUSY);
|
||||
uint16_t done = 0U;
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_RD_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break; /* 芯片异常:交出残缺数据,由上层 CRC/seq 兜 */
|
||||
}
|
||||
const uint32_t dw = readReg32((uint16_t)(ECAT_PRAM_RD_DATA + alias));
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)(addr & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
buf[done + i] = (uint8_t)(dw >> (8U * (lead + i)));
|
||||
}
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
}
|
||||
|
||||
void pramWrite(uint16_t addr, const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
if (len == 0U) {
|
||||
return;
|
||||
}
|
||||
writeReg32(ECAT_PRAM_WR_ADDR_LEN, ((uint32_t)len << 16) | addr);
|
||||
writeReg32(ECAT_PRAM_WR_CMD, PRAM_BUSY);
|
||||
uint16_t done = 0U;
|
||||
uint16_t alias = 0U;
|
||||
while (done < len) {
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_AVAIL) == 0U && --n != 0U) {}
|
||||
if (n == 0U) {
|
||||
break;
|
||||
}
|
||||
const uint8_t lead = (done == 0U) ? (uint8_t)((uint16_t)(addr + done) & 3U) : 0U;
|
||||
const uint8_t take = (uint8_t)((len - done) < (4U - lead) ? (len - done) : (4U - lead));
|
||||
uint32_t dw = 0U;
|
||||
for (uint8_t i = 0U; i < take; ++i) {
|
||||
dw |= (uint32_t)buf[done + i] << (8U * (lead + i));
|
||||
}
|
||||
writeReg32((uint16_t)(ECAT_PRAM_WR_DATA + alias), dw);
|
||||
alias = (uint16_t)((alias + 4U) & 0x1FU);
|
||||
done = (uint16_t)(done + take);
|
||||
}
|
||||
uint32_t n = POLL_MAX;
|
||||
while ((readReg32(ECAT_PRAM_WR_CMD) & PRAM_BUSY) != 0U && --n != 0U) {}
|
||||
}
|
||||
|
||||
/** @brief 按地址路由:<0x1000 走 CSR 间接端口,≥0x1000 走 PRAM FIFO。
|
||||
* CSR 端口 size=1 任意地址、size=2 需偶地址(DS 表 12-14)。 */
|
||||
void escRead(MEM_ADDR *pData, uint16_t address, uint16_t len)
|
||||
{
|
||||
uint8_t *dst = (uint8_t *)pData;
|
||||
if (address >= ESC_PRAM_BASE) {
|
||||
pramRead(address, dst, len);
|
||||
return;
|
||||
}
|
||||
uint16_t addr = address;
|
||||
uint16_t rem = len;
|
||||
if ((addr & 1U) != 0U && rem > 0U) {
|
||||
*dst++ = (uint8_t)csrRead(addr, 1U);
|
||||
addr++; rem--;
|
||||
}
|
||||
while (rem >= 2U) {
|
||||
const uint16_t w = (uint16_t)csrRead(addr, 2U);
|
||||
dst[0] = (uint8_t)(w & 0xFFU);
|
||||
dst[1] = (uint8_t)(w >> 8);
|
||||
dst += 2; addr += 2; rem -= 2;
|
||||
}
|
||||
if (rem > 0U) {
|
||||
*dst = (uint8_t)csrRead(addr, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
pramWrite(address, src, len);
|
||||
return;
|
||||
}
|
||||
uint16_t addr = address;
|
||||
uint16_t rem = len;
|
||||
if ((addr & 1U) != 0U && rem > 0U) {
|
||||
csrWrite(addr, *src, 1U);
|
||||
src++; addr++; rem--;
|
||||
}
|
||||
while (rem >= 2U) {
|
||||
csrWrite(addr, (uint16_t)src[0] | ((uint16_t)src[1] << 8), 2U);
|
||||
src += 2; addr += 2; rem -= 2;
|
||||
}
|
||||
if (rem > 0U) {
|
||||
csrWrite(addr, *src, 1U);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief RST# 受控复位。GD32 自身复位期间 PB10 回浮空,由 R44 上拉维持高,不会误复位。 */
|
||||
void escHardwareReset()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
gpio_init(ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ESC_NRST_PIN);
|
||||
gpio_bit_reset(ESC_NRST_PORT, ESC_NRST_PIN);
|
||||
delay_1ms(ESC_NRST_ASSERT_MS);
|
||||
gpio_bit_set(ESC_NRST_PORT, ESC_NRST_PIN);
|
||||
/* 驱动高却读回低 ⇒ 该网络被短路到地或引脚损伤,后面所有 HBI 读都会是 0;
|
||||
* 先在这里报出来,别让上层误判成「EEPROM 坏了」。 */
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] RST# %ums, 释放回读=%d\r\n", (unsigned)ESC_NRST_ASSERT_MS,
|
||||
gpio_input_bit_get(ESC_NRST_PORT, ESC_NRST_PIN) == SET ? 1 : 0);
|
||||
}
|
||||
|
||||
/** @brief EXMC Region0。时序常数照抄 APP 1.5.57 实测结论,不要「优化」:
|
||||
* trdrd/twrwr/twrrd≥13ns 由 (1 固有 + BUSLAT) HCLK 承担,BUSLAT=1 只有 11.1ns,
|
||||
* 常温勉强、温漂即偶发读回 0xFFFF;BUSLAT=4→27.8ns 余量 2.1 倍。
|
||||
* LQFP100 只有 NE0(PD7) 一个片选,归 Region0。 */
|
||||
bool exmcInit()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_GPIOD);
|
||||
rcu_periph_clock_enable(RCU_GPIOE);
|
||||
rcu_periph_clock_enable(RCU_AF);
|
||||
rcu_periph_clock_enable(RCU_EXMC);
|
||||
|
||||
for (const PinSpec &p : kExmcDataPins) {
|
||||
gpio_init(p.port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, p.pin);
|
||||
}
|
||||
for (const PinSpec &p : kExmcCtrlPins) {
|
||||
gpio_init(p.port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, p.pin);
|
||||
}
|
||||
|
||||
exmc_norsram_parameter_struct ns;
|
||||
exmc_norsram_timing_parameter_struct rt;
|
||||
exmc_norsram_timing_parameter_struct wt;
|
||||
/* 顺序不能反:exmc_norsram_struct_para_init() 不是给这两个指针赋默认值,而是
|
||||
* **拿着这两个指针往里写 14 个默认字段**(gd32e50x_exmc.c:134-146)。先调后赋值
|
||||
* = 对栈上残留值做 14 次写,命不中合法地址就是 HardFault,而 BOOT 的 fault 入口是
|
||||
* 启动文件里的 B . 且没看门狗 —— 实测表现就是日志停在 RST# 之后一行都没有。
|
||||
* APP 侧 esc_exmc_bus.cpp:181-183 就是这个顺序,照抄。 */
|
||||
ns.read_write_timing = &rt;
|
||||
ns.write_timing = &wt;
|
||||
exmc_norsram_struct_para_init(&ns);
|
||||
ns.norsram_region = EXMC_BANK0_NORSRAM_REGION0;
|
||||
ns.write_mode = EXMC_ASYN_WRITE;
|
||||
ns.extended_mode = DISABLE;
|
||||
ns.asyn_wait = DISABLE;
|
||||
ns.nwait_signal = DISABLE;
|
||||
ns.memory_write = ENABLE;
|
||||
ns.nwait_config = EXMC_NWAIT_CONFIG_BEFORE;
|
||||
ns.wrap_burst_mode = DISABLE;
|
||||
ns.nwait_polarity = EXMC_NWAIT_POLARITY_LOW;
|
||||
ns.burst_mode = DISABLE;
|
||||
ns.databus_width = EXMC_NOR_DATABUS_WIDTH_16B;
|
||||
ns.memory_type = EXMC_MEMORY_TYPE_SRAM;
|
||||
ns.address_data_mux = DISABLE;
|
||||
rt.asyn_access_mode = EXMC_ACCESS_MODE_A;
|
||||
rt.syn_data_latency = EXMC_DATALAT_2_CLK;
|
||||
rt.syn_clk_division = EXMC_SYN_CLOCK_RATIO_DISABLE;
|
||||
rt.bus_latency = 4U;
|
||||
rt.asyn_data_setuptime = 20U;
|
||||
rt.asyn_address_holdtime = 2U;
|
||||
rt.asyn_address_setuptime = 4U;
|
||||
wt = rt;
|
||||
|
||||
exmc_norsram_init(&ns);
|
||||
exmc_norsram_enable(EXMC_BANK0_NORSRAM_REGION0);
|
||||
EXMC_SNCTL0 &= ~0x00000080U; /* DFP 复位值 0x30DB 的 reserved bit7,库跑完仍是 1 */
|
||||
|
||||
/* 探活第一步:HBI_IDX_0 复位默认 0x1234(DS 9.5.4.2,EEPROM 空白时同样工作)。
|
||||
* RST# 释放后 ESC 还要内部启动与加载 EEPROM,故轮询而非单读。 */
|
||||
uint16_t idx0 = BOOT_ESC_REG16(HBI_IDX0_OFFSET);
|
||||
for (uint32_t w = 0U; idx0 != 0x1234U && w < ESC_HBI_POLL_MS; w += ESC_HBI_POLL_STEP_MS) {
|
||||
delay_1ms(ESC_HBI_POLL_STEP_MS);
|
||||
idx0 = BOOT_ESC_REG16(HBI_IDX0_OFFSET);
|
||||
}
|
||||
if (idx0 != 0x1234U) {
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] FAIL HBI_IDX_0=0x%04X(要 0x1234) → LAN 没驱动总线\r\n",
|
||||
(unsigned)idx0);
|
||||
return false;
|
||||
}
|
||||
/* 第二步 BYTE_TEST:只有 DATA 偏移算对才读得到(0x40000/0x60000 错时这步才失败) */
|
||||
if (readReg32(LAN9252_REG_BYTE_TEST) != LAN9252_BYTE_TEST_VALUE) {
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] FAIL BYTE_TEST → HBI_DATA_0 偏移错\r\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief 打开 LAN9252 IRQ 引脚。出厂 IRQ_EN=0 且 ECAT_INT_EN=0,不配则 PB1 永不中断、
|
||||
* PDI_Isr 一次都不执行(APP 1.5.43 就栽在这)。极性与下面 EXTI 一致:推挽低有效。
|
||||
* AL Event Mask(0x0204) 由 SSC 在状态迁移时自行维护,不在此处写。 */
|
||||
void enableEcatIrqOutput()
|
||||
{
|
||||
writeReg32(LAN9252_REG_INT_STS, INT_EN_ECAT_INT);
|
||||
writeReg32(LAN9252_REG_INT_EN, INT_EN_ECAT_INT);
|
||||
writeReg32(LAN9252_REG_IRQ_CFG, IRQ_CFG_IRQ_EN | IRQ_CFG_IRQ_TYPE);
|
||||
if ((readReg32(LAN9252_REG_IRQ_CFG) & IRQ_CFG_IRQ_EN) == 0U) {
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] WARN IRQ_EN 回读 0,PB1 不会中断\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void escIrqGpioEnable()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
gpio_init(ESC_IRQ_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, ESC_IRQ_PIN);
|
||||
gpio_exti_source_select(ESC_IRQ_SRC_PORT, ESC_IRQ_SRC_PIN);
|
||||
exti_init(ESC_IRQ_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
|
||||
exti_interrupt_flag_clear(ESC_IRQ_EXTI);
|
||||
nvic_irq_enable(ESC_IRQ_IRQn, 0U, 0U);
|
||||
}
|
||||
|
||||
/* 由 HW_Init 真正初始化(见文件头注 2) */
|
||||
volatile uint16_t s_alEventMask = 0xFFFFU;
|
||||
volatile uint32_t s_timerBase = 0U;
|
||||
TSYNCMAN ESCMEM s_smBuf;
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
/** @brief 本镜像这一轮是否真的拉起过 HBI 总线(exmcInit 成功)。
|
||||
* 跳转前补发 RST# 以此为前提:没起过总线就不可能留 HBI 索引残值,而那条路径上电才
|
||||
* 几毫秒就要释放 RST#,踩 LAN9252 的 tpurstd(电源稳定→释放复位 ≥25ms)。 */
|
||||
static bool s_escBusUp = false;
|
||||
|
||||
/**
|
||||
* @brief 复位并拉起 LAN9252,等 PDI 就绪,打开 IRQ 输出与 EXTI1。
|
||||
* @return true = 邮箱可用;false = 硬件层不通,调用方应只跑 SPI OTA、不启 SSC。
|
||||
*/
|
||||
extern "C" bool bootEcatBringup(void)
|
||||
{
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; /* DWT:SSC 的 1ms 软件时基 */
|
||||
DWT->CYCCNT = 0U;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
|
||||
escHardwareReset();
|
||||
if (!exmcInit()) {
|
||||
return false;
|
||||
}
|
||||
s_escBusUp = true; /* 从这里起 HBI 上会留 BOOT 写过的索引/数据 */
|
||||
|
||||
uint16_t dl = (uint16_t)csrRead(ESC_REG_DL_STATUS, 2U, false);
|
||||
uint32_t waited = 0U;
|
||||
while ((dl & 0x0001U) == 0U && waited < ESC_HBI_POLL_MS) {
|
||||
delay_1ms(ESC_HBI_POLL_STEP_MS);
|
||||
waited += ESC_HBI_POLL_STEP_MS;
|
||||
dl = (uint16_t)csrRead(ESC_REG_DL_STATUS, 2U, false);
|
||||
}
|
||||
if ((dl & 0x0001U) == 0U) {
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] FAIL PDI 未就绪 DL=0x%04X EEP=0x%04X\r\n",
|
||||
(unsigned)dl, (unsigned)csrRead(ESC_REG_EEP_CSR, 2U, false));
|
||||
return false;
|
||||
}
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] PDI ok DL=0x%04X (%ums),邮箱可用\r\n",
|
||||
(unsigned)dl, (unsigned)waited);
|
||||
enableEcatIrqOutput();
|
||||
escIrqGpioEnable();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief 跳转 APP 前对 LAN9252 补一发硬复位(1.1.0.13 加,1.1.0.14 收口)。
|
||||
* @note 为什么需要:BOOT 侧 HBI 全是索引两步事务,bootEcatDeinit 末尾那条
|
||||
* writeReg32 把 HBI_IDX_0 留在 0x0054(IRQ_CFG)。带着残值跳进 APP,APP 起板探针
|
||||
* (esc_lan9252.cpp 判 IDX_0==0x1234,DS 9.5.4.2 的复位默认值)读到的是 0x0054,
|
||||
* 直接报「EXMC/HBI 总线未就绪」——实测:刷完首启必现,断电重上即好。
|
||||
* @note 为什么必须 gate 在 s_escBusUp 上:1.1.0.13 无条件下发,于是
|
||||
* path=jump_latest_now(上电 3ms 直跳、ESC 从没起过)也在电源刚稳的窗口里拉了
|
||||
* RST#,释放点距上电仅约 15ms,实测跳进 APP 后整机静默(串口停在 [J5]、SPI 无
|
||||
* 应答、无复位循环)。那条路径上没有残值可清,白踩一次时序。
|
||||
* @note 延时长不了:本函数在 __disable_irq() 之后跑,delay_1ms→SysTickTimer::delayMs
|
||||
* 靠 SysTick 中断递减 = 永久死等(BOOT 无 IWDG,直接变砖);delay_1us 是
|
||||
* __NOP 环,实测比标称快约 4 倍(30ms 只走出 8ms),当复位脉宽不够格。
|
||||
* 故用 DWT CYCCNT 忙等:与中断状态无关,且按周期计。 */
|
||||
void escResetForJump()
|
||||
{
|
||||
if (!s_escBusUp) {
|
||||
return; /* 这轮没动过 HBI:别在上电早期碰 RST# */
|
||||
}
|
||||
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
gpio_init(ESC_NRST_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, ESC_NRST_PIN);
|
||||
gpio_bit_reset(ESC_NRST_PORT, ESC_NRST_PIN);
|
||||
|
||||
const uint32_t hold = ESC_NRST_ASSERT_MS * (SystemCoreClock / 1000U);
|
||||
const uint32_t t0 = DWT->CYCCNT;
|
||||
while ((DWT->CYCCNT - t0) < hold) { } /* 低脉宽 30ms:≥trstia 200µs、≥tpurstd 25ms */
|
||||
|
||||
gpio_bit_set(ESC_NRST_PORT, ESC_NRST_PIN);
|
||||
const uint32_t settle = 2U * (SystemCoreClock / 1000U);
|
||||
const uint32_t t1 = DWT->CYCCNT;
|
||||
while ((DWT->CYCCNT - t1) < settle) { }
|
||||
|
||||
BOOT_ECAT_LOG("[BOOT][ESC] jump reset: RST# %ums released, pin=%d (HBI_IDX_0 应回 0x1234)\r\n",
|
||||
(unsigned)ESC_NRST_ASSERT_MS,
|
||||
gpio_input_bit_get(ESC_NRST_PORT, ESC_NRST_PIN) == SET ? 1 : 0);
|
||||
}
|
||||
|
||||
/** @brief 跳转 APP 前拆掉 BOOT 留下的 HBI 用户与引脚占用。 */
|
||||
extern "C" void bootEcatDeinit(void)
|
||||
{
|
||||
NVIC_DisableIRQ(ESC_IRQ_IRQn);
|
||||
NVIC_ClearPendingIRQ(ESC_IRQ_IRQn);
|
||||
exti_interrupt_disable(ESC_IRQ_EXTI);
|
||||
exti_flag_clear(ESC_IRQ_EXTI);
|
||||
writeReg32(LAN9252_REG_INT_EN, 0U); /* 免得 APP 起来后 PB1 还挂着 ESC 驱动的电平 */
|
||||
writeReg32(LAN9252_REG_IRQ_CFG, 0U);
|
||||
escResetForJump(); /* 1.1.0.13: 交还总线前把芯片复位到上电默认态 */
|
||||
exmc_norsram_disable(EXMC_BANK0_NORSRAM_REGION0);
|
||||
rcu_periph_clock_disable(RCU_EXMC); /* PD/PE 回浮空,交还 APP 自己初始化 */
|
||||
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
|
||||
/** @brief PB1 → ESC IRQ。PDI_Isr 内部做 AL 事件读与邮箱 SM 搬运。
|
||||
* @note 必须 extern "C":本行在下面的 extern "C" { 之前,按 C++ 修饰名编译就
|
||||
* 覆盖不了启动文件的弱符号,向量表 IRQn7 会留在 B . 桩上 —— 第一条 ESC
|
||||
* 中断即永久空转(BOOT 无看门狗,现场表现为静默变砖)。判据是读向量表,
|
||||
* 不是看编译通过:v1.1.0.8 实测该槽位 = 0x080001D3(桩),已修。 */
|
||||
extern "C" void EXTI1_IRQHandler(void)
|
||||
{
|
||||
if (exti_interrupt_flag_get(ESC_IRQ_EXTI) != RESET) {
|
||||
exti_interrupt_flag_clear(ESC_IRQ_EXTI);
|
||||
PDI_Isr();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
/** @brief SSC 特有初始化。MainInit() 不调它(ecatappl.c:571 明确要求应用层调),
|
||||
* 所以 EcatTask::start() 必须显式调一次。 */
|
||||
UINT8 HW_Init(void)
|
||||
{
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
s_timerBase = DWT->CYCCNT;
|
||||
s_alEventMask = (UINT16)csrRead(ESC_AL_EVENTMASK_OFFSET, 2U);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
void HW_Release(void) {}
|
||||
|
||||
UINT16 HW_GetALEventRegister(void)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
const UINT16 v = (UINT16)csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
hbiExit(prev);
|
||||
return v;
|
||||
}
|
||||
|
||||
UINT16 HW_GetALEventRegister_Isr(void)
|
||||
{
|
||||
return (UINT16)csrRead(ESC_AL_EVENT_OFFSET, 2U);
|
||||
}
|
||||
|
||||
void HW_ResetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
s_alEventMask &= (UINT16)~intMask;
|
||||
csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
void HW_SetALEventMask(UINT16 intMask)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
s_alEventMask |= intMask;
|
||||
csrWrite(ESC_AL_EVENTMASK_OFFSET, s_alEventMask, 2U);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
void HW_SetLed(UINT8 RunLed, UINT8 ErrLed)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
csrWrite(ESC_RUN_LED_OVERRIDE, RunLed ? 0x0001U : 0x0000U, 2U);
|
||||
csrWrite(ESC_ERROR_LED_OVERRIDE, ErrLed ? 0x0001U : 0x0000U, 2U);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 len)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
escRead(pData, Address, len);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 len)
|
||||
{
|
||||
const uint32_t prev = hbiEnter();
|
||||
escWrite(pData, Address, len);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
/* ISR 版本:已在 EXTI1 上下文,hbiEnter 会把 EXTI1 自己关掉再开回来,无意义,直接走。 */
|
||||
void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 len)
|
||||
{
|
||||
escRead(pData, Address, len);
|
||||
}
|
||||
|
||||
void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 len)
|
||||
{
|
||||
escWrite(pData, Address, len);
|
||||
}
|
||||
|
||||
/** @brief SM Activate 寄存器 = 0x0800 + 8n + 6(1.5.39:+4 是 SM Control,从未生效)。 */
|
||||
void HW_DisableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
const UINT16 a = (UINT16)(ESC_SYNCMAN_REG_OFFSET + (UINT16)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = hbiEnter();
|
||||
const uint8_t v = (uint8_t)csrRead(a, 1U) & (uint8_t)~0x01U;
|
||||
csrWrite(a, v, 1U);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
void HW_EnableSyncManChannel(UINT8 channel)
|
||||
{
|
||||
const UINT16 a = (UINT16)(ESC_SYNCMAN_REG_OFFSET + (UINT16)channel * SIZEOF_SM_REGISTER + 6U);
|
||||
const uint32_t prev = hbiEnter();
|
||||
const uint8_t v = (uint8_t)csrRead(a, 1U) | 0x01U;
|
||||
csrWrite(a, v, 1U);
|
||||
hbiExit(prev);
|
||||
}
|
||||
|
||||
/** @brief SM 寄存器经间接端口访问,给不出真指针:静态缓冲,每次调用重读。 */
|
||||
TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel)
|
||||
{
|
||||
const UINT16 base = (UINT16)(ESC_SYNCMAN_REG_OFFSET + (UINT16)channel * SIZEOF_SM_REGISTER);
|
||||
const uint32_t prev = hbiEnter();
|
||||
escRead((MEM_ADDR *)&s_smBuf, base, SIZEOF_SM_REGISTER);
|
||||
hbiExit(prev);
|
||||
return &s_smBuf;
|
||||
}
|
||||
|
||||
UINT32 HW_GetTimerValue(void) { return DWT->CYCCNT - s_timerBase; }
|
||||
void HW_ClearTimerValue(void) { s_timerBase = DWT->CYCCNT; }
|
||||
|
||||
} /* extern "C" */
|
||||
@@ -0,0 +1,803 @@
|
||||
/**
|
||||
\addtogroup CoE CAN Application Profile over EtherCAT
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file coeappl.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains an example for CoE services and the CoE object dictionary
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 COE1: update invalid end entry in the object dictionaries (error with some compilers)<br>
|
||||
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 COE2: Change return value of Get ObjectSize to UINT32<br>
|
||||
Change object size to UINT32<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 APPL3: Include library demo application<br>
|
||||
V5.01 COE1: Remove alignment entry (SI33) from objects 0x1C32/0x1C33<br>
|
||||
V5.01 EL9800 1: Read/Write functions for setting object 0x8020 moved to el9800appl.c<br>
|
||||
V5.01 EL9800 2: Add TxPdo Parameter object 0x1802<br>
|
||||
V5.01 SDO6: Update SDO response interface handling. (used if the object access function returns "ABORTIDX_WORKING" and SDO_RES_INTERFACE is active)<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
|
||||
V5.0 COE1: Add reserved Subindex7 to object 0x1C32 and 0x1C33.<br>
|
||||
V5.0 COE4: Create object dictionary on device startup.<br>
|
||||
V5.0 COE6: Update entry descriptions for object with the code ARRAY.<br>
|
||||
V5.0 ECAT2: Reference to application specific object dictionary is set to "ApplicationObjDic".<br>
|
||||
<br>Changes to version V4.30:<br>
|
||||
V4.40 COE 6: change dynamic object dictionary handling<br>
|
||||
V4.40 CiA402 2: enable freerun Sync Mode for CiA402 Device (The motion controller application will only be triggered if DC Synchronisation is disabled)<br>
|
||||
V4.40 COE5: move identification and synchronisation object values to ecat_def.h<br>
|
||||
V4.40 DIAG1: Add diagnosis object 0x10F3<br>
|
||||
V4.40 OBJ3: Update the global object dictionary pointer if a new list head is added<br>
|
||||
V4.40 COE4: Prevent access NULL pointer<br>
|
||||
V4.40 SYNC1: Initialize 0x1C3x objects<br>
|
||||
V4.40 HW1: Add (adapt) objects for FC1100 support<br>
|
||||
<br>Changes to version V4.20:<br>
|
||||
V4.30 OBJ 3: Handle object dictionary in double link list<br>
|
||||
V4.30 OBJ 2: General EtherCAT device objects are moved from chnappl.h to coeappl.c<br>
|
||||
V4.30 SYNC: set 0x1C32:05; 0x1C33:05 (minCyleTime) to MIN_PD_CYCLE_TIME
|
||||
(should be set dynamic if required)<br>
|
||||
V4.30 PDO: rename PDO specific function calls
|
||||
(PDO specific functions are moved to ecatappl.c)<br>
|
||||
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
|
||||
V4.11 ECAT 1: C166: MinDelayTime renamed in CalcAndCopyTime<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.10 ECAT 1: To save and restore arrays in the object directory.<br>
|
||||
The code to get the size of an array entry was changed.<br>
|
||||
V4.10 COE 1: The SM-Parameter were extended<br>
|
||||
<br>Changes to version V4.06:<br>
|
||||
V4.07 COEAPPL 1: The PDO numbers were adapted according top the modular device profile,<br>
|
||||
the PDO numbers increment now with every module (in chnappl.h)<br>
|
||||
V4.07 COEAPPL 2: The example is working for the NIOS with the evaluation board DBC2C20 V1.2<br>
|
||||
which is available by Altera<br>
|
||||
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
|
||||
set the switch MCI_HW to 1 when using the MCI,<br>
|
||||
set the switch SPI_HW to 1 when using the SPI<br>
|
||||
<br>Changes to version V4.03:<br>
|
||||
V4.04 SDO 1: The SDO interface was changed in that way that a SDO response<br>
|
||||
could be sent by the application to a later time. In that case<br>
|
||||
the functions OBJ_Read and OBJ_Write shall return the value<br>
|
||||
ABORTIDX_WORKING. To send the SDO response the new function SDOS_SdoRes<br>
|
||||
has to be called by the application. While waiting for the call<br>
|
||||
of SDOS_SdoRes the SDO interface will answer to another SDO request<br>
|
||||
with the error MBXERR_SERVICEINWORK in the mailbox protocol.<br>
|
||||
In this example the reading and writing of object 0x8020 is delayed,<br>
|
||||
the function SDOS_SdoRes will be called from COE_Main.<br>
|
||||
<br>Changes to version V4.00:<br>
|
||||
V4.01 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
|
||||
in chnappl.h (chnappl.inc made problems with some developing tools)<br>
|
||||
<br>Changes to version V3.20:<br>
|
||||
V4.00 COEAPPL 1: The object dictionary and the mapping functions are now defined<br>
|
||||
in chnappl.c and were adapted to the modular device profile<br>
|
||||
V4.00 COEAPPL 2: The handling of backup parameters was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 COEAPPL 3: The handling of the TxPDO Toggle was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 COEAPPL 4: The handling of the TxPDO State was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification<br>
|
||||
V4.00 ECAT 1: The handling of the Sync Manager Parameter was included according to<br>
|
||||
the EtherCAT Guidelines and Protocol Enhancements Specification
|
||||
*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "ecatslv.h"
|
||||
|
||||
#define _COEAPPL_ 1
|
||||
#include "coeappl.h"
|
||||
#undef _COEAPPL_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _COEAPPL_ (#ifdef is used in coeappl.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#define _OBJD_
|
||||
|
||||
/*Add Application specific Objects*/
|
||||
#include "el9800appl.h"
|
||||
|
||||
|
||||
#undef _OBJD_
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Backup Parameter
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Generic EtherCAT device object
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1000
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1000 (Device type) variable to handle the object data
|
||||
*/
|
||||
UINT32 u32Devicetype = (DEVICE_PROFILE_TYPE);
|
||||
|
||||
/**
|
||||
* \brief 0x1000 (Device type) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1000 = {DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1000 (Device type) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1000[] = "Device type";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1001
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) variable to handle the object data
|
||||
*/
|
||||
UINT16 u16ErrorRegister = 0x0;
|
||||
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1001 = {DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1001 (Error Register) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1001[] = "Error register";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1008
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1008 (Device name) variable to handle the object data
|
||||
*/
|
||||
CHAR acDevicename[] = DEVICE_NAME;
|
||||
|
||||
/**
|
||||
* \brief 0x1008 (Device name) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1008 = {DEFTYPE_VISIBLESTRING, BYTE2BIT(DEVICE_NAME_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1008 (Device name) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1008[] = "Device name";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1009
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) variable to handle the object data
|
||||
*/
|
||||
CHAR acHardwareversion[] = DEVICE_HW_VERSION;
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x1009 = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_HW_VERSION_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1009[] = "Hardware version";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x100A
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x100A (Software version) variable to handle the object data
|
||||
*
|
||||
* 必须显式给到 DEVICE_SW_VERSION_LEN+1:条目描述符按 LEN 声明位长,SSC 会照长度
|
||||
* 整块回给主站;用 =DEVICE_SW_VERSION 推出来只有 4 字节,读到的是越界内容。
|
||||
* 真实版本号由 main.cpp 起板时 snprintf 覆盖(数组要可写,别改成 OBJCONST)。
|
||||
*/
|
||||
CHAR acSoftwareversion[DEVICE_SW_VERSION_LEN + 1] = DEVICE_SW_VERSION;
|
||||
|
||||
/** @brief 覆盖 0x100A 上报的软件版本串(由 main.cpp 起板时调用)。
|
||||
*
|
||||
* 容量只有本文件知道(数组定义在这儿),所以 setter 放这儿,别让调用方
|
||||
* 为拿一个 DEVICE_SW_VERSION_LEN 去 include 整套 SSC 头。超长直接截断,不越界。
|
||||
*/
|
||||
void AppSetSoftwareVersion(const char *version)
|
||||
{
|
||||
unsigned int i = 0U;
|
||||
|
||||
if (version == NULL) {
|
||||
return;
|
||||
}
|
||||
/* 本文件没 include stdio,手写有界拷贝:超长就截断,末尾必带 NUL。 */
|
||||
for (; i + 1U < sizeof(acSoftwareversion) && version[i] != '\0'; i++) {
|
||||
acSoftwareversion[i] = version[i];
|
||||
}
|
||||
acSoftwareversion[i] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) entry description
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM sEntryDesc0x100A = {DEFTYPE_VISIBLESTRING,BYTE2BIT(DEVICE_SW_VERSION_LEN), ACCESS_READ};
|
||||
|
||||
/**
|
||||
* \brief 0x1009 (Hardware version) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x100A[] = "Software version";
|
||||
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1018
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1018 (Identity) object structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 u16SubIndex0; /**< \brief SubIndex 0*/
|
||||
UINT32 u32VendorID; /**< \brief EtherCAT vendor ID */
|
||||
UINT32 u32Productcode; /**< \brief Product code*/
|
||||
UINT32 u32Revision; /**< \brief Revision number*/
|
||||
UINT32 u32Serialnumber; /**< \brief Serial number*/
|
||||
} OBJ_STRUCT_PACKED_END
|
||||
TOBJ1018;
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) variable to handle the object data
|
||||
*/
|
||||
TOBJ1018 sIdentity = {4, (VENDOR_ID), (PRODUCT_CODE), (REVISION_NUMBER), (SERIAL_NUMBER)};
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) entry descriptions
|
||||
*
|
||||
* Subindex 000
|
||||
* SubIndex 001: Vendor ID
|
||||
* SubIndex 002: Product code
|
||||
* SubIndex 003: Revision
|
||||
* SubIndex 004: Serial number
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1018[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ },
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}};
|
||||
|
||||
/**
|
||||
* \brief 0x1018 (Identity) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1018[] = "Identity\000Vendor ID\000Product code\000Revision\000Serial number\000\377";
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x10F1
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x10F1 (Error settings) entry description
|
||||
* Subindex 000
|
||||
* SubIndex 001: Local Error Reaction
|
||||
* SubIndex 002: Sync Error Counter Limit
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x10F1[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ },
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE},
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}};
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
|
||||
/**
|
||||
* \brief 0x10F1 (Error settings) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x10F1[] = "Error Settings\000Local Error Reaction\000Sync Error Counter Limit\000\377";
|
||||
|
||||
|
||||
//object declaration and initialization in objdef.h
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1C00
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) object structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 u16SubIndex0; /**< \brief SubIndex 0*/
|
||||
UINT8 aEntries[4]; /**< \brief Entry buffer (one for each SyncManager)*/
|
||||
} OBJ_STRUCT_PACKED_END
|
||||
TOBJ1C00;
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) variable to handle the object data
|
||||
*
|
||||
* SI 0 : 4 (4 SyncManagers available)
|
||||
* SI1 (SM0): Mailbox Out (0x1)
|
||||
* SI2 (SM1): Mailbox In (0x2)
|
||||
* SI3 (SM2): Process data Out (0x3)
|
||||
* SI4 (SM3): Process data In (0x4)
|
||||
*
|
||||
*/
|
||||
TOBJ1C00 sSyncmanagertype = {0x04, {0x01, 0x02, 0x03, 0x04}};
|
||||
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) entry descriptions
|
||||
*
|
||||
* The object is an array so only the first entry is described (which is used for all entries).
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C00[2] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ},
|
||||
{DEFTYPE_UNSIGNED8, 0x08, ACCESS_READ}};
|
||||
|
||||
/**
|
||||
* \brief 0x1C00 (Sync manager type) object name
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C00[] = "Sync manager type";
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- 0x1C3x
|
||||
-----------------------------------------------*/
|
||||
/**
|
||||
* \brief 0x1C3x (SyncManager parameter) entry description
|
||||
*
|
||||
* The entry descriptions are used for 0x1C32 and 0x1C33
|
||||
*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM asEntryDesc0x1C3x[] = {
|
||||
{DEFTYPE_UNSIGNED8, 0x8, ACCESS_READ }, /* Subindex 000 */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, (ACCESS_READ | ACCESS_WRITE_PREOP)}, /* SubIndex 001: Synchronization Type */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 002: Cycle Time */
|
||||
{0x0000, 0x20, 0}, /* SubIndex 003: Shift Time (not supported)*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 004: Synchronization Types supported */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 005: Minimum Cycle Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 006: Calc and Copy Time */
|
||||
{0x0000, 0x20, 0}, /* Subindex 007 Minimum Delay Time (not supported)*/
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READWRITE}, /* SubIndex 008: Get Cycle Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READ}, /* SubIndex 009: Delay Time */
|
||||
{DEFTYPE_UNSIGNED32, 0x20, ACCESS_READWRITE}, /* SubIndex 010: Sync0 Cycle Time */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 011: SM-Event Missed */
|
||||
{DEFTYPE_UNSIGNED16, 0x10, ACCESS_READ}, /* SubIndex 012: Cycle Time Too Small */
|
||||
{0x0000, 0x10, 0}, /* SubIndex 013: Shift Too Short Counter (not supported)*/
|
||||
{0x0000, 0x10, 0}, /* Subindex 014: RxPDO Toggle Failed (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 015: Minimum Cycle Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 016: Maximum Cycle Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 017: Minimum SM Sync Distance (not supported)*/
|
||||
{0x0000, 0x20, 0}, /* Subindex 018: Maximum SM Sync Distance (not supported)*/
|
||||
{0x0000, 0, 0}, /* Subindex 019 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 020 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 021 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 022 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 023 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 024 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 025 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 026 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 027 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 028 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 029 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 030 doesn't exist */
|
||||
{0x0000, 0, 0}, /* Subindex 031 doesn't exist */
|
||||
{DEFTYPE_BOOLEAN, 0x01, ACCESS_READ}}; /* SubIndex 032: Sync error */
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/**
|
||||
* \brief 0x1C32 (SyncManager 2 parameter) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C32[] = "SM output parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377";
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/**
|
||||
* \brief 0x1C33 (SyncManager 3 parameter) object and entry names
|
||||
*/
|
||||
OBJCONST UCHAR OBJMEM aName0x1C33[] = "SM input parameter\000Synchronization Type\000Cycle Time\000\000Synchronization Types supported\000Minimum Cycle Time\000Calc and Copy Time\000\000Get Cycle Time\000Delay Time\000Sync0 Cycle Time\000SM-Event Missed\000Cycle Time Too Small\000Shift Time Too Short\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000Sync Error\000\377";
|
||||
|
||||
/******************************************************************************
|
||||
** Object Dictionary
|
||||
******************************************************************************/
|
||||
/**
|
||||
* \brief Object dictionary pointer
|
||||
*/
|
||||
TOBJECT OBJMEM * ObjDicList = NULL;
|
||||
|
||||
/**
|
||||
* \brief List of generic application independent objects
|
||||
*/
|
||||
TOBJECT OBJMEM GenObjDic[] = {
|
||||
/* Object 0x1000 */
|
||||
{NULL,NULL, 0x1000, {DEFTYPE_UNSIGNED32, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1000, aName0x1000, &u32Devicetype, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1001 */
|
||||
{NULL,NULL, 0x1001, {DEFTYPE_UNSIGNED8, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1001, aName0x1001, &u16ErrorRegister, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1008 */
|
||||
{NULL,NULL, 0x1008, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1008, aName0x1008, acDevicename, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1009 */
|
||||
{NULL,NULL, 0x1009, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x1009, aName0x1009, acHardwareversion, NULL, NULL, 0x0000 },
|
||||
/* Object 0x100A */
|
||||
{NULL,NULL, 0x100A, {DEFTYPE_VISIBLESTRING, 0 | (OBJCODE_VAR << 8)}, &sEntryDesc0x100A, aName0x100A, acSoftwareversion, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1018 */
|
||||
{NULL,NULL, 0x1018, {DEFTYPE_IDENTITY, 4 | (OBJCODE_REC << 8)}, asEntryDesc0x1018, aName0x1018, &sIdentity, NULL, NULL, 0x0000 },
|
||||
/* Object 0x10F1 */
|
||||
{NULL,NULL, 0x10F1, {DEFTYPE_RECORD, 2 | (OBJCODE_REC << 8)}, asEntryDesc0x10F1, aName0x10F1, &sErrorSettings, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C00 */
|
||||
{NULL,NULL, 0x1C00, {DEFTYPE_UNSIGNED8, 4 | (OBJCODE_ARR << 8)}, asEntryDesc0x1C00, aName0x1C00, &sSyncmanagertype, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C32 */
|
||||
{NULL,NULL, 0x1C32, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C32, &sSyncManOutPar, NULL, NULL, 0x0000 },
|
||||
/* Object 0x1C33 */
|
||||
{NULL,NULL, 0x1C33, {DEFTYPE_SMPAR, 32 | (OBJCODE_REC << 8)}, asEntryDesc0x1C3x, aName0x1C33, &sSyncManInPar, NULL, NULL, 0x0000 },
|
||||
|
||||
/*end of entries*/
|
||||
/*ECATCHANGE_START(V5.11) COE1*/
|
||||
{NULL,NULL, 0xFFFF, {0, 0}, NULL, NULL, NULL, NULL, NULL, 0x000}};
|
||||
/*ECATCHANGE_END(V5.11) COE1*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief returns the pointer to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void)
|
||||
{
|
||||
return (OBJCONST TOBJECT OBJMEM *) ObjDicList;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function initialize the several objects
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_ObjInit(void)
|
||||
{
|
||||
|
||||
/* initialize the Sync Manager Output parameter object 0x1C32 */
|
||||
|
||||
sSyncManOutPar.subindex0 = 32;
|
||||
/*
|
||||
subindex 1 contains the actual synchronization mode, it could be written
|
||||
from the master to switch between ECAT FreeRun and ECAT Synchron Mode
|
||||
if the slave supports both modes,
|
||||
in DC mode (selected by the DC registers) this value will be overwritten
|
||||
with SYNCTYPE_DCSYNC0 or SYNCTYPE_DCSYNC1 */
|
||||
/*default mode is ECAT Synchron Mode */
|
||||
sSyncManOutPar.u16SyncType = SYNCTYPE_FREERUN;
|
||||
|
||||
/* subindex 2 contains the cycle time of the application,
|
||||
in ECAT FreeRun mode it could be used for a timer interrupt to run the application,
|
||||
in ECAT Synchron mode it could be written from the master with its local cycle time
|
||||
that the slave can check if this cycle time is supported,
|
||||
in DC Mode this value will be overwritten with the DC cycle time register */
|
||||
sSyncManOutPar.u32CycleTime = 0;
|
||||
/* only for DC Mode important: the subindex 3 contains the time shift between the
|
||||
SYNC0 (SYNC1) signal and when the outputs are put to the hardware to allow the
|
||||
master a very exactly calculation of delay times*/
|
||||
sSyncManOutPar.u32ShiftTime = 0;
|
||||
|
||||
/* the subindex 4 contains the supported synchronization types */
|
||||
|
||||
sSyncManOutPar.u16SyncTypesSupported = SYNCTYPE_FREERUNSUPP /* ECAT FreeRun Mode is supported */
|
||||
| SYNCTYPE_TIMESVARIABLE /* the execution times depend on the connected modules */
|
||||
| SYNCTYPE_SYNCHRONSUPP /* ECAT Synchron Mode is supported */
|
||||
| SYNCTYPE_DCSYNC0SUPP /* DC Sync0 Mode is supported */
|
||||
| SYNCTYPE_DCSYNC1SUPP /* DC Sync1 Mode is supported */
|
||||
| SYNCTYPE_SUBCYCLESUPP /*Subordinated application cycles supported*/
|
||||
;
|
||||
|
||||
/* subindex 5 contains the minimum cycle time the slave is able to support,
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine).
|
||||
For the sample application this value is set to MIN_PD_CYCLE_TIME */
|
||||
sSyncManOutPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
|
||||
/* only for DC Mode important: subindex 6 contains the minimum delay time the slave
|
||||
needs after receiving the SM2-event before the SYNC0(SYNC1) can be received without delays
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine) */
|
||||
sSyncManOutPar.u32CalcAndCopyTime = (PD_OUTPUT_CALC_AND_COPY_TIME);
|
||||
|
||||
/*subindex 8: trigger cycle time measurement*/
|
||||
sSyncManOutPar.u16GetCycleTime = 0;
|
||||
|
||||
/*subindex 9: time from start driving outputs until outputs are valid*/
|
||||
sSyncManOutPar.u32DelayTime = (PD_OUTPUT_DELAY_TIME);
|
||||
|
||||
/*subindex 32: indicates if a synchronisation error has occurred*/
|
||||
sSyncManOutPar.u8SyncError = 0;
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
/* initialize the Sync Manager Input parameter object 0x1C33 */
|
||||
sSyncManInPar.subindex0 = 32;
|
||||
/* default mode is ECAT Synchron Mode, if output size > 0 the inputs are updated with the SM2-event */
|
||||
sSyncManInPar.u16SyncType = SYNCTYPE_FREERUN;
|
||||
|
||||
/* subindex 2: same as 0x1C32:02 */
|
||||
sSyncManInPar.u32CycleTime = sSyncManOutPar.u32CycleTime;
|
||||
/* only for DC Mode important: subindex 3 contains the time shift between the
|
||||
SYNC0 (SYNC1) signal and when the inputs are got to the hardware to allow the
|
||||
master a very exactly calculation of delay times,
|
||||
will be calculated dynamically because it depends on the connected modules
|
||||
(in this example we will make an online measurement in the ESC Interrupt Routine) */
|
||||
sSyncManInPar.u32ShiftTime = 0;
|
||||
/* subindex 4: same as 0x1C32:04 */
|
||||
sSyncManInPar.u16SyncTypesSupported = sSyncManOutPar.u16SyncTypesSupported;
|
||||
|
||||
/* subindex 5: same as 0x1C32:05 */
|
||||
sSyncManInPar.u32MinCycleTime = MIN_PD_CYCLE_TIME;
|
||||
/* subindex 6: delay read inputs, calculation and copy to SM buffer*/
|
||||
sSyncManInPar.u32CalcAndCopyTime = (PD_INPUT_CALC_AND_COPY_TIME);
|
||||
/*subindex 8: trigger cycle time measurement*/
|
||||
sSyncManInPar.u16GetCycleTime = 0;
|
||||
/*subindex 9: delay to prepare input latch*/
|
||||
sSyncManInPar.u32DelayTime = (PD_INPUT_DELAY_TIME);
|
||||
|
||||
/*subindex 32: incremented if a synchronisation error has occurred*/
|
||||
sSyncManInPar.u8SyncError = 0;
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*Indicate no user specified Sync mode*/
|
||||
bSyncSetByUser = FALSE;
|
||||
|
||||
{
|
||||
UINT16 result = COE_ObjDictionaryInit();
|
||||
if(result != 0)
|
||||
{
|
||||
/*clear already linked objects*/
|
||||
COE_ClearObjDictionary();
|
||||
}
|
||||
}
|
||||
|
||||
u8PendingSdo = 0;
|
||||
bStoreCompleteAccess = FALSE;
|
||||
u16StoreIndex = 0;
|
||||
u8StoreSubindex = 0;
|
||||
u32StoreDataSize = 0;
|
||||
pStoreData = NULL;
|
||||
pSdoPendFunc = NULL;
|
||||
|
||||
pSdoSegData = NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 object successful added to object dictionary
|
||||
ALSTATUSCODE_XX add object failed
|
||||
|
||||
\brief This function adds an object to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry)
|
||||
{
|
||||
if(pNewObjEntry != NULL)
|
||||
{
|
||||
if(ObjDicList == NULL)
|
||||
{
|
||||
/* Object dictionary is empty */
|
||||
ObjDicList = pNewObjEntry;
|
||||
ObjDicList->pNext = NULL;
|
||||
ObjDicList->pPrev = NULL;
|
||||
return 0;
|
||||
}
|
||||
else if(ObjDicList->Index > pNewObjEntry->Index)
|
||||
{
|
||||
/*insert new object dictionary head*/
|
||||
pNewObjEntry->pPrev = NULL;
|
||||
pNewObjEntry->pNext = ObjDicList;
|
||||
ObjDicList->pPrev = pNewObjEntry;
|
||||
ObjDicList = pNewObjEntry;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
TOBJECT OBJMEM * pDicEntry = ObjDicList;
|
||||
while(pDicEntry != NULL)
|
||||
{
|
||||
if(pDicEntry->Index == pNewObjEntry->Index)
|
||||
{
|
||||
/*object already exists in object dictionary*/
|
||||
return ALSTATUSCODE_UNSPECIFIEDERROR;
|
||||
}
|
||||
else if(pDicEntry->Index > pNewObjEntry->Index)
|
||||
{
|
||||
pNewObjEntry->pPrev = pDicEntry->pPrev;
|
||||
pNewObjEntry->pNext = pDicEntry;
|
||||
|
||||
if(pDicEntry->pPrev != NULL)
|
||||
pDicEntry->pPrev->pNext = pNewObjEntry;
|
||||
|
||||
pDicEntry->pPrev = pNewObjEntry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if(pDicEntry->pNext == NULL)
|
||||
{
|
||||
/*Last entry reached => add object to list tail*/
|
||||
pDicEntry->pNext = pNewObjEntry;
|
||||
pNewObjEntry->pPrev = pDicEntry;
|
||||
pNewObjEntry->pNext = NULL;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*The new object index is smaller than the current index. Get next object handle.*/
|
||||
pDicEntry = pDicEntry->pNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ALSTATUSCODE_UNSPECIFIEDERROR;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function removes an object to the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void COE_RemoveDicEntry(UINT16 index)
|
||||
{
|
||||
TOBJECT OBJMEM * pDicEntry = ObjDicList;
|
||||
|
||||
while(pDicEntry != NULL)
|
||||
{
|
||||
if(pDicEntry->Index == index)
|
||||
{
|
||||
TOBJECT OBJMEM *pPrevEntry = pDicEntry->pPrev;
|
||||
TOBJECT OBJMEM *pNextEntry = pDicEntry->pNext;
|
||||
|
||||
if(pPrevEntry != NULL)
|
||||
pPrevEntry->pNext = pNextEntry;
|
||||
|
||||
if(pNextEntry != NULL)
|
||||
pNextEntry->pPrev = pPrevEntry;
|
||||
|
||||
pDicEntry->pPrev = NULL;
|
||||
pDicEntry->pNext = NULL;
|
||||
/*Update Object dictionary pointer if list head was removed*/
|
||||
if(pDicEntry->Index == ObjDicList->Index)
|
||||
{
|
||||
ObjDicList = pNextEntry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pDicEntry = pDicEntry->pNext;
|
||||
}
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function clear the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void COE_ClearObjDictionary(void)
|
||||
{
|
||||
TOBJECT OBJMEM * pObjEntry = (TOBJECT OBJMEM *) ObjDicList;
|
||||
UINT16 Index = 0;
|
||||
|
||||
while(pObjEntry != NULL)
|
||||
{
|
||||
Index = pObjEntry->Index;
|
||||
pObjEntry = pObjEntry->pNext;
|
||||
|
||||
COE_RemoveDicEntry(Index);
|
||||
}
|
||||
ObjDicList = NULL;
|
||||
}
|
||||
|
||||
|
||||
UINT16 AddObjectsToObjDictionary(TOBJECT OBJMEM * pObjEntry)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
TOBJECT OBJMEM * pEntry = (TOBJECT OBJMEM *)pObjEntry;
|
||||
|
||||
while(pEntry->Index != 0xFFFF)
|
||||
{
|
||||
result = COE_AddObjectToDic(pEntry);
|
||||
|
||||
if(result != 0)
|
||||
return result;
|
||||
|
||||
pEntry++;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\return 0 object dictionary created successful
|
||||
ALSTATUSCODE_XX create object dictionary failed
|
||||
|
||||
\brief This function initialize the object dictionary
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 COE_ObjDictionaryInit(void)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
|
||||
/*Reset object dictionary pointer*/
|
||||
ObjDicList = NULL;
|
||||
|
||||
result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) GenObjDic);
|
||||
|
||||
if(result != 0)
|
||||
return result;
|
||||
if(ApplicationObjDic != NULL)
|
||||
{
|
||||
result = AddObjectsToObjDictionary((TOBJECT OBJMEM *) ApplicationObjDic);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief is called for background calculations which should not influence the
|
||||
ECAT_Application in synchronous modes
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_Main(void)
|
||||
{
|
||||
UINT8 abort = 0;
|
||||
if(pSdoPendFunc != NULL)
|
||||
{
|
||||
abort = pSdoPendFunc(u16StoreIndex,u8StoreSubindex,u32StoreDataSize,pStoreData,bStoreCompleteAccess);
|
||||
|
||||
if(abort != ABORTIDX_WORKING)
|
||||
{
|
||||
switch(u8PendingSdo)
|
||||
{
|
||||
case SDO_PENDING_SEG_WRITE:
|
||||
if(pSdoSegData)
|
||||
{
|
||||
/* the allocated buffer can be released */
|
||||
FREEMEM( (UINT16 VARMEM *) pSdoSegData );
|
||||
pSdoSegData = NULL;
|
||||
}
|
||||
case SDO_PENDING_WRITE:
|
||||
/*send SDO Download Response*/
|
||||
SDOS_SdoRes(abort, 0, NULL);
|
||||
break;
|
||||
|
||||
case SDO_PENDING_SEG_READ:
|
||||
case SDO_PENDING_READ:
|
||||
/* send SDO upload response */
|
||||
SDOS_SdoRes(abort, u32StoreDataSize, pStoreData);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
u8PendingSdo = 0;
|
||||
u16StoreIndex = 0;
|
||||
u8StoreSubindex = 0;
|
||||
u32StoreDataSize = 0;
|
||||
pStoreData = NULL;
|
||||
bStoreCompleteAccess = 0;
|
||||
pSdoPendFunc = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* \addtogroup CoE CAN Application Profile over EtherCAT
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file coeappl.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
#ifndef _COEAPPL_H_
|
||||
#define _COEAPPL_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "objdef.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
#endif //_COEAPPL_H_
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_COEAPPL_) && (_COEAPPL_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
PROTO void COE_ObjInit(void);
|
||||
PROTO void COE_Main(void);
|
||||
PROTO UINT16 COE_ObjDictionaryInit(void);
|
||||
PROTO UINT16 COE_AddObjectToDic(TOBJECT OBJMEM * pNewObjEntry);
|
||||
PROTO void COE_RemoveDicEntry(UINT16 index);
|
||||
PROTO void COE_ClearObjDictionary(void);
|
||||
PROTO OBJCONST TOBJECT OBJMEM * COE_GetObjectDictionary(void);
|
||||
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file dc_app.h
|
||||
* @brief BOOT 侧 SSC 应用层接口声明(Phase 0 尺寸探针)
|
||||
*
|
||||
* el9800appl.h:13 无条件 #include "dc_app.h",SSC 的这份拷贝要求端口层提供该头。
|
||||
* 原型对齐 DCU_APP/comm/ssc/port/dc_app.h:23-33,去掉 APP 专有的 EcatDbg_* 埋点。
|
||||
* 实现在 boot_ecat_port.cpp。
|
||||
*/
|
||||
#ifndef COMM_ECAT_DC_APP_H
|
||||
#define COMM_ECAT_DC_APP_H
|
||||
|
||||
#include "ecat_def.h"
|
||||
#include "esc.h"
|
||||
#include "ecatslv.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* SSC 状态机回调 (ecatslv.c 调用) */
|
||||
UINT16 APPL_GenerateMapping(UINT16 *pInputSize, UINT16 *pOutputSize);
|
||||
UINT16 APPL_StartMailboxHandler(void);
|
||||
UINT16 APPL_StopMailboxHandler(void);
|
||||
UINT16 APPL_StartInputHandler(UINT16 *pIntMask);
|
||||
UINT16 APPL_StopInputHandler(void);
|
||||
UINT16 APPL_StartOutputHandler(void);
|
||||
UINT16 APPL_StopOutputHandler(void);
|
||||
void APPL_AckErrorInd(UINT16 stateTrans);
|
||||
|
||||
/* 周期数据映射回调 (ecatappl.c 调用) */
|
||||
void APPL_InputMapping(UINT16 *pInputData);
|
||||
void APPL_OutputMapping(UINT16 *pOutputData);
|
||||
void APPL_Application(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COMM_ECAT_DC_APP_H */
|
||||
@@ -0,0 +1,802 @@
|
||||
/**
|
||||
* @file ecat_def.h
|
||||
* @brief SSC V5.11 项目配置 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode)
|
||||
*
|
||||
* 基于 Beckhoff SSC V5.11 例程配置裁剪,适配本项目硬件:
|
||||
* - MCU: GD32E508VE (Cortex-M33, 180MHz, little-endian)
|
||||
* - ESC: LAN9252 via EXMC NOR/SRAM Region0, 16-bit HBI Indexed Address Mode
|
||||
* - 协议: CoE (SDO + PDO), DC SYNC0, 无 FoE/EoE/AoE/SoE
|
||||
*/
|
||||
#ifndef _ECATDEF_H_
|
||||
#define _ECATDEF_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Hardware Platform Selection
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** EL9800_HW: 保留=1 以启用 el9800hw.h 接口(本项目重写该文件为 GD32 EXMC 版本)。 */
|
||||
#ifndef EL9800_HW
|
||||
#define EL9800_HW 1
|
||||
#endif
|
||||
|
||||
#ifndef MCI_HW
|
||||
#define MCI_HW 0
|
||||
#endif
|
||||
|
||||
#ifndef FC1100_HW
|
||||
#define FC1100_HW 0
|
||||
#endif
|
||||
|
||||
#ifndef TIESC_HW
|
||||
#define TIESC_HW 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Controller / ESC Access Width
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** GD32E508 是 32-bit MCU,但 EXMC 数据总线为 16-bit。
|
||||
* SSC 用 CONTROLLER_16BIT 决定 MEM_ADDR 类型和指针算术。
|
||||
* 与例程(STM32F407 SPI 16-bit)保持一致。 */
|
||||
#ifndef CONTROLLER_16BIT
|
||||
#define CONTROLLER_16BIT 1
|
||||
#endif
|
||||
|
||||
#ifndef CONTROLLER_32BIT
|
||||
#define CONTROLLER_32BIT 0
|
||||
#endif
|
||||
|
||||
/** ESC 16-bit 访问(HBI DATA 寄存器为 16-bit word)。 */
|
||||
#ifndef ESC_16BIT_ACCESS
|
||||
#define ESC_16BIT_ACCESS 1
|
||||
#endif
|
||||
|
||||
#ifndef ESC_32BIT_ACCESS
|
||||
#define ESC_32BIT_ACCESS 0
|
||||
#endif
|
||||
|
||||
/** 邮箱本地内存 16-bit 访问。 */
|
||||
#ifndef MBX_16BIT_ACCESS
|
||||
#define MBX_16BIT_ACCESS 1
|
||||
#endif
|
||||
|
||||
/** GD32E508 为 little-endian。 */
|
||||
#ifndef BIG_ENDIAN_16BIT
|
||||
#define BIG_ENDIAN_16BIT 0
|
||||
#endif
|
||||
|
||||
#ifndef BIG_ENDIAN_FORMAT
|
||||
#define BIG_ENDIAN_FORMAT 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Platform-specific (STM32 IO defines disabled)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _STM32_IO4
|
||||
#define _STM32_IO4 0
|
||||
#endif
|
||||
|
||||
#ifndef _STM32_IO8
|
||||
#define _STM32_IO8 0
|
||||
#endif
|
||||
|
||||
#ifndef EXT_DEBUGER_INTERFACE
|
||||
#define EXT_DEBUGER_INTERFACE 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ LED / EEPROM
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** LAN9252 支持 ESC 硬件驱动 Run/Error LED。 */
|
||||
#ifndef UC_SET_ECAT_LED
|
||||
#define UC_SET_ECAT_LED 0
|
||||
#endif
|
||||
|
||||
#ifndef ESC_SUPPORT_ECAT_LED
|
||||
#define ESC_SUPPORT_ECAT_LED 1
|
||||
#endif
|
||||
|
||||
/** 不使用 EEPROM 仿真(本项目有真实 AT24C32)。 */
|
||||
#ifndef ESC_EEPROM_EMULATION
|
||||
#define ESC_EEPROM_EMULATION 0
|
||||
#endif
|
||||
|
||||
/** AT24C32 = 4KB。 */
|
||||
#ifndef ESC_EEPROM_SIZE
|
||||
#define ESC_EEPROM_SIZE 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_READ_SIZE
|
||||
#define EEPROM_READ_SIZE 0x08
|
||||
#endif
|
||||
|
||||
#ifndef EEPROM_WRITE_SIZE
|
||||
#define EEPROM_WRITE_SIZE 0x2
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Interrupt / Timer / DC
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** AL Event 中断使能(ESC IRQ -> EXTI1)。 */
|
||||
#ifndef AL_EVENT_ENABLED
|
||||
#define AL_EVENT_ENABLED 1
|
||||
#endif
|
||||
|
||||
/** Distributed Clocks 支持(SYNC0 -> EXTI0)。
|
||||
* BOOT 侧关闭:邮箱-only,无过程数据 ⇒ 无 SYNC0/DC 语义。 */
|
||||
#ifndef DC_SUPPORTED
|
||||
#define DC_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
/** 看门狗由定时器中断检查。
|
||||
* 1.5.47:1→0。ECAT_TIMER_INT=1 要求端口提供调用 ECAT_CheckTimer() 的 1ms 硬件
|
||||
* 定时器 ISR——本工程从未实现,导致 ECAT_CheckTimer 在整个固件里零调用者:
|
||||
* (a) EsmTimeoutCounter 永不递减 → 任何 pending 状态迁移(SAFEOP→OP 等)永不
|
||||
* 超时,从站会无限期忽略一切 AL Control(实测"进 PREOP 失败: AL=SAFEOP
|
||||
* code=0x0000"的卡死就是它);
|
||||
* (b) DC_CheckWatchdog/PLL 计数器(i16WaitForPllRunningCnt)不前进 → 若 ESC
|
||||
* 残留 DC 激活位(0x0981,上个 DC 会话遗留、跨重连不清),从站按 DC 同步
|
||||
* 模式进 SAFEOP→OP,OP 等待 PLL 序列永远不满足 → SAFEOP+0x0000 永久挂起
|
||||
* (00:27 复测日志: 等待期轨迹恒 SAFEOP+0x0000,LRW 每 7ms 也救不回)。
|
||||
* 改 0 后 MainLoop 按 DWT CYCCNT 轮询(hw_gd32exmc.cpp 的 HW_GetTimerValue/
|
||||
* HW_ClearTimerValue 本就是为此写的死代码),每 1ms 调一次 ECAT_CheckTimer。 */
|
||||
#ifndef ECAT_TIMER_INT
|
||||
#define ECAT_TIMER_INT 0
|
||||
#endif
|
||||
|
||||
/** 1.5.47:DWT CYCCNT @180MHz 的每毫秒 tick 数(ECAT_TIMER_INT=0 时 MainLoop 用)。 */
|
||||
#ifndef ECAT_TIMER_INC_P_MS
|
||||
#define ECAT_TIMER_INC_P_MS 180000U
|
||||
#endif
|
||||
|
||||
#ifndef INTERRUPTS_SUPPORTED
|
||||
#define INTERRUPTS_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Application Selection
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** 不使用 SSC 自带测试/示例应用,本项目自写 dc_app.c。 */
|
||||
#ifndef TEST_APPLICATION
|
||||
#define TEST_APPLICATION 0
|
||||
#endif
|
||||
|
||||
#ifndef EL9800_APPLICATION
|
||||
#define EL9800_APPLICATION 0
|
||||
#endif
|
||||
|
||||
#ifndef CiA402_DEVICE
|
||||
#define CiA402_DEVICE 0
|
||||
#endif
|
||||
|
||||
#ifndef SAMPLE_APPLICATION
|
||||
#define SAMPLE_APPLICATION 0
|
||||
#endif
|
||||
|
||||
#ifndef SAMPLE_APPLICATION_INTERFACE
|
||||
#define SAMPLE_APPLICATION_INTERFACE 0
|
||||
#endif
|
||||
|
||||
/** 不使用 SSC 默认 main(),本项目有自己的 task 系统。 */
|
||||
#ifndef USE_DEFAULT_MAIN
|
||||
#define USE_DEFAULT_MAIN 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Mailbox / Protocol Selection
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef MAILBOX_QUEUE
|
||||
#define MAILBOX_QUEUE 1
|
||||
#endif
|
||||
|
||||
/** CoE (CANopen over EtherCAT) — SDO + PDO mapping。 */
|
||||
#ifndef COE_SUPPORTED
|
||||
#define COE_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#ifndef COMPLETE_ACCESS_SUPPORTED
|
||||
#define COMPLETE_ACCESS_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#ifndef SEGMENTED_SDO_SUPPORTED
|
||||
#define SEGMENTED_SDO_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#ifndef SDO_RES_INTERFACE
|
||||
#define SDO_RES_INTERFACE 1
|
||||
#endif
|
||||
|
||||
#ifndef USE_SINGLE_PDO_MAPPING_ENTRY_DESCR
|
||||
#define USE_SINGLE_PDO_MAPPING_ENTRY_DESCR 0
|
||||
#endif
|
||||
|
||||
#ifndef BACKUP_PARAMETER_SUPPORTED
|
||||
#define BACKUP_PARAMETER_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef STORE_BACKUP_PARAMETER_IMMEDIATELY
|
||||
#define STORE_BACKUP_PARAMETER_IMMEDIATELY 0
|
||||
#endif
|
||||
|
||||
/** 以下协议全部关闭。 */
|
||||
#ifndef AOE_SUPPORTED
|
||||
#define AOE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef DIAGNOSIS_SUPPORTED
|
||||
#define DIAGNOSIS_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef EMERGENCY_SUPPORTED
|
||||
#define EMERGENCY_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef VOE_SUPPORTED
|
||||
#define VOE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef SOE_SUPPORTED
|
||||
#define SOE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef EOE_SUPPORTED
|
||||
#define EOE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef STATIC_ETHERNET_BUFFER
|
||||
#define STATIC_ETHERNET_BUFFER 0
|
||||
#endif
|
||||
|
||||
#ifndef FOE_SUPPORTED
|
||||
#define FOE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifndef FOE_SAVE_FILES
|
||||
#define FOE_SAVE_FILES 0
|
||||
#endif
|
||||
|
||||
/** MAILBOX_SUPPORTED: CoE 开启则自动需要邮箱。 */
|
||||
#ifndef MAILBOX_SUPPORTED
|
||||
#define MAILBOX_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#ifndef BOOTSTRAPMODE_SUPPORTED
|
||||
#define BOOTSTRAPMODE_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ State Machine / Watchdog
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef OP_PD_REQUIRED
|
||||
#define OP_PD_REQUIRED 1
|
||||
#endif
|
||||
|
||||
/** PreOP 超时 2000ms。 */
|
||||
#ifndef PREOPTIMEOUT
|
||||
#define PREOPTIMEOUT 0x7D0
|
||||
#endif
|
||||
|
||||
/** SafeOP->OP 超时 9000ms。 */
|
||||
#ifndef SAFEOP2OPTIMEOUT
|
||||
#define SAFEOP2OPTIMEOUT 0x2328
|
||||
#endif
|
||||
|
||||
#ifndef EXPLICIT_DEVICE_ID
|
||||
#define EXPLICIT_DEVICE_ID 0
|
||||
#endif
|
||||
|
||||
/** LAN9252 支持 SM 看门狗。 */
|
||||
#ifndef ESC_SM_WD_SUPPORTED
|
||||
#define ESC_SM_WD_SUPPORTED 1
|
||||
#endif
|
||||
|
||||
#ifndef STATIC_OBJECT_DIC
|
||||
#define STATIC_OBJECT_DIC 0
|
||||
#endif
|
||||
|
||||
#ifndef ESC_EEPROM_ACCESS_SUPPORT
|
||||
#define ESC_EEPROM_ACCESS_SUPPORT 0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Compiler / Type Defines (ARM Cortex-M33, GCC/AC6)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef BOOL
|
||||
#define BOOL unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef UINT8
|
||||
#define UINT8 unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef UINT16
|
||||
#define UINT16 unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef UINT32
|
||||
#define UINT32 unsigned long
|
||||
#endif
|
||||
|
||||
#ifndef UINT64
|
||||
#define UINT64 unsigned long long
|
||||
#endif
|
||||
|
||||
#ifndef USHORT
|
||||
#define USHORT unsigned short
|
||||
#endif
|
||||
|
||||
#ifndef INT8
|
||||
#define INT8 char
|
||||
#endif
|
||||
|
||||
#ifndef INT16
|
||||
#define INT16 short
|
||||
#endif
|
||||
|
||||
#ifndef INT32
|
||||
#define INT32 long
|
||||
#endif
|
||||
|
||||
#ifndef CHAR
|
||||
#define CHAR char
|
||||
#endif
|
||||
|
||||
#ifndef UCHAR
|
||||
#define UCHAR unsigned char
|
||||
#endif
|
||||
|
||||
#ifndef SIZEOF
|
||||
#define SIZEOF(x) sizeof(x)
|
||||
#endif
|
||||
|
||||
/** MEM_ADDR: SSC 用于 ESC 内存访问的指针类型。
|
||||
* CONTROLLER_16BIT=1 时为 UINT16。 */
|
||||
#ifndef MEM_ADDR
|
||||
#define MEM_ADDR UINT16
|
||||
#endif
|
||||
|
||||
/** VARMEM: 动态变量内存类型修饰符(flat memory 留空)。 */
|
||||
#ifndef VARMEM
|
||||
#define VARMEM
|
||||
#endif
|
||||
|
||||
#ifndef VARMEMSET
|
||||
#define VARMEMSET memset
|
||||
#endif
|
||||
|
||||
#ifndef VARMEMCPY
|
||||
#define VARMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
/** GET_MEM_SIZE: 将字节数向上对齐到 MEM_ADDR 边界。 */
|
||||
#ifndef GET_MEM_SIZE
|
||||
#define GET_MEM_SIZE(ByteSize) (((ByteSize)+1) >> 1)
|
||||
#endif
|
||||
|
||||
/** GET_MEM_BYTES: 将 MEM_ADDR 单位转回字节数。 */
|
||||
#ifndef GET_MEM_BYTES
|
||||
#define GET_MEM_BYTES(MemSize) ((MemSize) << 1)
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Memory Model (flat memory, no near/far/huge)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef HUGE
|
||||
#define HUGE
|
||||
#endif
|
||||
|
||||
#ifndef HMEMSET
|
||||
#define HMEMSET memset
|
||||
#endif
|
||||
|
||||
#ifndef HMEMCPY
|
||||
#define HMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef HMEMCMP
|
||||
#define HMEMCMP memcmp
|
||||
#endif
|
||||
|
||||
#ifndef ESCMEM
|
||||
#define ESCMEM
|
||||
#endif
|
||||
|
||||
#ifndef ESCMEMCPY
|
||||
#define ESCMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef ESCMEMSET
|
||||
#define ESCMEMSET memset
|
||||
#endif
|
||||
|
||||
#ifndef ESCMBXMEMCPY
|
||||
#define ESCMBXMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef MBXMEM
|
||||
#define MBXMEM
|
||||
#endif
|
||||
|
||||
#ifndef MBXMEMCPY
|
||||
#define MBXMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef MBXMEMCMP
|
||||
#define MBXMEMCMP memcmp
|
||||
#endif
|
||||
|
||||
#ifndef MBXMEMSET
|
||||
#define MBXMEMSET memset
|
||||
#endif
|
||||
|
||||
#ifndef MBXSTRLEN
|
||||
#define MBXSTRLEN strlen
|
||||
#endif
|
||||
|
||||
#ifndef MBXSTRCPY
|
||||
#define MBXSTRCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef OBJCONST
|
||||
#define OBJCONST const
|
||||
#endif
|
||||
|
||||
#ifndef VARCONST
|
||||
#define VARCONST
|
||||
#endif
|
||||
|
||||
#ifndef VARVOLATILE
|
||||
#define VARVOLATILE volatile
|
||||
#endif
|
||||
|
||||
#ifndef OBJMEM
|
||||
#define OBJMEM
|
||||
#endif
|
||||
|
||||
#ifndef OBJTOMBXMEMCPY
|
||||
#define OBJTOMBXMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef OBJTOMBXSTRCPY
|
||||
#define OBJTOMBXSTRCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef OBJMEMCPY
|
||||
#define OBJMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef OBJSTRLEN
|
||||
#define OBJSTRLEN strlen
|
||||
#endif
|
||||
|
||||
#ifndef OBJSTRCPY
|
||||
#define OBJSTRCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef MAKE_HUGE_PTR
|
||||
#define MAKE_HUGE_PTR
|
||||
#endif
|
||||
|
||||
#ifndef MAKE_PTR_TO_ESC
|
||||
#define MAKE_PTR_TO_ESC
|
||||
#endif
|
||||
|
||||
#ifndef EMCYMEMCPY
|
||||
#define EMCYMEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef EMCYMEMSET
|
||||
#define EMCYMEMSET memset
|
||||
#endif
|
||||
|
||||
#ifndef EMCYMEM
|
||||
#define EMCYMEM
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ 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 __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef STRUCT_PACKED_END
|
||||
#define STRUCT_PACKED_END
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_START
|
||||
#define MBX_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef MBX_STRUCT_PACKED_END
|
||||
#define MBX_STRUCT_PACKED_END
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_START
|
||||
#define OBJ_STRUCT_PACKED_START __attribute__((packed))
|
||||
#endif
|
||||
|
||||
#ifndef OBJ_STRUCT_PACKED_END
|
||||
#define OBJ_STRUCT_PACKED_END
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Mailbox Size
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** 最小邮箱字节数(主站可设置的下限)。 */
|
||||
#ifndef MIN_MBX_SIZE
|
||||
#define MIN_MBX_SIZE 0x0022
|
||||
#endif
|
||||
|
||||
/** 最大邮箱字节数(主站可设置的上限)。 */
|
||||
#ifndef MAX_MBX_SIZE
|
||||
#define MAX_MBX_SIZE 0x0100
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ PDO Size (需与 DCU_ESI.xml / gen_eeprom.py / dc_app.c 三处一致)
|
||||
------
|
||||
------ 1.5.56 扩到 DPRAM 硬上限:64B/384B → 384B/640B。
|
||||
------ 输出 384B = 48×64bit:兼容单帧区 48B + CANFD 批量发送区 336B
|
||||
------ 输入 640B = 80×64bit:桥状态 32B + 电机窗口 288B + 长帧 40B + 批量接收区 280B
|
||||
------ 上限依据:邮箱占 0x1000-0x13FF,过程数据区仅 0x1400-0x1FFF=3072B,
|
||||
------ SM2/SM3 均 3-buffer 实占 3×size ⇒ out+in ≤ 1024B。384+640 恰好用满。
|
||||
------ 推导与实测(SII 余量/EXMC 耗时)见 comm/ethercat_pdo_size.h。
|
||||
------
|
||||
------ 这两个宏是**编译期硬门限**,必须先于其它改动生效:
|
||||
------ - ecatappl.c:157 用它们定义 PD 暂存区 aPdOutputData/aPdInputData
|
||||
------ - ecatslv.c:509/574 在 SM 校验时拿 SMLength 与之比较,超限直接返回
|
||||
------ ALSTATUSCODE_INVALIDSMINCFG/OUTCFG,表现为 PREOP→SAFEOP 失败
|
||||
------ 忘改这里会在进 SAFEOP 时报 AL 错误,而不是静默截断。
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** 最大输出 PDO 字节数(RxPDO,主站→从站)。BOOT:0x180 → 0x2(邮箱-only,无过程数据)。
|
||||
* 不能给 0:ecatappl.c:157 是 `UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)]`,
|
||||
* 0 会编出零长数组。给 2 ⇒ 1 个 UINT16,同时让 ecatslv.c:574 的 SM 校验
|
||||
* 对所有真实 PDO 配置超限,SAFEOP 天然进不去(我们只需要 PREOP)。 */
|
||||
#ifndef MAX_PD_OUTPUT_SIZE
|
||||
#define MAX_PD_OUTPUT_SIZE 0x2
|
||||
#endif
|
||||
|
||||
/** 最大输入 PDO 字节数(TxPDO,从站→主站)。BOOT:0x280 → 0x2,理由同上
|
||||
* (ecatslv.c:509 会拒绝任何 SMLength>2 的 SM3 配置)。 */
|
||||
#ifndef MAX_PD_INPUT_SIZE
|
||||
#define MAX_PD_INPUT_SIZE 0x2
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Device Identity (CoE Object 0x1018)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef VENDOR_ID
|
||||
#define VENDOR_ID 0x00000000
|
||||
#endif
|
||||
|
||||
#ifndef PRODUCT_CODE
|
||||
#define PRODUCT_CODE 0x00009252
|
||||
#endif
|
||||
|
||||
#ifndef REVISION_NUMBER
|
||||
#define REVISION_NUMBER 0x00010000
|
||||
#endif
|
||||
|
||||
#ifndef SERIAL_NUMBER
|
||||
#define SERIAL_NUMBER 0x00000001
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Device Information (CoE Objects 0x1000/0x1008/0x1009/0x100A)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DEVICE_PROFILE_TYPE
|
||||
#define DEVICE_PROFILE_TYPE 0x00000000
|
||||
#endif
|
||||
|
||||
#ifndef DEVICE_NAME
|
||||
#define DEVICE_NAME "GD32E508 DCU BOOT"
|
||||
#endif
|
||||
|
||||
#ifndef DEVICE_NAME_LEN
|
||||
#define DEVICE_NAME_LEN 0x12
|
||||
#endif
|
||||
|
||||
#ifndef DEVICE_HW_VERSION
|
||||
#define DEVICE_HW_VERSION "1.0"
|
||||
#endif
|
||||
|
||||
#ifndef DEVICE_HW_VERSION_LEN
|
||||
#define DEVICE_HW_VERSION_LEN 0x3
|
||||
#endif
|
||||
|
||||
#ifndef DEVICE_SW_VERSION
|
||||
#define DEVICE_SW_VERSION "1.0"
|
||||
#endif
|
||||
|
||||
/* 0x100A 的位长。SSC 默认 3 字符只装得下占位 "1.0";这里抬到 16,真实值由
|
||||
* main.cpp 起板时 snprintf 覆盖("1.1.0.12"),主站按首个 NUL 截断后回显。
|
||||
* 这是唯一不靠串口/ESP32 中继就能知道"对面到底哪一版"的总线读数。 */
|
||||
#ifndef DEVICE_SW_VERSION_LEN
|
||||
#define DEVICE_SW_VERSION_LEN 0x10
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ ESC Memory Address Ranges (SM buffer allocation boundaries)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef MIN_PD_WRITE_ADDRESS
|
||||
#define MIN_PD_WRITE_ADDRESS 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PD_WRITE_ADDRESS
|
||||
#define MAX_PD_WRITE_ADDRESS 0x1FFF
|
||||
#endif
|
||||
|
||||
#ifndef MIN_PD_READ_ADDRESS
|
||||
#define MIN_PD_READ_ADDRESS 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PD_READ_ADDRESS
|
||||
#define MAX_PD_READ_ADDRESS 0x1FFF
|
||||
#endif
|
||||
|
||||
#ifndef MIN_MBX_WRITE_ADDRESS
|
||||
#define MIN_MBX_WRITE_ADDRESS 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef MAX_MBX_WRITE_ADDRESS
|
||||
#define MAX_MBX_WRITE_ADDRESS 0x1FFF
|
||||
#endif
|
||||
|
||||
#ifndef MIN_MBX_READ_ADDRESS
|
||||
#define MIN_MBX_READ_ADDRESS 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef MAX_MBX_READ_ADDRESS
|
||||
#define MAX_MBX_READ_ADDRESS 0x1FFF
|
||||
#endif
|
||||
|
||||
/* 1.5.59 根治邮箱楔死:本板邮箱几何构建期常量。
|
||||
* 取值 = gen_eeprom.py 的 MBX_RX_OFFSET/SIZE(0x1000/256) 与 MBX_TX_OFFSET/SIZE
|
||||
* (0x1200/256),即 DCU_ESI.xml <Sm> 与 ESC 上电自 SII 加载的寄存器真值;主站
|
||||
* config_init 也只是照抄同一份 SII。MBX_StartMailboxHandler 以此直达缓存,
|
||||
* 不再信任单次 CSR 间接读(错值曾以 0x1204/0x1208 进入缓存 → 所有 SDO 响应
|
||||
* 写错址 → 断电才解的楔死),CSR 读仅作交叉校验(mailbox.c 打 WARN)。
|
||||
* ★ 改 SII 邮箱几何必须同步改这里(gen_eeprom.py 顶部注释同样有约束)。★ */
|
||||
#ifndef MBX_SM0_START
|
||||
#define MBX_SM0_START 0x1000
|
||||
#endif
|
||||
|
||||
#ifndef MBX_SM0_SIZE
|
||||
#define MBX_SM0_SIZE 0x0100
|
||||
#endif
|
||||
|
||||
#ifndef MBX_SM1_START
|
||||
#define MBX_SM1_START 0x1200
|
||||
#endif
|
||||
|
||||
#ifndef MBX_SM1_SIZE
|
||||
#define MBX_SM1_SIZE 0x0100
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ PDO Timing (DC sync parameters, unit: ns)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef MIN_PD_CYCLE_TIME
|
||||
#define MIN_PD_CYCLE_TIME 0x186A0
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PD_CYCLE_TIME
|
||||
#define MAX_PD_CYCLE_TIME 0xC3500000
|
||||
#endif
|
||||
|
||||
#ifndef PD_OUTPUT_CALC_AND_COPY_TIME
|
||||
#define PD_OUTPUT_CALC_AND_COPY_TIME 0x0
|
||||
#endif
|
||||
|
||||
#ifndef PD_OUTPUT_DELAY_TIME
|
||||
#define PD_OUTPUT_DELAY_TIME 0x0
|
||||
#endif
|
||||
|
||||
#ifndef PD_INPUT_CALC_AND_COPY_TIME
|
||||
#define PD_INPUT_CALC_AND_COPY_TIME 0x0
|
||||
#endif
|
||||
|
||||
#ifndef PD_INPUT_DELAY_TIME
|
||||
#define PD_INPUT_DELAY_TIME 0x0
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Dynamic Memory
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FREEMEM
|
||||
#define FREEMEM(pointer) free((pointer))
|
||||
#endif
|
||||
|
||||
#ifndef GETMEM
|
||||
#define GETMEM(size) malloc((size))
|
||||
#endif
|
||||
|
||||
#ifndef APPL_AllocMailboxBuffer
|
||||
#define APPL_AllocMailboxBuffer(size) malloc((size))
|
||||
#endif
|
||||
|
||||
#ifndef APPL_FreeMailboxBuffer
|
||||
#define APPL_FreeMailboxBuffer(pointer) free((pointer))
|
||||
#endif
|
||||
|
||||
#ifndef MEMCPY
|
||||
#define MEMCPY memcpy
|
||||
#endif
|
||||
|
||||
#ifndef ALLOCMEM
|
||||
#define ALLOCMEM(size) malloc((size))
|
||||
#endif
|
||||
|
||||
#endif /* _ECATDEF_H_ */
|
||||
@@ -0,0 +1,711 @@
|
||||
/**
|
||||
\addtogroup EcatAppl EtherCAT application
|
||||
@{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
\file ecatappl.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains the Process Data interface
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10.1:<br>
|
||||
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
|
||||
V5.11 ECAT1: update EEPROM access reset operation<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT11: create application interface function pointer, add eeprom emulation interface functions<br>
|
||||
V5.11 ECAT2: update EEPROM access retry cycle (add 10ms delay between two retry cycles)<br>
|
||||
V5.11 ECAT3: handle bus cycle calculation for input/output only devices and create warning diag message only if calculation failed<br>
|
||||
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
|
||||
V5.11 ECAT6: add function to calculate bus cycle time<br>
|
||||
V5.11 ECAT8: call PDO_InputMapping only once if DC is enabled and COE is not supported<br>
|
||||
V5.11 EEPROM1: fix compiler error during pEEPROM pointer initialization<br>
|
||||
V5.11 EEPROM2: write Station alias value to EEPROM data register on EEPROM reload command<br>
|
||||
V5.11 EEPROM3: clear EEPROM error bits<br>
|
||||
V5.11 EEPROM4: prevent the variable in the EEPROM busy loop to be removed by the compiler<br>
|
||||
V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 ECAT1: Correct calculation of blinking and flashing sequence<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
V5.10 ECAT2: Prevent EEPROM data null pointer access (if the pointer is null an command error is set)<br>
|
||||
EEPROM emulation return command error if unknown command was received<br>
|
||||
V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)<br>
|
||||
Bugfix calculate LED blink frequency<br>
|
||||
V5.10 ECAT7: Add "bInitFinished" to indicate if the initialization is complete<br>
|
||||
V5.10 HW2: Change HW_GetTimer return value to UINT32<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 APPL3: Include library demo application<br>
|
||||
V5.01 ESC1: Change ESC access function (if EEPROM Emulation is active)<br>
|
||||
V5.01 ESC2: Add missed value swapping<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 TEST1: Add test application. See Application Note ET9300 for more details.<br>
|
||||
V5.0 ECAT2: Application specific functions are moved to application files.<br>
|
||||
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
|
||||
V5.0 ESC1: ESC 32Bit Access added.<br>
|
||||
V5.0 ESC3: Add EEPROM emulation support.<br>
|
||||
V5.0 ESM3: Handling pending ESM transitions.<br>
|
||||
V5.0 ESC5: Enhance EEPROM access handling.<br>
|
||||
V5.0 PDO1: AL Event flags are not rechecked in PDO_OutputMappping(). (Already checked before call function)<br>
|
||||
V5.0 SYNC1: Add missed SM event indication (0x1C32/0x1C33 SI11).<br>
|
||||
<br>Changes to version V4.30:<br>
|
||||
V4.40 DIAG1: add diagnosis message support<br>
|
||||
V4.40 PDO1: merge content of HW_InputMapping (spihw.c/mcihw.c) to PDO_InputMapping. merge content of HW_OutputMapping (spihw.c/mcihw.c) to PDO_OutputMapping.<br>
|
||||
V4.40 PDO2: Generic process data length calculation<br>
|
||||
V4.40 ECAT2: call cyclic CheckIfLocalError() to check the local flags<br>
|
||||
V4.40 HW0: Generic hardware access functions. Add Function (PDI_Isr()), content merged from spihw.c and mcihw.c.<br>
|
||||
V4.40 WD1: define (ESC_SM_WD_SUPPORTED) to choose ESC SyncManager watchdog or local watchdog<br>
|
||||
V4.40 ESM2: Change state transition behaviour from SafeOP to OP<br>
|
||||
V4.40 TIMER1: Change bus cycle time calculation and trigger of ECAT_CheckTimer() if ECAT_TIMER_INT is reset<br>
|
||||
V4.40 HW1: Add support for fc1100 hardware<br>
|
||||
<br>Changes to version V4.20:<br>
|
||||
V4.30 EL9800: EL9800_x cyclic application is moved to el9800.c<br>
|
||||
V4.30 OBJ 3: add object dictionary initialization<br>
|
||||
V4.30 SYNC: add CalcSMCycleTime() (calculation of bus cycle time); change synchronisation control functions<br>
|
||||
V4.30 PDO: include PDO specific functions (moved from coeappl.c).<br>
|
||||
xxx_InputMapping(); xxx_OutputMapping(); xxx_ReadInputs(); xxx_ResetOutputs(); xxx_Application()<br>
|
||||
V4.30 CiA402: Add CiA402_StateMachine() and CiA402_Application() call<br>
|
||||
V4.20 DC 1: Add DC pending Statemachine handling<br>
|
||||
V4.20 PIC24: Add EL9800_4 (PIC24) required source code<br>
|
||||
V4.20 LED 1: Modified LED Handling<br>
|
||||
V4.11 APPL 1: The checkWatchdog() function should not called in checkTimer() if this function is triggered by an Interrupt<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.10 LED 1: The handling of the EtherCAT-Error-LED was added<br>
|
||||
V4.10 AOE 3: The AoE fragment size has to be initialized during the state transition<br>
|
||||
from INIT to PREOP<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.08 LED 1: The handling of the EtherCAT-LED can be (de-)selected by the switch LEDS_SUPPORTED<br>
|
||||
because the ET1100 and ET1200 have an output port which could be connected directly.<br>
|
||||
<br>Changes to version V4.01:<br>
|
||||
V4.02 ECAT 1: The watchdog timer variables shall be initialized.<br>
|
||||
<br>Changes to version V4.00:<br>
|
||||
V4.01 APPL 1: If the application is running in synchron mode and no SM event<br>
|
||||
is received, the application should be called from the main loop<br>
|
||||
V4.01 APPL 2: In FreeRun mode the output should only be copied if the slave is in OP<br>
|
||||
<br>Changes to version V3.20:<br>
|
||||
V4.00 APPL 1: The watchdog checking should be done by a microcontroller<br>
|
||||
timer because the watchdog trigger of the ESC will be reset too<br>
|
||||
if only a part of the sync manager data is written<br>
|
||||
V4.00 APPL 2: The setting of EtherCAT state LEDs were included<br>
|
||||
V4.00 APPL 3: The outputs should be reset to a safe state,<br>
|
||||
when the state OP is left<br>
|
||||
V4.00 APPL 4: An example for the EEPROM access through the ESC is shown in<br>
|
||||
the function APPL_StartMailboxHandler<br>
|
||||
V4.00 APPL 5: The inputs should be read once when the state transition<br>
|
||||
from PREOP to SAFEOP is made<br>
|
||||
V4.00 APPL 6: The main function was split in MainInit and MainLoop
|
||||
*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "ecatslv.h"
|
||||
|
||||
#define _ECATAPPL_ 1
|
||||
#include "ecatappl.h"
|
||||
#undef _ECATAPPL_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _ECATAPPL_ (#ifdef is used in ecatappl.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#include "coeappl.h"
|
||||
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT11*/
|
||||
#define _APPL_INTERFACE_ 1
|
||||
#include "applInterface.h"
|
||||
#undef _APPL_INTERFACE_
|
||||
/* ECATCHANGE_END(V5.11) ECAT11*/
|
||||
|
||||
#include "el9800appl.h"
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local Types and Defines
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef ECAT_TIMER_INC_P_MS
|
||||
/**
|
||||
* \todo Define the timer ticks per ms
|
||||
*/
|
||||
#warning "Define the timer ticks per ms"
|
||||
#endif /* #ifndef ECAT_TIMER_INC_P_MS */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local variables and constants
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/*variables only required to calculate values for SM Synchronisation objects (0x1C3x)*/
|
||||
UINT16 u16BusCycleCntMs; //used to calculate the bus cycle time in Ms
|
||||
UINT32 StartTimerCnt; //variable to store the timer register value when get cycle time was triggered
|
||||
BOOL bCycleTimeMeasurementStarted; // indicates if the bus cycle measurement is started
|
||||
|
||||
UINT16 aPdOutputData[(MAX_PD_OUTPUT_SIZE>>1)];
|
||||
UINT16 aPdInputData[(MAX_PD_INPUT_SIZE>>1)];
|
||||
|
||||
/*ECAT_DBG_START(1.6.0 临时剖分埋点)*/
|
||||
/* 1.6.0 负载归因埋点:DWT 周期累加器,经 0x2000 sub5..8 上报,主站 1s 差分出速率。
|
||||
* 只为定位 free-run 模式下每周期 508µs 计入时间的真实去向;定位完成后整块移除。
|
||||
* EcatDbg_Dwt() 定义在 comm/ethercat_sync.cpp (extern "C")。 */
|
||||
extern UINT32 EcatDbg_Dwt(void);
|
||||
#define ecatdbg_dwt() EcatDbg_Dwt()
|
||||
/* 1.6.0:输出探针(实现见 comm/ethercat_pdo.cpp,C 链接) */
|
||||
extern UINT8 EcatPdo_OutputProbe(UINT8 const *head);
|
||||
/* 1.6.22:稀疏写 ESC 计划(密排 count 只写有效段; 实现见 ethercat_pdo.cpp) */
|
||||
extern UINT8 EcatPdo_SparseEscPlan(UINT16 *off0, UINT16 *len0, UINT16 *off1, UINT16 *len1);
|
||||
/* 1.6.25:差分写 ESC 多段计划(段头/段尾 + 3-buffer 冲刷; 实现见 ethercat_pdo.cpp) */
|
||||
extern UINT8 EcatPdo_EscWritePlan(UINT16 *offs, UINT16 *lens, UINT8 maxSegs);
|
||||
volatile UINT32 g_dbg_pdiCyc = 0; /* sub5: PDI_Isr 全函数累计周期 */
|
||||
volatile UINT32 g_dbg_outCyc = 0; /* sub6: PDO_OutputMapping(输出探针/全量读) */
|
||||
volatile UINT32 g_dbg_freerunCyc = 0; /* sub7: MainLoop free-run 分支(SM 同步下恒 0) */
|
||||
volatile UINT32 g_dbg_pdiCnt = 0; /* sub8: PDI_Isr 触发次数(=每周期 1 次) */
|
||||
volatile UINT32 g_dbg_inCyc = 0; /* sub9: PDO_InputMapping 全程(APPL+640B 写) */
|
||||
volatile UINT32 g_dbg_inWriteCyc = 0; /* sub10: 其中 HW_EscWriteIsr 640B 过程数据写 */
|
||||
volatile UINT32 g_dbg_alCyc = 0; /* sub11: PDI_Isr 内 AL 事件读×2+超限应答 */
|
||||
volatile UINT32 g_dbg_appCyc = 0; /* sub12: ECAT_Application(桥 processOutput) */
|
||||
/*ECAT_DBG_END*/
|
||||
|
||||
/*variables are declared in ecatslv.c*/
|
||||
extern VARVOLATILE UINT16 u16dummy;
|
||||
BOOL bInitFinished = FALSE; /** < \brief indicates if the initialization is finished*/
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ local functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function will copies the inputs from the local memory to the ESC memory
|
||||
to the hardware
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void PDO_InputMapping(void)
|
||||
{
|
||||
/*ECAT_DBG(1.6.0 剖分:输入映射全程 + 640B 写细分)*/
|
||||
/* 1.6.22 稀疏写:APPL 仍整填 aPdInputData(对齐 SDO 0x6000),但 HW_EscWriteIsr
|
||||
* 只写密排有效段+段3/5,跳过空槽区 —— 省的是 PRAM 字节时间,不是 RAM 拷贝。
|
||||
* 1.6.25 差分写:在稀疏之上再跳过「与影子一致且 3-buffer 冲刷完成」的段。 */
|
||||
const UINT32 dbgT0 = ecatdbg_dwt();
|
||||
APPL_InputMapping((UINT16*)aPdInputData);
|
||||
const UINT32 dbgT1 = ecatdbg_dwt();
|
||||
{
|
||||
UINT16 offs[4], lens[4];
|
||||
const UINT8 nseg = EcatPdo_EscWritePlan(offs, lens, 4U);
|
||||
if (nseg == 0U) {
|
||||
HW_EscWriteIsr(((MEM_ADDR *) aPdInputData), nEscAddrInputData, nPdInputSize );
|
||||
} else {
|
||||
for (UINT8 i = 0U; i < nseg; ++i) {
|
||||
HW_EscWriteIsr(((MEM_ADDR *)((UINT8 *)aPdInputData + offs[i])),
|
||||
(UINT16)(nEscAddrInputData + offs[i]), lens[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_dbg_inWriteCyc += ecatdbg_dwt() - dbgT1;
|
||||
g_dbg_inCyc += ecatdbg_dwt() - dbgT0;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function will copies the outputs from the ESC memory to the local memory
|
||||
to the hardware. This function is only called in case of an SM2
|
||||
(output process data) event.
|
||||
|
||||
1.6.0 输出按需全量读(负载优化):每周期先读输出 PDO 头 12B 探针(覆盖 SEQ@0
|
||||
与 WIN@8),经 EcatPdo_OutputProbe 判定——seq 无变化沿且无清统计请求时,
|
||||
跳过 384B 全量读(只补读 SM2 末字完成事件应答),省掉每周期 ~190µs 的
|
||||
PRAM 搬运(含 ESC 核仲裁等待)。主站滚动窗口扫槽依赖的 WIN 字节在探针内
|
||||
每周期刷新,窗口扫描不受影响。SM2 应答语义不变:首字(探针内)+末字
|
||||
(两分支各自覆盖)。
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void PDO_OutputMapping(void)
|
||||
{
|
||||
/*ECAT_DBG(1.6.0 剖分:输出映射全程)*/
|
||||
const UINT32 dbgT0 = ecatdbg_dwt();
|
||||
|
||||
/* 1.6.0:输出按需全量读——探针 12B 判定是否真的有新输出 */
|
||||
UINT8 head[12];
|
||||
HW_EscReadIsr(((MEM_ADDR *) head), nEscAddrOutputData, 12U );
|
||||
|
||||
if ( EcatPdo_OutputProbe(head) )
|
||||
{
|
||||
HW_EscReadIsr(((MEM_ADDR *)aPdOutputData), nEscAddrOutputData, nPdOutputSize );
|
||||
|
||||
APPL_OutputMapping((UINT16*) aPdOutputData);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 无新输出:补读 SM2 末字完成事件应答(首字已随探针读过) */
|
||||
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
|
||||
}
|
||||
g_dbg_outCyc += ecatdbg_dwt() - dbgT0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function shall be called every 1ms.
|
||||
\brief If the switch ECAT_TIMER_INT is 0, the watchdog control is implemented without using
|
||||
\brief interrupts. In this case a local timer register is checked every ECAT_Main cycle
|
||||
\brief and the function is triggered if 1 ms is elapsed
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ECAT_CheckTimer(void)
|
||||
{
|
||||
if(sSyncManOutPar.u32CycleTime == 0)
|
||||
{
|
||||
u16BusCycleCntMs++;
|
||||
}
|
||||
|
||||
/*decrement the state transition timeout counter*/
|
||||
if(bEcatWaitForAlControlRes && (EsmTimeoutCounter > 0))
|
||||
{
|
||||
EsmTimeoutCounter--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DC_CheckWatchdog();
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT6*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called from the PDI_Isr and is used to calculate the bus cycle time
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void HandleBusCycleCalculation(void)
|
||||
{
|
||||
/*calculate the cycle time if device is in SM Sync mode and Cycle time was not calculated yet*/
|
||||
if ( !bDcSyncActive && bEscIntEnabled)
|
||||
{
|
||||
BOOL bTiggerCalcCycleTime = FALSE;
|
||||
|
||||
if(sSyncManOutPar.u16GetCycleTime == 1)
|
||||
bTiggerCalcCycleTime = TRUE;
|
||||
if(bTiggerCalcCycleTime)
|
||||
{
|
||||
/*get bus cycle time triggered */
|
||||
sSyncManOutPar.u32CycleTime = 0;
|
||||
sSyncManOutPar.u16GetCycleTime = 0;
|
||||
|
||||
sSyncManInPar.u32CycleTime = 0;
|
||||
sSyncManInPar.u16GetCycleTime = 0;
|
||||
|
||||
u16BusCycleCntMs = 0;
|
||||
bCycleTimeMeasurementStarted = TRUE;
|
||||
StartTimerCnt = (UINT32) HW_GetTimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if(bCycleTimeMeasurementStarted == TRUE)
|
||||
{
|
||||
UINT32 CurTimerCnt = (UINT32)HW_GetTimer();
|
||||
/*ECATCHANGE_START(V5.11) ECAT3*/
|
||||
UINT32 CalcCycleTime = 0;
|
||||
|
||||
|
||||
#if ECAT_TIMER_INC_P_MS
|
||||
CalcCycleTime = (UINT32)u16BusCycleCntMs * 1000000 + (((INT32)(CurTimerCnt-StartTimerCnt))*1000000/ECAT_TIMER_INC_P_MS); //get elapsed cycle time in ns
|
||||
#endif
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
sSyncManOutPar.u32CycleTime = CalcCycleTime;
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
sSyncManInPar.u32CycleTime = CalcCycleTime;
|
||||
u16BusCycleCntMs = 0;
|
||||
StartTimerCnt = 0;
|
||||
bCycleTimeMeasurementStarted = FALSE;
|
||||
|
||||
/*ECATCHANGE_END(V5.11) ECAT3*/
|
||||
/* CiA402 Motion controller cycle time is only set if DC Synchronisation is active*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*ECATCHANGE_END(V5.11) ECAT6*/
|
||||
|
||||
void PDI_Isr(void)
|
||||
{
|
||||
/*ECAT_DBG(1.6.0 剖分:ISR 全程)*/
|
||||
const UINT32 dbgT0 = ecatdbg_dwt();
|
||||
if(bEscIntEnabled)
|
||||
{
|
||||
/* get the AL event register */
|
||||
const UINT32 dbgTa = ecatdbg_dwt();
|
||||
UINT16 ALEvent = HW_GetALEventRegister_Isr();
|
||||
g_dbg_alCyc += ecatdbg_dwt() - dbgTa;
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
if(bDcRunning && bDcSyncActive)
|
||||
{
|
||||
/* Reset SM/Sync0 counter. Will be incremented on every Sync0 event*/
|
||||
u16SmSync0Counter = 0;
|
||||
}
|
||||
if(sSyncManOutPar.u16SmEventMissedCounter > 0)
|
||||
sSyncManOutPar.u16SmEventMissedCounter--;
|
||||
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT6*/
|
||||
//calculate the bus cycle time if required
|
||||
HandleBusCycleCalculation();
|
||||
/*ECATCHANGE_END(V5.11) ECAT6*/
|
||||
|
||||
/* Outputs were updated, set flag for watchdog monitoring */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
|
||||
|
||||
/*
|
||||
handle output process data event
|
||||
*/
|
||||
if ( bEcatOutputUpdateRunning )
|
||||
{
|
||||
/* slave is in OP, update the outputs */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Just acknowledge the process data event in the INIT,PreOP and SafeOP state */
|
||||
HW_EscReadWordIsr(u16dummy,nEscAddrOutputData);
|
||||
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
|
||||
}
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
if (( ALEvent & PROCESS_INPUT_EVENT ) && (nPdOutputSize == 0))
|
||||
{
|
||||
//calculate the bus cycle time if required
|
||||
HandleBusCycleCalculation();
|
||||
}
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
/*
|
||||
Call ECAT_Application() in SM Sync mode
|
||||
*/
|
||||
if (sSyncManOutPar.u16SyncType == SYNCTYPE_SM_SYNCHRON)
|
||||
{
|
||||
/* The Application is synchronized to process data Sync Manager event*/
|
||||
const UINT32 dbgTb = ecatdbg_dwt();
|
||||
ECAT_Application();
|
||||
g_dbg_appCyc += ecatdbg_dwt() - dbgTb;
|
||||
}
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
/*ECATCHANGE_START(V5.11) ESM7*/
|
||||
&& ((sSyncManInPar.u16SyncType == SYNCTYPE_SM_SYNCHRON) || (sSyncManInPar.u16SyncType == SYNCTYPE_SM2_SYNCHRON))
|
||||
/*ECATCHANGE_END(V5.11) ESM7*/
|
||||
)
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
const UINT32 dbgTc = ecatdbg_dwt();
|
||||
PDO_InputMapping();
|
||||
g_dbg_inCyc += ecatdbg_dwt() - dbgTc;
|
||||
}
|
||||
|
||||
/*
|
||||
Check if cycle exceed
|
||||
*/
|
||||
/*if next SM event was triggered during runtime increment cycle exceed counter*/
|
||||
const UINT32 dbgTd = ecatdbg_dwt();
|
||||
ALEvent = HW_GetALEventRegister_Isr();
|
||||
g_dbg_alCyc += ecatdbg_dwt() - dbgTd;
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
sSyncManOutPar.u16CycleExceededCounter++;
|
||||
sSyncManInPar.u16CycleExceededCounter = sSyncManOutPar.u16CycleExceededCounter;
|
||||
|
||||
/* Acknowledge the process data event*/
|
||||
HW_EscReadWordIsr(u16dummy,nEscAddrOutputData);
|
||||
HW_EscReadWordIsr(u16dummy,(nEscAddrOutputData+nPdOutputSize-2));
|
||||
}
|
||||
} //if(bEscIntEnabled)
|
||||
/*ECAT_DBG(1.6.0 剖分:ISR 全程 收尾)*/
|
||||
g_dbg_pdiCyc += ecatdbg_dwt() - dbgT0;
|
||||
g_dbg_pdiCnt++;
|
||||
}
|
||||
|
||||
void Sync0_Isr(void)
|
||||
{
|
||||
|
||||
Sync0WdCounter = 0;
|
||||
|
||||
if(bDcSyncActive)
|
||||
{
|
||||
|
||||
if ( bEcatInputUpdateRunning )
|
||||
{
|
||||
LatchInputSync0Counter++;
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
if(u16SmSync0Value > 0)
|
||||
{
|
||||
/* Check if Sm-Sync sequence is invalid */
|
||||
if (u16SmSync0Counter > u16SmSync0Value)
|
||||
{
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
if ((nPdOutputSize > 0) && (sSyncManOutPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit))
|
||||
{
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
sSyncManOutPar.u16SmEventMissedCounter = sSyncManOutPar.u16SmEventMissedCounter + 3;
|
||||
}
|
||||
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
if ((nPdInputSize > 0) && (nPdOutputSize == 0) && (sSyncManInPar.u16SmEventMissedCounter <= sErrorSettings.u16SyncErrorCounterLimit))
|
||||
{
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
sSyncManInPar.u16SmEventMissedCounter = sSyncManInPar.u16SmEventMissedCounter + 3;
|
||||
}
|
||||
|
||||
} // if (u16SmSync0Counter > u16SmSync0Value)
|
||||
|
||||
|
||||
if ((nPdOutputSize == 0) && (nPdInputSize > 0))
|
||||
{
|
||||
/* Input only with DC, check if the last input data was read*/
|
||||
UINT16 ALEvent = HW_GetALEventRegister_Isr();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ((ALEvent & PROCESS_INPUT_EVENT) == 0)
|
||||
{
|
||||
/* no input data was read by the master, increment the sm missed counter*/
|
||||
u16SmSync0Counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset SM/Sync0 counter*/
|
||||
u16SmSync0Counter = 0;
|
||||
|
||||
sSyncManInPar.u16SmEventMissedCounter = 0;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u16SmSync0Counter++;
|
||||
}
|
||||
}//SM -Sync monitoring enabled
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
|
||||
if(!bEscIntEnabled && bEcatOutputUpdateRunning)
|
||||
{
|
||||
/* Output mapping was not done by the PDI ISR */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
|
||||
/* Application is synchronized to SYNC0 event*/
|
||||
ECAT_Application();
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
&& (LatchInputSync0Value > 0) && (LatchInputSync0Value == LatchInputSync0Counter) ) /* Inputs shall be latched on a specific Sync0 event */
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
|
||||
if(LatchInputSync0Value == 1)
|
||||
{
|
||||
/* if inputs are latched on every Sync0 event (otherwise the counter is reset on the next Sync1 event) */
|
||||
LatchInputSync0Counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sync1_Isr(void)
|
||||
{
|
||||
Sync1WdCounter = 0;
|
||||
|
||||
if ( bEcatInputUpdateRunning
|
||||
&& (sSyncManInPar.u16SyncType == SYNCTYPE_DCSYNC1)
|
||||
&& (LatchInputSync0Value == 0)) /* Inputs are latched on Sync1 (LatchInputSync0Value == 0), if LatchInputSync0Value > 0 inputs are latched with Sync0 */
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
}
|
||||
|
||||
/* Reset Sync0 latch counter (to start next Sync0 latch cycle) */
|
||||
LatchInputSync0Counter = 0;
|
||||
}
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function shall called within a 1ms cycle.
|
||||
Set Run and Error Led depending on the Led state
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pObjectDictionary Pointer to application specific object dictionary.
|
||||
NULL if no specific object are available.
|
||||
\return 0 if initialization was successful
|
||||
|
||||
\brief This function initialize the EtherCAT Sample Code
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT16 MainInit(void)
|
||||
{
|
||||
UINT16 Error = 0;
|
||||
/*Hardware init function need to be called from the application layer*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) EEPROM1*/
|
||||
#ifdef SET_EEPROM_PTR
|
||||
SET_EEPROM_PTR
|
||||
#endif
|
||||
/*ECATCHANGE_END(V5.11) EEPROM1*/
|
||||
|
||||
/* initialize the EtherCAT Slave Interface */
|
||||
ECAT_Init();
|
||||
/* initialize the objects */
|
||||
COE_ObjInit();
|
||||
|
||||
|
||||
/*Timer initialization*/
|
||||
u16BusCycleCntMs = 0;
|
||||
StartTimerCnt = 0;
|
||||
bCycleTimeMeasurementStarted = FALSE;
|
||||
|
||||
/*indicate that the slave stack initialization finished*/
|
||||
bInitFinished = TRUE;
|
||||
|
||||
/*Application Init need to be called from the application layer*/
|
||||
return Error;
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function shall be called cyclically from main
|
||||
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MainLoop(void)
|
||||
{
|
||||
/*return if initialization not finished */
|
||||
if(bInitFinished == FALSE)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
/* FreeRun-Mode: bEscIntEnabled = FALSE, bDcSyncActive = FALSE
|
||||
Synchron-Mode: bEscIntEnabled = TRUE, bDcSyncActive = FALSE
|
||||
DC-Mode: bEscIntEnabled = TRUE, bDcSyncActive = TRUE */
|
||||
if (
|
||||
(!bEscIntEnabled || !bEcatFirstOutputsReceived) /* SM-Synchronous, but not SM-event received */
|
||||
&& !bDcSyncActive /* DC-Synchronous */
|
||||
)
|
||||
{
|
||||
/* if the application is running in ECAT Synchron Mode the function ECAT_Application is called
|
||||
from the ESC interrupt routine (in mcihw.c or spihw.c),
|
||||
in ECAT Synchron Mode it should be additionally checked, if the SM-event is received
|
||||
at least once (bEcatFirstOutputsReceived = 1), otherwise no interrupt is generated
|
||||
and the function ECAT_Application has to be called here (with interrupts disabled,
|
||||
because the SM-event could be generated while executing ECAT_Application) */
|
||||
if ( !bEscIntEnabled )
|
||||
{
|
||||
/* application is running in ECAT FreeRun Mode,
|
||||
first we have to check, if outputs were received */
|
||||
UINT16 ALEvent = HW_GetALEventRegister();
|
||||
ALEvent = SWAPWORD(ALEvent);
|
||||
|
||||
if ( ALEvent & PROCESS_OUTPUT_EVENT )
|
||||
{
|
||||
/* set the flag for the state machine behaviour */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
if ( bEcatOutputUpdateRunning )
|
||||
{
|
||||
/* update the outputs */
|
||||
PDO_OutputMapping();
|
||||
}
|
||||
}
|
||||
else if ( nPdOutputSize == 0 )
|
||||
{
|
||||
/* if no outputs are transmitted, the watchdog must be reset, when the inputs were read */
|
||||
if ( ALEvent & PROCESS_INPUT_EVENT )
|
||||
{
|
||||
/* Outputs were updated, set flag for watchdog monitoring */
|
||||
bEcatFirstOutputsReceived = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DISABLE_ESC_INT();
|
||||
/*ECAT_DBG(1.6.0 剖分:free-run 应用+输入映射块)*/
|
||||
{
|
||||
const UINT32 dbgT1 = ecatdbg_dwt();
|
||||
ECAT_Application();
|
||||
|
||||
if ( bEcatInputUpdateRunning )
|
||||
{
|
||||
/* EtherCAT slave is at least in SAFE-OPERATIONAL, update inputs */
|
||||
PDO_InputMapping();
|
||||
}
|
||||
g_dbg_freerunCyc += ecatdbg_dwt() - dbgT1;
|
||||
}
|
||||
ENABLE_ESC_INT();
|
||||
}
|
||||
#if !ECAT_TIMER_INT
|
||||
/* there is no interrupt routine for the hardware timer so check the timer register if the desired cycle elapsed*/
|
||||
{
|
||||
UINT32 CurTimer = (UINT32)HW_GetTimer();
|
||||
|
||||
if(CurTimer>= ECAT_TIMER_INC_P_MS)
|
||||
{
|
||||
ECAT_CheckTimer();
|
||||
|
||||
HW_ClearTimer();
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* call EtherCAT functions */
|
||||
ECAT_Main();
|
||||
|
||||
/* call lower prior application part */
|
||||
COE_Main();
|
||||
CheckIfEcatError();
|
||||
|
||||
}
|
||||
|
||||
/*The main function was moved to the application files.*/
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief ECAT_Application (prev. SSC versions "COE_Application")
|
||||
this function calculates and the physical process signals and triggers the input mapping
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
void ECAT_Application(void)
|
||||
{
|
||||
|
||||
APPL_Application();
|
||||
|
||||
/* PDO Input mapping is called from the specific trigger ISR */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** @} */
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
\addtogroup EcatAppl EtherCAT application
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file ecatappl.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
V5.10 ECAT4: Update alignment marco for 8 to 15 bit alignments (16 and 32 Bit controllers)<br>
|
||||
Bugfix calculate LED blink frequency<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Description
|
||||
------
|
||||
------ ecatappl.h
|
||||
------
|
||||
------ EtherCAT Slave Application
|
||||
------ ------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _ECATAPPL_H_
|
||||
#define _ECATAPPL_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/*Set to unsigned short to handle bit entries correct*/
|
||||
#define BOOLEAN(x) unsigned short(x):1 /**< \brief Marco to define BOOLEAN object entry*/
|
||||
#define BIT1(x) unsigned short(x):1 /**< \brief Marco to define BIT1 object entry*/
|
||||
#define BIT2(x) unsigned short(x):2 /**< \brief Marco to define BIT2 object entry*/
|
||||
#define BIT3(x) unsigned short(x):3 /**< \brief Marco to define BIT3 object entry*/
|
||||
#define BIT4(x) unsigned short(x):4 /**< \brief Marco to define BIT4 object entry*/
|
||||
#define BIT5(x) unsigned short(x):5 /**< \brief Marco to define BIT5 object entry*/
|
||||
#define BIT6(x) unsigned short(x):6 /**< \brief Marco to define BIT5 object entry*/
|
||||
#define BIT7(x) unsigned short(x):7 /**< \brief Marco to define BIT6 object entry*/
|
||||
#define BIT8(x) unsigned short(x):8 /**< \brief Marco to define BIT7 object entry*/
|
||||
#define ALIGN0(x)
|
||||
#define ALIGN1(x) unsigned short(x):1; /**< \brief Marco to define ALIGN1 object entry*/
|
||||
#define ALIGN2(x) unsigned short(x):2; /**< \brief Marco to define ALIGN2 object entry*/
|
||||
#define ALIGN3(x) unsigned short(x):3; /**< \brief Marco to define ALIGN3 object entry*/
|
||||
#define ALIGN4(x) unsigned short(x):4; /**< \brief Marco to define ALIGN4 object entry*/
|
||||
#define ALIGN5(x) unsigned short(x):5; /**< \brief Marco to define ALIGN5 object entry*/
|
||||
#define ALIGN6(x) unsigned short(x):6; /**< \brief Marco to define ALIGN6 object entry*/
|
||||
#define ALIGN7(x) unsigned short(x):7; /**< \brief Marco to define ALIGN7 object entry*/
|
||||
#define ALIGN8(x) unsigned short(x):8; /**< \brief Marco to define ALIGN8 object entry*/
|
||||
#define ALIGN9(x) unsigned short(x):9; /**< \brief Marco to define ALIGN9 object entry*/
|
||||
#define ALIGN10(x) unsigned short(x):10; /**< \brief Marco to define ALIGN10 object entry*/
|
||||
#define ALIGN11(x) unsigned short(x):11; /**< \brief Marco to define ALIGN11 object entry*/
|
||||
#define ALIGN12(x) unsigned short(x):12; /**< \brief Marco to define ALIGN12 object entry*/
|
||||
#define ALIGN13(x) unsigned short(x):13; /**< \brief Marco to define ALIGN13 object entry*/
|
||||
#define ALIGN14(x) unsigned short(x):14; /**< \brief Marco to define ALIGN14 object entry*/
|
||||
#define ALIGN15(x) unsigned short(x):15; /**< \brief Marco to define ALIGN15 object entry*/
|
||||
#endif //_ECATAPPL_H_
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_ECATAPPL_) && (_ECATAPPL_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
PROTO BOOL bEcatWaitForInputUpdate; /**< \brief Wait for input update*/
|
||||
PROTO BOOL bEtherCATRunLed; /**< \brief Current run LED value*/
|
||||
PROTO BOOL bEtherCATErrorLed; /**< \brief Current error LED value*/
|
||||
PROTO BOOL bRunApplication; /**< \brief Indicates if the stack shall be running (if false the Hardware will be released)*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
PROTO void ECAT_CheckTimer(void);
|
||||
PROTO void PDI_Isr(void);
|
||||
PROTO void Sync0_Isr(void);
|
||||
PROTO void Sync1_Isr(void);
|
||||
PROTO void ECAT_Application(void);
|
||||
PROTO void PDO_ResetOutputs(void);
|
||||
PROTO void PDO_ReadInputs(void);
|
||||
PROTO void PDO_InputMapping(void);
|
||||
|
||||
PROTO void CalcSMCycleTime(void);
|
||||
|
||||
|
||||
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
\addtogroup CoE CAN Application Profile over EtherCAT
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file ecatcoe.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
This file contains the CoE mailbox interface
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 TEST6: add test object to trigger Slave-to-Slave communication<br>
|
||||
V5.11 TEST7: add test behaviour to send an emergency on every SDO request (in SafeOP)<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 SDO7: "SDOS_SdoInfoInd()" never return pending SDO Info service. Delete "NOERROR_INWORK" handling.<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.40 SDO1: add initial value for "nSdoInfoFragmentsLeft"<br>
|
||||
V4.40 MBX6: change return value if no mailbox buffer is available<br>
|
||||
V4.08 MBX 1: If the switch MAILBOX_QUEUE was set, we have to put all SDO Info Responses in the Send Queue
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "sdoserv.h"
|
||||
#define _ECATCOE_ 1
|
||||
#include "ecatcoe.h"
|
||||
#undef _ECATCOE_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _ECATCOE_ (#ifdef is used in ecatcoe.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Types and Defines
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#define ECATCOE 0x4300
|
||||
#define ECATCOEMAX 0x02
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ static variables
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ static functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function intialize the CoE Interface.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void COE_Init(void)
|
||||
{
|
||||
pCoeSendStored = 0;
|
||||
nSdoInfoFragmentsLeft = 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pCoeMbx Pointer to the received mailbox data from the master.
|
||||
|
||||
\return result of the operation (0 (success) or mailbox error code (MBXERR_.... defined in
|
||||
mailbox.h))
|
||||
|
||||
\brief This function is called when a CoE (CAN application layer over EtherCAT) service is received from
|
||||
the master.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx)
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
switch ((pCoeMbx->CoeHeader & COEHEADER_COESERVICEMASK) >> COEHEADER_COESERVICESHIFT)
|
||||
{
|
||||
case COESERVICE_SDOREQUEST:
|
||||
/* SDO-Request received, call SDOS_SdoInd to process the SDO-Request
|
||||
if an existing SDO-Stack shall be used, the corresponding function
|
||||
should be called */
|
||||
result = SDOS_SdoInd( (TINITSDOMBX MBXMEM *) pCoeMbx );
|
||||
break;
|
||||
|
||||
case COESERVICE_SDOINFO:
|
||||
/* SDO-Information Request received, call SDOS_SdoInfoInd to process the SDO-Request */
|
||||
result = SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pCoeMbx );
|
||||
/*NOERROR_INWORK is never returned by SDOS_SdoInfoInd() => delete return code handling*/
|
||||
break;
|
||||
|
||||
|
||||
case COESERVICE_SDORESPONSE:
|
||||
case COESERVICE_EMERGENCY:
|
||||
case COESERVICE_TXPDO:
|
||||
case COESERVICE_RXPDO:
|
||||
case COESERVICE_TXPDOREMREQ:
|
||||
case COESERVICE_RXPDOREMREQ:
|
||||
/* these CoE services are not supported yet */
|
||||
result = MBXERR_SERVICENOTSUPPORTED;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = MBXERR_INVALIDHEADER;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the free mailbox to sent.
|
||||
|
||||
\return result of the operation (0 (success)
|
||||
|
||||
\brief This function is called when a CoE service to be sent is stored and can
|
||||
\brief be put in the send mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*ECATCHANGE_START(V5.11) COE4*/
|
||||
UINT8 COE_ContinueInd(TMBX MBXMEM * pMbx)
|
||||
/*ECATCHANGE_END(V5.11) COE4*/
|
||||
{
|
||||
if (pCoeSendStored)
|
||||
{
|
||||
/* send the stored CoE service which could not be sent before */
|
||||
MBX_MailboxSendReq(pCoeSendStored, 0);
|
||||
pCoeSendStored = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* send the next fragment of the last CoE service (only for SDO-Information possible) */
|
||||
/* in mailbox queue mode pMbx is always 0, so a mailbox buffer shall be get */
|
||||
pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(SIZEOF(TMBX));
|
||||
/* it shall be checked if a valid pointer was returned */
|
||||
/*ECATCHANGE_START(V5.11) COE4*/
|
||||
if (pMbx == NULL)
|
||||
{
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
else
|
||||
/*ECATCHANGE_END(V5.11) COE4*/
|
||||
{
|
||||
/* copy the stored SDO-Info-Header in the request */
|
||||
MBXMEMCPY(pMbx, aSdoInfoHeader, SDO_INFO_HEADER_BYTE_SIZE);
|
||||
/* call SDOS_SdoInfoInd to generate and send the next fragment */
|
||||
SDOS_SdoInfoInd( (TSDOINFORMATION MBXMEM *) pMbx );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* \addtogroup CoE CAN Application Profile over EtherCAT
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file ecatcoe.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.11 COE4: "change prototype of ""COE_ContinueInd()"" return <> 0 if a failure occurred"<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
#ifndef _ECATCOE_H_
|
||||
|
||||
#define _ECATCOE_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "mailbox.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------
|
||||
- Error codes
|
||||
-----------------------------------------------*/
|
||||
#define ERROR_COEINVALIDSERVICE 0x01 /**< \brief Invalid SDO service*/
|
||||
#define ERROR_COENOTSUPPORTED 0x02 /**< \brief Not supported service*/
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- COE services
|
||||
-----------------------------------------------*/
|
||||
#define COESERVICE_EMERGENCY 0x01 /**< \brief CoE Emergency*/
|
||||
#define COESERVICE_SDOREQUEST 0x02 /**< \brief CoE SDO request*/
|
||||
#define COESERVICE_SDORESPONSE 0x03 /**< \brief CoE SDO response*/
|
||||
#define COESERVICE_TXPDO 0x04 /**< \brief CoE TxPDO*/
|
||||
#define COESERVICE_RXPDO 0x05 /**< \brief CoE RxPDO*/
|
||||
#define COESERVICE_TXPDOREMREQ 0x06 /**< \brief CoE TxPDO map request*/
|
||||
#define COESERVICE_RXPDOREMREQ 0x07 /**< \brief CoE RxPDO map request*/
|
||||
#define COESERVICE_SDOINFO 0x08 /**< \brief CoE SDO Info*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- COE Structures
|
||||
-----------------------------------------------*/
|
||||
#define COEHEADER_COESERVICESHIFT 12 /**< \brief CoE service shift (within CoE header)*/
|
||||
#define COEHEADER_COESERVICEMASK 0xF000 /**< \brief CoE service mask (within CoE header)*/
|
||||
|
||||
#define COE_HEADER_SIZE 2 /**< \brief CoE header size*/
|
||||
|
||||
typedef UINT16 TCOEHEADER; /**< \brief CoE header*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief CoE Mailbox header
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
UINT16 Data[((MAX_MBX_DATA_SIZE)-(COE_HEADER_SIZE)) >> 1]; /**< \brief CoE data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TCOEMBX;
|
||||
|
||||
|
||||
#endif //_ECATCOE_H_
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_ECATCOE_) && (_ECATCOE_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
PROTO TMBX MBXMEM * VARMEM pCoeSendStored; /** if the mailbox service could not be sent (or stored),
|
||||
the CoE service will be stored in this variable
|
||||
and will be sent automatically from the mailbox handler
|
||||
(COE_ContinueInd) when the send mailbox will be read
|
||||
the next time from the master */
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
PROTO void COE_Init(void);
|
||||
PROTO UINT8 COE_ServiceInd(TCOEMBX MBXMEM *pCoeMbx);
|
||||
/*ECATCHANGE_START(V5.11) COE4*/
|
||||
PROTO UINT8 COE_ContinueInd(TMBX MBXMEM * pMbx);
|
||||
/*ECATCHANGE_END(V5.11) COE4*/
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* \addtogroup ESM EtherCAT State Machine
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file ecatslv.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT4: enhance SM/Sync monitoring for input/output only slaves<br>
|
||||
V5.11 ECAT9: add new AL Status Codes (ALSTATUSCODE_FW_SII_NOT_MATCH/0x0006, ALSTATUSCODE_FW_UPDATE_FAILED / 0x0007)<br>
|
||||
V5.11 ESM7: "add Sync define for 0x22 (""SYNCTYPE_SM2_SYNCHRON""), support value 0x22 for 0x1C33.1 (SM2 sync)"<br>
|
||||
V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 ECAT12: Add new AL Status Codes (0x2E,0x52,0x70)<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
|
||||
#ifndef _ECATSLV_H_
|
||||
#define _ECATSLV_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#if MCI_HW
|
||||
#include "mcihw.h"
|
||||
#endif
|
||||
#if EL9800_HW
|
||||
#include "el9800hw.h"
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef OBJGETNEXTSTR
|
||||
/* the old definition was not working with all compilers */
|
||||
/* old: #define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( ((UINT32) p) + OBJSTRLEN( (OBJCONST CHAR OBJMEM *) p ) + 1 ) ) */
|
||||
#define OBJGETNEXTSTR(p) ( (OBJCONST CHAR OBJMEM * )( &((p)[OBJSTRLEN( (OBJCONST CHAR OBJMEM *) (p) ) + 1]) ) ) /**< \brief Macro to get next name within the objetc name string*/
|
||||
#endif
|
||||
|
||||
#ifndef LO_BYTE
|
||||
#define LO_BYTE 0 /**< \brief LowByte within an WORD*/
|
||||
#endif
|
||||
|
||||
#ifndef HI_BYTE
|
||||
#define HI_BYTE 1 /**< \brief HighByte within an WORD*/
|
||||
#endif
|
||||
|
||||
#ifndef LOLO_BYTE
|
||||
#define LOLO_BYTE 0 /**< \brief LowLowByte within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef LOHI_BYTE
|
||||
#define LOHI_BYTE 1 /**< \brief LowHighByte within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef HILO_BYTE
|
||||
#define HILO_BYTE 2 /**< \brief HighLowByte within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef HIHI_BYTE
|
||||
#define HIHI_BYTE 3 /**< \brief HighHighByte within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef LO_WORD
|
||||
#define LO_WORD 0 /**< \brief LowWord within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef HI_WORD
|
||||
#define HI_WORD 1 /**< \brief HighWord within an DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef SWAPWORD
|
||||
#define SWAPWORD(x) (x) /**< \brief Macro to swap a WORD*/
|
||||
#endif
|
||||
|
||||
#ifndef SWAPDWORD
|
||||
#define SWAPDWORD(x) (x) /**< \brief Macro to swap a DWORD*/
|
||||
#endif
|
||||
|
||||
#ifndef LOBYTE
|
||||
#define LOBYTE(x) ((x)&0xFF) /**< \brief LowByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef HIBYTE
|
||||
#define HIBYTE(x) (((x)&0xFF00)>>8)/**< \brief HighwByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef LOLOBYTE
|
||||
#define LOLOBYTE(x) ((x)&0xFF)/**< \brief LowLowByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef LOHIBYTE
|
||||
#define LOHIBYTE(x) (((x)&0xFF00)>>8)/**< \brief LowHighByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef HILOBYTE
|
||||
#define HILOBYTE(x) (((x)&0xFF0000)>>16)/**< \brief HighLowByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef HIHIBYTE
|
||||
#define HIHIBYTE(x) (((x)&0xFF000000)>>24)/**< \brief HighHighByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef LOWORD
|
||||
#define LOWORD(x) ((x)&0xFFFF)/**< \brief LowWord mask*/
|
||||
#endif
|
||||
|
||||
#ifndef HIWORD
|
||||
#define HIWORD(x) (((x)&0xFFFF0000)>>16)/**< \brief HighByte mask*/
|
||||
#endif
|
||||
|
||||
#ifndef BIT2BYTE
|
||||
#define BIT2BYTE(x) (((x)+7)>>3) /**< \brief Marco to convert Bit size to Byte size (round up)*/
|
||||
#endif
|
||||
|
||||
#ifndef BYTE2BIT
|
||||
#define BYTE2BIT(x) ((x)<<3) /**< \brief Marco to convert Byte size to Bit size*/
|
||||
#endif
|
||||
|
||||
#ifndef BIT2WORD
|
||||
#define BIT2WORD(x) (((x)+15)>>4) /**< \brief Marco to convert Bit size to Word size (round up)*/
|
||||
#endif
|
||||
|
||||
#ifndef BYTE2WORD
|
||||
#define BYTE2WORD(x) (((x)+1)>>1) /**< \brief Marco to convert Bytes size to Word size (round up)*/
|
||||
#endif
|
||||
|
||||
#ifndef ROUNDUPBYTE2WORD
|
||||
#define ROUNDUPBYTE2WORD(x) ((((x)+1)>>1)<<1) /**< \brief Round up Byte size to even number of Words*/
|
||||
#endif
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- State defines
|
||||
-----------------------------------------------*/
|
||||
#define STATE_INIT ((UINT8) 0x01) /**< \brief State INIT*/
|
||||
#define STATE_PREOP ((UINT8) 0x02) /**< \brief State PreOP*/
|
||||
#define STATE_BOOT ((UINT8) 0x03) /**< \brief State BOOT*/
|
||||
#define STATE_SAFEOP ((UINT8) 0x04) /**< \brief State SafeOP*/
|
||||
#define STATE_OP ((UINT8) 0x08) /**< \brief State OP*/
|
||||
|
||||
#define STATE_MASK ((UINT8) 0x0F) /**< \brief State mask*/
|
||||
#define STATE_CHANGE ((UINT8) 0x10) /**< \brief State change (Error indication/acknowledge) mask and value*/
|
||||
#define STATE_DEVID ((UINT8) 0x20) /**< \brief Request/response Explicit device ID*/
|
||||
|
||||
#define BOOT_2_INIT ((UINT8)((STATE_BOOT) << 4) | (STATE_INIT)) /**< \brief State transition BOOT to INIT*/
|
||||
#define BOOT_2_PREOP ((UINT8)((STATE_BOOT) << 4) | (STATE_PREOP)) /**< \brief State transition BOOT to INIT*/
|
||||
#define BOOT_2_SAFEOP ((UINT8)((STATE_BOOT) << 4) | (STATE_SAFEOP)) /**< \brief State transition BOOT to INIT*/
|
||||
#define BOOT_2_OP ((UINT8)((STATE_BOOT) << 4) | (STATE_OP)) /**< \brief State transition BOOT to INIT*/
|
||||
|
||||
#define INIT_2_BOOT ((UINT8)((STATE_INIT) << 4) | (STATE_BOOT)) /**< \brief State transition INIT to BOOT*/
|
||||
#define PREOP_2_BOOT ((UINT8)((STATE_PREOP) << 4) | (STATE_BOOT)) /**< \brief State transition PREOP to BOOT*/
|
||||
#define SAFEOP_2_BOOT ((UINT8)((STATE_SAFEOP) << 4) | (STATE_BOOT)) /**< \brief State transition SAFEOP to BOOT*/
|
||||
#define OP_2_BOOT ((UINT8)((STATE_OP) << 4) | (STATE_BOOT)) /**< \brief State transition OP to BOOT*/
|
||||
|
||||
#define INIT_2_INIT ((UINT8)((STATE_INIT) << 4) | (STATE_INIT)) /**< \brief State transition INIT to INIT*/
|
||||
#define INIT_2_PREOP ((UINT8)((STATE_INIT) << 4) | (STATE_PREOP)) /**< \brief State transition INIT to PREOP*/
|
||||
#define INIT_2_SAFEOP ((UINT8)((STATE_INIT) << 4) | (STATE_SAFEOP)) /**< \brief State transition INIT to SAFEOP*/
|
||||
#define INIT_2_OP ((UINT8)((STATE_INIT) << 4) | (STATE_OP)) /**< \brief State transition INIT to OP*/
|
||||
|
||||
#define PREOP_2_INIT ((UINT8)((STATE_PREOP) << 4) | (STATE_INIT)) /**< \brief State transition PREOP to INIT*/
|
||||
#define PREOP_2_PREOP ((UINT8)((STATE_PREOP) << 4) | (STATE_PREOP)) /**< \brief State transition PREOP to PREOP*/
|
||||
#define PREOP_2_SAFEOP ((UINT8)((STATE_PREOP) << 4) | (STATE_SAFEOP)) /**< \brief State transition PREOP to SAFEOP*/
|
||||
#define PREOP_2_OP ((UINT8)((STATE_PREOP) << 4) | (STATE_OP)) /**< \brief State transition PREOP to OP*/
|
||||
|
||||
#define SAFEOP_2_INIT ((UINT8)((STATE_SAFEOP) << 4) | (STATE_INIT)) /**< \brief State transition SAFEOP to INIT*/
|
||||
#define SAFEOP_2_PREOP ((UINT8)((STATE_SAFEOP) << 4) | (STATE_PREOP)) /**< \brief State transition SAFEOP to PREOP*/
|
||||
#define SAFEOP_2_SAFEOP ((UINT8)((STATE_SAFEOP) << 4) | (STATE_SAFEOP)) /**< \brief State transition SAFEOP to SAFEOP*/
|
||||
#define SAFEOP_2_OP ((UINT8)((STATE_SAFEOP) << 4) |( STATE_OP)) /**< \brief State transition SAFEOP to OP*/
|
||||
|
||||
#define OP_2_INIT ((UINT8)((STATE_OP) << 4) | (STATE_INIT)) /**< \brief State transition OP to INIT*/
|
||||
#define OP_2_PREOP ((UINT8)((STATE_OP) << 4) | (STATE_PREOP)) /**< \brief State transition OP to PREOP*/
|
||||
#define OP_2_SAFEOP ((UINT8)((STATE_OP) << 4) | (STATE_SAFEOP)) /**< \brief State transition OP to SAFEOP*/
|
||||
#define OP_2_OP ((UINT8)((STATE_OP) << 4) | (STATE_OP)) /**< \brief State transition OP to OP*/
|
||||
|
||||
|
||||
/*-------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
ESM transition error codes
|
||||
|
||||
*/
|
||||
|
||||
#define SYNCMANCHODDADDRESS 0x00 /**< \brief Emergency and Diagnosis code for an odd SyncManager address*/
|
||||
#define SYNCMANCHADDRESS 0x01 /**< \brief Emergency and Diagnosis code for an invalid SyncManager address*/
|
||||
#define SYNCMANCHSIZE 0x02 /**< \brief Emergency and Diagnosis code for an invalid SyncManager size*/
|
||||
#define SYNCMANCHSETTINGS 0x03 /**< \brief Emergency and Diagnosis code for an invalid SyncManager settings*/
|
||||
#define ERROR_SYNCMANCH(code, channel) ((code)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/
|
||||
#define ERROR_SYNCMANCHODDADDRESS(channel) ((SYNCMANCHODDADDRESS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/
|
||||
#define ERROR_SYNCMANCHADDRESS(channel) ((SYNCMANCHADDRESS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/
|
||||
#define ERROR_SYNCMANCHSIZE(channel) ((SYNCMANCHSIZE)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/
|
||||
#define ERROR_SYNCMANCHSETTINGS(channel) ((SYNCMANCHSETTINGS)+((channel)<<2)) /**< \brief Marco to add SyncManager channel*/
|
||||
#define ERROR_SYNCTYPES 0x80 /**< \brief Emergency and Diagnosis code for an invalid Sync types*/
|
||||
#define ERROR_DCSYNCCONTROL 0x81 /**< \brief Emergency and Diagnosis code for an invalid DC Sync control*/
|
||||
#define ERROR_DCSYNC0CYCLETIME 0x82 /**< \brief Emergency and Diagnosis code for an invalid Sync0 cycle time*/
|
||||
#define ERROR_DCSYNC1CYCLETIME 0x83 /**< \brief Emergency and Diagnosis code for an invalid Sync1 cycle time*/
|
||||
#define ERROR_DCCYCLEPARAMETER 0x84 /**< \brief Emergency and Diagnosis code for an invalid Cycle parameter*/
|
||||
#define ERROR_DCLATCHCONTROL 0x85 /**< \brief Emergency and Diagnosis code for an invalid Latch control*/
|
||||
|
||||
#define ERROR_INVALIDSTATE 0xF0 /**< \brief Emergency and Diagnosis code for an invalid State*/
|
||||
#define ERROR_NOMEMORY 0xF1 /**< \brief Emergency and Diagnosis code for no memory*/
|
||||
#define ERROR_OBJECTDICTIONARY 0xF2 /**< \brief Emergency and Diagnosis code for generic object dictionary error*/
|
||||
#define ERROR_NOSYNCMANACCESS 0xF3 /**< \brief Emergency and Diagnosis code for no SyncManager access*/
|
||||
#define ERROR_NOOFRXPDOS 0xF4 /**< \brief Emergency and Diagnosis code for no RxPDOs*/
|
||||
#define ERROR_NOOFTXPDOS 0xF5 /**< \brief Emergency and Diagnosis code for no TxPDOs*/
|
||||
#define ERROR_STATECHANGE 0xF6 /**< \brief Emergency and Diagnosis code state change error*/
|
||||
|
||||
#define NOERROR_NOSTATECHANGE 0xFE /**< \brief Indicates no state change*/
|
||||
#define NOERROR_INWORK 0xFF /**< \brief Indication for no error but operation is pending*/
|
||||
|
||||
#define EMCY_SM_ERRORCODE 0xA000 /**< \brief Emergency and Diagnosis code for an SyncManager error*/
|
||||
#define EMCY_SM_DEVICESPECIFIC 0xFF00 /**< \brief Emergency and Diagnosis code for a device specific error*/
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- AL Status Codes
|
||||
-----------------------------------------------*/
|
||||
#define ALSTATUSCODE_NOERROR 0x0000 /**< \brief No error*/
|
||||
#define ALSTATUSCODE_UNSPECIFIEDERROR 0x0001 /**< \brief Unspecified error*/
|
||||
#define ALSTATUSCODE_NOMEMORY 0x0002 /**< \brief No Memory*/
|
||||
/* ECATCHANGE_START(V5.11) ECAT9*/
|
||||
#define ALSTATUSCODE_FW_SII_NOT_MATCH 0x0006 /**< \brief Firmware and EEPROM do not match. Slave needs BOOT-INIT transition*/
|
||||
#define ALSTATUSCODE_FW_UPDATE_FAILED 0x0007 /**< \brief Firmware update not successful. Old firmware still running*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT9*/
|
||||
#define ALSTATUSCODE_INVALIDALCONTROL 0x0011 /**< \brief Invalid requested state change*/
|
||||
#define ALSTATUSCODE_UNKNOWNALCONTROL 0x0012 /**< \brief Unknown requested state*/
|
||||
#define ALSTATUSCODE_BOOTNOTSUPP 0x0013 /**< \brief Bootstrap not supported*/
|
||||
#define ALSTATUSCODE_NOVALIDFIRMWARE 0x0014 /**< \brief No valid firmware*/
|
||||
#define ALSTATUSCODE_INVALIDMBXCFGINBOOT 0x0015 /**< \brief Invalid mailbox configuration (BOOT state)*/
|
||||
#define ALSTATUSCODE_INVALIDMBXCFGINPREOP 0x0016 /**< \brief Invalid mailbox configuration (PreOP state)*/
|
||||
#define ALSTATUSCODE_INVALIDSMCFG 0x0017 /**< \brief Invalid sync manager configuration*/
|
||||
#define ALSTATUSCODE_NOVALIDINPUTS 0x0018 /**< \brief No valid inputs available*/
|
||||
#define ALSTATUSCODE_NOVALIDOUTPUTS 0x0019 /**< \brief No valid outputs*/
|
||||
#define ALSTATUSCODE_SYNCERROR 0x001A /**< \brief Synchronization error*/
|
||||
#define ALSTATUSCODE_SMWATCHDOG 0x001B /**< \brief Sync manager watchdog*/
|
||||
#define ALSTATUSCODE_SYNCTYPESNOTCOMPATIBLE 0x001C /**< \brief Invalid Sync Manager Types*/
|
||||
#define ALSTATUSCODE_INVALIDSMOUTCFG 0x001D /**< \brief Invalid Output Configuration*/
|
||||
#define ALSTATUSCODE_INVALIDSMINCFG 0x001E /**< \brief Invalid Input Configuration*/
|
||||
#define ALSTATUSCODE_INVALIDWDCFG 0x001F /**< \brief Invalid Watchdog Configuration*/
|
||||
#define ALSTATUSCODE_WAITFORCOLDSTART 0x0020 /**< \brief Slave needs cold start*/
|
||||
#define ALSTATUSCODE_WAITFORINIT 0x0021 /**< \brief Slave needs INIT*/
|
||||
#define ALSTATUSCODE_WAITFORPREOP 0x0022 /**< \brief Slave needs PREOP*/
|
||||
#define ALSTATUSCODE_WAITFORSAFEOP 0x0023 /**< \brief Slave needs SAFEOP*/
|
||||
#define ALSTATUSCODE_INVALIDINPUTMAPPING 0x0024 /**< \brief Invalid Input Mapping*/
|
||||
#define ALSTATUSCODE_INVALIDOUTPUTMAPPING 0x0025 /**< \brief Invalid Output Mapping*/
|
||||
#define ALSTATUSCODE_INCONSISTENTSETTINGS 0x0026 /**< \brief Inconsistent Settings*/
|
||||
#define ALSTATUSCODE_FREERUNNOTSUPPORTED 0x0027 /**< \brief FreeRun not supported*/
|
||||
#define ALSTATUSCODE_SYNCHRONNOTSUPPORTED 0x0028 /**< \brief SyncMode not supported*/
|
||||
#define ALSTATUSCODE_FREERUNNEEDS3BUFFERMODE 0x0029 /**< \brief FreeRun needs 3Buffer Mode*/
|
||||
#define ALSTATUSCODE_BACKGROUNDWATCHDOG 0x002A /**< \brief Background Watchdog*/
|
||||
#define ALSTATUSCODE_NOVALIDINPUTSANDOUTPUTS 0x002B /**< \brief No Valid Inputs and Outputs*/
|
||||
#define ALSTATUSCODE_FATALSYNCERROR 0x002C /**< \brief Fatal Sync Error*/
|
||||
#define ALSTATUSCODE_NOSYNCERROR 0x002D /**< \brief No Sync Error*/
|
||||
#define ALSTATUSCODE_CYCLETIMETOOSMALL 0x002E /**< \brief EtherCAT cycle time smaller Minimum Cycle Time supported by slave*/
|
||||
#define ALSTATUSCODE_DCINVALIDSYNCCFG 0x0030 /**< \brief Invalid DC SYNCH Configuration*/
|
||||
#define ALSTATUSCODE_DCINVALIDLATCHCFG 0x0031 /**< \brief Invalid DC Latch Configuration*/
|
||||
#define ALSTATUSCODE_DCPLLSYNCERROR 0x0032 /**< \brief PLL Error*/
|
||||
#define ALSTATUSCODE_DCSYNCIOERROR 0x0033 /**< \brief DC Sync IO Error*/
|
||||
#define ALSTATUSCODE_DCSYNCMISSEDERROR 0x0034 /**< \brief DC Sync Timeout Error*/
|
||||
#define ALSTATUSCODE_DCINVALIDSYNCCYCLETIME 0x0035 /**< \brief DC Invalid Sync Cycle Time*/
|
||||
#define ALSTATUSCODE_DCSYNC0CYCLETIME 0x0036 /**< \brief DC Sync0 Cycle Time*/
|
||||
#define ALSTATUSCODE_DCSYNC1CYCLETIME 0x0037 /**< \brief DC Sync1 Cycle Time*/
|
||||
#define ALSTATUSCODE_MBX_AOE 0x0041 /**< \brief MBX_AOE*/
|
||||
#define ALSTATUSCODE_MBX_EOE 0x0042 /**< \brief MBX_EOE*/
|
||||
#define ALSTATUSCODE_MBX_COE 0x0043 /**< \brief MBX_COE*/
|
||||
#define ALSTATUSCODE_MBX_FOE 0x0044 /**< \brief MBX_FOE*/
|
||||
#define ALSTATUSCODE_MBX_SOE 0x0045 /**< \brief MBX_SOE*/
|
||||
#define ALSTATUSCODE_MBX_VOE 0x004F /**< \brief MBX_VOE*/
|
||||
#define ALSTATUSCODE_EE_NOACCESS 0x0050 /**< \brief EEPROM no access*/
|
||||
#define ALSTATUSCODE_EE_ERROR 0x0051 /**< \brief EEPROM Error*/
|
||||
#define ALSTATUSCODE_EXT_HARDWARE_NOT_READY 0x0052 /**< \brief External hardware not ready. This AL Status Code should be used if the EtherCAT-Slave refused the state transition due to an external connection to another device or signal is missing*/
|
||||
#define ALSTATUSCODE_MODULE_ID_LIST_NOT_MATCH 0x0070 /**< \brief Detected Module Ident List (0xF030) and Configured Module Ident List (0xF050) does not match*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- Configured Sync Type (0x1C32.1 / 0x1C33.1)
|
||||
-----------------------------------------------*/
|
||||
#define SYNCTYPE_FREERUN 0x0000 /**< \brief Sync type FreeRun*/
|
||||
/*ECATCHANGE_START(V5.11) ESM7*/
|
||||
#define SYNCTYPE_SM_SYNCHRON 0x0001 /**< \brief SyncManager synchron (synchron to the corresponding SM, 0x1C32.1 -> SM2 ; 0x1C33.1 -> SM3) */
|
||||
#define SYNCTYPE_SM2_SYNCHRON 0x0022 /**< \brief SyncManager2 synchron (only used for 0x1C33.1)*/
|
||||
/*ECATCHANGE_END(V5.11) ESM7*/
|
||||
#define SYNCTYPE_DCSYNC0 0x0002 /**< \brief Sync type Sync0 synchron*/
|
||||
#define SYNCTYPE_DCSYNC1 0x0003 /**< \brief Sync type Sync1 synchron*/
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- AL Event masks
|
||||
-----------------------------------------------*/
|
||||
#define AL_CONTROL_EVENT ((UINT16) 0x01) /**< \brief AL Control event*/
|
||||
#define SYNC0_EVENT ((UINT16) 0x04) /**< \brief Sync0 event*/
|
||||
#define SYNC1_EVENT ((UINT16) 0x08) /**< \brief Sync1 event*/
|
||||
#define SM_CHANGE_EVENT ((UINT16) 0x10) /**< \brief SyncManager changed event*/
|
||||
#define EEPROM_CMD_PENDING ((UINT16) 0x20) /**< \brief EEPROM command pending*/
|
||||
#define MAILBOX_WRITE_EVENT ((UINT16) 0x0100) /**< \brief MBoxOut write event*/
|
||||
#define MAILBOX_READ_EVENT ((UINT16) 0x0200) /**< \brief MBoxIn read event*/
|
||||
#define PROCESS_OUTPUT_EVENT ((UINT16) 0x0400) /**< \brief Output process data write event*/
|
||||
#define PROCESS_INPUT_EVENT ((UINT16) 0x0800) /**< \brief Input process data read event*/
|
||||
|
||||
|
||||
#ifndef MAX_PD_SYNC_MAN_CHANNELS
|
||||
#define MAX_PD_SYNC_MAN_CHANNELS 2 /**< \brief maximum number of process data SyncManager channels*/
|
||||
#endif
|
||||
#define MAX_NUMBER_OF_SYNCMAN ((MAX_PD_SYNC_MAN_CHANNELS)+2) /**< \brief Maximum number of SyncManagers (max number of Pd SM + 2 mailbox SyncManager)*/
|
||||
|
||||
#define MAILBOX_WRITE 0 /**< \brief SyncManager ID for MBoxOut (master to slave)*/
|
||||
#define MAILBOX_READ 1 /**< \brief SyncManager ID for MBoxIn (slave to master)*/
|
||||
#define PROCESS_DATA_OUT 2 /**< \brief SyncManager ID for output process data (master to slave)*/
|
||||
#define PROCESS_DATA_IN 3 /**< \brief SyncManager ID for input process data (slave to master)*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define MEMORY_START_ADDRESS 0x1000 /**< \brief ESC DPRAM start address*/
|
||||
|
||||
|
||||
#ifndef DC_SYNC_ACTIVE
|
||||
#define DC_SYNC_ACTIVE ESC_DC_SYNC0_ACTIVE_MASK /**< \brief Expected activated Sync signals by the master*/
|
||||
#endif
|
||||
#ifndef DC_EVENT_MASK
|
||||
#define DC_EVENT_MASK PROCESS_OUTPUT_EVENT /**< \brief AL Event mask (value of register 0x204) in case of DC synchronisation*/
|
||||
#endif
|
||||
|
||||
|
||||
#endif //_ECATSLV_H_
|
||||
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_ECATSLV_) && (_ECATSLV_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
PROTO BOOL bEcatOutputUpdateRunning; /**< \brief Indicates the OP state, will be set in StartOutputHandler
|
||||
and reset in StopOutputHandler*/
|
||||
|
||||
PROTO BOOL bEcatInputUpdateRunning; /**< \brief Indicates the SAFEOP or OP state, will be set in StartInputHandler
|
||||
and reset in Stop InputHandler*/
|
||||
|
||||
PROTO BOOL bEcatFirstOutputsReceived; /**< \brief Indicates if outputs were received (SM2-event)
|
||||
or inputs were read (SM3-event, if the output size is 0),
|
||||
has to be set by the application and reset in StopOutputHandler*/
|
||||
|
||||
PROTO BOOL bWdTrigger; /**< \brief Indicates that the SM2 WD trigger bit (0x814 bit6) is set.*/
|
||||
|
||||
PROTO BOOL bDcSyncActive; /**< \brief Indicates that the Distributed Clocks synchronization is active,*/
|
||||
|
||||
PROTO INT16 EsmTimeoutCounter; /**< \brief Counter used to detect an ESM timeout.
|
||||
-1 indicates a deactivated counter and 0 is expired*/
|
||||
|
||||
#define MAX_SM_EVENT_MISSED 4 /**< \brief threshold of max missed counter value (0x1C32.11 / 0x1C33.11)*/
|
||||
|
||||
PROTO BOOL bDcRunning; /**< \brief Indicates if Sync0 events are received*/
|
||||
|
||||
PROTO UINT16 u16SmSync0Counter; /**< /brief Incremented by one on every Sync0 event and reset to 0 on every SM event. It is used to check the SM/Sync0 sequence */
|
||||
PROTO UINT16 u16SmSync0Value; /**< /brief Allowed Sync0 events within one SM cycle. If 0 the Sequence check is disabled */
|
||||
|
||||
PROTO BOOL bSmSyncSequenceValid; /**< \brief Set to true if SM/Sync0 sequence is valid*/
|
||||
|
||||
PROTO INT16 i16WaitForPllRunningTimeout; /**< \brief The time bPllRunnig shall be true while state change from SafeOp to Op*/
|
||||
|
||||
PROTO INT16 i16WaitForPllRunningCnt; /**< \brief Incremented with every valid Sync-SyncManger cycle */
|
||||
|
||||
PROTO UINT16 Sync0WdCounter; /**< \brief Sync0 watchdog counter*/
|
||||
PROTO UINT16 Sync0WdValue; /**< \brief Sync0 watchdog value*/
|
||||
|
||||
PROTO UINT16 Sync1WdCounter; /**< \brief Sync1 watchdog counter*/
|
||||
PROTO UINT16 Sync1WdValue; /**< \brief Sync1 watchdog value*/
|
||||
|
||||
PROTO UINT16 LatchInputSync0Value; /**< \brief Sync0 event on which the inputs shall be latched and copied to the ESC buffer. If the inputs shall be latched base don Sync1 the value is set to 0.*/
|
||||
PROTO UINT16 LatchInputSync0Counter; /**< \brief Sync0 counter used to get the Sync0 event to latch the Inputs. */
|
||||
|
||||
|
||||
|
||||
|
||||
PROTO BOOL bEscIntEnabled; /**< \brief Indicates that the ESC interrupt is enabled (SM2/3 or SYNC0/1-event),
|
||||
will be set in StartInputHandler and reset in StopInputHandler*/
|
||||
|
||||
PROTO BOOL b3BufferMode; /**< \brief Indicates that inputs and outputs are running in 3-Buffer-Mode*/
|
||||
|
||||
PROTO BOOL bLocalErrorFlag; /**< \brief Contains the information if the application has a local error*/
|
||||
PROTO UINT16 u16LocalErrorCode; /**< \brief Reason for local error*/
|
||||
PROTO BOOL bApplEsmPending; /**< \brief Indicates if the local application ESM function need to be called from Al_ConntrolRes (is true if NOERR_INWORK is returned by generic ESM function)*/
|
||||
PROTO BOOL bEcatWaitForAlControlRes; /**< \brief Contains the information that the state machine waits for an acknowledge
|
||||
for the last AL_ControlInd from the application/generic stack*/
|
||||
|
||||
PROTO UINT16 nEcatStateTrans; /**< \brief Current state transition*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) ECAT4*/
|
||||
|
||||
PROTO UINT16 nPdInputSize; /**< \brief Contains the input size (SM3 size)/SM2 if no outputs are supported, has to be written by the application*/
|
||||
|
||||
PROTO UINT16 nPdOutputSize; /**< \brief Contains the output size (SM2 size), has to be written by the application*/
|
||||
/*ECATCHANGE_END(V5.11) ECAT4*/
|
||||
|
||||
PROTO UINT8 nMaxSyncMan; /**< \brief Contains the maximum number of Sync Manager channels, will be initialized in ECAT_Main*/
|
||||
|
||||
PROTO UINT16 nMaxEscAddress; /**< \brief Max supported address by the ESC (register(0x1000) + size of the supported DPRAM in Byte)*/
|
||||
|
||||
PROTO UINT8 nAlStatus; /**< \brief Contains the actual AL Status, will be written in AL_ControlInd*/
|
||||
|
||||
|
||||
PROTO UINT16 EcatWdValue; /**< \brief Contains the value of the watchdog in 100us, will be written in StartInputHandler.
|
||||
In case that the ESC watchdog feature is used this variable just indicates if the watchdog is enabled or disabled*/
|
||||
PROTO UINT16 nEscAddrOutputData; /**< \brief Contains the SM address for the output process data*/
|
||||
PROTO UINT16 nEscAddrInputData; /**< \brief Contains the SM address for the input process data*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
PROTO void EnableSyncManChannel(UINT8 channel);
|
||||
PROTO void DisableSyncManChannel(UINT8 channel);
|
||||
PROTO TSYNCMAN ESCMEM *GetSyncMan(UINT8 channel);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
PROTO void SetALStatus(UINT8 alStatus, UINT16 alStatusCode);
|
||||
PROTO void AL_ControlInd(UINT8 alControl, UINT16 alStatusCode);
|
||||
PROTO void DC_CheckWatchdog(void);
|
||||
PROTO void CheckIfEcatError(void);
|
||||
PROTO void ECAT_Init(void);
|
||||
|
||||
PROTO void ECAT_Main(void);
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file el9800appl.h
|
||||
* @brief EL9800 应用层桩头文件 — 本项目不使用 EL9800 示例应用
|
||||
*
|
||||
* ecatappl.c / ecatslv.c 无条件 include 此文件。
|
||||
* 本项目的应用逻辑在 dc_app.c/h 中实现。
|
||||
*/
|
||||
#ifndef _EL9800APPL_H_
|
||||
#define _EL9800APPL_H_
|
||||
|
||||
#include "ecatappl.h"
|
||||
#include "objdef.h"
|
||||
#include "dc_app.h"
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Application Object Dictionary
|
||||
------
|
||||
------ Phase 1: 空(无应用层专用 OD 条目)。
|
||||
------ Phase 3: 在此添加 DCU 专用对象(如 0x2000 传感器数据)。
|
||||
------ coeappl.c 中 COE_ObjInit() 会检查此指针,NULL 则跳过。
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#if defined(_EL9800APPL_) && (_EL9800APPL_ == 1)
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
PROTO TOBJECT OBJMEM * VARMEM ApplicationObjDic;
|
||||
|
||||
#undef PROTO
|
||||
|
||||
#endif /* _EL9800APPL_H_ */
|
||||
@@ -0,0 +1,152 @@
|
||||
/**
|
||||
* @file el9800hw.h
|
||||
* @brief SSC 硬件抽象层 — GD32E508 + LAN9252 (EXMC 16-bit HBI Indexed Mode)
|
||||
*
|
||||
* 本文件替换 Beckhoff EL9800 SPI 版本,提供 SSC 所需的 HW_EscRead/Write 宏
|
||||
* 和中断/定时器控制宏。实际实现在 hw_gd32exmc.cpp。
|
||||
*
|
||||
* HBI Indexed 寻址(A2=LAN9252 Pin29←PD12=EXMC_A17, A1=Pin25←PD11=EXMC_A16;
|
||||
* EXMC 16-bit 模式地址左移 1 位,EXMC_A16=CPU 偏移 0x20000、A17=0x40000):
|
||||
* IDX_0 = EXMC offset 0x00000 (A2=0, A1=0)
|
||||
* DATA_0 = EXMC offset 0x40000 (A2=1, A1=0) 低 16 位
|
||||
* = EXMC offset 0x60000 (A2=1, A1=1) 高 16 位
|
||||
*/
|
||||
#ifndef _EL9800HW_H_
|
||||
#define _EL9800HW_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "esc.h"
|
||||
|
||||
/* GD32E508 CMSIS (通过 RTE 组件获取) */
|
||||
#include "RTE_Components.h"
|
||||
#include CMSIS_device_header
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Hardware Timer Settings
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/** SSC 看门狗定时器分辨率:ticks per ms。
|
||||
* 本项目使用 SysTick (180MHz / 1000 = 180000 ticks/ms)。
|
||||
* 实际由 hw_gd32exmc.cpp 中 HW_GetTimer() 实现决定。 */
|
||||
#define ECAT_TIMER_INC_P_MS 180000
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Interrupt Control (ESC IRQ = EXTI1, PB1)
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DISABLE_ESC_INT
|
||||
#define DISABLE_ESC_INT() NVIC_DisableIRQ(EXTI1_IRQn)
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_ESC_INT
|
||||
#define ENABLE_ESC_INT() NVIC_EnableIRQ(EXTI1_IRQn)
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ AL Event Interrupt Mask (保留宏)
|
||||
------
|
||||
------ 1.5.27 起主循环 HBI 访问的互斥已内置到 hw_gd32exmc.cpp 的 HW_* 函数
|
||||
------ (EscExmcBus::maskEnter/maskExit),SSC 源码不再需要显式调用本组宏。
|
||||
------ 保留定义仅为兼容 SSC 变体中可能出现的引用。
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef DISABLE_AL_EVENT_INT
|
||||
#define DISABLE_AL_EVENT_INT() do { NVIC_DisableIRQ(EXTI0_IRQn); NVIC_DisableIRQ(EXTI1_IRQn); } while(0)
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_AL_EVENT_INT
|
||||
#define ENABLE_AL_EVENT_INT() do { NVIC_EnableIRQ(EXTI1_IRQn); NVIC_EnableIRQ(EXTI0_IRQn); } while(0)
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Timer Access
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#ifndef HW_GetTimer
|
||||
#define HW_GetTimer() HW_GetTimerValue()
|
||||
#endif
|
||||
|
||||
#ifndef HW_ClearTimer
|
||||
#define HW_ClearTimer() HW_ClearTimerValue()
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ ESC Access Macros (SSC 核心接口)
|
||||
------
|
||||
------ MEM_ADDR = UINT16 (CONTROLLER_16BIT=1)
|
||||
------ 所有宏最终调用 HW_EscRead / HW_EscWrite (hw_gd32exmc.cpp)
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#define HW_EscReadByte(ByteValue, Address) HW_EscRead(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1)
|
||||
#define HW_EscReadWord(WordValue, Address) HW_EscRead(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2)
|
||||
#define HW_EscReadDWord(DWordValue, Address) HW_EscRead(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4)
|
||||
#define HW_EscReadMbxMem(pData, Address, Len) HW_EscRead(((MEM_ADDR *)(pData)), ((UINT16)(Address)), (Len))
|
||||
|
||||
#define HW_EscReadByteIsr(ByteValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1)
|
||||
#define HW_EscReadWordIsr(WordValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2)
|
||||
#define HW_EscReadDWordIsr(DWordValue, Address) HW_EscReadIsr(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4)
|
||||
|
||||
#define HW_EscWriteByte(ByteValue, Address) HW_EscWrite(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1)
|
||||
#define HW_EscWriteWord(WordValue, Address) HW_EscWrite(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2)
|
||||
#define HW_EscWriteDWord(DWordValue, Address) HW_EscWrite(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4)
|
||||
#define HW_EscWriteMbxMem(pData, Address, Len) HW_EscWrite(((MEM_ADDR *)(pData)), ((UINT16)(Address)), (Len))
|
||||
|
||||
#define HW_EscWriteByteIsr(ByteValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(ByteValue)), ((UINT16)(Address)), 1)
|
||||
#define HW_EscWriteWordIsr(WordValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(WordValue)), ((UINT16)(Address)), 2)
|
||||
#define HW_EscWriteDWordIsr(DWordValue, Address) HW_EscWriteIsr(((MEM_ADDR *)&(DWordValue)), ((UINT16)(Address)), 4)
|
||||
|
||||
#endif /* _EL9800HW_H_ */
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Function Prototypes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#if _EL9800HW_
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
PROTO UINT8 HW_Init(void);
|
||||
PROTO void HW_Release(void);
|
||||
PROTO UINT16 HW_GetALEventRegister(void);
|
||||
PROTO UINT16 HW_GetALEventRegister_Isr(void);
|
||||
PROTO void HW_ResetALEventMask(UINT16 intMask);
|
||||
PROTO void HW_SetALEventMask(UINT16 intMask);
|
||||
PROTO void HW_SetLed(UINT8 RunLed, UINT8 ErrLed);
|
||||
|
||||
PROTO void HW_EscRead(MEM_ADDR *pData, UINT16 Address, UINT16 Len);
|
||||
PROTO void HW_EscReadIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len);
|
||||
PROTO void HW_EscWrite(MEM_ADDR *pData, UINT16 Address, UINT16 Len);
|
||||
PROTO void HW_EscWriteIsr(MEM_ADDR *pData, UINT16 Address, UINT16 Len);
|
||||
|
||||
PROTO void HW_DisableSyncManChannel(UINT8 channel);
|
||||
PROTO void HW_EnableSyncManChannel(UINT8 channel);
|
||||
PROTO TSYNCMAN ESCMEM *HW_GetSyncMan(UINT8 channel);
|
||||
|
||||
/* Timer helpers (implemented in hw_gd32exmc.cpp) */
|
||||
PROTO UINT32 HW_GetTimerValue(void);
|
||||
PROTO void HW_ClearTimerValue(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef PROTO
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* \addtogroup ESCRegister ESC Register
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file esc.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief EtherCAT Slave Controller related defines and Types
|
||||
|
||||
\version 5.10
|
||||
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 ESC1: Update address register offset for 32Bit ESC access<br>
|
||||
V5.10 ESC2: Check if defined SM settings do not exceed the available DPRAM range (in error case AL Status 0x14 is returned)<br>
|
||||
V5.10 ESC3: Handle DC cControl register values in case of 32Bit ESC access (a Sync activation mask need to defined/used)<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
#ifndef _ESC_H_
|
||||
#define _ESC_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
#define SIZEOF_SM_REGISTER 8 /**< \brief Each SyncManger has 8Byte Configuration/Status Registers*/
|
||||
#define MAX_NO_OF_SYNC_MAN 16/**< \brief Maximum Number of SyncManager channels supported by an ESC*/
|
||||
|
||||
#define BL_PAGE_SIZE 512
|
||||
|
||||
/*---------------------------------------------
|
||||
- ESC Offsets (Detailed Information about the registers are located in the ESC Datasheets e.g. ET1100 Datasheet www.beckhoff.com/english.asp?download/ethercat_development_products.htm?id=71003127100387)
|
||||
-----------------------------------------------*/
|
||||
#define ESC_INFO_OFFSET 0x0000 /**< \brief ESC information registers startoffset*/
|
||||
|
||||
#define ESC_COMM_INFO_OFFSET 0x0004 /**< \brief Communication information registers startoffset*/
|
||||
|
||||
#define ESC_DPRAM_SIZE_OFFSET 0x0006 /**< \brief Register Description: Size of the supported DPRAM in KB*/
|
||||
#define ESC_SM_CHANNELS_MASK 0xFF00 /**< \brief Mask based on ESC offset "ESC_COMM_INFO_OFFSET"*/
|
||||
#define ESC_SM_CHANNELS_SHIFT 8 /**< \brief Bit shift based on ESC offset "ESC_COMM_INFO_OFFSET"*/
|
||||
#define ESC_DPRAM_SIZE_MASK 0x00FF
|
||||
|
||||
|
||||
#define ESC_SLAVE_ADDRESS_OFFSET 0x0010 /**< \brief Register Description: Address used for node addressing (FPxx commands)*/
|
||||
|
||||
|
||||
|
||||
#define ESC_AL_CONTROL_OFFSET 0x0120 /**< \brief Register Description: Initiate State Transition of the Device State Machine*/
|
||||
#define ESC_AL_STATUS_OFFSET 0x0130 /**< \brief Register Description: Actual State of the Device State Machine*/
|
||||
#define ESC_AL_STATUS_CODE_OFFSET 0x0134 /**< \brief Register Description: AL Status Code*/
|
||||
|
||||
#define ESC_RUN_LED_OVERRIDE 0x0138 /**< \brief Register Description: Set Ecat Run indication via ESC. (not all ESC types support this feature)*/
|
||||
#define ESC_ERROR_LED_OVERRIDE 0x0139 /**< \brief Register Description: Set Ecat Error indication via ESC. (not all ESC types support this feature)*/
|
||||
|
||||
#define ESC_PDI_CONTROL_OFFSET 0x0140 /**< \brief Register Description: Specifies the process data interface*/
|
||||
|
||||
#define ESC_AL_EVENTMASK_OFFSET 0x0204 /**< \brief Register Description: AL Event masking of the AL Event Request register Events for mapping to PDI IRQ signal*/
|
||||
#define ESC_AL_EVENT_OFFSET 0x0220 /**< \brief Register Description: "Mirror" register for ESC events*/
|
||||
|
||||
#define ESC_WD_DIVIDER_OFFSET 0x0400 /**< \brief Register Description: Number of 25 MHz tics (minus 2) that represents the basic watchdog increment. (Default value is 100us = 2498)*/
|
||||
|
||||
#define ESC_PD_WD_TIME 0x0420 /**< \brief Register Description: Number of basic watchdog increments (Default value with Watchdog divider 100us means 100ms Watchdog)*/
|
||||
#define ESC_PD_WD_STATE 0x0440 /**< \brief Register Description: Watchdog Status of Process Data (triggered by SyncManagers)*/
|
||||
#define ESC_PD_WD_TRIGGER_MASK 0x0001 /**< \brief Trigger state of the process data watchdog*/
|
||||
|
||||
#define ESC_EEPROM_CONFIG_OFFSET 0x0500 /**< \brief Register Description: EEPROM Access Configuration*/
|
||||
/*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - START*/
|
||||
#define ESC_EEPROM_ASSIGN_TO_PDI_MASK 0x0001 /**< \brief Description (0x500.0): PDI has EEPROM control*/
|
||||
#define ESC_EEPROM_LOCKED_BY_PDI_MASK 0x0100 /**< \brief Description (0x500.8): PDI locked EEPROM access*/
|
||||
/*EEPROM config and access state bit mask (based on "ESC_EEPROM_CONFIG_OFFSET") - END*/
|
||||
|
||||
#define ESC_EEPROM_CONTROL_OFFSET 0x0502
|
||||
/* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - START*/
|
||||
#define ESC_EEPROM_SUPPORTED_READBYTES_MASK 0x0040 /**< \brief Description (0x502.6): Supported number of EEPROM read bytes: 0-> 4 Bytes; 1 -> 8 Bytes*/
|
||||
#define ESC_EEPROM_CMD_MASK 0x0700 /**< \brief Description (0x502.8:10): Command bit mask*/
|
||||
#define ESC_EEPROM_CMD_READ_MASK 0x0100 /**< \brief Description (0x502.8): Currently executed read command*/
|
||||
#define ESC_EEPROM_CMD_WRITE_MASK 0x0200 /**< \brief Description (0x502.9): Initialize Write Command*/
|
||||
#define ESC_EEPROM_CMD_RELOAD_MASK 0x0400 /**< \brief Description (0x502.10): Trigger EEPROM reload*/
|
||||
#define ESC_EEPROM_ERROR_MASK 0x7800 /**< \brief Description : Mask all EEPROM error bits; Checksum error (0x0502.11); EEPROM not loaded (0x0502.12); Missing EEPROM Acknowledge (0x0502.13); Write Error (0x0502.14)*/
|
||||
#define ESC_EEPROM_ERROR_CRC 0x0800 /**< \brief Description (0x502.11): EEPROM CRC Error*/
|
||||
#define ESC_EEPROM_ERROR_LOAD 0x1000 /**< \brief Description (0x502.11): EEPROM loading status (0 = OK)*/
|
||||
#define ESC_EEPROM_ERROR_CMD_ACK 0x2000 /**< \brief Description (0x502.13): EEPROM Acknowledge/Command*/
|
||||
#define ESC_EEPROM_BUSY_MASK 0x8000 /**< \brief Description (0x502.15): EEPROM Busy*/
|
||||
/* EEPROM command and status bit masks (based on "ESC_EEPROM_CONTROL_OFFSET") - END*/
|
||||
#define ESC_EEPROM_ADDRESS_OFFSET 0x0504
|
||||
#define ESC_EEPROM_DATA_OFFSET 0x0508
|
||||
|
||||
#define ESC_SYNCMAN_REG_OFFSET 0x0800 /**< \brief Register Description: Start address of the SyncManager Configuration/Staus registers*/
|
||||
#define ESC_SYNCMAN_CONTROL_OFFSET 0x0804 /**< \brief Register Description: SyncManager Setting Register*/
|
||||
|
||||
|
||||
#define ESC_SYNCMAN_ACTIVE_OFFSET 0x0806 /**< \brief Register Description: SyncManager Activation Register*/
|
||||
|
||||
|
||||
#define ESC_SYSTEMTIME_OFFSET 0x0910 /**< \brief Register Description: Local copy of the System Time*/
|
||||
|
||||
#define ESC_DC_UNIT_CONTROL_OFFSET 0x0980 /**< \brief Register Description: Control registers for Cycle and Sync Unit (can be predefined with the "AssignActivate" Element in the device description, for further information see ETG.2000)*/
|
||||
#define ESC_DC_SYNC_UNIT_ACTIVE_MASK 0x0100 /**< \brief Description (0x980.8): Sync Out Unit is activated*/
|
||||
#define ESC_DC_SYNC0_ACTIVE_MASK 0x0200 /**< \brief Description (0x980.9): Sync0 generation is activated*/
|
||||
#define ESC_DC_SYNC1_ACTIVE_MASK 0x0400 /**< \brief Description (0x980.10): Sync1 generation is activated*/
|
||||
#define ESC_DC_SYNC_UNIT_AUTO_ACTIVE_MASK 0x0800 /**< \brief Description (0x980.11): Sync Out Unit is activated automatic when System time was written*/
|
||||
|
||||
#define ESC_DC_SYNC0_CYCLETIME_OFFSET 0x09A0 /**< \brief Register Description: 32Bit Time between two consecutive SYNC0 pulses in ns*/
|
||||
#define ESC_DC_SYNC1_CYCLETIME_OFFSET 0x09A4 /**< \brief Register Description: 32Bit Time between two consecutive SYNC1 pulses in ns*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- Sync Manager
|
||||
-----------------------------------------------*/
|
||||
/** \brief SyncManager Register struture*/
|
||||
typedef struct STRUCT_PACKED_START
|
||||
{
|
||||
|
||||
UINT16 PhysicalStartAddress; /**< \brief SyncManager address*/
|
||||
UINT16 Length; /**< \brief SyncManager length*/
|
||||
|
||||
UINT16 Settings[2]; /**< \brief Register values 0x0804 : 0x0807*/
|
||||
|
||||
/*Defines to access :
|
||||
* - SM Control register 0x0804
|
||||
* - SM Status register 0x0805
|
||||
*
|
||||
* all defines are based on 0x0804
|
||||
*/
|
||||
|
||||
/*SyncManger control (0x0804) access*/
|
||||
#define SM_SETTING_CONTROL_OFFSET 0 /**< \brief Offset to value of register 0x0804*/
|
||||
#define SM_SETTING_MODE_MASK 0x0002 /**< \brief SyncManager mode mask*/
|
||||
#define SM_SETTING_MODE_THREE_BUFFER_VALUE 0x0000 /**< \brief SyncManager 3Buffer mode value*/
|
||||
#define SM_SETTING_MODE_ONE_BUFFER_VALUE 0x0002 /**< \brief SyncManager 1Buffer mode value*/
|
||||
#define SM_SETTING_DIRECTION_MASK 0x000C /**< \brief SyncManager direction mask*/
|
||||
#define SM_SETTING_DIRECTION_READ_VALUE 0x0000 /**< \brief SyncManager read direction*/
|
||||
#define SM_SETTING_DIRECTION_WRITE_VALUE 0x0004 /**< \brief SyncManager write direction*/
|
||||
#define SM_SETTING_WATCHDOG_VALUE 0x0040 /**< \brief SyncManager watchdog value*/
|
||||
|
||||
/*SyncManger status (0x0805) access*/
|
||||
#define SM_STATUS_MBX_BUFFER_FULL 0x0800 /**< \brief Indicates in one buffer mode if buffer was completely written*/
|
||||
|
||||
/*Defines to access :
|
||||
* - SM Active register 0x0806
|
||||
* - SM PDI Control register 0x0807
|
||||
*
|
||||
* all defines are based on 0x0806
|
||||
*/
|
||||
|
||||
/*SyncManger active (0x0806) access*/
|
||||
#define SM_SETTING_ACTIVATE_OFFSET 1 /**< \brief Offset to value of register 0x0806*/
|
||||
#define SM_SETTING_ENABLE_VALUE 0x0001 /**< \brief SyncManager enable*/
|
||||
#define SM_SETTING_REPAET_REQ_MASK 0x0002 /**< \brief SyncManager Repeat request*/
|
||||
#define SM_SETTING_REPEAT_REQ_SHIFT 0 /**< \brief SyncManager Repeat request shift*/
|
||||
|
||||
/*SyncManger PDI Control (0x0807) access*/
|
||||
#define SM_SETTING_PDI_DISABLE 0x0100 /**< \brief Bit0 of register 0x0807 (if 1 SM is disabled from PDI)*/
|
||||
#define SM_SETTING_REPEAT_ACK 0x0200 /**< \brief Bit1 of register 0x0807*/
|
||||
}STRUCT_PACKED_END
|
||||
TSYNCMAN;
|
||||
|
||||
|
||||
|
||||
#endif //_ESC_H_
|
||||
/** @}*/
|
||||
@@ -0,0 +1,954 @@
|
||||
/**
|
||||
\addtogroup Mailbox Mailbox Functions
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file mailbox.c
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief Implementation
|
||||
|
||||
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 0):<br>
|
||||
There are two mailbox buffer for sending and receiving mailbox services.<br>
|
||||
Normal operation:<br>
|
||||
When starting the mailbox handler psWriteMbx contains mailbox buffer 1,<br>
|
||||
psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
|
||||
In this state a repeat request would be ignored because there was no service sent yet.<br>
|
||||
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psWriteMbx gets mailbox buffer 2<br>
|
||||
and psReadMbx gets the sent mailbox buffer 1, psRepeatMbx and psStoreMbx are still 0.<br>
|
||||
When the first mailbox service was read from the master, the sent mailbox buffer 1 is stored<br>
|
||||
in psRepeatMbx (in MBX_MailboxReadInd).<br>
|
||||
After that psReadMbx gets always the actual sent mailbox buffer, psWriteMbx is set to 0 (another<br>
|
||||
received mailbox service from the master will not be handled until the sent mailbox service was read<br>
|
||||
and MBX_MailboxReadInd was called).<br>
|
||||
When the mailbox service is read, psWriteMbx gets the Buffer of psRepeatMbx and psRepeatMbx gets the <br>
|
||||
buffer of psReadMbx.<br>
|
||||
Repeat Request from the master:<br>
|
||||
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
|
||||
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
|
||||
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
|
||||
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0<br>
|
||||
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
|
||||
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
|
||||
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
|
||||
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0.<br>
|
||||
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
|
||||
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.<br>
|
||||
|
||||
<br>Description of the mailbox buffer handling (MAILBOX_QUEUE = 1):<br>
|
||||
There are two mailbox buffer for sending and receiving mailbox services.<br>
|
||||
Normal operation (psWriteMbx is only used for local storage):<br>
|
||||
When starting the mailbox handler psReadMbx, psRepeatMbx and psStoreMbx are 0.<br>
|
||||
In this state a repeat request would be ignored because there was no service sent yet.<br>
|
||||
When a mailbox service is received from the master (in MBX_CheckAndCopyMailbox) a mailbox buffer<br>
|
||||
will be get with APPL_AllocMailboxBuffer and the corresponding protocol service function will<br>
|
||||
be called (in MBX_WriteMailboxInd). This buffer shall be used for the protocol service response.<br>
|
||||
When the first mailbox service is sent (in MBX_CopyToSendMailbox), psReadMbx gets the sent mailbox buffer,<br>
|
||||
psRepeatMbx and psStoreMbx are still 0.<br>
|
||||
When the first mailbox service was read from the master, the sent mailbox buffer (psReadMbx) is stored<br>
|
||||
in psRepeatMbx (in MBX_MailboxReadInd).<br>
|
||||
After that psReadMbx gets always the actual sent mailbox buffer (in MBX_CopyToSendMailbox)<br>
|
||||
When the mailbox service is read, psRepeatMbx is returned (with APPL_FreeMailboxBuffer) and psRepeatMbx gets the <br>
|
||||
buffer of psReadMbx.<br>
|
||||
Repeat Request from the master:<br>
|
||||
When a Repeat from the master is requested (MBX_MailboxRepeatReq), there are three different possibilities:<br>
|
||||
1. no mailbox service was sent since the mailbox handler was started (psRepeatMbx = 0): nothing to do<br>
|
||||
2. the acknowledge of the last sent mailbox service was received (in MBX_MailboxReadInd) (bSendMbxIsFull = 0):<br>
|
||||
the last sent mailbox service (psRepeatMbx) will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0<br>
|
||||
3. the acknowledge of the last sent mailbox service was not received (psReadMbx and psRepeatMbx contain different buffers,<br>
|
||||
psReadMbx is still in the mailbox (because MBX_MailboxReadInd is not called yet, bSendMbxIsFull = 1): <br>
|
||||
psReadMbx will be deleted in the mailbox (call of DisableSyncManChannel and EnableSyncManChannel) and<br>
|
||||
stored in psStoreMbx, psRepeatMbx will be sent again (in MBX_CopyToSendMailbox) and stored in psReadMbx,<br>
|
||||
psRepeatMbx will be set to 0.<br>
|
||||
When the repeated mailbox service was sent (call of MBX_MailboxReadInd), psReadMbx will be stored in psRepeatMbx<br>
|
||||
and psStoreMbx will be sent (in MBX_CopyToSendMailbox) and stored in psReadMbx, psStoreMbx will be set to 0.
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 ECAT7: add missing big endian swapping<br>
|
||||
V5.11 HW1: "move hardware independent functions ""HW_DisableSyncManChannel()"", ""HW_EnableSyncManChannel()"", ""HW_GetSyncMan()"", ""HW_ResetALEventMask()"", ""HW_SetALEventMask()"" to ecatalv.c"<br>
|
||||
V5.11 MBX2: "check in IP if enough dynamic memory is available to handle mailbox communication, if it is not the case the mbx error ""no memory"" is returned on any mbx request"<br>
|
||||
V5.11 MBX3: set application triggered emergency and EoE data to pending if no mailbox queue is supported and another mailbox request is currently handled, Handle only one mailbox request at a time (in case that MAILBPX_QUEUE is disabled)<br>
|
||||
V5.11 TEST4: add new mailbox test behaviour (the master mailbox cnt shall be incremented by 1 and the slave mailbox cnt is alternating)<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 EOE3: Change local send frame pending indication variable to a global variable (it need to be resetted if the mailbox is stopped and a frame is pending)<br>
|
||||
V5.10 ESC6: Update SyncManager register size (only for 8Bit ESC access)<br>
|
||||
V5.10 MBX2: Allocate only memory for current configured mailbox size (max supported size was allocated before)<br>
|
||||
<br>Changes to version V5.0:<br>
|
||||
V5.01 ESC2: Add missed value swapping<br>
|
||||
V5.01 MBX2: Return an error if length specified in mailbnox header is too large.<br>
|
||||
<br>Changes to version V4.40:<br>
|
||||
V5.0 ECAT3: Global dummy variables used for dummy ESC operations.<br>
|
||||
V5.0 ESC1: ESC 32Bit Access added.<br>
|
||||
V5.0 MBX2: Prevent double swapping (MBX repeat service) of MBX length field.<br>
|
||||
V5.0 MBX4: Unlock SM buffer based on SM state.<br>
|
||||
<br>Changes to version V4.11:<br>
|
||||
V4.40 MBX8: Handle pending mailbox data even if mailbox queue is enabled<br>
|
||||
V4.40 MBX7: Reset SyncManger 1 Repeat Acknowledge bit if local variable is reset<br>
|
||||
V4.40 EMCY2: Send queued emergency messages<br>
|
||||
V4.40 BOOT2: Support only FoE in Bootstrap state<br>
|
||||
V4.40 MBX5: Prevent multiple free buffer<br>
|
||||
V4.40 MBX6: change return value if no mailbox buffer is available<br>
|
||||
<br>Changes to version V4.08:<br>
|
||||
V4.11 MBX 1: free some dynamic memory to avoid memory leaks in the mailbox queue<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.10 MBX 1: check Mailbox message buffer and print emergency messages<br>
|
||||
during state change Init -> PreOP<br>
|
||||
<br>Changes to version V4.07:<br>
|
||||
V4.08 MBX 1: The mailbox queue was added (switch MAILBOX_QUEUE), if the<br>
|
||||
mailbox should be handled in the ESC-Interrupt Service Routine<br>
|
||||
the defines ENTER_MBX_CRITICAL and LEAVE_MBX_CRITICAL could be used<br>
|
||||
to disable the interrupts or change the priority<br>
|
||||
V4.08 AOE 1: AoE was added<br>
|
||||
<br>Changes to version V4.06:<br>
|
||||
V4.07 ECAT 1: The sources for SPI and MCI were merged (in ecat_def.h<br>
|
||||
set the switch MCI_HW to 1 when using the MCI,<br>
|
||||
set the switch SPI_HW to 1 when using the SPI
|
||||
*/
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
#include "ecatslv.h"
|
||||
|
||||
|
||||
#define _MAILBOX_ 1
|
||||
#include "mailbox.h"
|
||||
#undef _MAILBOX_
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
/*remove definition of _MAILBOX_ (#ifdef is used in mailbox.h)*/
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
|
||||
#include "ecatcoe.h"
|
||||
|
||||
#include <stdio.h> /* printf: SM 几何交叉校验 WARN(1.5.59,见 MBX_StartMailboxHandler) */
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Types and Defines
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal Variables
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
/*variables are declared in ecatslv.c*/
|
||||
extern VARVOLATILE UINT16 u16dummy;
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
BOOL bNoMbxMemoryAvailable; /**< \brief Indicates if enough dynamic memory is available to handle at least one mailbox datagram */
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
/*--------------------------------------------------------------------------------------
|
||||
------
|
||||
------ internal functions
|
||||
------
|
||||
--------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------------------------------------------------
|
||||
------
|
||||
------ functions
|
||||
------
|
||||
---------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// PutInMbxQueue
|
||||
//
|
||||
|
||||
UINT8 PutInMbxQueue(TMBX MBXMEM * pMbx, TMBXQUEUE MBXMEM * pQueue)
|
||||
{
|
||||
UINT16 lastInQueue;
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
|
||||
lastInQueue = pQueue->lastInQueue+1;
|
||||
if (lastInQueue == pQueue->maxQueueSize)
|
||||
{
|
||||
// Umbruch der Queue
|
||||
lastInQueue = 0;
|
||||
}
|
||||
|
||||
if (pQueue->firstInQueue == lastInQueue)
|
||||
{
|
||||
// Ueberlauf der Queue -> letztes Element wieder herausnehmen
|
||||
LEAVE_MBX_CRITICAL;
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
|
||||
pQueue->queue[pQueue->lastInQueue] = pMbx;
|
||||
pQueue->lastInQueue = lastInQueue;
|
||||
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// GetOutOfMbxQueue
|
||||
//
|
||||
|
||||
TMBX MBXMEM * GetOutOfMbxQueue(TMBXQUEUE MBXMEM * pQueue)
|
||||
{
|
||||
TMBX MBXMEM * pMbx;
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
if (pQueue->firstInQueue != pQueue->lastInQueue)
|
||||
{
|
||||
// Queue ist nicht leer
|
||||
UINT16 firstInQueue = pQueue->firstInQueue;
|
||||
pMbx = pQueue->queue[firstInQueue];
|
||||
firstInQueue++;
|
||||
pQueue->firstInQueue = firstInQueue;
|
||||
if (pQueue->firstInQueue == pQueue->maxQueueSize)
|
||||
{
|
||||
// Umbruch der Queue
|
||||
pQueue->firstInQueue = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
pMbx = 0;
|
||||
|
||||
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return pMbx;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function intialize the Mailbox Interface.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_Init(void)
|
||||
{
|
||||
u16ReceiveMbxSize = MIN_MBX_SIZE;
|
||||
u16SendMbxSize = MAX_MBX_SIZE;
|
||||
u16EscAddrReceiveMbx = MIN_MBX_WRITE_ADDRESS;
|
||||
u16EscAddrSendMbx = MIN_MBX_READ_ADDRESS;
|
||||
|
||||
sMbxReceiveQueue.firstInQueue = 0;
|
||||
sMbxReceiveQueue.lastInQueue = 0;
|
||||
sMbxReceiveQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE;
|
||||
sMbxSendQueue.firstInQueue = 0;
|
||||
sMbxSendQueue.lastInQueue = 0;
|
||||
sMbxSendQueue.maxQueueSize = MAX_MBX_QUEUE_SIZE;
|
||||
psWriteMbx = NULL;
|
||||
|
||||
|
||||
psRepeatMbx = NULL;
|
||||
psReadMbx = NULL;
|
||||
psStoreMbx = NULL;
|
||||
|
||||
bMbxRepeatToggle = FALSE;
|
||||
/*Reset Repeat acknowledge bit of SyncManager1 (0x80F bit 2)*/
|
||||
{
|
||||
UINT16 sm1Activate = 0;
|
||||
HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
sm1Activate &= SWAPWORD(~0x0200);
|
||||
HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
}
|
||||
bMbxRunning = FALSE;
|
||||
bSendMbxIsFull = FALSE;
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
u8MailboxSendReqStored = 0;
|
||||
u8MbxWriteCounter = 0;
|
||||
u8MbxReadCounter = 0;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function includes the state transition from INIT to
|
||||
\brief PRE-OPERATIONAL in the EtherCAT Slave corresponding to
|
||||
\brief local management service Start Mailbox Handler
|
||||
\brief it is checked if the mailbox areas overlaps each other
|
||||
\brief and the Sync Manager channels 0 and 1 are enabled.
|
||||
\brief This function shall only be called if mailbox is supported.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
UINT16 MBX_StartMailboxHandler(void)
|
||||
{
|
||||
UINT16 result = 0;
|
||||
/* 1.5.59 根治"邮箱楔死"(响应帧头落在 SM1 start+4/+8、逐会话漂移,断电才清):
|
||||
* 根因链 = INIT→PREOP 时经 GetSyncMan/CSR 单次间接读(EXMC HBI)把 SM0/SM1
|
||||
* 几何缓存进 u16EscAddr* 与 u16*MbxSize 等变量;总线毛刺给出的"合法"错值
|
||||
* (0x1204/0x1208 均为偶地址且在检查范围内)即被缓存,此后所有 SDO 响应写错址,
|
||||
* 主站按协议从 0x1200 读永远错位,重枚举重读仍错 → 只有断电能解。本板邮箱
|
||||
* 几何是 SII 固定值(gen_eeprom.py 的 MBX_RX/TX_OFFSET/SIZE,ESC 上电自行
|
||||
* 加载,主站 config_init 也只是照抄),改为构建期常量(ecat_def.h:
|
||||
* MBX_SM0/1_START/SIZE)直达缓存,CSR 读降级为交叉校验:失配打 WARN(说明
|
||||
* ESC 寄存器被非常规改写,需查)但缓存仍取常量——CSR 错值从此没有进入
|
||||
* 缓存的路径,楔死无建立点;运行期内存踩踏也会在每次 INIT→PREOP 被常量覆写。
|
||||
* 注意:ESC 寄存器经 PDI/CSR 读出即主机序(与 GetSyncMan 既有路径一致,勿加
|
||||
* SWAPWORD;PRAM 帧数据才是线上大端,那边交换是对的)。 */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
TSYNCMAN ESCMEM * pSyncMan = (TSYNCMAN ESCMEM *)GetSyncMan(MAILBOX_WRITE);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
if (pSyncMan->PhysicalStartAddress != MBX_SM0_START || pSyncMan->Length != MBX_SM0_SIZE)
|
||||
{
|
||||
printf("[ECAT][MBX] WARN: SM0 reg 0x%04X/%u != build-time 0x%04X/%u -> keep build-time\r\n",
|
||||
(unsigned)pSyncMan->PhysicalStartAddress, (unsigned)pSyncMan->Length,
|
||||
(unsigned)MBX_SM0_START, (unsigned)MBX_SM0_SIZE);
|
||||
}
|
||||
/* store size of the receive mailbox */
|
||||
u16ReceiveMbxSize = MBX_SM0_SIZE;
|
||||
/* store the address of the receive mailbox */
|
||||
u16EscAddrReceiveMbx = MBX_SM0_START;
|
||||
|
||||
/* get address of the send mailbox sync manager (SM1) */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
pSyncMan =(TSYNCMAN ESCMEM *) GetSyncMan(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
if (pSyncMan->PhysicalStartAddress != MBX_SM1_START || pSyncMan->Length != MBX_SM1_SIZE)
|
||||
{
|
||||
printf("[ECAT][MBX] WARN: SM1 reg 0x%04X/%u != build-time 0x%04X/%u -> keep build-time\r\n",
|
||||
(unsigned)pSyncMan->PhysicalStartAddress, (unsigned)pSyncMan->Length,
|
||||
(unsigned)MBX_SM1_START, (unsigned)MBX_SM1_SIZE);
|
||||
}
|
||||
|
||||
/* store the size of the send mailbox */
|
||||
u16SendMbxSize = MBX_SM1_SIZE;
|
||||
/* store the address of the send mailbox */
|
||||
u16EscAddrSendMbx = MBX_SM1_START;
|
||||
|
||||
// HBu 02.05.06: it should be checked if there are overlaps in the sync manager areas
|
||||
if ((u16EscAddrReceiveMbx + u16ReceiveMbxSize) > u16EscAddrSendMbx && (u16EscAddrReceiveMbx < (u16EscAddrSendMbx + u16SendMbxSize)))
|
||||
{
|
||||
return ALSTATUSCODE_INVALIDMBXCFGINPREOP;
|
||||
}
|
||||
|
||||
/* enable the receive mailbox sync manager channel */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
EnableSyncManChannel(MAILBOX_WRITE);
|
||||
/* enable the send mailbox sync manager channel */
|
||||
EnableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize);
|
||||
if(psWriteMbx == NULL)
|
||||
{
|
||||
bNoMbxMemoryAvailable = TRUE;
|
||||
|
||||
//check if at least enough memory for an mailbox error is available (other wise stop the state transition)
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/
|
||||
if(psWriteMbx == NULL)
|
||||
{
|
||||
result = ALSTATUSCODE_NOMEMORY;
|
||||
}
|
||||
|
||||
APPL_FreeMailboxBuffer(psWriteMbx);
|
||||
}
|
||||
else
|
||||
{
|
||||
bNoMbxMemoryAvailable = FALSE;
|
||||
APPL_FreeMailboxBuffer(psWriteMbx);
|
||||
}
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
|
||||
\brief This function includes the state transition from
|
||||
\brief PRE-OPERATIONAL to INIT in the EtherCAT Slave corresponding to
|
||||
\brief local management service Stop Mailbox Handler
|
||||
\brief the Sync Manager channels 0 and 1 are disabled
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_StopMailboxHandler(void)
|
||||
{
|
||||
TMBX MBXMEM * pMbx;
|
||||
|
||||
/* mailbox handler is stopped */
|
||||
bMbxRunning = FALSE;
|
||||
/* disable the receive mailbox sync manager channel */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
DisableSyncManChannel(MAILBOX_WRITE);
|
||||
/* disable the send mailbox sync manager channel */
|
||||
DisableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
/* initialize variables again */
|
||||
|
||||
|
||||
if (psRepeatMbx != NULL)
|
||||
APPL_FreeMailboxBuffer(psRepeatMbx);
|
||||
|
||||
if (psStoreMbx != NULL && psStoreMbx != psRepeatMbx)
|
||||
APPL_FreeMailboxBuffer(psStoreMbx);
|
||||
|
||||
if (psReadMbx != NULL && psReadMbx != psRepeatMbx && psReadMbx != psStoreMbx)
|
||||
APPL_FreeMailboxBuffer(psReadMbx);
|
||||
|
||||
|
||||
psRepeatMbx = NULL;
|
||||
psReadMbx = NULL;
|
||||
psStoreMbx = NULL;
|
||||
|
||||
bMbxRepeatToggle = FALSE;
|
||||
/*Reset Repeat acknowledge bit of SyncManager1 (0x080F bit 2)*/
|
||||
{
|
||||
UINT16 sm1Activate = 0;
|
||||
HW_EscReadWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
sm1Activate &= SWAPWORD(~0x0200);
|
||||
HW_EscWriteWord(sm1Activate,(ESC_SYNCMAN_ACTIVE_OFFSET + SIZEOF_SM_REGISTER));
|
||||
}
|
||||
bSendMbxIsFull = FALSE;
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
u8MailboxSendReqStored = 0;
|
||||
u8MbxWriteCounter = 0;
|
||||
u8MbxReadCounter = 0;
|
||||
|
||||
do
|
||||
{
|
||||
pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
} while (pMbx != NULL);
|
||||
do
|
||||
{
|
||||
pMbx = GetOutOfMbxQueue(&sMbxSendQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
} while (pMbx != NULL);
|
||||
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the received Mailbox command from Master.
|
||||
|
||||
\brief The function checks the mailbox header for the requested service and calls the
|
||||
\brief corresponding XXXX_ServiceInd-function
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MailboxServiceInd(TMBX MBXMEM *pMbx)
|
||||
{
|
||||
UINT8 result;
|
||||
|
||||
|
||||
switch ( (pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] & MBX_MASK_TYPE) >> MBX_SHIFT_TYPE )
|
||||
{
|
||||
case MBX_TYPE_COE:
|
||||
/* CoE datagram received */
|
||||
result = COE_ServiceInd((TCOEMBX MBXMEM *) pMbx);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
result = MBXERR_UNSUPPORTEDPROTOCOL;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the received Mailbox command from Master.
|
||||
|
||||
\brief This function is called when the Master has written the Receive-Mailbox.
|
||||
\brief It will only be called if the send mailbox is empty, that a response for the
|
||||
\brief mailbox service could be stored.
|
||||
\brief The function checks the mailbox header for the requested service and calls the
|
||||
\brief corresponding XXXX_ServiceInd-function
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx)
|
||||
{
|
||||
UINT8 result = 0;
|
||||
UINT8 mbxCounter = pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] >> MBX_SHIFT_COUNTER;
|
||||
UINT16 MbxLen = SWAPWORD(pMbx->MbxHeader.Length);
|
||||
|
||||
if(MbxLen > MAX_MBX_SIZE)
|
||||
{
|
||||
/* Mailbox error response: size specified in mailbox header too large*/
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE;
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(MBXERR_INVALIDSIZE);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
else
|
||||
/* if the mailbox datagram counter (Bit 4-6 of Byte 5 of the mailbox header) is unequal zero,
|
||||
the master supports the mailbox data link layer,
|
||||
in that case a repeated mailbox write request will be detected, if the counter is unequal zero
|
||||
and unchanged */
|
||||
if ( mbxCounter == 0 || mbxCounter != u8MbxWriteCounter )
|
||||
{
|
||||
/* new mailbox service received */
|
||||
/* mbxCounter = 0: old EtherCAT master */
|
||||
/* new MBX service received, store the new mailbox counter */
|
||||
u8MbxWriteCounter = mbxCounter;
|
||||
|
||||
{
|
||||
/* check the protocol type and call the XXXX_ServiceInd-function */
|
||||
|
||||
result = PutInMbxQueue(pMbx, &sMbxReceiveQueue);
|
||||
}
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~MBX_MASK_TYPE;
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(result);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
}
|
||||
// the mailbox buffer has to be freed here
|
||||
else
|
||||
{
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
pMbx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called when the Master has read the Send-Mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxReadInd(void)
|
||||
{
|
||||
bSendMbxIsFull = FALSE;
|
||||
// HBu 02.05.06: the pointer psRepeatMbx is only free if there is no stored
|
||||
// mailbox service from the last repeat
|
||||
if ( psRepeatMbx && psStoreMbx == NULL )
|
||||
{
|
||||
/* the last sent service is not stored for repeat any longer */
|
||||
APPL_FreeMailboxBuffer(psRepeatMbx);
|
||||
psRepeatMbx = NULL;
|
||||
}
|
||||
|
||||
/* the actual sent service has to be stored for repeat */
|
||||
psRepeatMbx = psReadMbx;
|
||||
|
||||
if ( psStoreMbx )
|
||||
{
|
||||
/* there was a buffer stored */
|
||||
MBX_CopyToSendMailbox(psStoreMbx);
|
||||
/* no more buffer to be stored any more */
|
||||
psStoreMbx = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
TMBX MBXMEM *pMbx = GetOutOfMbxQueue(&sMbxSendQueue);
|
||||
if (pMbx)
|
||||
{
|
||||
MBX_CopyToSendMailbox(pMbx);
|
||||
}
|
||||
}
|
||||
|
||||
if ( u8MailboxSendReqStored )
|
||||
{
|
||||
/* there are mailbox services stored to be sent */
|
||||
if ( u8MailboxSendReqStored & COE_SERVICE )
|
||||
{
|
||||
UINT8 result = 0;
|
||||
/* reset the flag indicating that CoE service to be sent was stored */
|
||||
u8MailboxSendReqStored &= ~COE_SERVICE;
|
||||
|
||||
/* call CoE function that will send the stored CoE service */
|
||||
result = COE_ContinueInd(psWriteMbx);
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
/*Set the pending CoE indication is an error occurred during the continue indication*/
|
||||
u8MailboxSendReqStored |= COE_SERVICE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called if the Master has requested a resending of the last
|
||||
\brief sent mailbox
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_MailboxRepeatReq(void)
|
||||
{
|
||||
if (psRepeatMbx)
|
||||
{
|
||||
TMBX MBXMEM *pMbx = psRepeatMbx;
|
||||
/* send mailbox service stored for repeat */
|
||||
/* HBu 13.10.06: if a repeat request is received (again) before the previously repeated mailbox telegram
|
||||
was read from the master (psStoreMbx != NULL) the next mailbox telegram to be sent is still in the
|
||||
read mailbox so it has not to updated exchanged */
|
||||
ENTER_MBX_CRITICAL;
|
||||
|
||||
if (bSendMbxIsFull && psStoreMbx == NULL)
|
||||
{
|
||||
/* mailbox is full, take the buffer off */
|
||||
/*ECATCHANGE_START(V5.11) HW1*/
|
||||
DisableSyncManChannel(MAILBOX_READ);
|
||||
|
||||
/* store the buffer to be sent next */
|
||||
psStoreMbx = psReadMbx;
|
||||
/* enable the mailbox again */
|
||||
EnableSyncManChannel(MAILBOX_READ);
|
||||
/*ECATCHANGE_END(V5.11) HW1*/
|
||||
|
||||
/* HBu 15.02.06: flag has to be reset otherwise the mailbox service
|
||||
will not be copied by MBX_CopyToSendMailbox */
|
||||
bSendMbxIsFull = FALSE;
|
||||
}
|
||||
|
||||
|
||||
MBX_CopyToSendMailbox(pMbx);
|
||||
// HBu 17.06.06: psRepeatMbx has to be set to 0, when it was repeated, otherwise it would be returned twice
|
||||
// to the empty queue (MAILBOX_QUEUE=1) or a buffer get lost, if the the next repeat request will happen before
|
||||
// the repeated buffer was read
|
||||
psRepeatMbx = NULL;
|
||||
LEAVE_MBX_CRITICAL;
|
||||
}
|
||||
|
||||
// Repeat was finished, toggle the acknowledge bit
|
||||
bMbxRepeatToggle = !bMbxRepeatToggle;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to a Mailbox command to be sent (read by the Master)
|
||||
\param flags Bit 0-6: mailbox protocol type:
|
||||
0x01 - emergency service
|
||||
0x02 - CoE service
|
||||
0x04 - SoE service
|
||||
0x80 - EoE service
|
||||
0x10 - AoE service
|
||||
0x20 - VoE service
|
||||
Bit 7: 0 - no more fragments to be sent for the requested mailbox service
|
||||
1 - additional fragments to be sent for the mailbox service, the
|
||||
corresponding XXXX_ContinueInd-function will be called to get
|
||||
the next fragment
|
||||
|
||||
\return 0: Success - mailbox command could be stored in the send mailbox
|
||||
1: Failed - mailbox command could not be stored in the send mailbox, the
|
||||
XXXX_ContinueInd service will be called when the mailbox was
|
||||
read from the master to
|
||||
|
||||
\brief This function puts a new Mailbox service in the Send Mailbox
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MBX_MailboxSendReq( TMBX MBXMEM * pMbx, UINT8 flags )
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
/* HBu 06.02.06: in INIT-state a mailbox send request shall be refused */
|
||||
if ( (nAlStatus & STATE_MASK) == STATE_INIT )
|
||||
return ERROR_INVALIDSTATE;
|
||||
|
||||
|
||||
ENTER_MBX_CRITICAL;
|
||||
DISABLE_MBX_INT;
|
||||
|
||||
/* the counter in the mailbox header has to be incremented with every new mailbox service to be sent
|
||||
if the mailbox data link layer is supported (software switch MAILBOX_REPEAT_SUPPORTED set)*/
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] &= ~MBX_MASK_COUNTER;
|
||||
/* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */
|
||||
/* u8MbxCounter holds the actual counter for the mailbox header, only the values
|
||||
1-7 are allowed if the mailbox data link layer is supported */
|
||||
if ( (u8MbxReadCounter & 0x07) == 0 )
|
||||
u8MbxReadCounter = 1;
|
||||
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_COUNTER] |= u8MbxReadCounter << MBX_SHIFT_COUNTER;
|
||||
|
||||
/* try to copy the mailbox command in the ESC */
|
||||
if ( MBX_CopyToSendMailbox(pMbx) != 0 )
|
||||
{
|
||||
/* no success, send mailbox was full, set flag */
|
||||
result = PutInMbxQueue(pMbx, &sMbxSendQueue);
|
||||
if (result != 0)
|
||||
flags |= FRAGMENTS_FOLLOW;
|
||||
else
|
||||
u8MbxReadCounter++;
|
||||
}
|
||||
/* HBu 13.02.06: Repeat-Counter was incremented too much if the mailbox service could not be sent */
|
||||
else
|
||||
{
|
||||
u8MbxReadCounter++;
|
||||
}
|
||||
|
||||
if ( flags & FRAGMENTS_FOLLOW )
|
||||
{
|
||||
/* store the mailbox service that the corresponding XXX_ContinueInd function will
|
||||
be called when the send mailbox will have been read by the master because there
|
||||
are mailbox commands to be sent for this service */
|
||||
u8MailboxSendReqStored |= (flags & ((UINT8) ~FRAGMENTS_FOLLOW));
|
||||
}
|
||||
|
||||
ENABLE_MBX_INT;
|
||||
LEAVE_MBX_CRITICAL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is used to check if the received mailbox command can be processed.
|
||||
|
||||
Also the contents of the Receive Mailbox will be copied in the variable sMbx.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_CheckAndCopyMailbox( void )
|
||||
{
|
||||
UINT16 mbxLen;
|
||||
|
||||
/* get the size of the received mailbox command and acknowledge the event*/
|
||||
HW_EscReadWord(mbxLen,u16EscAddrReceiveMbx);
|
||||
|
||||
/* the size has to be swapped here, all other bytes of the mailbox service will be swapped later */
|
||||
mbxLen = SWAPWORD(mbxLen);
|
||||
|
||||
/* ECATCHANGE_START(V5.11) MBX2*/
|
||||
if(bNoMbxMemoryAvailable == TRUE)
|
||||
{
|
||||
/* Return a no memory error in case of any mailbox request*/
|
||||
TMBX MBXMEM *pMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(10); /* a mailbox error datagram length*/
|
||||
|
||||
if(pMbx != NULL)
|
||||
{
|
||||
HMEMSET(pMbx,0x00,10);
|
||||
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE);
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(MBXERR_NOMOREMEMORY);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
|
||||
APPL_FreeMailboxBuffer(pMbx);
|
||||
}
|
||||
}
|
||||
/* ECATCHANGE_END(V5.11) MBX2*/
|
||||
/* the length of the mailbox data is in the first two bytes of the mailbox,
|
||||
so the length of the mailbox header has to be added */
|
||||
mbxLen += MBX_HEADER_SIZE;
|
||||
|
||||
/* in this example there are only two mailbox buffers available in the firmware (one for processing and
|
||||
one to stored the last sent response for a possible repeat request), so a
|
||||
received mailbox service can only be processed if a free buffer is available */
|
||||
if ( ( bSendMbxIsFull ) /* a received mailbox service will not be processed
|
||||
as long as the send mailbox is still full
|
||||
(waits to be read from the master) */
|
||||
||( u8MailboxSendReqStored ) /* a mailbox service to be sent is still stored
|
||||
so the received mailbox service will not be processed
|
||||
until all stored mailbox services are sent */
|
||||
)
|
||||
{
|
||||
/* set flag that the processing of the mailbox service will be checked in the
|
||||
function MBX_Main (called from ECAT_Main) */
|
||||
bReceiveMbxIsLocked = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* received mailbox command can be processed, reset flag */
|
||||
bReceiveMbxIsLocked = FALSE;
|
||||
|
||||
/* if the read mailbox size is too big for the buffer, set the copy size to the maximum buffer size, otherwise
|
||||
memory could be overwritten,
|
||||
the evaluation of the mailbox size will be done in the mailbox protocols called from MBX_WriteMailboxInd */
|
||||
|
||||
if (mbxLen > u16ReceiveMbxSize)
|
||||
mbxLen = u16ReceiveMbxSize;
|
||||
|
||||
psWriteMbx = (TMBX MBXMEM *) APPL_AllocMailboxBuffer(u16ReceiveMbxSize);
|
||||
|
||||
/* if there is no more memory for mailbox buffer, the mailbox should not be read */
|
||||
if (psWriteMbx == NULL)
|
||||
{
|
||||
/* set flag that the processing of the mailbox service will be checked in the
|
||||
function MBX_Main (called from ECAT_Main) */
|
||||
bReceiveMbxIsLocked = TRUE;
|
||||
return;
|
||||
}
|
||||
/* copy the mailbox header and data*/
|
||||
HW_EscReadMbxMem((MEM_ADDR MBXMEM *) psWriteMbx,u16EscAddrReceiveMbx,mbxLen);
|
||||
|
||||
|
||||
{
|
||||
/*Read Control and Status of SyncManager 0 to check if the buffer is unlocked*/
|
||||
VARVOLATILE UINT16 smstate = 0x00;
|
||||
HW_EscReadWord(smstate,ESC_SYNCMAN_CONTROL_OFFSET);
|
||||
/*ECATCHANGE_START(V5.11) ECAT7*/
|
||||
smstate = SWAPWORD(smstate);
|
||||
/*ECATCHANGE_END(V5.11) ECAT7*/
|
||||
|
||||
if(smstate & SM_STATUS_MBX_BUFFER_FULL)
|
||||
{
|
||||
/*Unlock the mailbox SyncManger buffer*/
|
||||
u16dummy = 0;
|
||||
HW_EscReadWord(u16dummy,(u16EscAddrReceiveMbx + u16ReceiveMbxSize - 2));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* in MBX_MailboxWriteInd the mailbox protocol will be processed */
|
||||
MBX_MailboxWriteInd( psWriteMbx );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\param pMbx Pointer to the Mailbox command to be send to the master.
|
||||
|
||||
\brief This function copies data to the Send Mailbox.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
UINT8 MBX_CopyToSendMailbox( TMBX MBXMEM *pMbx )
|
||||
{
|
||||
if ( (nAlStatus & STATE_MASK) == STATE_INIT)
|
||||
{
|
||||
/* the mailbox is disabled if the slave is in the INIT state */
|
||||
return( ERROR_INVALIDSTATE );
|
||||
}
|
||||
|
||||
|
||||
if ( !bMbxRunning )
|
||||
{
|
||||
/* the mailbox is disabled if the slave is in the INIT state */
|
||||
return( ERROR_INVALIDSTATE );
|
||||
}
|
||||
|
||||
if ( bSendMbxIsFull )
|
||||
{
|
||||
/* mailbox service cannot be sent because the send mailbox is still full */
|
||||
return MBXERR_NOMOREMEMORY;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* the variable mbxSize contains the size of the mailbox data to be sent */
|
||||
UINT16 mbxSize = pMbx->MbxHeader.Length;
|
||||
HW_EscWriteMbxMem((MEM_ADDR *)pMbx, u16EscAddrSendMbx, (mbxSize + MBX_HEADER_SIZE));
|
||||
/* ECATCHANGE_HW(V5.10) HW1*/
|
||||
|
||||
{
|
||||
/*Read Control and Status of SyncManager 1 to check if the buffer is still marked as empty*/
|
||||
VARVOLATILE UINT16 smstate = 0x00;
|
||||
HW_EscReadWord(smstate,(ESC_SYNCMAN_CONTROL_OFFSET + SIZEOF_SM_REGISTER));
|
||||
/*ECATCHANGE_START(V5.11) ECAT7*/
|
||||
smstate = SWAPWORD(smstate);
|
||||
/*ECATCHANGE_END(V5.11) ECAT7*/
|
||||
|
||||
if(!(smstate & SM_STATUS_MBX_BUFFER_FULL))
|
||||
{
|
||||
UINT8 BytesLeft = u16SendMbxSize - (mbxSize + MBX_HEADER_SIZE);
|
||||
|
||||
/*Write last Byte to trigger mailbox full flag*/
|
||||
/*Read last 2 Bytes and write them again (required if low Byte of the WORD were written before)*/
|
||||
u16dummy = 0;
|
||||
if(BytesLeft < 2)
|
||||
{
|
||||
/*The last 2Bytes are overlapping the already written buffer*/
|
||||
|
||||
/*Get the valid 16Bit address*/
|
||||
UINT32 LastDataAddress = ((mbxSize + MBX_HEADER_SIZE)/2)*2;
|
||||
|
||||
/*Copy the buffer to overwrite*/
|
||||
MEMCPY((UINT16 *)&u16dummy,(((UINT8 *)pMbx) + LastDataAddress),(2 - BytesLeft));
|
||||
}
|
||||
|
||||
HW_EscWriteWord(u16dummy,(u16EscAddrSendMbx + u16SendMbxSize - 2));
|
||||
}
|
||||
}
|
||||
|
||||
/* store last send mailbox service for a possible repeat
|
||||
one buffer includes the last send service (psRepeatMbx),
|
||||
the other one the actual service to be sent (psReadMbx),
|
||||
there is no buffer available for a mailbox receive service
|
||||
until the last sent buffer was read from the master
|
||||
the exception is after the INIT2PREOP transition, in that
|
||||
case there is no last sent service (psReadMbx = 0) */
|
||||
if ( psReadMbx )
|
||||
psWriteMbx = NULL;
|
||||
else
|
||||
{
|
||||
/* only the first time after the INIT2PREOP-transition */
|
||||
psWriteMbx = &asMbx[1];
|
||||
}
|
||||
// HBu 17.06.06: psRepeatMbx was already updated in MBX_MailboxReadInd
|
||||
// psRepeatMbx = psReadMbx;
|
||||
psReadMbx = pMbx;
|
||||
|
||||
/* set flag that send mailbox is full now */
|
||||
bSendMbxIsFull = TRUE;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
\brief This function is called cyclically to check if a received Mailbox service was
|
||||
stored.
|
||||
*////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void MBX_Main(void)
|
||||
{
|
||||
TMBX MBXMEM *pMbx = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
UINT8 result = 0;
|
||||
|
||||
pMbx = GetOutOfMbxQueue(&sMbxReceiveQueue);
|
||||
if ( pMbx )
|
||||
result = MailboxServiceInd(pMbx);
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
/* Mailbox error response: type 0 (mailbox service protocol) */
|
||||
pMbx->MbxHeader.Length = 4;
|
||||
pMbx->MbxHeader.Flags[MBX_OFFS_TYPE] &= ~(MBX_MASK_TYPE);
|
||||
pMbx->Data[0] = SWAPWORD(MBXSERVICE_MBXERRORCMD);
|
||||
pMbx->Data[1] = SWAPWORD(result);
|
||||
MBX_MailboxSendReq(pMbx, 0);
|
||||
}
|
||||
}
|
||||
while ( pMbx != NULL );
|
||||
|
||||
|
||||
|
||||
if ( bReceiveMbxIsLocked )
|
||||
{
|
||||
/* the work on the receive mailbox is locked, check if it can be unlocked (if all
|
||||
mailbox commands has been sent */
|
||||
MBX_CheckAndCopyMailbox();
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* \addtogroup Mailbox Mailbox Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file mailbox.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 MBX1: "return value of ""MBX_StartMailboxHandler()"" changed to UINT16"<br>
|
||||
V5.11 MBX3: set application triggered emergency and EoE data to pending if no mailbox queue is supported and another mailbox request is currently handled, Handle only one mailbox request at a time (in case that MAILBPX_QUEUE is disabled)<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
|
||||
#ifndef _MAILBOX_H_
|
||||
#define _MAILBOX_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "ecat_def.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#define MBX_TYPE_AOE 1 /**< \brief Mailbox type AoE*/
|
||||
#define MBX_TYPE_EOE 2 /**< \brief Mailbox type EoE*/
|
||||
#define MBX_TYPE_COE 3 /**< \brief Mailbox type CoE*/
|
||||
#define MBX_TYPE_FOE 4 /**< \brief Mailbox type FoE*/
|
||||
#define MBX_TYPE_SOE 5 /**< \brief Mailbox type SoE*/
|
||||
#define MBX_TYPE_VOE 15 /**< \brief Mailbox type VoE*/
|
||||
|
||||
#define EMCY_SERVICE ((UINT8) 0x0001) /**< \brief Emergency service*/
|
||||
#define COE_SERVICE ((UINT8) 0x0002) /**< \brief CoE service*/
|
||||
#define SOE_SERVICE ((UINT8) 0x0004) /**< \brief SoE service*/
|
||||
#define EOE_SERVICE ((UINT8) 0x0008) /**< \brief EoE service*/
|
||||
#define AOE_SERVICE ((UINT8) 0x0010) /**< \brief AoE service*/
|
||||
#define VOE_SERVICE ((UINT8) 0x0020) /**< \brief VoE service*/
|
||||
#define FOE_SERVICE ((UINT8) 0x0040) /**< \brief FoE service*/
|
||||
#define FRAGMENTS_FOLLOW ((UINT8) 0x0080) /**< \brief Fragments follow service*/
|
||||
|
||||
#ifndef DISABLE_MBX_INT
|
||||
#define DISABLE_MBX_INT /**< \brief Disable mailbox interrupt*/
|
||||
#endif
|
||||
#ifndef ENABLE_MBX_INT
|
||||
#define ENABLE_MBX_INT /**< \brief Enable mailbox interrupt*/
|
||||
#endif
|
||||
|
||||
#ifndef ENTER_MBX_CRITICAL
|
||||
#define ENTER_MBX_CRITICAL /**< \brief Enter mailbox critical section*/
|
||||
#endif
|
||||
|
||||
#ifndef LEAVE_MBX_CRITICAL
|
||||
#define LEAVE_MBX_CRITICAL /**< \brief Leave mailbox critical section*/
|
||||
#endif
|
||||
|
||||
#ifndef MAX_MBX_QUEUE_SIZE
|
||||
#define MAX_MBX_QUEUE_SIZE 10 /**< \brief Mailbox queue size*/
|
||||
#endif
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- Command Codes for the mailbox type 0
|
||||
-----------------------------------------------*/
|
||||
#define MBXSERVICE_MBXERRORCMD 0x01 /**< \brief Mailbox error command*/
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- Error Codes for a mailbox error response
|
||||
-----------------------------------------------*/
|
||||
#define MBXERR_SYNTAX 0x01 /**< \brief Mailbox error "syntax"*/
|
||||
#define MBXERR_UNSUPPORTEDPROTOCOL 0x02 /**< \brief Mailbox error "unsupported protocol"*/
|
||||
#define MBXERR_INVALIDCHANNEL 0x03 /**< \brief Mailbox error "invalid channel"*/
|
||||
#define MBXERR_SERVICENOTSUPPORTED 0x04 /**< \brief Mailbox error "service not supported"*/
|
||||
#define MBXERR_INVALIDHEADER 0x05 /**< \brief Mailbox error "invalid header"*/
|
||||
#define MBXERR_SIZETOOSHORT 0x06 /**< \brief Mailbox error "Size too short"*/
|
||||
#define MBXERR_NOMOREMEMORY 0x07 /**< \brief Mailbox error "No memory"*/
|
||||
#define MBXERR_INVALIDSIZE 0x08 /**< \brief Mailbox error "Invalid size"*/
|
||||
#define MBXERR_SERVICEINWORK 0x09 /**< \brief Mailbox error "Service in work"*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mailbox header
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 Length; /**< \brief Length*/
|
||||
UINT16 Address; /**< \brief Address*/
|
||||
|
||||
UINT16 Flags[1]; /**< \brief Flags*/
|
||||
#define MBX_OFFS_TYPE 0 /**< \brief Protocol type offset*/
|
||||
#define MBX_MASK_TYPE 0x0F00 /**< \brief Protocol type mask*/
|
||||
#define MBX_SHIFT_TYPE 8 /**< \brief Protocol type shift*/
|
||||
#define MBX_OFFS_COUNTER 0 /**< \brief Protocol counter offset*/
|
||||
#define MBX_MASK_COUNTER 0xF000 /**< \brief Protocol counter mask*/
|
||||
#define MBX_SHIFT_COUNTER 12 /**< \brief Protocol counter shift*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TMBXHEADER;
|
||||
|
||||
|
||||
#define MBX_HEADER_SIZE SIZEOF(TMBXHEADER) /**< \brief Mailbox header size*/
|
||||
|
||||
|
||||
#define MAX_MBX_DATA_SIZE (MAX_MBX_SIZE - MBX_HEADER_SIZE) /**< \brief Mailbox data size*/
|
||||
|
||||
/**
|
||||
* \brief Mailbox datagram
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
UINT16 Data[(MAX_MBX_DATA_SIZE >> 1)]; /**< \brief Mailbox data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TMBX;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Mailbox queue
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
UINT16 firstInQueue; /**< \brief First in Queue*/
|
||||
UINT16 lastInQueue; /**< \brief Last in Queue*/
|
||||
UINT16 maxQueueSize; /**< \brief Max queue size*/
|
||||
TMBX MBXMEM * queue[(MAX_MBX_QUEUE_SIZE)+1]; /**< \brief Queue buffer*/
|
||||
} TMBXQUEUE;
|
||||
|
||||
|
||||
|
||||
#endif //_MAILBOX_H_
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_MAILBOX_) && (_MAILBOX_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
PROTO BOOL bReceiveMbxIsLocked; /**< \brief Receive mailbox is locked (MBoxOut, default SM0)*/
|
||||
PROTO BOOL bSendMbxIsFull; /**< \brief Send mailbox is full (MBoxIn, default SM1)*/
|
||||
PROTO BOOL bMbxRunning; /**< \brief Mailbox running (At least PreOP state)*/
|
||||
PROTO BOOL bMbxRepeatToggle; /**< \brief Mailbox repeat toggle*/
|
||||
PROTO UINT16 u16SendMbxSize; /**< \brief Send mailbox size (default SM1)*/
|
||||
PROTO UINT16 u16ReceiveMbxSize; /**< \brief Receive mailbox size (default SM0)*/
|
||||
PROTO UINT16 u16EscAddrReceiveMbx; /**< \brief Receive mailbox address (default SM0)*/
|
||||
PROTO UINT16 u16EscAddrSendMbx; /**< \brief Send mailbox address (default SM1)*/
|
||||
PROTO UINT8 u8MbxWriteCounter; /**< \brief Write mailbox counter*/
|
||||
PROTO UINT8 u8MbxReadCounter; /**< \brief Read mailbox counter*/
|
||||
PROTO TMBX MBXMEM asMbx[2]; /**< \brief Mailbox buffer (for repeat service)*/
|
||||
PROTO UINT8 u8MailboxSendReqStored; /**< \brief Mailbox send request stored*/
|
||||
PROTO TMBX MBXMEM * psWriteMbx; /**< \brief Pointer to write mailbox buffer*/
|
||||
PROTO TMBX MBXMEM * psReadMbx; /**< \brief Pointer to read mailbox buffer*/
|
||||
PROTO TMBX MBXMEM * psRepeatMbx; /**< \brief Pointer to repeat mailbox buffer*/
|
||||
PROTO TMBX MBXMEM * psStoreMbx; /**< \brief Pointer to store mailbox buffer*/
|
||||
PROTO TMBXQUEUE MBXMEM sMbxSendQueue; /**< \brief Send mailbox queue*/
|
||||
PROTO TMBXQUEUE MBXMEM sMbxReceiveQueue; /**< \brief Receive mailbox queue*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
PROTO void MBX_Init(void);
|
||||
/* ECATCHANGE_START(V5.11) MBX1*/
|
||||
PROTO UINT16 MBX_StartMailboxHandler(void);
|
||||
/* ECATCHANGE_END(V5.11) MBX1*/
|
||||
PROTO void MBX_StopMailboxHandler(void);
|
||||
PROTO void MBX_MailboxWriteInd(TMBX MBXMEM *pMbx);
|
||||
PROTO void MBX_MailboxReadInd(void);
|
||||
PROTO void MBX_MailboxRepeatReq(void);
|
||||
PROTO UINT8 MBX_MailboxSendReq(TMBX MBXMEM * pMbx, UINT8 flags);
|
||||
PROTO void MBX_CheckAndCopyMailbox(void);
|
||||
PROTO UINT8 MBX_CopyToSendMailbox(TMBX MBXMEM *pMbx);
|
||||
PROTO void MBX_Main(void);
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* \addtogroup CoE CAN Application Profile over EtherCAT
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
\file objdef.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief CAN application profile over EtherCAT object dictionary
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 COE2: update synctype supported flag for freerun<br>
|
||||
V5.11 COE3: change 0x10F3.2 (Sync Error limit) from UINT32 to UINT16 (according to the ETG.1020)<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 COE1: Define one entry description for all 0x1C3x objects and change data type of SI11,12,13 to UINT16 (according ETG.1020)<br>
|
||||
V5.10 COE2: Change return value of Get ObjectSize to UINT32<br>
|
||||
Change object size to UINT32<br>
|
||||
V5.10 ECAT13: Update Synchronisation handling (FreeRun,SM Sync, Sync0, Sync1)<br>
|
||||
Compare DC UINT configuration (by ESC Config data) vs. DC activation register settings<br>
|
||||
Update 0x1C3x entries<br>
|
||||
V5.10 ECAT8: Update Datatype defines according ETG.1020<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
|
||||
#ifndef _OBJDEF_H_
|
||||
#define _OBJDEF_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "sdoserv.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/*---------------------------------------------
|
||||
- DataTypes
|
||||
-----------------------------------------------*/
|
||||
#define DEFTYPE_NULL 0x0000 /**< \brief Null*/
|
||||
#define DEFTYPE_BOOLEAN 0x0001 /**< \brief BOOLEAN*/
|
||||
#define DEFTYPE_INTEGER8 0x0002 /**< \brief INTEGER8*/
|
||||
#define DEFTYPE_INTEGER16 0x0003 /**< \brief INTEGER16*/
|
||||
#define DEFTYPE_INTEGER32 0x0004 /**< \brief INTEGER32*/
|
||||
#define DEFTYPE_UNSIGNED8 0x0005 /**< \brief UNSIGNED8*/
|
||||
#define DEFTYPE_UNSIGNED16 0x0006 /**< \brief UNSIGNED16*/
|
||||
#define DEFTYPE_UNSIGNED32 0x0007 /**< \brief UNSIGNED32*/
|
||||
#define DEFTYPE_REAL32 0x0008 /**< \brief REAL32*/
|
||||
#define DEFTYPE_VISIBLESTRING 0x0009 /**< \brief VISIBLE_STRING*/
|
||||
#define DEFTYPE_OCTETSTRING 0x000A /**< \brief OCTET_STRING*/
|
||||
#define DEFTYPE_UNICODE_STRING 0x000B /**< \brief UNICODE_STRING*/
|
||||
#define DEFTYPE_TIME_OF_DAY 0x000C /**< \brief TIME_OF_DAY*/
|
||||
#define DEFTYPE_TIME_DIFFERENCE 0x000D /**< \brief TIME_DIFFERENCE*/
|
||||
#define DEFTYPE_INTEGER24 0x0010 /**< \brief INTEGER24*/
|
||||
#define DEFTYPE_REAL64 0x0011 /**< \brief REAL64*/
|
||||
#define DEFTYPE_INTEGER40 0x0012 /**< \brief INTEGER40*/
|
||||
#define DEFTYPE_INTEGER48 0x0013 /**< \brief INTEGER48*/
|
||||
#define DEFTYPE_INTEGER56 0x0014 /**< \brief INTEGER56*/
|
||||
#define DEFTYPE_INTEGER64 0x0015 /**< \brief INTEGER64*/
|
||||
#define DEFTYPE_UNSIGNED24 0x0016 /**< \brief UNSIGNED24*/
|
||||
#define DEFTYPE_UNSIGNED40 0x0018 /**< \brief UNSIGNED40*/
|
||||
#define DEFTYPE_UNSIGNED48 0x0019 /**< \brief UNSIGNED48*/
|
||||
#define DEFTYPE_UNSIGNED56 0x001A /**< \brief UNSIGNED56*/
|
||||
#define DEFTYPE_UNSIGNED64 0x001B /**< \brief UNSIGNED64*/
|
||||
#define DEFTYPE_GUID 0x001D /**< \brief DEFTYPE_GUID*/
|
||||
#define DEFTYPE_BYTE 0x001E /**< \brief DEFTYPE_BYTE*/
|
||||
#define DEFTYPE_WORD 0x001F /**< \brief DEFTYPE_WORD*/
|
||||
#define DEFTYPE_DWORD 0x0020 /**< \brief DEFTYPE_DWORD*/
|
||||
#define DEFTYPE_PDOMAPPING 0x0021 /**< \brief PDO_MAPPING*/
|
||||
#define DEFTYPE_IDENTITY 0x0023 /**< \brief IDENTITY*/
|
||||
#define DEFTYPE_COMMAND 0x0025 /**< \brief COMMAND_PAR*/
|
||||
#define DEFTYPE_PDOCOMPAR 0x0027 /**< \brief PDO_PARAMETER*/
|
||||
#define DEFTYPE_ENUM 0x0028 /**< \brief DEFTYPE_ENUM */
|
||||
#define DEFTYPE_SMPAR 0x0029 /**< \brief SM_SYNCHRONISATION*/
|
||||
#define DEFTYPE_RECORD 0x002A /**< \brief DEFTYPE_RECORD */
|
||||
#define DEFTYPE_BACKUP 0x002B /**< \brief BACKUP_PARAMETER*/
|
||||
#define DEFTYPE_MDP 0x002C /**< \brief MODULAR_DEVICE_PROFILE*/
|
||||
#define DEFTYPE_BITARR8 0x002D /**< \brief BITARR8*/
|
||||
#define DEFTYPE_BITARR16 0x002E /**< \brief BITARR16*/
|
||||
#define DEFTYPE_BITARR32 0x002F /**< \brief BITARR32*/
|
||||
#define DEFTYPE_BIT1 0x0030 /**< \brief BIT1*/
|
||||
#define DEFTYPE_BIT2 0x0031 /**< \brief BIT2*/
|
||||
#define DEFTYPE_BIT3 0x0032 /**< \brief BIT3*/
|
||||
#define DEFTYPE_BIT4 0x0033 /**< \brief BIT4*/
|
||||
#define DEFTYPE_BIT5 0x0034 /**< \brief BIT5*/
|
||||
#define DEFTYPE_BIT6 0x0035 /**< \brief BIT6*/
|
||||
#define DEFTYPE_BIT7 0x0036 /**< \brief BIT7*/
|
||||
#define DEFTYPE_BIT8 0x0037 /**< \brief BIT8*/
|
||||
#define DEFTYPE_ARRAY_OF_INT 0x0260 /**< \brief DEFTYPE_ARRAY_OF_INT*/
|
||||
#define DEFTYPE_ARRAY_OF_SINT 0x0261 /**< \brief DEFTYPE_ARRAY_OF_SINT*/
|
||||
#define DEFTYPE_ARRAY_OF_DINT 0x0262 /**< \brief DEFTYPE_ARRAY_OF_DINT*/
|
||||
#define DEFTYPE_ARRAY_OF_UDINT 0x0263 /**< \brief DEFTYPE_ARRAY_OF_UDINT*/
|
||||
#define DEFTYPE_ERRORHANDLING 0x0281 /**< \brief DEFTYPE_ERRORHANDLING*/
|
||||
#define DEFTYPE_DIAGHISTORY 0x0282 /**< \brief DEFTYPE_DIAGHISTORY*/
|
||||
#define DEFTYPE_SYNCSTATUS 0x0283 /**< \brief DEFTYPE_SYNCSTATUS*/
|
||||
#define DEFTYPE_SYNCSETTINGS 0x0284 /**< \brief DEFTYPE_SYNCSETTINGS*/
|
||||
#define DEFTYPE_FSOEFRAME 0x0285 /**< \brief DEFTYPE_FSOEFRAME*/
|
||||
#define DEFTYPE_FSOECOMMPAR 0x0286 /**< \brief DEFTYPE_FSOECOMMPAR*/
|
||||
|
||||
|
||||
/*---------------------------------------------
|
||||
- Flags for supported Synctypes (0x1C32.4 / 0x1C33.4)
|
||||
-----------------------------------------------*/
|
||||
|
||||
/*ECATCHANGE_START(V5.11) COE2*/
|
||||
#define SYNCTYPE_FREERUNSUPP 0x0001 /**< \brief FreeRun supported*/
|
||||
/*ECATCHANGE_END(V5.11) COE2*/
|
||||
#define SYNCTYPE_SYNCHRONSUPP 0x0002 /**< \brief SyncManager synchron supported*/
|
||||
#define SYNCTYPE_DCSYNC0SUPP 0x0004 /**< \brief Sync0 synchron supported*/
|
||||
#define SYNCTYPE_DCSYNC1SUPP 0x0008 /**< \brief Sync1 synchron supported*/
|
||||
#define SYNCTYPE_SUBCYCLESUPP 0x0010 /**< \brief Subordinated Application with fixed Sync0 supported*/
|
||||
#define SYNCTYPE_LOCALSHIFTSUPP 0x0020 /**< \brief Event shift with local timer supported. Output shift : 0x1C32.4 ; Input shift : 0x1C33.4 (not supported by the default SSC)*/
|
||||
#define SYNCTYPE_SHIFTBYSYNC1SUPP 0x0040 /**< \brief Event shift with Sync1 event supported. Output shift : 0x1C32.4 ; Input shift : 0x1C33.4*/
|
||||
#define SYNCTYPE_MEASURE_DELAYSUPP 0x0200 /**< \brief Delay times should be measured (not supported by the default SSC, only the cycle time can be measured (1ms resolution)*/
|
||||
#define SYNCTYPE_FIXED_DELAYSUPP 0x0400 /**< \brief Delay times are fixed*/
|
||||
#define SYNCTYPE_TIMESVARIABLE 0x4000 /**< \brief Dynamic Cycle Times supported*/
|
||||
|
||||
|
||||
#define IS_PDO_ASSIGN(x) ((x >= 0x1C10) && (x <= 0x1C2F)) /**< \brief Marco to check if object index is SyncManager assign object*/
|
||||
#define IS_RX_PDO(x) (((x) >= 0x1600) && ((x) <= 0x17FF)) /**< \brief Marco to check if object index RxPDO mapping object*/
|
||||
#define IS_TX_PDO(x) (((x) >= 0x1A00) && ((x) <= 0x1BFF)) /**< \brief Marco to check if object index TxPDO mapping object*/
|
||||
|
||||
|
||||
/**
|
||||
* \brief Object dictionary entry structure
|
||||
*/
|
||||
typedef struct OBJ_ENTRY
|
||||
{
|
||||
struct OBJ_ENTRY *pPrev; /**< \brief Previous entry(object) in the object dictionary list*/
|
||||
struct OBJ_ENTRY *pNext; /**< \brief Next entry(object) in the object dictionary list*/
|
||||
|
||||
UINT16 Index; /**< \brief Object index*/
|
||||
TSDOINFOOBJDESC ObjDesc; /**< \brief Object access, type and code*/
|
||||
OBJCONST TSDOINFOENTRYDESC OBJMEM *pEntryDesc; /**< \brief pointer to object entry descriptions*/
|
||||
OBJCONST UCHAR OBJMEM *pName; /**< \brief Pointer to object and entry names*/
|
||||
void MBXMEM *pVarPtr; /**< \brief Pointer to object buffer*/
|
||||
UINT8 (* Read)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to read function (if NULL default read function will be used)*/
|
||||
UINT8 (* Write)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer to write function (if NULL default write function will be used)*/
|
||||
UINT16 NonVolatileOffset; /**< \brief Offset within the non volatile memory (need to be defined for backup objects)*/
|
||||
}
|
||||
TOBJECT;
|
||||
|
||||
|
||||
/**
|
||||
* Object 0x1C3x (SyncManager Parameter) data structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 subindex0;/**< \brief SubIndex0*/
|
||||
UINT16 u16SyncType; /**< \brief SunbIndex 001: Sync type*/
|
||||
UINT32 u32CycleTime;/**< \brief SunbIndex 002: Cycle time*/
|
||||
UINT32 u32ShiftTime;/**< \brief SunbIndex 003: Shift time (not supported, variable is only used as a place holder)*/
|
||||
UINT16 u16SyncTypesSupported;/**< \brief SunbIndex 004: Supported Sync types*/
|
||||
UINT32 u32MinCycleTime;/**< \brief SunbIndex 005: Min cycle time*/
|
||||
UINT32 u32CalcAndCopyTime;/**< \brief SunbIndex 006: Calc and Copy time*/
|
||||
UINT32 u32Si7Reserved;/**< \brief SunbIndex 007: 32Bit Reserved*/
|
||||
UINT16 u16GetCycleTime;/**< \brief SunbIndex 008: Get cycle time*/
|
||||
UINT32 u32DelayTime; /**< \brief SunbIndex 009: Delay time*/
|
||||
UINT32 u32Sync0CycleTime; /**< \brief SunbIndex 010: Sync0 cycle time*/
|
||||
UINT16 u16SmEventMissedCounter; /**< \brief SunbIndex 011: SyncManager event missed counter*/
|
||||
UINT16 u16CycleExceededCounter; /**< \brief SunbIndex 012: Cycle exceed counter*/
|
||||
UINT16 u16Si13Reserved; /**< \brief SunbIndex 013: Shift too short (not supported, only padding)*/
|
||||
UINT16 u16Si14Reserved; /**< \brief SubIndex14 not supported*/
|
||||
UINT32 u32Si15Reserved; /**< \brief SubIndex15 not supported*/
|
||||
UINT32 u32Si16Reserved; /**< \brief SubIndex16 not supported*/
|
||||
UINT32 u32Si17Reserved; /**< \brief SubIndex17 not supported*/
|
||||
UINT32 u32Si18Reserved; /**< \brief SubIndex18 not supported*/
|
||||
UINT8 u8SyncError; /**< \brief Sync Error*/
|
||||
}OBJ_STRUCT_PACKED_END
|
||||
TSYNCMANPAR;
|
||||
|
||||
/**
|
||||
* Cycle diagnosis
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 syncFailedCounter; /**< \brief Sync Failed Counter*/
|
||||
}OBJ_STRUCT_PACKED_END
|
||||
TCYCLEDIAG;
|
||||
|
||||
|
||||
/**
|
||||
* Object 0x10F1 (Error Settings Object) data structure
|
||||
*/
|
||||
typedef struct OBJ_STRUCT_PACKED_START {
|
||||
UINT16 u16SubIndex0; /**< \brief SubIndex0*/
|
||||
UINT32 u32LocalErrorReaction; /**< \brief Local error reaction*/
|
||||
/*ECATCHANGE_START(V5.11) COE3*/
|
||||
UINT16 u16SyncErrorCounterLimit; /**< \brief Sync error counter limit*/
|
||||
/*ECATCHANGE_END(V5.11) COE3*/
|
||||
} OBJ_STRUCT_PACKED_END
|
||||
TOBJ10F1;
|
||||
|
||||
#endif //_OBJDEF_H_
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_OBJDEF_) && (_OBJDEF_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Indicates if the synchronisation was written by the user on startup (by writing 0x1C3x.1).
|
||||
* If it was not written the sync mode will be set based on the DC registers 0x980:0x981
|
||||
*/
|
||||
PROTO BOOL bSyncSetByUser;
|
||||
|
||||
/**
|
||||
* \brief Cycle diagnosis variable
|
||||
*/
|
||||
PROTO TCYCLEDIAG sCycleDiag;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Object 0x1C32 (SyncManager 2 Parameter) object variable
|
||||
*/
|
||||
PROTO TSYNCMANPAR MBXMEM sSyncManOutPar;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Object 0x1C33 (SyncManager 3 Parameter) object variable
|
||||
*/
|
||||
PROTO TSYNCMANPAR MBXMEM sSyncManInPar;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Object 0x10F1 (Error Settings) object variable
|
||||
*
|
||||
* SubIndex0 : 2
|
||||
* Local error reaction : 1
|
||||
* Sync error counter limit : MAX_SM_EVENT_MISSED
|
||||
*/
|
||||
PROTO TOBJ10F1 sErrorSettings
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_OBJDEF_) && (_OBJDEF_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
= {2, 0x01, MAX_SM_EVENT_MISSED}
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
* \brief Default entry name "SubIndex 000"
|
||||
*/
|
||||
PROTO CHAR OBJMEM aSubindexDesc[13]
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_OBJDEF_) && (_OBJDEF_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
= { "SubIndex 000" }
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
PROTO OBJCONST TOBJECT OBJMEM * OBJ_GetObjectHandle( UINT16 index );
|
||||
PROTO UINT32 OBJ_GetObjectLength( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 bCompleteAccess);
|
||||
PROTO UINT16 OBJ_GetNoOfObjects(UINT8 listType);
|
||||
PROTO UINT16 OBJ_GetObjectList(UINT16 listType, UINT16 *pIndex, UINT16 size, UINT16 MBXMEM *pData,UINT8 *pAbort);
|
||||
PROTO UINT16 OBJ_GetDesc( UINT16 index, UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData );
|
||||
PROTO OBJCONST TSDOINFOENTRYDESC OBJMEM * OBJ_GetEntryDesc(OBJCONST TOBJECT OBJMEM * pObjEntry, UINT8 Subindex);
|
||||
PROTO OBJCONST TSDOINFOOBJDESC OBJMEM * OBJ_GetObjDesc(OBJCONST TOBJECT OBJMEM * pObjEntry);
|
||||
PROTO UINT16 OBJ_GetEntryOffset(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry);
|
||||
PROTO UINT8 CheckSyncTypeValue(UINT16 index, UINT16 NewSyncType);
|
||||
PROTO UINT8 OBJ_Read(UINT16 index, UINT8 subindex, UINT32 objSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess);
|
||||
PROTO UINT8 OBJ_Write(UINT16 index, UINT8 subindex, UINT32 dataSize, OBJCONST TOBJECT OBJMEM * pObjEntry, UINT16 MBXMEM * pData, UINT8 bCompleteAccess);
|
||||
PROTO void COE_WriteBackupEntry(UINT8 subindex, OBJCONST TOBJECT OBJMEM * pObjEntry);
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,641 @@
|
||||
/**
|
||||
* \addtogroup CoE CAN Application Profile over EtherCAT
|
||||
* @{
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
\file sdoserv.h
|
||||
\author EthercatSSC@beckhoff.com
|
||||
\brief SDO Service definitions
|
||||
|
||||
\version 5.11
|
||||
|
||||
<br>Changes to version V5.10:<br>
|
||||
V5.11 ECAT10: change PROTO handling to prevent compiler errors<br>
|
||||
V5.11 SDO10: add new SDO abort code 0x6010004 (complete access not supported)<br>
|
||||
<br>Changes to version V5.01:<br>
|
||||
V5.10 SDO3: Add new SDO Abort Code (0x06090033)<br>
|
||||
<br>Changes to version - :<br>
|
||||
V5.01 : Start file change log
|
||||
*/
|
||||
|
||||
#ifndef _SDOSERV_H_
|
||||
|
||||
#define _SDOSERV_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Includes
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
#include "ecatcoe.h"
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Defines and Types
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \addtogroup SdoErrorCodes SDO Error Codes
|
||||
* @{
|
||||
*/
|
||||
#define ERROR_SDOINVALIDCOMMAND 0x1101 /**< \brief Invalid SDO command*/
|
||||
#define ERROR_SDOINVALIDHEADER 0x1102 /**< \brief Invalid SDO header*/
|
||||
#define ERROR_SDONOTSUPPORTED 0x1103 /**< \brief SDO service not supported*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SdoHeaderData SDO Header and Data
|
||||
* @{
|
||||
*/
|
||||
#define SDOHEADER_SIZEINDICATOR ((UINT8) 0x01) /**< \brief Size Indicator size of data in Data Set Size specified*/
|
||||
#define SDOHEADER_TRANSFERTYPE ((UINT8) 0x02) /**< \brief Transfer Type<br>1: Expedited<br>0: Normal*/
|
||||
#define SDOHEADER_DATASETSIZE ((UINT8) 0x0C) /**< \brief Data Set Size (used for Expedited transfer)*/
|
||||
#define SDOHEADER_COMPLETEACCESS ((UINT8) 0x10) /**< \brief Complete Access indication<br>0: Only specified entry will be transferred<br>1: Whole object (with or without Subindex0) will be transferred)*/
|
||||
#define SDOHEADER_COMMAND ((UINT8) 0xE0) /**< \brief SDO Command*/
|
||||
#define SDOHEADERSHIFT_SIZEINDICATOR ((UINT8) 0) /**< \brief Shift for Size Indicator*/
|
||||
#define SDOHEADERSHIFT_TRANSFERTYPE ((UINT8) 1) /**< \brief Shift for Transfer Type*/
|
||||
#define SDOHEADERSHIFT_DATASETSIZE ((UINT8) 2) /**< \brief Shift for Data Set Size*/
|
||||
#define SDOHEADERSHIFT_INDEXACCESS ((UINT8) 4) /**< \brief Shift for Complete Access*/
|
||||
#define SDOHEADERSHIFT_COMMAND ((UINT8) 5) /**< \brief Shift for SDO Command*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SdoServices SDO Services
|
||||
* @{
|
||||
*/
|
||||
#define SDOSERVICE_INITIATEDOWNLOADREQ ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO download request*/
|
||||
#define SDOSERVICE_INITIATEDOWNLOADRES ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO download response*/
|
||||
#define SDOSERVICE_DOWNLOADSEGMENTREQ ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented download request*/
|
||||
#define SDOSERVICE_DOWNLOADSEGMENTRES ((UINT8) (0x01 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented download response*/
|
||||
#define SDOSERVICE_INITIATEUPLOADREQ ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO upload request*/
|
||||
#define SDOSERVICE_INITIATEUPLOADRES ((UINT8) (0x02 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO upload response*/
|
||||
#define SDOSERVICE_UPLOADSEGMENTREQ ((UINT8) (0x03 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented upload request*/
|
||||
#define SDOSERVICE_UPLOADSEGMENTRES ((UINT8) (0x00 << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO segmented upload response*/
|
||||
#define SDOSERVICE_ABORTTRANSFER ((UINT8) (((UINT8) 0x04) << (SDOHEADERSHIFT_COMMAND))) /**< \brief SDO abort*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SdoHeaderData SDO Header and Data
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* Structure to handle the basic SDO header
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 Sdo[2]; /**< \brief 32Bit SDO header buffer*/
|
||||
#define SDOHEADER_COMMANDOFFSET 0 /**< \brief Memory offset for the command*/
|
||||
#define SDOHEADER_INDEXLOOFFSET 0 /**< \brief Memory offset for the low Byte of the object index*/
|
||||
#define SDOHEADER_INDEXHIOFFSET 1 /**< \brief Memory offset for the high Byte of the object index*/
|
||||
#define SDOHEADER_SUBINDEXOFFSET 1 /**< \brief Memory offset for subindex*/
|
||||
#define SDOHEADER_COMMANDMASK 0xFF /**< \brief Mask to get the command Byte*/
|
||||
#define SDOHEADER_INDEXLOSHIFT 8 /**< \brief Shift to get the low Byte of the object index*/
|
||||
#define SDOHEADER_INDEXHIMASK 0xFF /**< \brief Mask to get the high byte of the object index*/
|
||||
#define SDOHEADER_SUBINDEXSHIFT 8 /**< \brief Shift to get the subindex*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDOHEADER;
|
||||
|
||||
|
||||
#define INITSDO_HEADER_SIZE SIZEOF(TINITSDOHEADER) /**< \brief Size of the SDO header*/
|
||||
|
||||
|
||||
/**
|
||||
*Complete SDO datagram including mailbox header, CoE header and SDO header
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDOMBX;
|
||||
|
||||
|
||||
#define MAX_EXPEDITED_DATA 4 /**< \brief Maximum number of Bytes handled via an expedited transfer*/
|
||||
#define MIN_SEGMENTED_DATA ((UINT16) 7) /**< \brief Minimum number of Bytes required for a segmented transfer*/
|
||||
#define EXPEDITED_FRAME_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + (MAX_EXPEDITED_DATA) ) /**< \brief Size of an expedited mailbox datagram*/
|
||||
#define DOWNLOAD_NORM_REQ_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Size of an normal download request*/
|
||||
/* HBu 06.02.06: names of defines changed */
|
||||
/* HBu 21.03.06: the SDO_Download-Response has to have always 8 bytes */
|
||||
#define DOWNLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Frame size of an normal download response*/
|
||||
#define UPLOAD_NORM_RES_SIZE ( COE_HEADER_SIZE + INITSDO_HEADER_SIZE + 4 ) /**< \brief Frame size of an normal upload response*/
|
||||
#define SEGMENT_NORM_HEADER_SIZE ( COE_HEADER_SIZE + 1 ) /**< \brief Minimum SDO header size*/
|
||||
#define SEGMENT_NORM_RES_SIZE ( (SEGMENT_NORM_HEADER_SIZE) + (MIN_SEGMENTED_DATA) ) /**< \brief Minimum SDO size*/
|
||||
#define SEGMENT_MBX_SIZE ( MBX_HEADER_SIZE + (SEGMENT_NORM_HEADER_SIZE) )/**< \brief Minimum mailbox datagram*/
|
||||
|
||||
|
||||
/**
|
||||
* Segmented SDO header including data
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 SegHeader; /**< \brief 8Bit header buffer and first 8Bit of the Data*/
|
||||
#define SEGHEADER_MASK ((UINT16) 0xFF) /**< \brief SDO header mask*/
|
||||
#define SEGHEADER_NOMOREFOLLOWS ((UINT16) 0x01) /**< \brief More segments follow flag*/
|
||||
#define SEGHEADER_SEGDATASIZE ((UINT16) 0x0E) /**< \brief Segmented data size*/
|
||||
#define SEGHEADER_TOGGLE ((UINT16) 0x10) /**< \brief Toogle flag*/
|
||||
#define SEGHEADER_COMMAND ((UINT16) 0xE0) /**< \brief Command identifier*/
|
||||
#define SEGHEADERSHIFT_SEGDATASIZE ((UINT16) 1) /**< \brief Shift for Segmented data size*/
|
||||
#define SEGHEADERSHIFT_TOGGLE ((UINT16) 4) /**< \brief Shift for toggle flag*/
|
||||
#define SEGHEADERSHIFT_COMMAND ((UINT16) 5) /**< \brief Shift for command identifier*/
|
||||
#define SEGHDATA_MASK ((UINT16) 0xFF00) /**< \brief Data mask*/
|
||||
#define SEGDATASHIFT ((UINT16) 8) /**< \brief Shift for first data Byte*/
|
||||
UINT16 Data[(((MAX_MBX_DATA_SIZE)-(SEGMENT_NORM_HEADER_SIZE) - 1 ) >> 1)]; /**< \brief Data buffer (second and following Bytes, the first byte is located in the header variable)*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOSEGHEADERDATA;
|
||||
|
||||
|
||||
/**
|
||||
*Expedited download request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
UINT16 Data[(MAX_EXPEDITED_DATA) >> 1]; /**< \brief Data buffer*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDODOWNLOADEXPREQMBX;
|
||||
|
||||
/**
|
||||
*Normal download request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
UINT16 CompleteSize[2]; /**< \brief Complete object size*/
|
||||
UINT16 Data[(((MAX_MBX_DATA_SIZE)-(DOWNLOAD_NORM_REQ_SIZE)) >> 1)]; /**< \brief Data buffer*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDODOWNLOADNORMREQMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Expedited and normal download response:
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDODOWNLOADRESMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Segmented download request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TSDOSEGHEADERDATA SdoHeader; /**< \brief SDO header (data is included in header)*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TDOWNLOADSDOSEGREQMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Segmented download response
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
UINT8 SegHeader; /**< \brief SDO header*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TDOWNLOADSDOSEGRESMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Expedited and normal upload request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDOUPLOADREQMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Expedited upload response
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
UINT16 Data[((MAX_EXPEDITED_DATA) >> 1)]; /**< \brief Data buffer*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDOUPLOADEXPRESMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Normal upload response
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
UINT16 CompleteSize[2]; /**< \brief Complete object size*/
|
||||
UINT16 Data[(((MAX_MBX_DATA_SIZE)-(UPLOAD_NORM_RES_SIZE)) >> 1)]; /**< \brief Data buffer*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TINITSDOUPLOADNORMRESMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Segmented upload request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
UINT8 SegHeader; /**< \brief SDO header*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TUPLOADSDOSEGREQMBX;
|
||||
|
||||
/**
|
||||
*Segmented upload response
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TSDOSEGHEADERDATA SdoHeader; /**< \brief SDO header (data is included in header)*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TUPLOADSDOSEGRESMBX;
|
||||
|
||||
|
||||
/**
|
||||
*Abort request
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TINITSDOHEADER SdoHeader; /**< \brief SDO header*/
|
||||
UINT32 AbortCode; /**< \brief SDO abort code*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TABORTSDOTRANSFERREQMBX;
|
||||
|
||||
|
||||
#define ABORT_NORM_RES_SIZE (SIZEOF(TABORTSDOTRANSFERREQMBX) - SIZEOF(TMBXHEADER))/**< \brief Size of the SDO Abort datagram (without the mailbox header)*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SDOAbort SDO Abort Codes
|
||||
* @{
|
||||
*/
|
||||
#define ABORT_NOERROR 0x00000000 /**< \brief No SDO error*/
|
||||
#define ABORT_TOGGLE_BIT_NOT_CHANGED 0x05030000 /**< \brief Toggle bit not changed*/
|
||||
#define ABORT_SDO_PROTOCOL_TIMEOUT 0x05040000 /**< \brief SDO timeout*/
|
||||
#define ABORT_COMMAND_SPECIFIER_UNKNOWN 0x05040001 /**< \brief Command specifier unknown*/
|
||||
#define ABORT_OUT_OF_MEMORY 0x05040005 /**< \brief Out of memory*/
|
||||
#define ABORT_UNSUPPORTED_ACCESS 0x06010000 /**< \brief Unsupported Access*/
|
||||
#define ABORT_WRITE_ONLY_ENTRY 0x06010001 /**< \brief Write only entry*/
|
||||
#define ABORT_READ_ONLY_ENTRY 0x06010002 /**< \brief Read only entry*/
|
||||
#define ABORT_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x06010003 /**< \brief Entry can not be written because Subindex0 is not 0*/
|
||||
/* ECATCHANGE_START(V5.11) SDO10*/
|
||||
#define ABORT_COMPLETE_ACCESS_NOT_SUPPORTED 0x06010004 /**< \brief The object can not be accessed via complete access*/
|
||||
/* ECATCHANGE_END(V5.11) SDO10*/
|
||||
#define ABORT_OBJECT_NOT_EXISTING 0x06020000 /**< \brief Object not existing*/
|
||||
#define ABORT_OBJECT_CANT_BE_PDOMAPPED 0x06040041 /**< \brief Object can not be mapped to PDO*/
|
||||
#define ABORT_MAPPED_OBJECTS_EXCEED_PDO 0x06040042 /**< \brief Mapped Object exceeds PDO*/
|
||||
#define ABORT_PARAM_IS_INCOMPATIBLE 0x06040043 /**< \brief Parameter is incompatible*/
|
||||
#define ABORT_INTERNAL_DEVICE_INCOMPATIBILITY 0x06040047 /**< \brief Device incompatibility*/
|
||||
#define ABORT_HARDWARE_ERROR 0x06060000 /**< \brief Hardware error*/
|
||||
#define ABORT_PARAM_LENGTH_ERROR 0x06070010 /**< \brief Parameter length error*/
|
||||
#define ABORT_PARAM_LENGTH_TOO_LONG 0x06070012 /**< \brief Parameter is too long*/
|
||||
#define ABORT_PARAM_LENGTH_TOO_SHORT 0x06070013 /**< \brief Parameter is too short*/
|
||||
#define ABORT_SUBINDEX_NOT_EXISTING 0x06090011 /**< \brief Subindex (Entry) not exists*/
|
||||
#define ABORT_VALUE_EXCEEDED 0x06090030 /**< \brief Value exceeds*/
|
||||
#define ABORT_VALUE_TOO_GREAT 0x06090031 /**< \brief Value is too great*/
|
||||
#define ABORT_VALUE_TOO_SMALL 0x06090032 /**< \brief Value is too small*/
|
||||
#define ABORT_MODULE_ID_LIST_NOT_MATCH 0x06090033 /**< \brief Detected Module Ident List (0xF030) and Configured Module Ident list (0xF050) does not match*/
|
||||
#define ABORT_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x06090036 /**< \brief Value is less than minimum value*/
|
||||
#define ABORT_GENERAL_ERROR 0x08000000 /**< \brief General error*/
|
||||
#define ABORT_DATA_CANNOT_BE_READ_OR_STORED 0x08000020 /**< \brief Data can not be read or written*/
|
||||
#define ABORT_DATA_CANNOT_BE_READ_OR_STORED_BECAUSE_OF_LOCAL_CONTROL 0x08000021 /**< \brief Data can not be accessed because of local control*/
|
||||
#define ABORT_DATA_CANNOT_BE_READ_OR_STORED_IN_THIS_STATE 0x08000022 /**< \brief Data can not be read or written in the current state*/
|
||||
#define ABORT_NO_OBJECT_DICTIONARY_IS_PRESENT 0x08000023 /**< \brief Object is not in the object dictionary*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SDOAbortIndex SDO Abort Codes Identifier
|
||||
* @{
|
||||
* Internal the SDO abort codes are handled in an array.
|
||||
*/
|
||||
#define ABORTIDX_TOGGLE_BIT_NOT_CHANGED 0x01 /**< \brief Index of "Toggle bit not changed"*/
|
||||
#define ABORTIDX_SDO_PROTOCOL_TIMEOUT 0x02 /**< \brief Index of "SDO timeout"*/
|
||||
#define ABORTIDX_COMMAND_SPECIFIER_UNKNOWN 0x03 /**< \brief Index of "Command specifier unknown"*/
|
||||
#define ABORTIDX_OUT_OF_MEMORY 0x04 /**< \brief Index of "Out of memory"*/
|
||||
#define ABORTIDX_UNSUPPORTED_ACCESS 0x05 /**< \brief Index of "Unsupported Access"*/
|
||||
#define ABORTIDX_WRITE_ONLY_ENTRY 0x06 /**< \brief Index of "Write only entry"*/
|
||||
#define ABORTIDX_READ_ONLY_ENTRY 0x07 /**< \brief Index of "Read only entry"*/
|
||||
#define ABORTIDX_OBJECT_NOT_EXISTING 0x08 /**< \brief Index of "Object not existing"*/
|
||||
#define ABORTIDX_OBJECT_CANT_BE_PDOMAPPED 0x09 /**< \brief Index of "Object can not be mapped to PDO"*/
|
||||
#define ABORTIDX_MAPPED_OBJECTS_EXCEED_PDO 0x0A /**< \brief Index of "Mapped Object exceeds PDO"*/
|
||||
#define ABORTIDX_PARAM_IS_INCOMPATIBLE 0x0B /**< \brief Index of "Parameter is incompatible"*/
|
||||
#define ABORTIDX_INTERNAL_DEVICE_INCOMPATIBILITY 0x0C /**< \brief Index of "Device incompatibility"*/
|
||||
#define ABORTIDX_HARDWARE_ERROR 0x0D /**< \brief Index of "Hardware error"*/
|
||||
#define ABORTIDX_PARAM_LENGTH_ERROR 0x0E /**< \brief Index of "Parameter length error"*/
|
||||
#define ABORTIDX_PARAM_LENGTH_TOO_LONG 0x0F /**< \brief Index of "Parameter is too long"*/
|
||||
#define ABORTIDX_PARAM_LENGTH_TOO_SHORT 0x10 /**< \brief Index of "Parameter is too short"*/
|
||||
#define ABORTIDX_SUBINDEX_NOT_EXISTING 0x11 /**< \brief Index of "Subindex (Entry) not exists"*/
|
||||
#define ABORTIDX_VALUE_EXCEEDED 0x12 /**< \brief Index of "Value exceeds"*/
|
||||
#define ABORTIDX_VALUE_TOO_GREAT 0x13 /**< \brief Index of "Value is too great"*/
|
||||
#define ABORTIDX_VALUE_TOO_SMALL 0x14 /**< \brief Index of "Value is too small"*/
|
||||
#define ABORTIDX_MODULE_ID_LIST_NOT_MATCH 0x15 /**< \brief Index of "Unequal Module Id list"*/
|
||||
#define ABORTIDX_MAX_VALUE_IS_LESS_THAN_MIN_VALUE 0x16 /**< \brief Index of "Value is less than minimum value"*/
|
||||
#define ABORTIDX_GENERAL_ERROR 0x17 /**< \brief Index of "General error"*/
|
||||
#define ABORTIDX_DATA_CANNOT_BE_READ_OR_STORED 0x18 /**< \brief Index of "Data can not be read or written"*/
|
||||
#define ABORTIDX_DATA_CANNOT_BE_ACCESSED_BECAUSE_OF_LOCAL_CONTROL 0x19 /**< \brief Index of "Data can not be accessed because of local control"*/
|
||||
#define ABORTIDX_IN_THIS_STATE_DATA_CANNOT_BE_READ_OR_STORED 0x1A /**< \brief Index of "Data can not be read or written in the current state"*/
|
||||
#define ABORTIDX_NO_OBJECT_DICTIONARY_IS_PRESENT 0x1B /**< \brief Index of "Object is not in the object dictionary"*/
|
||||
#define ABORTIDX_ENTRY_CANT_BE_WRITTEN_SI0_NOT_0 0x1C /**< \brief Index of "Entry can not be written because Subindex0 is not 0"*/
|
||||
/* ECATCHANGE_START(V5.11) SDO10*/
|
||||
#define ABORTIDX_COMPLETE_ACCESS_NOT_SUPPORTED 0x1D /**< \brief The object can not be accessed via complete access*/
|
||||
/* ECATCHANGE_END(V5.11) SDO10*/
|
||||
#define ABORTIDX_WORKING 0xFF /**< \brief Index of application is handling the SDO request*/
|
||||
/** @}*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
*\addtogroup SdoServices SDO Services
|
||||
* @{
|
||||
*/
|
||||
#define SDOINFOSERVICE_OBJDICTIONARYLIST_Q 0x01 /**< \brief SDO Info Get object list request*/
|
||||
#define SDOINFOSERVICE_OBJDICTIONARYLIST_S 0x02 /**< \brief SDO Info Get object list response*/
|
||||
#define SDOINFOSERVICE_OBJDESCRIPTION_Q 0x03 /**< \brief SDO Info Get object description request*/
|
||||
#define SDOINFOSERVICE_OBJDESCRIPTION_S 0x04 /**< \brief SDO Info Get object description response*/
|
||||
#define SDOINFOSERVICE_ENTRYDESCRIPTION_Q 0x05 /**< \brief SDO Info Get entry description request*/
|
||||
#define SDOINFOSERVICE_ENTRYDESCRIPTION_S 0x06 /**< \brief SDO Info Get entry description request*/
|
||||
#define SDOINFOSERVICE_ERROR_Q 0x07 /**< \brief SDO Info Error*/
|
||||
#define SDOINFOSERVICE_INCOMPLETE 0x80 /**< \brief SDO Info Incomplete flag*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
*\addtogroup SdoHeaderData SDO Header and Data
|
||||
* @{
|
||||
*/
|
||||
/**
|
||||
* SDO Info Object list
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 ListType; /**< \brief List type variable*/
|
||||
#define INFO_LIST_TYPE_LENGTH 0 /**< \brief Length of all list types*/
|
||||
#define INFO_LIST_TYPE_ALL 1 /**< \brief All objects*/
|
||||
#define INFO_LIST_TYPE_RXPDO 2 /**< \brief Only RxPDO mappable objects*/
|
||||
#define INFO_LIST_TYPE_TXPDO 3 /**< \brief Only TxPDO mappable objects*/
|
||||
#define INFO_LIST_TYPE_BACKUP 4 /**< \brief Only backup objects*/
|
||||
#define INFO_LIST_TYPE_SET 5 /**< \brief Only setting objects*/
|
||||
#define INFO_LIST_TYPE_MAX 5 /**< \brief Max list identifier*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOLIST;
|
||||
|
||||
|
||||
/**
|
||||
* Object description
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 DataType; /**< \brief Object data type*/
|
||||
UINT16 ObjFlags; /**< \brief Object flags (including object code and max subindex)*/
|
||||
|
||||
/*Object Code and MaxSubindex will be set manually in the object dictionary. The value is always created in little endian format*/
|
||||
#define OBJFLAGS_MAXSUBINDEXMASK 0x00FF /**< \brief Max subindex mask*/
|
||||
#define OBJFLAGS_MAXSUBINDEXSHIFT 0 /**< \brief Max subindex shift*/
|
||||
#define OBJFLAGS_OBJCODEMASK 0x0F00 /**< \brief Mask Object code mask*/
|
||||
#define OBJFLAGS_OBJCODESHIFT 8 /**< \brief Object code shift*/
|
||||
|
||||
#define OBJCODE_VAR 0x07 /**< \brief Object code VARIABLE*/
|
||||
#define OBJCODE_ARR 0x08 /**< \brief Object code ARRAY*/
|
||||
#define OBJCODE_REC 0x09 /**< \brief Object code RECORD*/
|
||||
|
||||
// char Name[]; // rest of mailbox data
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOOBJDESC;
|
||||
|
||||
|
||||
#define SDO_INFO_OBJ_DESC_SIZE SIZEOF(TSDOINFOOBJDESC) /**< \brief Object description size*/
|
||||
|
||||
|
||||
/**
|
||||
* SDO Info object description service data
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 Index; /**< \brief Object index*/
|
||||
TSDOINFOOBJDESC Res; /**< \brief Description data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOOBJ;
|
||||
|
||||
|
||||
#define SDO_INFO_OBJ_DESC_RES_SIZE SIZEOF(TSDOINFOOBJ)/**< \brief Size of the object description service data*/
|
||||
|
||||
|
||||
/**
|
||||
* SDO Info entry description
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 DataType; /**< \brief Entry data type*/
|
||||
UINT16 BitLength; /**< \brief Entry bit size*/
|
||||
UINT16 ObjAccess; /**< \brief Entry access rights*/
|
||||
/**<
|
||||
Bit 0: Read Access in Pre-Op
|
||||
<br>Bit 1: Read Access in Safe-Op
|
||||
<br>Bit 2: Read Access in Op
|
||||
<br>Bit 3: Write Access in Pre-Op
|
||||
<br>Bit 4: Write Access in Safe-Op
|
||||
<br>Bit 5: Write Access in Op
|
||||
<br>Bit 6: mappable in RxPDO
|
||||
<br>Bit 7: mappable in TxPDO
|
||||
<br>Bit 8: entry will be included in backup
|
||||
<br>Bit 9: entry will be included in settings*/
|
||||
|
||||
#define ACCESS_READWRITE 0x003F /**< \brief Read/write in all states*/
|
||||
#define ACCESS_READ 0x0007 /**< \brief Read only in all states*/
|
||||
#define ACCESS_READ_PREOP 0x0001 /**< \brief Read only in PreOP*/
|
||||
#define ACCESS_READ_SAFEOP 0x0002 /**< \brief Read only in SafeOP*/
|
||||
#define ACCESS_READ_OP 0x0004 /**< \brief Read only in OP*/
|
||||
#define ACCESS_WRITE 0x0038 /**< \brief Write only in all states*/
|
||||
#define ACCESS_WRITE_PREOP 0x0008 /**< \brief Write only in PreOP*/
|
||||
#define ACCESS_WRITE_SAFEOP 0x0010 /**< \brief Write only in SafeOP*/
|
||||
#define ACCESS_WRITE_OP 0x0020 /**< \brief Write only in OP*/
|
||||
#define OBJACCESS_NOPDOMAPPING 0x0000 /**< \brief Not PDO mappable*/
|
||||
#define OBJACCESS_RXPDOMAPPING 0x0040 /**< \brief Mappable in RxPDOs*/
|
||||
#define OBJACCESS_TXPDOMAPPING 0x0080 /**< \brief Mappable in TxPDOs*/
|
||||
#define OBJACCESS_BACKUP 0x0100 /**< \brief Backup entry*/
|
||||
#define OBJACCESS_SETTINGS 0x0200 /**< \brief Setting Entry*/
|
||||
#define OBJACCESS_SAFEINPUTS 0x0400 /**< \brief Safe input*/
|
||||
#define OBJACCESS_SAFEOUTPUTS 0x0800 /**< \brief Safe output*/
|
||||
#define OBJACCESS_SAFEPARAMETER 0x1000 /**< \brief Safe parameter*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOENTRYDESC;
|
||||
|
||||
|
||||
/**
|
||||
* SDO Info entry description service data
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 Index; /**< \brief Index of the parent object*/
|
||||
UINT16 Info; /**< \brief Info data including the subindex and identifier for the payload data*/
|
||||
#define ENTRY_MASK_SUBINDEX 0x00FF /**< \brief Subindex mask*/
|
||||
#define ENTRY_SUBINDEX_SHIFT 0 /**< \brief Subindex shift*/
|
||||
#define ENTRY_MASK_VALUEINFO 0xFF00 /**< \brief Value info mask*/
|
||||
#define ENTRY_VALUEINFO_SHIFT 8 /**< \brief Value info shift*/
|
||||
|
||||
TSDOINFOENTRYDESC Res; /**< \brief Entry description data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOENTRY;
|
||||
|
||||
|
||||
/**
|
||||
* SDO info Error
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT32 ErrorCode; /**< \brief SDO Info error code*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOERROR;
|
||||
|
||||
|
||||
#define SDO_INFO_ERROR_SIZE SIZEOF(TSDOINFOERROR)/**< \brief SDO Info error size*/
|
||||
|
||||
|
||||
/**
|
||||
*SDO Info header
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
UINT16 InfoHead; /**< \brief Opcode, Incomplete flag and 8 reserved bits*/
|
||||
#define INFOHEAD_OPCODE_MASK 0x007F /**< \brief Opcode mask*/
|
||||
#define INFOHEAD_OPCODE_SHIFT 0 /**< \brief Opcode shift*/
|
||||
#define INFOHEADER_INCOMPLETE_MASK 0x0080 /**< \brief Incomplete mask*/
|
||||
#define INFOHEADER_INCOMPLETE_SHIFT 0 /**< \brief Incomplete shift, the incomplete value "SDOINFOSERVICE_INCOMPLETE" is defined as a UINT8 => no shift required*/
|
||||
|
||||
UINT16 FragmentsLeft; /**< \brief Number of fragments which will follow*/
|
||||
|
||||
union MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TSDOINFOLIST List;/**< \brief List data*/
|
||||
TSDOINFOOBJ Obj;/**< \brief Object Description data*/
|
||||
TSDOINFOENTRY Entry;/**< \brief Entry description data*/
|
||||
TSDOINFOERROR Error;/**< \brief Error data*/
|
||||
UINT16 Data[1];/**< \brief Unspecified data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
Data;/**< \brief Service data*/
|
||||
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFOHEADER;
|
||||
|
||||
|
||||
/**
|
||||
* SDO Info complete datagram
|
||||
*/
|
||||
typedef struct MBX_STRUCT_PACKED_START
|
||||
{
|
||||
TMBXHEADER MbxHeader; /**< \brief Mailbox header*/
|
||||
TCOEHEADER CoeHeader; /**< \brief CoE header*/
|
||||
TSDOINFOHEADER SdoHeader; /**< \brief SDO Info header and data*/
|
||||
}MBX_STRUCT_PACKED_END
|
||||
TSDOINFORMATION;
|
||||
|
||||
|
||||
#define SIZEOF_SDOINFOHEAD 4 /**< \brief SDO Info header size*/
|
||||
#define SIZEOF_SDOINFO ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD )) /**< \brief SDO Info and CoE Size*/
|
||||
#define SIZEOF_SDOINFOSTRUCT ( (MBX_HEADER_SIZE) + (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) ) /**< \brief Complete SDO Info datagram size*/
|
||||
#define SIZEOF_SDOINFOLISTHEAD 2 /**< \brief SDO Info list header size*/
|
||||
#define SIZEOF_SDOINFOLISTSTRUCT (( COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOLISTHEAD) ) /**< \brief Complete SDO Info list size*/
|
||||
#define SIZEOF_SDOINFOOBJSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_OBJ_DESC_RES_SIZE) ) /**< \brief Complete SDO Info object description response size*/
|
||||
#define SIZEOF_SDOINFOENTRYREQHEAD 4 /**< \brief SDO Info entry description request size*/
|
||||
#define SIZEOF_SDOINFOENTRYREQSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SIZEOF_SDOINFOENTRYREQHEAD) ) /**< \brief SDO Info entry description request size*/
|
||||
#define SIZEOF_SDOINFOERRORSTRUCT ( (COE_HEADER_SIZE) + (SIZEOF_SDOINFOHEAD) + (SDO_INFO_ERROR_SIZE) ) /**< \brief SDO Info error size*/
|
||||
|
||||
|
||||
#define SDO_INFO_HEADER_BYTE_SIZE ((SIZEOF_SDOINFOSTRUCT)+(SIZEOF_SDOINFOLISTHEAD)) /**< \brief SDO Info size*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SdoPendingHandler SDO Pending Response
|
||||
* @{
|
||||
* If this feature is enabled (SDO_RES_INTERFACE 1) a SDO request may set to pending and is completed afterwards.
|
||||
* This can be used to forward the read/write request an additional thread, uController.
|
||||
*/
|
||||
#define SDO_PENDING_WRITE 0x1 /**< \brief SDO write request is pending*/
|
||||
#define SDO_PENDING_SEG_WRITE 0x2 /**< \brief SDO segmented write request is pending*/
|
||||
#define SDO_PENDING_READ 0x3 /**< \brief SDO read request is pending*/
|
||||
#define SDO_PENDING_SEG_READ 0x4 /**< \brief SDO segmented read request is pending*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
#endif //_SDOSRV_H_
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Variables
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
/* ECATCHANGE_START(V5.11) ECAT10*/
|
||||
#if defined(_SDOSERV_) && (_SDOSERV_ == 1)
|
||||
/* ECATCHANGE_END(V5.11) ECAT10*/
|
||||
#define PROTO
|
||||
#else
|
||||
#define PROTO extern
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \addtogroup SdoPendingHandler SDO Pending Response
|
||||
*@{
|
||||
*/
|
||||
PROTO UINT8 u8PendingSdo; /**< \brief Identifier for the pending SDO service (see SDO_PENDING_XXX defines)*/
|
||||
PROTO BOOL bStoreCompleteAccess; /**< \brief Indicates if the pending SDO service is an complete access*/
|
||||
PROTO UINT8 u8StoreSubindex; /**< \brief Contains the subindex for the pending SDO request*/
|
||||
PROTO UINT16 u16StoreIndex; /**< \brief Contains the Index for the pending SDO request*/
|
||||
PROTO UINT32 u32StoreDataSize; /**< \brief Complete data size to be handled*/
|
||||
PROTO UINT16 MBXMEM *pStoreData; /**< \brief Pointer to the buffer to be handled*/
|
||||
PROTO UINT8 (* pSdoPendFunc)( UINT16 Index, UINT8 Subindex, UINT32 Size, UINT16 MBXMEM * pData, UINT8 bCompleteAccess ); /**< \brief Function pointer for the SDO pending request handler*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
PROTO MEM_ADDR VARMEM aSdoInfoHeader[GET_MEM_SIZE(SDO_INFO_HEADER_BYTE_SIZE)]; /**< \brief SDO Info buffer*/
|
||||
|
||||
|
||||
/**
|
||||
* \addtogroup SegmentedSdo Segmented SDO
|
||||
* @{
|
||||
*/
|
||||
PROTO UINT16 VARMEM * VARMEM pSdoSegData; /**< \brief Pointer to the buffer handling to full object/entry data*/
|
||||
PROTO UINT16 VARMEM nSdoInfoFragmentsLeft; /**< \brief Number of fragments which need to be transmitted*/
|
||||
/** @}*/
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------------------------
|
||||
------
|
||||
------ Global Functions
|
||||
------
|
||||
-----------------------------------------------------------------------------------------*/
|
||||
PROTO UINT8 SDOS_SdoInfoInd(TSDOINFORMATION MBXMEM *pSdoInfoInd);
|
||||
PROTO UINT8 SDOS_SdoInd(TINITSDOMBX MBXMEM *pSdoInd);
|
||||
|
||||
PROTO void SDOS_SdoRes(UINT8 abort, UINT32 objLength, UINT16 MBXMEM *pData);
|
||||
|
||||
#undef PROTO
|
||||
/** @}*/
|
||||
+47
-2
@@ -13,6 +13,10 @@ extern OTAHandler ota;
|
||||
/** @brief Bootloader 全局运行实例。 */
|
||||
BootloaderSystem sys;
|
||||
|
||||
/* CoE 0x100A「Software version」的写入口,实现在 comm/ecat/coeappl.c(C 链接)。
|
||||
* BOOT 没有第二条路把"我是哪一版"告诉总线,起板时填一次。 */
|
||||
extern "C" void AppSetSoftwareVersion(const char *version);
|
||||
|
||||
/**
|
||||
* @brief 初始化 Bootloader 运行所需的硬件外设。
|
||||
*
|
||||
@@ -43,6 +47,7 @@ void BootloaderSystem::initHardware() {
|
||||
(unsigned long)APP_MAX_SIZE);
|
||||
printf("[BOOT] SystemCoreClock=%lu Hz\r\n", (unsigned long)SystemCoreClock);
|
||||
printf("[BOOT] ESP32_EN PC9=%d (1=released, 0=HOLDING ESP32 IN RESET)\r\n",
|
||||
|
||||
(int)gpio_input_bit_get(GPIOC, GPIO_PIN_9));
|
||||
/* D3V3 正常时 HIGH;若此处读回 0 说明 ESP32 上拉电源域未就绪 */
|
||||
printf("[BOOT] D3V3 check: PC9 readback=%d\r\n",
|
||||
@@ -56,6 +61,16 @@ void BootloaderSystem::initHardware() {
|
||||
/** 让 SPI OTA 模块在收到完整帧后,转交给 ota 实例处理。 */
|
||||
SpiOta::setSender(&ota);
|
||||
|
||||
/** 版本号填进 CoE 0x100A(SSC 自带占位是 "1.0")。主站在刷前/刷后各读一次,
|
||||
* 这是唯一不依赖串口/ESP32 中继的「对面到底哪一版」总线读数。 */
|
||||
{
|
||||
char ver[20];
|
||||
snprintf(ver, sizeof ver, "%u.%u.%u.%u",
|
||||
(unsigned)BOOT_FW_VERSION_MAJOR, (unsigned)BOOT_FW_VERSION_MINOR,
|
||||
(unsigned)BOOT_FW_VERSION_PATCH, (unsigned)BOOT_FW_VERSION_BUILD);
|
||||
AppSetSoftwareVersion(ver);
|
||||
}
|
||||
|
||||
/** 写入 BOOT 版本号到 OTA 配置页(仅当版本变更时重写)。
|
||||
* 采用与 OTA_SET 一致的读-擦-写策略,但将 word6/word7 合并为单次页面擦除,
|
||||
* 避免两次 OTA_SET 调用导致的双重擦除。 */
|
||||
@@ -124,6 +139,21 @@ void BootloaderSystem::initHardware() {
|
||||
/** @brief 心跳打印间隔(毫秒)。 */
|
||||
static constexpr uint32_t kHeartbeatPrintIntervalMs = 2000U;
|
||||
|
||||
/**
|
||||
* @brief EtherCAT 邮箱栈:首次调用拉起,之后每圈推一次 SSC 状态机。
|
||||
*
|
||||
* 挂在 pollAllLinks 是刻意的:三条「留在 BOOT」的路径(wait_ota_window /
|
||||
* stay_in_ota / stay_boot_wait_ota)都过这个函数,而 A 区有效直接跳转的那条
|
||||
* 不过 —— 正常开机的板子因此完全不碰 PB10/EXMC,也不会顺手把 ESC 复位一遍。
|
||||
*
|
||||
* 尺寸:邮箱-only SSC + 真移植层实测定格在 ER_ROM0 64,552 B / 72KB(余 9,176 B),
|
||||
* RAM 32,392 B / 128KB(堆为 SSC 分段下载放大到 16KB)。来由与归因链见
|
||||
* sys/config.h 的 BOOT 版本说明块。
|
||||
*/
|
||||
extern "C" void ecatTaskStart(void);
|
||||
extern "C" void ecatTaskPoll(void);
|
||||
static bool g_ecatStarted = false;
|
||||
|
||||
/**
|
||||
* @brief 主循环辅助函数:轮询 CAN + SPI OTA 后台任务。
|
||||
*
|
||||
@@ -138,6 +168,14 @@ static void pollAllLinks(void) {
|
||||
* 仍调用一次以排空 can0_receive_count,避免计数堆积。
|
||||
*/
|
||||
CAN0_transmiter.communication_check();
|
||||
|
||||
if (g_ecatStarted) {
|
||||
ecatTaskPoll();
|
||||
} else {
|
||||
g_ecatStarted = true;
|
||||
/* 含 RST# 30ms + HBI/DL_STATUS 轮询,最坏 ~350ms 的一次性开销。 */
|
||||
ecatTaskStart();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,7 +275,11 @@ void BootloaderSystem::run() {
|
||||
pollAllLinks();
|
||||
heartbeatPrint(heartbeat_counter, 10U, "wait_ota_window");
|
||||
|
||||
if (ota.isActive()) {
|
||||
/* ECAT 会话也算「有人在灌」:ecatBegin 只置 ecat_session,不碰 ota_state,
|
||||
* 而实测 67KB 要 11.9s > 本窗口 10s。不算它就会走 ota_window_timeout_then_jump
|
||||
* → jumpToApplication 无条件把 EN 写回 AVAL → 跳进半截镜像 → HardFault →
|
||||
* FWDGT → 再来 10s,活锁。 */
|
||||
if (ota.isActive() || ota.ecatSessionActive()) {
|
||||
ota_requested = 1;
|
||||
#if DEBUG_PRINT
|
||||
printf("[BOOT] ota_requested at %lu ms\r\n", (unsigned long)delay_count);
|
||||
@@ -265,7 +307,10 @@ void BootloaderSystem::run() {
|
||||
pollAllLinks();
|
||||
heartbeatPrint(hb_ota, 1U, "stay_in_ota");
|
||||
|
||||
const bool active_now = ota.isActive();
|
||||
/* ECAT 会话同样计入「有人在灌」,理由见上面 wait_ota_window 处注释。
|
||||
* commit 成功会清掉 ecat_session,于是这里的「曾 active 现回 IDLE」
|
||||
* 分支照常评估;实际跳转更早由 pending_app_jump 在 process() 里做掉。 */
|
||||
const bool active_now = ota.isActive() || ota.ecatSessionActive();
|
||||
if (active_now) {
|
||||
was_active = true;
|
||||
idle_since = 0U;
|
||||
|
||||
+170
-9
@@ -17,6 +17,10 @@
|
||||
extern CAN_transmiter CAN0_transmiter;
|
||||
OTAHandler ota;
|
||||
|
||||
/** @brief BOOT EtherCAT 移植层的拆除入口(comm/ecat/boot_ecat_hw.cpp)。
|
||||
* 只声明不引 SSC 头,避免把 ecat_def.h 那套宏漏进 OTA.cpp。 */
|
||||
extern "C" void bootEcatDeinit(void);
|
||||
|
||||
namespace {
|
||||
__attribute__((naked, noreturn)) void branchToApplication(uint32_t msp_value, uint32_t reset_handler) {
|
||||
__asm volatile(
|
||||
@@ -169,6 +173,10 @@ u8 OTA_SET(u32 addr, u32 data) {
|
||||
temp_val[6] = *flash_ptr;
|
||||
flash_ptr = (volatile u32 *)n_BOOT_VERSION_ADDR1;
|
||||
temp_val[7] = *flash_ptr;
|
||||
/** word 8 = EtherCAT 下载 arm 令牌。漏读会被下面整页擦除静默清掉,
|
||||
* 表现为"刚 arm 完、一次标志更新就又不让刷了"。 */
|
||||
flash_ptr = (volatile u32 *)n_ECAT_OTA_ARM_ADDR;
|
||||
temp_val[8] = *flash_ptr;
|
||||
|
||||
/** 整页擦除后重建配置页。 */
|
||||
fmc_unlock();
|
||||
@@ -547,6 +555,10 @@ return;
|
||||
can_deinit(CAN0);
|
||||
spi_i2s_deinit(SPI0);
|
||||
exti_interrupt_disable(EXTI_15);
|
||||
/** EtherCAT 那侧:EXTI1/NVIC、LAN9252 IRQ 引脚、EXMC(PD/PE 引脚占用)、DWT。
|
||||
* 漏掉的话 APP 起来会吃到 BOOT 留的 pending ESC 中断,且 EXMC 引脚已被
|
||||
* 配成 AF 推挽,APP 自己的 ExmcBus::init() 会在一个被占用的总线上重跑。 */
|
||||
bootEcatDeinit();
|
||||
|
||||
#if DEBUG_PRINT
|
||||
printf("[J4]\r\n");
|
||||
@@ -586,6 +598,19 @@ return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG_PRINT
|
||||
/* 本行是「BOOT 交出 CPU」的确定标记。microlib + BootDebugUart::putChar
|
||||
* (sys/initial.cpp:47-55) 逐字符阻塞等 TBE,**没有 stdio 缓冲** —— 日志停在半截就是
|
||||
* CPU 真停在那里,不是尾巴没刷出来。1.1.0.13 实测最后停在 [J5-HEX] 那 16 字节之后
|
||||
* (其后那个 printf 的第一个字符都没出来)⇒ 卡点在 BOOT 里,不在 APP。
|
||||
* 带版本号:power-on banner 里没有版本行,刷完总得能确认板上是哪一版。 */
|
||||
printf("[J6] boot v%u.%u.%u.%u handoff\r\n",
|
||||
(unsigned)BOOT_FW_VERSION_MAJOR, (unsigned)BOOT_FW_VERSION_MINOR,
|
||||
(unsigned)BOOT_FW_VERSION_PATCH, (unsigned)BOOT_FW_VERSION_BUILD);
|
||||
/* 无缓冲的 microlib 下 fflush 是空操作,留着只为换回标准库时语义不变。 */
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 直接用内联汇编跳转,消除 naked 函数调用的所有不确定性。
|
||||
* 使用显式寄存器变量保证 msp_value 在 r0,reset_handler 在 r1。
|
||||
@@ -766,19 +791,155 @@ void OTAHandler::writeApplicationData(u32 offset, u8 *data, u32 size) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (i < size) {
|
||||
u32 word = 0xFFFFFFFFU;
|
||||
memcpy(&word, &data[i], size - i);
|
||||
fmc_state_enum st = fmc_word_program(write_addr + i, word);
|
||||
if (st != FMC_READY) {
|
||||
fmc_lock();
|
||||
}
|
||||
|
||||
/*============================================================================
|
||||
* EtherCAT (CoE SDO) 下载会话
|
||||
*
|
||||
* 刻意不新建第二套写 flash 的逻辑:擦、写、校验、CRC、标志位全部走 SPI/CAN
|
||||
* 那条已经在产线上跑过的路,所以「掉电中断 → AVAL 未置 → 留守 BOOT 可重灌」
|
||||
* 这条既有保证原样成立。
|
||||
*==========================================================================*/
|
||||
|
||||
bool OTAHandler::ecatArmed() const
|
||||
{
|
||||
if ((*(__IO u32 *)n_ECAT_OTA_ARM_ADDR) == OTA_ECAT_ARM_KEY) {
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 出厂首刷 / 救砖:BOOT 里没有可启动的 APP 时门控自动打开。
|
||||
*
|
||||
* 判据与 main.cpp run() 决定「jump_latest_now」的两个配置字逐字一致,所以
|
||||
* 只要 BOOT 因为任何原因留守(空片、下载中掉电、A 区 CRC 失败被 main.cpp:240
|
||||
* 标无效、FWDGT 复位被 main.cpp:208 标无效),总线上就能直接重灌;不需要
|
||||
* ESP32 在场,也不需要先有一次成功的刷写来置令牌 —— 这正是本方案立项的理由。
|
||||
* 反过来 APP 健康在跑时两个标志都是 OTA_AVAL,令牌仍是硬门:SDO 写不了
|
||||
* 令牌,也就刷不动一台正常机器。
|
||||
*/
|
||||
const u32 app_check = *(__IO u32 *)n_APP_LATEST_CHECK_ADDR;
|
||||
const u32 app_enable = *(__IO u32 *)n_APP_LATEST_EN_ADDR;
|
||||
return !(app_check == OTA_AVAL && app_enable == OTA_AVAL);
|
||||
}
|
||||
|
||||
bool OTAHandler::ecatArmSet(bool armed)
|
||||
{
|
||||
return OTA_SET(n_ECAT_OTA_ARM_ADDR, armed ? OTA_ECAT_ARM_KEY : OTA_NOT_AVAL)
|
||||
== FLASH_EEPROM_OK;
|
||||
}
|
||||
|
||||
bool OTAHandler::ecatBegin(u32 expectedSize)
|
||||
{
|
||||
if (!ecatArmed()) {
|
||||
#if DEBUG_PRINT
|
||||
printf("[FW] write tail err 0x%08lX st=%u\r\n", (unsigned long)(write_addr + i), (unsigned)st);
|
||||
printf("[ECAT-OTA] begin 拒绝:未 arm\r\n");
|
||||
#endif
|
||||
fmc_lock();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
/** SPI/CAN 会话正在进行时拒绝:BOOT 只能有一个 flash writer,
|
||||
* 两边交错 fmc_word_program / 整页擦除会写出静默错数据。 */
|
||||
if (isActive()) {
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] begin 拒绝:SPI/CAN OTA 会话占用中\r\n");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
fmc_lock();
|
||||
if (expectedSize == 0U || expectedSize > APP_FIRMWARE_SIZE) {
|
||||
return false;
|
||||
}
|
||||
if (ecat_session) {
|
||||
ecatAbort(); /* 上一轮没 commit,直接重来 */
|
||||
}
|
||||
|
||||
expected_size = expectedSize;
|
||||
received_size = 0U;
|
||||
ecat_recv = 0U;
|
||||
/** 整区预擦(内部先把 A 区 CHECK/AVAL/EN 三个标志全标无效,再按 expected_size
|
||||
* 算页数擦)。约 1s、阻塞主循环,期间邮箱不回应,主站 SDO 超时要给够;
|
||||
* 比"边收边按需擦"简单,且和 SPI 那条路同构。 */
|
||||
eraseApplicationArea();
|
||||
ecat_session = true;
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] begin ok size=%lu\r\n", (unsigned long)expectedSize);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OTAHandler::ecatFeed(const u8 *data, u32 len)
|
||||
{
|
||||
if (!ecat_session || data == nullptr || len == 0U) {
|
||||
return false;
|
||||
}
|
||||
if (ecat_recv + len > expected_size) {
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] feed 越界 recv=%lu len=%lu exp=%lu\r\n",
|
||||
(unsigned long)ecat_recv, (unsigned long)len, (unsigned long)expected_size);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
/** writeApplicationData 按 4 字节对齐算地址,所以游标必须 4 对齐。
|
||||
* 块长 4096 保证中间每块都对齐;最后一块允许短,但短完也就没有下一块了。 */
|
||||
if ((ecat_recv & 3U) != 0U) {
|
||||
return false;
|
||||
}
|
||||
writeApplicationData(ecat_recv, (u8 *)data, len);
|
||||
ecat_recv += len;
|
||||
received_size = ecat_recv;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OTAHandler::ecatCommit()
|
||||
{
|
||||
if (!ecat_session) {
|
||||
return false;
|
||||
}
|
||||
ecat_session = false;
|
||||
|
||||
if (ecat_recv != expected_size) {
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] commit 拒绝:只收了 %lu / %lu\r\n",
|
||||
(unsigned long)ecat_recv, (unsigned long)expected_size);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
if (!validateApplication()) {
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] commit 拒绝:向量表校验失败\r\n");
|
||||
#endif
|
||||
resetToIdle();
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 与 OTA.cpp:364-402 那条 SPI 提交路径逐字对齐,别改顺序:
|
||||
* CRC 必须先于 AVAL 写 —— verifyAppCrc() 对擦除态 0xFFFFFFFF 是返回 true 的
|
||||
* (OTA.cpp:1327-1329),真正的门控是下面这三个标志。 */
|
||||
const u32 latest_crc = calculateAppCrc(APPLATEST_START_ADDR, APP_FIRMWARE_SIZE / 4U);
|
||||
writeAppCrc(APPLATEST_START_ADDR, latest_crc);
|
||||
|
||||
/** 用完即弃:刷成功就把令牌收回擦除态,免得一次 arm 变成永久敞开。
|
||||
* 放在置标志之前是安全的 —— 此刻 CHECK/EN 仍是 NOT_AVAL(eraseApplicationArea
|
||||
* 标的),所以即便这一拍之后又有邮箱请求,ecatArmed 的救砖分支照样为真;
|
||||
* 等下面三个标志置起来、APP 能启动了,门就彻底关上。整页擦除一次,
|
||||
* 后面三个 OTA_SET 都会走 data==OTA_AVAL 的直接编程快路径,不再各擦一页。 */
|
||||
(void)ecatArmSet(false);
|
||||
|
||||
OTA_SET(n_APP_LATEST_CHECK_ADDR, OTA_AVAL);
|
||||
OTA_SET(n_APP_LATEST_AVAL_ADDR, OTA_AVAL);
|
||||
OTA_SET(n_APP_LATEST_EN_ADDR, OTA_AVAL);
|
||||
|
||||
/** 跳转不能在这里做 —— 调用栈还在 SSC 的邮箱事务里,中途跳走会把 SM1
|
||||
* 半包留在总线上。process() 在 pending_app_jump 分支会排在 IDLE 早退之前。 */
|
||||
pending_app_jump = true;
|
||||
#if DEBUG_PRINT
|
||||
printf("[ECAT-OTA] commit ok size=%lu,等主循环跳转\r\n", (unsigned long)ecat_recv);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void OTAHandler::ecatAbort()
|
||||
{
|
||||
ecat_session = false;
|
||||
ecat_recv = 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -137,6 +137,42 @@ class OTAHandler {
|
||||
*/
|
||||
void jumpToApplication();
|
||||
|
||||
/* ---- EtherCAT (CoE SDO) 下载会话 ------------------------------------
|
||||
*
|
||||
* BOOT 仍然是**唯一的 flash writer**:0x2501 的 SDO 写回调只调这几个方法,
|
||||
* 不自己碰 fmc_*。这条路与 SPI/CAN 那条共用 eraseApplicationArea /
|
||||
* writeApplicationData / validateApplication / CRC / 标志位,语义完全一致,
|
||||
* 所以「掉电中断 = AVAL 未置 = 留守 BOOT 可重灌」这条既有保证不变。
|
||||
*
|
||||
* 与 SPI/CAN 那条的区别:不走 AES 握手状态机(WAITING_RANDOM/CONFIRM 是
|
||||
* 48B 加密包专用),改由配置页 word8 的 arm 令牌门控 —— 见 ecatArmSet。
|
||||
* -------------------------------------------------------------------- */
|
||||
|
||||
/** @brief 是否已 arm(配置页 word8 == OTA_ECAT_ARM_KEY)。 */
|
||||
bool ecatArmed() const;
|
||||
|
||||
/** @brief 起会话:校验 arm/尺寸/无并发 SPI 会话,然后整区预擦。
|
||||
* @note 会阻塞约 1s(27 页擦除),期间邮箱不回应,主站 SDO 超时要给够。 */
|
||||
bool ecatBegin(u32 expectedSize);
|
||||
|
||||
/** @brief 落一块镜像。offset 由内部游标推进,调用方不传偏移。 */
|
||||
bool ecatFeed(const u8 *data, u32 len);
|
||||
|
||||
/** @brief 收齐后校验 + 写 CRC + 置 AVAL/CHECK/EN,并挂起延迟跳转。
|
||||
* @note 跳转不在这里发生(那时还在 SSC 邮箱事务里),由 process() 排空。 */
|
||||
bool ecatCommit();
|
||||
|
||||
/** @brief 放弃会话。已擦/已写的 A 区不恢复,靠 AVAL 未置守住。 */
|
||||
void ecatAbort();
|
||||
|
||||
/** @brief 已落字节数(0x2500:03 进度)。 */
|
||||
u32 ecatReceived() const { return ecat_recv; }
|
||||
/** @brief 会话是否活跃。 */
|
||||
bool ecatSessionActive() const { return ecat_session; }
|
||||
|
||||
/** @brief 经已认证的 SPI/CAN 通道置 arm 令牌。 */
|
||||
bool ecatArmSet(bool armed);
|
||||
|
||||
private:
|
||||
u32 ota_time; /**< OTA 会话计时器。 */
|
||||
|
||||
@@ -159,6 +195,9 @@ class OTAHandler {
|
||||
|
||||
bool pending_flash_erase = false; /**< 是否存在待处理的延迟擦除请求。 */
|
||||
bool pending_app_jump = false; /**< 是否存在待处理的应用跳转请求。 */
|
||||
|
||||
bool ecat_session = false; /**< EtherCAT(SDO) 下载会话是否进行中。 */
|
||||
u32 ecat_recv = 0U; /**< 本会话已落 A 区的字节数,即下一块的目标偏移。 */
|
||||
ota_send_fn_t transport_send = nullptr; /**< 当前底层回包发送函数。 */
|
||||
|
||||
/* Flash 写入环形缓冲(ISR 写入,主循环 drain 写 Flash);槽位数见 config.h */
|
||||
|
||||
+177
-4
@@ -12,11 +12,179 @@ typedef uint32_t u32;
|
||||
/** 调试打印总开关。置 1 时所有 printf 调试输出编译进去;置 0 时完全剔除,零开销。 */
|
||||
#define DEBUG_PRINT 1
|
||||
|
||||
/* BOOT 固件版本号 */
|
||||
/**
|
||||
* BOOT 固件版本变更说明(v1.1.0.6 / v1.1.0.7)
|
||||
*
|
||||
* v1.1.0.6: BOOT 原生 EtherCAT 收包。comm/ecat 那份与 APP 逐字节相同的 SSC 拷贝首次
|
||||
* 入编译配置(邮箱-only:DC_SUPPORTED=0、MAX_PD_IN/OUT=2、无 SAFEOP/OP,
|
||||
* APPL_Start{Input,Output}Handler 直接回 INVALIDALCONTROL 把从站钉在 PREOP);
|
||||
* 新增 boot_ecat_hw.cpp(EXMC Region0 沿用 APP 1.5.57 的 BUSLAT=4/ADDSET=4/DSET=20 与
|
||||
* SNCTL0 bit7 修补、HBI Indexed、CSR 间接端口双读校验 1.5.59、PRAM 逐 DWORD+AVAIL
|
||||
* 1.6.2、PB10 RST# 30ms、LAN9252 IRQ_CFG/INT_EN 使能 1.5.43、PB1→EXTI1→PDI_Isr)与
|
||||
* boot_ecat_app.cpp(0x2500 会话控制 + 0x2501 4KB 数据通道,SDO 分段由 SSC 攒完整块后
|
||||
* 才回调一次,故从站无 begin/data/commit 拼包状态机)。**没写 foe.c、没改
|
||||
* mailbox.c/ecatslv.c、没动 SII**——传输走这份拷贝本来就完整的 CoE SDO。
|
||||
* flash 写仍只有 OTAHandler 一个:ecatBegin/Feed/Commit 直接复用
|
||||
* eraseApplicationArea/writeApplicationData/validateApplication/CRC/OTA_SET,提交顺序与
|
||||
* SPI 那条一致,掉电中断=AVAL 未置=留守可重灌的既有保证不变;并发上 ecatBegin 在
|
||||
* SPI/CAN 会话活跃时拒绝。鉴权用配置页新 word8 arm 令牌(0xACA70001,避开擦除态与
|
||||
* OTA_AVAL)。jumpToApplication 补 bootEcatDeinit(EXTI1/NVIC、LAN9252 IRQ 引脚、EXMC、
|
||||
* DWT),否则 APP 会吃到 BOOT 留的 pending 中断并在被占用的总线上重跑 init。
|
||||
*
|
||||
* v1.1.0.7: 把上面那道 arm 门控改成能实际用的形态 —— 1.1.0.6 写完 ecatArmSet 却没有任何
|
||||
* 调用者,令牌永远置不上,每次 ecatBegin 都被拒,等于整条路是死的。现在 ecatArmed() =
|
||||
* 配置页令牌==KEY **或** A 区不可启动;判据与 main.cpp run() 决定 jump_latest_now 的
|
||||
* CHECK/EN 两字逐字一致,所以空板首刷、下载中断电、A 区 CRC 失败、FWDGT 复位这四种 BOOT
|
||||
* 留守情形天然放行,不需要 ESP32 在场(这正是「BOOT 原生刷」立项的理由);而 APP 正常在跑
|
||||
* 时两字皆 OTA_AVAL,令牌仍是硬门,SDO 写不了令牌也就刷不动一台正常机器。ecatCommit 成功
|
||||
* 路径在置三面标志之前 ecatArmSet(false) 收回令牌:用完即弃,不是一次 arm 永久敞开,同时
|
||||
* 给 ecatArmSet 一个真实调用者。此刻 CHECK/EN 还是 eraseApplicationArea 标的 NOT_AVAL,
|
||||
* 所以收回这一拍救砖分支照样为真、不影响本次提交;后面三个 OTA_SET 全走
|
||||
* data==OTA_AVAL 的直接编程快路径,总共只多擦一页。
|
||||
* 主站侧配套新增 ecat_native /api/ota/ecat(open/push/status/abort/probe/run)与
|
||||
* ecat.html「BOOT 刷写」页签;SII 身份字未动,所以 BOOT 与 APP 在总线上唯一的区别是
|
||||
* 0x1008 的字符串,而主站**只拿它当日志读数、不当门槛**(对着 APP 写 0x2500 只会 SDO
|
||||
* abort,对着老 BOOT 只会超时,两种都写不进一个字节)。能不能刷这一台,唯一裁决点就是
|
||||
* 本文件的 ecatArmed() —— 主站不复制第二份判据,否则两边一定会漂。
|
||||
*
|
||||
* 尺寸实测(v1.1.0.7 构建,map 里唯一的 ROM 区):ER_ROM0 Size 0xFC28=64,552B /
|
||||
* Max 0x12000=73,728B ⇒ 72KB BOOT 区余 9,176B(用 87.6%);产物 bin 64,848B。
|
||||
* 归因链:无 EtherCAT 基线 36,340B → 空移植层桩件 57,816B(净增 21,472B,对照组=
|
||||
* 关掉唯一调用者后 --gc-sections 把 SSC 全剥回 36,340B,故增量可归因)→ 真移植层
|
||||
* 64,552B。RAM 32,392B/131,072B:堆按 SSC 每笔分段下载 malloc 一整块而放大到
|
||||
* 0x4000(见 regions_GD32E508VE.h),栈仍是 0x400 —— 所以 boot_ecat_app.cpp 的 SDO
|
||||
* 回调里一律不许开块级大数组(s_dlStage 因此是文件级 static)。
|
||||
*
|
||||
* v1.1.0.8: 首次上板(空板、SWD 刷入、A 区全 0xFF)暴露两个问题,都是"静默"型的:
|
||||
* ① boot_ecat_hw.cpp 没 include sys/config.h,而 DEBUG_PRINT 只在那儿定义 ——
|
||||
* `#if DEBUG_PRINT` 按 0 处理,整套 [BOOT][ESC] 起板日志被编译掉,产物里连
|
||||
* "RST#"/"PDI ok" 这些串都搜不到(实测 grep 计数 0)。于是"看串口 PDI ok"这条
|
||||
* 判据从建立起就不可能成立。已补 include,并在注释里写明复核方法是 grep 产物而不是
|
||||
* 看编译通过。
|
||||
* ② ecatslv.c 的 Enable/DisableSyncManChannel 两个 do/while 等 SMx_Active(0x806) 的
|
||||
* PDI_DISABLE 位翻转,**没有任何上界**;MainInit() 一进来就调它们,位不翻就永久自旋。
|
||||
* BOOT 侧没开任何 MCU 看门狗,所以现场表现就是上电日志停在 path=stay_boot_wait_ota
|
||||
* 之后一行都没有(连 2s 心跳都没了)—— 与本次实测完全一致。现在两处各加 3000 次
|
||||
* 上界并把通道号/寄存器地址/最后读到的值 printf 出来,把死机换成一句能读的报错;
|
||||
* 正常路径一位即满足、一次不多转,行为不变。真正的根因(哪一路、为什么位不翻转)
|
||||
* 等这一版刷上去看报错再定,不在这里猜。
|
||||
*
|
||||
* v1.1.0.8 实测把上面 ② 的归因**证伪了**:刷上去后 `[BOOT][ESC] RST# 30ms, 释放回读=1`
|
||||
* 打出来了(①的 include 修复生效),然后一行都没有 —— 既没有 exmcInit 四条有界报错里的
|
||||
* 任何一条,也没有 2s 心跳。也就是说执行根本没走到 MainInit,SM 自旋不是这一拍的原因
|
||||
* (上界本身是该加的,保留)。反汇编本版产物定位(exmcInit 已内联进
|
||||
* bootEcatBringup@0x0800C3C8):printf 之后依次 4×rcu_periph_clock_enable、21×gpio_init、
|
||||
* exmc_norsram_struct_para_init/init/enable、SNCTL0 读改写清 bit7(0x0800C5EC),
|
||||
* 然后 **0x0800C5F8 是第一条 `LDRH r0,[r4,#0]`,r4=0x60000000**,其后才是 60 次
|
||||
* delay_1ms 的有界轮询与那句 FAIL printf。没打出 FAIL ⇒ 执行停在或就在这一条 LDRH 上。
|
||||
* 逐条排除(都有产物/实测依据,不是猜):MPU/SAU/GTZC 在 BOOT 树里一处都没有;本函数
|
||||
* 反汇编 0 条浮点指令;栈不是原因(ARM_LIB_STACK 0x400@0x2001FC00,越界只落进
|
||||
* 0x20007A88..0x2001FC00 这段没用 RAM,不会 fault);EXMC 参数与 APP 逐字段相同、两份
|
||||
* gd32e50x_exmc.[ch] 与 APP diff 为 0;s_delayCountdown 是 volatile(不会被 -O2 提成
|
||||
* b .);RST# 网络健康(驱动高读回 1)。总线侧独立旁证 MCU 彻底没在跑服务 PDI:
|
||||
* /api/open 后 AL Control(0x120)=0x0011 而 AL Event Request(0x220)=0x0011 的 bit0 仍挂起
|
||||
* —— PDI 只要读过 AL Control 就会清它;再请求 PREOP 超时且 AL Status Code=0x0000
|
||||
* (活着但拒绝会回非零错误码,回 0 码=没人应答)。同一条判据对着 APP 每天都能过,有灵敏度。
|
||||
*
|
||||
* ③ 本版真修(从 v1.1.0.8 的 .bin 向量表直接读出来的,不依赖推理):EXTI1_IRQHandler
|
||||
* 定义在本文件那个 extern "C" { 块**之外**(块从 471 行才开),按 C++ 修饰名编译,
|
||||
* 覆盖不了启动文件里的弱符号 ⇒ 向量表 IRQn7(偏移 0x05C) 实测 = 0x080001D3,正是
|
||||
* startup 的 `B .` 桩;连带整个 PDI_Isr 子树被 --gc-sections 当死代码剥掉,那一版镜像
|
||||
* 里「ESC 中断」这条路根本不存在。加 extern "C" 后复测:0x05C=0x08001A11、map 里出现
|
||||
* .text.EXTI1_IRQHandler、Code 61,388→62,684(+1,296B 就是原先被剥掉的那截);
|
||||
* 另外四个 BOOT 用到的中断(CAN0_TX/RX0、EXTI5_9、EXTI10_15)实测向量都已是 REAL。
|
||||
* 判据是读产物向量表,不是看编译通过 —— APP 侧 ethercat_sync.cpp:189 那句
|
||||
* 「中断入口 C 包装」注释就是同一个坑的历史记录。
|
||||
*
|
||||
* 尺寸实测(v1.1.0.9):ER_ROM0 0x104B8=66,744B / Max 0x12000=73,728B ⇒ 72KB 区余
|
||||
* 6,984B;bin 67,040B;RW/RAM 侧不变(Debug 构建实测 ER_ROM0 0x12450=74,832B 已经装不进
|
||||
* 72KB,差 1,104B —— 排障要用调试器请直接拿 Release 的 .axf,它本来就带 -gdwarf-5 全符号)。
|
||||
* 剩下的唯一未知是那条 LDRH 属于「AHB 不返回」还是「触发了 fault」,两者修法不同,判据
|
||||
* 是接 SWD 读 PC:PC=0x0800C5F8 ⇒ 总线不返回;PC 落在 0x080001C2..0x080001D4 这批 `B .`
|
||||
* 桩 ⇒ fault,接着读 CFSR/HFSR(0xE000ED28/0xE000ED2C) 与出栈 PC 定位。
|
||||
*
|
||||
* v1.1.0.10: 上面那句「唯一未知」也一并推翻 —— 逐行对 APP 起板路径时查出来根因,
|
||||
* 根本走不到那条 LDRH。boot_ecat_hw.cpp 原来是
|
||||
* exmc_norsram_struct_para_init(&ns); ns.read_write_timing=&rt; ns.write_timing=&wt;
|
||||
* 而库函数 exmc_norsram_struct_para_init() **不是给这两个指针赋默认值,是拿着这两个
|
||||
* 指针往里写 14 个默认字段**(gd32e50x_exmc.c:134-146)。先调后赋值 = 对栈上残留值做
|
||||
* 14 次写。v1.1.0.9 反汇编实测:ns 在 sp+0x40,两个指针槽在 sp+0x74/sp+0x78,
|
||||
* 0x0800C596 就 BL 了 para_init,直到 0x0800C59A/0x0800C59E 才把 &rt/&wt 写进去 ——
|
||||
* 也就是说调用时那两个指针是上一帧留下的垃圾。命不中合法地址就是 HardFault,落进启动
|
||||
* 文件的 B . 桩(本镜像里 HardFault=0x080001C2),BOOT 又没开 IWDG ⇒ 表现就是日志停在
|
||||
* RST# 之后一行都没有、连 2s 心跳都没了,与两次实测完全一致。APP 侧
|
||||
* esc_exmc_bus.cpp:181-183 一直是先赋指针再调,所以 APP 正常 —— 这就是两边的差别。
|
||||
* 修法照抄 APP 的顺序(两行上移),并加注释钉住「para_init 会解引用这两个指针」。
|
||||
* 同类调用全仓扫过:can/spi/dma 的 *_para_init 形参都是扁平结构体,无嵌套指针,只有
|
||||
* exmc 这一个有。判据:改完反汇编看 0x…C596 之前必须已经出现两条 STR 到 ns 的指针槽。
|
||||
*
|
||||
* v1.1.0.11: EtherCAT 刷写的**头一步就被从站自己拒了** —— 0x2500 的条目描述符把
|
||||
* sub1(cmd)/sub2(expected) 都写成了 ACCESS_READ,而 SSC 的 OBJ_Write 在 objdef.c:1325
|
||||
* 就做通用写权限检查,过了它才在 1352 分发到 ObjFoeDlCtlWrite。于是主站"声明总长"直接
|
||||
* 吃 abort,回调一次都没进过:SDO 写这半边从加进来那天起就没通过,之前上板证实的只有读。
|
||||
* 实测读数(native 已会把从站的 SDO abort 码打进日志):写 0x2500:01/:02/:03 一律
|
||||
* 0x06010002 "Attempt to write to a read only object";对照写 0x2501:00(唯一条目声明
|
||||
* ACCESS_READWRITE)拿到的是 0x08000000 General error —— 那才是进了本对象回调、因无会话
|
||||
* 而拒的正常形状。这条对照把病因精确钉在"逐条目权限位",排除了邮箱/SOEM/SSC 栈。
|
||||
* 改法:sub1/sub2 → ACCESS_READWRITE,sub3(进度)/sub4(状态) 保持只读;只改常量表 2 字节。
|
||||
* 判据(刷完第一件事):主站『探测』应先回 armed=1,再跑 run —— 日志里若还出现
|
||||
* 0x06010002 就是没刷进去;变成"begin ok/已灌 xxxx B"才说明门过了。
|
||||
*
|
||||
* v1.1.0.12: 两件事,都是"给正在跑 APP 的机器做常驻刷写"逼出来的。
|
||||
* ① 10s OTA 窗口不认 EtherCAT 会话 ⇒ **半截灌写会被送回 APP,形成活锁**。
|
||||
* 实测 67,136 B 要 11.9s(17 块 / ~0.7s 每块),而 APP_DELAY_BOOT_TIME=10000 是固定的,
|
||||
* 循环里只有 ota.isActive() 能让它转进无超时的 stay_in_ota —— 而 ecatBegin 只置
|
||||
* ecat_session、**不碰 ota_state**。所以窗口一到点就走 ota_window_timeout_then_jump,
|
||||
* 而 jumpToApplication 又无条件把 n_APP_LATEST_EN 写回 OTA_AVAL(OTA.cpp:545),
|
||||
* 于是半截镜像被当合法 APP 跳进去 → HardFault → FWDGT 复位 → BOOT 又留守 10s → 循环。
|
||||
* 改法:wait_ota_window 与 stay_in_ota 的活性判据都并上 ota.ecatSessionActive()
|
||||
* (现成 getter,OTA.h:171)。commit 成功会清 ecat_session,退出评估照常;
|
||||
* 实际回跳仍由 pending_app_jump 在 process() 里先做掉。
|
||||
* ⚠ 仍存的约束:**交接之后到 begin 之前只有 10s**,所以页面必须"一键把整条链跑完",
|
||||
* 不能靠人逐步点。
|
||||
* ② 0x100A 从 SSC 占位 "1.0" 改成真版本号:DEVICE_SW_VERSION_LEN 0x3→0x10,
|
||||
* coeappl.c 的 acSoftwareversion 显式给 LEN+1(原来靠初始化器推出 4 字节,
|
||||
* 而条目位长按 LEN 声明 ⇒ 主站按 16 字节读会越界),initHardware 里 snprintf 成真版本。
|
||||
* 主站已接:probe 回 sw、页面有「软件版本(0x100A)」格、刷写日志与完成态都带版本。
|
||||
* v1.1.0.13: 跳 APP 前补一发 LAN9252 硬复位(bootEcatDeinit 里新增 escResetForJump)。
|
||||
* 动机(板上实测):1.6.34 刷完首启必现 GD32 自检「ethercat 降级 / EXMC 总线未就绪」
|
||||
* (fault_codes[10]=1、module_mask 缺 bit10/11),断电重上电即好。故障态现场读数:
|
||||
* I210 链路 Up 100M、DL_STATUS=0x5613(bit0 PDI operational)、AL=PREOP、邮箱读得到
|
||||
* name="GD32E508 DCU " sw="1.6.34" —— 芯片有电、PDI 通、不是 D3 睡死,是**它没被复位**。
|
||||
* APP 起板探的第一项是 HBI_IDX_0==0x1234(DS 9.5.4.2 复位默认值),而 BOOT 侧 HBI 全是
|
||||
* 索引两步事务,bootEcatDeinit 最后一条 writeReg32 把它留在 0x0054(IRQ_CFG);带着残值
|
||||
* 跳进 APP 就报「LAN 没驱动总线」。此前复位只挂在 APP 自己 hardwareReset() 那一发 RST#
|
||||
* 上,它落不上就整体失败——而 jumpToApplication 是改 VTOR 后跳转,不是 NVIC_SystemReset。
|
||||
* 实现坑(别改回去):bootEcatDeinit 跑在 __disable_irq() 之后,**只能用 delay_1us()
|
||||
* 这种 __NOP() 忙等**;复用 escHardwareReset()/delay_1ms() 会因 SysTick 中断不递减而
|
||||
* 永久死等,BOOT 没开 IWDG ⇒ 刷完直接静默变砖,比原问题更糟。
|
||||
* 判据:重刷 BOOT 后走「刷完不复位」这条链,自检应全 0;若仍报 fault 1 且 APP 串口打
|
||||
* HBI_IDX_0=0x0054 ⇒ 复位仍没落到芯片上(BOOT/APP 两处都试过),转查 ECAT_NRST 网络与
|
||||
* hardwareReset 的 pin reads 回读;若是 0xFFFF/0x0000 ⇒ 芯片真没驱动总线,另案。
|
||||
* v1.1.0.14: 1.1.0.13 那发 RST# 补得不对,这里收口(板上实测:每次上电都卡在跳转后)。
|
||||
* ① gate:改由 s_escBusUp(bootEcatBringup 里 exmcInit 成功才置位)决定发不发。
|
||||
* 13 是无条件下发,于是 path=jump_latest_now(上电 3ms 直跳、这轮 ESC 从没起过)
|
||||
* 也在电源刚稳的窗口里拉了 RST#,释放点距上电仅约 15ms —— 踩 LAN9252 的
|
||||
* tpurstd(电源稳定→释放复位 ≥25ms),且那条路径上根本没有 HBI 残值要清。
|
||||
* 实测后果:跳进 APP 后整机静默,串口停在 [J5],ESP32 侧 SPI 完全无应答
|
||||
* (tx 706 / ready_timeout 2118 / role unknown),ESP32 uptime 一直涨 = 没有复位
|
||||
* 循环,GD32 停在某处不动。APP 镜像本身完好(CRC 两次 OK)。
|
||||
* ② 脉宽:delay_1us() 是 __NOP 环且按 4 cycle/迭代标定,实测 30000µs 只走出 8ms
|
||||
* (J3→J4 的间隔),当复位脉宽不够格;换成 DWT CYCCNT 忙等真 30ms + 释放后 2ms。
|
||||
* 这里不能用 delay_1ms():bootEcatDeinit 跑在 __disable_irq() 之后,SysTick 中断
|
||||
* 不递减 = 永久死等,BOOT 无 IWDG,比原问题更糟。
|
||||
* ③ 观测:跳转前打 [J6] boot v<ver> handoff 并 fflush(stdout)。BOOT 的 printf 是
|
||||
* stdio 行缓冲,跳进 APP 后 __rt_lib_init 重铺 FILE 结构会把未冲出的尾巴一起丢掉
|
||||
* —— 之前「串口停在 [J5-HEX] 半截」既可能是卡死也可能是跳转成功,读不出真相。
|
||||
* 判据:上电直跳应看到 [J6] 且 APP 正常起(自检 ethercat 不再降级);走总线刷写路径
|
||||
* 则 [BOOT][ESC] jump reset 出现、APP 起板 IDX_0 探到 0x1234。若直跳仍卡,说明与
|
||||
* RST# 无关,转查 v1.1.0.12 首次上板的两条(10s 窗口并 ecatSessionActive、
|
||||
* main.cpp:69 新增 snprintf —— BOOT 栈只有 1KB,拿 .map + fromelf 数栈余量)。
|
||||
*/
|
||||
#define BOOT_FW_VERSION_MAJOR 1U
|
||||
#define BOOT_FW_VERSION_MINOR 1U
|
||||
#define BOOT_FW_VERSION_PATCH 0U
|
||||
#define BOOT_FW_VERSION_BUILD 5U /* v1.1.0.5: isrProcessFrame 分发尾部缓存只收 ACK/ERROR,滤掉 WAIT——之前 WAIT 会污染 s_lastRespBuf,ESP32 超时重发时 DUP-SEQ 回放的仍是过期 WAIT 而非最终 ACK,重发链被拉长。曾同时试验过 stay_in_ota 去掉固定 1ms delay + process() 20us 步进条件化(v1.1.0.5 中间态),实测 ESP32 会在 armSpiDmasForNextCs() 的 dma disable/enable 窗口里抓 CS 读到错帧、saw_wait=0,已回滚为固定节拍。 */
|
||||
#define BOOT_FW_VERSION_BUILD 14U /* 变更来由见上方注释块 */
|
||||
|
||||
/**
|
||||
* Flash 布局说明(GD32E508VE,512KB)
|
||||
@@ -47,11 +215,12 @@ inline constexpr u32 SLEEP_ENABLE = 0x11111111U;
|
||||
inline constexpr u32 SLEEP_DISABLE = 0x00000000U;
|
||||
|
||||
/**
|
||||
* OTA 配置页一共使用 8 个 word:
|
||||
* OTA 配置页一共使用 9 个 word(页大小 2KB,余量充足):
|
||||
* word 0~5: latest_aval / backup_aval / latest_en / backup_en / latest_check / backup_check
|
||||
* word 6~7: BOOT 版本号(major|minor / patch|build)
|
||||
* word 8 : EtherCAT 下载 arm 令牌,值 == OTA_ECAT_ARM_KEY 才允许 SDO 刷 APP
|
||||
*/
|
||||
inline constexpr u32 OTA_CONFIG_NUM = 8U;
|
||||
inline constexpr u32 OTA_CONFIG_NUM = 9U;
|
||||
inline constexpr u32 n_APP_LATEST_AVAL_ADDR = OTA_CONFIG_PAGE_ADDR + 0x00U;
|
||||
inline constexpr u32 n_APP_BACKUP_AVAL_ADDR = OTA_CONFIG_PAGE_ADDR + 0x04U;
|
||||
inline constexpr u32 n_APP_LATEST_EN_ADDR = OTA_CONFIG_PAGE_ADDR + 0x08U;
|
||||
@@ -60,6 +229,10 @@ inline constexpr u32 n_APP_LATEST_CHECK_ADDR = OTA_CONFIG_PAGE_ADDR + 0x10U;
|
||||
inline constexpr u32 n_APP_BACKUP_CHECK_ADDR = OTA_CONFIG_PAGE_ADDR + 0x14U;
|
||||
inline constexpr u32 n_BOOT_VERSION_ADDR0 = OTA_CONFIG_PAGE_ADDR + 0x18U; /**< BOOT版本 word0: major|minor */
|
||||
inline constexpr u32 n_BOOT_VERSION_ADDR1 = OTA_CONFIG_PAGE_ADDR + 0x1CU; /**< BOOT版本 word1: patch|build */
|
||||
inline constexpr u32 n_ECAT_OTA_ARM_ADDR = OTA_CONFIG_PAGE_ADDR + 0x20U; /**< EtherCAT 下载 arm 令牌 word8 */
|
||||
/** @brief arm 令牌值。刻意避开擦除态 0xFFFFFFFF 与 OTA_AVAL 0x00000000,
|
||||
* 所以没写过这一字的旧板子/新板子都天然读成「未 arm」。 */
|
||||
inline constexpr u32 OTA_ECAT_ARM_KEY = 0xACA70001U;
|
||||
|
||||
/**
|
||||
* 本项目把 0x00000000 视为“有效/可用”,
|
||||
|
||||
Reference in New Issue
Block a user