feat: update CAN integration and T2G seat firmware

This commit is contained in:
vee
2026-08-01 15:55:21 +08:00
parent 8cda026ac2
commit 4aded5c21d
1443 changed files with 1007122 additions and 4978 deletions
+59
View File
@@ -0,0 +1,59 @@
CYPRESS END USER LICENSE AGREEMENT
PLEASE READ THIS END USER LICENSE AGREEMENT ("Agreement") CAREFULLY BEFORE DOWNLOADING, INSTALLING, OR USING THIS SOFTWARE AND ACCOMPANYING DOCUMENTATION ("Software"). BY DOWNLOADING, INSTALLING, OR USING THE SOFTWARE, YOU ARE AGREEING TO BE BOUND BY THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, PROMPTLY RETURN AND DO NOT USE THE SOFTWARE. IF YOU HAVE PURCHASED THE SOFTWARE, YOUR RIGHT TO RETURN THE SOFTWARE EXPIRES 30 DAYS AFTER YOUR PURCHASE AND APPLIES ONLY TO THE ORIGINAL PURCHASER.
License. Subject to the terms and conditions of this Agreement, Cypress Semiconductor Corporation ("Cypress") and its suppliers grant to you a non-exclusive, non-transferable license under its copyright rights to use the Software in object code form solely for the purpose of creating programming code for use on Cypress hardware products and transferring such programming code onto such hardware products ("Purpose"). For purposes of this Agreement, "Software" shall also include any upgrades, updates, bug fixes or modified versions provided to you by Cypress. You may make one backup copy of the Software.
SAMPLE (ES) SOFTWARE IS MADE AVAILABLE SOLELY FOR PURPOSES OF EVALUATION. ALL ES SOFTWARE IS PROVIDED “AS-IS” WITH NO WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED. CYPRESS DOES NOT WARRANT THAT ES SOFTWARE IS FULLY VERIFIED, TESTED, OR WILL OPERATE IN ACCORDANCE WITH DATA SHEET SPECIFICATIONS. CYPRESS DISCLAIMS ANY OBLIGATIONS FOR TECHNICAL SUPPORT AND BUG FIXES. CYPRESS SHALL NOT BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION DIRECT, INDIRECT, INCIDENTAL, SPECIAL, RELIANCE, OR CONSEQUENTIAL DAMAGES ARISING FROM OR IN CONNECTION WITH THE USE OF ES SOFTWARE IN ANY MANNER WHATSOEVER, EVEN IF CYPRESS HAS BEEN ADVISED OF THE POSSIBILITY THEREOF. CYPRESS MAKES NO REPRESENTATION THAT ES SOFTWARE PROVIDE ANY PARTICULAR FUNCTIONALITY, OR THAT ES SOFTWARE WILL MEET THE REQUIREMENTS OF A PARTICULAR USER APPLICATION. CYPRESS DOES NOT WARRANT THAT ES SOFTWARE IS ERROR-FREE, NOR DOES CYPRESS MAKE ANY OTHER REPRESENTATIONS OR WARRANTIES, WHETHER EXPRESS OR IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. THE FOREGOING STATES THE ENTIRE LIABILITY OF CYPRESS WITH RESPECT TO ES SOFTWARE.
Free and Open Source Software. Portions of the Software may be licensed under free and/or open source licenses such as the GNU General Public License ("FOSS"). FOSS is subject to the applicable license agreement and not this Agreement. If you are entitled to receive the source code from Cypress for any FOSS included with the Software, you may obtain the source code at no charge from http://www.cypress.com/go/opensource. The applicable license terms will accompany each source code package. To review the license terms applicable to any FOSS for which Cypress is not required to provide you with source code, please see the Software's installation directory on your computer.
Source Code. The Software may include source code of Cypress or its suppliers that is used to create applications that execute on Cypress hardware products or on other platforms in order to program Cypress hardware products ("Cypress Source Code"). Applications that you create using the Software that execute on Cypress hardware products are referred to as "Firmware." Applications that you create using the Software that execute on other platforms in order to program Cypress hardware products are referred to "Host Applications." The Cypress Source Code, Host Applications, and Firmware are subject to the license granted in this paragraph and not the license granted in the paragraph entitled "License." Subject to the terms and conditions of this Agreement, Cypress grants you a non-exclusive, non-transferable license under its copyright rights to (i) copy, use, modify, and compile the Cypress Source Code, Host Applications, and Firmware solely for the Purpose, and (ii) distribute Firmware in binary code form only, only when installed onto a Cypress hardware product, and (iii) distribute Host Applications, in binary code form only, only when installed on a device that includes a Cypress hardware product that the Host Application is intended to program, and (iv) to freely distribute any hardware setup information file (.inf file) created by the Software to allow a Microsoft Windows operating system to install the driver for a Cypress hardware product. Cypress retains ownership of the Cypress Source Code and any compiled version thereof. Subject to Cypress' ownership of the underlying Cypress Source Code and Software, you retain ownership of any modifications you make to the Cypress Source Code. You agree not to remove any Cypress copyright or other notices from the Cypress Source Code and any modifications thereof. You agree to keep the Cypress Source Code confidential. Any reproduction, modification, translation, compilation, or representation of the Cypress Source Code except as permitted in this paragraph is prohibited without the express written permission of Cypress.
Driver Files. The Software may include driver files in binary code form, to allow the use of a Cypress hardware product with a particular host operating system ("Driver"). Unless different license terms accompany a Driver, subject to the terms and conditions of this Agreement, Cypress grants you a non-exclusive, non-transferable license under its copyright rights to distribute the Driver, in binary code form only, only when installed on a device that includes the Cypress hardware product that the Driver is intended to enable.
Proprietary Rights. The Software, including all intellectual property rights therein, is and will remain the sole and exclusive property of Cypress or its suppliers. Except as otherwise expressly provided in this Agreement, you may not: (i) modify, adapt, or create derivative works based upon the Software; (ii) copy the Software; (iii) except and only to the extent explicitly permitted by applicable law despite this limitation, decompile, translate, reverse engineer, disassemble or otherwise reduce the Software to human-readable form; or (iv) use the Software or any sample code other than for the Purpose.
No Support. Cypress may, but is not required to, provide technical support for the Software.
Term and Termination. This Agreement is effective until terminated, and either party may terminate this Agreement at any time with or without cause. Your license rights under this Agreement will terminate immediately without notice from Cypress if you fail to comply with any provision of this Agreement. Upon termination, you must destroy all copies of Software in your possession or control. The following paragraphs shall survive any termination of this Agreement: "Free and Open Source Software," "Proprietary Rights," "Compliance With Law," "Disclaimer," "Limitation of Liability," and "General."
Compliance With Law. Each party agrees to comply with all applicable laws, rules and regulations in connection with its activities under this Agreement. Without limiting the foregoing, the Software may be subject to export control laws and regulations of the United States and other countries. You agree to comply strictly with all such laws and regulations and acknowledge that you have the responsibility to obtain licenses to export, re-export, or import the Software.
Disclaimer. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH REGARD TO THE SOFTWARE, INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress reserves the right to make changes to the Software without notice. Cypress does not assume any liability arising out of the application or use of Software or any product or circuit described in the Software. Cypress does not authorize its products for use as critical components in life-support systems where a malfunction or failure may reasonably be expected to result in significant injury to the user. The inclusion of Cypress' product in a life-support system or application implies that the manufacturer of such system or application assumes all risk of such use and in doing so indemnifies Cypress against all charges.
Limitation of Liability. IN NO EVENT WILL CYPRESS OR ITS SUPPLIERS, RESELLERS, OR DISTRIBUTORS BE LIABLE FOR ANY LOST REVENUE, PROFIT, OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR PUNITIVE DAMAGES HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF CYPRESS OR ITS SUPPLIERS, RESELLERS, OR DISTRIBUTORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN NO EVENT SHALL CYPRESS' OR ITS SUPPLIERS', RESELLERS', OR DISTRIBUTORS' TOTAL LIABILITY TO YOU, WHETHER IN CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, EXCEED THE PRICE PAID BY YOU FOR THE SOFTWARE. THE FOREGOING LIMITATIONS SHALL APPLY EVEN IF THE ABOVE-STATED WARRANTY FAILS OF ITS ESSENTIAL PURPOSE. BECAUSE SOME STATES OR JURISDICTIONS DO NOT ALLOW LIMITATION OR EXCLUSION OF CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
Restricted Rights. The Software under this Agreement is commercial computer software as that term is described in 48 C.F.R. 252.227-7014(a)(1). If acquired by or on behalf of a civilian agency, the U.S. Government acquires this commercial computer software and/or commercial computer software documentation subject to the terms of this Agreement as specified in 48 C.F.R. 12.212 (Computer Software) and 12.211 (Technical Data) of the Federal Acquisition Regulations ("FAR") and its successors. If acquired by or on behalf of any agency within the Department of Defense ("DOD"), the U.S. Government acquires this commercial computer software and/or commercial computer software documentation subject to the terms of this Agreement as specified in 48 C.F.R. 227.7202-3 of the DOD FAR Supplement ("DFAR") and its successors.
General. This Agreement will bind and inure to the benefit of each party's successors and assigns, provided that you may not assign or transfer this Agreement, in whole or in part, without Cypress' written consent. This Agreement shall be governed by and construed in accordance with the laws of the State of California, United States of America, as if performed wholly within the state and without giving effect to the principles of conflict of law. The parties consent to personal and exclusive jurisdiction of and venue in, the state and federal courts within Santa Clara County, California; provided however, that nothing in this Agreement will limit Cypress' right to bring legal action in any venue in order to protect or enforce its intellectual property rights. No failure of either party to exercise or enforce any of its rights under this Agreement will act as a waiver of such rights. If any portion hereof is found to be void or unenforceable, the remaining provisions of this Agreement shall remain in full force and effect. This Agreement is the complete and exclusive agreement between the parties with respect to the subject matter hereof, superseding and replacing any and all prior agreements, communications, and understandings (both written and oral) regarding such subject matter. Any notice to Cypress will be deemed effective when actually received and must be sent to Cypress Semiconductor Corporation, ATTN: General Counsel, 198 Champion Court, San Jose, CA 95134 USA.
====================================================================================================================================================================================================================================
Export control classification:
The Software contained in this package may be subject to export control:
ALNR: N
ECCN: N_
ECCN-US: EAR99
SI-class: N
MYCLASS: N
MXCLASS: N
HKCLASS: N
Software classified with “ALNR not equal to N“ are subject to European or German export authorization when being exported out of the EU.
Software classified with “ECCN not equal to N“ are subject to US re-export authorization.
Software classified with “SICLASS not equal to N" are subject to Singaporean Strategic Goods Control authorization when being exported out of Singapore.
Even without a label, or with label "ALNR:N" or "ECCN:N" authorization may be required due to the final whereabouts and purpose for which the goods are to be used.
Furthermore, the specified export control classifications are not binding, as export control regulations are subject to interpretations, changes and possible differences in national requirements. The ultimate determination of applicable export control classifications and related categorizations remains solely your responsibility.
Infineon makes no representation as to the accuracy or reliability of the information provided. You agree to use this information at your own risk and to hold Infineon harmless in the event of any damages or penalties incurred by any party based on your use or reliance on the information provided here.
+35 -28
View File
@@ -1,37 +1,44 @@
# T2G seat-controller firmware
# Self-contained T2G seat-controller firmware
`Seat_Controller_demo/` is the repository-owned IAR interface project for the
physical seat bench. It combines the verified heater, fan and four-motor demos
and is the only firmware accepted by the hardware executor.
`Seat_Controller_demo/` is the repository-owned IAR project used by the real
seat-bench executor. It contains the 2026-07-30 heater, ventilation, four-axis
motion, SMV8 position memory, Hall feedback and ADC current-diagnostic code.
The Infineon/T2G vendor library is intentionally not duplicated here. The IAR
project keeps its original relative `common/` and `tviibh8m/` references;
`tools/build_seat_firmware.ps1` stages the repository project beside junctions
to `COGNITIVE_AGENT_T2G_SDK_ROOT`, builds it, and copies only the resulting
`Seat_Controller_demo.hex` and `Seat_Controller_demo.out` back into the
repository.
The matching Cypress 7.9.0 build dependencies are vendored beside it:
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\build_seat_firmware.ps1 -Profile combined
```text
firmware/t2g/
├── common/
├── tviibh8m/
└── Seat_Controller_demo/
```
The build script never opens a debug probe or flashes a target. Physical
programming is performed only by an authorized `execution_mode=hardware`
request when `COGNITIVE_AGENT_SEAT_AUTO_FLASH_ENABLED=true` and the CAN
identity handshake does not match the repository firmware.
This preserves the IAR project's `../../common` and `../../tviibh8m` paths and
removes any dependency on the original Desktop SDK directory. The applicable
vendor terms are retained in `CYPRESS_SDK_LICENSE.txt`. An IAR installation is
still a toolchain prerequisite, just as the ZLG driver remains a CAN-adapter
prerequisite; neither is project source data.
Automatic programming uses `IAR/Seat_Controller_program.mac`, not the
interactive-debug `Seat_Controller_demo.mac`. The automatic-programming macro
forces motor, fan and heater outputs low before download, clears the retained
debug-session marker, and uses a bounded Download-and-Debug handoff with
`--leave_target_running`. `--download_only` is intentionally not used because
it leaves the CM0+ stopped after programming.
Build without flashing:
Firmware identity protocol:
```powershell
powershell -ExecutionPolicy Bypass -File .\tools\build_seat_firmware.ps1
```
- request: `0x304`, `53 45 41 54 01 00 00 00`
- expected response: `0x305`, `53 45 41 54 01 01 00 3F`
Set `COGNITIVE_AGENT_IAR_BUILD` or pass `-IarBuild` when `iarbuild.exe` is not
at a standard location. The script stages the project beside repository-local
vendor junctions, copies the resulting HEX/OUT back, removes build products,
and never opens a debug probe.
The response identifies protocol 1, firmware 1.1.0 and the combined capability
mask. It is a non-actuating readiness probe and is checked before any seat
control frame is sent.
The latest firmware uses periodic CAN `0x304`/`0x305` for motor and thermal
current diagnostics. The backend therefore verifies it passively by observing
`0x301` through `0x305`, `0x4FA`, all four motor IDs and MaxHall
`480/374/917/647`; it sends no identity request and no actuator frame during
verification.
When real hardware execution is enabled, automatic programming defaults to
enabled and uses `IAR/Seat_Controller_program.mac` to force outputs safe before
download. A missing protocol signature triggers programming, a boot wait and
passive re-verification before the original request continues. Set
`COGNITIVE_AGENT_SEAT_AUTO_FLASH_ENABLED=false` to opt out explicitly; dry-run
execution never programs hardware.
@@ -6,7 +6,6 @@
#include "cy_project.h"
#include "cy_device_headers.h"
#include "bsp_seat_move_can.h"
#include "motor_control.h"
#include "safety_manager.h"
#include "seat_move_application.h"
@@ -31,8 +30,6 @@ static void SeatMove_HoldSafeOff(void)
int main(void)
{
bool startupInhibited;
(void)BspSeatBackrestHeater_ForceOff();
(void)BspSeatFan_ForceOff();
SystemInit();
@@ -48,38 +45,28 @@ int main(void)
Cy_SysReset_ClearAllResetReasons();
SafetyManager_Init(g_seatMoveResetReason);
startupInhibited = SafetyManager_IsInhibited();
if(SafetyManager_IsInhibited())
{
SeatMove_HoldSafeOff();
}
__enable_irq();
g_seatMoveInitResult = Motor_Init();
if(g_seatMoveInitResult != MOTOR_API_SUCCESS)
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
}
/* A retained reset/debug fault must remain fail-safe, but it must not
* suppress CAN diagnostics. Motor_Init() establishes the timer and SPI
* state needed by status reporting, so re-apply the inhibit afterwards. */
if(startupInhibited)
{
Motor_LatchSafetyInhibitFromIsr(0u);
Motor_ForceSafeOff();
SeatMove_HoldSafeOff();
}
Motor_SetWatchdogServiceEnabled(true);
if(!SeatControllerThermal_Init())
{
SafetyManager_LatchFault(SAFETY_FAULT_DRIVER);
SeatMove_HoldSafeOff();
}
if(!SeatMoveApplication_Init())
{
/* When CAN itself could not initialize there is no diagnostic path to
* preserve. Other startup failures remain visible through identity
* and inhibited status frames while every actuator stays safe-off. */
if(g_bspSeatMoveCanInitStatus != (uint32_t)CY_CANFD_SUCCESS)
{
SeatMove_HoldSafeOff();
}
SeatMove_HoldSafeOff();
}
for(;;)
@@ -2,6 +2,7 @@
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_backrest_heater_adc.h"
#include "motor_control.h"
#include "seat_backrest_heater_config.h"
volatile seat_backrest_heater_level_t g_seatBackrestHeaterRequestedLevel =
@@ -19,6 +20,7 @@ volatile uint16_t g_seatBackrestHeaterSenseRaw = 0u;
volatile uint32_t g_seatBackrestHeaterEstimatedCurrentMa = 0u;
static bool s_faultLatched = false;
static uint32_t s_activeStartTimeMs = 0u;
static uint32_t s_senseSampleElapsedMs = 0u;
static uint32_t s_senseConsecutiveErrors = 0u;
@@ -48,6 +50,7 @@ bool SeatBackrestHeater_Init(void)
}
s_faultLatched = false;
s_activeStartTimeMs = 0u;
s_senseSampleElapsedMs = 0u;
s_senseConsecutiveErrors = 0u;
g_seatBackrestHeaterRequestedLevel =
@@ -92,7 +95,12 @@ void SeatBackrestHeater_Task1ms(void)
if(g_seatBackrestHeaterAppliedLevel !=
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
g_seatBackrestHeaterActiveTimeMs++;
/* The 1 ms application loop includes CAN service, ADC work and ISR
* latency, so counting loop iterations makes a nominal 600 s limit
* drift several seconds late. Use the motor driver's hardware
* monotonic timebase, which is already running before thermal init. */
g_seatBackrestHeaterActiveTimeMs =
(uint32_t)(Motor_GetTimeMs() - s_activeStartTimeMs);
if(g_seatBackrestHeaterActiveTimeMs >=
SEAT_BACKREST_HEATER_MAX_ACTIVE_TIME_MS)
{
@@ -160,6 +168,7 @@ bool SeatBackrestHeater_ClearFault(void)
g_seatBackrestHeaterState = SEAT_BACKREST_HEATER_STATE_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterActiveTimeMs = 0u;
s_activeStartTimeMs = 0u;
s_senseConsecutiveErrors = 0u;
return true;
}
@@ -183,6 +192,7 @@ static bool SeatBackrestHeater_ApplyLevel(
g_seatBackrestHeaterState = SEAT_BACKREST_HEATER_STATE_OFF;
g_seatBackrestHeaterDutyPercent = 0u;
g_seatBackrestHeaterActiveTimeMs = 0u;
s_activeStartTimeMs = 0u;
return true;
}
@@ -207,6 +217,7 @@ static bool SeatBackrestHeater_ApplyLevel(
if(g_seatBackrestHeaterAppliedLevel ==
SEAT_BACKREST_HEATER_LEVEL_OFF)
{
s_activeStartTimeMs = Motor_GetTimeMs();
g_seatBackrestHeaterActiveTimeMs = 0u;
}
g_seatBackrestHeaterAppliedLevel = level;
@@ -1,6 +1,7 @@
#include "seat_controller_thermal.h"
#include "bsp_seat_backrest_heater.h"
#include "bsp_seat_current_adc.h"
#include "bsp_seat_fan.h"
#include "safety_manager.h"
#include "seat_backrest_heater.h"
@@ -12,6 +13,7 @@
#define SEAT_CONTROLLER_THERMAL_MODE_ID (0x3CEu)
#define SEAT_CONTROLLER_THERMAL_SWITCH_ID (0x42Au)
#define SEAT_CONTROLLER_THERMAL_FEEDBACK_ID (0x4FAu)
#define SEAT_CONTROLLER_THERMAL_CURRENT_DIAGNOSTIC_ID (0x305u)
#define SEAT_CONTROLLER_THERMAL_FEEDBACK_MS (100u)
volatile uint8_t g_seatControllerFanFeedbackRaw = 1u;
@@ -20,6 +22,9 @@ volatile uint32_t g_seatControllerThermalRxCount = 0u;
volatile uint32_t g_seatControllerThermalInvalidCount = 0u;
volatile uint32_t g_seatControllerThermalFeedbackTxCount = 0u;
volatile uint32_t g_seatControllerThermalFeedbackTxFailureCount = 0u;
volatile uint32_t g_seatControllerThermalOutputFaultStage = 0u;
volatile uint16_t g_seatControllerFanCurrentSenseRaw = 0u;
volatile uint32_t g_seatControllerFanCurrentAdcErrorCount = 0u;
static bool s_fanRunning = false;
static bool s_heaterModeValid = false;
@@ -32,6 +37,8 @@ static uint8_t s_heaterLastVehicleGear =
static uint8_t s_heaterConfiguredMaxLevel = 3u;
static uint32_t s_fanStartupRemainingMs = 0u;
static uint32_t s_feedbackElapsedMs = 0u;
static uint32_t s_fanCurrentSampleElapsedMs = 0u;
static bool s_fanCurrentSampleValid = false;
static void SeatControllerThermal_HandleControl29e(
const bsp_seat_move_can_frame_t *frame);
@@ -48,9 +55,12 @@ static void SeatControllerThermal_StopHeater(uint8_t feedbackRaw);
static uint8_t SeatControllerThermal_GearToDuty(uint8_t physicalGear);
static void SeatControllerThermal_LatchOutputFault(void);
static void SeatControllerThermal_ServiceFeedback(void);
static void SeatControllerThermal_ServiceFanCurrent(void);
bool SeatControllerThermal_Init(void)
{
uint16_t fanCurrentRaw;
if(!SeatBackrestHeater_Init())
{
SeatControllerThermal_ForceSafeOff();
@@ -61,6 +71,16 @@ bool SeatControllerThermal_Init(void)
SeatControllerThermal_ForceSafeOff();
return false;
}
if(!BspSeatCurrentAdc_Init() ||
!BspSeatCurrentAdc_Sample(BSP_SEAT_CURRENT_ADC_FAN,
&fanCurrentRaw))
{
SeatControllerThermal_ForceSafeOff();
return false;
}
g_seatControllerFanCurrentSenseRaw = fanCurrentRaw;
s_fanCurrentSampleValid = true;
s_fanCurrentSampleElapsedMs = 0u;
SeatControllerThermal_StopFan(1u);
SeatControllerThermal_StopHeater(1u);
@@ -101,6 +121,7 @@ void SeatControllerThermal_HandleFrame(const bsp_seat_move_can_frame_t *frame)
void SeatControllerThermal_Service1ms(void)
{
SeatBackrestHeater_Task1ms();
SeatControllerThermal_ServiceFanCurrent();
if((g_seatBackrestHeaterState ==
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF) ||
(g_seatBackrestHeaterState ==
@@ -131,6 +152,7 @@ void SeatControllerThermal_Service1ms(void)
if((s_fanStartupRemainingMs == 0u) &&
!BspSeatFan_SetDutyPercent(s_fanTargetDutyPercent))
{
g_seatControllerThermalOutputFaultStage = 1u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -138,6 +160,28 @@ void SeatControllerThermal_Service1ms(void)
SeatControllerThermal_ServiceFeedback();
}
static void SeatControllerThermal_ServiceFanCurrent(void)
{
uint16_t raw;
s_fanCurrentSampleElapsedMs++;
if(s_fanCurrentSampleElapsedMs < SEAT_FAN_CURRENT_SAMPLE_MS)
{
return;
}
s_fanCurrentSampleElapsedMs = 0u;
if(BspSeatCurrentAdc_Sample(BSP_SEAT_CURRENT_ADC_FAN, &raw))
{
g_seatControllerFanCurrentSenseRaw = raw;
s_fanCurrentSampleValid = true;
}
else
{
g_seatControllerFanCurrentAdcErrorCount++;
s_fanCurrentSampleValid = false;
}
}
void SeatControllerThermal_ForceSafeOff(void)
{
SeatBackrestHeater_EmergencyOff();
@@ -159,7 +203,7 @@ static void SeatControllerThermal_HandleControl29e(
uint8_t heaterOn;
uint8_t heaterGear;
if(frame->dlc < 3u)
if(frame->dlc != SEAT_FAN_CAN_DLC)
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
@@ -183,6 +227,7 @@ static void SeatControllerThermal_HandleControl29e(
{
if(!SeatControllerThermal_RequestFan(fanGear))
{
g_seatControllerThermalOutputFaultStage = 2u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -202,6 +247,7 @@ static void SeatControllerThermal_HandleControl29e(
{
if(!SeatControllerThermal_RequestHeater(heaterGear))
{
g_seatControllerThermalOutputFaultStage = 3u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -219,7 +265,7 @@ static void SeatControllerThermal_HandleConfig2aa(
uint8_t fanConfig;
uint8_t heaterConfig;
if(frame->dlc < 3u)
if(frame->dlc != SEAT_FAN_CAN_DLC)
{
g_seatControllerThermalInvalidCount++;
return;
@@ -239,6 +285,7 @@ static void SeatControllerThermal_HandleConfig2aa(
if(s_fanRunning &&
!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
{
g_seatControllerThermalOutputFaultStage = 4u;
SeatControllerThermal_LatchOutputFault();
}
if(g_seatBackrestHeaterAppliedLevel !=
@@ -246,6 +293,7 @@ static void SeatControllerThermal_HandleConfig2aa(
{
if(!SeatControllerThermal_RequestHeater(s_heaterLastVehicleGear))
{
g_seatControllerThermalOutputFaultStage = 5u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -255,7 +303,7 @@ static void SeatControllerThermal_HandleMode3ce(
const bsp_seat_move_can_frame_t *frame)
{
uint8_t mode;
if(frame->dlc < 1u)
if(frame->dlc != SEAT_FAN_CAN_DLC)
{
s_heaterModeValid = false;
g_seatControllerThermalInvalidCount++;
@@ -276,7 +324,7 @@ static void SeatControllerThermal_HandleSwitch42a(
{
uint8_t fanSwitch;
uint8_t heaterSwitch;
if(frame->dlc < 1u)
if(frame->dlc != SEAT_FAN_CAN_DLC)
{
g_seatControllerThermalInvalidCount++;
SeatControllerThermal_StopFan(0u);
@@ -289,6 +337,7 @@ static void SeatControllerThermal_HandleSwitch42a(
{
if(!SeatControllerThermal_RequestFan(s_fanLastVehicleGear))
{
g_seatControllerThermalOutputFaultStage = 6u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -300,6 +349,7 @@ static void SeatControllerThermal_HandleSwitch42a(
{
if(!SeatControllerThermal_RequestHeater(s_heaterLastVehicleGear))
{
g_seatControllerThermalOutputFaultStage = 7u;
SeatControllerThermal_LatchOutputFault();
}
}
@@ -405,6 +455,13 @@ static void SeatControllerThermal_LatchOutputFault(void)
static void SeatControllerThermal_ServiceFeedback(void)
{
uint32_t data0;
uint32_t currentData0;
uint32_t currentData1;
uint8_t currentFlags;
uint8_t outputState;
uint8_t fanErrors;
uint8_t heaterErrors;
s_feedbackElapsedMs++;
if(!s_feedbackPending &&
(s_feedbackElapsedMs < SEAT_CONTROLLER_THERMAL_FEEDBACK_MS))
@@ -413,7 +470,31 @@ static void SeatControllerThermal_ServiceFeedback(void)
}
data0 = ((uint32_t)g_seatControllerFanFeedbackRaw & 0x07u) |
(((uint32_t)g_seatControllerHeaterFeedbackRaw & 0x07u) << 3u);
if(BspSeatMoveCan_Send(SEAT_CONTROLLER_THERMAL_FEEDBACK_ID, data0, 0u))
currentFlags = (uint8_t)
((s_fanCurrentSampleValid ? 0x01u : 0u) |
((g_seatBackrestHeaterState !=
SEAT_BACKREST_HEATER_STATE_FAULT_SAFE_OFF) ? 0x02u : 0u));
outputState = (uint8_t)
((s_fanRunning ? 0x01u : 0u) |
(((uint8_t)g_seatBackrestHeaterAppliedLevel & 0x03u) << 1u));
fanErrors = (g_seatControllerFanCurrentAdcErrorCount > UINT8_MAX) ?
UINT8_MAX :
(uint8_t)g_seatControllerFanCurrentAdcErrorCount;
heaterErrors = (g_seatBackrestHeaterAdcErrorCount > UINT8_MAX) ?
UINT8_MAX :
(uint8_t)g_seatBackrestHeaterAdcErrorCount;
currentData0 = (uint32_t)currentFlags |
((uint32_t)outputState << 8u) |
((uint32_t)g_seatControllerFanCurrentSenseRaw << 16u);
currentData1 = (uint32_t)g_seatBackrestHeaterSenseRaw |
((uint32_t)fanErrors << 16u) |
((uint32_t)heaterErrors << 24u);
if(BspSeatMoveCan_Send(SEAT_CONTROLLER_THERMAL_FEEDBACK_ID, data0, 0u) &&
BspSeatMoveCan_Send(
SEAT_CONTROLLER_THERMAL_CURRENT_DIAGNOSTIC_ID,
currentData0,
currentData1))
{
g_seatControllerThermalFeedbackTxCount++;
s_feedbackPending = false;
@@ -17,5 +17,9 @@ extern volatile uint32_t g_seatControllerThermalRxCount;
extern volatile uint32_t g_seatControllerThermalInvalidCount;
extern volatile uint32_t g_seatControllerThermalFeedbackTxCount;
extern volatile uint32_t g_seatControllerThermalFeedbackTxFailureCount;
/* Read-only HIL diagnostic; see seat_controller_thermal.c stage constants. */
extern volatile uint32_t g_seatControllerThermalOutputFaultStage;
extern volatile uint16_t g_seatControllerFanCurrentSenseRaw;
extern volatile uint32_t g_seatControllerFanCurrentAdcErrorCount;
#endif /* SEAT_CONTROLLER_THERMAL_H */
@@ -1,5 +1,7 @@
#include "seat_motor_manager.h"
#include "seat_move_config.h"
#include <stddef.h>
#include <string.h>
@@ -7,6 +9,7 @@
#define SEAT_MOTOR_SPEED_CAL_WARMUP_MS (500ul)
#define SEAT_MOTOR_SPEED_CAL_MEASURE_MS (1000ul)
#define SEAT_MOTOR_SPEED_CAL_DUTY_PERCENT (95u)
#define SEAT_MOTOR_DEFAULT_CAL_LEG_TIMEOUT_MS (70000ul)
typedef char seat_motor_manager_requires_four_motors[
(MOTOR_MODE_COUNT == 4) ? 1 : -1];
@@ -48,7 +51,7 @@ static uint32_t SeatMotor_MaxValueX100(motor_mode_t mode)
}
if(mode == MOTOR_MODE_1_ANGLE)
{
return MOTOR_MODE1_MAX_ANGLE_DEG_X100;
return MOTOR_MODE1_MAX_VERTICAL_MM_X100;
}
if(mode == MOTOR_MODE_2_ANGLE)
{
@@ -56,11 +59,32 @@ static uint32_t SeatMotor_MaxValueX100(motor_mode_t mode)
}
if(mode == MOTOR_MODE_3_LINEAR_TRACK)
{
return MOTOR_MODE3_MAX_POSITION_MM_X100;
return MOTOR_MODE3_MAX_ANGLE_DEG_X100;
}
return 0ul;
}
static uint8_t SeatMotor_DefaultDutyPercent(motor_mode_t mode)
{
if(mode == MOTOR_MODE_0_LINEAR)
{
return SEAT_MOVE_M0_DEFAULT_DUTY_PERCENT;
}
if(mode == MOTOR_MODE_1_ANGLE)
{
return SEAT_MOVE_M1_DEFAULT_DUTY_PERCENT;
}
if(mode == MOTOR_MODE_2_ANGLE)
{
return SEAT_MOVE_M2_DEFAULT_DUTY_PERCENT;
}
if(mode == MOTOR_MODE_3_LINEAR_TRACK)
{
return SEAT_MOVE_M3_DEFAULT_DUTY_PERCENT;
}
return 0u;
}
static bool SeatMotor_TimeReached(uint32_t now, uint32_t deadline)
{
return ((int32_t)(now - deadline) >= 0);
@@ -71,6 +95,26 @@ static uint32_t SeatMotor_AbsDelta(uint32_t lhs, uint32_t rhs)
return (lhs >= rhs) ? (lhs - rhs) : (rhs - lhs);
}
static bool SeatMotor_TargetWithinHallTolerance(
const seat_motor_task_t *task,
const motor_position_info_t *info)
{
uint32_t targetHallCount;
if((NULL == task) || (NULL == info) ||
(false == info->positionValid) || (0ul == info->maxValueX100) ||
(0ul == info->maxHallCount) ||
(task->targetValueX100 > info->maxValueX100))
{
return false;
}
targetHallCount = (uint32_t)
(((uint64_t)task->targetValueX100 * info->maxHallCount +
(info->maxValueX100 / 2ul)) / info->maxValueX100);
return SeatMotor_AbsDelta(targetHallCount, info->hallCount) <=
SEAT_MOVE_POSITION_TARGET_HALL_TOLERANCE;
}
static bool SeatMotor_DutyValid(uint8_t dutyPercent)
{
return (dutyPercent >= MOTOR_SPEED_MIN_DUTY_PERCENT) &&
@@ -496,6 +540,16 @@ void SeatMotorManager_Service(void)
(info.lastEvent == MOTOR_EVENT_FAR_ENDPOINT);
if((info.lastEvent == MOTOR_EVENT_STALL) || endpointEvent)
{
/* The low-level safety event has already stopped the hardware.
* At a discrete Hall endpoint, the mechanism may stop up to two
* pulses short of the mathematically rounded target. This is a
* successful position arrival, not a fault/retry condition. */
if(SeatMotor_TaskIsPosition(task) &&
SeatMotor_TargetWithinHallTolerance(task, &info))
{
SeatMotor_SetComplete(mode);
continue;
}
if(SeatMotor_TaskIsPosition(task) &&
(0u == task->retryCount))
{
@@ -619,6 +673,69 @@ bool SeatMotor_MoveRelativeDuty(motor_mode_t mode,
commandSequence);
}
bool SeatMotor_MoveAbsoluteNormalized(motor_mode_t mode,
uint32_t targetPercentX100,
uint32_t timeoutMs,
uint8_t dutyPercent,
uint32_t commandSequence)
{
motor_position_info_t info;
uint32_t targetValueX100;
if((targetPercentX100 > 10000ul) || (0ul == timeoutMs) ||
(MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid) || (0ul == info.maxValueX100))
{
return false;
}
targetValueX100 = (uint32_t)
((((uint64_t)targetPercentX100 * info.maxValueX100) + 5000ull) /
10000ull);
return SeatMotor_StartPositionTask(mode,
targetValueX100,
dutyPercent,
0ul,
timeoutMs,
commandSequence,
false,
false,
SEAT_MOTOR_TASK_POSITION);
}
bool SeatMotor_MoveRelativeNormalized(motor_mode_t mode,
int32_t deltaPercentX100,
uint32_t timeoutMs,
uint8_t dutyPercent,
uint32_t commandSequence)
{
motor_position_info_t info;
int32_t targetPercentX100;
if((MOTOR_API_SUCCESS != Motor_GetPositionInfo(mode, &info)) ||
(false == info.positionValid))
{
return false;
}
targetPercentX100 = (int32_t)info.percentX100 + deltaPercentX100;
if(targetPercentX100 < 0)
{
targetPercentX100 = 0;
}
else if(targetPercentX100 > 10000)
{
targetPercentX100 = 10000;
}
else
{
/* Target already lies in the normalized range. */
}
return SeatMotor_MoveAbsoluteNormalized(mode,
(uint32_t)targetPercentX100,
timeoutMs,
dutyPercent,
commandSequence);
}
bool SeatMotor_MoveTimedDuty(motor_mode_t mode,
motor_direction_t direction,
uint32_t durationMs,
@@ -793,19 +910,27 @@ bool SeatMotor_MoveAbsoluteInTime(motor_mode_t mode,
SEAT_MOTOR_TASK_POSITION);
}
bool SeatMotor_CalibrateRange(motor_mode_t mode, uint32_t commandSequence)
bool SeatMotor_CalibrateRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence)
{
motor_calibration_result_t result;
if(false == SeatMotor_ModeValid(mode))
if((false == SeatMotor_ModeValid(mode)) || (0ul == legTimeoutMs))
{
return false;
}
if((false == SeatMotor_PrepareNewTask(mode, commandSequence)) ||
(MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(
mode, MOTOR_SPEED_MIN_DUTY_PERCENT)) ||
(MOTOR_API_SUCCESS != Motor_Calibrate(mode,
mode, MOTOR_SPEED_MIN_DUTY_PERCENT)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return false;
}
tasks[(uint32_t)mode].state = SEAT_MOTOR_TASK_RANGE_CALIBRATION;
if((MOTOR_API_SUCCESS != Motor_Calibrate(mode,
MOTOR_CALIBRATE_FIND_BOTH,
legTimeoutMs,
&result)) ||
(false == result.nearEndpointFound) ||
(false == result.farEndpointFound))
@@ -817,6 +942,39 @@ bool SeatMotor_CalibrateRange(motor_mode_t mode, uint32_t commandSequence)
return true;
}
bool SeatMotor_HomeKnownRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence)
{
motor_calibration_result_t result;
uint8_t dutyPercent;
if((false == SeatMotor_ModeValid(mode)) || (0ul == legTimeoutMs))
{
return false;
}
dutyPercent = SeatMotor_DefaultDutyPercent(mode);
if((false == SeatMotor_PrepareNewTask(mode, commandSequence)) ||
(MOTOR_API_SUCCESS != Motor_SetFixedDutyPercent(
mode, dutyPercent)))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return false;
}
tasks[(uint32_t)mode].state = SEAT_MOTOR_TASK_RANGE_CALIBRATION;
if((MOTOR_API_SUCCESS != Motor_Calibrate(mode,
MOTOR_CALIBRATE_FIND_NEAR,
legTimeoutMs,
&result)) ||
(false == result.nearEndpointFound))
{
SeatMotor_SetFailed(mode, SEAT_MOTOR_RESULT_CALIBRATION_FAILED);
return false;
}
SeatMotor_SetComplete(mode);
return true;
}
bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
uint32_t commandSequence)
{
@@ -852,6 +1010,27 @@ bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
return true;
}
bool SeatMotorManager_ClearFault(motor_mode_t mode)
{
seat_motor_task_t *task;
uint32_t sequence;
if((false == SeatMotor_ModeValid(mode)) || Motor_IsMoving(mode) ||
Motor_IsDriverFaultLatched())
{
return false;
}
task = &tasks[(uint32_t)mode];
if(false != task->active)
{
return false;
}
sequence = task->commandSequence;
(void)memset(task, 0, sizeof(*task));
task->commandSequence = sequence;
return true;
}
bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount)
{
motor_calibration_result_t calibration;
@@ -860,6 +1039,7 @@ bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount)
if((MOTOR_API_SUCCESS != Motor_Calibrate(mode,
MOTOR_CALIBRATE_FIND_NEAR,
SEAT_MOTOR_DEFAULT_CAL_LEG_TIMEOUT_MS,
&calibration)) ||
(false == calibration.nearEndpointFound) ||
(MOTOR_API_SUCCESS != Motor_GetPersistentState(mode, &state)))
@@ -911,7 +1091,14 @@ bool SeatMotorManager_GetStatus(motor_mode_t mode,
status->hallCount = position.hallCount;
status->maxHallCount = position.maxHallCount;
status->maxSpeedPpsX100 = persistent.maxSpeedPulsesPerSecondX100;
status->hallSpeedPpsX100 = speed.actualPulsesPerSecondX100;
status->positionPercentX100 = position.percentX100;
status->positionMmX100 = position.positionMmX100;
status->horizontalMmX100 = position.horizontalMmX100;
status->verticalMmX100 = position.verticalMmX100;
status->angleDegX100 = position.angleDegX100;
status->dutyPercent = speed.currentDutyPercent;
status->retryCount = task->retryCount;
status->active = task->active;
status->retried = (task->retryCount != 0u);
status->positionValid = position.positionValid;
@@ -13,6 +13,7 @@ typedef enum
SEAT_MOTOR_TASK_POSITION,
SEAT_MOTOR_TASK_TIMED,
SEAT_MOTOR_TASK_RETRY_DELAY,
SEAT_MOTOR_TASK_RANGE_CALIBRATION,
SEAT_MOTOR_TASK_SPEED_CAL_WARMUP,
SEAT_MOTOR_TASK_SPEED_CAL_MEASURE,
SEAT_MOTOR_TASK_SPEED_CAL_SETTLE,
@@ -45,7 +46,14 @@ typedef struct
uint32_t hallCount;
uint32_t maxHallCount;
uint32_t maxSpeedPpsX100;
uint32_t hallSpeedPpsX100;
uint32_t positionPercentX100;
uint32_t positionMmX100;
uint32_t horizontalMmX100;
uint32_t verticalMmX100;
uint32_t angleDegX100;
uint8_t dutyPercent;
uint8_t retryCount;
bool active;
bool retried;
bool positionValid;
@@ -64,6 +72,16 @@ bool SeatMotor_MoveAbsoluteDuty(motor_mode_t mode,
uint32_t targetValueX100,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveAbsoluteNormalized(motor_mode_t mode,
uint32_t targetPercentX100,
uint32_t timeoutMs,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveRelativeNormalized(motor_mode_t mode,
int32_t deltaPercentX100,
uint32_t timeoutMs,
uint8_t dutyPercent,
uint32_t commandSequence);
bool SeatMotor_MoveRelativeDuty(motor_mode_t mode,
int32_t deltaValueX100,
uint8_t dutyPercent,
@@ -94,9 +112,17 @@ bool SeatMotor_MoveAbsoluteInTime(motor_mode_t mode,
uint32_t commandSequence);
/* Range calibration is interruptible by Motor_RequestAbortAllFromIsr(). */
bool SeatMotor_CalibrateRange(motor_mode_t mode, uint32_t commandSequence);
bool SeatMotor_CalibrateRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence);
/* Re-establish zero at the known near endpoint while retaining the confirmed
* default MaxHall. This performs one near-seek only, not a full-range test. */
bool SeatMotor_HomeKnownRange(motor_mode_t mode,
uint32_t legTimeoutMs,
uint32_t commandSequence);
bool SeatMotor_StartMaximumSpeedCalibration(motor_mode_t mode,
uint32_t commandSequence);
bool SeatMotorManager_ClearFault(motor_mode_t mode);
/* Optional endpoint-based position recovery primitive. */
bool SeatMotor_RecoverPosition(motor_mode_t mode, uint32_t savedHallCount);
File diff suppressed because it is too large Load Diff
@@ -10,22 +10,35 @@
#define SEAT_MOVE_STORE_SLOT_WORDS (CY_WORK_SES_SIZE_IN_WORD)
#define SEAT_MOVE_STORE_SLOT_COUNT (2u)
#define SEAT_MOVE_STORE_LEGACY_V5_MAGIC (0x534D5635u) /* "SMV5" */
#define SEAT_MOVE_STORE_LEGACY_V5_VERSION (5u)
#define SEAT_MOVE_STORE_LEGACY_V6_MAGIC (0x534D5636u) /* "SMV6" */
#define SEAT_MOVE_STORE_LEGACY_V6_VERSION (6u)
#define SEAT_MOVE_STORE_LEGACY_V7_MAGIC (0x534D5637u) /* "SMV7" */
#define SEAT_MOVE_STORE_LEGACY_V7_VERSION (7u)
#define SEAT_MOVE_STORE_HEADER_WORDS (4u)
#define SEAT_MOVE_STORE_MOTOR_WORDS (5u)
#define SEAT_MOVE_STORE_MOTOR_WORDS (6u)
#define SEAT_MOVE_STORE_MOTOR_HALL (0u)
#define SEAT_MOVE_STORE_MOTOR_MAX_HALL (1u)
#define SEAT_MOVE_STORE_MOTOR_MAX_SPEED (2u)
#define SEAT_MOVE_STORE_MOTOR_RAW_EDGES (3u)
#define SEAT_MOVE_STORE_MOTOR_ABS_VALUE (4u)
#define SEAT_MOVE_STORE_MOTOR_VALID (5u)
#define SEAT_MOVE_STORE_CRC_WORD_INDEX \
(SEAT_MOVE_STORE_HEADER_WORDS + \
(SEAT_MOVE_MOTOR_COUNT * SEAT_MOVE_STORE_MOTOR_WORDS))
#define SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX \
(SEAT_MOVE_STORE_HEADER_WORDS + \
(SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT * SEAT_MOVE_STORE_MOTOR_WORDS))
typedef char seat_move_store_record_must_fit[
(SEAT_MOVE_STORE_CRC_WORD_INDEX < SEAT_MOVE_STORE_SLOT_WORDS) ? 1 : -1];
typedef char seat_move_store_slots_must_fit_work_flash[
((SEAT_MOVE_STORE_ADDRESS +
(SEAT_MOVE_STORE_SLOT_COUNT * CY_WORK_SES_SIZE_IN_BYTE)) <=
CY_WFLASH_SM_SBM_END) ? 1 : -1];
typedef enum
{
SEAT_MOVE_STORE_FORMAT_INVALID = 0,
SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR,
SEAT_MOVE_STORE_FORMAT_LEGACY_INCOMPATIBLE,
SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR
} seat_move_store_format_t;
@@ -33,13 +46,21 @@ static uint32_t s_record[SEAT_MOVE_STORE_SLOT_WORDS];
static uint32_t s_activeSlot = 0u;
static bool s_valid = false;
static bool s_clean = false;
static seat_move_store_format_t s_format = SEAT_MOVE_STORE_FORMAT_INVALID;
static bool s_corrupt = false;
static uint32_t SeatMoveStore_Crc32(const uint32_t *words, uint32_t wordCount);
static uint32_t SeatMoveStore_SlotAddress(uint32_t slot);
static uint32_t SeatMoveStore_MaxValueX100(motor_mode_t mode);
static uint32_t SeatMoveStore_AbsoluteValueX100(motor_mode_t mode,
uint32_t hallCount,
uint32_t maxHallCount);
static seat_move_store_format_t SeatMoveStore_ReadSlot(
uint32_t slot, uint32_t *destination);
static bool SeatMoveStore_SequenceNewer(uint32_t candidate, uint32_t reference);
static bool SeatMoveStore_IsBlank(const uint32_t *record);
static bool SeatMoveStore_PayloadMatches(const uint32_t *candidate);
static bool SeatMoveStore_WriteRecord(const uint32_t *record,
uint32_t targetSlot);
void SeatMoveStore_Init(void)
{
@@ -54,14 +75,18 @@ void SeatMoveStore_Init(void)
Cy_FlashInit(false);
slot0Format = SeatMoveStore_ReadSlot(0u, slot0);
slot1Format = SeatMoveStore_ReadSlot(1u, slot1);
slot0Valid = (slot0Format != SEAT_MOVE_STORE_FORMAT_INVALID);
slot1Valid = (slot1Format != SEAT_MOVE_STORE_FORMAT_INVALID);
slot0Valid = (slot0Format == SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR);
slot1Valid = (slot1Format == SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR);
s_valid = slot0Valid || slot1Valid;
s_corrupt = !s_valid &&
(((slot0Format == SEAT_MOVE_STORE_FORMAT_INVALID) &&
!SeatMoveStore_IsBlank(slot0)) ||
((slot1Format == SEAT_MOVE_STORE_FORMAT_INVALID) &&
!SeatMoveStore_IsBlank(slot1)));
if(!s_valid)
{
s_clean = false;
s_activeSlot = 0u;
s_format = SEAT_MOVE_STORE_FORMAT_INVALID;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = 0xFFFFFFFFu;
@@ -73,7 +98,6 @@ void SeatMoveStore_Init(void)
(!slot0Valid || SeatMoveStore_SequenceNewer(slot1[2], slot0[2])))
{
s_activeSlot = 1u;
s_format = slot1Format;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = slot1[index];
@@ -82,7 +106,6 @@ void SeatMoveStore_Init(void)
else
{
s_activeSlot = 0u;
s_format = slot0Format;
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
s_record[index] = slot0[index];
@@ -93,33 +116,51 @@ void SeatMoveStore_Init(void)
bool SeatMoveStore_Restore(void)
{
motor_persistent_state_t states[SEAT_MOVE_MOTOR_COUNT];
uint32_t motorIndex;
uint32_t restoreMotorCount;
if(!s_valid || !s_clean)
{
return false;
}
/* Preserve the already calibrated motors 0..2 when upgrading an SMV2
* three-motor record. Motor3 remains at its estimated defaults with
* positionValid=false until it is physically connected and calibrated. */
restoreMotorCount =
(s_format == SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR) ?
SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT : SEAT_MOVE_MOTOR_COUNT;
for(motorIndex = 0u; motorIndex < restoreMotorCount; motorIndex++)
for(motorIndex = 0u; motorIndex < SEAT_MOVE_MOTOR_COUNT; motorIndex++)
{
uint32_t base = SEAT_MOVE_STORE_HEADER_WORDS +
(motorIndex * SEAT_MOVE_STORE_MOTOR_WORDS);
motor_persistent_state_t state;
uint32_t savedAbsoluteValue;
uint32_t expectedAbsoluteValue;
state.positionHallCount = s_record[base];
state.maxHallCount = s_record[base + 1u];
state.maxSpeedPulsesPerSecondX100 = s_record[base + 2u];
state.rawRisingEdges = s_record[base + 3u];
state.positionValid = (s_record[base + 4u] == 1u);
states[motorIndex].positionHallCount =
s_record[base + SEAT_MOVE_STORE_MOTOR_HALL];
states[motorIndex].maxHallCount =
s_record[base + SEAT_MOVE_STORE_MOTOR_MAX_HALL];
states[motorIndex].maxSpeedPulsesPerSecondX100 =
s_record[base + SEAT_MOVE_STORE_MOTOR_MAX_SPEED];
states[motorIndex].rawRisingEdges =
s_record[base + SEAT_MOVE_STORE_MOTOR_RAW_EDGES];
savedAbsoluteValue =
s_record[base + SEAT_MOVE_STORE_MOTOR_ABS_VALUE];
states[motorIndex].positionValid =
(s_record[base + SEAT_MOVE_STORE_MOTOR_VALID] == 1u);
expectedAbsoluteValue = SeatMoveStore_AbsoluteValueX100(
(motor_mode_t)motorIndex,
states[motorIndex].positionHallCount,
states[motorIndex].maxHallCount);
if((s_record[base + SEAT_MOVE_STORE_MOTOR_VALID] > 1u) ||
(savedAbsoluteValue != expectedAbsoluteValue) ||
(MOTOR_API_SUCCESS != Motor_ValidatePersistentState(
(motor_mode_t)motorIndex,
&states[motorIndex])))
{
return false;
}
}
for(motorIndex = 0u; motorIndex < SEAT_MOVE_MOTOR_COUNT; motorIndex++)
{
if(MOTOR_API_SUCCESS != Motor_ApplyPersistentState(
(motor_mode_t)motorIndex, &state))
(motor_mode_t)motorIndex,
&states[motorIndex]))
{
return false;
}
@@ -127,15 +168,55 @@ bool SeatMoveStore_Restore(void)
return true;
}
void SeatMoveStore_RejectRestore(void)
{
/* A structurally valid record can still fail semantic validation (for
* example, an inconsistent Hall/physical value). Keep its sequence so
* the next clean save supersedes both flash slots, but never expose the
* record as restorable. This condition is recoverable by finding the
* known near endpoint; an actual flash erase/write failure remains a
* latched SAFETY_FAULT_CONFIG_STORE in the caller. */
s_clean = false;
s_corrupt = true;
}
bool SeatMoveStore_MarkMovementStarted(void)
{
uint32_t dirtyRecord[SEAT_MOVE_STORE_SLOT_WORDS];
uint32_t wordIndex;
uint32_t targetSlot;
/* No restorable state exists on a first boot or after an interrupted
* motion. There is therefore no stale clean record to invalidate. */
if(!s_valid || !s_clean)
{
s_clean = false;
return true;
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
dirtyRecord[wordIndex] = s_record[wordIndex];
}
dirtyRecord[2] = s_record[2] + 1u;
dirtyRecord[3] = 0u;
dirtyRecord[SEAT_MOVE_STORE_CRC_WORD_INDEX] =
SeatMoveStore_Crc32(dirtyRecord, SEAT_MOVE_STORE_CRC_WORD_INDEX);
targetSlot = s_activeSlot ^ 1u;
/* The dirty record is committed before PWM is allowed to start. If
* power fails while this blocking write is in progress, the old clean
* record still describes the unchanged pre-motion position. Once this
* call returns, the newer dirty sequence suppresses that old position. */
return SeatMoveStore_WriteRecord(dirtyRecord, targetSlot);
}
bool SeatMoveStore_Save(void)
{
uint32_t newRecord[SEAT_MOVE_STORE_SLOT_WORDS];
uint32_t motorIndex;
uint32_t wordIndex;
uint32_t targetSlot = s_valid ? (s_activeSlot ^ 1u) : 0u;
uint32_t address = SeatMoveStore_SlotAddress(targetSlot);
volatile const uint32_t *flashWords =
(volatile const uint32_t *)(uintptr_t)address;
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
@@ -151,46 +232,36 @@ bool SeatMoveStore_Save(void)
uint32_t base = SEAT_MOVE_STORE_HEADER_WORDS +
(motorIndex * SEAT_MOVE_STORE_MOTOR_WORDS);
motor_persistent_state_t state;
motor_position_info_t position;
if(MOTOR_API_SUCCESS != Motor_GetPersistentState(
(motor_mode_t)motorIndex, &state))
(motor_mode_t)motorIndex, &state) ||
MOTOR_API_SUCCESS != Motor_GetPositionInfo(
(motor_mode_t)motorIndex, &position))
{
return false;
}
newRecord[base] = state.positionHallCount;
newRecord[base + 1u] = state.maxHallCount;
newRecord[base + 2u] = state.maxSpeedPulsesPerSecondX100;
newRecord[base + 3u] = state.rawRisingEdges;
newRecord[base + 4u] = state.positionValid ? 1u : 0u;
newRecord[base + SEAT_MOVE_STORE_MOTOR_HALL] =
state.positionHallCount;
newRecord[base + SEAT_MOVE_STORE_MOTOR_MAX_HALL] =
state.maxHallCount;
newRecord[base + SEAT_MOVE_STORE_MOTOR_MAX_SPEED] =
state.maxSpeedPulsesPerSecondX100;
newRecord[base + SEAT_MOVE_STORE_MOTOR_RAW_EDGES] =
state.rawRisingEdges;
newRecord[base + SEAT_MOVE_STORE_MOTOR_ABS_VALUE] =
position.valueX100;
newRecord[base + SEAT_MOVE_STORE_MOTOR_VALID] =
state.positionValid ? 1u : 0u;
}
newRecord[SEAT_MOVE_STORE_CRC_WORD_INDEX] =
SeatMoveStore_Crc32(newRecord, SEAT_MOVE_STORE_CRC_WORD_INDEX);
Cy_FlashSectorErase(address, CY_FLASH_DRIVER_BLOCKING);
if(!Cy_WorkFlashBlankCheck(address, CY_FLASH_DRIVER_BLOCKING))
if(s_valid && s_clean && SeatMoveStore_PayloadMatches(newRecord))
{
return false;
return true;
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
Cy_FlashWriteWork(address + (wordIndex * sizeof(uint32_t)),
&newRecord[wordIndex],
CY_FLASH_DRIVER_BLOCKING);
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
if(flashWords[wordIndex] != newRecord[wordIndex])
{
return false;
}
s_record[wordIndex] = newRecord[wordIndex];
}
s_activeSlot = targetSlot;
s_valid = true;
s_clean = true;
s_format = SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR;
return true;
return SeatMoveStore_WriteRecord(newRecord, targetSlot);
}
bool SeatMoveStore_IsValid(void)
@@ -203,6 +274,11 @@ bool SeatMoveStore_IsClean(void)
return s_clean;
}
bool SeatMoveStore_HasCorruptRecord(void)
{
return s_corrupt;
}
uint32_t SeatMoveStore_GetSequence(void)
{
return s_valid ? s_record[2] : 0u;
@@ -239,6 +315,43 @@ static uint32_t SeatMoveStore_SlotAddress(uint32_t slot)
return SEAT_MOVE_STORE_ADDRESS + (slot * CY_WORK_SES_SIZE_IN_BYTE);
}
static uint32_t SeatMoveStore_MaxValueX100(motor_mode_t mode)
{
switch(mode)
{
case MOTOR_MODE_0_LINEAR:
return MOTOR_MODE0_MAX_POSITION_MM_X100;
case MOTOR_MODE_1_ANGLE:
return MOTOR_MODE1_MAX_VERTICAL_MM_X100;
case MOTOR_MODE_2_ANGLE:
return MOTOR_MODE2_MAX_ANGLE_DEG_X100;
case MOTOR_MODE_3_LINEAR_TRACK:
return MOTOR_MODE3_MAX_ANGLE_DEG_X100;
default:
return 0u;
}
}
static uint32_t SeatMoveStore_AbsoluteValueX100(motor_mode_t mode,
uint32_t hallCount,
uint32_t maxHallCount)
{
uint32_t maxValue = SeatMoveStore_MaxValueX100(mode);
uint32_t boundedHall = hallCount;
if((0u == maxValue) || (0u == maxHallCount))
{
return UINT32_MAX;
}
if(boundedHall > maxHallCount)
{
boundedHall = maxHallCount;
}
return (uint32_t)((((uint64_t)boundedHall * maxValue) +
(maxHallCount / 2u)) /
maxHallCount);
}
static seat_move_store_format_t SeatMoveStore_ReadSlot(
uint32_t slot, uint32_t *destination)
{
@@ -266,13 +379,21 @@ static seat_move_store_format_t SeatMoveStore_ReadSlot(
{
return SEAT_MOVE_STORE_FORMAT_CURRENT_FOUR_MOTOR;
}
if((destination[0] == SEAT_MOVE_STORE_LEGACY_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_LEGACY_VERSION) &&
(destination[SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX] ==
if((((destination[0] == SEAT_MOVE_STORE_LEGACY_V5_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_LEGACY_V5_VERSION)) ||
((destination[0] == SEAT_MOVE_STORE_LEGACY_V6_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_LEGACY_V6_VERSION)) ||
((destination[0] == SEAT_MOVE_STORE_LEGACY_V7_MAGIC) &&
(destination[1] == SEAT_MOVE_STORE_LEGACY_V7_VERSION))) &&
(destination[SEAT_MOVE_STORE_CRC_WORD_INDEX] ==
SeatMoveStore_Crc32(destination,
SEAT_MOVE_STORE_LEGACY_CRC_WORD_INDEX)))
SEAT_MOVE_STORE_CRC_WORD_INDEX)))
{
return SEAT_MOVE_STORE_FORMAT_LEGACY_THREE_MOTOR;
/* SMV5/SMV6 used obsolete Hall ranges. SMV7 stored the final
* numeric ranges against a non-identity channel map. Treat all as
* obsolete rather than corrupt, but never restore their positions.
* A new four-axis calibration will commit an SMV8 record. */
return SEAT_MOVE_STORE_FORMAT_LEGACY_INCOMPATIBLE;
}
return SEAT_MOVE_STORE_FORMAT_INVALID;
}
@@ -281,3 +402,82 @@ static bool SeatMoveStore_SequenceNewer(uint32_t candidate, uint32_t reference)
{
return ((int32_t)(candidate - reference) > 0);
}
static bool SeatMoveStore_IsBlank(const uint32_t *record)
{
uint32_t index;
if(record == NULL)
{
return false;
}
for(index = 0u; index < SEAT_MOVE_STORE_SLOT_WORDS; index++)
{
if(record[index] != 0xFFFFFFFFu)
{
return false;
}
}
return true;
}
static bool SeatMoveStore_PayloadMatches(const uint32_t *candidate)
{
uint32_t wordIndex;
if(candidate == NULL)
{
return false;
}
for(wordIndex = SEAT_MOVE_STORE_HEADER_WORDS;
wordIndex < SEAT_MOVE_STORE_CRC_WORD_INDEX;
wordIndex++)
{
if(candidate[wordIndex] != s_record[wordIndex])
{
return false;
}
}
return true;
}
static bool SeatMoveStore_WriteRecord(const uint32_t *record,
uint32_t targetSlot)
{
uint32_t address;
uint32_t wordIndex;
volatile const uint32_t *flashWords;
if((record == NULL) || (targetSlot >= SEAT_MOVE_STORE_SLOT_COUNT))
{
return false;
}
address = SeatMoveStore_SlotAddress(targetSlot);
flashWords = (volatile const uint32_t *)(uintptr_t)address;
Cy_FlashSectorErase(address, CY_FLASH_DRIVER_BLOCKING);
if(!Cy_WorkFlashBlankCheck(address, CY_FLASH_DRIVER_BLOCKING))
{
return false;
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
Cy_FlashWriteWork(address + (wordIndex * sizeof(uint32_t)),
&record[wordIndex],
CY_FLASH_DRIVER_BLOCKING);
}
for(wordIndex = 0u; wordIndex < SEAT_MOVE_STORE_SLOT_WORDS; wordIndex++)
{
if(flashWords[wordIndex] != record[wordIndex])
{
return false;
}
s_record[wordIndex] = record[wordIndex];
}
s_activeSlot = targetSlot;
s_valid = true;
s_clean = (record[3] == 1u);
s_corrupt = false;
return true;
}
@@ -6,9 +6,14 @@
void SeatMoveStore_Init(void);
bool SeatMoveStore_Restore(void);
/* Keep the record sequence for replacement, but make a failed restore
* non-restorable so known-range homing can rebuild a clean position. */
void SeatMoveStore_RejectRestore(void);
bool SeatMoveStore_MarkMovementStarted(void);
bool SeatMoveStore_Save(void);
bool SeatMoveStore_IsValid(void);
bool SeatMoveStore_IsClean(void);
bool SeatMoveStore_HasCorruptRecord(void);
uint32_t SeatMoveStore_GetSequence(void);
#endif /* SEAT_MOVE_STORE_H */
@@ -17,6 +17,8 @@ static void BspSeatBackrestHeaterCan_RxCallback(
static uint32_t s_heaterDutyPercent = 0u;
volatile uint32_t g_bspSeatBackrestHeaterStartFailStage = 0u;
volatile uint32_t g_bspSeatBackrestHeaterCanInitStatus =
(uint32_t)CY_CANFD_SUCCESS;
volatile uint32_t g_bspSeatBackrestHeaterCanRxCount = 0u;
@@ -227,8 +229,10 @@ bool BspSeatBackrestHeater_InitPwm(void)
bool BspSeatBackrestHeater_Start(uint32_t dutyPercent)
{
g_bspSeatBackrestHeaterStartFailStage = 0u;
if(!BspSeatBackrestHeater_SetDutyPercent(dutyPercent))
{
g_bspSeatBackrestHeaterStartFailStage = 1u;
return false;
}
@@ -245,6 +249,7 @@ bool BspSeatBackrestHeater_Start(uint32_t dutyPercent)
if((Cy_Tcpwm_Pwm_GetStatus(SEAT_BACKREST_HEATER_PWM_TCPWM) &
TCPWM_GRP_CNT_STATUS_RUNNING_Msk) == 0u)
{
g_bspSeatBackrestHeaterStartFailStage = 2u;
BspSeatBackrestHeater_FailSafeOff();
return false;
}
@@ -263,6 +268,7 @@ bool BspSeatBackrestHeater_Start(uint32_t dutyPercent)
SEAT_BACKREST_HEATER_PWM_PIN) !=
SEAT_BACKREST_HEATER_PWM_HSIOM)
{
g_bspSeatBackrestHeaterStartFailStage = 3u;
BspSeatBackrestHeater_FailSafeOff();
return false;
}
@@ -30,6 +30,13 @@ void BspSeatBackrestHeater_FailSafeOff(void);
bool BspSeatBackrestHeater_IsRunning(void);
uint32_t BspSeatBackrestHeater_GetDutyPercent(void);
/*
* Start-path diagnostic for bench/HIL fault isolation.
* 0 = no start failure, 1 = duty/compare write, 2 = counter did not run,
* 3 = P16.7 HSIOM switch/readback.
*/
extern volatile uint32_t g_bspSeatBackrestHeaterStartFailStage;
/* Initialize the proven CAN4/TJA1044 board interface at 500 kbit/s. */
bool BspSeatBackrestHeaterCan_Init(void);
@@ -1,17 +1,8 @@
#include "bsp_seat_backrest_heater_adc.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "bsp_seat_current_adc.h"
#include "seat_backrest_heater_config.h"
#define HEATER_ADC_OPERATION_FREQ_MAX_HZ (13340000u)
#define HEATER_ADC_SAMPLE_TIME_MIN_NS (600ull)
#define HEATER_ADC_POLL_TIMEOUT (100000u)
#define HEATER_ADC_DIV_ROUND_UP(a, b) (((a) + (b) - 1u) / (b))
static bool BspSeatBackrestHeaterAdc_ConfigClock(uint32_t *actualClockHz);
static bool BspSeatBackrestHeaterAdc_WaitDone(void);
static void BspSeatBackrestHeaterAdc_ClearStatus(void);
#include "seat_current_adc_config.h"
static bool s_adcInitialized = false;
static uint16_t s_heaterSenseRaw = 0u;
@@ -19,94 +10,10 @@ static uint16_t s_heaterSenseRaw = 0u;
bool BspSeatBackrestHeaterAdc_Init(void)
{
#if (SEAT_BACKREST_HEATER_SENSE_ENABLE == 1u)
uint32_t actualClockHz;
uint32_t samplingCycles;
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_ANALOG,
.hsiom = SEAT_BACKREST_HEATER_SENSE_GPIO_HSIOM,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_adc_config_t adcConfig =
{
.preconditionTime = 0u,
.powerupTime = 0u,
.enableIdlePowerDown = false,
.msbStretchMode = CY_ADC_MSB_STRETCH_MODE_1CYCLE,
.enableHalfLsbConv = false,
.sarMuxEnable = true,
.adcEnable = true,
.sarIpEnable = true,
};
cy_stc_adc_channel_config_t channelConfig =
{
.triggerSelection = CY_ADC_TRIGGER_OFF,
.channelPriority = 0u,
.preenptionType = CY_ADC_PREEMPTION_FINISH_RESUME,
.isGroupEnd = true,
.doneLevel = CY_ADC_DONE_LEVEL_PULSE,
.pinAddress = SEAT_BACKREST_HEATER_SENSE_PIN_ADDRESS,
.portAddress = SEAT_BACKREST_HEATER_SENSE_PORT_ADDRESS,
.extMuxSelect = 0u,
.extMuxEnable = true,
.preconditionMode = CY_ADC_PRECONDITION_MODE_OFF,
.overlapDiagMode = CY_ADC_OVERLAP_DIAG_MODE_OFF,
.sampleTime = 0u,
.calibrationValueSelect = CY_ADC_CALIBRATION_VALUE_REGULAR,
.postProcessingMode = CY_ADC_POST_PROCESSING_MODE_NONE,
.resultAlignment = CY_ADC_RESULT_ALIGNMENT_RIGHT,
.signExtention = CY_ADC_SIGN_EXTENTION_UNSIGNED,
.averageCount = 0u,
.rightShift = 0u,
.rangeDetectionMode = CY_ADC_RANGE_DETECTION_MODE_INSIDE_RANGE,
.rangeDetectionLoThreshold = 0u,
.rangeDetectionHiThreshold = SEAT_BACKREST_HEATER_ADC_FULL_SCALE,
.mask.grpDone = true,
.mask.grpCancelled = false,
.mask.grpOverflow = false,
.mask.chRange = false,
.mask.chPulse = false,
.mask.chOverflow = false,
};
if(Cy_GPIO_Pin_Init(SEAT_BACKREST_HEATER_SENSE_PORT,
SEAT_BACKREST_HEATER_SENSE_PIN,
&pinConfig) != CY_GPIO_SUCCESS)
if(!BspSeatCurrentAdc_Init())
{
return false;
}
if(!BspSeatBackrestHeaterAdc_ConfigClock(&actualClockHz))
{
return false;
}
samplingCycles = (uint32_t)HEATER_ADC_DIV_ROUND_UP(
(HEATER_ADC_SAMPLE_TIME_MIN_NS * (uint64_t)actualClockHz),
1000000000ull);
if(samplingCycles == 0u)
{
samplingCycles = 1u;
}
channelConfig.sampleTime = (uint16_t)samplingCycles;
Cy_Adc_DeInit(SEAT_BACKREST_HEATER_SENSE_ADC);
if(Cy_Adc_Init(SEAT_BACKREST_HEATER_SENSE_ADC,
&adcConfig) != CY_ADC_SUCCESS)
{
return false;
}
if(Cy_Adc_Channel_Init(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL,
&channelConfig) != CY_ADC_SUCCESS)
{
return false;
}
Cy_Adc_Channel_Enable(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL);
s_adcInitialized = true;
return BspSeatBackrestHeaterAdc_Sample();
#else
@@ -118,35 +25,14 @@ bool BspSeatBackrestHeaterAdc_Init(void)
bool BspSeatBackrestHeaterAdc_Sample(void)
{
#if (SEAT_BACKREST_HEATER_SENSE_ENABLE == 1u)
uint16_t raw = 0u;
cy_stc_adc_ch_status_t resultStatus = { false };
uint16_t raw;
if(!s_adcInitialized)
if(!s_adcInitialized ||
!BspSeatCurrentAdc_Sample(BSP_SEAT_CURRENT_ADC_HEATER, &raw))
{
return false;
}
BspSeatBackrestHeaterAdc_ClearStatus();
Cy_Adc_Channel_SoftwareTrigger(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL);
if(!BspSeatBackrestHeaterAdc_WaitDone())
{
return false;
}
if(Cy_Adc_Channel_GetResult(SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL,
&raw,
&resultStatus) != CY_ADC_SUCCESS)
{
return false;
}
if(!resultStatus.valid)
{
return false;
}
s_heaterSenseRaw =
(uint16_t)(raw & SEAT_BACKREST_HEATER_ADC_FULL_SCALE);
BspSeatBackrestHeaterAdc_ClearStatus();
s_heaterSenseRaw = raw;
return true;
#else
return true;
@@ -162,98 +48,11 @@ uint32_t BspSeatBackrestHeaterAdc_GetEstimatedCurrentMilliamp(void)
{
uint64_t numerator =
(uint64_t)s_heaterSenseRaw *
(uint64_t)SEAT_BACKREST_HEATER_ADC_REFERENCE_MV *
(uint64_t)SEAT_CURRENT_ADC_REFERENCE_MV *
(uint64_t)SEAT_BACKREST_HEATER_SENSE_KILIS;
uint64_t denominator =
(uint64_t)SEAT_BACKREST_HEATER_ADC_FULL_SCALE *
(uint64_t)SEAT_CURRENT_ADC_FULL_SCALE *
(uint64_t)SEAT_BACKREST_HEATER_SENSE_RESISTOR_OHM;
return (uint32_t)(numerator / denominator);
}
static bool BspSeatBackrestHeaterAdc_ConfigClock(uint32_t *actualClockHz)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup =
Cy_SysClk_GetClockGroup(SEAT_BACKREST_HEATER_SENSE_PCLK);
if(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS)
{
return false;
}
divider = HEATER_ADC_DIV_ROUND_UP(
periClockHz, HEATER_ADC_OPERATION_FREQ_MAX_HZ);
if((divider == 0u) || (divider > 65536u))
{
return false;
}
if(Cy_SysClk_PeriphDisableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(
SEAT_BACKREST_HEATER_SENSE_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphEnableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
*actualClockHz = periClockHz / divider;
return true;
}
static bool BspSeatBackrestHeaterAdc_WaitDone(void)
{
uint32_t timeout = HEATER_ADC_POLL_TIMEOUT;
cy_stc_adc_interrupt_source_t status = { false };
do
{
(void)Cy_Adc_Channel_GetInterruptStatus(
SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL, &status);
if(status.grpDone)
{
return true;
}
timeout--;
} while(timeout > 0u);
return false;
}
static void BspSeatBackrestHeaterAdc_ClearStatus(void)
{
cy_stc_adc_interrupt_source_t clearSource =
{
.grpDone = true,
.grpCancelled = true,
.grpOverflow = true,
.chRange = false,
.chPulse = false,
.chOverflow = true,
};
(void)Cy_Adc_Channel_ClearInterruptStatus(
SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL, &clearSource);
}
@@ -0,0 +1,332 @@
#include "bsp_seat_current_adc.h"
#include "cy_project.h"
#include "cy_device_headers.h"
#include "seat_current_adc_config.h"
#define SEAT_CURRENT_ADC_DIV_ROUND_UP(a, b) (((a) + (b) - 1u) / (b))
typedef struct
{
volatile stc_GPIO_PRT_t *gpioPort;
uint32_t gpioPin;
en_hsiom_sel_t gpioHsiom;
volatile stc_PASS_SAR_CH_t *adcChannel;
cy_en_adc_pin_address_t pinAddress;
} bsp_seat_current_adc_profile_t;
static const bsp_seat_current_adc_profile_t s_profiles[] =
{
{
SEAT_CURRENT_M0_PORT,
SEAT_CURRENT_M0_PIN,
SEAT_CURRENT_M0_GPIO_HSIOM,
SEAT_CURRENT_M0_CHANNEL,
SEAT_CURRENT_M0_PIN_ADDRESS,
},
{
SEAT_CURRENT_M1_PORT,
SEAT_CURRENT_M1_PIN,
SEAT_CURRENT_M1_GPIO_HSIOM,
SEAT_CURRENT_M1_CHANNEL,
SEAT_CURRENT_M1_PIN_ADDRESS,
},
{
SEAT_CURRENT_M2_PORT,
SEAT_CURRENT_M2_PIN,
SEAT_CURRENT_M2_GPIO_HSIOM,
SEAT_CURRENT_M2_CHANNEL,
SEAT_CURRENT_M2_PIN_ADDRESS,
},
{
SEAT_CURRENT_M3_PORT,
SEAT_CURRENT_M3_PIN,
SEAT_CURRENT_M3_GPIO_HSIOM,
SEAT_CURRENT_M3_CHANNEL,
SEAT_CURRENT_M3_PIN_ADDRESS,
},
{
SEAT_CURRENT_HEATER_PORT,
SEAT_CURRENT_HEATER_PIN,
SEAT_CURRENT_HEATER_GPIO_HSIOM,
SEAT_CURRENT_HEATER_CHANNEL,
SEAT_CURRENT_HEATER_PIN_ADDRESS,
},
{
SEAT_CURRENT_FAN_PORT,
SEAT_CURRENT_FAN_PIN,
SEAT_CURRENT_FAN_GPIO_HSIOM,
SEAT_CURRENT_FAN_CHANNEL,
SEAT_CURRENT_FAN_PIN_ADDRESS,
},
};
typedef char bsp_seat_current_adc_profile_count_must_match[
((sizeof(s_profiles) / sizeof(s_profiles[0])) ==
(uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT) ? 1 : -1];
static bool s_initialized = false;
static bool s_sampleActive = false;
static uint16_t s_lastRaw[BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT];
static bool s_lastRawValid[BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT];
static bool BspSeatCurrentAdc_ConfigClock(uint32_t *actualClockHz);
static bool BspSeatCurrentAdc_WaitDone(
volatile stc_PASS_SAR_CH_t *channel);
static void BspSeatCurrentAdc_ClearStatus(
volatile stc_PASS_SAR_CH_t *channel);
bool BspSeatCurrentAdc_Init(void)
{
uint32_t actualClockHz;
uint32_t samplingCycles;
uint32_t index;
cy_stc_gpio_pin_config_t pinConfig =
{
.outVal = 0u,
.driveMode = CY_GPIO_DM_ANALOG,
.hsiom = HSIOM_SEL_GPIO,
.intEdge = 0u,
.intMask = 0u,
.vtrip = 0u,
.slewRate = 0u,
.driveSel = 0u,
};
cy_stc_adc_config_t adcConfig =
{
.preconditionTime = 0u,
.powerupTime = 0u,
.enableIdlePowerDown = false,
.msbStretchMode = CY_ADC_MSB_STRETCH_MODE_1CYCLE,
.enableHalfLsbConv = false,
.sarMuxEnable = true,
.adcEnable = true,
.sarIpEnable = true,
};
cy_stc_adc_channel_config_t channelConfig =
{
.triggerSelection = CY_ADC_TRIGGER_OFF,
.channelPriority = 0u,
.preenptionType = CY_ADC_PREEMPTION_FINISH_RESUME,
.isGroupEnd = true,
.doneLevel = CY_ADC_DONE_LEVEL_PULSE,
.pinAddress = CY_ADC_PIN_ADDRESS_AN0,
.portAddress = SEAT_CURRENT_ADC_PORT_ADDRESS,
.extMuxSelect = 0u,
.extMuxEnable = true,
.preconditionMode = CY_ADC_PRECONDITION_MODE_OFF,
.overlapDiagMode = CY_ADC_OVERLAP_DIAG_MODE_OFF,
.sampleTime = 0u,
.calibrationValueSelect = CY_ADC_CALIBRATION_VALUE_REGULAR,
.postProcessingMode = CY_ADC_POST_PROCESSING_MODE_NONE,
.resultAlignment = CY_ADC_RESULT_ALIGNMENT_RIGHT,
.signExtention = CY_ADC_SIGN_EXTENTION_UNSIGNED,
.averageCount = 0u,
.rightShift = 0u,
.rangeDetectionMode = CY_ADC_RANGE_DETECTION_MODE_INSIDE_RANGE,
.rangeDetectionLoThreshold = 0u,
.rangeDetectionHiThreshold = SEAT_CURRENT_ADC_FULL_SCALE,
.mask.grpDone = true,
.mask.grpCancelled = false,
.mask.grpOverflow = false,
.mask.chRange = false,
.mask.chPulse = false,
.mask.chOverflow = false,
};
if(s_initialized)
{
return true;
}
for(index = 0u;
index < (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT;
index++)
{
pinConfig.hsiom = s_profiles[index].gpioHsiom;
if(Cy_GPIO_Pin_Init(s_profiles[index].gpioPort,
s_profiles[index].gpioPin,
&pinConfig) != CY_GPIO_SUCCESS)
{
return false;
}
s_lastRaw[index] = 0u;
s_lastRawValid[index] = false;
}
if(!BspSeatCurrentAdc_ConfigClock(&actualClockHz))
{
return false;
}
samplingCycles = (uint32_t)SEAT_CURRENT_ADC_DIV_ROUND_UP(
(SEAT_CURRENT_ADC_SAMPLE_TIME_MIN_NS * (uint64_t)actualClockHz),
1000000000ull);
if(samplingCycles == 0u)
{
samplingCycles = 1u;
}
channelConfig.sampleTime = (uint16_t)samplingCycles;
Cy_Adc_DeInit(SEAT_CURRENT_ADC_INSTANCE);
if(Cy_Adc_Init(SEAT_CURRENT_ADC_INSTANCE,
&adcConfig) != CY_ADC_SUCCESS)
{
return false;
}
for(index = 0u;
index < (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT;
index++)
{
channelConfig.pinAddress = s_profiles[index].pinAddress;
if(Cy_Adc_Channel_Init(s_profiles[index].adcChannel,
&channelConfig) != CY_ADC_SUCCESS)
{
Cy_Adc_DeInit(SEAT_CURRENT_ADC_INSTANCE);
return false;
}
Cy_Adc_Channel_Enable(s_profiles[index].adcChannel);
}
s_initialized = true;
return true;
}
bool BspSeatCurrentAdc_Sample(bsp_seat_current_adc_signal_t signal,
uint16_t *raw)
{
uint32_t index = (uint32_t)signal;
uint16_t sample = 0u;
cy_stc_adc_ch_status_t resultStatus = { false };
volatile stc_PASS_SAR_CH_t *channel;
bool success = false;
if(!s_initialized || s_sampleActive ||
(index >= (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT))
{
return false;
}
s_sampleActive = true;
channel = s_profiles[index].adcChannel;
BspSeatCurrentAdc_ClearStatus(channel);
Cy_Adc_Channel_SoftwareTrigger(channel);
if(BspSeatCurrentAdc_WaitDone(channel) &&
(Cy_Adc_Channel_GetResult(channel,
&sample,
&resultStatus) == CY_ADC_SUCCESS) &&
resultStatus.valid)
{
sample = (uint16_t)(sample & SEAT_CURRENT_ADC_FULL_SCALE);
s_lastRaw[index] = sample;
s_lastRawValid[index] = true;
if(raw != NULL)
{
*raw = sample;
}
success = true;
}
BspSeatCurrentAdc_ClearStatus(channel);
s_sampleActive = false;
return success;
}
bool BspSeatCurrentAdc_GetLastRaw(bsp_seat_current_adc_signal_t signal,
uint16_t *raw)
{
uint32_t index = (uint32_t)signal;
if((raw == NULL) ||
(index >= (uint32_t)BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT) ||
!s_lastRawValid[index])
{
return false;
}
*raw = s_lastRaw[index];
return true;
}
static bool BspSeatCurrentAdc_ConfigClock(uint32_t *actualClockHz)
{
uint32_t periClockHz = 0u;
uint32_t divider;
uint32_t clockGroup = Cy_SysClk_GetClockGroup(SEAT_CURRENT_ADC_PCLK);
if((actualClockHz == NULL) ||
(Cy_SysClk_GetClkPeriFrequency(&periClockHz) != CY_SYSCLK_SUCCESS))
{
return false;
}
divider = SEAT_CURRENT_ADC_DIV_ROUND_UP(
periClockHz, SEAT_CURRENT_ADC_OPERATION_FREQ_MAX_HZ);
if((divider == 0u) || (divider > 65536u))
{
return false;
}
if(Cy_SysClk_PeriphDisableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphAssignDivider(
SEAT_CURRENT_ADC_PCLK,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphSetDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER,
divider - 1u) != CY_SYSCLK_SUCCESS)
{
return false;
}
if(Cy_SysClk_PeriphEnableDivider(
clockGroup,
CY_SYSCLK_DIV_16_BIT,
SEAT_CURRENT_ADC_DIVIDER_NUMBER) != CY_SYSCLK_SUCCESS)
{
return false;
}
*actualClockHz = periClockHz / divider;
return true;
}
static bool BspSeatCurrentAdc_WaitDone(
volatile stc_PASS_SAR_CH_t *channel)
{
uint32_t timeout = SEAT_CURRENT_ADC_POLL_TIMEOUT;
cy_stc_adc_interrupt_source_t status = { false };
do
{
(void)Cy_Adc_Channel_GetInterruptStatus(channel, &status);
if(status.grpDone)
{
return true;
}
timeout--;
} while(timeout > 0u);
return false;
}
static void BspSeatCurrentAdc_ClearStatus(
volatile stc_PASS_SAR_CH_t *channel)
{
cy_stc_adc_interrupt_source_t clearSource =
{
.grpDone = true,
.grpCancelled = true,
.grpOverflow = true,
.chRange = false,
.chPulse = false,
.chOverflow = true,
};
(void)Cy_Adc_Channel_ClearInterruptStatus(channel, &clearSource);
}
@@ -0,0 +1,27 @@
#ifndef BSP_SEAT_CURRENT_ADC_H
#define BSP_SEAT_CURRENT_ADC_H
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
BSP_SEAT_CURRENT_ADC_MOTOR0 = 0,
BSP_SEAT_CURRENT_ADC_MOTOR1,
BSP_SEAT_CURRENT_ADC_MOTOR2,
BSP_SEAT_CURRENT_ADC_MOTOR3,
BSP_SEAT_CURRENT_ADC_HEATER,
BSP_SEAT_CURRENT_ADC_FAN,
BSP_SEAT_CURRENT_ADC_SIGNAL_COUNT
} bsp_seat_current_adc_signal_t;
/* Initializes PASS0 SAR1 once and enables every shared seat-current channel. */
bool BspSeatCurrentAdc_Init(void);
/* Main-loop polling API. It must not be called from an ISR. */
bool BspSeatCurrentAdc_Sample(bsp_seat_current_adc_signal_t signal,
uint16_t *raw);
bool BspSeatCurrentAdc_GetLastRaw(bsp_seat_current_adc_signal_t signal,
uint16_t *raw);
#endif /* BSP_SEAT_CURRENT_ADC_H */
@@ -29,6 +29,8 @@ static volatile bsp_seat_can_frame_t s_canRxQueue[SEAT_FAN_CAN_RX_QUEUE_DEPTH];
static volatile uint8_t s_canRxQueueHead = 0u;
static volatile uint8_t s_canRxQueueTail = 0u;
volatile uint32_t g_bspSeatFanStartFailStage = 0u;
static const cy_stc_id_filter_t s_canStandardIdFilters[] =
{
CANFD_CONFIG_STD_ID_FILTER_RANGE(
@@ -223,8 +225,10 @@ bool BspSeatFan_InitPwm(void)
bool BspSeatFan_Start(uint32_t dutyPercent)
{
g_bspSeatFanStartFailStage = 0u;
if(!BspSeatFan_SetDutyPercent(dutyPercent))
{
g_bspSeatFanStartFailStage = 1u;
return false;
}
@@ -244,6 +248,7 @@ bool BspSeatFan_Start(uint32_t dutyPercent)
if((Cy_Tcpwm_Pwm_GetStatus(SEAT_FAN_PWM_TCPWM) &
TCPWM_GRP_CNT_STATUS_RUNNING_Msk) == 0u)
{
g_bspSeatFanStartFailStage = 2u;
BspSeatFan_FailSafeOff();
return false;
}
@@ -261,6 +266,7 @@ bool BspSeatFan_Start(uint32_t dutyPercent)
if(Cy_GPIO_GetHSIOM(SEAT_FAN_PWM_PORT,
SEAT_FAN_PWM_PIN) != SEAT_FAN_PWM_HSIOM)
{
g_bspSeatFanStartFailStage = 3u;
BspSeatFan_FailSafeOff();
return false;
}
@@ -27,6 +27,9 @@ bool BspSeatFan_SetDutyPercent(uint32_t dutyPercent);
/* Disable TCPWM and restore both known fan-control pins to GPIO low. */
void BspSeatFan_FailSafeOff(void);
/* Read-only HIL diagnostic: 0=none, 1=compare, 2=counter, 3=HSIOM. */
extern volatile uint32_t g_bspSeatFanStartFailStage;
/* Initialize the proven CAN4/TJA1044 board interface at 500 kbit/s. */
bool BspSeatCan_Init(void);
@@ -2,6 +2,7 @@
#include "cy_project.h"
#include "cy_device_headers.h"
#include "motor_control.h"
#include "seat_move_config.h"
static bool BspSeatMoveCan_ConfigClock(void);
@@ -49,10 +50,6 @@ static const cy_stc_id_filter_t s_standardIdFilters[] =
SEAT_MOVE_CAN_COMMAND_ID,
SEAT_MOVE_CAN_COMMAND_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
CANFD_CONFIG_STD_ID_FILTER_RANGE(
SEAT_CONTROLLER_IDENTITY_REQUEST_ID,
SEAT_CONTROLLER_IDENTITY_REQUEST_ID,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0),
};
static cy_stc_sysint_irq_t s_canIrqConfig =
@@ -382,6 +379,15 @@ static void BspSeatMoveCan_RxCallback(bool rxFifoMessage,
g_bspSeatMoveCanRxCount++;
g_bspSeatMoveCanLastRxId = message->idConfig.identifier;
if((message->idConfig.identifier == SEAT_MOVE_CAN_COMMAND_ID) &&
(message->dataConfig.dataLengthCode == SEAT_MOVE_CAN_DLC) &&
(((uint8_t)(message->dataConfig.data[0] & 0xFFu)) == 0x09u) &&
(((uint8_t)((message->dataConfig.data[0] >> 8u) & 0xFFu)) == 0x10u) &&
(message->dataConfig.data[1] == 0u))
{
Motor_RequestAbortAllFromIsr(
(uint16_t)(message->dataConfig.data[0] >> 16u));
}
head = s_rxQueueHead;
nextHead = head + 1u;
if(nextHead >= SEAT_MOVE_CAN_RX_QUEUE_DEPTH)
@@ -85,16 +85,8 @@
#define SEAT_BACKREST_HEATER_SENSE_PORT GPIO_PRT12
#define SEAT_BACKREST_HEATER_SENSE_PIN (2u)
#define SEAT_BACKREST_HEATER_SENSE_GPIO_HSIOM P12_2_GPIO
#define SEAT_BACKREST_HEATER_SENSE_ADC PASS0_SAR1
#define SEAT_BACKREST_HEATER_SENSE_ADC_CHANNEL PASS0_SAR1_CH6
#define SEAT_BACKREST_HEATER_SENSE_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN6
#define SEAT_BACKREST_HEATER_SENSE_PORT_ADDRESS CY_ADC_PORT_ADDRESS_SARMUX0
#define SEAT_BACKREST_HEATER_SENSE_PCLK PCLK_PASS0_CLOCK_SAR1
#define SEAT_BACKREST_HEATER_SENSE_DIVIDER_NUMBER (4u)
#define SEAT_BACKREST_HEATER_SENSE_RESISTOR_OHM (1200u)
#define SEAT_BACKREST_HEATER_SENSE_KILIS (5000u)
#define SEAT_BACKREST_HEATER_ADC_REFERENCE_MV (3300u)
#define SEAT_BACKREST_HEATER_ADC_FULL_SCALE (4095u)
#define SEAT_BACKREST_HEATER_SENSE_RESISTOR_OHM (1200u)
#define SEAT_BACKREST_HEATER_SENSE_KILIS (5000u)
/* BTS7010 control facts confirmed on the power-driver schematic page. */
#define SEAT_BACKREST_HEATER_BTS7010_IN_ACTIVE_HIGH (1u)
@@ -0,0 +1,71 @@
#ifndef SEAT_CURRENT_ADC_CONFIG_H
#define SEAT_CURRENT_ADC_CONFIG_H
#include "cy_project.h"
#include "cy_device_headers.h"
/*
* PASS0 SAR1 is shared by all seat-current measurements. The schematic
* connects VREFH and the DRV8718 AREF rail to filtered 5 V, so raw-to-voltage
* calculations must use 5000 mV rather than the GPIO-domain 3.3 V supply.
*
* Logical motor mapping:
* M0 horizontal : ZYTTKZDY-AD, P10.6, ADC[1]_2
* M1 seat pan : JXBKZDY-AD, P12.1, ADC[1]_5
* M2 backrest : ZYKBKZDY-AD, P10.7, ADC[1]_3
* M3 legrest : ZYGDKZDY-AD, P10.5, ADC[1]_1
* heater current : KBJRD-AD, P12.2, ADC[1]_6
* fan current : FSDY-AD, P12.6, ADC[1]_10
*/
#define SEAT_CURRENT_ADC_INSTANCE PASS0_SAR1
#define SEAT_CURRENT_ADC_PCLK PCLK_PASS0_CLOCK_SAR1
#define SEAT_CURRENT_ADC_DIVIDER_NUMBER (4u)
#define SEAT_CURRENT_ADC_REFERENCE_MV (5000u)
#define SEAT_CURRENT_ADC_FULL_SCALE (4095u)
#define SEAT_CURRENT_ADC_OPERATION_FREQ_MAX_HZ (13340000u)
#define SEAT_CURRENT_ADC_SAMPLE_TIME_MIN_NS (600ull)
#define SEAT_CURRENT_ADC_POLL_TIMEOUT (100000u)
#define SEAT_CURRENT_M0_PORT GPIO_PRT10
#define SEAT_CURRENT_M0_PIN (6u)
#define SEAT_CURRENT_M0_GPIO_HSIOM P10_6_GPIO
#define SEAT_CURRENT_M0_CHANNEL PASS0_SAR1_CH2
#define SEAT_CURRENT_M0_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN2
#define SEAT_CURRENT_M1_PORT GPIO_PRT12
#define SEAT_CURRENT_M1_PIN (1u)
#define SEAT_CURRENT_M1_GPIO_HSIOM P12_1_GPIO
#define SEAT_CURRENT_M1_CHANNEL PASS0_SAR1_CH5
#define SEAT_CURRENT_M1_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN5
#define SEAT_CURRENT_M2_PORT GPIO_PRT10
#define SEAT_CURRENT_M2_PIN (7u)
#define SEAT_CURRENT_M2_GPIO_HSIOM P10_7_GPIO
#define SEAT_CURRENT_M2_CHANNEL PASS0_SAR1_CH3
#define SEAT_CURRENT_M2_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN3
#define SEAT_CURRENT_M3_PORT GPIO_PRT10
#define SEAT_CURRENT_M3_PIN (5u)
#define SEAT_CURRENT_M3_GPIO_HSIOM P10_5_GPIO
#define SEAT_CURRENT_M3_CHANNEL PASS0_SAR1_CH1
#define SEAT_CURRENT_M3_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN1
#define SEAT_CURRENT_HEATER_PORT GPIO_PRT12
#define SEAT_CURRENT_HEATER_PIN (2u)
#define SEAT_CURRENT_HEATER_GPIO_HSIOM P12_2_GPIO
#define SEAT_CURRENT_HEATER_CHANNEL PASS0_SAR1_CH6
#define SEAT_CURRENT_HEATER_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN6
#define SEAT_CURRENT_FAN_PORT GPIO_PRT12
#define SEAT_CURRENT_FAN_PIN (6u)
#define SEAT_CURRENT_FAN_GPIO_HSIOM P12_6_GPIO
#define SEAT_CURRENT_FAN_CHANNEL PASS0_SAR1_CH10
#define SEAT_CURRENT_FAN_PIN_ADDRESS CY_ADC_PIN_ADDRESS_AN10
#define SEAT_CURRENT_ADC_PORT_ADDRESS CY_ADC_PORT_ADDRESS_SARMUX0
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat current ADC mapping requires CYT4BFBCJE rev_d headers."
#endif
#endif /* SEAT_CURRENT_ADC_CONFIG_H */
@@ -77,6 +77,9 @@
#define SEAT_FAN_CAN_FEEDBACK_PERIOD_MS (100u)
#define SEAT_FAN_CAN_ACCEPT_42A_COMMAND (1u)
/* FSDY-AD is P12.6 / ADC[1]_10 on the shared seat-current ADC. */
#define SEAT_FAN_CURRENT_SAMPLE_MS (10u)
/*
* BREG3 retains the debug-session state through debug/XRES/WDT resets.
* DEBUG_ACTIVE is written by firmware as soon as it observes C_DEBUGEN, so a
@@ -9,7 +9,38 @@
#define SEAT_MOVE_TASK_PERIOD_US (1000u)
#define SEAT_MOVE_START_STAGGER_MS (100u)
#define SEAT_MOVE_MAX_COMMAND_DURATION_MS (60000u)
#define SEAT_MOVE_MIN_COMMAND_DURATION_MS (100u)
#define SEAT_MOVE_MIN_CALIBRATION_TIMEOUT_S (10u)
#define SEAT_MOVE_MAX_CALIBRATION_TIMEOUT_S (600u)
#define SEAT_MOVE_COMMAND_QUEUE_DEPTH (8u)
#define SEAT_MOVE_MAX_CONCURRENT_MOTORS (4u)
/* Host mode plans issue the next axis within two seconds. Delay the clean
* SMV8 commit until the bus has been motion-idle for three seconds so a
* four-axis posture writes Work Flash once instead of racing every command. */
#define SEAT_MOVE_STORE_SAVE_DEBOUNCE_MS (3000u)
/* 2026-07-30 range tests verified that CAN MotorID and low-level DRV8718
* mode use the same channel number on the connected bench. */
#define SEAT_MOVE_LOGICAL_M0_PHYSICAL_MODE (0u) /* horizontal */
#define SEAT_MOVE_LOGICAL_M1_PHYSICAL_MODE (1u) /* seat-pan lift */
#define SEAT_MOVE_LOGICAL_M2_PHYSICAL_MODE (2u) /* backrest */
#define SEAT_MOVE_LOGICAL_M3_PHYSICAL_MODE (3u) /* legrest lift */
#define SEAT_MOVE_M0_DEFAULT_TIMEOUT_MS (12000u)
#define SEAT_MOVE_M1_DEFAULT_TIMEOUT_MS (8000u)
#define SEAT_MOVE_M2_DEFAULT_TIMEOUT_MS (12000u)
#define SEAT_MOVE_M3_DEFAULT_TIMEOUT_MS (10000u)
#define SEAT_MOVE_M0_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M1_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M2_DEFAULT_DUTY_PERCENT (90u)
#define SEAT_MOVE_M3_DEFAULT_DUTY_PERCENT (90u)
/* A Hall target is discrete and an endpoint can physically stop one or two
* pulses short because of switch/gear backlash. If a position move is
* already this close when an endpoint/stall event stops the bridge, classify
* the requested position as reached instead of retrying and latching STALL.
* The raw lastMotorEvent remains available in 0x303 for diagnosis. */
#define SEAT_MOVE_POSITION_TARGET_HALL_TOLERANCE (2u)
/* CAN4 wiring verified by Seat_Fan_demo and D:\wcl\can. P3.1 is shared with
* the DRV8718 DVDD enable on this board; both users require it to remain high. */
@@ -34,27 +65,44 @@
#define SEAT_MOVE_CAN_RX_QUEUE_DEPTH (16u)
#define SEAT_MOVE_CAN_DLC (8u)
#define SEAT_MOVE_CAN_PROTOCOL_VERSION (1u)
/* Repository image identity carried by 0x301 byte 7 high nibble. Increment
* whenever a host-visible safety or motion fix requires automatic reflash. */
#define SEAT_MOVE_FIRMWARE_REVISION (3u)
#define SEAT_MOVE_CAN_COMMAND_ID (0x300u)
#define SEAT_MOVE_CAN_SYSTEM_STATUS_ID (0x301u)
#define SEAT_MOVE_CAN_MOTOR_STATUS_ID (0x302u)
#define SEAT_MOVE_CAN_DIAGNOSTIC_ID (0x303u)
#define SEAT_CONTROLLER_IDENTITY_REQUEST_ID (0x304u)
#define SEAT_CONTROLLER_IDENTITY_RESPONSE_ID (0x305u)
#define SEAT_CONTROLLER_IDENTITY_MAGIC (0x54414553u) /* "SEAT" */
#define SEAT_CONTROLLER_IDENTITY_PROTOCOL (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_MAJOR (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_MINOR (1u)
#define SEAT_CONTROLLER_FIRMWARE_VERSION_PATCH (0u)
#define SEAT_CONTROLLER_CAPABILITY_MASK (0x3Fu)
#define SEAT_CONTROLLER_IDENTITY_RESPONSE_DATA1 \
((SEAT_CONTROLLER_CAPABILITY_MASK << 24u) | \
(SEAT_CONTROLLER_FIRMWARE_VERSION_PATCH << 16u) | \
(SEAT_CONTROLLER_FIRMWARE_VERSION_MINOR << 8u) | \
SEAT_CONTROLLER_FIRMWARE_VERSION_MAJOR)
#define SEAT_MOVE_CAN_CURRENT_DIAGNOSTIC_ID (0x304u)
#define SEAT_MOVE_SYSTEM_STATUS_PERIOD_MS (100u)
#define SEAT_MOVE_MOTOR_STATUS_PERIOD_MS (100u)
#define SEAT_MOVE_DIAGNOSTIC_PERIOD_MS (500u)
#define SEAT_MOVE_MOTOR_STATUS_POLL_MS (25u)
/* One motor is emitted per poll; 25 ms gives each axis a 100 ms 0x303/0x304
* update period and captures the 200 ms high-current confirmation window. */
#define SEAT_MOVE_DIAGNOSTIC_POLL_MS (25u)
/*
* Motor stall classification uses two independent observations:
* 1. no more than the Hall pulse threshold in the rolling Hall window;
* 2. current above this axis-specific delta-from-idle threshold.
*
* M0/M1 use external amplifiers, so every threshold remains independently
* configurable. After increasing all normal moves to 90% duty, 500 raw is
* intentionally retained as a conservative, uncalibrated default that
* prioritizes avoiding nuisance STALL reports. A low-Hall condition that
* stays below this current threshold is still stopped as TIMEOUT after 5 s.
* Replace these defaults only after loaded NORMAL and secured-stall tests at
* the final duty and supply voltage.
*/
#define SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS (10u)
#define SEAT_MOVE_MOTOR_CURRENT_MAX_AGE_MS (100u)
#define SEAT_MOVE_MOTOR_CURRENT_BASELINE_FILTER_SHIFT (3u)
#define SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS (200u)
#define SEAT_MOVE_MOTOR_NO_MOTION_TIMEOUT_MS (5000u)
#define SEAT_MOVE_M0_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M1_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M2_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_M3_STALL_CURRENT_DELTA_RAW (500u)
#define SEAT_MOVE_MOTOR_CURRENT_MAX_ERRORS (3u)
/* BREG3 records a C-SPY session across debug/XRES/WDT resets. A genuine POR
* clears the latch, so normal vehicle operation never requires a debugger. */
@@ -63,13 +111,10 @@
#define SEAT_MOVE_DEBUG_EXIT_MAGIC (0x4D4F5645u) /* "MOVE" */
#define SEAT_MOVE_WDT_TIMEOUT_TICKS (3200u)
/* One dedicated 128-byte Work Flash small sector. */
/* Two dedicated 128-byte Work Flash small sectors. */
#define SEAT_MOVE_STORE_ADDRESS (CY_WFLASH_SM_SBM_TOP)
#define SEAT_MOVE_STORE_MAGIC (0x534D5633u) /* "SMV3" */
#define SEAT_MOVE_STORE_VERSION (3u)
#define SEAT_MOVE_STORE_LEGACY_MAGIC (0x534D5632u) /* "SMV2" */
#define SEAT_MOVE_STORE_LEGACY_VERSION (2u)
#define SEAT_MOVE_STORE_LEGACY_MOTOR_COUNT (3u)
#define SEAT_MOVE_STORE_MAGIC (0x534D5638u) /* "SMV8" */
#define SEAT_MOVE_STORE_VERSION (8u)
#if !defined(CYT4BFBCJE) || !defined(CY_MCU_rev_d)
#error "Seat_Controller_demo must be built for CYT4BFBCJE with rev_d headers."
@@ -79,17 +124,58 @@
#error "All four vehicle seat-motor outputs must be enabled."
#endif
#if (SEAT_MOVE_MAX_CONCURRENT_MOTORS == 0u) || \
(SEAT_MOVE_MAX_CONCURRENT_MOTORS > SEAT_MOVE_MOTOR_COUNT)
#error "Concurrent motor budget must be between one and four."
#endif
#if ((SEAT_MOVE_LOGICAL_M0_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M1_PHYSICAL_MODE) || \
(SEAT_MOVE_LOGICAL_M0_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M2_PHYSICAL_MODE) || \
(SEAT_MOVE_LOGICAL_M0_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M3_PHYSICAL_MODE) || \
(SEAT_MOVE_LOGICAL_M1_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M2_PHYSICAL_MODE) || \
(SEAT_MOVE_LOGICAL_M1_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M3_PHYSICAL_MODE) || \
(SEAT_MOVE_LOGICAL_M2_PHYSICAL_MODE == \
SEAT_MOVE_LOGICAL_M3_PHYSICAL_MODE))
#error "Each logical seat motor must map to a unique physical channel."
#endif
#if ((SEAT_MOVE_CAN_COMMAND_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_SYSTEM_STATUS_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_MOTOR_STATUS_ID > 0x7FFu) || \
(SEAT_MOVE_CAN_DIAGNOSTIC_ID > 0x7FFu) || \
(SEAT_CONTROLLER_IDENTITY_REQUEST_ID > 0x7FFu) || \
(SEAT_CONTROLLER_IDENTITY_RESPONSE_ID > 0x7FFu))
(SEAT_MOVE_CAN_CURRENT_DIAGNOSTIC_ID > 0x7FFu))
#error "Seat movement CAN identifiers must be 11-bit standard IDs."
#endif
#if (SEAT_MOVE_FIRMWARE_REVISION > 0x0Fu)
#error "The firmware revision must fit in the 0x301 high nibble."
#endif
#if (SEAT_MOVE_DEBUG_SESSION_BREG_INDEX >= SRSS_BACKUP_NUM_BREG)
#error "The selected debug-session backup register is not available."
#endif
#if ((SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS == 0u) || \
(SEAT_MOVE_MOTOR_CURRENT_MAX_AGE_MS < \
SEAT_MOVE_MOTOR_CURRENT_SAMPLE_MS) || \
(SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS == 0u) || \
(SEAT_MOVE_MOTOR_NO_MOTION_TIMEOUT_MS <= \
SEAT_MOVE_MOTOR_STALL_CURRENT_CONFIRM_MS) || \
(SEAT_MOVE_MOTOR_CURRENT_MAX_ERRORS == 0u))
#error "Motor current/stall timing configuration is invalid."
#endif
#if (SEAT_MOVE_POSITION_TARGET_HALL_TOLERANCE > 4u)
#error "Position target Hall tolerance must remain tightly bounded."
#endif
#if (SEAT_MOVE_STORE_SAVE_DEBOUNCE_MS < 1000u)
#error "The Work Flash save debounce must cover inter-axis host command gaps."
#endif
#endif /* SEAT_MOVE_CONFIG_H */
@@ -0,0 +1,27 @@
# 座椅电流阈值台架分析(2026-07-30)
数据源:`Tests/manual_hil/logs/06_current_threshold_*`,时间戳15:18~15:26。
> 后续配置说明:四轴普通移动占空比已统一提高到90%。由于本轮M0~M2没有形成有效受控堵转、M3又存在无Hall/TIMEOUT,表中的270/290/260/250未作为最终固件值;固件暂时使用四轴500 raw保守默认值,优先避免误报。低Hall但电流未达500时仍会在5秒后以TIMEOUT停机。
## 四轴结果
| 轴 | 停止delta P99 | 已验证自由运动delta P99 / 最大 | 有效低Hall带载证据 | 当时建议(未采用) |
|---|---:|---:|---|---:|
| M0 | 2 | 233 / 243 | 本轮STALL为自由运动,0个低Hall样本 | 270 |
| M1 | 2 | 255 / 255 | 本轮STALL为自由运动,0个低Hall样本 | 290 |
| M2 | 7 | 229 / 230 | 本轮STALL为自由运动,0个低Hall样本 | 260 |
| M3 | 5 | 无有效自由运动结果 | 位置命令持续低Hall,delta约284~301,最终TIMEOUT | 250 |
表中当时建议值高于M0~M2本轮全部自由运动峰值约11%~14%;M3的250原计划用于把持续“无Hall+带载”从5秒TIMEOUT提前分类为STALL。由于占空比随后改为90%且没有完成新负载下复核,这组建议没有进入当前固件;当前生效值为四轴500 raw。
## 数据限制与复核
- `STALL_M0`、`STALL_M1`、`STALL_M2`终态均为OK,位置继续变化,且STALL阶段低Hall样本数均为0;不能将其电流当作堵转电流。
- `STALL_M3`在50%放置阶段即TIMEOUT,因此没有进入受控堵转阶段。
- 四个ENDPOINT阶段在旧固件中均报告PASS,但旧归零逻辑可能在电机未产生真实Hall运动时直接清零,因此这些结果只保留为电流样本,不能单独证明机械归零成功。新固件要求至少4个原始Hall双边沿的离端验证以及相符的回程边沿后才能接受近端。
- 烧录新阈值后,先复跑`NORMAL`。随后使用固定软限位分别复跑`STALL_M0`~`STALL_M3`;必须看到低Hall与高电流连续重叠并返回STALL,才能将对应阈值标为最终台架值。
## 通风与加热电流
`THERMAL`阶段16项CAN/ADC检查全部PASS,确认通风采样为P12.6/ADC1_CH10、加热采样为P12.2/ADC1_CH6,ADC错误计数为0。由于PWM输出与ADC采样未同步,raw中存在开关相位离散值;本轮只用于确认链路和各档基线,不据此增加热管理过流阈值。
@@ -0,0 +1,20 @@
TestID,Result,Actual,Notes
HEAT-002,PASS,11 00 00 00 00 00 00 00,expected Byte0=11
HEAT-003,PASS,19 00 00 00 00 00 00 00,expected Byte0=19
HEAT-004,PASS,21 00 00 00 00 00 00 00,expected Byte0=21
HEAT-005,PASS,01 00 00 00 00 00 00 00,heater feedback field must be 0
THM-005,PASS,23 00 00 00 00 00 00 00,expected Byte0=23
THM-006,PASS,09 00 00 00 00 00 00 00,expected Byte0=09
THM-007,PASS,00 00 00 00 00 00 00 00,bad DLC must force fan and heater feedback to 0
MOV-001,PASS,"{'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 16384, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 9}",expected ack=4 reject=0 seq=0x4000
MOV-002,PASS,"{'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 10}",expected ack=1 reject=0 seq=0x4001
MOV-003,PASS,"{'ack': 2, 'system_state': 1, 'reject': 2, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 11}",expected ack=2 reject=2 seq=0x4002
MOV-004,PASS,"{'ack': 2, 'system_state': 1, 'reject': 3, 'sequence': 16387, 'opcode': 4, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 12}",expected ack=2 reject=3 seq=0x4003
MOV-005,PASS,"{'ack': 2, 'system_state': 1, 'reject': 4, 'sequence': 16388, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 13}",expected ack=2 reject=4 seq=0x4004
MOV-006,PASS,"{'ack': 2, 'system_state': 1, 'reject': 5, 'sequence': 16389, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 14}",expected ack=2 reject=5 seq=0x4005
MOV-007,PASS,"{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16390, 'opcode': 3, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 15}",expected ack=2 reject=6 seq=0x4006
CAL-001,PASS,"{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16391, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 0}",expected ack=2 reject=6 seq=0x4007
CAL-002,PASS,"{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16392, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 1}",expected ack=2 reject=6 seq=0x4008
MOV-017,PASS,"{'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 2}",expected ack=1 reject=0 seq=0x4002
MOV-018,PASS,"{'ack': 3, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 3}",expected ack=3 reject=0 seq=0x4002
MOV-019,PASS,"{'ack': 2, 'system_state': 1, 'reject': 8, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 4}",expected ack=2 reject=8 seq=0x4001
1 TestID Result Actual Notes
2 HEAT-002 PASS 11 00 00 00 00 00 00 00 expected Byte0=11
3 HEAT-003 PASS 19 00 00 00 00 00 00 00 expected Byte0=19
4 HEAT-004 PASS 21 00 00 00 00 00 00 00 expected Byte0=21
5 HEAT-005 PASS 01 00 00 00 00 00 00 00 heater feedback field must be 0
6 THM-005 PASS 23 00 00 00 00 00 00 00 expected Byte0=23
7 THM-006 PASS 09 00 00 00 00 00 00 00 expected Byte0=09
8 THM-007 PASS 00 00 00 00 00 00 00 00 bad DLC must force fan and heater feedback to 0
9 MOV-001 PASS {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 16384, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 9} expected ack=4 reject=0 seq=0x4000
10 MOV-002 PASS {'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 10} expected ack=1 reject=0 seq=0x4001
11 MOV-003 PASS {'ack': 2, 'system_state': 1, 'reject': 2, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 11} expected ack=2 reject=2 seq=0x4002
12 MOV-004 PASS {'ack': 2, 'system_state': 1, 'reject': 3, 'sequence': 16387, 'opcode': 4, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 12} expected ack=2 reject=3 seq=0x4003
13 MOV-005 PASS {'ack': 2, 'system_state': 1, 'reject': 4, 'sequence': 16388, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 13} expected ack=2 reject=4 seq=0x4004
14 MOV-006 PASS {'ack': 2, 'system_state': 1, 'reject': 5, 'sequence': 16389, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 14} expected ack=2 reject=5 seq=0x4005
15 MOV-007 PASS {'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16390, 'opcode': 3, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 15} expected ack=2 reject=6 seq=0x4006
16 CAL-001 PASS {'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16391, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 0} expected ack=2 reject=6 seq=0x4007
17 CAL-002 PASS {'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16392, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 1} expected ack=2 reject=6 seq=0x4008
18 MOV-017 PASS {'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 2} expected ack=1 reject=0 seq=0x4002
19 MOV-018 PASS {'ack': 3, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 3} expected ack=3 reject=0 seq=0x4002
20 MOV-019 PASS {'ack': 2, 'system_state': 1, 'reject': 8, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 4} expected ack=2 reject=8 seq=0x4001
@@ -0,0 +1,178 @@
{
"generated_local": "2026-07-30 09:33:10",
"results": [
{
"Actual": "11 00 00 00 00 00 00 00",
"Notes": "expected Byte0=11",
"Result": "PASS",
"TestID": "HEAT-002"
},
{
"Actual": "19 00 00 00 00 00 00 00",
"Notes": "expected Byte0=19",
"Result": "PASS",
"TestID": "HEAT-003"
},
{
"Actual": "21 00 00 00 00 00 00 00",
"Notes": "expected Byte0=21",
"Result": "PASS",
"TestID": "HEAT-004"
},
{
"Actual": "01 00 00 00 00 00 00 00",
"Notes": "heater feedback field must be 0",
"Result": "PASS",
"TestID": "HEAT-005"
},
{
"Actual": "23 00 00 00 00 00 00 00",
"Notes": "expected Byte0=23",
"Result": "PASS",
"TestID": "THM-005"
},
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09",
"Result": "PASS",
"TestID": "THM-006"
},
{
"Actual": "00 00 00 00 00 00 00 00",
"Notes": "bad DLC must force fan and heater feedback to 0",
"Result": "PASS",
"TestID": "THM-007"
},
{
"Actual": "{'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 16384, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 9}",
"Notes": "expected ack=4 reject=0 seq=0x4000",
"Result": "PASS",
"TestID": "MOV-001"
},
{
"Actual": "{'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 10}",
"Notes": "expected ack=1 reject=0 seq=0x4001",
"Result": "PASS",
"TestID": "MOV-002"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 2, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 11}",
"Notes": "expected ack=2 reject=2 seq=0x4002",
"Result": "PASS",
"TestID": "MOV-003"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 3, 'sequence': 16387, 'opcode': 4, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 12}",
"Notes": "expected ack=2 reject=3 seq=0x4003",
"Result": "PASS",
"TestID": "MOV-004"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 4, 'sequence': 16388, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 13}",
"Notes": "expected ack=2 reject=4 seq=0x4004",
"Result": "PASS",
"TestID": "MOV-005"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 5, 'sequence': 16389, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 14}",
"Notes": "expected ack=2 reject=5 seq=0x4005",
"Result": "PASS",
"TestID": "MOV-006"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16390, 'opcode': 3, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 15}",
"Notes": "expected ack=2 reject=6 seq=0x4006",
"Result": "PASS",
"TestID": "MOV-007"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16391, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 0}",
"Notes": "expected ack=2 reject=6 seq=0x4007",
"Result": "PASS",
"TestID": "CAL-001"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 6, 'sequence': 16392, 'opcode': 10, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 1}",
"Notes": "expected ack=2 reject=6 seq=0x4008",
"Result": "PASS",
"TestID": "CAL-002"
},
{
"Actual": "{'ack': 1, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 2}",
"Notes": "expected ack=1 reject=0 seq=0x4002",
"Result": "PASS",
"TestID": "MOV-017"
},
{
"Actual": "{'ack': 3, 'system_state': 1, 'reject': 0, 'sequence': 16386, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 3}",
"Notes": "expected ack=3 reject=0 seq=0x4002",
"Result": "PASS",
"TestID": "MOV-018"
},
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 8, 'sequence': 16385, 'opcode': 12, 'active_mask': 0, 'position_valid_mask': 0, 'faulted_mask': 0, 'alive': 4}",
"Notes": "expected ack=2 reject=8 seq=0x4001",
"Result": "PASS",
"TestID": "MOV-019"
}
],
"snapshot": {
"M0_hall": 0,
"M0_max_hall": 480,
"M0_position": 65535,
"M0_position_valid": false,
"M1_hall": 0,
"M1_max_hall": 374,
"M1_position": 65535,
"M1_position_valid": false,
"M2_hall": 0,
"M2_max_hall": 917,
"M2_position": 65535,
"M2_position_valid": false,
"M3_hall": 0,
"M3_max_hall": 647,
"M3_position": 65535,
"M3_position_valid": false,
"extension_period_check_ok": true,
"frames": {
"0x301": "10 00 01 40 0C 00 00 0B",
"0x302_M0": "00 30 00 00 40 FF FF 00",
"0x302_M1": "01 30 00 00 40 FF FF 00",
"0x302_M2": "02 30 00 00 40 FF FF 00",
"0x302_M3": "03 30 00 00 40 FF FF 00",
"0x303_M0": "00 00 00 00 E0 01 00 C0",
"0x303_M1": "01 00 00 00 76 01 00 C0",
"0x303_M2": "02 00 00 00 95 03 00 C0",
"0x303_M3": "03 00 00 00 87 02 00 B0",
"0x4FA": "00 00 00 00 00 00 00 00"
},
"parsed_301": {
"ack": 0,
"active_mask": 0,
"alive": 11,
"faulted_mask": 0,
"opcode": 12,
"position_valid_mask": 0,
"reject": 0,
"sequence": 16385,
"system_state": 1
},
"periods_ms": {
"0x301": 99.97523809523811,
"0x302_M0": 104.1326,
"0x302_M1": 104.20610000000002,
"0x302_M2": 104.14880000000001,
"0x302_M3": 104.20649999999998,
"0x303_M0": 504.3813333333333,
"0x303_M1": 504.003,
"0x303_M2": 503.91575,
"0x303_M3": 503.6393333333333,
"0x4FA": 110.21105263157894
}
},
"summary": {
"fail": 0,
"pass": 19,
"total": 19
}
}
@@ -0,0 +1,3 @@
TestID,Result,Actual,Notes
CAL-005-M2,FAIL,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 14, 'max_hall': 917, 'speed_x100': 20480}",expected MaxHall near 917 within 641..1192
CAL-005-M3,FAIL,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 12288}",expected MaxHall near 647 within 452..841
1 TestID Result Actual Notes
2 CAL-005-M2 FAIL ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 14, 'max_hall': 917, 'speed_x100': 20480} expected MaxHall near 917 within 641..1192
3 CAL-005-M3 FAIL ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 12288} expected MaxHall near 647 within 452..841
@@ -0,0 +1,111 @@
{
"generated_local": "2026-07-30 02:29:12",
"note": "Logical M0 excluded pending physical-mode3 Hall diagnosis",
"results": [
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 14, 'max_hall': 917, 'speed_x100': 20480}",
"Notes": "expected MaxHall near 917 within 641..1192",
"Result": "FAIL",
"TestID": "CAL-005-M2"
},
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 12288}",
"Notes": "expected MaxHall near 647 within 452..841",
"Result": "FAIL",
"TestID": "CAL-005-M3"
}
],
"summary": {
"fail": 2,
"pass": 0,
"total": 2
},
"trace": [
{
"ack": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 0,
"opcode": 10,
"position_valid_mask": 2,
"reject": 0,
"sequence": 24577,
"system_state": 5
},
"diag": {
"event": 0,
"fault_flags": 8,
"hall": 14,
"max_hall": 917,
"motor": 2,
"speed_x100": 20480
},
"motor": 2,
"payload": [
10,
18,
1,
96,
90,
165,
120,
0
],
"sequence": 24577,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 2,
"position": 65535,
"position_valid": false,
"result": 11,
"sequence": 24577,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 4,
"opcode": 10,
"position_valid_mask": 2,
"reject": 0,
"sequence": 24578,
"system_state": 5
},
"diag": {
"event": 0,
"fault_flags": 8,
"hall": 367,
"max_hall": 647,
"motor": 3,
"speed_x100": 12288
},
"motor": 3,
"payload": [
10,
19,
2,
96,
90,
165,
120,
0
],
"sequence": 24578,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 11,
"sequence": 24578,
"task": 7
}
}
]
}
@@ -0,0 +1,4 @@
TestID,Result,Actual,Notes
CAL-005-M1,PASS,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 24577, 'position': 10000, 'duty': 0} diag={'motor': 1, 'event': 3, 'fault_flags': 0, 'hall': 329, 'max_hall': 329, 'speed_x100': 24576}",expected MaxHall near 374 within 261..486
CAL-005-M2,FAIL,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 72, 'max_hall': 917, 'speed_x100': 0}",expected MaxHall near 917 within 641..1192
CAL-005-M3,FAIL,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24579, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24579, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 32768}",expected MaxHall near 647 within 452..841
1 TestID Result Actual Notes
2 CAL-005-M1 PASS ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 24577, 'position': 10000, 'duty': 0} diag={'motor': 1, 'event': 3, 'fault_flags': 0, 'hall': 329, 'max_hall': 329, 'speed_x100': 24576} expected MaxHall near 374 within 261..486
3 CAL-005-M2 FAIL ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 72, 'max_hall': 917, 'speed_x100': 0} expected MaxHall near 917 within 641..1192
4 CAL-005-M3 FAIL ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24579, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24579, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 32768} expected MaxHall near 647 within 452..841
@@ -0,0 +1,160 @@
{
"generated_local": "2026-07-30 02:02:57",
"note": "Logical M0 excluded pending physical-mode3 Hall diagnosis",
"results": [
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 24577, 'position': 10000, 'duty': 0} diag={'motor': 1, 'event': 3, 'fault_flags': 0, 'hall': 329, 'max_hall': 329, 'speed_x100': 24576}",
"Notes": "expected MaxHall near 374 within 261..486",
"Result": "PASS",
"TestID": "CAL-005-M1"
},
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24578, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0} status={'motor': 2, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24578, 'position': 65535, 'duty': 0} diag={'motor': 2, 'event': 0, 'fault_flags': 8, 'hall': 72, 'max_hall': 917, 'speed_x100': 0}",
"Notes": "expected MaxHall near 917 within 641..1192",
"Result": "FAIL",
"TestID": "CAL-005-M2"
},
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24579, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 2, 'faulted_mask': 4} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24579, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 367, 'max_hall': 647, 'speed_x100': 32768}",
"Notes": "expected MaxHall near 647 within 452..841",
"Result": "FAIL",
"TestID": "CAL-005-M3"
}
],
"summary": {
"fail": 2,
"pass": 1,
"total": 3
},
"trace": [
{
"ack": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 10,
"position_valid_mask": 0,
"reject": 0,
"sequence": 24577,
"system_state": 5
},
"diag": {
"event": 3,
"fault_flags": 0,
"hall": 329,
"max_hall": 329,
"motor": 1,
"speed_x100": 24576
},
"motor": 1,
"payload": [
10,
17,
1,
96,
90,
165,
120,
0
],
"sequence": 24577,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 1,
"position": 10000,
"position_valid": true,
"result": 2,
"sequence": 24577,
"task": 6
}
},
{
"ack": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 0,
"opcode": 10,
"position_valid_mask": 2,
"reject": 0,
"sequence": 24578,
"system_state": 5
},
"diag": {
"event": 0,
"fault_flags": 8,
"hall": 72,
"max_hall": 917,
"motor": 2,
"speed_x100": 0
},
"motor": 2,
"payload": [
10,
18,
2,
96,
90,
165,
120,
0
],
"sequence": 24578,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 2,
"position": 65535,
"position_valid": false,
"result": 11,
"sequence": 24578,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 4,
"opcode": 10,
"position_valid_mask": 2,
"reject": 0,
"sequence": 24579,
"system_state": 5
},
"diag": {
"event": 0,
"fault_flags": 8,
"hall": 367,
"max_hall": 647,
"motor": 3,
"speed_x100": 32768
},
"motor": 3,
"payload": [
10,
19,
3,
96,
90,
165,
120,
0
],
"sequence": 24579,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 11,
"sequence": 24579,
"task": 7
}
}
]
}
@@ -0,0 +1,2 @@
TestID,Result,ElapsedSeconds,StartFeedback,FinalFeedback,Notes
HEAT-006,PASS,600.007,11 00 00 00 00 00 00 00,01 00 00 00 00 00 00 00,600 s +/-1 s timeout and heater feedback safe-off
1 TestID Result ElapsedSeconds StartFeedback FinalFeedback Notes
2 HEAT-006 PASS 600.007 11 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 600 s +/-1 s timeout and heater feedback safe-off
@@ -0,0 +1,17 @@
{
"generated_local": "2026-07-30 08:22:57",
"result": {
"ElapsedSeconds": "600.007",
"FinalFeedback": "01 00 00 00 00 00 00 00",
"Notes": "600 s +/-1 s timeout and heater feedback safe-off",
"Result": "PASS",
"StartFeedback": "11 00 00 00 00 00 00 00",
"TestID": "HEAT-006"
},
"scope_note": "CAN timeout and feedback are verified; physical P16.7 PWM duty still requires an oscilloscope.",
"summary": {
"fail": 0,
"pass": 1,
"total": 1
}
}
@@ -0,0 +1,9 @@
TestID,Result,Actual,Notes
MOV-009,FAIL,before=0 anchorNear=0 far=0 near=0,FAR must increment Hall after near anchoring
MOV-010,FAIL,before=0 anchorNear=0 far=0 near=0,NEAR must decrement Hall after FAR jog
MOV-011,FAIL,before=0 anchorNear=0 far=0 near=0,FAR must increment Hall after near anchoring
MOV-012,FAIL,before=0 anchorNear=0 far=0 near=0,NEAR must decrement Hall after FAR jog
MOV-013,FAIL,before=0 anchorNear=0 far=0 near=0,FAR must increment Hall after near anchoring
MOV-014,FAIL,before=0 anchorNear=0 far=0 near=0,NEAR must decrement Hall after FAR jog
MOV-015,FAIL,before=0 anchorNear=0 far=0 near=0,FAR must increment Hall after near anchoring
MOV-016,FAIL,before=0 anchorNear=0 far=0 near=0,NEAR must decrement Hall after FAR jog
1 TestID Result Actual Notes
2 MOV-009 FAIL before=0 anchorNear=0 far=0 near=0 FAR must increment Hall after near anchoring
3 MOV-010 FAIL before=0 anchorNear=0 far=0 near=0 NEAR must decrement Hall after FAR jog
4 MOV-011 FAIL before=0 anchorNear=0 far=0 near=0 FAR must increment Hall after near anchoring
5 MOV-012 FAIL before=0 anchorNear=0 far=0 near=0 NEAR must decrement Hall after FAR jog
6 MOV-013 FAIL before=0 anchorNear=0 far=0 near=0 FAR must increment Hall after near anchoring
7 MOV-014 FAIL before=0 anchorNear=0 far=0 near=0 NEAR must decrement Hall after FAR jog
8 MOV-015 FAIL before=0 anchorNear=0 far=0 near=0 FAR must increment Hall after near anchoring
9 MOV-016 FAIL before=0 anchorNear=0 far=0 near=0 NEAR must decrement Hall after FAR jog
@@ -0,0 +1,480 @@
{
"generated_local": "2026-07-30 06:12:23",
"results": [
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "FAR must increment Hall after near anchoring",
"Result": "FAIL",
"TestID": "MOV-009"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "NEAR must decrement Hall after FAR jog",
"Result": "FAIL",
"TestID": "MOV-010"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "FAR must increment Hall after near anchoring",
"Result": "FAIL",
"TestID": "MOV-011"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "NEAR must decrement Hall after FAR jog",
"Result": "FAIL",
"TestID": "MOV-012"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "FAR must increment Hall after near anchoring",
"Result": "FAIL",
"TestID": "MOV-013"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "NEAR must decrement Hall after FAR jog",
"Result": "FAIL",
"TestID": "MOV-014"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "FAR must increment Hall after near anchoring",
"Result": "FAIL",
"TestID": "MOV-015"
},
{
"Actual": "before=0 anchorNear=0 far=0 near=0",
"Notes": "NEAR must decrement Hall after FAR jog",
"Result": "FAIL",
"TestID": "MOV-016"
}
],
"summary": {
"fail": 8,
"pass": 0,
"total": 8
},
"trace": [
{
"ack": {
"ack": 1,
"active_mask": 1,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20481,
"system_state": 2
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 480,
"motor": 0,
"speed_x100": 12288
},
"direction": 2,
"motor": 0,
"payload": "03 18 01 50 D0 07 F4 01",
"sequence": 20481,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 0,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20481,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 1,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20482,
"system_state": 2
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 480,
"motor": 0,
"speed_x100": 0
},
"direction": 1,
"motor": 0,
"payload": "03 14 02 50 D0 07 F4 01",
"sequence": 20482,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 0,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20482,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 1,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20483,
"system_state": 2
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 480,
"motor": 0,
"speed_x100": 57344
},
"direction": 2,
"motor": 0,
"payload": "03 18 03 50 D0 07 F4 01",
"sequence": 20483,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 0,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20483,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 1,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20484,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 329,
"motor": 1,
"speed_x100": 28672
},
"direction": 2,
"motor": 1,
"payload": "03 19 04 50 D0 07 F4 01",
"sequence": 20484,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 1,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20484,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 1,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20485,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 329,
"motor": 1,
"speed_x100": 24576
},
"direction": 1,
"motor": 1,
"payload": "03 15 05 50 D0 07 F4 01",
"sequence": 20485,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 1,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20485,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 1,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20486,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 329,
"motor": 1,
"speed_x100": 16384
},
"direction": 2,
"motor": 1,
"payload": "03 19 06 50 D0 07 F4 01",
"sequence": 20486,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 1,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20486,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 3,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20487,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 917,
"motor": 2,
"speed_x100": 45056
},
"direction": 2,
"motor": 2,
"payload": "03 1A 07 50 D0 07 F4 01",
"sequence": 20487,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 2,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20487,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 3,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20488,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 917,
"motor": 2,
"speed_x100": 40960
},
"direction": 1,
"motor": 2,
"payload": "03 16 08 50 D0 07 F4 01",
"sequence": 20488,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 2,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20488,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 3,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20489,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 917,
"motor": 2,
"speed_x100": 32768
},
"direction": 2,
"motor": 2,
"payload": "03 1A 09 50 D0 07 F4 01",
"sequence": 20489,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 2,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20489,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 7,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20490,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 367,
"motor": 3,
"speed_x100": 12288
},
"direction": 2,
"motor": 3,
"payload": "03 1B 0A 50 D0 07 F4 01",
"sequence": 20490,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20490,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 7,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20491,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 367,
"motor": 3,
"speed_x100": 8192
},
"direction": 1,
"motor": 3,
"payload": "03 17 0B 50 D0 07 F4 01",
"sequence": 20491,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20491,
"task": 7
}
},
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 7,
"opcode": 3,
"position_valid_mask": 0,
"reject": 0,
"sequence": 20492,
"system_state": 4
},
"diag": {
"event": 0,
"fault_flags": 1,
"hall": 0,
"max_hall": 367,
"motor": 3,
"speed_x100": 0
},
"direction": 2,
"motor": 3,
"payload": "03 1B 0C 50 D0 07 F4 01",
"sequence": 20492,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 5,
"sequence": 20492,
"task": 7
}
}
]
}
@@ -0,0 +1,4 @@
TestID,Result,Actual,Notes
CAL-005-M1-RERUN,FAIL,"{'ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 29697, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29697, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 329}}",repeatable M1 range must remain 329 +/-11 Hall
MOV-030-RERUN,FAIL,not run,blocked by failed M1 recalibration
MOV-011/012-M1-95PCT-DIAG,FAIL,"{'far': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29699, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29699, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'FAR'}, 'near': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29700, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29700, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'NEAR'}}",3 s each way at commanded 95% must change Hall
1 TestID Result Actual Notes
2 CAL-005-M1-RERUN FAIL {'ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 29697, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29697, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 329}} repeatable M1 range must remain 329 +/-11 Hall
3 MOV-030-RERUN FAIL not run blocked by failed M1 recalibration
4 MOV-011/012-M1-95PCT-DIAG FAIL {'far': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29699, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29699, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'FAR'}, 'near': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29700, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29700, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'NEAR'}} 3 s each way at commanded 95% must change Hall
@@ -0,0 +1,28 @@
{
"generated_local": "2026-07-30 03:35:26",
"results": [
{
"Actual": "{'ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 29697, 'opcode': 10, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29697, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 329}}",
"Notes": "repeatable M1 range must remain 329 +/-11 Hall",
"Result": "FAIL",
"TestID": "CAL-005-M1-RERUN"
},
{
"Actual": "not run",
"Notes": "blocked by failed M1 recalibration",
"Result": "FAIL",
"TestID": "MOV-030-RERUN"
},
{
"Actual": "{'far': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29699, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29699, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'FAR'}, 'near': {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29700, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 0}, 'status': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 29700, 'position': 65535, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 0, 'max_hall': 329}, 'direction': 'NEAR'}}",
"Notes": "3 s each way at commanded 95% must change Hall",
"Result": "FAIL",
"TestID": "MOV-011/012-M1-95PCT-DIAG"
}
],
"summary": {
"fail": 3,
"pass": 0,
"total": 3
}
}
@@ -0,0 +1,2 @@
TestID,Result,Actual,Notes
CAL-005-M3,FAIL,"ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 367, 'speed_x100': 8192}",expected MaxHall near 367 within 356..378
1 TestID Result Actual Notes
2 CAL-005-M3 FAIL ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 367, 'speed_x100': 8192} expected MaxHall near 367 within 356..378
@@ -0,0 +1,62 @@
{
"generated_local": "2026-07-30 05:10:23",
"note": "M3 focused regression after default range update to 367",
"results": [
{
"Actual": "ack={'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 24577, 'opcode': 10, 'active_mask': 8, 'position_valid_mask': 0, 'faulted_mask': 0} status={'motor': 3, 'task': 7, 'result': 11, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 24577, 'position': 65535, 'duty': 0} diag={'motor': 3, 'event': 0, 'fault_flags': 8, 'hall': 0, 'max_hall': 367, 'speed_x100': 8192}",
"Notes": "expected MaxHall near 367 within 356..378",
"Result": "FAIL",
"TestID": "CAL-005-M3"
}
],
"summary": {
"fail": 1,
"pass": 0,
"total": 1
},
"trace": [
{
"ack": {
"ack": 1,
"active_mask": 8,
"faulted_mask": 0,
"opcode": 10,
"position_valid_mask": 0,
"reject": 0,
"sequence": 24577,
"system_state": 5
},
"diag": {
"event": 0,
"fault_flags": 8,
"hall": 0,
"max_hall": 367,
"motor": 3,
"speed_x100": 8192
},
"motor": 3,
"payload": [
10,
19,
1,
96,
90,
165,
120,
0
],
"sequence": 24577,
"status": {
"active": false,
"direction": 0,
"duty": 0,
"motor": 3,
"position": 65535,
"position_valid": false,
"result": 11,
"sequence": 24577,
"task": 7
}
}
]
}
@@ -0,0 +1,8 @@
TestID,Result,Actual,Notes
MOV-008,PASS,"{'ack': 2, 'system_state': 1, 'reject': 7, 'sequence': 256, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}",M0 absolute command must be rejected while PositionValid=0
CAL-004,PASS,"{'calibration_ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 28672, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}}",STOP_ALL during M2 calibration must kill motion and not report OK
MOV-020,PASS,"{'start': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28672, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}}",old-sequence STOP_ALL remains an unconditional barrier
MOV-025,PASS,"{'starts': [{'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28929, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28930, 'opcode': 3, 'active_mask': 6, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}], 'active': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop_ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}, 'M2_status': {'motor': 2, 'task': 0, 'result': 3, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 28932, 'position': 65535, 'duty': 0}, 'remaining': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}}",three-axis subset: STOP_MOTOR M2 leaves M1/M3 active
MOV-026,PASS,"{'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 28933, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}",STOP_ALL clears every active axis
MOV-027-CAN,PASS,"{'first': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29185, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'reverse': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29186, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29186, 'position': 9149, 'duty': 0}}",CAN replacement passed; electrical deadtime still needs scope
MOV-030,FAIL,"{'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29441, 'opcode': 1, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29441, 'position': 5623, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 185, 'max_hall': 329}}",M1 must reach normalized 50% / about 165 of 329 Hall
1 TestID Result Actual Notes
2 MOV-008 PASS {'ack': 2, 'system_state': 1, 'reject': 7, 'sequence': 256, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0} M0 absolute command must be rejected while PositionValid=0
3 CAL-004 PASS {'calibration_ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 28672, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}} STOP_ALL during M2 calibration must kill motion and not report OK
4 MOV-020 PASS {'start': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28672, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}} old-sequence STOP_ALL remains an unconditional barrier
5 MOV-025 PASS {'starts': [{'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28929, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28930, 'opcode': 3, 'active_mask': 6, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}], 'active': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop_ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}, 'M2_status': {'motor': 2, 'task': 0, 'result': 3, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 28932, 'position': 65535, 'duty': 0}, 'remaining': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}} three-axis subset: STOP_MOTOR M2 leaves M1/M3 active
6 MOV-026 PASS {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 28933, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0} STOP_ALL clears every active axis
7 MOV-027-CAN PASS {'first': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29185, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'reverse': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29186, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29186, 'position': 9149, 'duty': 0}} CAN replacement passed; electrical deadtime still needs scope
8 MOV-030 FAIL {'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29441, 'opcode': 1, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29441, 'position': 5623, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 185, 'max_hall': 329}} M1 must reach normalized 50% / about 165 of 329 Hall
@@ -0,0 +1,314 @@
{
"generated_local": "2026-07-30 03:07:44",
"results": [
{
"Actual": "{'ack': 2, 'system_state': 1, 'reject': 7, 'sequence': 256, 'opcode': 1, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}",
"Notes": "M0 absolute command must be rejected while PositionValid=0",
"Result": "PASS",
"TestID": "MOV-008"
},
{
"Actual": "{'calibration_ack': {'ack': 1, 'system_state': 5, 'reject': 0, 'sequence': 28672, 'opcode': 10, 'active_mask': 4, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}}",
"Notes": "STOP_ALL during M2 calibration must kill motion and not report OK",
"Result": "PASS",
"TestID": "CAL-004"
},
{
"Actual": "{'start': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28672, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop': {'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 1, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}}",
"Notes": "old-sequence STOP_ALL remains an unconditional barrier",
"Result": "PASS",
"TestID": "MOV-020"
},
{
"Actual": "{'starts': [{'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28929, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28930, 'opcode': 3, 'active_mask': 6, 'position_valid_mask': 2, 'faulted_mask': 0}, {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}], 'active': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28931, 'opcode': 3, 'active_mask': 14, 'position_valid_mask': 2, 'faulted_mask': 0}, 'stop_ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}, 'M2_status': {'motor': 2, 'task': 0, 'result': 3, 'direction': 0, 'active': False, 'position_valid': False, 'sequence': 28932, 'position': 65535, 'duty': 0}, 'remaining': {'ack': 0, 'system_state': 2, 'reject': 0, 'sequence': 28932, 'opcode': 8, 'active_mask': 10, 'position_valid_mask': 2, 'faulted_mask': 0}}",
"Notes": "three-axis subset: STOP_MOTOR M2 leaves M1/M3 active",
"Result": "PASS",
"TestID": "MOV-025"
},
{
"Actual": "{'ack': 4, 'system_state': 1, 'reject': 0, 'sequence': 28933, 'opcode': 9, 'active_mask': 0, 'position_valid_mask': 2, 'faulted_mask': 0}",
"Notes": "STOP_ALL clears every active axis",
"Result": "PASS",
"TestID": "MOV-026"
},
{
"Actual": "{'first': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29185, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'reverse': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29186, 'opcode': 3, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 6, 'result': 2, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29186, 'position': 9149, 'duty': 0}}",
"Notes": "CAN replacement passed; electrical deadtime still needs scope",
"Result": "PASS",
"TestID": "MOV-027-CAN"
},
{
"Actual": "{'ack': {'ack': 1, 'system_state': 2, 'reject': 0, 'sequence': 29441, 'opcode': 1, 'active_mask': 2, 'position_valid_mask': 2, 'faulted_mask': 0}, 'terminal': {'motor': 1, 'task': 7, 'result': 5, 'direction': 0, 'active': False, 'position_valid': True, 'sequence': 29441, 'position': 5623, 'duty': 0}, 'diag': {'motor': 1, 'event': 0, 'fault_flags': 1, 'hall': 185, 'max_hall': 329}}",
"Notes": "M1 must reach normalized 50% / about 165 of 329 Hall",
"Result": "FAIL",
"TestID": "MOV-030"
}
],
"summary": {
"fail": 1,
"pass": 6,
"total": 7
},
"trace": [
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "01 10 00 01 88 13 40 1F",
"response": {
"ack": 2,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 1,
"position_valid_mask": 2,
"reject": 7,
"sequence": 256,
"system_state": 1
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "0A 12 00 70 5A A5 78 00",
"response": {
"ack": 1,
"active_mask": 4,
"faulted_mask": 0,
"opcode": 10,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28672,
"system_state": 5
}
},
{
"payload": "09 10 01 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 1,
"system_state": 1
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "03 19 00 70 B8 0B F4 01",
"response": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28672,
"system_state": 2
}
},
{
"payload": "09 10 01 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 1,
"system_state": 1
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "03 19 01 71 88 13 F4 01",
"response": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28929,
"system_state": 2
}
},
{
"payload": "03 1A 02 71 88 13 F4 01",
"response": {
"ack": 1,
"active_mask": 6,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28930,
"system_state": 2
}
},
{
"payload": "03 1B 03 71 88 13 F4 01",
"response": {
"ack": 1,
"active_mask": 14,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28931,
"system_state": 2
}
},
{
"payload": "08 12 04 71 00 00 00 00",
"response": {
"ack": 1,
"active_mask": 10,
"faulted_mask": 0,
"opcode": 8,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28932,
"system_state": 2
}
},
{
"payload": "09 10 05 71 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 28933,
"system_state": 1
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "03 15 01 72 B8 0B F4 01",
"response": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 29185,
"system_state": 2
}
},
{
"payload": "03 19 02 72 B0 04 F4 01",
"response": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 3,
"position_valid_mask": 2,
"reject": 0,
"sequence": 29186,
"system_state": 2
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
},
{
"payload": "01 11 01 73 88 13 40 1F",
"response": {
"ack": 1,
"active_mask": 2,
"faulted_mask": 0,
"opcode": 1,
"position_valid_mask": 2,
"reject": 0,
"sequence": 29441,
"system_state": 2
}
},
{
"payload": "09 10 00 00 00 00 00 00",
"response": {
"ack": 4,
"active_mask": 0,
"faulted_mask": 0,
"opcode": 9,
"position_valid_mask": 2,
"reject": 0,
"sequence": 0,
"system_state": 1
}
}
]
}
@@ -0,0 +1,51 @@
# Seat_Controller_demo 实物 HIL 调试状态
更新时间:2026-07-30(无标定、无示波器测试计划更新)
## 最终构建与总线状态
- IAR EWARM 9.40.1,`rev_d`:0 Errors,0 Warnings。最新HEX大小156373 bytes,SHA-256=`B718BB23B894C010A18E2006A07380389700C743642E5F4C6BB57874A7709295`。
- 最新同号映射/SMV8镜像构建完成;开始新一轮实物测试前应核对台架中烧录的确为该镜像,当前文档不把“已烧录”作为已确认项。
- ZCANPRO:USBCANFD-200U 设备0、通道0、500 kbit/s。
- ZCANPRO 错误帧计数:59;本轮最终回归前后没有增加。
- `0x303` 最终默认 MaxHall:M0=480、M1=374、M2=917、M3=647。
## 已完成自动测试
| 测试组 | 结果 | 说明 |
|---|---:|---|
| 基础 CAN/安全协议 | 19/19 PASS | 命令 ACK、拒绝码、序号重放/冲突、坏 DLC 安全关断及周期反馈均符合规范 |
| 通风/加热短矩阵 | 10/10 PASS | 风扇 1/2/3 挡、越界钳位、开关关闭、加热模式/挡位组合及反馈正确 |
| 加热 600 s 超时 | 1/1 PASS | 600.007 s 自动安全关闭,`0x4FA` 加热字段归零 |
| 周期反馈 | PASS | `0x301`≈100 ms,四个 `0x302` 轮询项≈104 ms,四个 `0x303` 轮询项≈504 ms,`0x4FA`≈110 ms |
| 移动协议层 | PASS | 点动/停止命令均得到正确 ACK;位置无效时绝对位置命令正确拒绝 |
## 已修复软件问题
1. 加热超时计时偏晚:原实现按主循环次数累计 1 ms,主循环实际周期大于 1 ms。已改为使用 `Motor_GetTimeMs()` 单调硬件时基,600 s 实测为 600.007 s。
2. 量程参数和通道语义错误:实测 physical Mode0/1/2/3 分别为水平480、坐盆374、靠背917、腿托647;工程旧映射把 M0/M1/M3 交换。现已改为 MotorID 与 physical Mode 同号映射。
3. 为防止旧绝对位置按错误通道恢复,Work Flash 双槽记录升级到 SMV8;SMV5/SMV6 旧量程记录和 SMV7 旧通道映射记录均只视为不兼容,不直接恢复位置。已增加 `HOME_KNOWN_RANGE(0x0B)`,可使用确认量程仅回最近端建立绝对原点。
4. 四个量程入口均改为“寻近端归零→寻远端机械端点→记录真实 Hall”,不再使用旧 1020 mm、85°、90°目标。
5. 旧已知量程归零固定使用50% Duty,并把启动宽限后无Hall直接当成端点。实测强制归零中M1 `374→0`、M2 `742→0`、M3 `312→0`均没有任何中间Hall,分别约1.6/1.6/2.6 s后被错误清零;只有M0真实完成480→0。现改为90% Duty;端点活动改用Hall双边沿且要求500 ms零边沿,第一次近端行程少于4个边沿时执行“远端4边沿验证→再次寻近端→往返边沿对称校验(容差1)”。无法证明运动则失败且PositionValid=0。桥接同时拒绝初始Hall>2却直接跳0的报告,并记录归零方向阶段以区分离端验证失败和回程失败。
## Hall 链路结论(已解除旧阻塞)
- 之前四路 Hall 固定高的观测来自霍尔线接触不稳,不能继续作为当前阻塞结论;重插后最新量程日志已证明四路均能连续计数。
- Mode0:PASS,480 Hall;Mode3最终确认量程为647 Hall,旧日志中的约641只作历史记录,不再替代当前基线。
- Mode1:到真实远端373 Hall 后约500 ms无新脉冲,旧脚本仍按790 Hall/85°等待,因此报告 STALL。电机和 Hall 均已工作,错误是旧量程目标不匹配。
- Mode2 同理:真实远端约918 Hall,旧脚本按1590 Hall/90°等待并误报 STALL。
- STALL 的“无 Hall 安全窗”无需放宽;量程测试应把寻远端时的机械端点静止判为 `FAR_ENDPOINT`。四轴预编译固件已经按此逻辑重建,全部0 error/0 warning。
## 当前剩余计划
不再运行四轴标定/25%、50%、75%定位,也不在缺少示波器时运行波形刺激。当前剩余3组必做测试:
1. 完整功能与配置模式:`04_can_full_function.py`完成CAN/实物冒烟;`05_seat_function_bridge.py`配合Windows选择界面执行配置表12个场景及10个可选热管理功能。
2. 正常掉电恢复:`02_power_memory.py`默认`PHASE="CLEAN"`,验证SMV8恢复且不使位置失效。
3. 协议保护:`03_protection_faults.py`默认`PHASE="AUTO"`,不依赖示波器或故障注入器。
最新手工脚本日志显示:`04_can_full_function.py`移动阶段10/10 PASS,四轴均完成约12%往返且使用90% Duty;`02_power_memory.py`旧日志仍因当时PositionValidMask=0而未执行;`03_protection_faults.py`协议保护6/6 PASS。配置模式已能发送目标,但零重力场景中M1在372/374 Hall、9947/10000处被旧逻辑误判STALL,M3在311/647处5秒无Hall后TIMEOUT,故该场景未通过。
本次固件和脚本保留“仅失效轴回最近端→Hall=0→保存SMV8→继续绝对目标”的自动恢复,并新增“忽略Mask、强制四轴真实归零并重建SMV8”。界面会直接显示四轴归一化位置、物理换算、Hall/MaxHall和端点,明确PositionValid不等于最近端。固件对位置任务增加±2 Hall目标到达容差,修正M1机械端部已到目标附近却锁存STALL的问题;M3的311/647仍超出容差,必须复测确认机械阻挡、负载/电源或Hall链路,不能伪判PASS。烧录新HEX后必做配置表12场景和正常掉电恢复,并建议复跑04移动阶段。运动中掉电仍是可选破坏性用例。Bus-Off、加热ADC、DRV8718故障和人工堵转在缺少相应安全注入条件时记为BLOCKED。PWM频率/占空比、换向死区和硬实时关断延迟在无示波器条件下也记为BLOCKED。
最新版配置表包含11种模式/12个场景:小憩模式只保留1个标准场景,新增“小腿解压悬浮模式”,轻松进出模式仍含2个场景。办公模式M0已修正为约346 Hall;当前剩余冲突为女王副驾(行驶)M3的22°应约198 Hall(原表178)。界面保留原表值并标红,但发送以物理目标重算的归一化值。
@@ -0,0 +1,11 @@
TestID,Result,Actual,Notes
THM-001,PASS,09 00 00 00 00 00 00 00,expected Byte0=09; fan and heater off
FAN-001,PASS,0A 00 00 00 00 00 00 00,expected Byte0=0A; gear1 feedback; 2 s startup boost is firmware-timed
FAN-002,PASS,0B 00 00 00 00 00 00 00,expected Byte0=0B; gear2 feedback
FAN-003,PASS,0C 00 00 00 00 00 00 00,expected Byte0=0C; gear3 feedback
FAN-004,PASS,0C 00 00 00 00 00 00 00,expected Byte0=0C; vehicle gear5 clamps to physical3
FAN-005,PASS,09 00 00 00 00 00 00 00,expected Byte0=09; fan switch off
THM-002,PASS,09 00 00 00 00 00 00 00,expected Byte0=09; max fan/heater level configured to2
THM-003,PASS,09 00 00 00 00 00 00 00,expected Byte0=09; valid heater mode; outputs stay off
THM-004,PASS,1B 00 00 00 00 00 00 00,expected Byte0=1B; both requested level3 and clamped to physical2
HEAT-001,PASS,09 00 00 00 00 00 00 00,expected Byte0=09; mode accepted without starting heater
1 TestID Result Actual Notes
2 THM-001 PASS 09 00 00 00 00 00 00 00 expected Byte0=09; fan and heater off
3 FAN-001 PASS 0A 00 00 00 00 00 00 00 expected Byte0=0A; gear1 feedback; 2 s startup boost is firmware-timed
4 FAN-002 PASS 0B 00 00 00 00 00 00 00 expected Byte0=0B; gear2 feedback
5 FAN-003 PASS 0C 00 00 00 00 00 00 00 expected Byte0=0C; gear3 feedback
6 FAN-004 PASS 0C 00 00 00 00 00 00 00 expected Byte0=0C; vehicle gear5 clamps to physical3
7 FAN-005 PASS 09 00 00 00 00 00 00 00 expected Byte0=09; fan switch off
8 THM-002 PASS 09 00 00 00 00 00 00 00 expected Byte0=09; max fan/heater level configured to2
9 THM-003 PASS 09 00 00 00 00 00 00 00 expected Byte0=09; valid heater mode; outputs stay off
10 THM-004 PASS 1B 00 00 00 00 00 00 00 expected Byte0=1B; both requested level3 and clamped to physical2
11 HEAT-001 PASS 09 00 00 00 00 00 00 00 expected Byte0=09; mode accepted without starting heater
@@ -0,0 +1,71 @@
{
"generated_local": "2026-07-30 09:34:50",
"results": [
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09; fan and heater off",
"Result": "PASS",
"TestID": "THM-001"
},
{
"Actual": "0A 00 00 00 00 00 00 00",
"Notes": "expected Byte0=0A; gear1 feedback; 2 s startup boost is firmware-timed",
"Result": "PASS",
"TestID": "FAN-001"
},
{
"Actual": "0B 00 00 00 00 00 00 00",
"Notes": "expected Byte0=0B; gear2 feedback",
"Result": "PASS",
"TestID": "FAN-002"
},
{
"Actual": "0C 00 00 00 00 00 00 00",
"Notes": "expected Byte0=0C; gear3 feedback",
"Result": "PASS",
"TestID": "FAN-003"
},
{
"Actual": "0C 00 00 00 00 00 00 00",
"Notes": "expected Byte0=0C; vehicle gear5 clamps to physical3",
"Result": "PASS",
"TestID": "FAN-004"
},
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09; fan switch off",
"Result": "PASS",
"TestID": "FAN-005"
},
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09; max fan/heater level configured to2",
"Result": "PASS",
"TestID": "THM-002"
},
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09; valid heater mode; outputs stay off",
"Result": "PASS",
"TestID": "THM-003"
},
{
"Actual": "1B 00 00 00 00 00 00 00",
"Notes": "expected Byte0=1B; both requested level3 and clamped to physical2",
"Result": "PASS",
"TestID": "THM-004"
},
{
"Actual": "09 00 00 00 00 00 00 00",
"Notes": "expected Byte0=09; mode accepted without starting heater",
"Result": "PASS",
"TestID": "HEAT-001"
}
],
"scope_note": "CAN control/feedback only; PWM duty needs oscilloscope",
"summary": {
"fail": 0,
"pass": 10,
"total": 10
}
}
@@ -1,12 +1,14 @@
# Seat_Controller_demo
`Seat_Controller_demo` 是 `CYT4BFBCJE` / `CY_MCU_rev_d` / Cortex-M0+ 的单板统一座椅控制器工程。它以已验证的 `Seat_Move_demo` 为安全和四电机骨架,并合入 `Seat_Backrest_Heater_demo` 与 `Seat_Fan_demo` 的 PWM/ADC 驱动和 CAN 控制语义。
`Seat_Controller_demo` 是 `CYT4BFBCJE` / `CY_MCU_rev_d` / Cortex-M0+ Flash 的单板统一座椅控制器工程,集成座椅通风、靠背加热和四电机移动。SDL 公共源码未修改,应用配置和功能代码均位于本工程目录。
## 单板资源
- 共享 CAN4:RX=P3.7、TX=P3.6、收发器/DVDD 使能=P3.1,高电平有效。
- 通风:P11.1 / TCPWM1-60;P11.2 保持 GPIO 低。
- 靠背加热:P16.7 / TCPWM1-65;电流诊断 P12.2 / SAR1 CH6。
- 通风电流:FSDY-AD=P12.6 / SAR1 CH10。
- 四轴电流:M0=P10.6/CH2、M1=P12.1/CH5、M2=P10.7/CH3、M3=P10.5/CH1。六路电流信号统一由 PASS0 SAR1 初始化,避免模块间反初始化。
- 四电机与 Hall:沿用 `Seat_Move_demo` 的 DRV8718、TCPWM1-12/13/14/19/20 和四路 Hall 资源。
- 资源分频:风扇=divider0、SCB4=divider1、电机=divider2、加热=divider3、ADC=divider4;各已分离。
@@ -17,17 +19,40 @@
- RX `0x29E/0x2AA/0x3CE/0x42A`:通风和加热。
- RX `0x300`:四电机命令。
- TX `0x4FA`:byte0 bits0..2 通风状态,bits3..5 加热状态,合成在同一帧中。
- TX `0x301/0x302/0x303`:移动 ACK、状态和诊断。
- TX `0x301/0x302/0x303`:移动 ACK、状态和 Hall 诊断。
- TX `0x304`:四轴电流诊断;B0 MotorID,B1 bits0..4依次为采样有效/零点有效/高电流/低Hall/标定中,B2..3 ADC raw,B4..5停止态零点raw,B6..7该轴堵转电流差值阈值raw。
- TX `0x305`:热管理电流诊断;B0采样有效标志,B1输出状态,B2..3通风电流raw,B4..5靠背加热电流raw,B6/B7为两路ADC累计错误计数(饱和到255)。
CAN 为 500 kbit/s、经典标准帧、11-bit ID、DLC 8。`seat_move_application.c` 只把 `0x300` 交给移动协议,其余已过滤热管理帧交给 `seat_controller_thermal.c`,避免多个模块争抢同一 CAN 外设。
CAN 为 500 kbit/s、经典标准帧、11-bit ID、DLC 8。移动协议版本为 1;支持 `MOVE_ABSOLUTE`、`MOVE_RELATIVE`、`MOVE_TIMED`、`STOP_MOTOR`、`STOP_ALL`、`CALIBRATE_RANGE`、`HOME_KNOWN_RANGE`、`QUERY_STATUS` 和 `CLEAR_FAULT`。四轴定义及完整字节布局见 `Docs/outputs/seat_move_four_motor_20260724/座椅四电机移动功能CAN矩阵表.xlsx`。
2026-07-30 四个独立量程入口已验证 CAN MotorID 与板级物理 Mode 同号映射:M0/physical0 水平前后、M1/physical1 坐盆上下、M2/physical2 靠背前后、M3/physical3 腿托上下。首次测试仍须目视确认没有错误执行机构动作。
## 2026-07-30 实车量程
| 逻辑轴 | 0% 最近端 | 100% 最远端 | MaxHallCount | 物理全量程 | 分辨率 |
|---|---|---|---:|---:|---:|
| M0 水平 | 座椅最前方 | 座椅最后方 | 480 | 215 mm | 0.4479 mm/Hall |
| M1 坐盆 | 坐盆最上方 | 坐盆斜向下方 | 374 | 水平 51 mm、垂直 56 mm | 水平 0.1364、垂直 0.1497 mm/Hall |
| M2 靠背 | 靠背最前方(垂直地面) | 向后躺倒 | 917 | 68° | 0.0742°/Hall |
| M3 腿托 | 腿托最下方(垂直地面) | 向上抬起 | 647 | 72° | 0.1113°/Hall |
`0x302` 报告 0~10000 的绝对归一化位置,`0x303` 报告当前/最大 Hall。物理绝对位置由同一归一化比例换算:M1 同时得到水平与垂直分量。CAN MotorID 0~3 与实测物理 Mode 0~3 同号对应。`PositionValidMask`只表示ECU位置记录可信,不表示座椅位于最近端。首次使用 SMV8 固件或存储无效时不能仅凭默认最大 Hall 推断当前位置;本台架量程已确认,可用 `HOME_KNOWN_RANGE(0x0B)` 将失效轴移动到最近端建立 Hall=0,再执行绝对目标,无需重新测量最远端。如果座椅在ECU掉电时被人工改变或实际位置不可信,即使Mask为0xF也必须真实执行回最近端,不能只修改有效位。
## 安全行为
- 启动最早阶段把电机、P11.1/P11.2 和 P16.7 强制为 GPIO 低。
- 任何热/风输出初始化或运行故障都会关断热/风并锁存全局 driver fault;移动也进入 inhibit。
- 移动保留 `STOP_ALL`、sequence、堵转、端点、超时、Work Flash 和 WDT 机制。
- 四电机使用独立任务、Sequence、Hall、超时和故障上下文;同轴换向先全关并等待死区,不同轴命令不会互相取消。
- 四轴分别具有启动宽限(M0/M1/M2=1 s,M3=2 s)。普通移动仅在“500 ms Hall双边沿窗口不超过2个边沿”且“相对停止态零点的轴电流超过独立阈值并持续200 ms”同时成立时报告 `STALL`;低Hall但电流低或采样过期时不误报堵转,连续5 s无运动后关断并报告 `TIMEOUT`。
- 四轴普通绝对/相对移动默认使用90% PWM占空比;堵转电流差值阈值在重新带载标定前统一使用500 raw保守默认值,优先避免正常运动误报。PWM软件上限保持95%。
- 位置任务因端点/堵转事件停止时,若最终Hall已进入目标±2脉冲范围则按到位完成;底层LastEvent仍由`0x303`保留用于诊断。超过该范围的STALL/TIMEOUT仍按失败处理。
- `HOME_KNOWN_RANGE`/全量标定的寻端语义与普通移动分离。端点要求500 ms内Hall双边沿为0,避免慢速移动仅有下降沿时被误判为端点;已知量程归零若第一次寻近端少于4个真实Hall边沿,会先向远端验证至少4个双边沿再重新寻近端,并校验回程与离端边沿数(允许1边沿误差),禁止把电机未启动直接清零。无法证明往返运动时归零失败并使PositionValid失效。位置和量程仍按Hall上升沿计数。
- `STOP_ALL` 不受旧 Sequence 限制;CAN RX ISR 先立即终止四路 PWM,因此也能打断阻塞式全行程标定,主流程随后完成 ACK 和 Sequence 重同步。
- `CALIBRATE_RANGE` 的 ParamB 是每个寻端行程的实际超时(10~600 s);标定期间继续发送 `0x301/0x302/0x303` 周期反馈。
- `HOME_KNOWN_RANGE(0x0B)` 的 ParamA 必须为 `0x4B52`,ParamB 为最近端寻找超时(10~600 s)。它使用各轴正常90% Duty,只寻找最近端并置 Hall=0/PositionValid=1,保留480/374/917/647,不访问最远端;已经在最近端时会执行约3 Hall的短离端验证后返回。
- 位置记忆使用 SMV8 双槽、版本、序号和 CRC。运动命令启动前先提交较新的 dirty 记录,防止运动中掉电后恢复旧位置;所有电机停稳后保存四轴当前 Hall、最大 Hall、物理绝对量和 PositionValid。正常掉电重启后 `0x302/0x303` 可直接取得上次停止位置。运动中掉电或存储不兼容时位置无效,应用只对失效轴执行 `0x0B` 回最近端,然后继续绝对目标。SMV5/SMV6 的旧量程记录和 SMV7 的旧通道映射记录均不会直接恢复。
- C-SPY `execUserExit()` 同时关断四电机 PWM、风扇 TCPWM60 和加热 TCPWM65,并验证物理输出 latch 为低。
- Motor3 仍未接线、未实标;固件可以解析协议,但上位机默认禁止发送该轴命令。
- 正常车辆上电不要求调试器连接;真正 POR 会清除调试会话标记。若 C-SPY 会话结束或调试器脱离,则本次供电周期锁存安全停机,防止烧录结束后执行器继续动作。
## 构建
@@ -36,4 +61,13 @@ CAN 为 500 kbit/s、经典标准帧、11-bit ID、DLC 8。`seat_move_applicatio
'.\IAR\Seat_Controller_demo.ewp' -build rev_d -log all
```
输出:`IAR/rev_d/Exe/Seat_Controller_demo.hex`。构建不会烧录或启动执行器。首次烧录后的热、风、Motor0..2 组合回归仍必须在台架完成,未完成前不得标记为 HIL verified。
输出:`IAR/rev_d/Exe/Seat_Controller_demo.hex`。量程标定程序位于仓库根目录 `量程测试脚本`。完整台架步骤、测试向量和判定标准见 `Docs/座椅控制器_CAN_测试规范.md`、`Docs/座椅控制器_CAN_测试向量.csv` 与 `Docs/座椅控制器_CAN_测试规范.docx`。
## 当前无示波器 HIL 入口
- `Tests/manual_hil/04_can_full_function.py`:通风、加热和四轴约12%往返冒烟;若位置未知,先只把失效轴回最近端,不重新测量量程。
- `Tests/manual_hil/05_seat_function_bridge.py`:在ZCANPRO内运行的CAN桥接。
- `Tests/manual_hil/run_seat_function_gui.bat`:Windows选择界面,从仓库根目录 `座椅功能模式配置表.xlsx` 连续动态读取当前11种模式/12个场景,显示物理目标、重算Hall、CAN结果并保存人工观察;小憩模式为单场景,并包含新增“小腿解压悬浮模式”。
- `Tests/manual_hil/02_power_memory.py` 默认只做正常掉电恢复;`03_protection_faults.py` 默认只做无需注入设备的协议保护。
本轮不执行 `01_calibration_position.py` 或 `04_waveform_full_function.py`,两者均已设置归档保护。具体顺序和日志回传要求见 `Tests/manual_hil/README_CN.md`。
@@ -36,7 +36,7 @@ CAN4:RX=P3.7,TX=P3.6,经典标准 CAN,500 kbit/s,11-bit ID,DLC=8,I
| `0x302` | TX | 每台电机 100 ms + Event | Byte0 电机 Sub ID,任务/结果、方向、位置、占空比 |
| `0x303` | TX | 每台电机 500 ms + Event | Byte0 电机 Sub ID,底层事件、Hall、校准量程和最大速度 |
`0x300` Byte1:bits0..1 MotorSelector=`0..3`,bits2..3 Direction,bits4..5 ProtocolVersion=`1`,bits6..7=`0`。MotorSelector=`3`在电机类Opcode中表示电机3;在`STOP_ALL`/`SAVE_SHUTDOWN`中由Opcode定义全局语义,仍要求Selector=`3`。除`STOP_ALL`外,16-bit sequence必须按有符号环形比较递增;`STOP_ALL`总是立即生效并重同步sequence。
`0x300` Byte1:bits0..1 MotorSelector=`0..3`,bits2..3 Direction,bits4..5 ProtocolVersion=`1`,bits6..7=`0`。电机类Opcode使用Selector=`0..3`;全局`STOP_ALL`固定要求Selector=`0`。除`STOP_ALL`外,16-bit sequence必须按有符号环形比较递增;`STOP_ALL`总是立即生效并重同步sequence。
`0x301` Byte1 bits0..3为LastOpcode、bits4..7为四路ActiveMask;Byte4 bits0..3为四路RetryMask、bit4为RecordClean、bit5为WatchdogReset、bit6为RecordValid、bit7保留。
@@ -46,7 +46,7 @@ CAN4:RX=P3.7,TX=P3.6,经典标准 CAN,500 kbit/s,11-bit ID,DLC=8,I
| 用途 | ID | Data bytes 0..7 |
| --- | ---: | --- |
| 全停并同步 sequence=1 | `0x300` | `09 13 01 00 00 00 00 00` |
| 全停并同步 sequence=1 | `0x300` | `09 10 01 00 00 00 00 00` |
| 电机0向远端 500 ms/50%,seq=2 | `0x300` | `03 14 02 00 F4 01 32 00` |
| 电机0向近端 500 ms/50%,seq=3 | `0x300` | `03 18 03 00 F4 01 32 00` |
| 电机1向远端 500 ms/50%,seq=4 | `0x300` | `03 15 04 00 F4 01 32 00` |
@@ -56,7 +56,7 @@ CAN4:RX=P3.7,TX=P3.6,经典标准 CAN,500 kbit/s,11-bit ID,DLC=8,I
| 电机3向远端 500 ms/50%,seq=8(接线后) | `0x300` | `03 17 08 00 F4 01 32 00` |
| 电机3向近端 500 ms/50%,seq=9(接线后) | `0x300` | `03 1B 09 00 F4 01 32 00` |
| 停止电机3,seq=10 | `0x300` | `08 13 0A 00 00 00 00 00` |
| 全停,seq=11 | `0x300` | `09 13 0B 00 00 00 00 00` |
| 全停,seq=11 | `0x300` | `09 10 0B 00 00 00 00 00` |
| 保存状态并受控关机,seq=12 | `0x300` | `0C 13 0C 00 00 00 00 00` |
电机3接线确认后,并发错峰测试可在很短间隔内依次发送四帧:
@@ -68,7 +68,7 @@ CAN4:RX=P3.7,TX=P3.6,经典标准 CAN,500 kbit/s,11-bit ID,DLC=8,I
03 17 17 00 E8 03 32 00 motor3 far, 1000 ms, seq=23
```
预期`0x301` ActiveMask逐步变为`1→3→7→15`;四路物理起动相隔约100 ms。测试结束发送`09 13 18 00 00 00 00 00`全停。
预期`0x301` ActiveMask逐步变为`1→3→7→15`;四路物理起动相隔约100 ms。测试结束发送`09 10 18 00 00 00 00 00`全停。
## 4. 位置、校准与存储
@@ -0,0 +1,112 @@
# Seat_Controller_demo 联调测试报告(2026-07-28)
## 1. 测试对象
- 控制器工程:`Seat_Controller_demo`
- 自动标定工程:`Seat_Move_Calibration_demo`
- 目标器件:CYT4BFBCJE / CY_MCU_rev_d / Cortex-M0+ / Flash
- 编译器:IAR EWARM 9.40.1(命令行构建工具 9.2.2.10770)
- CAN 工具:ZCANPRO + USBCANFD-200U 设备0通道0
## 2. 本次结果摘要
| 项目 | 结果 | 实际结果 |
|---|---|---|
| Seat_Controller_demo 全量构建 | PASS | 0 errors,0 warnings |
| Seat_Move_Calibration_demo 全量构建 | PASS | 0 errors,0 warnings |
| IAR 工作区完整性 | PASS | 已补齐并成功打开 `Seat_Controller_demo.eww` |
| 控制器固件下载 | BLOCKED | CMSIS-DAP 报 `Fatal error: Probe not found / Session aborted` |
| Windows 烧录器枚举 | BLOCKED | 未发现 CMSIS-DAP、J-Link、KitProg、MiniProg、Infineon/Cypress/SEGGER 调试设备 |
| USBCANFD-200U 枚举 | PASS | Windows 正常识别 `USBCANFD-200U`,VID_3068/PID_0009 |
| CAN 物理发送 | BLOCKED | ZCANPRO 报“发送失败”,无总线 ACK,错误帧计数由0增至17 |
| 控制器 CAN 功能用例 | NOT RUN | 新镜像未烧录且总线物理发送失败,不得引用历史帧判定 |
| 四轴自动标定 | NOT RUN | 新镜像未烧录;不得在当前链路状态下启动机械全行程 |
## 3. 构建证据
### 3.1 Seat_Controller_demo
命令:
```text
IarBuild.exe Seat_Controller_demo\IAR\Seat_Controller_demo.ewp -build rev_d -log all
```
结果:
```text
Total number of errors: 0
Total number of warnings: 0
Build succeeded
```
HEX SHA-256:
```text
9406655A88F19B98B66DFE825247AC9807F5F9C9AE463D6C0D5C6A328E9EF8C2
```
### 3.2 Seat_Move_Calibration_demo
命令:
```text
IarBuild.exe Seat_Move_Calibration_demo\IAR\Seat_Move_Calibration_demo.ewp -build rev_d -log errors
```
结果:
```text
Total number of errors: 0
Total number of warnings: 0
Build succeeded
```
HEX SHA-256:
```text
888D0A2A10E2D67BB3349ACBD609121E938257FF432899DDF041ABA206D19741
```
### 3.3 CAN 矩阵与测试集
- `座椅移动功能CAN矩阵表.xlsx` SHA-256:`D72D88D21CA92E4346C5CACDFFC246A58005C3660B7E3FC72BEE42C10BCB0E5E`
- `座椅控制器_CAN_测试向量.csv`:63条、16列、TestID 63个且无重复。
## 4. IAR 下载实测
1. 在 IAR 中通过 `File > Open Workspace` 打开 `Seat_Controller_demo.eww`。
2. 执行 Make,IDE 显示 `Build succeeded`、Errors 0、Warnings 0。
3. 执行 Download and Debug。
4. CMSIS-DAP 返回:
```text
Fatal error: Probe not found
Session aborted!
```
5. Windows PnP 复查没有任何在场的 CMSIS-DAP/J-Link/KitProg/MiniProg/Infineon/Cypress/SEGGER 调试器。工程调试配置仍为参考工程一致的 `CMSISDAP_ID` 和 Infineon `CYT4.ProbeConfig`,因此当前失败发生在探针枚举阶段,不是编译、链接、器件宏或应用代码阶段。
## 5. CAN 物理链路实测
1. Windows 正常枚举 USBCANFD-200U。
2. ZCANPRO 中设备0通道0处于已启动状态;历史视图中保留了此前的 `0x300`/`0x301` 各6帧,这些帧不是本次新镜像的验证结果。
3. 使用窗口内原有模板尝试发送标准 Classic CAN、DLC=8 的 `0x300 00 03 00 00 00 00 00 00`。
4. ZCANPRO 返回“发送失败”;发送/接收计数仍为6/6,错误帧计数从0升至17。
5. 该结果表明当前 CAN 总线上没有形成有效 ACK,常见原因包括开发板未供电、收发器未使能、CANH/CANL/地线连接错误、终端电阻缺失、波特率不一致或通道接错。由于报文未成功发送,本次没有产生受控执行器动作。
## 6. 恢复联调前检查表
按顺序完成以下检查后再继续:
1. 确认开发板12 V及逻辑电源均正常,调试器电源/状态灯正常。
2. 重新插拔烧录器 USB 数据线,并在 Windows 设备管理器中确认出现 CMSIS-DAP 类设备;只看到 USBCANFD-200U 不算烧录器在线。
3. 确认 SWDIO、SWCLK、XRES、GND、VTREF 接线,VTREF 电压与目标板一致。
4. 确认 CANH、CANL、CAN_GND 未反接,台架两端总终端约60 Ω,并确认使用设备0通道0。
5. 确认 ZCANPRO 通道为 500 kbit/s、Classic CAN、11-bit 标准帧。
6. 探针恢复后先烧录 `Seat_Controller_demo.hex`,复位并确认无动作上电;然后依次执行 SYS-002、MOV-001 STOP_ALL、MOV-002 QUERY 和热管理静态用例。
7. 只有在四轴映射、方向、急停和机械空间人工确认后,才允许执行短时点动、多轴并发和自动全行程标定。
## 7. 本次结论
软件构建、工程结构和测试资产通过离线验证;实机烧录与 CAN 功能测试因烧录器未被 Windows 枚举、CAN 总线无 ACK 而阻塞。恢复硬件链路后必须从安全上电和 STOP_ALL 用例重新开始,不能将 ZCANPRO 历史帧或旧固件响应记为本次 PASS。
@@ -0,0 +1,132 @@
from __future__ import annotations
import csv
from pathlib import Path
from docx import Document
from docx.oxml.ns import qn
HERE = Path(__file__).resolve().parent
DOCX = HERE / "座椅控制器_CAN_测试规范.docx"
CSV = HERE / "座椅控制器_CAN_测试向量.csv"
CONTENT_WIDTH_DXA = 9360
def attr_int(node, name):
return int(node.get(qn(name)))
def main():
doc = Document(DOCX)
errors = []
section = doc.sections[0]
expected_section = {
"page_width": 12240,
"page_height": 15840,
"top_margin": 1440,
"right_margin": 1440,
"bottom_margin": 1440,
"left_margin": 1440,
"header_distance": 708,
"footer_distance": 708,
}
for key, expected in expected_section.items():
actual = int(getattr(section, key).twips)
if actual != expected:
errors.append(f"section {key}: {actual} != {expected}")
expected_styles = {
"Normal": (11, 0, 6, 1.25),
"Heading 1": (16, 18, 10, None),
"Heading 2": (13, 14, 7, None),
"Heading 3": (12, 10, 5, None),
}
for name, (size, before, after, line) in expected_styles.items():
style = doc.styles[name]
if round(style.font.size.pt, 2) != size:
errors.append(f"style {name} size {style.font.size.pt} != {size}")
pf = style.paragraph_format
actual_before = pf.space_before.pt if pf.space_before is not None else 0
actual_after = pf.space_after.pt if pf.space_after is not None else 0
if round(actual_before, 2) != before:
errors.append(f"style {name} before mismatch")
if round(actual_after, 2) != after:
errors.append(f"style {name} after mismatch")
if line is not None and pf.line_spacing != line:
errors.append(f"style {name} line spacing {pf.line_spacing} != {line}")
for table_index, table in enumerate(doc.tables):
tbl_pr = table._tbl.tblPr
tbl_w = tbl_pr.find(qn("w:tblW"))
tbl_ind = tbl_pr.find(qn("w:tblInd"))
layout = tbl_pr.find(qn("w:tblLayout"))
if tbl_w is None or attr_int(tbl_w, "w:w") != CONTENT_WIDTH_DXA:
errors.append(f"table {table_index} tblW invalid")
if tbl_ind is None or attr_int(tbl_ind, "w:w") != 120:
errors.append(f"table {table_index} tblInd invalid")
if layout is None or layout.get(qn("w:type")) != "fixed":
errors.append(f"table {table_index} layout is not fixed")
grid = [attr_int(col, "w:w") for col in table._tbl.tblGrid]
if sum(grid) != CONTENT_WIDTH_DXA:
errors.append(f"table {table_index} grid sum {sum(grid)}")
for row_index, row in enumerate(table.rows):
if len(row.cells) != len(grid):
errors.append(f"table {table_index} row {row_index} cell count")
continue
for col_index, cell in enumerate(row.cells):
tc_pr = cell._tc.get_or_add_tcPr()
tc_w = tc_pr.find(qn("w:tcW"))
if tc_w is None or attr_int(tc_w, "w:w") != grid[col_index]:
errors.append(
f"table {table_index} row {row_index} col {col_index} tcW"
)
tc_mar = tc_pr.find(qn("w:tcMar"))
if tc_mar is None:
errors.append(
f"table {table_index} row {row_index} col {col_index} margins"
)
continue
expected_margins = {"top": 80, "bottom": 80, "start": 120, "end": 120}
for tag, expected in expected_margins.items():
node = tc_mar.find(qn(f"w:{tag}"))
if node is None or attr_int(node, "w:w") != expected:
errors.append(
f"table {table_index} row {row_index} col {col_index} {tag}"
)
all_text = "\n".join(p.text for p in doc.paragraphs)
for table in doc.tables:
for row in table.rows:
all_text += "\n" + " | ".join(cell.text for cell in row.cells)
required = [
"SMV8", "MaxHall", "480", "374", "917", "647",
"215 mm", "水平51 mm", "垂直56 mm", "68°", "72°",
"0x29E", "0x4FA", "0x300", "0x301", "0x302", "0x303",
"HOME_KNOWN_RANGE", "0x4B52", "HOM-004",
"运动中掉电", "PositionValid=0",
]
for item in required:
if item not in all_text:
errors.append(f"missing required text: {item}")
if "�" in all_text or "鏄" in all_text or "搴ф" in all_text:
errors.append("mojibake detected")
with CSV.open("r", encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
for row in rows:
if None in row or any(value is None for value in row.values()):
errors.append(f"malformed CSV row: {row.get('TestID', '<unknown>')}")
if row["TestID"] not in all_text:
errors.append(f"test vector missing from DOCX: {row['TestID']}")
print(f"paragraphs={len(doc.paragraphs)} tables={len(doc.tables)} tests={len(rows)}")
if errors:
for error in errors:
print(f"ERROR: {error}")
raise SystemExit(1)
print("STRUCTURAL_AUDIT=PASS")
if __name__ == "__main__":
main()
@@ -0,0 +1,780 @@
from __future__ import annotations
import csv
from collections import OrderedDict
from pathlib import Path
from docx import Document
from docx.enum.section import WD_SECTION_START
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches, Pt, RGBColor
HERE = Path(__file__).resolve().parent
CSV_PATH = HERE / "座椅控制器_CAN_测试向量.csv"
OUTPUT_PATH = HERE / "座椅控制器_CAN_测试规范.docx"
CONTENT_WIDTH_DXA = 9360
TABLE_INDENT_DXA = 120
CELL_TOP_BOTTOM_DXA = 80
CELL_START_END_DXA = 120
BLUE = "2E74B5"
DARK_BLUE = "1F4D78"
INK = "0B2545"
LIGHT_BLUE = "E8EEF5"
LIGHT_GRAY = "F2F4F7"
PALE_BLUE = "F4F6F9"
WHITE = "FFFFFF"
GRAY = "666666"
RED = "9B1C1C"
def set_run_font(run, size=None, bold=None, color=None, italic=None,
latin="Calibri", east_asia="Microsoft YaHei"):
run.font.name = latin
rpr = run._element.get_or_add_rPr()
rfonts = rpr.find(qn("w:rFonts"))
if rfonts is None:
rfonts = OxmlElement("w:rFonts")
rpr.insert(0, rfonts)
rfonts.set(qn("w:ascii"), latin)
rfonts.set(qn("w:hAnsi"), latin)
rfonts.set(qn("w:eastAsia"), east_asia)
if size is not None:
run.font.size = Pt(size)
if bold is not None:
run.bold = bold
if italic is not None:
run.italic = italic
if color is not None:
run.font.color.rgb = RGBColor.from_string(color)
def set_style_font(style, size, color="000000", bold=False):
style.font.name = "Calibri"
style.font.size = Pt(size)
style.font.bold = bold
style.font.color.rgb = RGBColor.from_string(color)
rpr = style.element.get_or_add_rPr()
rfonts = rpr.find(qn("w:rFonts"))
if rfonts is None:
rfonts = OxmlElement("w:rFonts")
rpr.insert(0, rfonts)
rfonts.set(qn("w:ascii"), "Calibri")
rfonts.set(qn("w:hAnsi"), "Calibri")
rfonts.set(qn("w:eastAsia"), "Microsoft YaHei")
def set_cell_margins(cell, top=CELL_TOP_BOTTOM_DXA,
start=CELL_START_END_DXA,
bottom=CELL_TOP_BOTTOM_DXA,
end=CELL_START_END_DXA):
tc_pr = cell._tc.get_or_add_tcPr()
tc_mar = tc_pr.first_child_found_in("w:tcMar")
if tc_mar is None:
tc_mar = OxmlElement("w:tcMar")
tc_pr.append(tc_mar)
for tag, value in (("top", top), ("start", start),
("bottom", bottom), ("end", end)):
node = tc_mar.find(qn(f"w:{tag}"))
if node is None:
node = OxmlElement(f"w:{tag}")
tc_mar.append(node)
node.set(qn("w:w"), str(value))
node.set(qn("w:type"), "dxa")
def set_cell_fill(cell, fill):
tc_pr = cell._tc.get_or_add_tcPr()
shd = tc_pr.find(qn("w:shd"))
if shd is None:
shd = OxmlElement("w:shd")
tc_pr.append(shd)
shd.set(qn("w:fill"), fill)
shd.set(qn("w:val"), "clear")
def set_repeat_table_header(row):
tr_pr = row._tr.get_or_add_trPr()
tbl_header = tr_pr.find(qn("w:tblHeader"))
if tbl_header is None:
tbl_header = OxmlElement("w:tblHeader")
tr_pr.append(tbl_header)
tbl_header.set(qn("w:val"), "true")
def set_table_borders(table, color="B7C9DB", size="6"):
tbl_pr = table._tbl.tblPr
borders = tbl_pr.find(qn("w:tblBorders"))
if borders is None:
borders = OxmlElement("w:tblBorders")
tbl_pr.append(borders)
for edge in ("top", "left", "bottom", "right", "insideH", "insideV"):
node = borders.find(qn(f"w:{edge}"))
if node is None:
node = OxmlElement(f"w:{edge}")
borders.append(node)
node.set(qn("w:val"), "single")
node.set(qn("w:sz"), size)
node.set(qn("w:space"), "0")
node.set(qn("w:color"), color)
def set_table_geometry(table, widths_dxa, indent_dxa=TABLE_INDENT_DXA):
if sum(widths_dxa) != CONTENT_WIDTH_DXA:
raise ValueError(f"table width must be {CONTENT_WIDTH_DXA}: {widths_dxa}")
table.autofit = False
tbl = table._tbl
tbl_pr = tbl.tblPr
tbl_w = tbl_pr.find(qn("w:tblW"))
if tbl_w is None:
tbl_w = OxmlElement("w:tblW")
tbl_pr.append(tbl_w)
tbl_w.set(qn("w:w"), str(CONTENT_WIDTH_DXA))
tbl_w.set(qn("w:type"), "dxa")
tbl_ind = tbl_pr.find(qn("w:tblInd"))
if tbl_ind is None:
tbl_ind = OxmlElement("w:tblInd")
tbl_pr.append(tbl_ind)
tbl_ind.set(qn("w:w"), str(indent_dxa))
tbl_ind.set(qn("w:type"), "dxa")
layout = tbl_pr.find(qn("w:tblLayout"))
if layout is None:
layout = OxmlElement("w:tblLayout")
tbl_pr.append(layout)
layout.set(qn("w:type"), "fixed")
grid = tbl.tblGrid
for child in list(grid):
grid.remove(child)
for width in widths_dxa:
col = OxmlElement("w:gridCol")
col.set(qn("w:w"), str(width))
grid.append(col)
for row in table.rows:
row.height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST
for index, cell in enumerate(row.cells):
tc_pr = cell._tc.get_or_add_tcPr()
tc_w = tc_pr.find(qn("w:tcW"))
if tc_w is None:
tc_w = OxmlElement("w:tcW")
tc_pr.append(tc_w)
tc_w.set(qn("w:w"), str(widths_dxa[index]))
tc_w.set(qn("w:type"), "dxa")
cell.width = Inches(widths_dxa[index] / 1440)
cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
set_cell_margins(cell)
set_table_borders(table)
def set_cell_text(cell, text, bold=False, color="000000", size=9,
align=WD_ALIGN_PARAGRAPH.LEFT):
cell.text = ""
p = cell.paragraphs[0]
p.alignment = align
p.paragraph_format.space_before = Pt(0)
p.paragraph_format.space_after = Pt(0)
p.paragraph_format.line_spacing = 1.08
r = p.add_run(str(text))
set_run_font(r, size=size, bold=bold, color=color)
def add_table(doc, headers, rows, widths_dxa, font_size=9,
center_cols=(), header_fill=LIGHT_BLUE):
table = doc.add_table(rows=1, cols=len(headers))
table.style = "Table Grid"
hdr = table.rows[0]
set_repeat_table_header(hdr)
for i, header in enumerate(headers):
set_cell_fill(hdr.cells[i], header_fill)
set_cell_text(hdr.cells[i], header, bold=True, color=INK, size=font_size,
align=WD_ALIGN_PARAGRAPH.CENTER)
for row_index, values in enumerate(rows):
cells = table.add_row().cells
for i, value in enumerate(values):
if row_index % 2 == 1:
set_cell_fill(cells[i], "F8FAFC")
set_cell_text(
cells[i], value, size=font_size,
align=(WD_ALIGN_PARAGRAPH.CENTER if i in center_cols
else WD_ALIGN_PARAGRAPH.LEFT),
)
set_table_geometry(table, widths_dxa)
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(2)
return table
def add_body(doc, text, bold_prefix=None):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(0)
p.paragraph_format.space_after = Pt(6)
p.paragraph_format.line_spacing = 1.25
if bold_prefix and text.startswith(bold_prefix):
r1 = p.add_run(bold_prefix)
set_run_font(r1, bold=True)
r2 = p.add_run(text[len(bold_prefix):])
set_run_font(r2)
else:
r = p.add_run(text)
set_run_font(r)
return p
def add_bullet(doc, text, level=0):
p = doc.add_paragraph(style="List Bullet" if level == 0 else "List Bullet 2")
p.paragraph_format.left_indent = Inches(0.375 + level * 0.25)
p.paragraph_format.first_line_indent = Inches(-0.188)
p.paragraph_format.space_before = Pt(0)
p.paragraph_format.space_after = Pt(4)
p.paragraph_format.line_spacing = 1.25
r = p.add_run(text)
set_run_font(r)
return p
def add_number(doc, text):
p = doc.add_paragraph(style="List Number")
p.paragraph_format.left_indent = Inches(0.375)
p.paragraph_format.first_line_indent = Inches(-0.188)
p.paragraph_format.space_before = Pt(0)
p.paragraph_format.space_after = Pt(4)
p.paragraph_format.line_spacing = 1.25
r = p.add_run(text)
set_run_font(r)
return p
def add_callout(doc, label, text, fill=PALE_BLUE, color=INK):
table = doc.add_table(rows=1, cols=1)
cell = table.cell(0, 0)
set_cell_fill(cell, fill)
cell.text = ""
p = cell.paragraphs[0]
p.paragraph_format.space_before = Pt(2)
p.paragraph_format.space_after = Pt(2)
p.paragraph_format.line_spacing = 1.15
r = p.add_run(f"{label} ")
set_run_font(r, bold=True, color=color)
r = p.add_run(text)
set_run_font(r, color=color)
set_table_geometry(table, [CONTENT_WIDTH_DXA])
set_table_borders(table, color="C6D7E8", size="8")
p2 = doc.add_paragraph()
p2.paragraph_format.space_after = Pt(2)
def add_field(paragraph, instr):
run = paragraph.add_run()
fld_char = OxmlElement("w:fldChar")
fld_char.set(qn("w:fldCharType"), "begin")
instr_text = OxmlElement("w:instrText")
instr_text.set(qn("xml:space"), "preserve")
instr_text.text = instr
fld_sep = OxmlElement("w:fldChar")
fld_sep.set(qn("w:fldCharType"), "separate")
text = OxmlElement("w:t")
text.text = "1"
fld_end = OxmlElement("w:fldChar")
fld_end.set(qn("w:fldCharType"), "end")
run._r.extend([fld_char, instr_text, fld_sep, text, fld_end])
set_run_font(run, size=9, color=GRAY)
def configure_document(doc):
section = doc.sections[0]
section.page_width = Inches(8.5)
section.page_height = Inches(11)
section.top_margin = Inches(1.0)
section.right_margin = Inches(1.0)
section.bottom_margin = Inches(1.0)
section.left_margin = Inches(1.0)
section.header_distance = Inches(0.492)
section.footer_distance = Inches(0.492)
normal = doc.styles["Normal"]
set_style_font(normal, 11)
normal.paragraph_format.space_before = Pt(0)
normal.paragraph_format.space_after = Pt(6)
normal.paragraph_format.line_spacing = 1.25
for name, size, color, before, after in (
("Heading 1", 16, BLUE, 18, 10),
("Heading 2", 13, BLUE, 14, 7),
("Heading 3", 12, DARK_BLUE, 10, 5),
):
style = doc.styles[name]
set_style_font(style, size, color=color, bold=True)
style.paragraph_format.space_before = Pt(before)
style.paragraph_format.space_after = Pt(after)
style.paragraph_format.keep_with_next = True
for name in ("List Bullet", "List Bullet 2", "List Number"):
style = doc.styles[name]
set_style_font(style, 11)
style.paragraph_format.space_after = Pt(4)
style.paragraph_format.line_spacing = 1.25
header = section.header
p = header.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
p.paragraph_format.space_after = Pt(0)
r = p.add_run("Seat Controller | CAN/HIL Test Specification")
set_run_font(r, size=9, bold=True, color=GRAY)
footer = section.footer
p = footer.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
p.paragraph_format.space_after = Pt(0)
r = p.add_run("V2.3 | 2026-07-30 | Page ")
set_run_font(r, size=9, color=GRAY)
add_field(p, "PAGE")
r = p.add_run(" / ")
set_run_font(r, size=9, color=GRAY)
add_field(p, "NUMPAGES")
settings = doc.settings.element
update_fields = settings.find(qn("w:updateFields"))
if update_fields is None:
update_fields = OxmlElement("w:updateFields")
settings.append(update_fields)
update_fields.set(qn("w:val"), "true")
def add_cover(doc):
p = doc.add_paragraph()
p.paragraph_format.space_before = Pt(70)
p.paragraph_format.space_after = Pt(14)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = p.add_run("工程测试规范")
set_run_font(r, size=12, bold=True, color=BLUE)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(10)
r = p.add_run("Seat_Controller_demo")
set_run_font(r, size=30, bold=True, color=INK)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(6)
r = p.add_run("座椅通风、加热与四轴移动 CAN/HIL 测试规范")
set_run_font(r, size=18, bold=True, color=DARK_BLUE)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
p.paragraph_format.space_after = Pt(42)
r = p.add_run("含四轴确认量程、SMV8位置记忆与未知位置单端恢复")
set_run_font(r, size=11, color=GRAY)
add_table(
doc,
["文档项", "内容"],
[
["版本", "V2.3 / 2026-07-30"],
["目标硬件", "CYT4BFBCJE / CY_MCU_rev_d / Cortex-M0+ Flash"],
["CAN", "Classic CAN,11-bit,500 kbit/s,DLC=8,Intel/Little-endian"],
["软件工程", "Seat_Controller_demo"],
["测试范围", "通风、靠背加热、四轴移动、标定、反馈、安全停机、掉电记忆"],
],
[1800, 7560],
font_size=10,
center_cols=(0,),
)
add_callout(
doc,
"安全等级 P0",
"任何全行程、堵转、加热或运动中断电测试都必须在固定台架、限流电源、机械净空和硬件急停条件下执行。",
fill="FFF4E5",
color=RED,
)
doc.add_page_break()
def add_overview(doc):
doc.add_heading("1 目的、范围与交付判定", level=1)
add_body(doc, "本规范用于验证 Seat_Controller_demo 在同一 CAN4 节点上融合座椅通风、靠背加热和四路电机移动后的功能、时序、故障安全与绝对位置连续性。测试结果分为 PASS、FAIL、BLOCKED、NOT RUN;硬件条件不足不能以 PASS 代替。")
add_bullet(doc, "静态范围:IAR 全量构建、目标宏、资源冲突、输出初始低电平。")
add_bullet(doc, "CAN 范围:热管理报文 0x29E/0x2AA/0x3CE/0x42A/0x4FA;移动报文 0x300~0x303。")
add_bullet(doc, "动作范围:单轴点动、绝对/相对位置、多轴并发、标定、停止与故障注入。")
add_bullet(doc, "记忆范围:SMV8 dirty/clean 双槽、CRC、正常掉电恢复、运动中掉电失效;SMV5/SMV6 旧量程及 SMV7 旧通道映射记录不恢复。")
add_callout(doc, "构建基线", "2026-07-30 使用 IAR EWARM 9.2 rev_d 配置全量构建;四轴量程入口均改为机械端点标定,台架动作与掉电用例由手动 HIL 脚本留存日志。")
doc.add_heading("1.1 通过、失败与阻塞", level=2)
add_table(
doc,
["结果", "定义", "处理"],
[
["PASS", "CAN、物理动作、时序与安全判据全部满足", "保留原始报文、波形和测量记录"],
["FAIL", "存在误动作、方向错误、停机超时、字段错误、丢帧或存储错误", "立即全停,记录复现条件并进入缺陷流程"],
["BLOCKED", "机械、仪器、线束或安全条件不满足", "明确阻塞项,不得按通过处理"],
["NOT RUN", "尚未执行", "列入后续计划"],
],
[950, 4610, 3800],
font_size=9,
center_cols=(0,),
)
def add_calibration(doc):
doc.add_heading("2 四轴坐标、量程与换算", level=1)
add_body(doc, "统一坐标定义:最近端=0 Hall=0%=绝对位置0;TowardFar 从最近端向最远端运动且 Hall 递增;TowardNear 反向且 Hall 递减;最远端=MaxHallCount=100%。")
add_table(
doc,
["ID", "功能/通道", "0% 最近端", "100% 最远端", "MaxHall", "全量程/分辨率"],
[
["M0", "水平前后\nphysical0", "座椅最前方", "座椅最后方", "480", "215 mm\n0.4479 mm/Hall"],
["M1", "坐盆斜上下\nphysical1", "坐盆最上方", "坐盆斜向下方", "374", "水平51 mm / 0.1364\n垂直56 mm / 0.1497"],
["M2", "靠背前后\nphysical2", "靠背最前方\n垂直地面", "向后躺倒", "917", "68°\n0.0742°/Hall"],
["M3", "腿托上下\nphysical3", "腿托最下方\n垂直地面", "向上抬起", "647", "72°\n0.1113°/Hall"],
],
[650, 1450, 1750, 1750, 1050, 2710],
font_size=8.5,
center_cols=(0, 4),
)
add_body(doc, "换算公式:AbsoluteValue = round(HallCount × FullScale / MaxHallCount);Normalized = round(HallCount × 10000 / MaxHallCount)。M1 的水平与垂直分量共用同一归一化比例。")
doc.add_heading("2.1 典型绝对位置检查点", level=2)
add_table(
doc,
["轴", "0%", "25%", "50%", "75%", "100%"],
[
["M0 Hall / mm", "0 / 0", "120 / 53.75", "240 / 107.50", "360 / 161.25", "480 / 215"],
["M1 Hall / H,V mm", "0 / 0,0", "94 / 12.75,14", "187 / 25.5,28", "281 / 38.25,42", "374 / 51,56"],
["M2 Hall / deg", "0 / 0", "229 / 17", "459 / 34", "688 / 51", "917 / 68"],
["M3 Hall / deg", "0 / 0", "162 / 18", "324 / 36", "485 / 54", "647 / 72"],
],
[1600, 1450, 1578, 1578, 1578, 1576],
font_size=8.5,
center_cols=(0, 1, 2, 3, 4, 5),
)
add_callout(doc, "首次上电/位置未知", "默认 MaxHall 只提供量程基线,不能提供当前原点。PositionValid只表示ECU位置记录可信,不表示机构位于最近端。本台架量程已确认;SMV8 无有效 clean 记录时,对失效轴执行 HOME_KNOWN_RANGE(0x0B)。若机构在ECU掉电时被人工改变或实际位置不可信,即使Mask为0xF也必须真实回最近端建立Hall=0,不能只修改有效位。保留480/374/917/647,无需访问最远端。")
def add_can_protocol(doc):
doc.add_heading("3 CAN 接口规范", level=1)
add_table(
doc,
["方向", "ID", "周期/触发", "用途"],
[
["RX", "0x29E", "事件", "风扇/加热主控制与档位"],
["RX", "0x2AA", "事件", "风扇/加热最大配置"],
["RX", "0x3CE", "事件", "加热模式有效性"],
["RX", "0x42A", "事件", "风扇/加热开关"],
["TX", "0x4FA", "100 ms + 状态变化", "风扇/加热组合反馈"],
["RX", "0x300", "事件", "四轴移动命令"],
["TX", "0x301", "100 ms + 状态变化", "系统状态与ACK"],
["TX", "0x302", "每轴100 ms;轴间约25 ms", "任务、归一化位置、驱动等级"],
["TX", "0x303", "每轴500 ms;轴间约125 ms", "Hall、量程、速度、诊断"],
],
[900, 950, 2850, 4660],
font_size=9,
center_cols=(0, 1),
)
doc.add_heading("3.1 热管理 RX 与 TX", level=2)
add_table(
doc,
["ID", "字节/位", "定义", "有效值与动作"],
[
["0x29E", "B0[1:0]", "FanOn", "1=开,2=关"],
["0x29E", "B0[3:2]", "FanMode", "1或2有效"],
["0x29E", "B0[5:4]", "HeaterOn", "1=开,2=关"],
["0x29E", "B2[3:0]", "FanGear", "1~5,物理夹紧至1~3及配置上限"],
["0x29E", "B2[7:4]", "HeaterGear", "1~5,物理夹紧至1~3及配置上限"],
["0x2AA", "B0", "SubId", "必须为1"],
["0x2AA", "B2[2:0]", "FanMax", "1→1档,2→2档,其余有效值→3档"],
["0x2AA", "B2[5:3]", "HeaterMax", "1→1档,2→2档,其余有效值→3档"],
["0x3CE", "B0[5:4]", "HeaterMode", "1或2有效;其它值使加热安全关闭"],
["0x42A", "B0[1:0]/[3:2]", "Fan/Heater switch", "1=开,2=关"],
["0x4FA", "B0[2:0]/[5:3]", "Fan/Heater feedback", "0=故障/无效,1=关,2/3/4=1/2/3档"],
],
[1000, 1500, 2200, 4660],
font_size=8.5,
center_cols=(0, 1),
)
add_body(doc, "风扇 PWM 为100 Hz:首次启动100%持续2.00 s,之后1/2/3档为50%/70%/100%;运行中换档不重复启动增压。靠背加热 PWM 为100 Hz,1/2/3档为30%/60%/90%,连续运行600 s后安全关闭。")
doc.add_heading("3.2 移动命令 0x300", level=2)
add_table(
doc,
["Byte", "字段", "说明"],
[
["0", "Opcode", "01绝对、02相对、03定时、08单停、09全停、0A标定、0B已知量程回最近端、0C查询、0D清故障"],
["1", "Control", "0x10 | MotorID[1:0] | Direction[3:2];Direction 0/1/2=停止/Far/Near"],
["2..3", "Sequence", "uint16 little-endian;重复帧不重做,旧序列拒绝;STOP_ALL例外"],
["4..5", "ParamA", "命令参数,little-endian"],
["6..7", "ParamB", "命令参数,little-endian"],
],
[950, 1800, 6610],
font_size=9,
center_cols=(0,),
)
add_table(
doc,
["Opcode", "命令", "ParamA", "ParamB"],
[
["01", "MOVE_ABSOLUTE", "0~10000", "0=默认或100~60000 ms"],
["02", "MOVE_RELATIVE", "int16 -10000~10000", "0=默认或100~60000 ms"],
["03", "MOVE_TIMED", "100~60000 ms", "驱动等级×10,300~1000"],
["08", "STOP_MOTOR", "0", "0"],
["09", "STOP_ALL", "0", "0"],
["0A", "CALIBRATE_RANGE", "0xA55A", "每寻端10~600 s"],
["0B", "HOME_KNOWN_RANGE", "0x4B52", "最近端寻找10~600 s"],
["0C", "QUERY_STATUS", "0", "0"],
["0D", "CLEAR_FAULT", "0xC33C", "0"],
],
[950, 2500, 2950, 2960],
font_size=9,
center_cols=(0,),
)
doc.add_heading("3.3 移动反馈 0x301~0x303", level=2)
add_table(
doc,
["ID", "字节", "内容"],
[
["0x301", "B0", "低4位ACK;高4位SystemState"],
["0x301", "B1/B2..3/B4", "RejectReason / Sequence / LastOpcode"],
["0x301", "B5", "bit0..3 ActiveMask,bit4 inhibit,bit6 calibration,bit7 fault summary"],
["0x301", "B6/B7", "低4位FaultedMask,高4位PositionValidMask / Alive+固件修订号"],
["0x302", "B0/B1", "MotorID / 低4位TaskState,高4位Result"],
["0x302", "B2", "方向、active、position valid、near、far、stall、driver fault"],
["0x302", "B3..4/B5..6/B7", "Sequence / 0..10000归一化位置(无效=FFFF)/ duty"],
["0x303", "B0/B1", "MotorID / 低4位LastEvent,高4位FaultFlags"],
["0x303", "B2..3/B4..5", "HallCount / MaxHallCount"],
["0x303", "B6/B7", "Hall速度0.5 pulse/s/LSB / Retry+Alive"],
],
[1050, 2100, 6210],
font_size=8.5,
center_cols=(0, 1),
)
add_body(doc, "ACK:0 NONE、1 ACCEPTED、2 REJECTED、3 DUPLICATE、4 STOP_ALL_ACCEPTED。SystemState:0 INIT、1 READY、2 ACTIVE、3 INHIBITED、4 FAULT、5 CALIBRATION。")
add_body(doc, "RejectReason:0 NONE、1 BAD_DLC、2 BAD_VERSION、3 BAD_OPCODE、4 BAD_MOTOR、5 BAD_DIRECTION、6 PARAM_RANGE、7 POSITION_INVALID、8 SEQUENCE_OLD、9 BUSY_SAME_MOTOR、10 INTERLOCK、11 FAULT_ACTIVE、12 SERVICE_MODE_REQUIRED、13 CAL_DATA_INVALID、14 INTERNAL_ERROR。")
add_body(doc, "TaskState:0 IDLE、1 ACCEPTED、2 POSITION_RUN、3 TIMED_RUN、4 CALIBRATION、5 STOPPING、6 COMPLETE、7 FAULT。Result:0 NONE、1 RUNNING、2 OK、3 STOPPED_BY_CMD、4 ENDPOINT_REACHED、5 STALL、6 TIMEOUT、7 DRIVER_FAULT、8 HALL_FAULT、9 INVALID_CMD、10 INTERLOCKED、11 CALIBRATION_FAILED。")
def add_persistence(doc):
doc.add_heading("4 SMV8 本地绝对位置记忆", level=1)
add_body(doc, "SMV8 使用两个连续128-byte Work Flash 小扇区交替提交记录。每轴保存绝对 Hall、MaxHall、最大速度、累计 Hall 上升沿、物理绝对量与 PositionValid;记录还包含 Magic/Version、单调Sequence、Clean标志和CRC32。SMV5/SMV6 使用旧量程尺度,SMV7 使用旧非同号通道映射;固件升级后均视为不兼容记录,不直接恢复旧绝对位置。")
add_number(doc, "动作命令启动前写入较新 dirty 记录,写完后才允许 PWM 启动。")
add_number(doc, "绝对、相对、定时、全量程标定或已知量程回最近端动作执行;多轴可并发,记录保持 dirty。")
add_number(doc, "应用队列为空、所有任务结束且底层惯性计数停止后,采集四轴最终状态。")
add_number(doc, "向另一槽写入较新 clean 记录并逐字回读校验;相同 clean 数据不重复擦写。")
add_number(doc, "下次上电选取序列较新的CRC有效记录;只有 clean 记录才恢复绝对位置。")
add_callout(doc, "掉电安全与恢复", "运动中掉电时,较新的 dirty 记录会压过运动前 clean 记录,因此旧位置不会被误恢复为有效。重启后 PositionValid=0,绝对/相对命令先被拒绝;应用仅对失效轴执行0x0B回最近端,确认Hall=0且MaxHall未变后再继续目标。", fill="FFF4E5", color=RED)
add_callout(doc, "PositionValid与机械归零", "PositionValidMask=0xF且四轴处于非零位置是正常状态,表示SMV8恢复了当前绝对位置;只有Near=1且Hall约为0才证明对应轴在最近端。位置可信性未知时,测试界面必须强制四轴逐一真实执行0x0B。归零使用90% Duty;端点要求500 ms Hall双边沿为0。第一次寻近端少于4个真实Hall边沿时,固件先向远端验证至少4个双边沿再重新寻近端,并校验回程与离端边沿数(允许1边沿误差),禁止把未启动直接清零。绝对位置和量程仍按Hall上升沿计数。", fill="E8F2FF", color=BLUE)
add_table(
doc,
["场景", "重启行为", "CAN期望"],
[
["正常停止后掉电", "恢复四轴Hall/MaxHall/绝对量,不自动动作", "0x301 PositionValidMask恢复;0x302/0x303与掉电前一致"],
["运动中掉电", "不恢复旧位置;失效轴执行0x0B回最近端", "先PositionValid=0/位置FFFF;归零后Near=1、Hall=0、PositionValid=1"],
["最新槽CRC坏、旧槽有效", "回退到旧的有效clean槽", "恢复旧槽数据,无自动动作"],
["版本不匹配/双槽无效", "保持位置未知,允许0x0B建立原点", "PositionValidMask对应位先为0,归零成功后置1"],
["物理绝对量与Hall不一致", "拒绝恢复,保留CAN以执行0x0B", "PositionValid=0;诊断提示记录无效;首次成功保存后清除"],
],
[1900, 3300, 4160],
font_size=9,
)
def add_setup_and_acceptance(doc):
doc.add_heading("5 台架、仪器与强制安全条件", level=1)
for text in (
"座椅总成可靠固定;四轴全行程无人员、工具、线束与硬限位外障碍。",
"12 V电源具有限流与硬件急停;加热测试增加电流钳和表面温度监控。",
"ZCANPRO + USBCANFD-200U 配置为500 kbit/s Classic CAN,终端电阻按拓扑配置。",
"完整资格测试使用示波器/逻辑分析仪观察PWM与方向/使能;当前无示波器时对应时序项必须记为BLOCKED。",
"第一次动作仅单轴300~500 ms、30%~50%,目视签字确认逻辑轴、机械方向和Hall符号。",
"CAN MotorID 0/1/2/3 必须分别驱动 physical0/1/2/3:水平、坐盆、靠背、腿托;若任一机构不符立即停止。",
):
add_bullet(doc, text)
add_callout(doc, "加热限制", "当前ADC电流估算只用于台架可见性,尚无量产过流阈值标定,不能代替外部限流与温度保护。", fill="FFF4E5", color=RED)
doc.add_heading("5.1 执行顺序", level=2)
for text in (
"全量IAR构建并记录0 error / 0 warning。",
"上电安全与周期反馈;不发送动作命令。",
"通风、加热独立与组合功能。",
"非法帧、查询、重复/旧Sequence与STOP_ALL。",
"四轴低风险正反点动并签字确认方向。",
"单轴定时、同轴换向、单停与四轴并发。",
"首次量程确认使用全行程标定;量程已知但位置无效时只执行0x0B回最近端。",
"正常停止掉电恢复、运动中掉电失效、双槽CRC回退。",
"堵转、驱动故障、CAN bus-off、ADC故障和调试会话安全。",
):
add_number(doc, text)
doc.add_heading("5.2 当前台架回归路线(不再标定、无示波器)", level=2)
add_body(doc, "当前四轴量程已经确认,不重复全轴标定和25/50/75%定位。无示波器不影响CAN反馈、Hall、实物方向、风量、温升和正常掉电恢复,但PWM频率/占空比、换向死区及硬实时关断延迟只能记为BLOCKED,不能根据CAN反馈推定PASS。")
for text in (
"运行04_can_full_function.py:位置有效时直接做热管理和四轴约12%往返;位置无效时先只把失效轴回最近端。",
"在ZCANPRO运行05_seat_function_bridge.py,再双击run_seat_function_gui.bat;按配置表执行12个场景。界面可自动归零失效轴,并保存CAN结果与人工实物观察。",
"先用GUI执行巡航沙发标准场景并停稳,使四轴均处于可信非零位置;再运行02_power_memory.py默认PHASE=CLEAN。倒计时结束后只切断座椅电源至少2秒,电脑/CAN盒保持工作,并验证重启无自动动作及四轴SMV8恢复。",
"运行03_protection_faults.py默认PHASE=AUTO,验证非法帧与Sequence保护。",
"可选MOVING_LOSS先验证M0位置失效,再默认发送0x0B恢复,因此无需重新全量程标定。",
):
add_number(doc, text)
add_callout(doc, "配置表换算冲突", "模式目标按物理值和当前480/374/917/647量程重算,不直接发送表内Hall文本。办公模式M0原表已修正为约346 Hall;当前剩余冲突为女王副驾(行驶)M3 22°应约198 Hall(原表178)。", fill="FFF4E5", color=RED)
doc.add_heading("5.3 核心通过标准", level=2)
add_table(
doc,
["类别", "判定"],
[
["周期", "0x4FA/0x301=100±20 ms;每轴0x302=100±20 ms;每轴0x303=500±50 ms"],
["PWM", "100 Hz±1%;稳态占空比目标±2%;风扇启动增压2.00 s±50 ms"],
["停止", "STOP_MOTOR软件处理≤10 ms;STOP_ALL全部PWM kill≤2 ms,ActiveMask清零≤20 ms"],
["换向", "先全关,死区≥100 ms且Hall安静后反向;桥臂不得重叠"],
["位置", "目标误差≤max(归一化100,2个Hall换算值);Hall符号与机械方向一致"],
["标定", "M0 336~624;M1 261~486;M2 641~1192;M3 452~841"],
["记忆/恢复", "正常重启Hall差≤1;运动中掉电不得恢复旧位置;0x0B后Hall≤2且MaxHall不变"],
["热安全", "加热600 s±1 s安全关闭;ADC连续3次读取错误后关闭;故障反馈=0"],
],
[1800, 7560],
font_size=9,
)
def load_test_vectors():
with CSV_PATH.open("r", encoding="utf-8-sig", newline="") as f:
return list(csv.DictReader(f))
def add_test_catalog(doc):
doc.add_heading("6 完整 CAN/HIL 测试用例目录", level=1)
add_body(doc, "以下用例与配套 CSV 一致。表内“动作”包含发送帧及观察时间;执行时必须在 CSV 的 ActualRx、ActualPhysical、Result、Notes 列回填原始结果。")
groups = OrderedDict()
for row in load_test_vectors():
prefix = row["TestID"].split("-")[0]
groups.setdefault(prefix, []).append(row)
names = {
"SYS": "系统与启动",
"THM": "热管理组合",
"FAN": "座椅通风",
"HEAT": "靠背加热",
"MOV": "四轴移动与协议",
"CAL": "全行程标定",
"HOM": "已知量程回最近端",
"MEM": "位置记忆与掉电",
"FBK": "周期反馈",
"FLT": "故障注入",
}
widths = [800, 650, 1700, 2150, 2300, 1760]
for prefix, rows in groups.items():
doc.add_heading(f"6.{list(groups).index(prefix)+1} {names.get(prefix, prefix)}", level=2)
matrix = []
for row in rows:
tx = row["TxID"]
data = row["TxData_Byte0_to_7"]
action = f"{tx} {data}\n观察: {row['DelayOrObserve']}"
expected = row["ExpectedCAN"]
if row["ExpectedPhysical"]:
expected += f"\n物理: {row['ExpectedPhysical']}"
matrix.append([
row["TestID"], row["Priority"], row["Precondition"],
action, expected, row["Acceptance"],
])
add_table(
doc,
["ID", "优先级", "前置条件", "动作", "预期", "通过标准"],
matrix,
widths,
font_size=7.4,
center_cols=(0, 1),
)
def add_record_sheet(doc):
doc.add_heading("7 测试记录与签署", level=1)
add_body(doc, "每个用例至少保存:发送时间、完整TX/RX帧、机械方向、Hall前后值、PWM频率/占空比、停止延迟、供电电流、结果与异常截图。")
add_table(
doc,
["记录项", "填写内容"],
[
["样件/软件", "样件编号:________________ HEX CRC:________________ 构建日期:________________"],
["台架", "电源/限流:________________ CAN工具:________________ 终端电阻:________________"],
["机械映射", "M0 □ M1 □ M2 □ M3 □ 方向与端点已签字确认"],
["量程实测", "M0:______ M1:______ M2:______ M3:______ Hall"],
["位置记忆", "正常掉电恢复:□PASS □FAIL 运动中掉电失效:□PASS □FAIL 0x0B恢复:□PASS □FAIL"],
["总结果", "□PASS □FAIL □BLOCKED □NOT RUN"],
["备注", "________________________________________________________________________"],
],
[1800, 7560],
font_size=9,
)
add_table(
doc,
["角色", "姓名", "签字", "日期"],
[
["测试执行", "", "", ""],
["软件复核", "", "", ""],
["硬件/安全复核", "", "", ""],
["项目批准", "", "", ""],
],
[1800, 2300, 3000, 2260],
font_size=10,
center_cols=(0, 1, 2, 3),
)
doc.add_heading("附录 A 交付文件", level=1)
for text in (
"Seat_Controller_demo/IAR/rev_d/Exe/Seat_Controller_demo.hex",
"Seat_Controller_demo/Docs/座椅控制器_CAN_测试规范.docx",
"Seat_Controller_demo/Docs/座椅控制器_CAN_测试规范.md",
"Seat_Controller_demo/Docs/座椅控制器_CAN_测试向量.csv",
"Seat_Controller_demo/Tests/manual_hil/README_CN.md",
"Seat_Controller_demo/Tests/manual_hil/04_can_full_function.py",
"Seat_Controller_demo/Tests/manual_hil/05_seat_function_bridge.py",
"Seat_Controller_demo/Tests/manual_hil/run_seat_function_gui.bat",
"量程测试脚本/tests/03_full_range_test",
):
add_bullet(doc, text)
doc.add_heading("附录 B 修订记录", level=1)
add_table(
doc,
["版本", "日期", "变更"],
[
["1.0", "2026-07-28", "统一通风、加热、移动的基础 CAN/HIL 规范"],
["2.0", "2026-07-29", "纳入四轴实测量程;修正端点/方向;新增 SMV7 每次移动结束位置记忆与掉电测试;废弃 SMV5/SMV6 旧量程记录"],
["2.1", "2026-07-30", "按四轴量程日志改为 MotorID/physical Mode 同号映射;SMV8 禁止恢复旧通道语义;新增四组手动 HIL 日志脚本"],
["2.2", "2026-07-30", "当前回归不再标定且无示波器;新增配置表模式选择界面、ZCANPRO桥接、量程重算冲突提示和三组必做流程"],
["2.3", "2026-07-30", "新增HOME_KNOWN_RANGE(0x0B):位置未知时仅回最近端建立Hall=0,保留确认量程;脚本自动恢复后继续目标"],
],
[1000, 1600, 6760],
font_size=9,
center_cols=(0, 1),
)
def build():
doc = Document()
configure_document(doc)
add_cover(doc)
add_overview(doc)
add_calibration(doc)
add_can_protocol(doc)
add_persistence(doc)
add_setup_and_acceptance(doc)
add_test_catalog(doc)
add_record_sheet(doc)
core = doc.core_properties
core.title = "Seat_Controller_demo 座椅通风、加热与四轴移动 CAN/HIL 测试规范"
core.subject = "CAN/HIL test specification with SMV8 position persistence"
core.author = "Seat Controller Engineering"
core.keywords = "Seat, CAN, HIL, Hall, Calibration, SMV8"
core.comments = "Generated from the verified Seat_Controller_demo protocol and test-vector sources."
doc.save(OUTPUT_PATH)
print(OUTPUT_PATH)
if __name__ == "__main__":
build()
@@ -0,0 +1,76 @@
TestID,Module,Priority,Precondition,TxID,DLC,TxData_Byte0_to_7,DelayOrObserve,ExpectedRxID,ExpectedCAN,ExpectedPhysical,Acceptance,ActualRx,ActualPhysical,Result,Notes
SYS-001,Build,P0,IAR EWARM 9.2 available,N/A,N/A,N/A,N/A,N/A,CYT4BFBCJE and CY_MCU_rev_d and Cortex-M0+ Flash,No hardware action,0 errors and 0 warnings,,,,
SYS-002,Boot,P0,Cold POR without debugger and outputs monitored,N/A,N/A,N/A,2 s,0x301/0x4FA,System READY and ActiveMask=0; thermal feedback Byte0=09,All motor/fan/heater outputs initially low,No debugger is required for normal CAN operation,,,,
SYS-003,Debug safety,P0,Download and Debug active then end C-SPY,N/A,N/A,N/A,20 ms,0x301 or bus stops,System fault/inhibit if still running,All PWM and direction outputs low,Outputs low within 20 ms and stay low until genuine POR,,,,
THM-001,Thermal off,P0,System booted,0x29E,8,22 00 00 00 00 00 00 00,200 ms,0x4FA,Byte0=09,Fan and heater off,Feedback period 100 ms ±20 ms,,,,
FAN-001,Fan gear1,P0,THM-001 passed,0x29E,8,25 00 01 00 00 00 00 00,2.3 s,0x4FA,Byte0=0A,P11.1 100 Hz; 100% for 2 s then 50%,Frequency ±1%; duty ±2%; startup 2.00 s ±50 ms,,,,
FAN-002,Fan gear2,P0,FAN-001 running,0x29E,8,25 00 02 00 00 00 00 00,300 ms,0x4FA,Byte0=0B,P11.1 changes to 70% without another startup boost,Duty 70% ±2%,,,,
FAN-003,Fan gear3,P0,FAN-002 running,0x29E,8,25 00 03 00 00 00 00 00,300 ms,0x4FA,Byte0=0C,P11.1 100%,Duty 100% ±2%,,,,
FAN-004,Fan vehicle gear5 clamp,P1,FAN running,0x29E,8,25 00 05 00 00 00 00 00,300 ms,0x4FA,Byte0=0C,Physical gear remains3 / 100%,Clamp and feedback agree,,,,
FAN-005,Fan switch off,P0,FAN-004 running,0x42A,8,02 00 00 00 00 00 00 00,100 ms,0x4FA,Fan field=1,P11.1 GPIO low,Fan stops within 10 ms,,,,
FAN-006,Unused output,P0,All fan tests,N/A,N/A,N/A,Continuous,N/A,N/A,P11.2 remains GPIO low,No pulse or high glitch observed,,,,
THM-002,Configure max gear2,P1,Outputs off,0x2AA,8,01 00 12 00 00 00 00 00,20 ms,N/A,Valid config accepted,No output starts,No thermal invalid counter increment,,,,
THM-003,Config clamp combined,P1,THM-002 and heater mode valid,0x3CE,8,10 00 00 00 00 00 00 00,10 ms,N/A,Heater mode valid,No output change,N/A,,,,
THM-004,Config clamp combined,P1,THM-003,0x29E,8,15 00 33 00 00 00 00 00,2.3 s,0x4FA,Byte0=1B,Fan and heater both clamp to physical level2,Fan70% and heater60% ±2%,,,,
HEAT-001,Heater mode,P0,Outputs off,0x3CE,8,10 00 00 00 00 00 00 00,20 ms,N/A,Mode=1 accepted,Heater stays off,No invalid count increment,,,,
HEAT-002,Heater gear1,P0,HEAT-001; max config reset to3 via 0x2AA Byte2=1B,0x29E,8,12 00 10 00 00 00 00 00,300 ms,0x4FA,Byte0=11,P16.7 100 Hz 30%,Frequency ±1%; duty ±2%,,,,
HEAT-003,Heater gear2,P0,HEAT-002,0x29E,8,12 00 20 00 00 00 00 00,300 ms,0x4FA,Byte0=19,P16.7 60%,Duty ±2%,,,,
HEAT-004,Heater gear3,P0,HEAT-003,0x29E,8,12 00 30 00 00 00 00 00,300 ms,0x4FA,Byte0=21,P16.7 90%,Duty ±2%,,,,
HEAT-005,Heater invalid mode,P0,Heater running,0x3CE,8,00 00 00 00 00 00 00 00,100 ms,0x4FA,Heater field=0,P16.7 low,Invalid count increments and heater stops,,,,
HEAT-006,Heater timeout,P1,Valid mode and gear1 active,0x29E,8,12 00 10 00 00 00 00 00,605 s,0x4FA,Heater field=0 after timeout,P16.7 low at 600 s,Timeout 600 s ±1 s,,,,Long duration
THM-005,Combined levels,P0,Valid mode and max3,0x29E,8,15 00 32 00 00 00 00 00,2.3 s,0x4FA,Byte0=23,Fan gear2 and heater gear3,Fan70%; heater90% ±2%,,,,
THM-006,Combined off,P0,THM-005 running,0x42A,8,0A 00 00 00 00 00 00 00,100 ms,0x4FA,Byte0=09,Fan and heater low,Both stop within 10 ms,,,,
THM-007,Bad DLC fail-safe,P0,Outputs running,0x29E,7,25 00 02 00 00 00 00,100 ms,0x4FA,Byte0=00,Fan and heater low,Invalid frame cannot leave an output active,,,,
MOV-001,STOP_ALL baseline,P0,System running,0x300,8,09 10 00 01 00 00 00 00,20 ms,0x301,Ack=4; Seq=0x0100; Opcode=09; ActiveMask=0,All four motors off,PWM kill ≤2 ms; feedback ≤20 ms,,,,
MOV-002,QUERY M0,P0,MOV-001,0x300,8,0C 10 01 01 00 00 00 00,20 ms,0x301/0x302/0x303,Ack=1; Seq=0x0101; M0 status and diagnostic,No movement,All three frames within 20 ms,,,,
MOV-003,Bad version,P0,MOV-002,0x300,8,0C 00 02 01 00 00 00 00,20 ms,0x301,Ack=2; Reject=2 BAD_VERSION,No movement,Outputs and tasks unchanged,,,,
MOV-004,Bad opcode,P0,MOV-003,0x300,8,04 10 03 01 00 00 00 00,20 ms,0x301,Ack=2; Reject=3 BAD_OPCODE,No movement,Outputs unchanged,,,,
MOV-005,Bad STOP_ALL motor,P0,MOV-004,0x300,8,09 11 04 01 00 00 00 00,20 ms,0x301,Ack=2; Reject=4 BAD_MOTOR,No movement,Outputs unchanged,,,,
MOV-006,Bad direction,P0,MOV-005,0x300,8,01 14 05 01 88 13 40 1F,20 ms,0x301,Ack=2; Reject=5 BAD_DIRECTION,No movement,Outputs unchanged,,,,
MOV-007,Bad timed range,P0,MOV-006,0x300,8,03 14 06 01 63 00 20 03,20 ms,0x301,Ack=2; Reject=6 PARAM_RANGE,No movement,Duration 99 ms rejected,,,,
MOV-008,Uncalibrated position,P0,Target axis PositionValid=0,0x300,8,01 10 07 01 88 13 40 1F,20 ms,0x301/0x302,Ack=2; Reject=7 or13; position=FFFF,No movement,Position command rejected,,,,
MOV-009,M0 toward-far jog,P0,Axis mapping visually safe,0x300,8,03 14 08 01 F4 01 F4 01,700 ms,0x301/0x302/0x303,Ack=1; TIMED_RUN then COMPLETE/OK; Seq=0x0108,Seat moves rearward toward100%; Hall increases,500 ms ±50 ms; only physical0 driven,,,,
MOV-010,M0 toward-near jog,P0,MOV-009,0x300,8,03 18 09 01 F4 01 F4 01,700 ms,0x302/0x303,TIMED_RUN then COMPLETE/OK,Seat moves forward toward0%; Hall decreases,Direction and Hall sign correct,,,,
MOV-011,M1 toward-far jog,P0,physical0 rewiring visually confirmed,0x300,8,03 15 0A 01 F4 01 F4 01,700 ms,0x302/0x303,M1 only; Seq=0x010A,Seat pan moves diagonally downward; Hall increases,No old leg-extension mechanism motion,,,,
MOV-012,M1 toward-near jog,P0,MOV-011,0x300,8,03 19 0B 01 F4 01 F4 01,700 ms,0x302/0x303,M1 only,Seat pan moves diagonally upward; Hall decreases,Mapping signed off,,,,
MOV-013,M2 toward-far jog,P0,M2 connected,0x300,8,03 16 0C 01 F4 01 BC 02,700 ms,0x302/0x303,M2 only,Backrest reclines; Hall increases,Mapping signed off,,,,
MOV-014,M2 toward-near jog,P0,MOV-013,0x300,8,03 1A 0D 01 F4 01 BC 02,700 ms,0x302/0x303,M2 only,Backrest moves toward upright; Hall decreases,Mapping signed off,,,,
MOV-015,M3 positive jog,P0,M3 connected and clear,0x300,8,03 17 0E 01 F4 01 F4 01,700 ms,0x302/0x303,M3 only,Legrest raises; Hall increases,Mapping signed off or BLOCKED if not connected,,,,
MOV-016,M3 negative jog,P0,MOV-015,0x300,8,03 1B 0F 01 F4 01 F4 01,700 ms,0x302/0x303,M3 only,Legrest lowers; Hall decreases,Mapping signed off or BLOCKED,,,,
MOV-017,Duplicate sequence,P0,MOV-016 complete,0x300,8,0C 10 10 01 00 00 00 00,20 ms,0x301,Ack=1 for first query,No movement,Store first response then repeat next row,,,,
MOV-018,Duplicate sequence,P0,MOV-017,0x300,8,0C 10 10 01 00 00 00 00,20 ms,0x301,Ack=3 DUPLICATE; Seq=0x0110,No movement,No second task/action,,,,
MOV-019,Old sequence,P0,MOV-018,0x300,8,0C 10 0F 01 00 00 00 00,20 ms,0x301,Ack=2; Reject=8 SEQUENCE_OLD,No movement,State unchanged,,,,
MOV-020,Old sequence STOP_ALL,P0,At least one motor active,0x300,8,09 10 01 00 00 00 00 00,20 ms,0x301,Ack=4 STOP_ALL_ACCEPTED; ActiveMask=0,All motors off,Old STOP_ALL still kills within2 ms,,,,
MOV-021,Concurrent start M0,P0,Four mappings signed off,0x300,8,03 14 00 02 B8 0B 20 03,120 ms,0x301/0x302,M0 active,Horizontal moving positive,Continue immediately with next axes,,,,
MOV-022,Concurrent start M1,P0,MOV-021 active,0x300,8,03 15 01 02 B8 0B 20 03,120 ms,0x301/0x302,M0 and M1 active,Two intended axes move,No M0 Sequence/state change,,,,
MOV-023,Concurrent start M2,P0,MOV-022 active,0x300,8,03 16 02 02 B8 0B BC 02,120 ms,0x301/0x302,ActiveMask includes M0-M2,Three intended axes move,Independent Hall increments,,,,
MOV-024,Concurrent start M3,P0,MOV-023 active and power budget safe,0x300,8,03 17 03 02 B8 0B 20 03,300 ms,0x301/0x302,ActiveMask=0F,Four intended axes move,No cross-axis cancellation or wrong output,,,,
MOV-025,STOP_MOTOR M2,P0,MOV-024 active,0x300,8,08 12 04 02 00 00 00 00,50 ms,0x301/0x302,M2 Result=STOPPED_BY_CMD; other active bits stay,M2 stops only,Stop ≤10 ms; other Hall continues,,,,
MOV-026,STOP_ALL four axes,P0,MOV-025,0x300,8,09 10 05 02 00 00 00 00,20 ms,0x301,Ack=4; ActiveMask=0,All axes stop,Kill ≤2 ms,,,,
MOV-027,Same-axis reverse deadtime,P0,M0 positive 3 s command active,0x300,8,03 18 06 02 E8 03 20 03,1500 ms,0x302,M0 eventually negative; no other sequence changes,M0 output all-off then negative,Deadtime ≥100 ms and no bridge overlap,,,,
MOV-028,Absolute 50 percent,P1,Target axis calibrated and at safe position,0x300,8,01 10 07 02 88 13 40 1F,15 s,0x302,POSITION_RUN then COMPLETE/OK; position near5000,M0 reaches50%,Error ≤max(100 raw; two Hall pulses),,,,
MOV-029,Relative minus10 percent,P1,MOV-028 complete,0x300,8,02 10 08 02 18 FC 40 1F,15 s,0x302,COMPLETE/OK; final near4000,M0 moves -10%,Same position tolerance,,,,
MOV-030,M1 absolute 50 percent,P1,M1 calibrated and full path clear,0x300,8,01 11 09 02 88 13 40 1F,15 s,0x302/0x303,COMPLETE/OK; position near5000; Hall near187,Seat pan absolute displacement H25.5 mm and V28 mm,Error ≤max(100 raw; two Hall pulses),,,,
MOV-031,M2 absolute 50 percent,P1,M2 calibrated and full path clear,0x300,8,01 12 0A 02 88 13 E0 2E,15 s,0x302/0x303,COMPLETE/OK; position near5000; Hall near459,Backrest absolute angle near34 degrees,Error ≤max(100 raw; two Hall pulses),,,,
MOV-032,M3 absolute 50 percent,P1,M3 calibrated and full path clear,0x300,8,01 13 0B 02 88 13 10 27,15 s,0x302/0x303,COMPLETE/OK; position near5000; Hall near324,Legrest absolute angle near36 degrees,Error ≤max(100 raw; two Hall pulses),,,,
CAL-001,Calibration bad key,P0,All axes stopped,0x300,8,0A 10 09 02 00 00 78 00,20 ms,0x301,Ack=2; Reject=6 PARAM_RANGE,No movement,Invalid key cannot start calibration,,,,
CAL-002,Calibration bad timeout,P0,All axes stopped,0x300,8,0A 10 0A 02 5A A5 09 00,20 ms,0x301,Ack=2; Reject=6 PARAM_RANGE,No movement,9 s rejected,,,,
CAL-003,Calibration M0,P0,Full travel safe and all axes stopped,0x300,8,0A 10 0B 02 5A A5 78 00,Until complete,0x301/0x302/0x303,SystemState=CALIBRATION; periodic frames continue; final OK,M0 seeks front then rear endpoints,Each leg ≤120 s; MaxHall 336-624; saved CRC,,,,Mechanical full travel
CAL-004,STOP during calibration,P0,CALIBRATION active,0x300,8,09 10 01 00 00 00 00 00,50 ms,0x301/0x302,Ack=4; ActiveMask=0; no false OK,Motor stops immediately,Kill ≤2 ms; incomplete range not saved,,,,Destructive sequence reset
CAL-005,Four-axis calibration,P0,SMV8 absent or erased; full mechanism safe,0x300,8,M0-M3 sequential 0A 10/11/12/13 SeqLE 5A A5 78 00,Up to 10 min,0x301/0x302/0x303,PositionValidMask progresses to0F; final MaxHall near 480/374/917/647,Axes calibrate one at a time; MotorID equals physical Mode,M0 336-624; M1 261-486; M2 641-1192; M3 452-841,,,,Mechanical full travel
CAL-006,Calibration persistence,P0,CAL-005 passed,Power cycle,N/A,N/A,5 s,0x301/0x303,PositionValidMask=0F; MaxHall values unchanged,No automatic repeated full travel,Valid SMV8 restores all four positions,,,,
CAL-007,One valid one old slot,P1,Inject one valid SMV8 slot and one old/nonblank slot,Power cycle,N/A,N/A,5 s,0x301/0x303,PositionValid from valid slot; no CAL_DATA_INVALID,No automatic motion,No false corrupt report,,,,Bench flash fixture
HOM-001,Known-range home bad key,P0,All axes stopped,0x300,8,0B 10 0C 02 00 00 78 00,20 ms,0x301,Ack=2; Reject=6 PARAM_RANGE,No movement,Wrong safety key cannot start homing,,,,
HOM-002,M0 known-range near home,P0,M0 PositionValid=0; front travel clear; confirmed MaxHall=480,0x300,8,0B 10 0D 02 52 4B 78 00,Until complete,0x301/0x302/0x303,Ack=1; CALIBRATION then COMPLETE/OK; Near=1; PositionValid=1; Hall<=2; MaxHall=480,Seat moves only to the front endpoint,Does not visit rear endpoint or alter MaxHall,,,,Mechanical near-end travel
HOM-003,Four-axis invalid-only recovery,P0,At least one invalid axis; all near-end paths clear,0x300,8,Send 0B only for invalid M0-M3 with ParamA=524B and ParamB=7800,Until complete,0x301/0x302/0x303,PositionValidMask progresses to0F; each recovered axis Hall<=2; MaxHall 480/374/917/647,Only invalid axes move to their defined near endpoints,Already-valid axes do not move; no far-end measurement,,,,Mechanical near-end travel
HOM-004,Absolute target after recovery,P0,HOM-003 passed,0x300,8,01 10 12 02 B0 04 30 75,Until complete,0x302/0x303,M0 COMPLETE/OK near1200 normalized and about58 Hall,Seat moves from front endpoint to about25.8 mm,Target error <=max(100 raw; two Hall pulses),,,,
MEM-001,Save after absolute move,P0,All axes calibrated; record 0x302/0x303 before move,0x300,8,01 10 20 03 88 13 40 1F,After all motors settle,0x302/0x303,M0 position near5000; Hall near240; MaxHall near480,M0 near107.5 mm,Position saved only after coast/motion is fully stopped,,,,
MEM-002,Restore last stopped position,P0,MEM-001 passed and power removed only after completion,Power cycle,N/A,N/A,5 s,0x301/0x302/0x303,PositionValidMask=0F; M0 position/Hall equal pre-power values,No motor automatically moves,Hall difference ≤1 and normalized difference ≤2 Hall equivalents,,,,
MEM-003,Save after timed stop,P0,All axes calibrated,0x300,8,03 17 21 03 F4 01 F4 01,After completion then power cycle,0x302/0x303,M3 Hall/position restore to the value captured at stop,Legrest remains still,Restored values equal final pre-power feedback within1 Hall,,,,
MEM-004,Power loss during movement,P0,All axes calibrated and M0 moving,Remove power while active,N/A,N/A,Power cycle,0x301/0x302/0x303,M0 PositionValid=0; normalized position=FFFF,No automatic movement,Old pre-motion position must not be restored; then HOM-002 restores the origin,,,,Destructive power-cycle test
MEM-005,Dual-slot CRC fallback,P1,Create newest corrupt slot and older valid clean SMV8 slot,Power cycle,N/A,N/A,5 s,0x301/0x302/0x303,Older valid clean state restored,No automatic movement,Position and MaxHall match valid slot,,,,Bench flash fixture
FBK-001,Feedback periods,P0,System idle,N/A,N/A,N/A,2 s,0x301/0x302/0x303,All MotorID 0-3 present and Alive continuous,No movement,301=100±20 ms; per-axis302=100±20 ms;303=500±50 ms,,,,
FLT-001,Hall stall isolation,P1,Two axes active; safely block target axis,N/A,N/A,N/A,Until fault,0x301/0x302/0x303,Only target FaultedMask bit; STALL result/flag,Target output off; other axis follows policy,No wrong-axis stop or fault bit,,,,Use safe low current
FLT-002,CAN bus-off,P0,At least one output active,Disconnect/force bus-off,N/A,N/A,20 ms,N/A,Bus-off counter/fault latched,All outputs low,Safe-off within20 ms,,,,
FLT-003,Heater ADC read fault,P1,Heater active; inject ADC read errors,N/A,N/A,N/A,50 ms,0x4FA,Heater feedback=0,P16.7 low,Three consecutive 10 ms sample errors stop heat,,,,
FLT-004,Initialization failure,P0,Force selected peripheral init failure then reset,N/A,N/A,N/A,100 ms,N/A,No active feedback,All motor/fan/heater outputs low,No transient start,,,,
1 TestID Module Priority Precondition TxID DLC TxData_Byte0_to_7 DelayOrObserve ExpectedRxID ExpectedCAN ExpectedPhysical Acceptance ActualRx ActualPhysical Result Notes
2 SYS-001 Build P0 IAR EWARM 9.2 available N/A N/A N/A N/A N/A CYT4BFBCJE and CY_MCU_rev_d and Cortex-M0+ Flash No hardware action 0 errors and 0 warnings
3 SYS-002 Boot P0 Cold POR without debugger and outputs monitored N/A N/A N/A 2 s 0x301/0x4FA System READY and ActiveMask=0; thermal feedback Byte0=09 All motor/fan/heater outputs initially low No debugger is required for normal CAN operation
4 SYS-003 Debug safety P0 Download and Debug active then end C-SPY N/A N/A N/A 20 ms 0x301 or bus stops System fault/inhibit if still running All PWM and direction outputs low Outputs low within 20 ms and stay low until genuine POR
5 THM-001 Thermal off P0 System booted 0x29E 8 22 00 00 00 00 00 00 00 200 ms 0x4FA Byte0=09 Fan and heater off Feedback period 100 ms ±20 ms
6 FAN-001 Fan gear1 P0 THM-001 passed 0x29E 8 25 00 01 00 00 00 00 00 2.3 s 0x4FA Byte0=0A P11.1 100 Hz; 100% for 2 s then 50% Frequency ±1%; duty ±2%; startup 2.00 s ±50 ms
7 FAN-002 Fan gear2 P0 FAN-001 running 0x29E 8 25 00 02 00 00 00 00 00 300 ms 0x4FA Byte0=0B P11.1 changes to 70% without another startup boost Duty 70% ±2%
8 FAN-003 Fan gear3 P0 FAN-002 running 0x29E 8 25 00 03 00 00 00 00 00 300 ms 0x4FA Byte0=0C P11.1 100% Duty 100% ±2%
9 FAN-004 Fan vehicle gear5 clamp P1 FAN running 0x29E 8 25 00 05 00 00 00 00 00 300 ms 0x4FA Byte0=0C Physical gear remains3 / 100% Clamp and feedback agree
10 FAN-005 Fan switch off P0 FAN-004 running 0x42A 8 02 00 00 00 00 00 00 00 100 ms 0x4FA Fan field=1 P11.1 GPIO low Fan stops within 10 ms
11 FAN-006 Unused output P0 All fan tests N/A N/A N/A Continuous N/A N/A P11.2 remains GPIO low No pulse or high glitch observed
12 THM-002 Configure max gear2 P1 Outputs off 0x2AA 8 01 00 12 00 00 00 00 00 20 ms N/A Valid config accepted No output starts No thermal invalid counter increment
13 THM-003 Config clamp combined P1 THM-002 and heater mode valid 0x3CE 8 10 00 00 00 00 00 00 00 10 ms N/A Heater mode valid No output change N/A
14 THM-004 Config clamp combined P1 THM-003 0x29E 8 15 00 33 00 00 00 00 00 2.3 s 0x4FA Byte0=1B Fan and heater both clamp to physical level2 Fan70% and heater60% ±2%
15 HEAT-001 Heater mode P0 Outputs off 0x3CE 8 10 00 00 00 00 00 00 00 20 ms N/A Mode=1 accepted Heater stays off No invalid count increment
16 HEAT-002 Heater gear1 P0 HEAT-001; max config reset to3 via 0x2AA Byte2=1B 0x29E 8 12 00 10 00 00 00 00 00 300 ms 0x4FA Byte0=11 P16.7 100 Hz 30% Frequency ±1%; duty ±2%
17 HEAT-003 Heater gear2 P0 HEAT-002 0x29E 8 12 00 20 00 00 00 00 00 300 ms 0x4FA Byte0=19 P16.7 60% Duty ±2%
18 HEAT-004 Heater gear3 P0 HEAT-003 0x29E 8 12 00 30 00 00 00 00 00 300 ms 0x4FA Byte0=21 P16.7 90% Duty ±2%
19 HEAT-005 Heater invalid mode P0 Heater running 0x3CE 8 00 00 00 00 00 00 00 00 100 ms 0x4FA Heater field=0 P16.7 low Invalid count increments and heater stops
20 HEAT-006 Heater timeout P1 Valid mode and gear1 active 0x29E 8 12 00 10 00 00 00 00 00 605 s 0x4FA Heater field=0 after timeout P16.7 low at 600 s Timeout 600 s ±1 s Long duration
21 THM-005 Combined levels P0 Valid mode and max3 0x29E 8 15 00 32 00 00 00 00 00 2.3 s 0x4FA Byte0=23 Fan gear2 and heater gear3 Fan70%; heater90% ±2%
22 THM-006 Combined off P0 THM-005 running 0x42A 8 0A 00 00 00 00 00 00 00 100 ms 0x4FA Byte0=09 Fan and heater low Both stop within 10 ms
23 THM-007 Bad DLC fail-safe P0 Outputs running 0x29E 7 25 00 02 00 00 00 00 100 ms 0x4FA Byte0=00 Fan and heater low Invalid frame cannot leave an output active
24 MOV-001 STOP_ALL baseline P0 System running 0x300 8 09 10 00 01 00 00 00 00 20 ms 0x301 Ack=4; Seq=0x0100; Opcode=09; ActiveMask=0 All four motors off PWM kill ≤2 ms; feedback ≤20 ms
25 MOV-002 QUERY M0 P0 MOV-001 0x300 8 0C 10 01 01 00 00 00 00 20 ms 0x301/0x302/0x303 Ack=1; Seq=0x0101; M0 status and diagnostic No movement All three frames within 20 ms
26 MOV-003 Bad version P0 MOV-002 0x300 8 0C 00 02 01 00 00 00 00 20 ms 0x301 Ack=2; Reject=2 BAD_VERSION No movement Outputs and tasks unchanged
27 MOV-004 Bad opcode P0 MOV-003 0x300 8 04 10 03 01 00 00 00 00 20 ms 0x301 Ack=2; Reject=3 BAD_OPCODE No movement Outputs unchanged
28 MOV-005 Bad STOP_ALL motor P0 MOV-004 0x300 8 09 11 04 01 00 00 00 00 20 ms 0x301 Ack=2; Reject=4 BAD_MOTOR No movement Outputs unchanged
29 MOV-006 Bad direction P0 MOV-005 0x300 8 01 14 05 01 88 13 40 1F 20 ms 0x301 Ack=2; Reject=5 BAD_DIRECTION No movement Outputs unchanged
30 MOV-007 Bad timed range P0 MOV-006 0x300 8 03 14 06 01 63 00 20 03 20 ms 0x301 Ack=2; Reject=6 PARAM_RANGE No movement Duration 99 ms rejected
31 MOV-008 Uncalibrated position P0 Target axis PositionValid=0 0x300 8 01 10 07 01 88 13 40 1F 20 ms 0x301/0x302 Ack=2; Reject=7 or13; position=FFFF No movement Position command rejected
32 MOV-009 M0 toward-far jog P0 Axis mapping visually safe 0x300 8 03 14 08 01 F4 01 F4 01 700 ms 0x301/0x302/0x303 Ack=1; TIMED_RUN then COMPLETE/OK; Seq=0x0108 Seat moves rearward toward100%; Hall increases 500 ms ±50 ms; only physical0 driven
33 MOV-010 M0 toward-near jog P0 MOV-009 0x300 8 03 18 09 01 F4 01 F4 01 700 ms 0x302/0x303 TIMED_RUN then COMPLETE/OK Seat moves forward toward0%; Hall decreases Direction and Hall sign correct
34 MOV-011 M1 toward-far jog P0 physical0 rewiring visually confirmed 0x300 8 03 15 0A 01 F4 01 F4 01 700 ms 0x302/0x303 M1 only; Seq=0x010A Seat pan moves diagonally downward; Hall increases No old leg-extension mechanism motion
35 MOV-012 M1 toward-near jog P0 MOV-011 0x300 8 03 19 0B 01 F4 01 F4 01 700 ms 0x302/0x303 M1 only Seat pan moves diagonally upward; Hall decreases Mapping signed off
36 MOV-013 M2 toward-far jog P0 M2 connected 0x300 8 03 16 0C 01 F4 01 BC 02 700 ms 0x302/0x303 M2 only Backrest reclines; Hall increases Mapping signed off
37 MOV-014 M2 toward-near jog P0 MOV-013 0x300 8 03 1A 0D 01 F4 01 BC 02 700 ms 0x302/0x303 M2 only Backrest moves toward upright; Hall decreases Mapping signed off
38 MOV-015 M3 positive jog P0 M3 connected and clear 0x300 8 03 17 0E 01 F4 01 F4 01 700 ms 0x302/0x303 M3 only Legrest raises; Hall increases Mapping signed off or BLOCKED if not connected
39 MOV-016 M3 negative jog P0 MOV-015 0x300 8 03 1B 0F 01 F4 01 F4 01 700 ms 0x302/0x303 M3 only Legrest lowers; Hall decreases Mapping signed off or BLOCKED
40 MOV-017 Duplicate sequence P0 MOV-016 complete 0x300 8 0C 10 10 01 00 00 00 00 20 ms 0x301 Ack=1 for first query No movement Store first response then repeat next row
41 MOV-018 Duplicate sequence P0 MOV-017 0x300 8 0C 10 10 01 00 00 00 00 20 ms 0x301 Ack=3 DUPLICATE; Seq=0x0110 No movement No second task/action
42 MOV-019 Old sequence P0 MOV-018 0x300 8 0C 10 0F 01 00 00 00 00 20 ms 0x301 Ack=2; Reject=8 SEQUENCE_OLD No movement State unchanged
43 MOV-020 Old sequence STOP_ALL P0 At least one motor active 0x300 8 09 10 01 00 00 00 00 00 20 ms 0x301 Ack=4 STOP_ALL_ACCEPTED; ActiveMask=0 All motors off Old STOP_ALL still kills within2 ms
44 MOV-021 Concurrent start M0 P0 Four mappings signed off 0x300 8 03 14 00 02 B8 0B 20 03 120 ms 0x301/0x302 M0 active Horizontal moving positive Continue immediately with next axes
45 MOV-022 Concurrent start M1 P0 MOV-021 active 0x300 8 03 15 01 02 B8 0B 20 03 120 ms 0x301/0x302 M0 and M1 active Two intended axes move No M0 Sequence/state change
46 MOV-023 Concurrent start M2 P0 MOV-022 active 0x300 8 03 16 02 02 B8 0B BC 02 120 ms 0x301/0x302 ActiveMask includes M0-M2 Three intended axes move Independent Hall increments
47 MOV-024 Concurrent start M3 P0 MOV-023 active and power budget safe 0x300 8 03 17 03 02 B8 0B 20 03 300 ms 0x301/0x302 ActiveMask=0F Four intended axes move No cross-axis cancellation or wrong output
48 MOV-025 STOP_MOTOR M2 P0 MOV-024 active 0x300 8 08 12 04 02 00 00 00 00 50 ms 0x301/0x302 M2 Result=STOPPED_BY_CMD; other active bits stay M2 stops only Stop ≤10 ms; other Hall continues
49 MOV-026 STOP_ALL four axes P0 MOV-025 0x300 8 09 10 05 02 00 00 00 00 20 ms 0x301 Ack=4; ActiveMask=0 All axes stop Kill ≤2 ms
50 MOV-027 Same-axis reverse deadtime P0 M0 positive 3 s command active 0x300 8 03 18 06 02 E8 03 20 03 1500 ms 0x302 M0 eventually negative; no other sequence changes M0 output all-off then negative Deadtime ≥100 ms and no bridge overlap
51 MOV-028 Absolute 50 percent P1 Target axis calibrated and at safe position 0x300 8 01 10 07 02 88 13 40 1F 15 s 0x302 POSITION_RUN then COMPLETE/OK; position near5000 M0 reaches50% Error ≤max(100 raw; two Hall pulses)
52 MOV-029 Relative minus10 percent P1 MOV-028 complete 0x300 8 02 10 08 02 18 FC 40 1F 15 s 0x302 COMPLETE/OK; final near4000 M0 moves -10% Same position tolerance
53 MOV-030 M1 absolute 50 percent P1 M1 calibrated and full path clear 0x300 8 01 11 09 02 88 13 40 1F 15 s 0x302/0x303 COMPLETE/OK; position near5000; Hall near187 Seat pan absolute displacement H25.5 mm and V28 mm Error ≤max(100 raw; two Hall pulses)
54 MOV-031 M2 absolute 50 percent P1 M2 calibrated and full path clear 0x300 8 01 12 0A 02 88 13 E0 2E 15 s 0x302/0x303 COMPLETE/OK; position near5000; Hall near459 Backrest absolute angle near34 degrees Error ≤max(100 raw; two Hall pulses)
55 MOV-032 M3 absolute 50 percent P1 M3 calibrated and full path clear 0x300 8 01 13 0B 02 88 13 10 27 15 s 0x302/0x303 COMPLETE/OK; position near5000; Hall near324 Legrest absolute angle near36 degrees Error ≤max(100 raw; two Hall pulses)
56 CAL-001 Calibration bad key P0 All axes stopped 0x300 8 0A 10 09 02 00 00 78 00 20 ms 0x301 Ack=2; Reject=6 PARAM_RANGE No movement Invalid key cannot start calibration
57 CAL-002 Calibration bad timeout P0 All axes stopped 0x300 8 0A 10 0A 02 5A A5 09 00 20 ms 0x301 Ack=2; Reject=6 PARAM_RANGE No movement 9 s rejected
58 CAL-003 Calibration M0 P0 Full travel safe and all axes stopped 0x300 8 0A 10 0B 02 5A A5 78 00 Until complete 0x301/0x302/0x303 SystemState=CALIBRATION; periodic frames continue; final OK M0 seeks front then rear endpoints Each leg ≤120 s; MaxHall 336-624; saved CRC Mechanical full travel
59 CAL-004 STOP during calibration P0 CALIBRATION active 0x300 8 09 10 01 00 00 00 00 00 50 ms 0x301/0x302 Ack=4; ActiveMask=0; no false OK Motor stops immediately Kill ≤2 ms; incomplete range not saved Destructive sequence reset
60 CAL-005 Four-axis calibration P0 SMV8 absent or erased; full mechanism safe 0x300 8 M0-M3 sequential 0A 10/11/12/13 SeqLE 5A A5 78 00 Up to 10 min 0x301/0x302/0x303 PositionValidMask progresses to0F; final MaxHall near 480/374/917/647 Axes calibrate one at a time; MotorID equals physical Mode M0 336-624; M1 261-486; M2 641-1192; M3 452-841 Mechanical full travel
61 CAL-006 Calibration persistence P0 CAL-005 passed Power cycle N/A N/A 5 s 0x301/0x303 PositionValidMask=0F; MaxHall values unchanged No automatic repeated full travel Valid SMV8 restores all four positions
62 CAL-007 One valid one old slot P1 Inject one valid SMV8 slot and one old/nonblank slot Power cycle N/A N/A 5 s 0x301/0x303 PositionValid from valid slot; no CAL_DATA_INVALID No automatic motion No false corrupt report Bench flash fixture
63 HOM-001 Known-range home bad key P0 All axes stopped 0x300 8 0B 10 0C 02 00 00 78 00 20 ms 0x301 Ack=2; Reject=6 PARAM_RANGE No movement Wrong safety key cannot start homing
64 HOM-002 M0 known-range near home P0 M0 PositionValid=0; front travel clear; confirmed MaxHall=480 0x300 8 0B 10 0D 02 52 4B 78 00 Until complete 0x301/0x302/0x303 Ack=1; CALIBRATION then COMPLETE/OK; Near=1; PositionValid=1; Hall<=2; MaxHall=480 Seat moves only to the front endpoint Does not visit rear endpoint or alter MaxHall Mechanical near-end travel
65 HOM-003 Four-axis invalid-only recovery P0 At least one invalid axis; all near-end paths clear 0x300 8 Send 0B only for invalid M0-M3 with ParamA=524B and ParamB=7800 Until complete 0x301/0x302/0x303 PositionValidMask progresses to0F; each recovered axis Hall<=2; MaxHall 480/374/917/647 Only invalid axes move to their defined near endpoints Already-valid axes do not move; no far-end measurement Mechanical near-end travel
66 HOM-004 Absolute target after recovery P0 HOM-003 passed 0x300 8 01 10 12 02 B0 04 30 75 Until complete 0x302/0x303 M0 COMPLETE/OK near1200 normalized and about58 Hall Seat moves from front endpoint to about25.8 mm Target error <=max(100 raw; two Hall pulses)
67 MEM-001 Save after absolute move P0 All axes calibrated; record 0x302/0x303 before move 0x300 8 01 10 20 03 88 13 40 1F After all motors settle 0x302/0x303 M0 position near5000; Hall near240; MaxHall near480 M0 near107.5 mm Position saved only after coast/motion is fully stopped
68 MEM-002 Restore last stopped position P0 MEM-001 passed and power removed only after completion Power cycle N/A N/A 5 s 0x301/0x302/0x303 PositionValidMask=0F; M0 position/Hall equal pre-power values No motor automatically moves Hall difference ≤1 and normalized difference ≤2 Hall equivalents
69 MEM-003 Save after timed stop P0 All axes calibrated 0x300 8 03 17 21 03 F4 01 F4 01 After completion then power cycle 0x302/0x303 M3 Hall/position restore to the value captured at stop Legrest remains still Restored values equal final pre-power feedback within1 Hall
70 MEM-004 Power loss during movement P0 All axes calibrated and M0 moving Remove power while active N/A N/A Power cycle 0x301/0x302/0x303 M0 PositionValid=0; normalized position=FFFF No automatic movement Old pre-motion position must not be restored; then HOM-002 restores the origin Destructive power-cycle test
71 MEM-005 Dual-slot CRC fallback P1 Create newest corrupt slot and older valid clean SMV8 slot Power cycle N/A N/A 5 s 0x301/0x302/0x303 Older valid clean state restored No automatic movement Position and MaxHall match valid slot Bench flash fixture
72 FBK-001 Feedback periods P0 System idle N/A N/A N/A 2 s 0x301/0x302/0x303 All MotorID 0-3 present and Alive continuous No movement 301=100±20 ms; per-axis302=100±20 ms;303=500±50 ms
73 FLT-001 Hall stall isolation P1 Two axes active; safely block target axis N/A N/A N/A Until fault 0x301/0x302/0x303 Only target FaultedMask bit; STALL result/flag Target output off; other axis follows policy No wrong-axis stop or fault bit Use safe low current
74 FLT-002 CAN bus-off P0 At least one output active Disconnect/force bus-off N/A N/A 20 ms N/A Bus-off counter/fault latched All outputs low Safe-off within20 ms
75 FLT-003 Heater ADC read fault P1 Heater active; inject ADC read errors N/A N/A N/A 50 ms 0x4FA Heater feedback=0 P16.7 low Three consecutive 10 ms sample errors stop heat
76 FLT-004 Initialization failure P0 Force selected peripheral init failure then reset N/A N/A N/A 100 ms N/A No active feedback All motor/fan/heater outputs low No transient start
@@ -0,0 +1,177 @@
# 座椅控制器 CAN/HIL 测试规范
版本:2.3(2026-07-30)
对象:`Seat_Controller_demo`、`量程测试脚本`
MCU:CYT4BFBCJE / CY_MCU_rev_d / Cortex-M0+ Flash
总线:Classic CAN,11-bit 标准帧,500 kbit/s,DLC=8,Intel/Little-endian
## 1. 目的与范围
本规范用于验证统一控制器的座椅通风、靠背加热、四轴移动、自动标定、反馈周期和安全停机。测试分为静态构建、无机械动作 CAN 检查、单轴低风险动作、多轴并发、全行程标定和故障注入。未经安全确认不得直接跳到多轴或标定阶段。
配套文件:
- 移动矩阵:`Seat_Controller_demo/Docs/outputs/seat_move_four_motor_20260724/座椅四电机移动功能CAN矩阵表.xlsx`
- 可执行向量:`Seat_Controller_demo/Docs/座椅控制器_CAN_测试向量.csv`
- 控制器镜像:`Seat_Controller_demo/IAR/rev_d/Exe/Seat_Controller_demo.hex`
- 量程程序:`量程测试脚本/tests/03_full_range_test`
- 功能模式源表:仓库根目录 `座椅功能模式配置表.xlsx`
- 当前实物操作说明:`Seat_Controller_demo/Tests/manual_hil/README_CN.md`
## 1.1 标定基线与绝对坐标
所有轴统一定义“最近端=0 Hall=0%=绝对位置0”,“从最近端向最远端=TowardFar=Hall递增”,“最远端=MaxHallCount=100%”。实测基线如下。
| MotorID | 功能 | 最近端(0%) | 最远端(100%) | MaxHall | 全量程 | 单 Hall 分辨率 |
|---:|---|---|---|---:|---|---|
| 0 | 水平前后 | 座椅最前方 | 座椅最后方 | 480 | 215 mm | 0.4479 mm |
| 1 | 坐盆斜上下 | 坐盆最上方 | 坐盆斜向下方 | 374 | 水平51 mm、垂直56 mm | 水平0.1364 mm、垂直0.1497 mm |
| 2 | 靠背前后 | 最前方,靠背垂直地面 | 向后躺倒 | 917 | 68° | 0.0742° |
| 3 | 腿托上下 | 最下方,腿托垂直地面 | 向上抬起 | 647 | 72° | 0.1113° |
绝对换算统一使用 `round(HallCount × FullScale / MaxHallCount)`;归一化位置使用 `round(HallCount × 10000 / MaxHallCount)`。M1 对同一归一化位置分别换算水平与垂直分量。CAN MotorID 与 DRV8718 物理 Mode 为同号映射。`PositionValidMask`表示ECU中的绝对位置记录是否可信,不表示轴位于最近端。首次刷写 SMV8、两个槽均无效或运动中掉电后,当前 Hall 原点不可信;本台架量程已确认,可先对失效轴执行 `HOME_KNOWN_RANGE(0x0B)` 回最近端建立 Hall=0,再接受绝对/相对位置命令,不需要重新访问最远端。若机构在ECU掉电时被外力改变或位置可信性未知,不能直接把当前位置赋为绝对位置,必须无条件对相关轴真实执行 `0x0B`;测试界面的强制归零操作会依次处理全部四轴。
## 2. 强制安全条件
1. 座椅必须固定,四轴全行程无人员、工具、线束和硬限位外障碍;12 V 电源具有限流和急停。
2. 第一次动作只允许单轴 300~500 ms、30%~50% 等级,并安排人员观察真实方向。
3. 必须确认同号轴映射:M0 水平→physical0,M1 坐盆→physical1,M2 靠背→physical2,M3 腿托→physical3。任一 MotorID 驱动了错误执行机构时立即停止,不得继续位置标定。
4. 运行量程测试脚本或发送全行程标定命令前,先准备可随时发送的 `0x300 09 10 00 00 00 00 00 00`,并保留硬件急停。
5. 加热测试需监控供电电流与表面温升;当前 ADC 电流值仅用于台架可见性,没有量产过流阈值标定,不能替代外部限流。
## 3. 信号速查
### 3.1 热管理
- RX `0x29E`:Byte0[1:0] 风扇开关,Byte0[3:2] 风扇模式,Byte0[5:4] 加热开关;Byte2[3:0] 风扇档位,Byte2[7:4] 加热档位。
- RX `0x2AA`:Byte0=1;Byte2[2:0] 风扇最大配置,Byte2[5:3] 加热最大配置。
- RX `0x3CE`:Byte0[5:4] 加热模式,1/2 有效。
- RX `0x42A`:Byte0[1:0] 风扇开关,Byte0[3:2] 加热开关。
- TX `0x4FA`:Byte0[2:0] 风扇反馈,Byte0[5:3] 加热反馈;0=故障/无效,1=关闭,2/3/4=1/2/3档。
风扇为 100 Hz;首次启动 100% 持续 2 s,随后 1/2/3档分别为 50%/70%/100%。靠背加热为 100 Hz,1/2/3档分别为 30%/60%/90%,连续加热 600 s 后安全关闭。
### 3.2 移动命令 `0x300`
Byte0=Opcode;Byte1=`0x10 | MotorID | (Direction<<2)`;Byte2..3=Sequence;Byte4..5=ParamA;Byte6..7=ParamB,所有多字节字段低字节在前。Direction:0=停止/不适用,1=TowardFar,2=TowardNear。
| Opcode | 命令 | ParamA | ParamB |
|---:|---|---|---|
| `01` | 绝对位置 | 0~10000 | 0=轴默认超时,或100~60000 ms |
| `02` | 相对位置 | int16,-10000~10000 | 0=轴默认超时,或100~60000 ms |
| `03` | 定时移动 | 100~60000 ms | 驱动等级×10,300~1000 |
| `08` | 单轴停止 | 0 | 0 |
| `09` | 全部停止 | 0 | 0 |
| `0A` | 全行程标定 | `0xA55A` | 每个寻端行程10~600 s |
| `0B` | 已知量程回最近端 | `0x4B52` | 最近端寻找10~600 s |
| `0C` | 查询状态 | 0 | 0 |
| `0D` | 清故障 | `0xC33C` | 0 |
### 3.3 移动反馈
- `0x301`:Byte0 低半字节 ACK、高半字节 SystemState;Byte1 RejectReason;Byte2..3 Sequence;Byte4 Opcode;Byte5 ActiveMask/抑制/标定/故障;Byte6 FaultedMask/PositionValidMask;Byte7 低半字节 Alive、高半字节 FirmwareRevision(当前为 2)。
- `0x302`:Byte0 MotorID;Byte1 TaskState/Result;Byte2 方向、Active、PositionValid、端点、堵转和驱动故障;Byte3..4 Sequence;Byte5..6 归一化位置;Byte7 驱动等级。
- `0x303`:Byte0 MotorID;Byte1 LastEvent/FaultFlags;Byte2..3 HallCount;Byte4..5 MaxHallCount;Byte6 Hall速度(0.5 pulse/s/LSB);Byte7 Retry/Alive。
ACK:0 NONE、1 ACCEPTED、2 REJECTED、3 DUPLICATE、4 STOP_ALL_ACCEPTED。RejectReason 与 TaskState/Result 以移动矩阵“枚举定义”页为准。`0x301` Byte5 bit5 当前固定为0,预留给未来通信监控。
RejectReason:0 NONE、1 BAD_DLC、2 BAD_VERSION、3 BAD_OPCODE、4 BAD_MOTOR、5 BAD_DIRECTION、6 PARAM_RANGE、7 POSITION_INVALID、8 SEQUENCE_OLD、9 BUSY_SAME_MOTOR、10 INTERLOCK、11 FAULT_ACTIVE、12 SERVICE_MODE_REQUIRED、13 CAL_DATA_INVALID、14 INTERNAL_ERROR。
TaskState:0 IDLE、1 ACCEPTED、2 POSITION_RUN、3 TIMED_RUN、4 CALIBRATION、5 STOPPING、6 COMPLETE、7 FAULT。Result:0 NONE、1 RUNNING、2 OK、3 STOPPED_BY_CMD、4 ENDPOINT_REACHED、5 STALL、6 TIMEOUT、7 DRIVER_FAULT、8 HALL_FAULT、9 INVALID_CMD、10 INTERLOCKED、11 CALIBRATION_FAILED。
### 3.4 本地位置记忆(SMV8)
- 两个128-byte Work Flash 小扇区交替保存,记录含 Magic/Version、单调 Sequence、Clean标志、四轴绝对 Hall、MaxHall、最大速度、累计 Hall 上升沿、物理绝对量、PositionValid 和 CRC32。
- 每个动作命令真正启动前先写入较新 dirty 记录;写完才允许 PWM 启动。若此时掉电,新记录阻止旧 clean 位置被恢复。
- 所有应用队列为空、四轴任务停止且底层惯性计数结束后,才写入较新 clean 记录。多轴并发只在最后一轴停稳后统一保存,避免运行中阻塞 Flash 操作。
- 正常重启恢复 Hall/MaxHall/绝对量后,`0x302` 直接报告归一化绝对位置,`0x303` 报告 Hall/MaxHall;不自动移动座椅。
- dirty、CRC错误、版本不匹配或物理绝对量与 Hall 换算不一致时不恢复位置,PositionValid=0,绝对/相对位置命令拒绝。量程已确认时用 `0x0B` 只寻找最近端:成功后Hall=0、PositionValid=1、MaxHall保持480/374/917/647,并在停稳后写入clean SMV8。
- `PositionValidMask=0xF`且四轴处于非零位置是正常状态;它说明SMV8保存的是四轴当前绝对位置。只有`0x302 Near=1`且`0x303 Hall≈0`才能证明对应轴位于最近端。强制归零的测试报告必须列出每轴真实发送的`0x0B`、终态Near、Hall和Position,禁止仅修改/观察Mask后宣称已归零。
- 已知量程归零使用90% Duty。端点活动窗口统计Hall双边沿,只有完整500 ms窗口内零边沿才能判定端点;绝对位置和量程仍按上升沿计数。第一次寻近端若累计不到4个原始Hall边沿,不允许直接把Hall清零;固件必须先向远端取得至少4个真实双边沿,再重新寻近端,回程边沿数应与离端边沿数相符(允许1个采样边界误差)。第二次仍无足够Hall或验证方向无法运动时结果必须为CALIBRATION_FAILED、PositionValid=0。已在最近端的轴因此会短暂离端再返回。
- 正常位置命令在安全事件停止时,若最终Hall已进入目标±2脉冲容差,则结果按OK完成;底层LastEvent仍保留在`0x303`供诊断。超过2脉冲的STALL/TIMEOUT不得由上位机放宽或伪判通过。
## 4. 仪器与记录
- IAR EWARM 9.2、CMSIS-DAP 烧录器。
- ZCANPRO + USBCANFD-200U,通道配置为 500 kbit/s Classic CAN,终端电阻按台架拓扑配置。
- 双通道以上示波器/逻辑分析仪:至少观察目标 PWM 和方向/使能;多轴干涉测试建议同时观察四路。
- 12 V 可限流电源、电流钳;加热测试另需温度计。
- 每个用例记录:发送时间、完整 TX/RX 帧、机械方向、Hall 前后值、PWM 频率/占空比、停止延迟、结果和异常截图。
若当前没有示波器,仍可执行CAN控制/反馈、Hall/绝对位置、实际机械方向、风量、温升、正常掉电记忆和协议拒绝测试;PWM频率/占空比、换向死区及硬实时关断延迟必须明确记为 `BLOCKED(缺少示波器)`,不得依据CAN反馈推定PASS。
## 5. 执行顺序
1. 全量 IAR 构建,确认 0 error / 0 warning 和目标宏。
2. 上电安全与周期反馈,不发送任何动作命令。
3. 通风与加热独立/组合测试。
4. 移动协议非法帧、查询、重复 Sequence、全停测试。
5. 每轴 300~500 ms 正反向点动并目视签字确认映射。
6. 单轴定时、同轴换向、单轴停止和四轴并发互不干涉。
7. 已知量程回最近端、每次移动结束保存、正常掉电恢复、运动中掉电失效及恢复、绝对/相对位置精度。
8. 堵转、驱动故障、CAN bus-off、C-SPY 结束和看门狗路径。
每个阶段开始前先发送全停,结束后再次发送全停。任何实物动作与预期不符时停止后续用例。
### 5.1 当前台架回归路线(已确认量程、无示波器)
本轮不重复全轴标定和25/50/75%定位。执行顺序为:
1. `04_can_full_function.py`:热管理档位/组合CAN反馈,以及每轴从当前位置偏移约12%再返回的实物冒烟。
2. `05_seat_function_bridge.py` + `run_seat_function_gui.bat`:从根目录配置表连续动态读取当前11种模式并展开为12个场景;小憩模式只保留1个标准场景,新增“小腿解压悬浮模式”,轻松进出模式包含2个场景。执行前显示预期物理动作、归一化位置、重算Hall和原表Hall,结束后保存CAN自动结果与人工实物观察。
3. `02_power_memory.py` 默认 `PHASE="CLEAN"`:只验证正常掉电SMV8恢复。
4. `03_protection_faults.py` 默认 `PHASE="AUTO"`:验证协议拒绝和Sequence保护。
运动中掉电会使M0位置有效性按设计失效。把 `02_power_memory.py` 改为 `PHASE="MOVING_LOSS"` 后,脚本先验证旧位置未被错误恢复,再默认用 `0x0B` 将M0回最近端恢复。旧 `01_calibration_position.py` 和 `04_waveform_full_function.py` 已设为归档保护,默认只记录SKIP。
配置表CAN目标以物理值按当前量程换算,不直接使用其Hall文本。办公模式M0原表已修正为约346 Hall;当前剩余显著冲突为女王副驾(行驶)M3 22°应为3056/约198 Hall而非178 Hall。
## 6. 通过标准
### 6.1 通用与周期
- 上电初始化和错误路径中所有执行器输出为低;P11.2 始终为 GPIO 低。
- `0x4FA`、`0x301` 周期 100 ms ±20 ms。
- 每个 MotorID 的 `0x302` 周期 100 ms ±20 ms,轴间约 25 ms;每个 MotorID 的 `0x303` 周期 500 ms ±50 ms,轴间约 125 ms。
- Alive 按模 16连续递增;2 s 监听窗口内 M0~M3 均不得漏报。
- PWM 频率 100 Hz ±1%;稳态占空比为目标值 ±2%。
### 6.2 通风与加热
- 风扇冷启动 100% 持续 2.00 s ±50 ms,之后切换目标档;已运行时换档不得重复 2 s 启动段。
- 风扇/加热档位反馈与实际限幅后物理档一致;关闭反馈为1,无效/故障反馈为0。
- 加热连续运行 600 s ±1 s 后输出关闭,状态为 TIMEOUT_SAFE_OFF,`0x4FA` 加热反馈为0。
- ADC 连续3次读取错误后加热关闭;不得把未标定电流值当作量产过流判据。
### 6.3 移动与干涉消除
- 定时动作持续时间误差不大于 ±50 ms 或命令时间的 ±2%(取较大者)。
- TowardFar Hall 单调增加,TowardNear Hall 单调减少;M0分别后移/前移,M1分别斜向下/斜向上,M2分别后躺/回正,M3分别抬起/放下。单轴动作不得改变其他轴 Sequence、TaskState 或 Hall(允许机械耦合造成的可解释微小惯性,但不得有驱动输出)。
- 同轴换向时先观察到方向/PWM 全关,死区不小于100 ms,再使能反向;正反桥臂不得重叠。
- `STOP_MOTOR` 只停止目标轴,软件处理延迟不大于10 ms;`STOP_ALL` 从完整 CAN 帧接收到所有 PWM kill 不大于2 ms,`ActiveMask=0` 反馈不大于20 ms。
- 重复帧只返回 DUPLICATE,不产生第二次动作;旧 Sequence 拒绝且输出不变;旧 Sequence 的 STOP_ALL 仍必须执行。
- 未标定时绝对/相对位置命令拒绝,CurrentPosition=`0xFFFF`;定时命令仍可用。
### 6.4 标定与位置
- 每轴按负端→正端完成,标定中 `0x301 SystemState=CALIBRATION`、Byte5 bit6=1,`0x302 TaskState=CALIBRATION`,周期反馈不中断。
- MaxHallCount 软件允许范围(实测值的70%~130%):M0 336~624,M1 261~486,M2 641~1192,M3 452~841,且大于等于8。台架目标值分别为480、374、917、647;超出允许范围标定失败。
- 成功后对应 PositionValid=1,负端位置0±1%,正端10000±1%。绝对位置目标误差不大于 `max(100 raw,2个Hall脉冲换算值)`。
- 每个移动会话开始前记录变 dirty,所有轴停稳后写入 clean SMV8;断电重启后 Hall、MaxHall、归一化位置和 PositionValid 恢复。一个有效 SMV8 槽加一个旧格式/损坏槽时仍使用有效槽。SMV5/SMV6 使用旧量程尺度,SMV7 使用旧非同号通道映射;升级后均不能直接恢复旧绝对位置,应对失效轴执行 `0x0B` 回最近端。
- `0x0B` 成功后只更新当前绝对原点,不重测量程;对应 `0x302` 必须报告 COMPLETE/OK、Near=1、PositionValid=1、位置0±1%,`0x303` Hall≤2且MaxHall仍为确认值。若初始Hall>2,上位机日志还必须观察到至少一个0与初始值之间的中间Hall,否则按FAIL处理,防止旧固件直接跳零。
- 标定期间发送 STOP_ALL 必须在2 ms内停机,不得提交未完成的新量程;允许保存“旧MaxHall+PositionValid=0”的安全状态,任务反馈为停止/标定失败,不能错误报告 OK。
- 正常停止后掉电,重启 Hall 差值不大于1;归一化位置差值不大于2个 Hall 对应值。运动中直接掉电,重启后该位置必须无效,禁止恢复为运动前的旧有效位置。
## 7. 测试结果分级
- PASS:CAN、物理动作、时序和安全判据全部满足。
- FAIL:任何输出误动作、映射错误、停止超时、反馈字段错误、周期丢失或持久化错误。
- BLOCKED:硬件未连接、无法安全完成全行程或缺少必要测量仪器;不得按 PASS 处理。
- NOT RUN:尚未执行。
## 8. 当前必须人工确认的项目
- MotorID 0/1/2/3 是否分别只驱动水平、坐盆、靠背、腿托机构。
- 四轴正/负方向是否与新矩阵 0%/100% 端定义一致。
- Work Flash 两个连续128-byte小扇区是否已在整车软件内存规划中保留,禁止与其它标定/NVM数据复用。
- 0x300~0x303 在整车网络中的 ID 是否已由网络负责人批准,避免与现网报文冲突。
File diff suppressed because it is too large Load Diff
@@ -8,14 +8,25 @@
#define MOTOR_API_FAILURE (1u)
#define MOTOR_PHYSICAL_VALUE_SCALE (100ul)
#define MOTOR_MODE0_DEFAULT_MAX_HALL_COUNT (480ul)
#define MOTOR_MODE0_MAX_POSITION_MM_X100 (10200ul)
#define MOTOR_MODE1_DEFAULT_MAX_HALL_COUNT (790ul)
#define MOTOR_MODE1_MAX_ANGLE_DEG_X100 (8500ul)
#define MOTOR_MODE2_DEFAULT_MAX_HALL_COUNT (1590ul)
#define MOTOR_MODE2_MAX_ANGLE_DEG_X100 (9000ul)
#define MOTOR_MODE3_DEFAULT_MAX_HALL_COUNT (960ul)
#define MOTOR_MODE3_MAX_POSITION_MM_X100 (24000ul)
/*
* 2026-07-30 endpoint-to-endpoint range calibration. Bench verification
* established identity mapping: logical M0..M3 use physical mode 0..3.
*/
#define MOTOR_MODE0_DEFAULT_MAX_HALL_COUNT (480ul) /* horizontal */
#define MOTOR_MODE0_MAX_POSITION_MM_X100 (21500ul)
#define MOTOR_MODE1_DEFAULT_MAX_HALL_COUNT (374ul) /* seat pan */
#define MOTOR_MODE1_MAX_VERTICAL_MM_X100 (5600ul)
#define MOTOR_MODE1_MAX_HORIZONTAL_MM_X100 (5100ul)
/* Compatibility name used by the generic scalar position API. */
#define MOTOR_MODE1_MAX_ANGLE_DEG_X100 \
MOTOR_MODE1_MAX_VERTICAL_MM_X100
#define MOTOR_MODE2_DEFAULT_MAX_HALL_COUNT (917ul) /* backrest */
#define MOTOR_MODE2_MAX_ANGLE_DEG_X100 (6800ul)
#define MOTOR_MODE3_DEFAULT_MAX_HALL_COUNT (647ul) /* legrest */
#define MOTOR_MODE3_MAX_ANGLE_DEG_X100 (7200ul)
/* Compatibility name used by the generic scalar position API. */
#define MOTOR_MODE3_MAX_POSITION_MM_X100 \
MOTOR_MODE3_MAX_ANGLE_DEG_X100
#define MOTOR_SPEED_VALUE_SCALE (100ul)
#define MOTOR_SPEED_MIN_DUTY_PERCENT (50u)
@@ -73,6 +84,8 @@ typedef struct
uint32_t valueX100;
uint32_t maxValueX100;
uint32_t positionMmX100;
uint32_t horizontalMmX100;
uint32_t verticalMmX100;
uint32_t angleDegX100;
uint32_t percentX100;
} motor_position_info_t;
@@ -103,6 +116,21 @@ typedef struct
uint8_t currentDutyPercent;
} motor_speed_info_t;
typedef struct
{
motor_mode_t mode;
bool sampleValid;
bool baselineValid;
bool highCurrent;
bool hallActivityLow;
bool calibrating;
uint16_t raw;
uint16_t baselineRaw;
uint16_t deltaRaw;
uint16_t stallThresholdRaw;
uint32_t sampleAgeMs;
} motor_current_info_t;
typedef struct
{
uint32_t positionHallCount;
@@ -122,9 +150,9 @@ uint32_t Motor_GetTimeMs(void);
void Motor_SetWatchdogServiceEnabled(bool enabled);
/*
* Physical values use fixed point x100: 102.00 mm is 10200 and
* 85.25 degrees is 8525. positionValid is false until an endpoint has
* been found after power-up.
* Physical values use fixed point x100: 215.00 mm is 21500 and
* 68.00 degrees is 6800. positionValid is restored from a verified clean
* record or becomes true after a known endpoint has been found.
*
* Ownership contract: call all control APIs and Motor_ServiceAll from one
* main-loop/owner context. They are not ISR-safe, thread-safe, or reentrant.
@@ -132,6 +160,7 @@ void Motor_SetWatchdogServiceEnabled(bool enabled);
* start calls from that owner.
*/
uint8_t Motor_GetPositionInfo(motor_mode_t mode, motor_position_info_t *info);
uint8_t Motor_GetCurrentInfo(motor_mode_t mode, motor_current_info_t *info);
/* Each mode has independent endpoint and stall reverse switches. */
uint8_t Motor_SetSafetyOptions(motor_mode_t mode,
@@ -146,6 +175,8 @@ bool Motor_IsMoving(motor_mode_t mode);
* Restore/store Hall-domain state. These calls are accepted only while the
* selected motor is stopped. max speed uses Hall rising edges/s x100.
*/
uint8_t Motor_ValidatePersistentState(motor_mode_t mode,
const motor_persistent_state_t *state);
uint8_t Motor_ApplyPersistentState(motor_mode_t mode,
const motor_persistent_state_t *state);
uint8_t Motor_GetPersistentState(motor_mode_t mode,
@@ -199,11 +230,17 @@ uint8_t Motor_GetSpeedInfo(motor_mode_t mode, motor_speed_info_t *info);
* range, preserves observed motion, and invalidates absolute position because
* physical travel cannot be rolled back. Each leg requires Hall coast
* tracking to settle before/after travel; quiet wait is bounded to five seconds.
* legTimeoutMs is the independent motion timeout applied to each endpoint leg.
*/
uint8_t Motor_Calibrate(motor_mode_t mode,
motor_calibration_action_t action,
uint32_t legTimeoutMs,
motor_calibration_result_t *result);
/* Application override used to keep CAN/status/safety services alive while
* the selected motor executes the blocking endpoint-calibration primitive. */
void Motor_CalibrationServiceHook(void);
/* Call continuously from the main loop; the ISR still kills PWM immediately. */
void Motor_ServiceAll(void);
@@ -218,16 +255,16 @@ void Motor_StopAll(void);
bool Motor_IsDriverFaultLatched(void);
uint8_t Motor0_GetPosition(motor_position_info_t *info);
uint8_t Motor1_GetAngle(motor_position_info_t *info);
uint8_t Motor1_GetPosition(motor_position_info_t *info);
uint8_t Motor2_GetAngle(motor_position_info_t *info);
uint8_t Motor3_GetPosition(motor_position_info_t *info);
uint8_t Motor3_GetAngle(motor_position_info_t *info);
uint8_t Motor0_MoveToPositionMmX100(uint32_t positionMmX100);
uint8_t Motor1_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor1_MoveToVerticalMmX100(uint32_t verticalMmX100);
uint8_t Motor2_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor3_MoveToPositionMmX100(uint32_t positionMmX100);
uint8_t Motor3_MoveToAngleDegX100(uint32_t angleDegX100);
uint8_t Motor0_SetSpeedPlan(uint32_t durationMs, uint32_t distanceMmX100);
uint8_t Motor1_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
uint8_t Motor1_SetSpeedPlan(uint32_t durationMs, uint32_t verticalMmX100);
uint8_t Motor2_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
uint8_t Motor3_SetSpeedPlan(uint32_t durationMs, uint32_t distanceMmX100);
uint8_t Motor3_SetSpeedPlan(uint32_t durationMs, uint32_t angleDegX100);
#endif /* MOTOR_CONTROL_H */
@@ -2326,6 +2326,12 @@
<file>
<name>$PROJ_DIR$\..\Bsp\bsp_seat_backrest_heater_adc.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\Bsp\bsp_seat_current_adc.c</name>
</file>
<file>
<name>$PROJ_DIR$\..\Bsp\bsp_seat_current_adc.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\Bsp\bsp_seat_fan.c</name>
</file>
@@ -2353,6 +2359,9 @@
<file>
<name>$PROJ_DIR$\..\Config\seat_fan_config.h</name>
</file>
<file>
<name>$PROJ_DIR$\..\Config\seat_current_adc_config.h</name>
</file>
</group>
<group>
<name>Docs</name>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
__pycache__/
logs/
seat_function_runtime/
@@ -0,0 +1,138 @@
"""Group 1: four-axis calibration plus 25/50/75 percent positioning."""
import importlib
import sys
import time
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
parse_303 = _hil_common.parse_303
AXES = (
(0, "M0 horizontal", 480, 336, 624, "near=seat front; far=seat rear"),
(1, "M1 seat pan", 374, 261, 486, "near=top; far=down"),
(2, "M2 backrest", 917, 641, 1192, "near=vertical/front; far=reclined"),
(3, "M3 legrest", 647, 452, 841, "near=down/vertical; far=raised"),
)
TARGETS = (2500, 5000, 7500)
ARCHIVED_DO_NOT_RUN = True
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
def calibrate_axis(s, axis, sequence):
motor, name, nominal, minimum, maximum, direction_note = axis
payload = command_payload(0x0A, motor, 0, sequence, 0xA55A, 120)
s.log("CALIBRATION_START %s %s" % (name, direction_note))
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note="full range calibration " + name)
status = None
if ack is not None and ack["ack"] == 1:
status = s.wait_motor_terminal(motor, sequence, 250.0)
time.sleep(0.8)
s.capture_state(1.0, clear=False)
diag = s.latest.get((0x303, motor))
passed = (ack is not None and ack["ack"] == 1 and
status is not None and status["task"] == 6 and
status["result"] == 2 and status["position_valid"] and
diag is not None and minimum <= diag["max_hall"] <= maximum and
abs(diag["hall"] - diag["max_hall"]) <= 3)
s.record("CAL-M%u" % motor, passed,
{"ack": ack, "status": status, "diag": diag},
"nominal=%u accepted=%u..%u; %s" %
(nominal, minimum, maximum, direction_note))
return passed
def position_axis(s, motor, target, sequence):
payload = command_payload(0x01, motor, 0, sequence, target, 20000)
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note="M%u absolute %u" % (motor, target))
status = None
if ack is not None and ack["ack"] == 1:
status = s.wait_motor_terminal(motor, sequence, 25.0)
time.sleep(0.6)
s.capture_state(0.8, clear=False)
diag = s.latest.get((0x303, motor))
expected_hall = None
hall_error = None
if diag is not None:
expected_hall = int(round(diag["max_hall"] * target / 10000.0))
hall_error = abs(diag["hall"] - expected_hall)
tolerance = max(4, int(round((diag or {"max_hall": 0})["max_hall"] * 0.02)))
passed = (ack is not None and ack["ack"] == 1 and
status is not None and status["task"] == 6 and
status["result"] == 2 and status["position_valid"] and
abs(status["position"] - target) <= 150 and
diag is not None and hall_error <= tolerance)
s.record("POS-M%u-%u" % (motor, target // 100), passed,
{"ack": ack, "status": status, "diag": diag,
"expected_hall": expected_hall, "hall_error": hall_error},
"normalized tolerance=150; Hall tolerance=%u" % tolerance)
return passed
def z_main():
global session
session = None
try:
session = HilSession("01_calibration_position")
s = session
if ARCHIVED_DO_NOT_RUN:
s.record("ARCHIVED", False, "not executed",
"confirmed ranges already exist; current test plan forbids repeated calibration",
status="SKIP")
return
s.write_operator_template([
"M0 CAL: near moved seat to front; far moved it rearward: PASS/FAIL ___",
"M1 CAL: near moved seat pan to top; far moved it down: PASS/FAIL ___",
"M2 CAL: near made backrest vertical; far reclined it: PASS/FAIL ___",
"M3 CAL: near lowered legrest vertical; far raised it: PASS/FAIL ___",
"No wrong actuator moved during any M0..M3 command: PASS/FAIL ___",
])
s.stop_all(0)
sequence = 0x0100
all_calibrated = True
for axis in AXES:
if s.stop_requested:
break
all_calibrated = calibrate_axis(s, axis, sequence) and all_calibrated
sequence += 1
time.sleep(1.0)
state = s.capture_state(2.0)
mask_ok = (state["system"] is not None and
state["system"]["position_valid_mask"] == 0x0F)
s.record("CAL-ALL-MASK", mask_ok, state["system"],
"PositionValidMask must be 0x0F")
if all_calibrated and mask_ok:
for motor in range(4):
for target in TARGETS:
if s.stop_requested:
break
position_axis(s, motor, target, sequence)
sequence += 1
else:
s.record("POSITION-MATRIX", False, "not executed",
"four-axis calibration/mask prerequisite failed",
status="NOT_RUN")
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error), "unhandled exception")
finally:
if session is not None:
try:
session.stop_all(0)
session.thermal_off()
except Exception as error:
session.log("FINAL_SAFE_OFF_ERROR " + repr(error))
session.finish({"expected_max_hall": [480, 374, 917, 647]})
@@ -0,0 +1,174 @@
"""Group 2: selectable SMV8 power-memory tests.
The safe default ``PHASE = "CLEAN"`` verifies a stopped power cycle without
invalidating position. ``MOVING_LOSS`` intentionally invalidates M0, verifies
that stale position is not restored, and then homes M0 to its known near end.
"""
import importlib
import sys
import time
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
PHASE = "CLEAN"
RECOVER_AFTER_MOVING_LOSS = True
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
def comparable_state(state):
output = []
for status, diag in zip(state["motors"], state["diagnostics"]):
output.append({
"position": None if status is None else status["position"],
"position_valid": None if status is None else status["position_valid"],
"active": None if status is None else status["active"],
"hall": None if diag is None else diag["hall"],
"max_hall": None if diag is None else diag["max_hall"],
})
return output
def clean_restore_test(s):
before = s.capture_state(2.0)
recovery_ok, recovery = s.ensure_known_absolute_positions(0x0B20)
s.record("MEM-POSITION-RECOVERY", recovery_ok, recovery,
"valid SMV8: no motion; invalid axis: near-end home only")
if recovery_ok:
before = s.capture_state(1.0)
ready = (before["system"] is not None and
before["system"]["position_valid_mask"] == 0x0F and
before["system"]["active_mask"] == 0 and
all(item is not None and not item["active"]
for item in before["motors"]))
s.record("MEM-PRECONDITION", ready, before,
"all axes have known absolute position and are stopped")
if not ready:
return False, before, None
s.countdown(5, "prepare to power-cycle the ECU/seat; do not move the seat")
cycled = s.wait_power_cycle(120.0)
time.sleep(2.0)
after = s.capture_state(3.0)
values_before = comparable_state(before)
values_after = comparable_state(after)
equal = cycled and after["system"] is not None and \
after["system"]["position_valid_mask"] == 0x0F and \
after["system"]["active_mask"] == 0
if equal:
for old, new in zip(values_before, values_after):
if (old["hall"] is None or new["hall"] is None or
abs(old["hall"] - new["hall"]) > 1 or
old["max_hall"] != new["max_hall"] or
old["position"] is None or new["position"] is None or
abs(old["position"] - new["position"]) > 30 or
new["active"]):
equal = False
break
s.record("MEM-CLEAN-RESTORE", equal,
{"power_cycle": cycled, "before": values_before,
"after": values_after, "system_after": after["system"]},
"SMV8 restores Hall/MaxHall/normalized position; no auto motion")
return equal, before, after
def moving_power_loss_test(s):
current = s.capture_state(1.5)
status = current["motors"][0]
if status is None or not status["position_valid"]:
s.record("MEM-MOVING-PRECONDITION", False, status,
"M0 must be calibrated", status="NOT_RUN")
return None
direction = 1 if status["position"] <= 5000 else 2
sequence = 0x0200
payload = command_payload(0x03, 0, direction, sequence, 15000, 600)
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note="M0 15 s at 60 percent for moving power loss")
time.sleep(0.7)
active = s.capture_state(0.6, clear=False)
active_ok = (ack is not None and ack["ack"] == 1 and
active["system"] is not None and
(active["system"]["active_mask"] & 0x01) != 0)
s.record("MEM-MOVING-START", active_ok,
{"ack": ack, "state": active, "direction": direction},
"M0 must be active before removing power")
if not active_ok:
return None
s.log("REMOVE POWER NOW WHILE M0 IS MOVING; restore after >=2 s")
cycled = s.wait_power_cycle(120.0)
time.sleep(2.0)
after = s.capture_state(3.0)
m0 = after["motors"][0]
passed = (cycled and after["system"] is not None and
(after["system"]["position_valid_mask"] & 0x01) == 0 and
after["system"]["active_mask"] == 0 and
m0 is not None and not m0["position_valid"] and
m0["position"] == 0xFFFF and not m0["active"])
s.record("MEM-MOVING-POWER-LOSS", passed,
{"power_cycle": cycled, "after": after},
"newer dirty SMV8 suppresses stale clean M0 position")
return after
def z_main():
global session
session = None
extra = {}
try:
phase = PHASE.strip().upper()
if phase not in ("CLEAN", "MOVING_LOSS", "ALL_DESTRUCTIVE"):
raise RuntimeError("Invalid PHASE=" + PHASE)
session = HilSession("02_power_memory_" + phase.lower())
s = session
s.write_operator_template([
"Selected PHASE=%s" % phase,
"Clean restart caused no seat movement or output pulse: PASS/FAIL ___",
"Moving-power-loss restart caused no automatic movement: PASS/FAIL ___",
"Power-off duration was at least 2 seconds in both steps: YES/NO ___",
"Dual-slot CRC fallback is fixture-only and was not injected: NOT_RUN/record ___",
])
s.stop_all(0)
clean_ok = True
if phase in ("CLEAN", "ALL_DESTRUCTIVE"):
clean_ok, before, clean_after = clean_restore_test(s)
extra["clean_before"] = before
extra["clean_after"] = clean_after
if phase == "MOVING_LOSS" or (phase == "ALL_DESTRUCTIVE" and
clean_ok and not s.stop_requested):
extra["moving_after"] = moving_power_loss_test(s)
if RECOVER_AFTER_MOVING_LOSS and not s.stop_requested:
recovered, details = \
s.ensure_known_absolute_positions(0x0B60)
s.record("MEM-MOVING-LOSS-RECOVERY", recovered, details,
"home invalid axes only; preserve confirmed MaxHall")
extra["moving_loss_recovery"] = details
elif phase == "ALL_DESTRUCTIVE":
s.record("MEM-MOVING-POWER-LOSS", False, "not executed",
"clean restore prerequisite failed", status="NOT_RUN")
s.record("MEM-CRC-FALLBACK", False, "fixture injection required",
"corrupt newest SMV8 slot while preserving older clean slot",
status="NOT_RUN")
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error), "unhandled exception")
finally:
if session is not None:
try:
session.stop_all(0)
session.thermal_off()
except Exception as error:
session.log("FINAL_SAFE_OFF_ERROR " + repr(error))
extra["phase"] = PHASE
extra["recover_after_moving_loss"] = RECOVER_AFTER_MOVING_LOSS
session.finish(extra)
@@ -0,0 +1,224 @@
"""Group 3: protocol protection and one selected physical fault injection.
Set PHASE before running in ZCANPRO:
AUTO, STALL, BUS_OFF, HEATER_ADC, or DRIVER_FAULT.
Only the selected physical phase is driven in one run/log directory.
"""
import importlib
import sys
import time
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
PHASE = "AUTO"
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
def auto_protocol(s):
sequence = 0x0300
# DLC=7 retains the complete header/sequence but omits Byte7.
bad_dlc = command_payload(0x0C, 0, 0, sequence)[:7]
response = s.send_command(bad_dlc, expected_ack=2, expected_reject=1,
note="invalid command DLC=7")
s.record("PROT-BAD-DLC", response is not None and
response["ack"] == 2 and response["reject"] == 1,
response, "reject BAD_DLC; no active motor")
sequence += 1
response = s.send_command(
command_payload(0x0C, 0, 0, sequence, version=2),
expected_ack=2, expected_reject=2, note="bad protocol version")
s.record("PROT-BAD-VERSION", response is not None and
response["reject"] == 2, response, "reject BAD_VERSION")
sequence += 1
response = s.send_command(
command_payload(0x7F, 0, 0, sequence),
expected_ack=2, expected_reject=3, note="undefined opcode")
s.record("PROT-BAD-OPCODE", response is not None and
response["reject"] == 3, response, "reject BAD_OPCODE")
sequence += 1
response = s.send_command(
command_payload(0x03, 0, 0, sequence, 1000, 600),
expected_ack=2, expected_reject=5, note="timed move direction=0")
s.record("PROT-BAD-DIRECTION", response is not None and
response["reject"] == 5, response, "reject BAD_DIRECTION")
sequence += 1
response = s.send_command(
command_payload(0x03, 0, 1, sequence, 99, 600),
expected_ack=2, expected_reject=6, note="duration below 100 ms")
s.record("PROT-PARAM-RANGE", response is not None and
response["reject"] == 6, response, "reject PARAM_RANGE")
sequence += 1
query = command_payload(0x0C, 0, 0, sequence)
first = s.send_command(query, expected_ack=1, expected_reject=0,
note="new query sequence")
duplicate = s.send_command(query, expected_ack=3, expected_reject=0,
note="exact duplicate")
old = s.send_command(command_payload(0x0C, 0, 0, sequence - 1),
expected_ack=2, expected_reject=8,
note="old sequence")
s.record("PROT-SEQUENCE", first is not None and duplicate is not None and
old is not None and first["ack"] == 1 and
duplicate["ack"] == 3 and old["reject"] == 8,
{"first": first, "duplicate": duplicate, "old": old},
"new/duplicate/old sequence handling")
def stall_phase(s):
state = s.capture_state(2.0)
m1 = state["motors"][1]
if m1 is None or not m1["position_valid"]:
s.record("FLT-STALL", False, m1, "calibrate M1 first",
status="NOT_RUN")
return
sequence = 0x0310
midpoint = s.send_command(command_payload(0x01, 1, 0, sequence,
5000, 15000),
expected_ack=1, expected_reject=0,
note="place M1 at midpoint")
terminal = s.wait_motor_terminal(1, sequence, 20.0)
if midpoint is None or terminal is None or terminal["result"] != 2:
s.record("FLT-STALL", False,
{"ack": midpoint, "terminal": terminal},
"failed to place M1 at midpoint")
return
s.countdown(5, "prepare to safely restrain M1 mid-travel after it starts")
sequence += 1
s.reset_current_peaks(1)
started = s.send_command(command_payload(0x03, 1, 1, sequence,
12000, 600),
expected_ack=1, expected_reject=0,
note="M1 stall injection window")
s.log("STALL_ACTION_NOW: restrain M1; release immediately after output stops")
terminal = s.wait_motor_terminal(1, sequence, 15.0)
state = s.capture_state(1.5)
diag = state["diagnostics"][1]
current_peak = s.current_peaks.get(1)
passed = (started is not None and terminal is not None and
terminal["result"] == 5 and diag is not None and
(diag["fault_flags"] & 0x01) != 0 and
current_peak is not None and
current_peak.get("high_current_seen", False) and
current_peak.get("hall_activity_low_seen", False) and
not terminal["active"])
s.record("FLT-STALL", passed,
{"ack": started, "terminal": terminal, "diag": diag,
"current_peak": current_peak},
"M1: low Hall and high current overlap; STALL stops output")
def bus_off_phase(s):
s.thermal_off()
s.transmit(0x29E, [0x25, 0, 0x02, 0, 0, 0, 0, 0], "fan gear2")
s.countdown(5, "force ECU CAN controller BUS-OFF with the fault-injection tool")
s.log("BUS_OFF_WINDOW 15s: observe motor/fan/heater outputs become low")
start = time.monotonic()
received = 0
while time.monotonic() - start < 15.0 and not s.stop_requested:
received += len(s.receive())
time.sleep(0.01)
s.record("FLT-BUS-OFF", False, {"rx_frames_during_window": received},
"judge safe-off timing from scope/operator observation",
status="NOT_RUN")
def heater_adc_phase(s):
s.transmit(0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0], "max level3")
s.transmit(0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0], "heater mode valid")
s.transmit(0x29E, [0x12, 0, 0x30, 0, 0, 0, 0, 0], "heater gear3")
active = s.wait_frame(0x4FA,
lambda value: len(value) >= 1 and
value[0] == 0x21,
3.0)
if active is None or active[0] != 0x21:
s.record("FLT-HEATER-ADC", False, active,
"heater did not enter gear3 before injection")
return
s.countdown(5, "inject three consecutive heater ADC read failures")
data = s.wait_frame(0x4FA,
lambda value: len(value) >= 1 and
((value[0] >> 3) & 0x07) == 0,
10.0)
passed = data is not None and ((data[0] >> 3) & 0x07) == 0
s.record("FLT-HEATER-ADC", passed, data,
"heater feedback off; confirm P16.7 low within 50 ms")
def driver_fault_phase(s):
sequence = 0x0320
s.countdown(5, "prepare DRV8718 nFAULT/SPI fault injection on M1")
started = s.send_command(command_payload(0x03, 1, 1, sequence,
10000, 600),
expected_ack=1, expected_reject=0,
note="M1 driver fault window")
s.log("DRIVER_FAULT_ACTION_NOW")
terminal = s.wait_motor_terminal(1, sequence, 12.0)
state = s.capture_state(1.5)
diag = state["diagnostics"][1]
passed = (started is not None and terminal is not None and
terminal["result"] == 7 and diag is not None and
(diag["fault_flags"] & 0x02) != 0 and
state["system"] is not None and
(state["system"]["faulted_mask"] & 0x02) != 0)
s.record("FLT-DRIVER", passed,
{"terminal": terminal, "diag": diag,
"system": state["system"]},
"driver fault latches and all motor outputs stop")
def z_main():
global session
session = None
try:
phase = PHASE.strip().upper()
if phase not in ("AUTO", "STALL", "BUS_OFF", "HEATER_ADC",
"DRIVER_FAULT"):
raise RuntimeError("Invalid PHASE=" + PHASE)
session = HilSession("03_protection_" + phase.lower())
s = session
s.write_operator_template([
"Selected PHASE=%s" % phase,
"All non-target motor outputs remained off/isolated: PASS/FAIL ___",
"Physical safe-off observation: ___",
"Oscilloscope timing is BLOCKED when no scope is available: ___",
])
s.stop_all(0)
if phase == "AUTO":
auto_protocol(s)
elif phase == "STALL":
stall_phase(s)
elif phase == "BUS_OFF":
bus_off_phase(s)
elif phase == "HEATER_ADC":
heater_adc_phase(s)
elif phase == "DRIVER_FAULT":
driver_fault_phase(s)
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error), "unhandled exception")
finally:
if session is not None:
try:
session.stop_all(0)
session.thermal_off()
except Exception as error:
session.log("FINAL_SAFE_OFF_ERROR " + repr(error))
session.finish({"phase": PHASE})
@@ -0,0 +1,214 @@
"""No-oscilloscope full-function CAN and physical smoke test.
PHASE may be ``ALL`` (default), ``THERMAL`` or ``MOTION``. The movement
phase makes a visible 12% excursion on each calibrated axis and returns it to
its starting normalized position; it does not recalibrate or visit the
25/50/75% matrix.
"""
import importlib
import math
import sys
import time
# ZCANPRO keeps one Python interpreter alive across extension-script runs.
# Always reload the shared helper so rerunning this file cannot retain an
# older HilSession class from sys.modules.
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
hex_data = _hil_common.hex_data
PHASE = "MOTION"
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
def wait_4fa(s, expected_byte0, timeout_s=3.0):
return s.wait_frame(
0x4FA,
lambda data: len(data) >= 1 and data[0] == expected_byte0,
timeout_s)
def thermal_case(s, test_id, can_id, payload, expected_byte0,
expected_action, hold_s):
s.drain()
s.transmit(can_id, payload, test_id)
feedback = wait_4fa(s, expected_byte0)
deadline = time.monotonic() + hold_s
while time.monotonic() < deadline and not s.stop_requested:
s.receive()
time.sleep(0.005)
current = s.latest.get(0x305)
passed = (feedback is not None and feedback[0] == expected_byte0 and
current is not None and current["fan_sample_valid"] and
current["heater_sample_valid"])
s.record(test_id, passed,
{"feedback": "timeout" if feedback is None else
hex_data(feedback),
"thermal_current": current},
"expected 0x4FA B0=%02X and valid 0x305 currents; physical=%s" %
(expected_byte0, expected_action))
return passed
def run_thermal(s):
s.thermal_off()
off = wait_4fa(s, 0x09)
s.record("FUNC-ALL-OFF-BASE", off is not None,
"timeout" if off is None else hex_data(off),
"fan and heater off before test")
s.transmit(0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0],
"max fan/heater level3")
s.transmit(0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0],
"valid heater mode")
time.sleep(0.2)
for gear, feedback in ((1, 0x0A), (2, 0x0B), (3, 0x0C)):
thermal_case(
s, "FUNC-FAN-G%d" % gear, 0x29E,
[0x25, 0, gear, 0, 0, 0, 0, 0], feedback,
"通风%d档可听/可感,且加热关闭" % gear, 2.5)
thermal_case(s, "FUNC-FAN-OFF", 0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0], 0x09,
"通风和加热均关闭", 0.5)
for gear, feedback in ((1, 0x11), (2, 0x19), (3, 0x21)):
thermal_case(
s, "FUNC-HEAT-G%d" % gear, 0x29E,
[0x12, 0, gear << 4, 0, 0, 0, 0, 0], feedback,
"靠背加热%d档有递增温感,且通风关闭" % gear, 4.0)
thermal_case(s, "FUNC-COMBINED-G2", 0x29E,
[0x15, 0, 0x22, 0, 0, 0, 0, 0], 0x1B,
"通风2档和加热2档同时工作", 4.0)
thermal_case(s, "FUNC-ALL-OFF-END", 0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0], 0x09,
"所有热管理输出关闭", 0.5)
def absolute_leg(s, motor, target, sequence, label):
payload = command_payload(0x01, motor, 0, sequence, target, 30000)
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note=label)
terminal = None
if ack is not None and ack["ack"] == 1:
terminal = s.wait_motor_terminal(motor, sequence, 35.0)
time.sleep(0.4)
s.capture_state(0.8, clear=False)
diag = s.latest.get((0x303, motor))
current = s.latest.get((0x304, motor))
max_hall = 0 if diag is None else diag["max_hall"]
normalized_tolerance = 100 if max_hall <= 0 else max(
100, int(math.ceil(20000.0 / max_hall)))
passed = (ack is not None and ack["ack"] == 1 and
terminal is not None and terminal["task"] == 6 and
terminal["result"] in (2, 4) and
terminal["position_valid"] and
current is not None and current["sample_valid"] and
current["baseline_valid"] and
abs(terminal["position"] - target) <= normalized_tolerance)
s.record(label, passed,
{"ack": ack, "terminal": terminal, "diagnostic": diag,
"current": current,
"current_peak": s.current_peaks.get(motor)},
"target=%u tolerance=%u" % (target, normalized_tolerance))
return passed
def excursion_target(current):
if current <= 5000:
return min(9500, current + 1200)
return max(500, current - 1200)
def run_motion(s):
initial = s.capture_state(2.0)
recovery_ok, recovery = s.ensure_known_absolute_positions(0x0B40)
s.record("FUNC-MOTION-POSITION-RECOVERY", recovery_ok, recovery,
"valid SMV8: no motion; invalid axis: near-end home only")
if recovery_ok:
initial = s.capture_state(1.0)
ready = (initial["system"] is not None and
initial["system"]["position_valid_mask"] == 0x0F and
initial["system"]["active_mask"] == 0 and
all(item is not None and item["position_valid"]
for item in initial["motors"]))
s.record("FUNC-MOTION-PRECONDITION", ready, initial,
"stored position or near-end recovery must yield mask 0x0F")
if not ready:
s.record("FUNC-MOTION-ROUNDTRIP", False, "not executed",
"absolute position unavailable", status="NOT_RUN")
return
# Recovery may have accepted 0x0B40..0x0B43. Keep every following motion
# command newer in the firmware's signed 16-bit sequence ordering.
sequence = 0x0C00
starting_positions = [item["position"] for item in initial["motors"]]
for motor, start in enumerate(starting_positions):
if s.stop_requested:
break
target = excursion_target(start)
s.reset_current_peaks(motor)
first_direction = "FAR" if target > start else "NEAR"
returned_direction = "NEAR" if target > start else "FAR"
first_ok = absolute_leg(
s, motor, target, sequence,
"FUNC-M%d-%s-EXCURSION" % (motor, first_direction))
sequence += 1
if first_ok and not s.stop_requested:
absolute_leg(
s, motor, start, sequence,
"FUNC-M%d-%s-RETURN" % (motor, returned_direction))
else:
s.record("FUNC-M%d-RETURN" % motor, False, "not executed",
"excursion failed or STOP requested", status="NOT_RUN")
sequence += 1
time.sleep(0.5)
def z_main():
global session
session = None
try:
phase = PHASE.strip().upper()
if phase not in ("ALL", "THERMAL", "MOTION"):
raise RuntimeError("Invalid PHASE=" + PHASE)
session = HilSession("04_can_full_function_" + phase.lower())
s = session
s.write_operator_template([
"Selected PHASE=%s" % phase,
"通风1/2/3档风量逐级变化,关闭后无风:PASS/FAIL ___",
"加热1/2/3档温感逐级变化,关闭后停止加热:PASS/FAIL ___",
"通风2档+加热2档能够同时工作:PASS/FAIL ___",
"M0水平、M1坐盆、M2靠背、M3腿托均先偏移约12%再返回:PASS/FAIL ___",
"任一单轴动作没有误驱动其它执行器:PASS/FAIL ___",
"无示波器:PWM频率/占空比/死区/硬实时关断延迟均记为BLOCKED",
])
s.stop_all(0)
if phase in ("ALL", "THERMAL"):
run_thermal(s)
if phase in ("ALL", "MOTION") and not s.stop_requested:
run_motion(s)
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error),
"unhandled exception")
finally:
if session is not None:
try:
session.stop_all(0)
session.thermal_off()
except Exception as error:
session.log("FINAL_SAFE_OFF_ERROR " + repr(error))
session.finish({
"phase": PHASE,
"scope_tests": "BLOCKED: oscilloscope unavailable",
"movement_excursion_normalized": 1200,
})
@@ -0,0 +1,167 @@
"""Group 4: ventilation, heating, and four-axis waveform stimulus windows."""
import importlib
import sys
import time
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
hex_data = _hil_common.hex_data
THERMAL_WINDOW_S = 4.0
MOTOR_WINDOW_MS = 1800
ARCHIVED_DO_NOT_RUN = True
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
def wait_4fa(s, expected_byte0, timeout_s=2.0):
return s.wait_frame(0x4FA,
lambda data: len(data) >= 1 and
data[0] == expected_byte0,
timeout_s)
def thermal_window(s, test_id, tx_id, payload, expected_byte0,
pin_note, duration_s=THERMAL_WINDOW_S):
start = time.monotonic() - s.started_monotonic
s.log("WAVE_WINDOW_START %s elapsed=%.6f pin=%s" %
(test_id, start, pin_note))
s.transmit(tx_id, payload, test_id)
feedback = wait_4fa(s, expected_byte0, 2.0)
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline and not s.stop_requested:
s.receive()
time.sleep(0.005)
end = time.monotonic() - s.started_monotonic
passed = feedback is not None and feedback[0] == expected_byte0
s.record(test_id, passed,
{"feedback": None if feedback is None else hex_data(feedback),
"window_start_s": start, "window_end_s": end,
"pin": pin_note},
"align oscilloscope capture to raw_can.csv/local window markers")
s.log("WAVE_WINDOW_END %s elapsed=%.6f" % (test_id, end))
def run_thermal(s):
s.thermal_off()
time.sleep(0.3)
s.transmit(0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0],
"configure max fan/heater level3")
s.transmit(0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0],
"valid heater mode")
time.sleep(0.2)
thermal_window(s, "WAVE-FAN-G1", 0x29E,
[0x25, 0, 0x01, 0, 0, 0, 0, 0], 0x0A,
"P11.1 100Hz: startup 100% 2s then 50%", 3.0)
thermal_window(s, "WAVE-FAN-G2", 0x29E,
[0x25, 0, 0x02, 0, 0, 0, 0, 0], 0x0B,
"P11.1 100Hz 70%")
thermal_window(s, "WAVE-FAN-G3", 0x29E,
[0x25, 0, 0x03, 0, 0, 0, 0, 0], 0x0C,
"P11.1 100Hz 100%")
s.transmit(0x42A, [0x02, 0, 0, 0, 0, 0, 0, 0], "fan off")
time.sleep(0.3)
thermal_window(s, "WAVE-HEAT-G1", 0x29E,
[0x12, 0, 0x10, 0, 0, 0, 0, 0], 0x11,
"P16.7 100Hz 30%")
thermal_window(s, "WAVE-HEAT-G2", 0x29E,
[0x12, 0, 0x20, 0, 0, 0, 0, 0], 0x19,
"P16.7 100Hz 60%")
thermal_window(s, "WAVE-HEAT-G3", 0x29E,
[0x12, 0, 0x30, 0, 0, 0, 0, 0], 0x21,
"P16.7 100Hz 90%")
s.thermal_off()
time.sleep(0.5)
def motor_window(s, motor, direction, sequence, label):
payload = command_payload(0x03, motor, direction, sequence,
MOTOR_WINDOW_MS, 600)
start = time.monotonic() - s.started_monotonic
s.log("WAVE_WINDOW_START %s elapsed=%.6f" % (label, start))
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note=label)
status = None
if ack is not None and ack["ack"] == 1:
status = s.wait_motor_terminal(motor, sequence,
MOTOR_WINDOW_MS / 1000.0 + 4.0)
end = time.monotonic() - s.started_monotonic
passed = (ack is not None and ack["ack"] == 1 and
status is not None and not status["active"] and
status["result"] in (2, 4))
s.record(label, passed,
{"ack": ack, "terminal": status,
"window_start_s": start, "window_end_s": end},
"timed drive 60%; result OK or endpoint accepted")
s.log("WAVE_WINDOW_END %s elapsed=%.6f" % (label, end))
time.sleep(0.5)
def run_motors(s):
state = s.capture_state(2.0)
sequence = 0x0400
for motor in range(4):
status = state["motors"][motor]
first_direction = 1
if status is not None and status["position_valid"] and \
status["position"] > 5000:
first_direction = 2
second_direction = 2 if first_direction == 1 else 1
motor_window(s, motor, first_direction, sequence,
"WAVE-M%u-%s" %
(motor, "FAR" if first_direction == 1 else "NEAR"))
sequence += 1
motor_window(s, motor, second_direction, sequence,
"WAVE-M%u-%s" %
(motor, "FAR" if second_direction == 1 else "NEAR"))
sequence += 1
def z_main():
global session
session = None
try:
session = HilSession("04_waveform_full_function")
s = session
if ARCHIVED_DO_NOT_RUN:
s.record("ARCHIVED", False, "not executed",
"oscilloscope is unavailable; use 04_can_full_function.py",
status="SKIP")
return
s.write_operator_template([
"Fan P11.1: G1 boost/50%, G2 70%, G3 100%, off: PASS/FAIL ___",
"Unused fan output P11.2 remained low: PASS/FAIL ___",
"Heater P16.7: G1 30%, G2 60%, G3 90%, off: PASS/FAIL ___",
"M0 moved horizontal in both directions: PASS/FAIL ___",
"M1 moved seat pan in both directions: PASS/FAIL ___",
"M2 moved backrest in both directions: PASS/FAIL ___",
"M3 moved legrest in both directions: PASS/FAIL ___",
"Motor reversal dead-time >=100 ms and no shoot-through: PASS/FAIL ___",
"Attach scope screenshot filenames/measurements: ___",
])
s.stop_all(0)
run_thermal(s)
run_motors(s)
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error), "unhandled exception")
finally:
if session is not None:
try:
session.stop_all(0)
session.thermal_off()
except Exception as error:
session.log("FINAL_SAFE_OFF_ERROR " + repr(error))
session.finish({"thermal_window_s": THERMAL_WINDOW_S,
"motor_window_ms": MOTOR_WINDOW_MS})
@@ -0,0 +1,920 @@
"""ZCANPRO bridge for the standalone selectable seat-function GUI.
Run this file in ZCANPRO's extension-script window and leave it running. The
normal Windows GUI writes armed JSON requests into ``seat_function_runtime``;
this bridge is the only process that imports and calls ``zcanpro``.
"""
from __future__ import unicode_literals
import csv
import datetime
import importlib
import json
import math
import os
import sys
import time
import zcanpro
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
command_payload = _hil_common.command_payload
frame_id = _hil_common.frame_id
hex_data = _hil_common.hex_data
parse_301 = _hil_common.parse_301
parse_302 = _hil_common.parse_302
parse_303 = _hil_common.parse_303
parse_304 = _hil_common.parse_304
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RUNTIME_DIR = os.path.join(BASE_DIR, "seat_function_runtime")
PENDING_DIR = os.path.join(RUNTIME_DIR, "requests", "pending")
PROCESSING_DIR = os.path.join(RUNTIME_DIR, "requests", "processing")
COMPLETED_DIR = os.path.join(RUNTIME_DIR, "requests", "completed")
STATUS_PATH = os.path.join(RUNTIME_DIR, "bridge_status.json")
EMERGENCY_PATH = os.path.join(RUNTIME_DIR, "emergency_stop.json")
LOG_ROOT = os.path.join(BASE_DIR, "logs", "05_seat_function_gui")
CONFIRMED_MAX_HALL = (480, 374, 917, 647)
POSITION_TIMEOUT_MS = 30000
POSITION_HALL_TOLERANCE = 2
HOME_KNOWN_RANGE_KEY = 0x4B52
HOME_LEG_TIMEOUT_S = 120
stop_task = False
def z_notify(event_type, obj):
del obj
global stop_task
if event_type == "stop":
stop_task = True
def _atomic_json(path, value):
temporary = path + ".bridge.tmp"
with open(temporary, "w", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2,
sort_keys=True, default=str)
stream.flush()
os.fsync(stream.fileno())
os.replace(temporary, path)
def _read_json(path):
try:
with open(path, "r", encoding="utf-8") as stream:
return json.load(stream)
except (OSError, ValueError):
return None
class RunLogger(object):
def __init__(self, request):
timestamp = time.strftime("%Y%m%d_%H%M%S")
suffix = request["request_id"].split("_")[-1]
self.run_dir = os.path.join(LOG_ROOT, "%s_%s" % (timestamp, suffix))
os.makedirs(self.run_dir, exist_ok=True)
self.started = time.monotonic()
self.raw_stream = open(os.path.join(self.run_dir, "raw_can.csv"),
"w", newline="", encoding="utf-8-sig")
self.raw_writer = csv.DictWriter(
self.raw_stream,
fieldnames=["local_time", "elapsed_s", "direction", "can_id",
"dlc", "data", "adapter_timestamp_us", "note"])
self.raw_writer.writeheader()
self.events = []
self.request = request
def frame(self, direction, can_id, data, timestamp_us=0, note=""):
self.raw_writer.writerow({
"local_time": datetime.datetime.now().isoformat(
timespec="milliseconds"),
"elapsed_s": "%.6f" % (time.monotonic() - self.started),
"direction": direction,
"can_id": "0x%03X" % int(can_id),
"dlc": len(data),
"data": hex_data(data),
"adapter_timestamp_us": int(timestamp_us or 0),
"note": note,
})
self.raw_stream.flush()
def event(self, event_type, value):
self.events.append({
"elapsed_s": round(time.monotonic() - self.started, 6),
"type": event_type,
"value": value,
})
def finish(self, result, details):
summary_row = {
"TestID": self.request["request_id"],
"Result": result,
"Actual": details.get("summary", ""),
"Notes": "See report.json and operator_observation.json",
}
with open(os.path.join(self.run_dir, "summary.csv"), "w",
newline="", encoding="utf-8-sig") as stream:
writer = csv.DictWriter(
stream, fieldnames=["TestID", "Result", "Actual", "Notes"])
writer.writeheader()
writer.writerow(summary_row)
report = {
"schema": "seat-function-report-v1",
"generated_local": datetime.datetime.now().isoformat(
timespec="seconds"),
"request": self.request,
"result": result,
"details": details,
"events": self.events,
"operator_observation": "See operator_observation.json after GUI review",
}
_atomic_json(os.path.join(self.run_dir, "report.json"), report)
with open(os.path.join(self.run_dir,
"operator_observation_template.txt"), "w",
encoding="utf-8-sig") as stream:
stream.write("实际动作 PASS/FAIL/BLOCKED:\n")
stream.write("观察说明:\n")
self.raw_stream.flush()
self.raw_stream.close()
class SeatFunctionBridge(object):
def __init__(self):
for path in (RUNTIME_DIR, PENDING_DIR, PROCESSING_DIR, COMPLETED_DIR,
LOG_ROOT):
os.makedirs(path, exist_ok=True)
buses = zcanpro.get_buses()
if not buses:
raise RuntimeError("ZCANPRO中没有已启动的CAN通道")
self.bus = buses[0]
self.bus_id = self.bus["busID"]
self.sequence = int(time.time() * 1000.0) & 0xFFFF
self.latest_system = None
self.latest_motors = [None, None, None, None]
self.latest_diagnostics = [None, None, None, None]
self.latest_currents = [None, None, None, None]
self.latest_thermal = None
self.bridge_state = "IDLE"
self.current_request_id = None
self.last_result = None
self.messages = []
self.logger = None
self.last_emergency_id = None
self.last_status_write = 0.0
self.message("桥接已启动:%s" % str(self.bus))
def message(self, value):
text = str(value)
zcanpro.write_log(text)
stamp = datetime.datetime.now().strftime("%H:%M:%S")
self.messages.append("[%s] %s" % (stamp, text))
self.messages = self.messages[-40:]
if self.logger is not None:
self.logger.event("message", text)
def next_sequence(self):
value = self.sequence
self.sequence = (self.sequence + 1) & 0xFFFF
return value
def publish(self, force=False):
now = time.monotonic()
if not force and (now - self.last_status_write) < 0.2:
return
value = {
"schema": "seat-function-bridge-status-v1",
"heartbeat_epoch": time.time(),
"bridge_state": self.bridge_state,
"current_request_id": self.current_request_id,
"bus": self.bus,
"system": self.latest_system,
"motors": self.latest_motors,
"diagnostics": self.latest_diagnostics,
"currents": self.latest_currents,
"thermal": self.latest_thermal,
"last_result": self.last_result,
"messages": self.messages[-12:],
}
_atomic_json(STATUS_PATH, value)
self.last_status_write = now
def transmit(self, can_id, data, note=""):
values = [int(item) & 0xFF for item in data]
if self.logger is not None:
self.logger.frame("TX", can_id, values, note=note)
frame = {"can_id": int(can_id), "is_canfd": 0, "canfd_brs": 0,
"data": values}
if not zcanpro.transmit(self.bus_id, [frame]):
raise RuntimeError("发送失败 0x%03X %s" %
(can_id, hex_data(values)))
def receive(self):
ok, frames = zcanpro.receive(self.bus_id)
if not ok or not frames:
return []
output = []
for frame in frames:
can_id = frame_id(frame)
data = list(frame.get("data", []))
if self.logger is not None:
self.logger.frame("RX", can_id, data,
frame.get("timestamp_us", 0))
if can_id == 0x301:
self.latest_system = parse_301(data)
elif can_id == 0x302:
value = parse_302(data)
if value is not None and 0 <= value["motor"] < 4:
self.latest_motors[value["motor"]] = value
elif can_id == 0x303:
value = parse_303(data)
if value is not None and 0 <= value["motor"] < 4:
self.latest_diagnostics[value["motor"]] = value
elif can_id == 0x304:
value = parse_304(data)
if value is not None and 0 <= value["motor"] < 4:
self.latest_currents[value["motor"]] = value
elif can_id == 0x4FA and len(data) >= 1:
self.latest_thermal = data
output.append((can_id, data))
return output
def drain(self):
for _ in range(12):
if not self.receive():
break
def check_emergency(self):
request = _read_json(EMERGENCY_PATH)
if request is None:
return False
request_id = request.get("request_id")
if request_id == self.last_emergency_id:
return False
self.last_emergency_id = request_id
valid = (request.get("schema") == "seat-function-emergency-v1" and
float(request.get("armed_until_epoch", 0)) >= time.time())
if not valid:
self.message("忽略过期或无效的STOP请求 %s" % request_id)
return False
self.message("执行GUI紧急STOP_ALL %s" % request_id)
sequence = self.next_sequence()
self.transmit(0x300, command_payload(0x09, 0, 0, sequence),
"GUI EMERGENCY STOP_ALL")
if request.get("thermal_off", True):
self.transmit(0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0],
"GUI emergency thermal off")
return True
def wait(self, predicate, timeout_s, allow_emergency=True):
deadline = time.monotonic() + timeout_s
while time.monotonic() < deadline and not stop_task:
if allow_emergency and self.check_emergency():
return "EMERGENCY", None
for can_id, data in self.receive():
value = predicate(can_id, data)
if value is not None:
return "OK", value
self.publish()
time.sleep(0.005)
return "TIMEOUT", None
def send_command(self, payload, expected_ack, timeout_s=2.0, note=""):
sequence = payload[2] | (payload[3] << 8)
self.drain()
self.transmit(0x300, payload, note)
def predicate(can_id, data):
if can_id != 0x301:
return None
value = parse_301(data)
if value is not None and value["sequence"] == sequence and \
value["ack"] == expected_ack:
return value
return None
state, value = self.wait(predicate, timeout_s)
if self.logger is not None:
self.logger.event("command", {
"payload": hex_data(payload), "wait_state": state,
"response": value, "note": note,
})
return state, value
def stop_all_sync(self):
sequence = self.next_sequence()
state, response = self.send_command(
command_payload(0x09, 0, 0, sequence), 4, 2.0,
"request baseline STOP_ALL")
return state == "OK" and response is not None
def capture(self, duration_s=1.2):
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline and not stop_task:
if self.check_emergency():
return False
self.receive()
self.publish()
time.sleep(0.005)
return True
def position_consistency_issues(self):
"""Check only ECU-internal Hall/normalized consistency.
This cannot detect a mechanism that was moved while the ECU was
unpowered. Such an externally unknown position must be resolved by a
real known-range home, regardless of PositionValidMask.
"""
issues = []
system_mask = 0 if self.latest_system is None else \
self.latest_system["position_valid_mask"]
for motor, expected_max in enumerate(CONFIRMED_MAX_HALL):
status = self.latest_motors[motor]
diag = self.latest_diagnostics[motor]
if status is None or diag is None:
issues.append("M%d缺少0x302/0x303" % motor)
continue
mask_valid = (system_mask & (1 << motor)) != 0
if bool(status["position_valid"]) != mask_valid:
issues.append("M%d PositionValid与0x301不一致" % motor)
continue
if not mask_valid:
continue
max_hall = int(diag["max_hall"])
hall = int(diag["hall"])
if abs(max_hall - expected_max) > max(4, int(expected_max * 0.05)):
issues.append("M%d MaxHall=%d(预期约%d)" %
(motor, max_hall, expected_max))
continue
if max_hall <= 0 or hall < 0 or hall > max_hall:
issues.append("M%d Hall=%d超出0..%d" %
(motor, hall, max_hall))
continue
expected_position = int(
(hall * 10000.0 / max_hall) + 0.5)
normalized_tolerance = max(
100,
int(math.ceil(10000.0 * POSITION_HALL_TOLERANCE /
max_hall)))
if abs(int(status["position"]) - expected_position) > \
normalized_tolerance:
issues.append(
"M%d Position=%d与Hall=%d/%d(应约%d)不一致" %
(motor, status["position"], hall, max_hall,
expected_position))
return issues
def execute_mode(self, request):
payload = request.get("payload") or {}
axes = payload.get("axes") or []
if len(axes) != 4 or sorted(int(axis.get("motor", -1))
for axis in axes) != [0, 1, 2, 3]:
return "BLOCKED", {"summary": "请求没有完整且唯一的M0..M3目标"}
if not self.stop_all_sync():
return "FAIL", {"summary": "基线STOP_ALL无有效ACK"}
if not self.capture(1.2):
return "STOPPED", {"summary": "执行前收到紧急停止"}
system = self.latest_system
if system is None:
return "BLOCKED", {"summary": "未收到0x301系统反馈"}
if system["position_valid_mask"] != 0x0F:
if not payload.get("allow_auto_home", False):
return "BLOCKED", {
"summary": "PositionValidMask=0x%X,且请求未授权自动回最近端" %
system["position_valid_mask"],
"system": system,
}
self.message(
"绝对位置未知:先将失效轴回最近端,再执行模式目标")
home_result, home_details = self.execute_initialize_known_ranges({
"payload": {"force_all_axes": False},
})
if home_result != "PASS":
return home_result, {
"summary": "模式执行前自动回最近端失败:%s" %
home_details.get("summary", ""),
"auto_home": home_details,
}
self.capture(0.5)
system = self.latest_system
if system is None or system["position_valid_mask"] != 0x0F:
return "FAIL", {
"summary": "自动回最近端完成后PositionValidMask仍不是0xF",
"auto_home": home_details,
"system": system,
}
else:
home_details = None
if system["faulted_mask"] != 0:
return "BLOCKED", {
"summary": "FaultedMask=0x%X,先排除故障" %
system["faulted_mask"],
"system": system,
}
range_issues = []
for motor, expected in enumerate(CONFIRMED_MAX_HALL):
diag = self.latest_diagnostics[motor]
if diag is None:
range_issues.append("M%d无0x303" % motor)
elif abs(diag["max_hall"] - expected) > max(4, int(expected * 0.05)):
range_issues.append("M%d MaxHall=%d(预期约%d)" %
(motor, diag["max_hall"], expected))
if range_issues:
return "BLOCKED", {
"summary": "量程反馈不符:" + ";".join(range_issues),
"diagnostics": self.latest_diagnostics,
}
consistency_issues = self.position_consistency_issues()
if consistency_issues:
return "BLOCKED", {
"summary": "位置记录内部不一致,请强制四轴归零:" +
";".join(consistency_issues),
"system": self.latest_system,
"motors": self.latest_motors,
"diagnostics": self.latest_diagnostics,
}
sequences = {}
acknowledgements = {}
for axis in sorted(axes, key=lambda value: int(value["motor"])):
if self.check_emergency():
return "STOPPED", {"summary": "下发过程中收到紧急停止"}
motor = int(axis["motor"])
target = int(axis["normalized"])
if target < 0 or target > 10000:
return "BLOCKED", {"summary": "M%d目标超出0..10000" % motor}
sequence = self.next_sequence()
sequences[motor] = sequence
state, response = self.send_command(
command_payload(0x01, motor, 0, sequence, target,
POSITION_TIMEOUT_MS),
1, 2.0, "%s M%d absolute=%d" %
(payload.get("name", "mode"), motor, target))
acknowledgements[motor] = response
if state == "EMERGENCY":
return "STOPPED", {"summary": "下发过程中收到紧急停止"}
if state != "OK" or response is None or response["reject"] != 0:
self.stop_all_sync()
return "FAIL", {
"summary": "M%d绝对位置命令未被接受" % motor,
"acknowledgements": acknowledgements,
}
# Firmware also enforces a 100 ms motor-start stagger. This
# smaller transmit gap keeps command ACKs distinct.
end_gap = time.monotonic() + 0.05
while time.monotonic() < end_gap:
self.receive()
time.sleep(0.005)
deadline = time.monotonic() + POSITION_TIMEOUT_MS / 1000.0 + 5.0
terminal = {}
while time.monotonic() < deadline and not stop_task:
if self.check_emergency():
return "STOPPED", {
"summary": "运动过程中收到紧急停止",
"acknowledgements": acknowledgements,
}
self.receive()
for motor, sequence in sequences.items():
status = self.latest_motors[motor]
if status is not None and status["sequence"] == sequence and \
not status["active"] and status["task"] in (6, 7):
terminal[motor] = status
self.publish()
if len(terminal) == 4:
break
time.sleep(0.005)
self.capture(1.0)
axis_results = []
passed = len(terminal) == 4
for axis in sorted(axes, key=lambda value: int(value["motor"])):
motor = int(axis["motor"])
target = int(axis["normalized"])
status = self.latest_motors[motor]
diag = self.latest_diagnostics[motor]
max_hall = int(axis.get("max_hall", CONFIRMED_MAX_HALL[motor]))
if diag is not None and diag["max_hall"] > 0:
max_hall = diag["max_hall"]
norm_tolerance = max(100, int(math.ceil(20000.0 / max_hall)))
expected_hall = int(target * max_hall / 10000.0 + 0.5)
hall_tolerance = max(2, int(math.ceil(
max_hall * norm_tolerance / 10000.0)))
item_pass = (
motor in terminal and status is not None and
status["task"] == 6 and status["result"] in (2, 4) and
status["position_valid"] and
abs(status["position"] - target) <= norm_tolerance and
diag is not None and
abs(diag["hall"] - expected_hall) <= hall_tolerance)
passed = passed and item_pass
axis_results.append({
"motor": motor,
"passed": item_pass,
"target_normalized": target,
"target_physical": axis.get("physical_value"),
"physical_unit": axis.get("unit"),
"expected_hall_from_reported_range": expected_hall,
"normalized_tolerance": norm_tolerance,
"hall_tolerance": hall_tolerance,
"status": status,
"diagnostic": diag,
})
return ("PASS" if passed else "FAIL"), {
"summary": "%s;四轴CAN位置%s,仍需GUI人工确认实物动作" %
(payload.get("name", "模式"),
"均正确" if passed else "存在不符"),
"acknowledgements": acknowledgements,
"axis_results": axis_results,
"system": self.latest_system,
"warnings": payload.get("warnings", []),
"workbook": payload.get("workbook"),
"workbook_sha256": payload.get("workbook_sha256"),
"auto_home": home_details,
}
def execute_initialize_known_ranges(self, request):
payload = request.get("payload") or request
force_all_axes = bool(payload.get("force_all_axes", False))
if not self.stop_all_sync():
return "FAIL", {"summary": "基线STOP_ALL无有效ACK"}
if not self.capture(1.2):
return "STOPPED", {"summary": "初始化前收到紧急停止"}
initial_mask = 0 if self.latest_system is None else \
self.latest_system["position_valid_mask"]
homed = []
for motor, expected_max in enumerate(CONFIRMED_MAX_HALL):
if not force_all_axes and (initial_mask & (1 << motor)) != 0:
homed.append({
"motor": motor,
"result": "ALREADY_VALID_NOT_HOMED",
"status": self.latest_motors[motor],
"diagnostic": self.latest_diagnostics[motor],
"note": "位置记录有效,未执行机械归零",
})
continue
if self.check_emergency():
return "STOPPED", {
"summary": "回最近端过程中收到紧急停止",
"axes": homed,
}
initial_status = self.latest_motors[motor]
initial_diag = self.latest_diagnostics[motor]
initial_current = self.latest_currents[motor]
initial_status = dict(initial_status) \
if initial_status is not None else None
initial_diag = dict(initial_diag) \
if initial_diag is not None else None
initial_current = dict(initial_current) \
if initial_current is not None else None
observed_halls = []
observed_directions = []
if initial_diag is not None:
observed_halls.append(int(initial_diag["hall"]))
current_peak = initial_current
home_started = time.monotonic()
sequence = self.next_sequence()
state, response = self.send_command(
command_payload(0x0B, motor, 0, sequence,
HOME_KNOWN_RANGE_KEY,
HOME_LEG_TIMEOUT_S),
1, 3.0, "M%d home near with known MaxHall=%d" %
(motor, expected_max))
if state == "EMERGENCY":
return "STOPPED", {
"summary": "回最近端过程中收到紧急停止",
"axes": homed,
}
if state != "OK" or response is None or response["reject"] != 0:
self.stop_all_sync()
return "FAIL", {
"summary": "M%d已知量程回零命令未被固件接受;若Reject=BAD_OPCODE,需烧录新HEX" %
motor,
"motor": motor,
"response": response,
"latest_system": self.latest_system,
"axes": homed,
}
def terminal_predicate(can_id, data):
nonlocal current_peak
if can_id == 0x303:
diagnostic = parse_303(data)
if diagnostic is not None and \
diagnostic["motor"] == motor:
observed_halls.append(int(diagnostic["hall"]))
return None
if can_id == 0x304:
current = parse_304(data)
if current is not None and current["motor"] == motor and \
(current_peak is None or
current["delta"] > current_peak["delta"]):
current_peak = current
return None
if can_id != 0x302:
return None
value = parse_302(data)
if value is not None and value["motor"] == motor and \
value["sequence"] == sequence:
direction = int(value["direction"])
if value["active"] and direction in (1, 2) and \
(not observed_directions or
observed_directions[-1] != direction):
observed_directions.append(direction)
if not value["active"] and value["task"] in (6, 7):
return value
return None
wait_state, terminal = self.wait(
terminal_predicate, HOME_LEG_TIMEOUT_S + 15.0)
if wait_state == "EMERGENCY":
return "STOPPED", {
"summary": "M%d回最近端时收到紧急停止" % motor,
"axes": homed,
}
self.capture(1.0)
status = self.latest_motors[motor]
diag = self.latest_diagnostics[motor]
if diag is not None:
observed_halls.append(int(diag["hall"]))
distinct_halls = sorted(set(observed_halls))
initial_hall = None if initial_diag is None else \
int(initial_diag["hall"])
can_motion_evidence = True
if initial_hall is not None and initial_hall > 2:
can_motion_evidence = any(
0 < value < initial_hall for value in distinct_halls)
passed = (terminal is not None and terminal["task"] == 6 and
terminal["result"] == 2 and
status is not None and status["position_valid"] and
status["near"] and status["position"] <= 100 and
diag is not None and diag["hall"] <= 2 and
can_motion_evidence and
abs(diag["max_hall"] - expected_max) <=
max(4, int(expected_max * 0.05)))
homed.append({
"motor": motor,
"result": "PASS" if passed else "FAIL",
"terminal": terminal,
"status": status,
"diagnostic": diag,
"initial_status": initial_status,
"initial_diagnostic": initial_diag,
"initial_current": initial_current,
"peak_current": current_peak,
"home_elapsed_s": round(time.monotonic() - home_started, 3),
"observed_hall_values": distinct_halls,
"observed_direction_sequence": observed_directions,
"can_motion_evidence": can_motion_evidence,
"firmware_minimum_raw_hall_verification_required": True,
"expected_max_hall": expected_max,
})
if not passed:
self.stop_all_sync()
if not can_motion_evidence:
failure_summary = (
"M%d从Hall=%d直接跳到0,未观察到真实归零运动;"
"拒绝把PositionValid置位结果判为PASS,请确认烧录了"
"带启动验证的新HEX" % (motor, initial_hall))
elif terminal is None:
failure_summary = \
"M%d回最近端命令超时,未收到固件终态" % motor
elif terminal.get("result") == 11 and \
1 in observed_directions and \
(not observed_directions or
observed_directions[-1] != 2):
failure_summary = (
"M%d离开最近端的Hall启动验证失败;"
"固件未进入回程,请检查该方向Hall边沿/机械启动,"
"并确认烧录最新HEX" % motor)
elif terminal.get("result") == 11 and \
observed_directions and \
observed_directions[-1] == 2:
failure_summary = (
"M%d已完成离端动作,但返回最近端的Hall/端点验证失败;"
"请检查回程Hall反馈并确认烧录最新HEX" % motor)
else:
failure_summary = \
"M%d回最近端后的位置/Hall反馈不符合要求" % motor
return "FAIL", {
"summary": failure_summary,
"axes": homed,
}
self.capture(1.5)
final_mask = 0 if self.latest_system is None else \
self.latest_system["position_valid_mask"]
range_issues = []
for motor, expected_max in enumerate(CONFIRMED_MAX_HALL):
diag = self.latest_diagnostics[motor]
if diag is None:
range_issues.append("M%d无0x303" % motor)
elif abs(diag["max_hall"] - expected_max) > \
max(4, int(expected_max * 0.05)):
range_issues.append("M%d MaxHall=%d(预期约%d)" %
(motor, diag["max_hall"], expected_max))
passed = final_mask == 0x0F and not range_issues
actually_homed = [item["motor"] for item in homed
if item["result"] == "PASS"]
if force_all_axes:
passed = passed and actually_homed == [0, 1, 2, 3]
summary_prefix = "四轴已实际回最近端并重建Hall=0"
elif actually_homed:
summary_prefix = "PositionValid失效轴已实际回最近端"
else:
summary_prefix = (
"四轴位置记录已有效,未执行机械归零;"
"PositionValid不等于位于最近端")
return ("PASS" if passed else "FAIL"), {
"summary": "%s;PositionValidMask=0x%X%s" %
(summary_prefix,
final_mask,
";" + ";".join(range_issues)
if range_issues else ""),
"initial_position_valid_mask": initial_mask,
"final_position_valid_mask": final_mask,
"force_all_axes": force_all_axes,
"actually_homed_axes": actually_homed,
"axes": homed,
"range_issues": range_issues,
"note": (
"HOME_KNOWN_RANGE只寻找最近端,不重新测量最远端或"
"25/50/75%位置;完成后的Hall/绝对位置由固件写入SMV8"),
}
def execute_thermal(self, request):
payload = request.get("payload") or {}
frames = payload.get("frames") or []
expected = int(payload.get("expected_byte0", -1))
if not frames or not (0 <= expected <= 255):
return "BLOCKED", {"summary": "热管理请求格式无效"}
if not self.stop_all_sync():
return "FAIL", {"summary": "基线STOP_ALL无有效ACK"}
self.latest_thermal = None
for item in frames:
if self.check_emergency():
return "STOPPED", {"summary": "下发过程中收到紧急停止"}
can_id, data = item
if len(data) != 8:
return "BLOCKED", {"summary": "热管理CAN帧DLC不是8"}
self.transmit(int(can_id), data, payload.get("key", "thermal"))
end_gap = time.monotonic() + 0.1
while time.monotonic() < end_gap:
self.receive()
time.sleep(0.005)
def active_predicate(can_id, data):
if can_id == 0x4FA and len(data) >= 1 and data[0] == expected:
return list(data)
return None
if (self.latest_thermal is not None and
len(self.latest_thermal) >= 1 and
self.latest_thermal[0] == expected):
state, active_feedback = "OK", list(self.latest_thermal)
else:
state, active_feedback = self.wait(active_predicate, 3.0)
active_ok = state == "OK" and active_feedback is not None
hold_s = float(payload.get("hold_s", 8.0))
if payload.get("key") == "全部关闭":
hold_s = 0.0
deadline = time.monotonic() + max(0.0, min(hold_s, 30.0))
while time.monotonic() < deadline and not stop_task:
if self.check_emergency():
return "STOPPED", {
"summary": "保持窗口内收到紧急停止",
"active_feedback": active_feedback,
}
self.receive()
self.publish()
time.sleep(0.01)
self.transmit(0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0],
"automatic thermal safe off")
def off_predicate(can_id, data):
if can_id == 0x4FA and len(data) >= 1 and data[0] == 0x09:
return list(data)
return None
off_state, off_feedback = self.wait(off_predicate, 3.0)
off_ok = off_state == "OK" and off_feedback is not None
passed = active_ok and off_ok
return ("PASS" if passed else "FAIL"), {
"summary": "%s:目标反馈%s,自动安全关闭%s;仍需GUI人工确认风量/温升" %
(payload.get("key", "热管理"),
"正确" if active_ok else "不符",
"正确" if off_ok else "不符"),
"active_feedback": active_feedback,
"expected_byte0": expected,
"hold_s": hold_s,
"off_feedback": off_feedback,
}
def process_request(self, path):
name = os.path.basename(path)
processing = os.path.join(PROCESSING_DIR, name)
try:
os.replace(path, processing)
except OSError:
return
request = _read_json(processing)
if request is None:
self.message("无法读取请求:%s" % name)
os.replace(processing, os.path.join(COMPLETED_DIR, name))
return
request_id = request.get("request_id", name)
self.current_request_id = request_id
self.bridge_state = "RUNNING"
self.logger = RunLogger(request)
self.message("开始请求 %s action=%s" %
(request_id, request.get("action")))
self.publish(force=True)
result = "FAIL"
details = {"summary": "未执行"}
try:
valid = (request.get("schema") == "seat-function-request-v1" and
float(request.get("armed_until_epoch", 0)) >= time.time())
if not valid:
result = "BLOCKED"
details = {"summary": "请求已过期或schema无效,未驱动任何输出"}
elif request.get("action") == "execute_mode":
result, details = self.execute_mode(request)
elif request.get("action") == "execute_thermal":
result, details = self.execute_thermal(request)
elif request.get("action") == "initialize_known_ranges":
result, details = self.execute_initialize_known_ranges(request)
else:
result = "BLOCKED"
details = {"summary": "未知action,未驱动任何输出"}
except Exception as error:
result = "FAIL"
details = {"summary": "桥接异常:%r" % error}
self.message(details["summary"])
try:
sequence = self.next_sequence()
self.transmit(0x300, command_payload(0x09, 0, 0, sequence),
"exception STOP_ALL")
self.transmit(0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0],
"exception thermal off")
except Exception as stop_error:
self.message("异常后的安全关闭失败:%r" % stop_error)
finally:
log_dir = self.logger.run_dir
self.logger.finish(result, details)
self.logger = None
self.last_result = {
"request_id": request_id,
"result": result,
"summary": details.get("summary", ""),
"log_dir": log_dir,
"report_path": os.path.join(log_dir, "report.json"),
}
self.message("完成 %s result=%s logs=%s" %
(request_id, result, log_dir))
self.current_request_id = None
self.bridge_state = "IDLE"
self.publish(force=True)
destination = os.path.join(COMPLETED_DIR, name)
if os.path.exists(destination):
destination += ".%d" % int(time.time())
os.replace(processing, destination)
def run(self):
self.publish(force=True)
while not stop_task:
self.check_emergency()
self.receive()
pending = sorted(
os.path.join(PENDING_DIR, name)
for name in os.listdir(PENDING_DIR)
if name.lower().endswith(".json"))
if pending:
self.process_request(pending[0])
self.publish()
time.sleep(0.01)
def z_main():
global stop_task
stop_task = False
bridge = None
try:
bridge = SeatFunctionBridge()
bridge.run()
except Exception as error:
zcanpro.write_log("SEAT_FUNCTION_BRIDGE_ERROR %r" % error)
finally:
if bridge is not None:
bridge.bridge_state = "STOPPED"
try:
sequence = bridge.next_sequence()
bridge.transmit(0x300,
command_payload(0x09, 0, 0, sequence),
"bridge exit STOP_ALL")
bridge.transmit(0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0],
"bridge exit thermal off")
except Exception as error:
bridge.message("退出安全关闭失败:%r" % error)
bridge.publish(force=True)
@@ -0,0 +1,762 @@
"""Seat-current threshold characterization for ZCANPRO.
Run one PHASE at a time:
NORMAL Safe default. Captures idle current and a 12% round trip on
every position-valid axis. Invalid axes are near-end homed;
no full-range calibration is performed.
IDLE Captures stopped current only; the seat does not move.
THERMAL Captures fan P12.6/ADC1_CH10 and heater P12.2/ADC1_CH6 at
off, levels 1/2/3 and the combined level-2 condition.
ENDPOINT_M0 ... ENDPOINT_M3. Homes exactly one axis to its known near end
and captures endpoint current. Confirm the path is clear.
STALL_M0 ... STALL_M3. Controlled/manual test of one axis. The script
first places the axis at 50%, counts down, then commands a
timed move. Restrain the selected mechanism only after the
script prints STALL_ACTION_NOW and release it when output stops.
Outputs are stored in the usual timestamped log directory:
current_samples.csv every decoded 0x304 sample
thermal_current_samples.csv every decoded 0x305 fan/heater sample
current_calibration.json per-stage statistics and threshold guidance
raw_can.csv unmodified CAN trace
The generated recommendation is advisory. A production threshold requires a
normal-current run and a controlled stall/endpoint run for each physical axis.
"""
import csv
import importlib
import json
import math
import os
import sys
import time
if "hil_common" in sys.modules:
_hil_common = importlib.reload(sys.modules["hil_common"])
else:
import hil_common as _hil_common
HilSession = _hil_common.HilSession
command_payload = _hil_common.command_payload
parse_302 = _hil_common.parse_302
parse_304 = _hil_common.parse_304
parse_305 = _hil_common.parse_305
PHASE = "STALL_M3"
IDLE_CAPTURE_S = 4.0
SETTLE_CAPTURE_S = 1.5
NORMAL_EXCURSION = 1200 # normalized 12.00%
NORMAL_MOVE_TIMEOUT_MS = 30000
ENDPOINT_TIMEOUT_S = 120
# Keep a failed-to-trip controlled stall short. Three seconds still covers
# the 500 ms Hall window, 200 ms current confirmation and several 0x304 frames.
STALL_MOVE_DURATION_MS = 3000
# MOVE_TIMED ParamB uses 0.1% units; 600 requests 60% duty.
STALL_LEVEL_X10 = 600
STALL_WAIT_TIMEOUT_S = 8.0
STALL_FIXTURE_COUNTDOWN_S = 10
THERMAL_OFF_SETTLE_S = 1.5
THERMAL_FAN_STARTUP_CAPTURE_S = 2.2
THERMAL_FAN_STEADY_CAPTURE_S = 3.0
THERMAL_HEATER_CAPTURE_S = 4.0
session = None
def z_notify(event_type, obj):
del obj
if event_type == "stop" and session is not None:
session.request_stop()
class CurrentRecorder(object):
FIELDS = [
"stage", "elapsed_s", "motor", "sample_valid", "baseline_valid",
"high_current", "hall_activity_low", "calibrating", "raw",
"baseline", "delta", "threshold",
]
THERMAL_FIELDS = [
"stage", "elapsed_s", "fan_sample_valid", "heater_sample_valid",
"fan_running", "heater_level", "fan_raw", "heater_raw",
"fan_adc_errors", "heater_adc_errors",
]
def __init__(self, hil_session):
self.session = hil_session
self.started = time.monotonic()
self.rows = []
self.path = os.path.join(hil_session.run_dir, "current_samples.csv")
self.stream = open(self.path, "w", newline="", encoding="utf-8-sig")
self.writer = csv.DictWriter(self.stream, fieldnames=self.FIELDS)
self.writer.writeheader()
self.thermal_rows = []
self.thermal_path = os.path.join(
hil_session.run_dir, "thermal_current_samples.csv")
self.thermal_stream = open(
self.thermal_path, "w", newline="", encoding="utf-8-sig")
self.thermal_writer = csv.DictWriter(
self.thermal_stream, fieldnames=self.THERMAL_FIELDS)
self.thermal_writer.writeheader()
def close(self):
if not self.stream.closed:
self.stream.flush()
self.stream.close()
if not self.thermal_stream.closed:
self.thermal_stream.flush()
self.thermal_stream.close()
def _record_motor(self, stage, value):
row = dict(value)
row["stage"] = stage
row["elapsed_s"] = round(time.monotonic() - self.started, 6)
self.rows.append(row)
self.writer.writerow({key: row.get(key) for key in self.FIELDS})
return row
def _record_thermal(self, stage, value):
row = dict(value)
row["stage"] = stage
row["elapsed_s"] = round(time.monotonic() - self.started, 6)
self.thermal_rows.append(row)
self.thermal_writer.writerow(
{key: row.get(key) for key in self.THERMAL_FIELDS})
return row
def poll(self, stage, target_motor=None):
captured = []
for can_id, data, frame in self.session.receive():
del frame
if can_id != 0x304:
if can_id == 0x305:
value = parse_305(data)
if value is not None:
self._record_thermal(stage, value)
continue
value = parse_304(data)
if value is None or value["motor"] >= 4:
continue
if target_motor is not None and value["motor"] != target_motor:
continue
row = self._record_motor(stage, value)
captured.append(row)
return captured
def capture_for(self, stage, duration_s, target_motor=None):
deadline = time.monotonic() + duration_s
rows = []
while time.monotonic() < deadline and not self.session.stop_requested:
rows.extend(self.poll(stage, target_motor))
time.sleep(0.005)
self.stream.flush()
self.thermal_stream.flush()
return rows
def wait_terminal(self, stage, motor, sequence, timeout_s):
deadline = time.monotonic() + timeout_s
terminal = None
rows = []
while time.monotonic() < deadline and not self.session.stop_requested:
frames = self.session.receive()
for can_id, data, frame in frames:
del frame
if can_id == 0x304:
value = parse_304(data)
if value is not None and value["motor"] == motor:
row = self._record_motor(stage, value)
rows.append(row)
elif can_id == 0x305:
value = parse_305(data)
if value is not None:
self._record_thermal(stage, value)
elif can_id == 0x302:
value = parse_302(data)
if value is not None and value["motor"] == motor and \
value["sequence"] == sequence and \
not value["active"] and value["task"] in (6, 7):
terminal = value
if terminal is not None:
break
time.sleep(0.005)
self.stream.flush()
self.thermal_stream.flush()
return terminal, rows
def percentile(values, percent):
if not values:
return None
ordered = sorted(values)
if len(ordered) == 1:
return ordered[0]
rank = (len(ordered) - 1) * float(percent) / 100.0
low = int(math.floor(rank))
high = int(math.ceil(rank))
if low == high:
return ordered[low]
weight = rank - low
return int(round(ordered[low] * (1.0 - weight) +
ordered[high] * weight))
def summarize_thermal_signal(rows, valid_key, raw_key, baseline_raw=None):
valid = [row for row in rows if row[valid_key]]
values = [row[raw_key] for row in valid]
deltas = ([abs(value - baseline_raw) for value in values]
if baseline_raw is not None else [])
return {
"sample_count": len(rows),
"valid_count": len(valid),
"invalid_count": len(rows) - len(valid),
"raw_min": min(values) if values else None,
"raw_mean": (round(sum(values) / float(len(values)), 2)
if values else None),
"raw_p50": percentile(values, 50),
"raw_p95": percentile(values, 95),
"raw_p99": percentile(values, 99),
"raw_max": max(values) if values else None,
"baseline_raw": baseline_raw,
"delta_p50": percentile(deltas, 50),
"delta_p95": percentile(deltas, 95),
"delta_p99": percentile(deltas, 99),
"delta_max": max(deltas) if deltas else None,
}
def summarize_thermal(rows, baselines=None):
baselines = baselines or {}
return {
"sample_count": len(rows),
"fan_p12_6_adc1_ch10": summarize_thermal_signal(
rows, "fan_sample_valid", "fan_raw", baselines.get("fan")),
"heater_p12_2_adc1_ch6": summarize_thermal_signal(
rows, "heater_sample_valid", "heater_raw",
baselines.get("heater")),
"fan_adc_error_max": max(
[row["fan_adc_errors"] for row in rows] or [0]),
"heater_adc_error_max": max(
[row["heater_adc_errors"] for row in rows] or [0]),
}
def summarize(rows):
valid = [row for row in rows
if row["sample_valid"] and row["baseline_valid"]]
deltas = [row["delta"] for row in valid]
raw_values = [row["raw"] for row in valid]
baselines = [row["baseline"] for row in valid]
return {
"sample_count": len(rows),
"valid_count": len(valid),
"invalid_count": len(rows) - len(valid),
"raw_min": min(raw_values) if raw_values else None,
"raw_mean": (round(sum(raw_values) / float(len(raw_values)), 2)
if raw_values else None),
"raw_max": max(raw_values) if raw_values else None,
"baseline_min": min(baselines) if baselines else None,
"baseline_mean": (round(sum(baselines) / float(len(baselines)), 2)
if baselines else None),
"baseline_max": max(baselines) if baselines else None,
"delta_p50": percentile(deltas, 50),
"delta_p95": percentile(deltas, 95),
"delta_p99": percentile(deltas, 99),
"delta_max": max(deltas) if deltas else None,
"high_current_samples": sum(1 for row in valid
if row["high_current"]),
"low_hall_samples": sum(1 for row in valid
if row["hall_activity_low"]),
"joint_low_hall_high_current_samples": sum(
1 for row in valid if row["hall_activity_low"] and
row["high_current"]),
"configured_thresholds": sorted(set(
row["threshold"] for row in valid)),
}
def next_sequence(state, fallback=0x2000):
system = state.get("system") if state is not None else None
if system is None:
return fallback
value = (int(system["sequence"]) + 1) & 0xFFFF
return 1 if value == 0 else value
def excursion_target(current):
if current <= 5000:
return min(9500, current + NORMAL_EXCURSION)
return max(500, current - NORMAL_EXCURSION)
def command_with_capture(s, recorder, motor, sequence, payload,
stage, timeout_s, expected_results):
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note=stage)
if ack is None or ack["ack"] != 1:
return False, ack, None, []
terminal, rows = recorder.wait_terminal(stage, motor, sequence, timeout_s)
passed = (terminal is not None and
terminal["result"] in expected_results and
not terminal["active"])
return passed, ack, terminal, rows
def idle_phase(s, recorder):
s.stop_all(0)
s.thermal_off()
rows = recorder.capture_for("IDLE", IDLE_CAPTURE_S)
summaries = {}
for motor in range(4):
summaries[str(motor)] = summarize(
[row for row in rows if row["motor"] == motor])
s.record("CUR-IDLE-M%d" % motor,
summaries[str(motor)]["valid_count"] >= 5,
summaries[str(motor)],
"stopped baseline and ADC validity")
return summaries
def wait_4fa(s, expected_byte0, timeout_s=3.0):
return s.wait_frame(
0x4FA,
lambda data: len(data) >= 1 and data[0] == expected_byte0,
timeout_s)
def thermal_current_case(s, recorder, test_id, can_id, payload,
expected_byte0, expected_fan_running,
expected_heater_level, segments, baselines):
s.drain()
s.transmit(can_id, payload, test_id)
feedback = wait_4fa(s, expected_byte0)
segment_stats = {}
all_rows = []
for suffix, duration_s in segments:
stage = test_id if not suffix else test_id + "_" + suffix
start = len(recorder.thermal_rows)
recorder.capture_for(stage, duration_s)
rows = recorder.thermal_rows[start:]
all_rows.extend(rows)
segment_stats[stage] = summarize_thermal(rows, baselines)
valid = [row for row in all_rows
if row["fan_sample_valid"] and row["heater_sample_valid"]]
latest = valid[-1] if valid else None
passed = (feedback is not None and latest is not None and
latest["fan_running"] == expected_fan_running and
latest["heater_level"] == expected_heater_level and
len(valid) >= 5)
details = {
"feedback": feedback,
"expected_4fa_byte0": expected_byte0,
"expected_fan_running": expected_fan_running,
"expected_heater_level": expected_heater_level,
"latest_thermal_current": latest,
"segments": segment_stats,
}
s.record(test_id, passed, details,
"0x305 fan=P12.6/ADC1_CH10; heater=P12.2/ADC1_CH6")
return details, all_rows
def thermal_phase(s, recorder):
stages = {}
details, off_rows = thermal_current_case(
s, recorder, "CUR-THERMAL-OFF-BASE", 0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0], 0x09, False, 0,
[("", THERMAL_OFF_SETTLE_S)], {})
stages["OFF_BASE"] = details
off_stats = summarize_thermal(off_rows)
baselines = {
"fan": off_stats["fan_p12_6_adc1_ch10"]["raw_p50"],
"heater": off_stats["heater_p12_2_adc1_ch6"]["raw_p50"],
}
s.transmit(0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0],
"thermal max fan/heater level3")
s.transmit(0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0],
"valid heater mode")
time.sleep(0.2)
for gear, feedback in ((1, 0x0A), (2, 0x0B), (3, 0x0C)):
test_id = "CUR-FAN-G%d" % gear
details, unused_rows = thermal_current_case(
s, recorder, test_id, 0x29E,
[0x25, 0, gear, 0, 0, 0, 0, 0], feedback, True, 0,
[("STARTUP", THERMAL_FAN_STARTUP_CAPTURE_S),
("STEADY", THERMAL_FAN_STEADY_CAPTURE_S)], baselines)
del unused_rows
stages["FAN_G%d" % gear] = details
off_id = "CUR-FAN-G%d-OFF" % gear
details, unused_rows = thermal_current_case(
s, recorder, off_id, 0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0], 0x09, False, 0,
[("", THERMAL_OFF_SETTLE_S)], baselines)
del unused_rows
stages["FAN_G%d_OFF" % gear] = details
for gear, feedback in ((1, 0x11), (2, 0x19), (3, 0x21)):
test_id = "CUR-HEATER-G%d" % gear
details, unused_rows = thermal_current_case(
s, recorder, test_id, 0x29E,
[0x12, 0, gear << 4, 0, 0, 0, 0, 0], feedback, False, gear,
[("", THERMAL_HEATER_CAPTURE_S)], baselines)
del unused_rows
stages["HEATER_G%d" % gear] = details
details, unused_rows = thermal_current_case(
s, recorder, "CUR-THERMAL-COMBINED-G2", 0x29E,
[0x15, 0, 0x22, 0, 0, 0, 0, 0], 0x1B, True, 2,
[("", THERMAL_HEATER_CAPTURE_S)], baselines)
del unused_rows
stages["COMBINED_G2"] = details
details, unused_rows = thermal_current_case(
s, recorder, "CUR-THERMAL-OFF-END", 0x42A,
[0x0A, 0, 0, 0, 0, 0, 0, 0], 0x09, False, 0,
[("", THERMAL_OFF_SETTLE_S)], baselines)
del unused_rows
stages["OFF_END"] = details
return {
"pin_mapping": {
"fan": "P12.6 / ADC1_CH10",
"heater": "P12.2 / ADC1_CH6",
},
"off_baseline_raw_p50": baselines,
"stages": stages,
}
def normal_phase(s, recorder):
initial = s.capture_state(1.5)
recovery_ok, recovery = s.ensure_known_absolute_positions(
next_sequence(initial, 0x2000))
s.record("CUR-NORMAL-POSITION", recovery_ok, recovery,
"restore memory or near-home invalid axes only")
if not recovery_ok:
return {}, None
state = s.capture_state(1.0)
sequence = next_sequence(state, 0x2100)
summaries = {}
for motor in range(4):
status = state["motors"][motor]
if status is None or not status["position_valid"]:
s.record("CUR-NORMAL-M%d" % motor, False, status,
"position unavailable", status="NOT_RUN")
continue
start = status["position"]
target = excursion_target(start)
stages = []
payload = command_payload(0x01, motor, 0, sequence,
target, NORMAL_MOVE_TIMEOUT_MS)
ok_out, ack_out, terminal_out, rows_out = command_with_capture(
s, recorder, motor, sequence, payload,
"M%d_NORMAL_OUT" % motor, 40.0, (2, 4))
sequence = (sequence + 1) & 0xFFFF
stages.extend(rows_out)
ok_back = False
ack_back = None
terminal_back = None
rows_back = []
if ok_out and not s.stop_requested:
payload = command_payload(0x01, motor, 0, sequence,
start, NORMAL_MOVE_TIMEOUT_MS)
ok_back, ack_back, terminal_back, rows_back = \
command_with_capture(
s, recorder, motor, sequence, payload,
"M%d_NORMAL_RETURN" % motor, 40.0, (2, 4))
stages.extend(rows_back)
sequence = (sequence + 1) & 0xFFFF
settle_rows = recorder.capture_for(
"M%d_POST_SETTLE" % motor, SETTLE_CAPTURE_S, motor)
stats = summarize(stages)
stats["post_settle"] = summarize(settle_rows)
stats["start_position"] = start
stats["excursion_target"] = target
summaries[str(motor)] = stats
passed = ok_out and ok_back and stats["valid_count"] >= 5 and \
stats["high_current_samples"] == 0
s.record("CUR-NORMAL-M%d" % motor, passed,
{"out_ack": ack_out, "out_terminal": terminal_out,
"return_ack": ack_back,
"return_terminal": terminal_back, "statistics": stats},
"normal motion must stay below configured stall threshold")
time.sleep(0.3)
return summaries, sequence
def endpoint_phase(s, recorder, motor):
state = s.capture_state(1.0)
sequence = next_sequence(state, 0x2200)
payload = command_payload(0x0B, motor, 0, sequence,
_hil_common.HOME_KNOWN_RANGE_KEY,
ENDPOINT_TIMEOUT_S)
passed, ack, terminal, rows = command_with_capture(
s, recorder, motor, sequence, payload,
"M%d_NEAR_ENDPOINT" % motor,
ENDPOINT_TIMEOUT_S + 10.0, (2, 4))
stats = summarize(rows)
s.record("CUR-ENDPOINT-M%d" % motor,
passed and stats["valid_count"] >= 5,
{"ack": ack, "terminal": terminal, "statistics": stats},
"known-range near home only; MaxHall is preserved")
return {str(motor): stats}, sequence + 1
def stall_phase(s, recorder, motor):
state = s.capture_state(1.0)
recovery_ok, recovery = s.ensure_known_absolute_positions(
next_sequence(state, 0x2300))
s.record("CUR-STALL-POSITION", recovery_ok, recovery,
"position must be valid before midpoint placement")
if not recovery_ok:
return {}, None
state = s.capture_state(0.8)
sequence = next_sequence(state, 0x2400)
midpoint = command_payload(0x01, motor, 0, sequence, 5000,
NORMAL_MOVE_TIMEOUT_MS)
placed, place_ack, place_terminal, normal_rows = command_with_capture(
s, recorder, motor, sequence, midpoint,
"M%d_STALL_PLACEMENT" % motor, 40.0, (2, 4))
if not placed:
s.record("CUR-STALL-M%d" % motor, False,
{"ack": place_ack, "terminal": place_terminal},
"midpoint placement failed", status="NOT_RUN")
return {}, sequence + 1
sequence = (sequence + 1) & 0xFFFF
s.log("STALL_FIXTURE_REQUIRED M%d: install a secured soft restraint now" %
motor)
s.countdown(
STALL_FIXTURE_COUNTDOWN_S,
"secure the M%d restraint before output starts; keep hands clear" %
motor)
payload = command_payload(0x03, motor, 1, sequence,
STALL_MOVE_DURATION_MS,
STALL_LEVEL_X10)
ack = s.send_command(payload, expected_ack=1, expected_reject=0,
note="M%d controlled stall window" % motor)
s.log("STALL_ACTION_NOW M%d: output active; keep hands clear" %
motor)
terminal = None
stall_rows = []
if ack is not None and ack["ack"] == 1:
terminal, stall_rows = recorder.wait_terminal(
"M%d_STALL" % motor, motor, sequence, STALL_WAIT_TIMEOUT_S)
s.log("STALL_ACTION_END M%d: release mechanism now" % motor)
s.stop_all(0)
settle_rows = recorder.capture_for(
"M%d_STALL_POST_SETTLE" % motor, SETTLE_CAPTURE_S, motor)
normal_stats = summarize(normal_rows)
stall_stats = summarize(stall_rows)
joint_rows = [row for row in stall_rows
if row["sample_valid"] and row["baseline_valid"] and
row["hall_activity_low"]]
joint_stats = summarize(joint_rows)
passed = (ack is not None and terminal is not None and
terminal["result"] == 5 and
joint_stats["valid_count"] >= 2)
result = {
"normal_placement": normal_stats,
"stall_all_samples": stall_stats,
"stall_low_hall_samples": joint_stats,
"post_settle": summarize(settle_rows),
"terminal": terminal,
}
s.record("CUR-STALL-M%d" % motor, passed, result,
"STALL requires overlapping low Hall and high current")
return {str(motor): result}, sequence + 1
def threshold_guidance(phase, idle_stats, phase_stats):
guidance = {}
for motor in range(4):
key = str(motor)
idle = idle_stats.get(key, {})
current = phase_stats.get(key, {})
observed_thresholds = current.get("configured_thresholds") or \
idle.get("configured_thresholds") or []
configured_threshold = observed_thresholds[0] \
if len(observed_thresholds) == 1 else observed_thresholds or None
item = {
"motor": motor,
"configured_threshold_raw": configured_threshold,
"status": "INSUFFICIENT_DATA",
"reason": "run NORMAL and STALL_M%d, then compare logs" % motor,
}
if phase == "NORMAL" and current:
normal_p99 = current.get("delta_p99")
normal_max = current.get("delta_max")
if normal_p99 is not None:
item.update({
"normal_delta_p99": normal_p99,
"normal_delta_max": normal_max,
"minimum_candidate_raw": max(
int(math.ceil(normal_p99 * 1.35)),
int((idle.get("delta_max") or 0) + 50)),
"status": "NEEDS_STALL_FLOOR",
"reason": "candidate must remain below controlled-stall current",
})
elif phase == "ENDPOINT_M%d" % motor and current:
item.update({
"endpoint_delta_p50": current.get("delta_p50"),
"endpoint_delta_p95": current.get("delta_p95"),
"endpoint_delta_max": current.get("delta_max"),
"status": "ENDPOINT_REFERENCE_ONLY",
"reason": ("compare this endpoint-current reference with "
"NORMAL and controlled STALL logs"),
})
elif phase == "STALL_M%d" % motor and current:
normal = current.get("normal_placement", {})
stall = current.get("stall_low_hall_samples", {})
normal_p99 = normal.get("delta_p99")
stall_p50 = stall.get("delta_p50")
stall_p95 = stall.get("delta_p95")
if normal_p99 is not None and stall_p50 is not None and \
stall_p50 > normal_p99:
proposed = int(round((normal_p99 * 1.35 +
stall_p50 * 0.80) / 2.0))
lower = int(math.ceil(normal_p99 * 1.35))
upper = int(math.floor(stall_p50 * 0.80))
item.update({
"normal_delta_p99": normal_p99,
"stall_low_hall_delta_p50": stall_p50,
"stall_low_hall_delta_p95": stall_p95,
"candidate_lower_raw": lower,
"candidate_upper_raw": upper,
"proposed_raw": proposed if lower <= upper else None,
"status": ("CANDIDATE_AVAILABLE" if lower <= upper else
"NO_SAFE_SEPARATION"),
"reason": "verify against a separate NORMAL log before editing firmware",
})
guidance[key] = item
return guidance
def z_main():
global session
session = None
recorder = None
phase = str(PHASE).strip().upper()
idle_stats = {}
phase_stats = {}
thermal_stats = {}
guidance = {}
report = None
try:
allowed = ["IDLE", "NORMAL", "THERMAL"] + \
["ENDPOINT_M%d" % motor for motor in range(4)] + \
["STALL_M%d" % motor for motor in range(4)]
if phase not in allowed:
raise RuntimeError("Invalid PHASE=%s; allowed=%s" %
(phase, ",".join(allowed)))
session = HilSession("06_current_threshold_" + phase.lower())
s = session
recorder = CurrentRecorder(s)
s.write_operator_template([
"Selected PHASE=%s" % phase,
"Seat path clear and emergency power switch reachable: PASS/FAIL ___",
"Only the selected axis was touched in STALL mode: PASS/FAIL ___",
"Mechanism released immediately after output stopped: PASS/FAIL ___",
"Observed actuator/direction matched MotorID: PASS/FAIL ___",
])
idle_stats = idle_phase(s, recorder)
phase_stats = {}
if phase == "NORMAL" and not s.stop_requested:
phase_stats, unused_sequence = normal_phase(s, recorder)
del unused_sequence
elif phase == "THERMAL" and not s.stop_requested:
thermal_stats = thermal_phase(s, recorder)
elif phase.startswith("ENDPOINT_M") and not s.stop_requested:
motor = int(phase[-1])
phase_stats, unused_sequence = endpoint_phase(s, recorder, motor)
del unused_sequence
elif phase.startswith("STALL_M") and not s.stop_requested:
motor = int(phase[-1])
phase_stats, unused_sequence = stall_phase(s, recorder, motor)
del unused_sequence
guidance = threshold_guidance(phase, idle_stats, phase_stats)
if not thermal_stats:
thermal_stats = {
"pin_mapping": {
"fan": "P12.6 / ADC1_CH10",
"heater": "P12.2 / ADC1_CH6",
},
"all_captured_samples": summarize_thermal(
recorder.thermal_rows),
}
report = {
"phase": phase,
"idle": idle_stats,
"phase_statistics": phase_stats,
"thermal_statistics": thermal_stats,
"threshold_guidance": guidance,
"units": "12-bit ADC raw delta from stopped baseline",
"configured_threshold_raw": {
key: value["configured_threshold_raw"]
for key, value in guidance.items()
},
"current_sample_csv": recorder.path,
"thermal_current_sample_csv": recorder.thermal_path,
}
except Exception as error:
if session is not None:
session.record("SCRIPT", False, repr(error),
"unhandled exception")
report = {
"phase": phase,
"idle": idle_stats,
"phase_statistics": phase_stats,
"thermal_statistics": thermal_stats,
"threshold_guidance": guidance,
"units": "12-bit ADC raw delta from stopped baseline",
"error": repr(error),
"current_sample_csv": (recorder.path
if recorder is not None else None),
"thermal_current_sample_csv": (
recorder.thermal_path if recorder is not None else None),
}
finally:
if session is not None:
try:
session.stop_all(0)
except Exception as error:
session.log("FINAL_STOP_ALL_ERROR " + repr(error))
try:
session.thermal_off()
except Exception as error:
session.log("FINAL_THERMAL_OFF_ERROR " + repr(error))
if recorder is not None:
recorder.close()
if session is not None:
if report is None:
report = {
"phase": phase,
"error": "script ended without a calibration report",
}
try:
report_path = os.path.join(
session.run_dir, "current_calibration.json")
with open(report_path, "w", encoding="utf-8") as stream:
json.dump(report, stream, ensure_ascii=False, indent=2,
sort_keys=True)
session.log("CURRENT_REPORT=" + report_path)
session.log("THRESHOLD_GUIDANCE=" + json.dumps(
guidance, ensure_ascii=False, sort_keys=True))
except Exception as error:
session.record("CURRENT_REPORT", False, repr(error),
"could not write current_calibration.json")
try:
session.finish(report)
except Exception as error:
session.log("FINAL_REPORT_ERROR " + repr(error))
@@ -0,0 +1,139 @@
# 座椅控制器剩余实物 HIL 测试操作说明
更新时间:2026-07-30
当前边界:不再执行四轴全行程标定和25/50/75%定位;当前没有示波器,不判定PWM频率、占空比、换向死区及硬实时关断延迟。
## 1. 已确认基线
| MotorID | 执行器 | 最近端(0%) | 最远端(100%) | MaxHall | 全量程 |
|---:|---|---|---|---:|---|
| M0 | 水平前后 | 座椅最前方 | 座椅最后方 | 480 | 215 mm |
| M1 | 坐盆斜上下 | 坐盆最上方 | 坐盆斜向下方 | 374 | 水平51 mm、垂直56 mm |
| M2 | 靠背前后 | 靠背垂直地面 | 靠背向后躺倒 | 917 | 68° |
| M3 | 腿托上下 | 腿托最下方、垂直地面 | 腿托向上抬起 | 647 | 72° |
所有绝对目标必须满足 `PositionValidMask=0x0F`。该掩码只表示ECU保存/恢复的绝对位置记录可信,不表示各轴位于最近端。正常停止后SMV8直接恢复上次Hall和绝对位置;若某轴记录无效,模式脚本只将失效轴移动到最近端建立Hall=0。若座椅在ECU掉电时被人工移动、操作者不能确认保存位置可信,即使Mask为0xF,也必须点击界面的“强制四轴归零并重建绝对位置”,让四轴真实回最近端后再执行模式。两种恢复都不重新测量最远端。界面以配置表的物理值按上述量程重算归一化位置和预期Hall,配置表原Hall只用于比对。
## 2. 当前还剩几组测试
根据2026-07-30当前进度:`04_can_full_function.py`四轴移动、热管理、协议保护和现有配置模式功能已经完成;正常掉电记忆旧日志因当时PositionValidMask=0而未执行。因此后续剩余 **1组必做实物测试**:
1. 正常掉电位置记忆:`02_power_memory.py` 保持默认 `PHASE="CLEAN"`,只验证SMV8正常掉电恢复,不使位置失效。
若后续再次修改配置表,可重新运行 `seat_function_gui.py --self-test` 核对模式/场景数量和Hall换算冲突,再选择性复测受影响模式。
可选破坏性测试:把 `02_power_memory.py` 改为 `PHASE="MOVING_LOSS"`,在M0运动时掉电。脚本先验证重启后旧位置没有被错误恢复,然后默认用 `0x0B` 将M0回最近端恢复PositionValid,因此不再要求完整量程标定,也不必作为最后一项。
Bus-Off、加热ADC错误和DRV8718故障只有在具备对应故障注入条件时才运行。堵转只能在安全低负载、机构中段人工实施。缺少条件时结果记为 `BLOCKED`,不能记为PASS。
## 3. 推荐执行顺序
### 3.1 前置检查
1. 固定座椅台架,清空四轴行程内人员、工具和线束,保持硬件急停或电源开关可立即操作。
2. ZCANPRO启动Classic CAN通道:11-bit、500 kbit/s、DLC 8。
3. 上电后监听 `0x301`、`0x302`、`0x303`、`0x4FA`,确认反馈持续、`ActiveMask=0`、`FaultedMask=0`。正常记忆有效时 `PositionValidMask=0x0F`。
4. 若PositionValidMask不是0x0F,清空四轴最近端方向的完整行程。新脚本/界面会发送 `0x0B`,只把失效轴回最近端;若返回 `BAD_OPCODE`,说明尚未烧录本次新HEX。
### 3.2 自动完整功能冒烟测试
在ZCANPRO扩展脚本窗口运行 `04_can_full_function.py`,默认 `PHASE="ALL"`:
脚本启动时会强制重新加载 `hil_common.py`。ZCANPRO使用常驻Python解释器,若不重载,修改公共辅助模块后再次运行可能继续使用旧类定义;日志中的 `HIL_COMMON_VERSION` 可用于确认实际加载版本。
- 通风1/2/3档、关闭;加热1/2/3档、关闭;通风2档+加热2档。
- 每轴从当前位置偏移约12%并回到原位置,验证M0/M1/M2/M3的执行器、方向、CAN ACK、终态位置和Hall反馈。
- 若SMV8位置有效则直接使用且不发生归零动作;若某轴无效则先仅将该轴回最近端,验证Hall≈0与MaxHall保持不变后再测试。
- 不进行全行程标定,不执行25/50/75%矩阵。
- 人工在 `operator_observations.txt` 中填写风量、温升、实际执行器和方向是否正确。
若只想测热管理或移动,把脚本顶部 `PHASE` 改为 `THERMAL` 或 `MOTION`。
### 3.3 配置表模式选择界面
界面采用普通Windows Python运行,CAN桥接必须留在ZCANPRO中运行:
1. 在ZCANPRO扩展脚本窗口运行 `05_seat_function_bridge.py`,不要关闭该脚本。
2. 双击 `run_seat_function_gui.bat`。
3. 顶部必须显示“ZCANPRO桥接:在线 / IDLE”。界面会分别显示四轴归一化位置、换算物理位置、Hall/MaxHall和端点;`PositionValidMask=0xF`只表示这些位置记录有效,不表示座椅在最近端。
4. 在“配置表座椅模式”中选择场景。当前配置表为11种模式:小憩模式只保留1个标准场景,新增“小腿解压悬浮模式”;轻松进出模式分为上下车门/关门(坐好),合计12个场景。
5. 界面显示四轴物理目标、CAN归一化值、当前量程重算Hall、原表Hall和预期动作。确认机械空间后点击“执行当前选择”。
6. 等待结果显示 `PASS` 或 `FAIL`;CAN PASS只证明控制与反馈一致,必须观察实物后选择“PASS-符合预期”或“FAIL-不符合预期”,填写说明并点击“保存人工观察”。
7. “通风/加热”页有10项单功能。除“全部关闭”外,输出保持8秒后由桥接自动安全关闭,并同时验证关闭反馈。
8. 任一动作不符时立即点击“STOP ALL + 热管理关闭”;若机构仍动作,直接切断台架电源。
若实际机械位置与界面记录不符或位置可信性未知,点击“强制四轴归零并重建绝对位置”。该按钮忽略Mask当前值,对M0~M3依次真实发送 `HOME_KNOWN_RANGE(0x0B)`:M0到座椅最前方、M1到坐盆最上方、M2到靠背垂直、M3到腿托最下方。已知量程归零使用各轴正常90% Duty;若第一次寻近端少于4个真实Hall边沿,固件不会直接清零,而会先向远端验证至少4个双边沿,再重新寻近端,并要求回程边沿数与离端边沿数相符(允许1个采样边界误差)。因此已经处于最近端的轴会短暂离端再返回;无法证明真实往返运动则归零FAIL且PositionValid失效。绝对位置与480/374/917/647量程仍按Hall上升沿计数,不因双边沿验证而改变。最终成功条件为四轴Hall≈0、Near=1、Position≈0、Mask=0xF,并在停稳后保存SMV8。按钮不运行全量程标定,MaxHall继续保留480/374/917/647。普通模式执行前的自动恢复仍只归零PositionValid失效轴。
配置表当前有一处显著冲突,界面会标红并使用物理值重算结果:
- 女王副驾(行驶)M3:22°应为归一化3056、约198 Hall,原表写178 Hall。
### 3.4 正常掉电记忆
在ZCANPRO运行 `02_power_memory.py`,保持 `PHASE="CLEAN"`:
1. 先用功能模式GUI执行“巡航沙发模式 / 标准场景”,确认PASS并停稳至少3秒。该场景四轴均处于非零中间位置,能有效区分“真实恢复”与默认零值。
2. 确认 `PositionValidMask=0xF`、`ActiveMask=0`、`FaultedMask=0`,四轴Hall约为246/112/405/342。停止 `05_seat_function_bridge.py`,不要再操作GUI。
3. 在ZCANPRO运行 `02_power_memory.py`,确认脚本顶部仍为 `PHASE="CLEAN"`。脚本会先记录掉电前状态;有效SMV8不会触发任何归零动作,随后开始5秒倒计时。
4. 倒计时结束时只切断座椅ECU/台架12 V电源,电脑、ZCANPRO和CAN盒必须持续工作。观察日志出现 `POWER_LOSS_DETECTED`,保持断电至少2秒,再恢复座椅电源。
5. 整个断电和重启过程中不得移动座椅、按键或发送其他CAN控制命令;重启后座椅不得自动动作。
6. 等待脚本自动结束。`MEM-PRECONDITION`、`MEM-CLEAN-RESTORE`应为PASS;掉电后应保持 `PositionValidMask=0xF`、`ActiveMask=0`,每轴Hall误差≤1、归一化位置误差≤30、MaxHall保持480/374/917/647。
7. 日志位于 `Tests/manual_hil/logs/02_power_memory_clean/<时间戳>/`;保存并回传 `report.json`、`summary.csv`、`raw_can.csv`和填好的`operator_observations.txt`。
若脚本在倒计时前自动归零了失效轴,本轮不应直接断电:待脚本结束后重新执行巡航沙发模式,再重跑 `PHASE="CLEAN"`,确保掉电前四轴都是可信的非零位置。
### 3.5 协议保护
在ZCANPRO运行 `03_protection_faults.py`,保持 `PHASE="AUTO"`。脚本不会故意驱动座椅,发送非法或重复命令并检查拒绝码。旧脚本的0x8xxx Sequence区间已修正为0x03xx,避免在STOP_ALL序号0之后被固件误判为旧序号。
### 3.6 电流阈值校准
在ZCANPRO扩展脚本窗口运行 `06_current_threshold_calibration.py`。每次只修改脚本顶部的 `PHASE` 并运行一个阶段,推荐顺序如下:
1. `IDLE`:四轴、通风和加热全部关闭,采集停止态零点;座椅不移动。
2. `THERMAL`:依次采集通风1/2/3档、加热1/2/3档和二档组合。通风电流为 `P12.6 / ADC1_CH10`,加热电流为 `P12.2 / ADC1_CH6`。
3. `NORMAL`:四轴依次从当前位置偏移12%并返回,取得正常运动电流P99和最大值。位置失效时只把失效轴回最近端,不做全行程标定。
4. `ENDPOINT_M0`~`ENDPOINT_M3`:每次只运行一个阶段,把所选轴移动到最近端并采集机械端点电流;`MaxHall` 保持480/374/917/647不变。
5. `STALL_M0`~`STALL_M3`:最后运行,并且每次只测一个轴。脚本先把所选轴放到50%,随后给出10秒倒计时;倒计时期间安装并固定软限位夹具,电机输出开始前必须双手离开机构。仅在台架固定、夹具可靠且急停可立即操作时实施;命令最长3秒,输出停止后拆除夹具。禁止在电机已经运动后用手直接约束机构。
不要在机械端点实施 `STALL_Mx`,也不要同时约束多个轴。没有安全人工堵转条件时,可只回传 `IDLE`、`NORMAL` 和四个 `ENDPOINT_Mx`,将STALL项标记为未执行。
每次运行会生成:
- `current_samples.csv`:每个 `0x304` 四轴电流样本,含raw、停止态baseline、delta、当前轴阈值、Hall低活动和高电流标志。
- `thermal_current_samples.csv`:每个 `0x305` 通风/加热样本,含P12.6风扇raw、P12.2加热raw、运行档位和ADC错误计数。
- `current_calibration.json`:各阶段P50/P95/P99/最大值、正常/低Hall重叠样本和候选阈值区间。
- `raw_can.csv`、`summary.csv`、`report.json`:完整CAN与自动判定记录。
四轴生产阈值不能只按理论电流设置。候选下界取正常运动P99的约1.35倍并高于停止噪声,候选上界取受控堵转低Hall样本P50的约0.8倍;只有下界小于上界且独立复跑 `NORMAL` 不触发高电流标志时才可写入 `seat_move_config.h` 的M0~M3独立阈值。`THERMAL` 当前用于建立各档实际电流基线,固件尚未用它自动判定通风/加热开路或过流。
## 4. 日志位置与回传
ZCANPRO手动脚本日志:
```text
Seat_Controller_demo/Tests/manual_hil/logs/<测试组>/YYYYMMDD_HHMMSS/
```
选择界面日志:
```text
Seat_Controller_demo/Tests/manual_hil/logs/05_seat_function_gui/YYYYMMDD_HHMMSS_<请求后缀>/
```
每次测试至少包含:
- `summary.csv`:自动判定汇总。
- `report.json`:请求、解析值、目标/实际位置和结果。
- `raw_can.csv`:所有TX/RX帧及时间。
- `operator_observation.json` 或 `operator_observations.txt`:实际机械、风量和温升观察。
回传时请提供完整时间戳目录,不要只截取PASS/FAIL一行。
## 5. 已归档、不再执行
- `01_calibration_position.py`:已设置 `ARCHIVED_DO_NOT_RUN=True`,误运行只生成SKIP日志,不做全轴标定。
- `04_waveform_full_function.py`:已设置 `ARCHIVED_DO_NOT_RUN=True`,无示波器时不再产生波形刺激;改用 `04_can_full_function.py`。
- PWM频率/占空比、换向死区、STOP硬实时关断延迟:当前统一记为 `BLOCKED(缺少示波器)`。
量程测试脚本的四个 `run_modeN_test.bat` 仍保留作维修/重新标定用途,但不属于本轮测试流程。
@@ -0,0 +1,538 @@
"""Shared ZCANPRO helpers for the manual seat-controller HIL suites."""
import csv
import datetime
import json
import os
import time
import zcanpro
SEAT_RX_IDS = (0x301, 0x302, 0x303, 0x304, 0x305, 0x4FA)
HIL_COMMON_VERSION = "2026-07-30-hall-current-stall-v4"
CONFIRMED_MAX_HALL = (480, 374, 917, 647)
HOME_KNOWN_RANGE_KEY = 0x4B52
HOME_LEG_TIMEOUT_S = 120
def hex_data(data):
return " ".join("%02X" % (int(value) & 0xFF) for value in data)
def frame_id(frame):
return int(frame.get("can_id", 0)) & 0x1FFFFFFF
def parse_301(data):
if data is None or len(data) < 8:
return None
return {
"ack": data[0] & 0x0F,
"system_state": (data[0] >> 4) & 0x0F,
"reject": data[1],
"sequence": data[2] | (data[3] << 8),
"opcode": data[4],
"active_mask": data[5] & 0x0F,
"position_valid_mask": (data[6] >> 4) & 0x0F,
"faulted_mask": data[6] & 0x0F,
"alive": data[7] & 0x0F,
"firmware_revision": (data[7] >> 4) & 0x0F,
}
def parse_302(data):
if data is None or len(data) < 8:
return None
return {
"motor": data[0],
"task": data[1] & 0x0F,
"result": (data[1] >> 4) & 0x0F,
"direction": data[2] & 0x03,
"active": bool(data[2] & 0x04),
"position_valid": bool(data[2] & 0x08),
"near": bool(data[2] & 0x10),
"far": bool(data[2] & 0x20),
"stall": bool(data[2] & 0x40),
"driver_fault": bool(data[2] & 0x80),
"sequence": data[3] | (data[4] << 8),
"position": data[5] | (data[6] << 8),
"duty": data[7],
}
def parse_303(data):
if data is None or len(data) < 8:
return None
return {
"motor": data[0],
"event": data[1] & 0x0F,
"fault_flags": (data[1] >> 4) & 0x0F,
"hall": data[2] | (data[3] << 8),
"max_hall": data[4] | (data[5] << 8),
"speed_pps_x100": data[6] * 50,
"retry_count": data[7] & 0x0F,
"alive": (data[7] >> 4) & 0x0F,
}
def parse_304(data):
if data is None or len(data) < 8:
return None
raw = data[2] | (data[3] << 8)
baseline = data[4] | (data[5] << 8)
threshold = data[6] | (data[7] << 8)
flags = data[1]
return {
"motor": data[0],
"sample_valid": bool(flags & 0x01),
"baseline_valid": bool(flags & 0x02),
"high_current": bool(flags & 0x04),
"hall_activity_low": bool(flags & 0x08),
"calibrating": bool(flags & 0x10),
"raw": raw,
"baseline": baseline,
"delta": abs(raw - baseline),
"threshold": threshold,
}
def parse_305(data):
if data is None or len(data) < 8:
return None
return {
"fan_sample_valid": bool(data[0] & 0x01),
"heater_sample_valid": bool(data[0] & 0x02),
"fan_running": bool(data[1] & 0x01),
"heater_level": (data[1] >> 1) & 0x03,
"fan_raw": data[2] | (data[3] << 8),
"heater_raw": data[4] | (data[5] << 8),
"fan_adc_errors": data[6],
"heater_adc_errors": data[7],
}
def command_payload(opcode, motor, direction, sequence, param_a=0,
param_b=0, version=1):
control = ((version & 0x03) << 4) | ((direction & 0x03) << 2) | \
(motor & 0x03)
return [opcode & 0xFF, control, sequence & 0xFF,
(sequence >> 8) & 0xFF, param_a & 0xFF,
(param_a >> 8) & 0xFF, param_b & 0xFF,
(param_b >> 8) & 0xFF]
class HilSession(object):
def __init__(self, group_name):
self.group_name = group_name
self.stop_requested = False
self.results = []
self.trace = []
self.latest = {}
self.current_peaks = {}
self.started_monotonic = time.monotonic()
base = os.path.dirname(os.path.abspath(__file__))
timestamp = time.strftime("%Y%m%d_%H%M%S")
self.run_dir = os.path.join(base, "logs", group_name, timestamp)
os.makedirs(self.run_dir, exist_ok=True)
self.raw_path = os.path.join(self.run_dir, "raw_can.csv")
self.raw_stream = open(self.raw_path, "w", newline="",
encoding="utf-8-sig")
self.raw_writer = csv.DictWriter(
self.raw_stream,
fieldnames=["local_time", "elapsed_s", "direction", "can_id",
"dlc", "data", "adapter_timestamp_us", "note"])
self.raw_writer.writeheader()
buses = zcanpro.get_buses()
if not buses:
self.raw_stream.close()
raise RuntimeError("No started CAN bus in ZCANPRO")
self.bus = buses[0]
self.bus_id = self.bus["busID"]
self.log("RUN_DIR=" + self.run_dir)
self.log("BUS=" + str(self.bus))
self.log("HIL_COMMON_VERSION=" + HIL_COMMON_VERSION)
def request_stop(self):
self.stop_requested = True
self.log("ZCANPRO stop requested")
def log(self, message):
zcanpro.write_log(str(message))
def _raw(self, direction, can_id, data, adapter_timestamp_us=0,
note=""):
self.raw_writer.writerow({
"local_time": datetime.datetime.now().isoformat(timespec="milliseconds"),
"elapsed_s": "%.6f" % (time.monotonic() - self.started_monotonic),
"direction": direction,
"can_id": "0x%03X" % int(can_id),
"dlc": len(data),
"data": hex_data(data),
"adapter_timestamp_us": int(adapter_timestamp_us or 0),
"note": note,
})
self.raw_stream.flush()
def transmit(self, can_id, data, note=""):
values = list(data)
frame = {"can_id": int(can_id), "is_canfd": 0,
"canfd_brs": 0, "data": values}
self._raw("TX", can_id, values, note=note)
if not zcanpro.transmit(self.bus_id, [frame]):
raise RuntimeError("Transmit failed: 0x%03X %s" %
(can_id, hex_data(values)))
def receive(self):
ok, frames = zcanpro.receive(self.bus_id)
if not ok or not frames:
return []
output = []
for frame in frames:
can_id = frame_id(frame)
data = list(frame.get("data", []))
self._raw("RX", can_id, data, frame.get("timestamp_us", 0))
if can_id == 0x301:
self.latest[0x301] = parse_301(data)
elif can_id == 0x302:
value = parse_302(data)
if value is not None and value["motor"] < 4:
self.latest[(0x302, value["motor"])] = value
elif can_id == 0x303:
value = parse_303(data)
if value is not None and value["motor"] < 4:
self.latest[(0x303, value["motor"])] = value
elif can_id == 0x304:
value = parse_304(data)
if value is not None and value["motor"] < 4:
motor = value["motor"]
self.latest[(0x304, motor)] = value
peak = self.current_peaks.get(motor)
if peak is None or value["delta"] > peak["delta"]:
high_seen = value["high_current"] or (
peak is not None and
peak.get("high_current_seen", False))
hall_low_seen = value["hall_activity_low"] or (
peak is not None and
peak.get("hall_activity_low_seen", False))
peak = dict(value)
peak["high_current_seen"] = high_seen
peak["hall_activity_low_seen"] = hall_low_seen
self.current_peaks[motor] = peak
else:
peak["high_current_seen"] = \
peak.get("high_current_seen", False) or \
value["high_current"]
peak["hall_activity_low_seen"] = \
peak.get("hall_activity_low_seen", False) or \
value["hall_activity_low"]
elif can_id == 0x305:
self.latest[0x305] = parse_305(data)
elif can_id == 0x4FA:
self.latest[0x4FA] = data
output.append((can_id, data, frame))
return output
def drain(self, rounds=12):
for _ in range(rounds):
if not self.receive():
break
def wait_frame(self, can_id, predicate=None, timeout_s=2.0):
deadline = time.monotonic() + timeout_s
while time.monotonic() < deadline and not self.stop_requested:
for rx_id, data, frame in self.receive():
del frame
if rx_id != can_id:
continue
if predicate is None or predicate(data):
return data
time.sleep(0.005)
# A frame that did not satisfy the predicate is not a valid response.
# Returning it used to hide explicit command rejections behind a later
# ordinary cyclic status frame.
return None
def send_command(self, payload, expected_ack=None, expected_reject=None,
timeout_s=2.0, note=""):
sequence = payload[2] | (payload[3] << 8)
self.drain()
self.transmit(0x300, payload, note)
def same_sequence(data):
value = parse_301(data)
return value is not None and value["sequence"] == sequence
# Preserve the first response for this command even when it is an
# unexpected ACK/Reject. Callers can then report the real ECU reject
# reason instead of timing out or recording an unrelated cyclic 0x301.
data = self.wait_frame(0x301, same_sequence, timeout_s)
value = parse_301(data)
matches_expected = value is not None
if matches_expected and expected_ack is not None:
matches_expected = value["ack"] == expected_ack
if matches_expected and expected_reject is not None:
matches_expected = value["reject"] == expected_reject
self.trace.append({"type": "command", "payload": hex_data(payload),
"response": value,
"matches_expected": matches_expected,
"expected_ack": expected_ack,
"expected_reject": expected_reject,
"note": note})
return value
def stop_all(self, sequence=0):
payload = command_payload(0x09, 0, 0, sequence)
return self.send_command(payload, expected_ack=4,
expected_reject=0, note="STOP_ALL")
def thermal_off(self):
self.transmit(0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0],
"fan+heater off")
def wait_motor_terminal(self, motor, sequence, timeout_s):
deadline = time.monotonic() + timeout_s
latest = None
while time.monotonic() < deadline and not self.stop_requested:
for can_id, data, frame in self.receive():
del frame
if can_id != 0x302:
continue
value = parse_302(data)
if value is None or value["motor"] != motor:
continue
latest = value
if value["sequence"] == sequence and not value["active"] and \
value["task"] in (6, 7):
return value
time.sleep(0.005)
return latest
def capture_state(self, duration_s=2.0, clear=True):
if clear:
for key in list(self.latest):
if key in (0x301, 0x305) or isinstance(key, tuple):
del self.latest[key]
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline and not self.stop_requested:
self.receive()
time.sleep(0.005)
return {
"system": self.latest.get(0x301),
"motors": [self.latest.get((0x302, motor)) for motor in range(4)],
"diagnostics": [self.latest.get((0x303, motor))
for motor in range(4)],
"currents": [self.latest.get((0x304, motor))
for motor in range(4)],
"thermal_current": self.latest.get(0x305),
}
def reset_current_peaks(self, motor=None):
if motor is None:
self.current_peaks.clear()
else:
self.current_peaks.pop(motor, None)
def ensure_known_absolute_positions(self, sequence_base=0x0B00):
"""Home only invalid axes to the known near endpoint.
This is not a range calibration: opcode 0x0B preserves the confirmed
MaxHall values and establishes Hall=0/PositionValid at the near end.
Axes restored from a clean SMV8 record are never moved here.
"""
initial = self.capture_state(1.5)
system = initial["system"]
if system is None:
return False, {
"summary": "no 0x301 system status before position recovery",
"initial": initial,
}
initial_mask = system["position_valid_mask"]
range_issues = []
for motor, expected in enumerate(CONFIRMED_MAX_HALL):
diagnostic = initial["diagnostics"][motor]
if diagnostic is None:
range_issues.append("M%d no 0x303" % motor)
elif abs(diagnostic["max_hall"] - expected) > \
max(4, int(expected * 0.05)):
range_issues.append("M%d MaxHall=%d expected~%d" %
(motor, diagnostic["max_hall"], expected))
if range_issues:
return False, {
"summary": "confirmed range mismatch: " +
"; ".join(range_issues),
"initial": initial,
}
invalid = [motor for motor in range(4)
if (initial_mask & (1 << motor)) == 0]
if not invalid:
return True, {
"summary": "clean SMV8 positions already valid; no homing",
"initial_position_valid_mask": initial_mask,
"final_position_valid_mask": initial_mask,
"homed_axes": [],
}
if system["active_mask"] != 0:
return False, {
"summary": "cannot recover position while an axis is active",
"initial": initial,
}
names = ("M0 seat forward", "M1 cushion upward",
"M2 backrest vertical", "M3 legrest downward")
self.log("POSITION_UNKNOWN: home invalid axes only: " +
", ".join(names[motor] for motor in invalid))
self.countdown(
3, "clear the listed near-end paths; automatic homing will start")
homed = []
for motor in invalid:
if self.stop_requested:
return False, {
"summary": "STOP requested during near-end recovery",
"homed_axes": homed,
}
sequence = (int(sequence_base) + motor) & 0xFFFF
payload = command_payload(
0x0B, motor, 0, sequence, HOME_KNOWN_RANGE_KEY,
HOME_LEG_TIMEOUT_S)
ack = self.send_command(
payload, expected_ack=1, expected_reject=0, timeout_s=3.0,
note="known-range near home M%d MaxHall=%d" %
(motor, CONFIRMED_MAX_HALL[motor]))
if (ack is None or ack["sequence"] != sequence or
ack["ack"] != 1 or ack["reject"] != 0):
return False, {
"summary": ("M%d near-home command rejected; BAD_OPCODE "
"means the new firmware has not been flashed") %
motor,
"motor": motor,
"ack": ack,
"homed_axes": homed,
}
terminal = self.wait_motor_terminal(
motor, sequence, HOME_LEG_TIMEOUT_S + 15.0)
state = self.capture_state(1.0, clear=False)
status = state["motors"][motor]
diagnostic = state["diagnostics"][motor]
expected_max = CONFIRMED_MAX_HALL[motor]
passed = (
terminal is not None and terminal["sequence"] == sequence and
terminal["task"] == 6 and terminal["result"] == 2 and
status is not None and status["position_valid"] and
status["near"] and status["position"] <= 100 and
diagnostic is not None and diagnostic["hall"] <= 2 and
abs(diagnostic["max_hall"] - expected_max) <=
max(4, int(expected_max * 0.05)))
item = {
"motor": motor,
"passed": passed,
"terminal": terminal,
"status": status,
"diagnostic": diagnostic,
"expected_max_hall": expected_max,
}
homed.append(item)
if not passed:
return False, {
"summary": "M%d near-home terminal/status mismatch" % motor,
"homed_axes": homed,
}
final = self.capture_state(1.5)
final_system = final["system"]
final_mask = 0 if final_system is None else \
final_system["position_valid_mask"]
final_range_issues = []
for motor, expected in enumerate(CONFIRMED_MAX_HALL):
diagnostic = final["diagnostics"][motor]
if diagnostic is None or \
abs(diagnostic["max_hall"] - expected) > \
max(4, int(expected * 0.05)):
final_range_issues.append(motor)
passed = final_mask == 0x0F and not final_range_issues
return passed, {
"summary": ("invalid axes homed with confirmed ranges; "
"PositionValidMask=0x%X") % final_mask,
"initial_position_valid_mask": initial_mask,
"final_position_valid_mask": final_mask,
"homed_axes": homed,
"range_issue_axes": final_range_issues,
"full_range_calibration": False,
}
def wait_power_cycle(self, timeout_s=120.0, outage_s=1.0):
self.log("POWER_ACTION: remove ECU/seat power, wait >=2 s, then restore it")
deadline = time.monotonic() + timeout_s
last_ecu = time.monotonic()
outage_seen = False
while time.monotonic() < deadline and not self.stop_requested:
now = time.monotonic()
for can_id, data, frame in self.receive():
del data, frame
if can_id in SEAT_RX_IDS:
if outage_seen and can_id == 0x301:
self.log("POWER_RESTORED: 0x301 received")
return True
last_ecu = now
if not outage_seen and (now - last_ecu) >= outage_s:
outage_seen = True
self.log("POWER_LOSS_DETECTED: seat feedback gap %.1f s" % outage_s)
time.sleep(0.01)
self.log("POWER_CYCLE_TIMEOUT outage_seen=%s" % outage_seen)
return False
def countdown(self, seconds, message):
self.log("OPERATOR_ACTION: " + message)
for remaining in range(int(seconds), 0, -1):
if self.stop_requested:
break
self.log("ACTION_COUNTDOWN %u" % remaining)
time.sleep(1.0)
def record(self, test_id, passed, actual, notes="", status=None):
if status is None:
status = "PASS" if passed else "FAIL"
row = {"TestID": test_id, "Result": status,
"Actual": actual, "Notes": notes}
self.results.append(row)
self.log("[%s] %s actual=%s notes=%s" %
(status, test_id, str(actual), str(notes)))
def write_operator_template(self, lines):
path = os.path.join(self.run_dir, "operator_observations.txt")
with open(path, "w", encoding="utf-8-sig") as stream:
stream.write("Fill PASS/FAIL and observations, then return this file with the logs.\n")
for line in lines:
stream.write("[ ] " + line + "\n")
def finish(self, extra=None):
summary_path = os.path.join(self.run_dir, "summary.csv")
with open(summary_path, "w", newline="", encoding="utf-8-sig") as stream:
writer = csv.DictWriter(
stream, fieldnames=["TestID", "Result", "Actual", "Notes"])
writer.writeheader()
writer.writerows(self.results)
report = {
"generated_local": datetime.datetime.now().isoformat(timespec="seconds"),
"group": self.group_name,
"bus": self.bus,
"summary": {
"total": len(self.results),
"pass": sum(1 for row in self.results
if row["Result"] == "PASS"),
"fail": sum(1 for row in self.results
if row["Result"] == "FAIL"),
"not_run": sum(1 for row in self.results
if row["Result"] in ("NOT_RUN", "SKIP")),
},
"results": self.results,
"trace": self.trace,
"extra": extra or {},
}
with open(os.path.join(self.run_dir, "report.json"), "w",
encoding="utf-8") as stream:
json.dump(report, stream, ensure_ascii=False, indent=2,
sort_keys=True, default=str)
self.raw_stream.flush()
self.raw_stream.close()
self.log("HIL_LOGS=" + self.run_dir)
@@ -0,0 +1,24 @@
@echo off
setlocal
cd /d "%~dp0"
where py >nul 2>nul
if errorlevel 1 goto use_python
set "SEAT_GUI_PY=py -3"
goto run_gui
:use_python
set "SEAT_GUI_PY=python"
:run_gui
echo Checking the latest seat mode workbook...
%SEAT_GUI_PY% seat_function_gui.py --self-test
if errorlevel 1 (
echo.
echo Workbook self-test failed. The GUI was not started.
pause
exit /b 1
)
%SEAT_GUI_PY% seat_function_gui.py
if errorlevel 1 pause
endlocal
@@ -0,0 +1,663 @@
"""Standalone Windows GUI for selectable seat-function CAN/HIL tests.
This process does not import ``zcanpro``. It exchanges short-lived JSON
requests with ``05_seat_function_bridge.py`` running inside ZCANPRO.
"""
from __future__ import unicode_literals
import datetime
import hashlib
import json
import os
from pathlib import Path
import sys
import time
import uuid
from seat_mode_model import flatten_scenarios, load_modes
BASE_DIR = Path(__file__).resolve().parent
REPOSITORY_DIR = BASE_DIR.parents[2]
WORKBOOK_PATH = REPOSITORY_DIR / "座椅功能模式配置表.xlsx"
RUNTIME_DIR = BASE_DIR / "seat_function_runtime"
PENDING_DIR = RUNTIME_DIR / "requests" / "pending"
STATUS_PATH = RUNTIME_DIR / "bridge_status.json"
EMERGENCY_PATH = RUNTIME_DIR / "emergency_stop.json"
THERMAL_FUNCTIONS = (
{
"key": "通风关闭",
"frames": [[0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x09,
"expected_action": "座椅通风关闭;同时将加热安全关闭",
},
{
"key": "通风1档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x29E, [0x25, 0, 0x01, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x0A,
"expected_action": "通风启动;约2秒启动增强后保持1档",
},
{
"key": "通风2档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x29E, [0x25, 0, 0x02, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x0B,
"expected_action": "通风启动并保持2档;加热关闭",
},
{
"key": "通风3档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x29E, [0x25, 0, 0x03, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x0C,
"expected_action": "通风启动并保持3档;加热关闭",
},
{
"key": "加热关闭",
"frames": [[0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x09,
"expected_action": "靠背加热关闭;同时将通风安全关闭",
},
{
"key": "加热1档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0]],
[0x29E, [0x12, 0, 0x10, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x11,
"expected_action": "靠背加热启动并保持1档;通风关闭",
},
{
"key": "加热2档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0]],
[0x29E, [0x12, 0, 0x20, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x19,
"expected_action": "靠背加热启动并保持2档;通风关闭",
},
{
"key": "加热3档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0]],
[0x29E, [0x12, 0, 0x30, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x21,
"expected_action": "靠背加热启动并保持3档;通风关闭",
},
{
"key": "通风2档+加热2档",
"frames": [[0x2AA, [0x01, 0, 0x1B, 0, 0, 0, 0, 0]],
[0x3CE, [0x10, 0, 0, 0, 0, 0, 0, 0]],
[0x29E, [0x15, 0, 0x22, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x1B,
"expected_action": "通风和加热同时保持2档",
},
{
"key": "全部关闭",
"frames": [[0x42A, [0x0A, 0, 0, 0, 0, 0, 0, 0]]],
"expected_byte0": 0x09,
"expected_action": "通风和加热均关闭",
},
)
def _atomic_json(path, value):
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(path.name + ".%s.tmp" % os.getpid())
with temporary.open("w", encoding="utf-8") as stream:
json.dump(value, stream, ensure_ascii=False, indent=2,
sort_keys=True)
stream.flush()
os.fsync(stream.fileno())
os.replace(str(temporary), str(path))
def _read_json(path):
try:
with path.open("r", encoding="utf-8") as stream:
return json.load(stream)
except (OSError, ValueError):
return None
def _workbook_sha256():
digest = hashlib.sha256()
with WORKBOOK_PATH.open("rb") as stream:
for chunk in iter(lambda: stream.read(65536), b""):
digest.update(chunk)
return digest.hexdigest().upper()
def make_request(action, payload):
now = time.time()
request_id = "%s_%s" % (
datetime.datetime.now().strftime("%Y%m%d_%H%M%S"),
uuid.uuid4().hex[:8])
request = {
"schema": "seat-function-request-v1",
"request_id": request_id,
"action": action,
"created_epoch": now,
"armed_until_epoch": now + 20.0,
"payload": payload,
}
_atomic_json(PENDING_DIR / (request_id + ".json"), request)
return request
def _self_test():
modes = load_modes(WORKBOOK_PATH)
scenarios = flatten_scenarios(modes)
conflicts = []
for scenario in scenarios:
conflicts.extend("%s: %s" % (scenario["key"], warning)
for warning in scenario["warnings"])
print(json.dumps({
"workbook": str(WORKBOOK_PATH),
"sha256": _workbook_sha256(),
"mode_count": len(modes),
"scenario_count": len(scenarios),
"modes": [mode["name"] for mode in modes],
"scenarios": [scenario["key"] for scenario in scenarios],
"conflicts": conflicts,
"thermal_function_count": len(THERMAL_FUNCTIONS),
}, ensure_ascii=False, indent=2))
class SeatFunctionApp(object):
def __init__(self, root):
import tkinter as tk
from tkinter import messagebox, scrolledtext, ttk
self.tk = tk
self.ttk = ttk
self.messagebox = messagebox
self.scrolledtext = scrolledtext
self.root = root
self.root.title("座椅功能 CAN/HIL 测试台")
self.root.geometry("1180x900")
self.root.minsize(980, 760)
self.root.protocol("WM_DELETE_WINDOW", self._close)
self.scenarios = flatten_scenarios(load_modes(WORKBOOK_PATH))
self.thermal_items = list(THERMAL_FUNCTIONS)
self.selected_scenario = self.scenarios[0]
self.selected_thermal = self.thermal_items[0]
self.last_status_signature = None
self.last_log_dir = None
self.seen_bridge_messages = []
self.last_displayed_request_id = None
self._build()
self._show_scenario(self.selected_scenario)
self._poll_status()
def _build(self):
tk, ttk = self.tk, self.ttk
top = ttk.Frame(self.root, padding=(10, 8))
top.pack(fill="x")
ttk.Label(top, text="座椅功能 CAN/HIL 测试台",
font=("Microsoft YaHei UI", 16, "bold")).pack(side="left")
self.bridge_var = tk.StringVar(value="ZCANPRO桥接:未连接")
self.mask_var = tk.StringVar(value="PositionValidMask:-- ActiveMask:--")
ttk.Label(top, textvariable=self.bridge_var).pack(side="right")
ttk.Label(self.root, textvariable=self.mask_var, padding=(12, 0)).pack(
fill="x")
ttk.Label(
self.root,
text=("说明:PositionValid=1只表示ECU中的Hall绝对位置记录可信,"
"不表示电机位于最近端;机械位置不确定时必须执行真实归零。"),
foreground="#8A4B08", padding=(12, 2)).pack(fill="x")
position_box = ttk.LabelFrame(
self.root, text="ECU当前绝对位置(0=最近端,10000=最远端)",
padding=(6, 3))
position_box.pack(fill="x", padx=10, pady=(4, 0))
columns = ("axis", "valid", "normalized", "physical", "hall",
"endpoint", "state")
self.position_tree = ttk.Treeview(
position_box, columns=columns, show="headings", height=4)
headings = ("轴", "记录", "归一化位置", "换算物理位置", "Hall/MaxHall",
"端点", "状态")
widths = (48, 72, 105, 180, 120, 88, 170)
for column, heading, width in zip(columns, headings, widths):
self.position_tree.heading(column, text=heading)
self.position_tree.column(
column, width=width,
stretch=column in ("physical", "state"))
self.position_tree.tag_configure(
"invalid", background="#FFE1E1", foreground="#8B0000")
self.position_tree.tag_configure(
"active", background="#E4F1FF")
self.position_tree.pack(fill="x")
self._update_position_display(None)
notebook = ttk.Notebook(self.root)
notebook.pack(fill="both", expand=True, padx=10, pady=8)
mode_tab = ttk.Frame(notebook, padding=8)
thermal_tab = ttk.Frame(notebook, padding=8)
notebook.add(mode_tab, text="配置表座椅模式")
notebook.add(thermal_tab, text="通风 / 加热")
self._build_mode_tab(mode_tab)
self._build_thermal_tab(thermal_tab)
controls = ttk.Frame(self.root, padding=(10, 0, 10, 6))
controls.pack(fill="x")
self.execute_button = ttk.Button(
controls, text="执行当前选择", command=self._execute_selected)
self.execute_button.pack(side="left")
ttk.Button(controls, text="强制四轴归零并重建绝对位置",
command=self._initialize_known_ranges).pack(
side="left", padx=(10, 0))
ttk.Button(controls, text="STOP ALL + 热管理关闭",
command=self._emergency_stop).pack(side="left", padx=10)
ttk.Label(controls, text="实际观察:").pack(side="left", padx=(18, 3))
self.observation_var = tk.StringVar(value="请选择")
ttk.Combobox(controls, textvariable=self.observation_var,
state="readonly", width=12,
values=("请选择", "PASS-符合预期", "FAIL-不符合预期",
"BLOCKED-未能观察")).pack(side="left")
self.note_var = tk.StringVar()
ttk.Entry(controls, textvariable=self.note_var, width=38).pack(
side="left", padx=6, fill="x", expand=True)
ttk.Button(controls, text="保存人工观察",
command=self._save_observation).pack(side="right")
log_box = ttk.LabelFrame(self.root, text="实际CAN结果 / 日志", padding=5)
log_box.pack(fill="both", expand=True, padx=10, pady=(0, 10))
self.log_text = self.scrolledtext.ScrolledText(
log_box, height=12, wrap="word", font=("Consolas", 9))
self.log_text.pack(fill="both", expand=True)
self.log_text.configure(state="disabled")
def _build_mode_tab(self, parent):
ttk = self.ttk
parent.columnconfigure(1, weight=1)
parent.rowconfigure(0, weight=1)
left = ttk.Frame(parent)
left.grid(row=0, column=0, sticky="nsw", padx=(0, 8))
self.mode_list = self.tk.Listbox(left, width=31, exportselection=False,
font=("Microsoft YaHei UI", 10))
self.mode_list.pack(fill="y", expand=True)
for scenario in self.scenarios:
self.mode_list.insert("end", scenario["key"])
self.mode_list.selection_set(0)
self.mode_list.bind("<<ListboxSelect>>", self._mode_selected)
right = ttk.Frame(parent)
right.grid(row=0, column=1, sticky="nsew")
right.columnconfigure(0, weight=1)
right.rowconfigure(1, weight=1)
ttk.Label(right, text="CAN目标以物理值和480/374/917/647重新计算;原表Hall仅作审计。",
foreground="#224488").grid(row=0, column=0, sticky="w", pady=(0, 5))
columns = ("axis", "physical", "normalized", "expected", "source", "action")
self.axis_tree = ttk.Treeview(right, columns=columns, show="headings",
height=6)
headings = ("轴", "物理目标", "CAN归一化", "重算Hall", "原表Hall", "预期动作")
widths = (48, 80, 90, 80, 80, 350)
for column, heading, width in zip(columns, headings, widths):
self.axis_tree.heading(column, text=heading)
self.axis_tree.column(column, width=width, stretch=column == "action")
self.axis_tree.tag_configure("conflict", background="#FFD7D7")
self.axis_tree.grid(row=1, column=0, sticky="nsew")
self.mode_warning_var = self.tk.StringVar()
ttk.Label(right, textvariable=self.mode_warning_var,
foreground="#B00020", wraplength=780).grid(
row=2, column=0, sticky="ew", pady=(6, 0))
def _build_thermal_tab(self, parent):
ttk = self.ttk
parent.columnconfigure(1, weight=1)
parent.rowconfigure(0, weight=1)
self.thermal_list = self.tk.Listbox(parent, width=28, exportselection=False,
font=("Microsoft YaHei UI", 10))
self.thermal_list.grid(row=0, column=0, sticky="nsw", padx=(0, 10))
for item in self.thermal_items:
self.thermal_list.insert("end", item["key"])
self.thermal_list.selection_set(0)
self.thermal_list.bind("<<ListboxSelect>>", self._thermal_selected)
info = ttk.Frame(parent)
info.grid(row=0, column=1, sticky="nsew")
info.columnconfigure(0, weight=1)
self.thermal_expected_var = self.tk.StringVar()
self.thermal_frames_var = self.tk.StringVar()
ttk.Label(info, text="预期实物动作", font=("Microsoft YaHei UI", 11,
"bold")).grid(
row=0, column=0, sticky="w")
ttk.Label(info, textvariable=self.thermal_expected_var,
wraplength=760).grid(row=1, column=0, sticky="ew", pady=(4, 14))
ttk.Label(info, text="将发送的CAN帧", font=("Microsoft YaHei UI", 11,
"bold")).grid(
row=2, column=0, sticky="w")
ttk.Label(info, textvariable=self.thermal_frames_var,
font=("Consolas", 10), wraplength=760).grid(
row=3, column=0, sticky="ew", pady=(4, 14))
ttk.Label(info, text="无示波器时仅判定CAN反馈、风量/温升和安全关闭;不判PWM频率或占空比。",
foreground="#7A5200", wraplength=760).grid(
row=4, column=0, sticky="ew")
self._show_thermal(self.selected_thermal)
def _mode_selected(self, event=None):
del event
selection = self.mode_list.curselection()
if selection:
self.selected_scenario = self.scenarios[int(selection[0])]
self.thermal_list.selection_clear(0, "end")
self._show_scenario(self.selected_scenario)
def _thermal_selected(self, event=None):
del event
selection = self.thermal_list.curselection()
if selection:
self.selected_thermal = self.thermal_items[int(selection[0])]
self.mode_list.selection_clear(0, "end")
self._show_thermal(self.selected_thermal)
def _show_scenario(self, scenario):
for item in self.axis_tree.get_children():
self.axis_tree.delete(item)
for axis in scenario["axes"]:
tag = "conflict" if axis["conflict"] else ""
self.axis_tree.insert("", "end", values=(
"M%d" % axis["motor"],
"%s%s" % (axis["physical_value"], axis["unit"]),
axis["normalized"], axis["expected_hall"],
"--" if axis["source_hall"] is None else axis["source_hall"],
axis["expected_action"]), tags=(tag,))
self.mode_warning_var.set(
";".join(scenario["warnings"]) if scenario["warnings"] else
"原表Hall与当前量程重算一致(允许显示物理值造成的±1 Hall舍入差)。")
def _show_thermal(self, item):
suffix = "" if item["key"] == "全部关闭" else \
";测试保持8秒后自动关闭通风和加热"
self.thermal_expected_var.set(item["expected_action"] + suffix)
lines = []
for can_id, data in item["frames"]:
lines.append("0x%03X %s" % (
can_id, " ".join("%02X" % value for value in data)))
lines.append("预期0x4FA Byte0 = 0x%02X" % item["expected_byte0"])
self.thermal_frames_var.set("\n".join(lines))
def _bridge_status(self):
status = _read_json(STATUS_PATH)
if (status is not None and
time.time() - float(status.get("heartbeat_epoch", 0)) < 3.0):
return status
return None
@staticmethod
def _physical_position_text(motor, normalized):
ratio = float(normalized) / 10000.0
if motor == 0:
return "%.1f mm" % (215.0 * ratio)
if motor == 1:
return "水平%.1f / 垂直%.1f mm" % (
51.0 * ratio, 56.0 * ratio)
if motor == 2:
return "%.1f°" % (68.0 * ratio)
return "%.1f°" % (72.0 * ratio)
def _update_position_display(self, bridge_status):
for item in self.position_tree.get_children():
self.position_tree.delete(item)
motors = [] if bridge_status is None else \
(bridge_status.get("motors") or [])
diagnostics = [] if bridge_status is None else \
(bridge_status.get("diagnostics") or [])
for motor in range(4):
status = motors[motor] if motor < len(motors) else None
diag = diagnostics[motor] if motor < len(diagnostics) else None
if status is None or diag is None:
values = ("M%d" % motor, "--", "--", "--", "--", "--",
"等待0x302/0x303")
self.position_tree.insert(
"", "end", values=values, tags=("invalid",))
continue
valid = bool(status.get("position_valid", False))
position = int(status.get("position", 0))
hall = int(diag.get("hall", 0))
max_hall = int(diag.get("max_hall", 0))
endpoint = "最近端" if status.get("near") else \
("最远端" if status.get("far") else "中间位置")
state = "运动中" if status.get("active") else "静止"
consistent = valid and max_hall > 0 and 0 <= hall <= max_hall
if consistent:
hall_position = int(hall * 10000.0 / max_hall + 0.5)
tolerance = max(100, int(20000.0 / max_hall + 0.999))
consistent = abs(position - hall_position) <= tolerance
if not valid:
state = "绝对位置未知,需归零"
elif not consistent:
state = "Hall/位置不一致,需归零"
tag = "active" if status.get("active") else \
("" if consistent else "invalid")
self.position_tree.insert("", "end", values=(
"M%d" % motor,
"有效" if valid else "无效",
position if valid else "--",
self._physical_position_text(motor, position)
if valid else "--",
"%d/%d" % (hall, max_hall),
endpoint if valid else "未知",
state), tags=(tag,))
def _execute_selected(self):
bridge_status = self._bridge_status()
if bridge_status is None:
self.messagebox.showerror(
"桥接未连接", "请先在ZCANPRO中运行05_seat_function_bridge.py。")
return
if bridge_status.get("bridge_state") != "IDLE":
self.messagebox.showwarning(
"桥接忙", "当前桥接状态为%s,请等待完成或先按STOP ALL。" %
bridge_status.get("bridge_state", "UNKNOWN"))
return
mode_selection = self.mode_list.curselection()
if mode_selection:
scenario = self.selected_scenario
actions = "\n".join(axis["expected_action"] for axis in scenario["axes"])
warnings = "\n".join(scenario["warnings"])
prompt = "%s\n\n%s" % (scenario["key"], actions)
system = bridge_status.get("system") or {}
position_valid_mask = int(system.get("position_valid_mask", 0))
if position_valid_mask != 0x0F:
prompt += (
"\n\n当前PositionValidMask=0x%X。执行前将仅把失效轴依次"
"移动到最近端归零,再移动到上述绝对目标:\n"
"M0座椅最前方 / M1坐盆最上方 / "
"M2靠背垂直 / M3腿托最下方。" % position_valid_mask)
if warnings:
prompt += "\n\n配置表冲突(将使用重算值):\n" + warnings
prompt += "\n\n确认机构无障碍并执行?"
if not self.messagebox.askyesno("确认座椅动作", prompt):
return
payload = {
"kind": "mode",
"name": scenario["key"],
"axes": scenario["axes"],
"warnings": scenario["warnings"],
"workbook": str(WORKBOOK_PATH),
"workbook_sha256": _workbook_sha256(),
"allow_auto_home": True,
}
request = make_request("execute_mode", payload)
else:
item = self.selected_thermal
if not self.messagebox.askyesno(
"确认热管理动作", "%s\n\n%s\n\n确认执行?" %
(item["key"], item["expected_action"])):
return
thermal_payload = dict(item)
thermal_payload["hold_s"] = 8.0
request = make_request("execute_thermal", thermal_payload)
self._append_log("REQUEST %s 已提交" % request["request_id"])
def _initialize_known_ranges(self):
bridge_status = self._bridge_status()
if bridge_status is None:
self.messagebox.showerror(
"桥接未连接", "请先在ZCANPRO中运行05_seat_function_bridge.py。")
return
if bridge_status.get("bridge_state") != "IDLE":
self.messagebox.showwarning(
"桥接忙", "当前桥接状态为%s。" %
bridge_status.get("bridge_state", "UNKNOWN"))
return
prompt = (
"此操作会忽略当前PositionValidMask,四个电机都会依次真实移动到"
"最近端并重建Hall=0。它用于机械位置可能被人工改变、Hall记录不可信"
"或需要重新建立绝对原点的情况。\n\n"
"此操作不重新测量最远端量程,继续使用已确认MaxHall:\n\n"
"M0:座椅移动到最前方\n"
"M1:坐盆移动到最上方\n"
"M2:靠背移动到垂直地面\n"
"M3:腿托移动到最下方(垂直地面)\n\n"
"为防止无Hall/驱动力不足被误认为端点:若某轴一开始几乎没有"
"Hall运动,固件会先向远端跨过至少4个真实Hall边沿,再重新寻找"
"最近端。因此已经在最近端的轴会出现一次很短的离端再返回动作。\n\n"
"完成后四轴Hall应约为0,绝对位置写入SMV8,"
"PositionValidMask应为0xF。\n"
"确认四轴最近端完整行程内无人员、工具和线束,并开始?")
if not self.messagebox.askyesno("确认强制四轴机械归零", prompt):
return
request = make_request("initialize_known_ranges", {
"name": "强制四轴回最近端并重建绝对位置",
"confirmed_max_hall": [480, 374, 917, 647],
"full_range_measurement": False,
"force_all_axes": True,
})
self._append_log(
"FORCE HOME ALL REQUEST %s 已提交" % request["request_id"])
def _emergency_stop(self):
now = time.time()
_atomic_json(EMERGENCY_PATH, {
"schema": "seat-function-emergency-v1",
"request_id": "STOP_%s" % uuid.uuid4().hex[:8],
"created_epoch": now,
"armed_until_epoch": now + 20.0,
"stop_all": True,
"thermal_off": True,
})
self._append_log("EMERGENCY STOP请求已写入;如机构仍动作请立即切断台架电源。")
def _save_observation(self):
if not self.last_log_dir:
self.messagebox.showwarning("没有测试结果", "请先完成一项测试。")
return
result = self.observation_var.get()
if result == "请选择":
self.messagebox.showwarning("缺少判定", "请选择实际观察结果。")
return
path = Path(self.last_log_dir) / "operator_observation.json"
value = {
"saved_local": datetime.datetime.now().isoformat(timespec="seconds"),
"result": result,
"note": self.note_var.get().strip(),
}
_atomic_json(path, value)
self._append_log("人工观察已保存到 %s" % path)
def _append_log(self, message):
stamp = datetime.datetime.now().strftime("%H:%M:%S")
self.log_text.configure(state="normal")
self.log_text.insert("end", "[%s] %s\n" % (stamp, message))
self.log_text.see("end")
self.log_text.configure(state="disabled")
def _append_report_details(self, report_path):
if not report_path:
return
report = _read_json(Path(report_path))
if report is None:
return
details = report.get("details") or {}
for axis in details.get("axis_results", []):
status = axis.get("status") or {}
diagnostic = axis.get("diagnostic") or {}
self._append_log(
"M%d %s target=%s actual_pos=%s expected_hall=%s actual_hall=%s max=%s" %
(axis.get("motor", -1),
"PASS" if axis.get("passed") else "FAIL",
axis.get("target_normalized"), status.get("position"),
axis.get("expected_hall_from_reported_range"),
diagnostic.get("hall"), diagnostic.get("max_hall")))
for axis in details.get("axes", []):
status = axis.get("status") or {}
diagnostic = axis.get("diagnostic") or {}
initial_diagnostic = axis.get("initial_diagnostic") or {}
peak_current = axis.get("peak_current") or {}
self._append_log(
("HOME M%d %s initial_hall=%s final=%s/%s near=%s "
"phases=%s motion_evidence=%s peak_current=%s elapsed=%ss") %
(axis.get("motor", -1), axis.get("result", "--"),
initial_diagnostic.get("hall"), diagnostic.get("hall"),
diagnostic.get("max_hall"), status.get("near"),
axis.get("observed_direction_sequence"),
axis.get("can_motion_evidence"),
peak_current.get("delta"), axis.get("home_elapsed_s")))
if "active_feedback" in details:
active = details.get("active_feedback")
off = details.get("off_feedback")
self._append_log("0x4FA active=%s auto_off=%s" % (active, off))
def _poll_status(self):
status = _read_json(STATUS_PATH)
online = (status is not None and
time.time() - float(status.get("heartbeat_epoch", 0)) < 3.0)
if online:
self.bridge_var.set("ZCANPRO桥接:在线 / %s" %
status.get("bridge_state", "UNKNOWN"))
system = status.get("system") or {}
self.mask_var.set(
("PositionValidMask:0x%X(位置记录有效位,不代表最近端) "
"ActiveMask:0x%X FaultedMask:0x%X") %
(int(system.get("position_valid_mask", 0)),
int(system.get("active_mask", 0)),
int(system.get("faulted_mask", 0))))
self._update_position_display(status)
signature = json.dumps({
"state": status.get("bridge_state"),
"request": status.get("current_request_id"),
"result": status.get("last_result"),
"messages": status.get("messages", [])[-8:],
}, ensure_ascii=False, sort_keys=True)
if signature != self.last_status_signature:
self.last_status_signature = signature
result = status.get("last_result")
if (result and result.get("request_id") !=
self.last_displayed_request_id):
self.last_displayed_request_id = result.get("request_id")
self.last_log_dir = result.get("log_dir")
self._append_log("RESULT %s: %s - %s" %
(result.get("request_id", "--"),
result.get("result", "--"),
result.get("summary", "")))
self._append_report_details(result.get("report_path"))
for message in status.get("messages", [])[-8:]:
if message not in self.seen_bridge_messages:
self._append_log(str(message))
self.seen_bridge_messages.append(message)
self.seen_bridge_messages = self.seen_bridge_messages[-80:]
else:
self.bridge_var.set("ZCANPRO桥接:离线")
self.mask_var.set("PositionValidMask:-- ActiveMask:--")
self._update_position_display(None)
self.root.after(500, self._poll_status)
def _close(self):
self.root.destroy()
def main():
if "--self-test" in sys.argv:
_self_test()
return 0
import tkinter as tk
root = tk.Tk()
SeatFunctionApp(root)
root.mainloop()
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,285 @@
"""Load seat-function modes from the customer XLSX without third-party modules.
The workbook is the audit source. CAN targets are always recalculated from
the confirmed 2026-07-30 physical ranges instead of trusting its pulse text.
"""
from __future__ import unicode_literals
import posixpath
import re
import zipfile
from xml.etree import ElementTree
AXES = (
{
"motor": 0,
"name": "水平电机",
"short_name": "水平前后",
"unit": "mm",
"full_scale": 215.0,
"max_hall": 480,
"near": "座椅最前方",
"far": "座椅最后方",
},
{
"motor": 1,
"name": "坐盆电机",
"short_name": "坐盆斜上下",
"unit": "%",
"full_scale": 100.0,
"max_hall": 374,
"near": "坐盆最上方",
"far": "坐盆斜向下方",
},
{
"motor": 2,
"name": "靠背电机",
"short_name": "靠背前后",
"unit": "°",
"full_scale": 68.0,
"max_hall": 917,
"near": "靠背垂直地面",
"far": "靠背向后躺倒68°",
},
{
"motor": 3,
"name": "腿托电机",
"short_name": "腿托上下",
"unit": "°",
"full_scale": 72.0,
"max_hall": 647,
"near": "腿托最下方(垂直地面)",
"far": "腿托向上抬起72°",
},
)
_NS = "{http://schemas.openxmlformats.org/spreadsheetml/2006/main}"
_REL_NS = "{http://schemas.openxmlformats.org/officeDocument/2006/relationships}"
_PKG_REL_NS = "{http://schemas.openxmlformats.org/package/2006/relationships}"
_NUMBER_RE = re.compile(r"[-+]?\d+(?:\.\d+)?")
_MODE_START_ROW = 2
_MODE_SCAN_LAST_ROW = 200
class SeatModeWorkbookError(RuntimeError):
"""Raised when the expected mode table cannot be read safely."""
def _round_half_up(value):
return int(float(value) + 0.5)
def _shared_strings(archive):
try:
root = ElementTree.fromstring(archive.read("xl/sharedStrings.xml"))
except KeyError:
return []
values = []
for item in root.findall(_NS + "si"):
values.append("".join(node.text or "" for node in
item.iter(_NS + "t")))
return values
def _sheet_path(archive, sheet_name):
workbook = ElementTree.fromstring(archive.read("xl/workbook.xml"))
relationship_id = None
sheets = workbook.find(_NS + "sheets")
if sheets is not None:
for sheet in sheets.findall(_NS + "sheet"):
if sheet.attrib.get("name") == sheet_name:
relationship_id = sheet.attrib.get(_REL_NS + "id")
break
if not relationship_id:
raise SeatModeWorkbookError("工作簿中找不到工作表 %s" % sheet_name)
rels = ElementTree.fromstring(
archive.read("xl/_rels/workbook.xml.rels"))
target = None
for rel in rels.findall(_PKG_REL_NS + "Relationship"):
if rel.attrib.get("Id") == relationship_id:
target = rel.attrib.get("Target")
break
if not target:
raise SeatModeWorkbookError("找不到工作表 %s 的XML关系" % sheet_name)
if target.startswith("/"):
return target.lstrip("/")
return posixpath.normpath(posixpath.join("xl", target))
def _read_cells(workbook_path, sheet_name="Sheet1"):
with zipfile.ZipFile(workbook_path, "r") as archive:
shared = _shared_strings(archive)
root = ElementTree.fromstring(archive.read(
_sheet_path(archive, sheet_name)))
output = {}
for cell in root.iter(_NS + "c"):
address = cell.attrib.get("r")
cell_type = cell.attrib.get("t")
value_node = cell.find(_NS + "v")
if cell_type == "inlineStr":
inline = cell.find(_NS + "is")
value = "" if inline is None else "".join(
node.text or "" for node in inline.iter(_NS + "t"))
elif value_node is None:
value = ""
elif cell_type == "s":
index = int(value_node.text or "0")
value = shared[index] if index < len(shared) else ""
else:
value = value_node.text or ""
if address:
output[address] = value
return output
def _sections(text):
"""Return [(scenario_name, body)] for plain or labelled multi-scenarios."""
lines = [line.strip() for line in (text or "").replace("\r", "").split("\n")]
labelled = []
current_name = None
current_lines = []
for line in lines:
if line and not re.match(r"^\d+[、.]", line) and line.endswith((":", ":")):
if current_name is not None:
labelled.append((current_name, "\n".join(current_lines)))
current_name = line[:-1].strip()
current_lines = []
elif line:
current_lines.append(line)
if current_name is not None:
labelled.append((current_name, "\n".join(current_lines)))
return labelled
return [("标准场景", "\n".join(line for line in lines if line))]
def _axis_numbers(body, pulse=False):
values = {}
for line in body.splitlines():
match = re.match(r"^(\d+)[、.]\s*[^::]+[::]\s*(.*)$", line.strip())
if match is None:
continue
motor = int(match.group(1)) - 1
number = _NUMBER_RE.search(match.group(2))
if 0 <= motor < 4 and number is not None:
raw = float(number.group(0))
values[motor] = int(raw) if pulse else raw
return values
def _format_value(value):
if abs(value - round(value)) < 0.000001:
return str(int(round(value)))
return ("%.2f" % value).rstrip("0").rstrip(".")
def _action_description(axis, value):
if value <= 0.000001:
endpoint = axis["near"]
elif value >= axis["full_scale"] - 0.000001:
endpoint = axis["far"]
else:
endpoint = "从最近端向最远端 %.1f%%" % (
value * 100.0 / axis["full_scale"])
return "%s:%s%s(%s)" % (
axis["short_name"], _format_value(value), axis["unit"], endpoint)
def load_modes(workbook_path):
"""Load the contiguous Sheet1 R:T mode table and return scenarios."""
cells = _read_cells(workbook_path, "Sheet1")
modes = []
for row in range(_MODE_START_ROW, _MODE_SCAN_LAST_ROW + 1):
mode_name = cells.get("R%d" % row, "").strip()
physical_text = cells.get("S%d" % row, "").strip()
pulse_text = cells.get("T%d" % row, "").strip()
if not mode_name:
if modes:
break
continue
physical_sections = _sections(physical_text)
pulse_sections = dict(_sections(pulse_text))
scenarios = []
for scenario_name, physical_body in physical_sections:
physical_values = _axis_numbers(physical_body, pulse=False)
pulse_body = pulse_sections.get(scenario_name)
if pulse_body is None and len(pulse_sections) == 1:
pulse_body = next(iter(pulse_sections.values()))
source_halls = _axis_numbers(pulse_body or "", pulse=True)
axes = []
warnings = []
for axis in AXES:
motor = axis["motor"]
if motor not in physical_values:
raise SeatModeWorkbookError(
"%s/%s 缺少M%d物理目标" %
(mode_name, scenario_name, motor))
value = physical_values[motor]
if value < 0 or value > axis["full_scale"]:
raise SeatModeWorkbookError(
"%s/%s M%d目标%.2f超出0..%.2f%s" %
(mode_name, scenario_name, motor, value,
axis["full_scale"], axis["unit"]))
normalized = _round_half_up(
value * 10000.0 / axis["full_scale"])
expected_hall = _round_half_up(
normalized * axis["max_hall"] / 10000.0)
source_hall = source_halls.get(motor)
# A one-pulse difference can come from rounding a displayed
# physical target, so only larger differences are conflicts.
conflict = (source_hall is not None and
abs(source_hall - expected_hall) > 1)
if conflict:
warnings.append(
"M%d原表=%d Hall,当前量程重算=%d Hall" %
(motor, source_hall, expected_hall))
axes.append({
"motor": motor,
"name": axis["name"],
"short_name": axis["short_name"],
"physical_value": value,
"unit": axis["unit"],
"full_scale": axis["full_scale"],
"normalized": normalized,
"expected_hall": expected_hall,
"source_hall": source_hall,
"max_hall": axis["max_hall"],
"conflict": conflict,
"expected_action": _action_description(axis, value),
})
scenarios.append({
"mode": mode_name,
"scenario": scenario_name,
"key": "%s / %s" % (mode_name, scenario_name),
"axes": axes,
"warnings": warnings,
"source_physical_text": physical_body,
"source_pulse_text": pulse_body or "",
})
modes.append({"name": mode_name, "scenarios": scenarios})
if not modes:
raise SeatModeWorkbookError(
"Sheet1 R:T中没有读取到连续的功能模式配置")
mode_names = [mode["name"] for mode in modes]
duplicate_names = sorted(set(
name for name in mode_names if mode_names.count(name) > 1))
if duplicate_names:
raise SeatModeWorkbookError(
"功能模式名称重复:%s" % "、".join(duplicate_names))
nap_modes = [mode for mode in modes if mode["name"] == "小憩模式"]
if len(nap_modes) != 1 or len(nap_modes[0]["scenarios"]) != 1:
raise SeatModeWorkbookError(
"小憩模式必须且只能保留1种模式/1个场景")
calf_float_modes = [mode for mode in modes if all(
token in mode["name"] for token in ("小腿", "悬浮", "解压"))]
if len(calf_float_modes) != 1:
raise SeatModeWorkbookError(
"必须且只能配置1种小腿悬浮解压模式")
return modes
def flatten_scenarios(modes):
return [scenario for mode in modes for scenario in mode["scenarios"]]
+201
View File
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,411 @@
/******************************************************************************
* @file cachel1_armv7.h
* @brief CMSIS Level 1 Cache API for Armv7-M and later
* @version V1.0.1
* @date 19. April 2021
******************************************************************************/
/*
* Copyright (c) 2020-2021 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef ARM_CACHEL1_ARMV7_H
#define ARM_CACHEL1_ARMV7_H
/**
\ingroup CMSIS_Core_FunctionInterface
\defgroup CMSIS_Core_CacheFunctions Cache Functions
\brief Functions that configure Instruction and Data cache.
@{
*/
/* Cache Size ID Register Macros */
#define CCSIDR_WAYS(x) (((x) & SCB_CCSIDR_ASSOCIATIVITY_Msk) >> SCB_CCSIDR_ASSOCIATIVITY_Pos)
#define CCSIDR_SETS(x) (((x) & SCB_CCSIDR_NUMSETS_Msk ) >> SCB_CCSIDR_NUMSETS_Pos )
#ifndef __SCB_DCACHE_LINE_SIZE
#define __SCB_DCACHE_LINE_SIZE 32U /*!< Cortex-M7 cache line size is fixed to 32 bytes (8 words). See also register SCB_CCSIDR */
#endif
#ifndef __SCB_ICACHE_LINE_SIZE
#define __SCB_ICACHE_LINE_SIZE 32U /*!< Cortex-M7 cache line size is fixed to 32 bytes (8 words). See also register SCB_CCSIDR */
#endif
/**
\brief Enable I-Cache
\details Turns on I-Cache
*/
__STATIC_FORCEINLINE void SCB_EnableICache (void)
{
#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
if (SCB->CCR & SCB_CCR_IC_Msk) return; /* return if ICache is already enabled */
__DSB();
__ISB();
SCB->ICIALLU = 0UL; /* invalidate I-Cache */
__DSB();
__ISB();
SCB->CCR |= (uint32_t)SCB_CCR_IC_Msk; /* enable I-Cache */
__DSB();
__ISB();
#endif
}
/**
\brief Disable I-Cache
\details Turns off I-Cache
*/
__STATIC_FORCEINLINE void SCB_DisableICache (void)
{
#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
__DSB();
__ISB();
SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk; /* disable I-Cache */
SCB->ICIALLU = 0UL; /* invalidate I-Cache */
__DSB();
__ISB();
#endif
}
/**
\brief Invalidate I-Cache
\details Invalidates I-Cache
*/
__STATIC_FORCEINLINE void SCB_InvalidateICache (void)
{
#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
__DSB();
__ISB();
SCB->ICIALLU = 0UL;
__DSB();
__ISB();
#endif
}
/**
\brief I-Cache Invalidate by address
\details Invalidates I-Cache for the given address.
I-Cache is invalidated starting from a 32 byte aligned address in 32 byte granularity.
I-Cache memory blocks which are part of given address + given size are invalidated.
\param[in] addr address
\param[in] isize size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_InvalidateICache_by_Addr (volatile void *addr, int32_t isize)
{
#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
if ( isize > 0 ) {
int32_t op_size = isize + (((uint32_t)addr) & (__SCB_ICACHE_LINE_SIZE - 1U));
uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_ICACHE_LINE_SIZE - 1U) */;
__DSB();
do {
SCB->ICIMVAU = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */
op_addr += __SCB_ICACHE_LINE_SIZE;
op_size -= __SCB_ICACHE_LINE_SIZE;
} while ( op_size > 0 );
__DSB();
__ISB();
}
#endif
}
/**
\brief Enable D-Cache
\details Turns on D-Cache
*/
__STATIC_FORCEINLINE void SCB_EnableDCache (void)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t ccsidr;
uint32_t sets;
uint32_t ways;
if (SCB->CCR & SCB_CCR_DC_Msk) return; /* return if DCache is already enabled */
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
ccsidr = SCB->CCSIDR;
/* invalidate D-Cache */
sets = (uint32_t)(CCSIDR_SETS(ccsidr));
do {
ways = (uint32_t)(CCSIDR_WAYS(ccsidr));
do {
SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |
((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) );
#if defined ( __CC_ARM )
__schedule_barrier();
#endif
} while (ways-- != 0U);
} while(sets-- != 0U);
__DSB();
SCB->CCR |= (uint32_t)SCB_CCR_DC_Msk; /* enable D-Cache */
__DSB();
__ISB();
#endif
}
/**
\brief Disable D-Cache
\details Turns off D-Cache
*/
__STATIC_FORCEINLINE void SCB_DisableDCache (void)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t ccsidr;
uint32_t sets;
uint32_t ways;
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk; /* disable D-Cache */
__DSB();
ccsidr = SCB->CCSIDR;
/* clean & invalidate D-Cache */
sets = (uint32_t)(CCSIDR_SETS(ccsidr));
do {
ways = (uint32_t)(CCSIDR_WAYS(ccsidr));
do {
SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) );
#if defined ( __CC_ARM )
__schedule_barrier();
#endif
} while (ways-- != 0U);
} while(sets-- != 0U);
__DSB();
__ISB();
#endif
}
/**
\brief Invalidate D-Cache
\details Invalidates D-Cache
*/
__STATIC_FORCEINLINE void SCB_InvalidateDCache (void)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t ccsidr;
uint32_t sets;
uint32_t ways;
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
ccsidr = SCB->CCSIDR;
/* invalidate D-Cache */
sets = (uint32_t)(CCSIDR_SETS(ccsidr));
do {
ways = (uint32_t)(CCSIDR_WAYS(ccsidr));
do {
SCB->DCISW = (((sets << SCB_DCISW_SET_Pos) & SCB_DCISW_SET_Msk) |
((ways << SCB_DCISW_WAY_Pos) & SCB_DCISW_WAY_Msk) );
#if defined ( __CC_ARM )
__schedule_barrier();
#endif
} while (ways-- != 0U);
} while(sets-- != 0U);
__DSB();
__ISB();
#endif
}
/**
\brief Clean D-Cache
\details Cleans D-Cache
*/
__STATIC_FORCEINLINE void SCB_CleanDCache (void)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t ccsidr;
uint32_t sets;
uint32_t ways;
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
ccsidr = SCB->CCSIDR;
/* clean D-Cache */
sets = (uint32_t)(CCSIDR_SETS(ccsidr));
do {
ways = (uint32_t)(CCSIDR_WAYS(ccsidr));
do {
SCB->DCCSW = (((sets << SCB_DCCSW_SET_Pos) & SCB_DCCSW_SET_Msk) |
((ways << SCB_DCCSW_WAY_Pos) & SCB_DCCSW_WAY_Msk) );
#if defined ( __CC_ARM )
__schedule_barrier();
#endif
} while (ways-- != 0U);
} while(sets-- != 0U);
__DSB();
__ISB();
#endif
}
/**
\brief Clean & Invalidate D-Cache
\details Cleans and Invalidates D-Cache
*/
__STATIC_FORCEINLINE void SCB_CleanInvalidateDCache (void)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
uint32_t ccsidr;
uint32_t sets;
uint32_t ways;
SCB->CSSELR = 0U; /* select Level 1 data cache */
__DSB();
ccsidr = SCB->CCSIDR;
/* clean & invalidate D-Cache */
sets = (uint32_t)(CCSIDR_SETS(ccsidr));
do {
ways = (uint32_t)(CCSIDR_WAYS(ccsidr));
do {
SCB->DCCISW = (((sets << SCB_DCCISW_SET_Pos) & SCB_DCCISW_SET_Msk) |
((ways << SCB_DCCISW_WAY_Pos) & SCB_DCCISW_WAY_Msk) );
#if defined ( __CC_ARM )
__schedule_barrier();
#endif
} while (ways-- != 0U);
} while(sets-- != 0U);
__DSB();
__ISB();
#endif
}
/**
\brief D-Cache Invalidate by address
\details Invalidates D-Cache for the given address.
D-Cache is invalidated starting from a 32 byte aligned address in 32 byte granularity.
D-Cache memory blocks which are part of given address + given size are invalidated.
\param[in] addr address
\param[in] dsize size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_InvalidateDCache_by_Addr (volatile void *addr, int32_t dsize)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if ( dsize > 0 ) {
int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
__DSB();
do {
SCB->DCIMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */
op_addr += __SCB_DCACHE_LINE_SIZE;
op_size -= __SCB_DCACHE_LINE_SIZE;
} while ( op_size > 0 );
__DSB();
__ISB();
}
#endif
}
/**
\brief D-Cache Clean by address
\details Cleans D-Cache for the given address
D-Cache is cleaned starting from a 32 byte aligned address in 32 byte granularity.
D-Cache memory blocks which are part of given address + given size are cleaned.
\param[in] addr address
\param[in] dsize size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr (volatile void *addr, int32_t dsize)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if ( dsize > 0 ) {
int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
__DSB();
do {
SCB->DCCMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */
op_addr += __SCB_DCACHE_LINE_SIZE;
op_size -= __SCB_DCACHE_LINE_SIZE;
} while ( op_size > 0 );
__DSB();
__ISB();
}
#endif
}
/**
\brief D-Cache Clean and Invalidate by address
\details Cleans and invalidates D_Cache for the given address
D-Cache is cleaned and invalidated starting from a 32 byte aligned address in 32 byte granularity.
D-Cache memory blocks which are part of given address + given size are cleaned and invalidated.
\param[in] addr address (aligned to 32-byte boundary)
\param[in] dsize size of memory block (in number of bytes)
*/
__STATIC_FORCEINLINE void SCB_CleanInvalidateDCache_by_Addr (volatile void *addr, int32_t dsize)
{
#if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
if ( dsize > 0 ) {
int32_t op_size = dsize + (((uint32_t)addr) & (__SCB_DCACHE_LINE_SIZE - 1U));
uint32_t op_addr = (uint32_t)addr /* & ~(__SCB_DCACHE_LINE_SIZE - 1U) */;
__DSB();
do {
SCB->DCCIMVAC = op_addr; /* register accepts only 32byte aligned values, only bits 31..5 are valid */
op_addr += __SCB_DCACHE_LINE_SIZE;
op_size -= __SCB_DCACHE_LINE_SIZE;
} while ( op_size > 0 );
__DSB();
__ISB();
}
#endif
}
/*@} end of CMSIS_Core_CacheFunctions */
#endif /* ARM_CACHEL1_ARMV7_H */
@@ -0,0 +1,888 @@
/**************************************************************************//**
* @file cmsis_armcc.h
* @brief CMSIS compiler ARMCC (Arm Compiler 5) header file
* @version V5.3.2
* @date 27. May 2021
******************************************************************************/
/*
* Copyright (c) 2009-2021 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CMSIS_ARMCC_H
#define __CMSIS_ARMCC_H
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 400677)
#error "Please use Arm Compiler Toolchain V4.0.677 or later!"
#endif
/* CMSIS compiler control architecture macros */
#if ((defined (__TARGET_ARCH_6_M ) && (__TARGET_ARCH_6_M == 1)) || \
(defined (__TARGET_ARCH_6S_M ) && (__TARGET_ARCH_6S_M == 1)) )
#define __ARM_ARCH_6M__ 1
#endif
#if (defined (__TARGET_ARCH_7_M ) && (__TARGET_ARCH_7_M == 1))
#define __ARM_ARCH_7M__ 1
#endif
#if (defined (__TARGET_ARCH_7E_M) && (__TARGET_ARCH_7E_M == 1))
#define __ARM_ARCH_7EM__ 1
#endif
/* __ARM_ARCH_8M_BASE__ not applicable */
/* __ARM_ARCH_8M_MAIN__ not applicable */
/* __ARM_ARCH_8_1M_MAIN__ not applicable */
/* CMSIS compiler control DSP macros */
#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
#define __ARM_FEATURE_DSP 1
#endif
/* CMSIS compiler specific defines */
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __INLINE
#define __INLINE __inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static __inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE static __forceinline
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __declspec(noreturn)
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __PACKED
#define __PACKED __attribute__((packed))
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT __packed struct
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION __packed union
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
#define __UNALIGNED_UINT32(x) (*((__packed uint32_t *)(x)))
#endif
#ifndef __UNALIGNED_UINT16_WRITE
#define __UNALIGNED_UINT16_WRITE(addr, val) ((*((__packed uint16_t *)(addr))) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
#define __UNALIGNED_UINT16_READ(addr) (*((const __packed uint16_t *)(addr)))
#endif
#ifndef __UNALIGNED_UINT32_WRITE
#define __UNALIGNED_UINT32_WRITE(addr, val) ((*((__packed uint32_t *)(addr))) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
#define __UNALIGNED_UINT32_READ(addr) (*((const __packed uint32_t *)(addr)))
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __attribute__((aligned(x)))
#endif
#ifndef __RESTRICT
#define __RESTRICT __restrict
#endif
#ifndef __COMPILER_BARRIER
#define __COMPILER_BARRIER() __memory_changed()
#endif
/* ######################### Startup and Lowlevel Init ######################## */
#ifndef __PROGRAM_START
#define __PROGRAM_START __main
#endif
#ifndef __INITIAL_SP
#define __INITIAL_SP Image$$ARM_LIB_STACK$$ZI$$Limit
#endif
#ifndef __STACK_LIMIT
#define __STACK_LIMIT Image$$ARM_LIB_STACK$$ZI$$Base
#endif
#ifndef __VECTOR_TABLE
#define __VECTOR_TABLE __Vectors
#endif
#ifndef __VECTOR_TABLE_ATTRIBUTE
#define __VECTOR_TABLE_ATTRIBUTE __attribute__((used, section("RESET")))
#endif
/* ########################## Core Instruction Access ######################### */
/** \defgroup CMSIS_Core_InstructionInterface CMSIS Core Instruction Interface
Access to dedicated instructions
@{
*/
/**
\brief No Operation
\details No Operation does nothing. This instruction can be used for code alignment purposes.
*/
#define __NOP __nop
/**
\brief Wait For Interrupt
\details Wait For Interrupt is a hint instruction that suspends execution until one of a number of events occurs.
*/
#define __WFI __wfi
/**
\brief Wait For Event
\details Wait For Event is a hint instruction that permits the processor to enter
a low-power state until one of a number of events occurs.
*/
#define __WFE __wfe
/**
\brief Send Event
\details Send Event is a hint instruction. It causes an event to be signaled to the CPU.
*/
#define __SEV __sev
/**
\brief Instruction Synchronization Barrier
\details Instruction Synchronization Barrier flushes the pipeline in the processor,
so that all instructions following the ISB are fetched from cache or memory,
after the instruction has been completed.
*/
#define __ISB() __isb(0xF)
/**
\brief Data Synchronization Barrier
\details Acts as a special kind of Data Memory Barrier.
It completes when all explicit memory accesses before this instruction complete.
*/
#define __DSB() __dsb(0xF)
/**
\brief Data Memory Barrier
\details Ensures the apparent order of the explicit memory operations before
and after the instruction, without ensuring their completion.
*/
#define __DMB() __dmb(0xF)
/**
\brief Reverse byte order (32 bit)
\details Reverses the byte order in unsigned integer value. For example, 0x12345678 becomes 0x78563412.
\param [in] value Value to reverse
\return Reversed value
*/
#define __REV __rev
/**
\brief Reverse byte order (16 bit)
\details Reverses the byte order within each halfword of a word. For example, 0x12345678 becomes 0x34127856.
\param [in] value Value to reverse
\return Reversed value
*/
#ifndef __NO_EMBEDDED_ASM
__attribute__((section(".rev16_text"))) __STATIC_INLINE __ASM uint32_t __REV16(uint32_t value)
{
rev16 r0, r0
bx lr
}
#endif
/**
\brief Reverse byte order (16 bit)
\details Reverses the byte order in a 16-bit value and returns the signed 16-bit result. For example, 0x0080 becomes 0x8000.
\param [in] value Value to reverse
\return Reversed value
*/
#ifndef __NO_EMBEDDED_ASM
__attribute__((section(".revsh_text"))) __STATIC_INLINE __ASM int16_t __REVSH(int16_t value)
{
revsh r0, r0
bx lr
}
#endif
/**
\brief Rotate Right in unsigned value (32 bit)
\details Rotate Right (immediate) provides the value of the contents of a register rotated by a variable number of bits.
\param [in] op1 Value to rotate
\param [in] op2 Number of Bits to rotate
\return Rotated value
*/
#define __ROR __ror
/**
\brief Breakpoint
\details Causes the processor to enter Debug state.
Debug tools can use this to investigate system state when the instruction at a particular address is reached.
\param [in] value is ignored by the processor.
If required, a debugger can use it to store additional information about the breakpoint.
*/
#define __BKPT(value) __breakpoint(value)
/**
\brief Reverse bit order of value
\details Reverses the bit order of the given value.
\param [in] value Value to reverse
\return Reversed value
*/
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
#define __RBIT __rbit
#else
__attribute__((always_inline)) __STATIC_INLINE uint32_t __RBIT(uint32_t value)
{
uint32_t result;
uint32_t s = (4U /*sizeof(v)*/ * 8U) - 1U; /* extra shift needed at end */
result = value; /* r will be reversed bits of v; first get LSB of v */
for (value >>= 1U; value != 0U; value >>= 1U)
{
result <<= 1U;
result |= value & 1U;
s--;
}
result <<= s; /* shift when v's highest bits are zero */
return result;
}
#endif
/**
\brief Count leading zeros
\details Counts the number of leading zeros of a data value.
\param [in] value Value to count the leading zeros
\return number of leading zeros in value
*/
#define __CLZ __clz
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
/**
\brief LDR Exclusive (8 bit)
\details Executes a exclusive LDR instruction for 8 bit value.
\param [in] ptr Pointer to data
\return value of type uint8_t at (*ptr)
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __LDREXB(ptr) ((uint8_t ) __ldrex(ptr))
#else
#define __LDREXB(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint8_t ) __ldrex(ptr)) _Pragma("pop")
#endif
/**
\brief LDR Exclusive (16 bit)
\details Executes a exclusive LDR instruction for 16 bit values.
\param [in] ptr Pointer to data
\return value of type uint16_t at (*ptr)
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __LDREXH(ptr) ((uint16_t) __ldrex(ptr))
#else
#define __LDREXH(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint16_t) __ldrex(ptr)) _Pragma("pop")
#endif
/**
\brief LDR Exclusive (32 bit)
\details Executes a exclusive LDR instruction for 32 bit values.
\param [in] ptr Pointer to data
\return value of type uint32_t at (*ptr)
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __LDREXW(ptr) ((uint32_t ) __ldrex(ptr))
#else
#define __LDREXW(ptr) _Pragma("push") _Pragma("diag_suppress 3731") ((uint32_t ) __ldrex(ptr)) _Pragma("pop")
#endif
/**
\brief STR Exclusive (8 bit)
\details Executes a exclusive STR instruction for 8 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
\return 0 Function succeeded
\return 1 Function failed
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __STREXB(value, ptr) __strex(value, ptr)
#else
#define __STREXB(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop")
#endif
/**
\brief STR Exclusive (16 bit)
\details Executes a exclusive STR instruction for 16 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
\return 0 Function succeeded
\return 1 Function failed
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __STREXH(value, ptr) __strex(value, ptr)
#else
#define __STREXH(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop")
#endif
/**
\brief STR Exclusive (32 bit)
\details Executes a exclusive STR instruction for 32 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
\return 0 Function succeeded
\return 1 Function failed
*/
#if defined(__ARMCC_VERSION) && (__ARMCC_VERSION < 5060020)
#define __STREXW(value, ptr) __strex(value, ptr)
#else
#define __STREXW(value, ptr) _Pragma("push") _Pragma("diag_suppress 3731") __strex(value, ptr) _Pragma("pop")
#endif
/**
\brief Remove the exclusive lock
\details Removes the exclusive lock which is created by LDREX.
*/
#define __CLREX __clrex
/**
\brief Signed Saturate
\details Saturates a signed value.
\param [in] value Value to be saturated
\param [in] sat Bit position to saturate to (1..32)
\return Saturated value
*/
#define __SSAT __ssat
/**
\brief Unsigned Saturate
\details Saturates an unsigned value.
\param [in] value Value to be saturated
\param [in] sat Bit position to saturate to (0..31)
\return Saturated value
*/
#define __USAT __usat
/**
\brief Rotate Right with Extend (32 bit)
\details Moves each bit of a bitstring right by one bit.
The carry input is shifted in at the left end of the bitstring.
\param [in] value Value to rotate
\return Rotated value
*/
#ifndef __NO_EMBEDDED_ASM
__attribute__((section(".rrx_text"))) __STATIC_INLINE __ASM uint32_t __RRX(uint32_t value)
{
rrx r0, r0
bx lr
}
#endif
/**
\brief LDRT Unprivileged (8 bit)
\details Executes a Unprivileged LDRT instruction for 8 bit value.
\param [in] ptr Pointer to data
\return value of type uint8_t at (*ptr)
*/
#define __LDRBT(ptr) ((uint8_t ) __ldrt(ptr))
/**
\brief LDRT Unprivileged (16 bit)
\details Executes a Unprivileged LDRT instruction for 16 bit values.
\param [in] ptr Pointer to data
\return value of type uint16_t at (*ptr)
*/
#define __LDRHT(ptr) ((uint16_t) __ldrt(ptr))
/**
\brief LDRT Unprivileged (32 bit)
\details Executes a Unprivileged LDRT instruction for 32 bit values.
\param [in] ptr Pointer to data
\return value of type uint32_t at (*ptr)
*/
#define __LDRT(ptr) ((uint32_t ) __ldrt(ptr))
/**
\brief STRT Unprivileged (8 bit)
\details Executes a Unprivileged STRT instruction for 8 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
*/
#define __STRBT(value, ptr) __strt(value, ptr)
/**
\brief STRT Unprivileged (16 bit)
\details Executes a Unprivileged STRT instruction for 16 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
*/
#define __STRHT(value, ptr) __strt(value, ptr)
/**
\brief STRT Unprivileged (32 bit)
\details Executes a Unprivileged STRT instruction for 32 bit values.
\param [in] value Value to store
\param [in] ptr Pointer to location
*/
#define __STRT(value, ptr) __strt(value, ptr)
#else /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
/**
\brief Signed Saturate
\details Saturates a signed value.
\param [in] value Value to be saturated
\param [in] sat Bit position to saturate to (1..32)
\return Saturated value
*/
__attribute__((always_inline)) __STATIC_INLINE int32_t __SSAT(int32_t val, uint32_t sat)
{
if ((sat >= 1U) && (sat <= 32U))
{
const int32_t max = (int32_t)((1U << (sat - 1U)) - 1U);
const int32_t min = -1 - max ;
if (val > max)
{
return max;
}
else if (val < min)
{
return min;
}
}
return val;
}
/**
\brief Unsigned Saturate
\details Saturates an unsigned value.
\param [in] value Value to be saturated
\param [in] sat Bit position to saturate to (0..31)
\return Saturated value
*/
__attribute__((always_inline)) __STATIC_INLINE uint32_t __USAT(int32_t val, uint32_t sat)
{
if (sat <= 31U)
{
const uint32_t max = ((1U << sat) - 1U);
if (val > (int32_t)max)
{
return max;
}
else if (val < 0)
{
return 0U;
}
}
return (uint32_t)val;
}
#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
/*@}*/ /* end of group CMSIS_Core_InstructionInterface */
/* ########################### Core Function Access ########################### */
/** \ingroup CMSIS_Core_FunctionInterface
\defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions
@{
*/
/**
\brief Enable IRQ Interrupts
\details Enables IRQ interrupts by clearing special-purpose register PRIMASK.
Can only be executed in Privileged modes.
*/
/* intrinsic void __enable_irq(); */
/**
\brief Disable IRQ Interrupts
\details Disables IRQ interrupts by setting special-purpose register PRIMASK.
Can only be executed in Privileged modes.
*/
/* intrinsic void __disable_irq(); */
/**
\brief Get Control Register
\details Returns the content of the Control Register.
\return Control Register value
*/
__STATIC_INLINE uint32_t __get_CONTROL(void)
{
register uint32_t __regControl __ASM("control");
return(__regControl);
}
/**
\brief Set Control Register
\details Writes the given value to the Control Register.
\param [in] control Control Register value to set
*/
__STATIC_INLINE void __set_CONTROL(uint32_t control)
{
register uint32_t __regControl __ASM("control");
__regControl = control;
__ISB();
}
/**
\brief Get IPSR Register
\details Returns the content of the IPSR Register.
\return IPSR Register value
*/
__STATIC_INLINE uint32_t __get_IPSR(void)
{
register uint32_t __regIPSR __ASM("ipsr");
return(__regIPSR);
}
/**
\brief Get APSR Register
\details Returns the content of the APSR Register.
\return APSR Register value
*/
__STATIC_INLINE uint32_t __get_APSR(void)
{
register uint32_t __regAPSR __ASM("apsr");
return(__regAPSR);
}
/**
\brief Get xPSR Register
\details Returns the content of the xPSR Register.
\return xPSR Register value
*/
__STATIC_INLINE uint32_t __get_xPSR(void)
{
register uint32_t __regXPSR __ASM("xpsr");
return(__regXPSR);
}
/**
\brief Get Process Stack Pointer
\details Returns the current value of the Process Stack Pointer (PSP).
\return PSP Register value
*/
__STATIC_INLINE uint32_t __get_PSP(void)
{
register uint32_t __regProcessStackPointer __ASM("psp");
return(__regProcessStackPointer);
}
/**
\brief Set Process Stack Pointer
\details Assigns the given value to the Process Stack Pointer (PSP).
\param [in] topOfProcStack Process Stack Pointer value to set
*/
__STATIC_INLINE void __set_PSP(uint32_t topOfProcStack)
{
register uint32_t __regProcessStackPointer __ASM("psp");
__regProcessStackPointer = topOfProcStack;
}
/**
\brief Get Main Stack Pointer
\details Returns the current value of the Main Stack Pointer (MSP).
\return MSP Register value
*/
__STATIC_INLINE uint32_t __get_MSP(void)
{
register uint32_t __regMainStackPointer __ASM("msp");
return(__regMainStackPointer);
}
/**
\brief Set Main Stack Pointer
\details Assigns the given value to the Main Stack Pointer (MSP).
\param [in] topOfMainStack Main Stack Pointer value to set
*/
__STATIC_INLINE void __set_MSP(uint32_t topOfMainStack)
{
register uint32_t __regMainStackPointer __ASM("msp");
__regMainStackPointer = topOfMainStack;
}
/**
\brief Get Priority Mask
\details Returns the current state of the priority mask bit from the Priority Mask Register.
\return Priority Mask value
*/
__STATIC_INLINE uint32_t __get_PRIMASK(void)
{
register uint32_t __regPriMask __ASM("primask");
return(__regPriMask);
}
/**
\brief Set Priority Mask
\details Assigns the given value to the Priority Mask Register.
\param [in] priMask Priority Mask
*/
__STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
{
register uint32_t __regPriMask __ASM("primask");
__regPriMask = (priMask);
}
#if ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
/**
\brief Enable FIQ
\details Enables FIQ interrupts by clearing special-purpose register FAULTMASK.
Can only be executed in Privileged modes.
*/
#define __enable_fault_irq __enable_fiq
/**
\brief Disable FIQ
\details Disables FIQ interrupts by setting special-purpose register FAULTMASK.
Can only be executed in Privileged modes.
*/
#define __disable_fault_irq __disable_fiq
/**
\brief Get Base Priority
\details Returns the current value of the Base Priority register.
\return Base Priority register value
*/
__STATIC_INLINE uint32_t __get_BASEPRI(void)
{
register uint32_t __regBasePri __ASM("basepri");
return(__regBasePri);
}
/**
\brief Set Base Priority
\details Assigns the given value to the Base Priority register.
\param [in] basePri Base Priority value to set
*/
__STATIC_INLINE void __set_BASEPRI(uint32_t basePri)
{
register uint32_t __regBasePri __ASM("basepri");
__regBasePri = (basePri & 0xFFU);
}
/**
\brief Set Base Priority with condition
\details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,
or the new value increases the BASEPRI priority level.
\param [in] basePri Base Priority value to set
*/
__STATIC_INLINE void __set_BASEPRI_MAX(uint32_t basePri)
{
register uint32_t __regBasePriMax __ASM("basepri_max");
__regBasePriMax = (basePri & 0xFFU);
}
/**
\brief Get Fault Mask
\details Returns the current value of the Fault Mask register.
\return Fault Mask register value
*/
__STATIC_INLINE uint32_t __get_FAULTMASK(void)
{
register uint32_t __regFaultMask __ASM("faultmask");
return(__regFaultMask);
}
/**
\brief Set Fault Mask
\details Assigns the given value to the Fault Mask register.
\param [in] faultMask Fault Mask value to set
*/
__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
{
register uint32_t __regFaultMask __ASM("faultmask");
__regFaultMask = (faultMask & (uint32_t)1U);
}
#endif /* ((defined (__ARM_ARCH_7M__ ) && (__ARM_ARCH_7M__ == 1)) || \
(defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
/**
\brief Get FPSCR
\details Returns the current value of the Floating Point Status/Control register.
\return Floating Point Status/Control register value
*/
__STATIC_INLINE uint32_t __get_FPSCR(void)
{
#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
(defined (__FPU_USED ) && (__FPU_USED == 1U)) )
register uint32_t __regfpscr __ASM("fpscr");
return(__regfpscr);
#else
return(0U);
#endif
}
/**
\brief Set FPSCR
\details Assigns the given value to the Floating Point Status/Control register.
\param [in] fpscr Floating Point Status/Control value to set
*/
__STATIC_INLINE void __set_FPSCR(uint32_t fpscr)
{
#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
(defined (__FPU_USED ) && (__FPU_USED == 1U)) )
register uint32_t __regfpscr __ASM("fpscr");
__regfpscr = (fpscr);
#else
(void)fpscr;
#endif
}
/*@} end of CMSIS_Core_RegAccFunctions */
/* ################### Compiler specific Intrinsics ########################### */
/** \defgroup CMSIS_SIMD_intrinsics CMSIS SIMD Intrinsics
Access to dedicated SIMD instructions
@{
*/
#if ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) )
#define __SADD8 __sadd8
#define __QADD8 __qadd8
#define __SHADD8 __shadd8
#define __UADD8 __uadd8
#define __UQADD8 __uqadd8
#define __UHADD8 __uhadd8
#define __SSUB8 __ssub8
#define __QSUB8 __qsub8
#define __SHSUB8 __shsub8
#define __USUB8 __usub8
#define __UQSUB8 __uqsub8
#define __UHSUB8 __uhsub8
#define __SADD16 __sadd16
#define __QADD16 __qadd16
#define __SHADD16 __shadd16
#define __UADD16 __uadd16
#define __UQADD16 __uqadd16
#define __UHADD16 __uhadd16
#define __SSUB16 __ssub16
#define __QSUB16 __qsub16
#define __SHSUB16 __shsub16
#define __USUB16 __usub16
#define __UQSUB16 __uqsub16
#define __UHSUB16 __uhsub16
#define __SASX __sasx
#define __QASX __qasx
#define __SHASX __shasx
#define __UASX __uasx
#define __UQASX __uqasx
#define __UHASX __uhasx
#define __SSAX __ssax
#define __QSAX __qsax
#define __SHSAX __shsax
#define __USAX __usax
#define __UQSAX __uqsax
#define __UHSAX __uhsax
#define __USAD8 __usad8
#define __USADA8 __usada8
#define __SSAT16 __ssat16
#define __USAT16 __usat16
#define __UXTB16 __uxtb16
#define __UXTAB16 __uxtab16
#define __SXTB16 __sxtb16
#define __SXTAB16 __sxtab16
#define __SMUAD __smuad
#define __SMUADX __smuadx
#define __SMLAD __smlad
#define __SMLADX __smladx
#define __SMLALD __smlald
#define __SMLALDX __smlaldx
#define __SMUSD __smusd
#define __SMUSDX __smusdx
#define __SMLSD __smlsd
#define __SMLSDX __smlsdx
#define __SMLSLD __smlsld
#define __SMLSLDX __smlsldx
#define __SEL __sel
#define __QADD __qadd
#define __QSUB __qsub
#define __PKHBT(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0x0000FFFFUL) | \
((((uint32_t)(ARG2)) << (ARG3)) & 0xFFFF0000UL) )
#define __PKHTB(ARG1,ARG2,ARG3) ( ((((uint32_t)(ARG1)) ) & 0xFFFF0000UL) | \
((((uint32_t)(ARG2)) >> (ARG3)) & 0x0000FFFFUL) )
#define __SMMLA(ARG1,ARG2,ARG3) ( (int32_t)((((int64_t)(ARG1) * (ARG2)) + \
((int64_t)(ARG3) << 32U) ) >> 32U))
#define __SXTB16_RORn(ARG1, ARG2) __SXTB16(__ROR(ARG1, ARG2))
#define __SXTAB16_RORn(ARG1, ARG2, ARG3) __SXTAB16(ARG1, __ROR(ARG2, ARG3))
#endif /* ((defined (__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) ) */
/*@} end of group CMSIS_SIMD_intrinsics */
#endif /* __CMSIS_ARMCC_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,298 @@
/**************************************************************************//**
* @file cmsis_compiler.h
* @brief CMSIS compiler generic header file
* @version V5.1.0
* @date 09. October 2018
******************************************************************************/
/*
* Copyright (c) 2009-2018 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CMSIS_COMPILER_H
#define __CMSIS_COMPILER_H
#include <stdint.h>
/*
* Arm Compiler 4/5
*/
#if defined ( __CC_ARM )
#include "cmsis_armcc.h"
/*
* Arm Compiler 6.6 LTM (armclang)
*/
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100)
#include "cmsis_armclang_ltm.h"
/*
* Arm Compiler above 6.10.1 (armclang)
*/
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)
#include "cmsis_armclang.h"
/*
* GNU Compiler
*/
#elif defined ( __GNUC__ )
#include "cmsis_gcc.h"
/*
* IAR Compiler
*/
#elif defined ( __ICCARM__ )
#include <cmsis_iccarm.h>
/*
* GHS Compiler
*/
#elif defined ( __ghs__ )
#include <cmsis_ghs.h>
/*
* Wind River DIAB Compiler
*/
#elif defined ( __DCC__ )
#include <cmsis_diab.h>
/*
* TI Arm Compiler
*/
#elif defined ( __TI_ARM__ )
#include <cmsis_ccs.h>
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __PACKED
#define __PACKED __attribute__((packed))
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __attribute__((packed))
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION union __attribute__((packed))
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
struct __attribute__((packed)) T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __attribute__((aligned(x)))
#endif
#ifndef __RESTRICT
#define __RESTRICT __restrict
#endif
#ifndef __COMPILER_BARRIER
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
#define __COMPILER_BARRIER() (void)0
#endif
/*
* TASKING Compiler
*/
#elif defined ( __TASKING__ )
/*
* The CMSIS functions have been implemented as intrinsics in the compiler.
* Please use "carm -?i" to get an up to date list of all intrinsics,
* Including the CMSIS ones.
*/
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __PACKED
#define __PACKED __packed__
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __packed__
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION union __packed__
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
struct __packed__ T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __align(x)
#endif
#ifndef __RESTRICT
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
#define __RESTRICT
#endif
#ifndef __COMPILER_BARRIER
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
#define __COMPILER_BARRIER() (void)0
#endif
/*
* COSMIC Compiler
*/
#elif defined ( __CSMC__ )
#include <cmsis_csm.h>
#ifndef __ASM
#define __ASM _asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
// NO RETURN is automatically detected hence no warning here
#define __NO_RETURN
#endif
#ifndef __USED
#warning No compiler specific solution for __USED. __USED is ignored.
#define __USED
#endif
#ifndef __WEAK
#define __WEAK __weak
#endif
#ifndef __PACKED
#define __PACKED @packed
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT @packed struct
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION @packed union
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
@packed struct T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored.
#define __ALIGNED(x)
#endif
#ifndef __RESTRICT
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
#define __RESTRICT
#endif
#ifndef __COMPILER_BARRIER
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
#define __COMPILER_BARRIER() (void)0
#endif
#else
#error Unknown compiler.
#endif
#endif /* __CMSIS_COMPILER_H */
@@ -0,0 +1,143 @@
/***************************************************************************//**
* \file cmsis_diab.h
*
* \brief
* Wind River DIAB specific CMSIS definitions
*
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#ifndef __CMSIS_DIAB_H
#define __CMSIS_DIAB_H
#include <diab/asm.h> // intrinsic functions
/* CMSIS compiler specific defines */
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __RESTRICT
#define __RESTRICT restrict
#endif
#ifndef __INLINE
#define __INLINE __inline__
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static __inline__
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __attribute__((always_inline)) static __inline__
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __UNALIGNED_UINT32
struct __attribute__((packed)) T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __attribute__((aligned(x)))
#endif
#ifndef __PACKED
#define __PACKED __attribute__((packed, aligned(1)))
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __attribute__((packed, aligned(1)))
#endif
#ifndef __DMB
#define __DMB() __ASM("dmb")
#endif
#ifndef __DSB
#define __DSB() __ASM("dsb")
#endif
#ifndef __ISB
#define __ISB() __ASM("isb")
#endif
#ifndef __NOP
#define __NOP() __ASM("nop")
#endif
#ifndef __WFI
#define __WFI() __ASM("wfi")
#endif
#ifndef __WFE
#define __WFE() __ASM("wfe")
#endif
#ifndef __enable_irq
#define __enable_irq() __ASM("cpsie i")
#endif
#ifndef __disable_irq
#define __disable_irq() __ASM("cpsid i")
#endif
#ifndef __COMPILER_BARRIER
#define __COMPILER_BARRIER() __asm volatile ("nop")//__memory_barrier()
#endif
#ifndef __get_FPSCR
#ifdef __VFP__
__asm volatile unsigned long __get_FPSCR(void)
{
! "r0"
MRS r0, fpscr
}
#else
#define __get_FPSCR() (0U)
#endif
#endif
#ifndef __get_PRIMASK
__asm volatile unsigned long __get_PRIMASK(void)
{
! "r0"
MRS r0, primask
}
#endif
#ifndef __set_PRIMASK
__asm volatile void __set_PRIMASK(unsigned long priMaskValue)
{
% reg priMaskValue
!
MSR primask, priMaskValue
}
#endif
#ifndef __get_BASEPRI
__asm volatile unsigned long __get_BASEPRI(void)
{
! "r0"
MRS r0, basepri
}
#endif
#ifndef __set_BASEPRI
__asm volatile void __set_BASEPRI(unsigned long basepriValue)
{
% reg basepriValue
!
MSR basepri, basepriValue
}
#endif
#ifndef __get_IPSR
__asm volatile unsigned long __get_IPSR(void)
{
! "r0"
MRS r0, ipsr
}
#endif
#endif /* __CMSIS_DIAB_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,109 @@
/***************************************************************************//**
* \file cmsis_ghs.h
*
* \brief
* GHS specific CMSIS definitions
*
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#ifndef __CMSIS_GHS_H
#define __CMSIS_GHS_H
#include <arm_ghs.h> // GHS MULTI intrinsic functions (e.g. __enable_interrupt())
#include <ghs_barrier.h>
/* CMSIS compiler specific defines */
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __RESTRICT
#define __RESTRICT restrict
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE static inline
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __UNALIGNED_UINT32
struct __attribute__((packed)) T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __attribute__((aligned(x)))
#endif
#ifndef __PACKED
#define __PACKED __attribute__((packed, aligned(1)))
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __attribute__((packed, aligned(1)))
#endif
#ifndef __DMB
#define __DMB() __ASM("dmb")
#endif
#ifndef __DSB
#define __DSB() __ASM("dsb")
#endif
#ifndef __ISB
#define __ISB() __ASM("isb")
#endif
#ifndef __WFI
#define __WFI() __ASM("wfi")
#endif
#ifndef __WFE
#define __WFE() __ASM("wfe")
#endif
#ifndef __enable_irq
#define __enable_irq() __ASM("cpsie i")
#endif
#ifndef __disable_irq
#define __disable_irq() __ASM("cpsid i")
#endif
#ifndef __COMPILER_BARRIER
#define __COMPILER_BARRIER() __memory_changed()
#endif
#ifndef __get_FPSCR
/**
\brief Get FPSCR
\details Returns the current value of the Floating Point Status/Control register.
\return Floating Point Status/Control register value
*/
__attribute__((always_inline)) __STATIC_INLINE uint32_t __get_FPSCR(void)
{
#if ((defined (__FPU_PRESENT) && (__FPU_PRESENT == 1U)) && \
(defined (__FPU_USED ) && (__FPU_USED == 1U)) )
return __VMRS(__VFP_FPSCR);;
#else
return(0U);
#endif
}
#endif
#ifndef __get_PRIMASK
#define __get_PRIMASK() (__MRS(__PRIMASK))
#endif
#ifndef __set_PRIMASK
#define __set_PRIMASK(VALUE) (__MSR(__PRIMASK, (VALUE)))
#endif
#endif /* __CMSIS_GHS_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,39 @@
/**************************************************************************//**
* @file cmsis_version.h
* @brief CMSIS Core(M) Version definitions
* @version V5.0.5
* @date 02. February 2022
******************************************************************************/
/*
* Copyright (c) 2009-2022 ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef __CMSIS_VERSION_H
#define __CMSIS_VERSION_H
/* CMSIS Version definitions */
#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */
#define __CM_CMSIS_VERSION_SUB ( 6U) /*!< [15:0] CMSIS Core(M) sub version */
#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \
__CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,275 @@
/******************************************************************************
* @file mpu_armv7.h
* @brief CMSIS MPU API for Armv7-M MPU
* @version V5.1.2
* @date 25. May 2020
******************************************************************************/
/*
* Copyright (c) 2017-2020 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef ARM_MPU_ARMV7_H
#define ARM_MPU_ARMV7_H
#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes
#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes
#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes
#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes
#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes
#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte
#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes
#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes
#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes
#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes
#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes
#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes
#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes
#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes
#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes
#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte
#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes
#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes
#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes
#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes
#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes
#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes
#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes
#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes
#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes
#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte
#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes
#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes
#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access
#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only
#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only
#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access
#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only
#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access
/** MPU Region Base Address Register Value
*
* \param Region The region to be configured, number 0 to 15.
* \param BaseAddress The base address for the region.
*/
#define ARM_MPU_RBAR(Region, BaseAddress) \
(((BaseAddress) & MPU_RBAR_ADDR_Msk) | \
((Region) & MPU_RBAR_REGION_Msk) | \
(MPU_RBAR_VALID_Msk))
/**
* MPU Memory Access Attributes
*
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
* \param IsShareable Region is shareable between multiple bus masters.
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
*/
#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \
((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \
(((IsShareable) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \
(((IsCacheable) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \
(((IsBufferable) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk))
/**
* MPU Region Attribute and Size Register Value
*
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_.
* \param SubRegionDisable Sub-region disable field.
* \param Size Region size of the region to be configured, for example 4K, 8K.
*/
#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \
((((DisableExec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \
(((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \
(((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \
(((SubRegionDisable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) | \
(((Size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) | \
(((MPU_RASR_ENABLE_Msk))))
/**
* MPU Region Attribute and Size Register Value
*
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
* \param IsShareable Region is shareable between multiple bus masters.
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
* \param SubRegionDisable Sub-region disable field.
* \param Size Region size of the region to be configured, for example 4K, 8K.
*/
#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \
ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size)
/**
* MPU Memory Access Attribute for strongly ordered memory.
* - TEX: 000b
* - Shareable
* - Non-cacheable
* - Non-bufferable
*/
#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U)
/**
* MPU Memory Access Attribute for device memory.
* - TEX: 000b (if shareable) or 010b (if non-shareable)
* - Shareable or non-shareable
* - Non-cacheable
* - Bufferable (if shareable) or non-bufferable (if non-shareable)
*
* \param IsShareable Configures the device memory as shareable or non-shareable.
*/
#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U))
/**
* MPU Memory Access Attribute for normal memory.
* - TEX: 1BBb (reflecting outer cacheability rules)
* - Shareable or non-shareable
* - Cacheable or non-cacheable (reflecting inner cacheability rules)
* - Bufferable or non-bufferable (reflecting inner cacheability rules)
*
* \param OuterCp Configures the outer cache policy.
* \param InnerCp Configures the inner cache policy.
* \param IsShareable Configures the memory as shareable or non-shareable.
*/
#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) >> 1U), ((InnerCp) & 1U))
/**
* MPU Memory Access Attribute non-cacheable policy.
*/
#define ARM_MPU_CACHEP_NOCACHE 0U
/**
* MPU Memory Access Attribute write-back, write and read allocate policy.
*/
#define ARM_MPU_CACHEP_WB_WRA 1U
/**
* MPU Memory Access Attribute write-through, no write allocate policy.
*/
#define ARM_MPU_CACHEP_WT_NWA 2U
/**
* MPU Memory Access Attribute write-back, no write allocate policy.
*/
#define ARM_MPU_CACHEP_WB_NWA 3U
/**
* Struct for a single MPU Region
*/
typedef struct {
uint32_t RBAR; //!< The region base address register value (RBAR)
uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR
} ARM_MPU_Region_t;
/** Enable the MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control)
{
__DMB();
MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
#endif
__DSB();
__ISB();
}
/** Disable the MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable(void)
{
__DMB();
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
#endif
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
__DSB();
__ISB();
}
/** Clear and disable the given MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr)
{
MPU->RNR = rnr;
MPU->RASR = 0U;
}
/** Configure an MPU region.
* \param rbar Value for RBAR register.
* \param rasr Value for RASR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr)
{
MPU->RBAR = rbar;
MPU->RASR = rasr;
}
/** Configure the given MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rasr Value for RASR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr)
{
MPU->RNR = rnr;
MPU->RBAR = rbar;
MPU->RASR = rasr;
}
/** Memcpy with strictly ordered memory access, e.g. used by code in ARM_MPU_Load().
* \param dst Destination data is copied to.
* \param src Source data is copied from.
* \param len Amount of data words to be copied.
*/
__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len)
{
uint32_t i;
for (i = 0U; i < len; ++i)
{
dst[i] = src[i];
}
}
/** Load the given number of MPU regions from a table.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt)
{
const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U;
while (cnt > MPU_TYPE_RALIASES) {
ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize);
table += MPU_TYPE_RALIASES;
cnt -= MPU_TYPE_RALIASES;
}
ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize);
}
#endif
+240
View File
@@ -0,0 +1,240 @@
/***************************************************************************//**
* \file cy_project.h
*
* \brief
* Project specific header
*
* \note
* It contains references to all generated header files and should
* not be modified.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#ifndef CY_PROJECT_H
#define CY_PROJECT_H
#ifdef __cplusplus
extern "C" {
#endif
#include "syslib/cy_syslib.h"
#include "cpu/cy_cpu.h"
#include "gpio/cy_gpio.h"
#include "ipc/cy_ipc_drv.h"
#include "ipc/cy_ipc_pipe.h"
#include "ipc/cy_ipc_sema.h"
#include "ipc/cy_ipc_config.h"
#include "mcwdt/cy_mcwdt.h"
#include "prot/cy_prot.h"
#include "scb/cy_scb_common.h"
#include "scb/cy_scb_ezi2c.h"
#include "scb/cy_scb_i2c.h"
#include "scb/cy_scb_spi.h"
#include "scb/cy_scb_uart.h"
#include "sysint/cy_sysint.h"
#include "sysclk/cy_sysclk.h"
#include "syswdt/cy_syswdt.h"
#include "sysrtc/cy_sysrtc.h"
#include "tcpwm/cy_tcpwm.h"
#include "tcpwm/cy_tcpwm_counter.h"
#include "tcpwm/cy_tcpwm_pwm.h"
#include "tcpwm/cy_tcpwm_quaddec.h"
#include "tcpwm/cy_tcpwm_sr.h"
#include "syspm/cy_syspm.h"
#include "syslib/cy_syslib.h"
#include "trigmux/cy_trigmux.h"
#include "systick/cy_systick.h"
#include "mpu/cy_mpu.h"
#include "dma/cy_pdma.h"
#include "dma/cy_mdma.h"
#include "adc/cy_adc.h"
#include "crypto/cy_crypto.h"
#include "crypto/cy_crypto_server.h"
#include "lin/cy_lin.h"
#include "canfd/cy_canfd.h"
#include "evtgen/cy_evtgen.h"
#include "flash/cy_flash.h"
#include "sysflt/cy_sysflt.h"
#include "lvd/cy_lvd.h"
#include "smartio/cy_smartio.h"
#include "sysreset/cy_sysreset.h"
#include "srom/cy_srom.h"
#if defined (tviibe2m) || defined (tviibe4m) || defined (tviic2d6m) || defined (tviic2d4m) || defined (tviic2d6mddr) || defined (tviice4m)
#include "cxpi/cy_cxpi.h"
#endif
#if defined (tviibh8m) || defined (tviibh16m)
#include "flexray/cy_fr.h"
#include "sd_host/cy_sd_host.h"
#include "audioss/cy_i2s.h"
#include "smif/ver2/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "ethernet/cy_ethif.h"
#include "sysreghc/cy_sysreghc.h"
#endif
#if defined (tviibh4m)
#include "sd_host/cy_sd_host.h"
#include "audioss/cy_i2s.h"
#include "smif/ver2/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "ethernet/cy_ethif.h"
#include "sysreghc/cy_sysreghc.h"
#endif
#if defined (tviic2d6m)
#include "audioss/mixer/cy_mixer.h"
#include "audioss/pwm/cy_pwm.h"
#include "audioss/dac/cy_dac.h"
#include "audioss/tdm/cy_tdm.h"
#include "audioss/sg/cy_sg.h"
#include "audioss/i2s/cy_i2s.h"
#include "axidma/cy_axidma.h"
#include "smif/ver4/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "fpdlink/cy_fpdlink.h"
#include "mipicsi2/cy_mipicsi2.h"
#include "syspmic/cy_syspmic.h"
#include "ethernet/cy_ethif.h"
#endif
#if defined (tviic2d4m)
#include "audioss/mixer/cy_mixer.h"
#include "audioss/i2s/cy_i2s.h"
#include "audioss/pwm/cy_pwm.h"
#include "audioss/dac/cy_dac.h"
#include "audioss/tdm/cy_tdm.h"
#include "audioss/sg/cy_sg.h"
#include "axidma/cy_axidma.h"
#include "smif/ver3_1/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "fpdlink/cy_fpdlink.h"
#include "mipicsi2/cy_mipicsi2.h"
#include "syspmic/cy_syspmic.h"
#include "ethernet/cy_ethif.h"
#include "audio/cy_cs42448.h"
#endif
#if defined (tviic2d6mddr)
#include "audioss/mixer/cy_mixer.h"
#include "audioss/i2s/cy_i2s.h"
#include "audioss/pwm/cy_pwm.h"
#include "audioss/dac/cy_dac.h"
#include "audioss/tdm/cy_tdm.h"
#include "audioss/sg/cy_sg.h"
#include "axidma/cy_axidma.h"
#include "smif/ver4/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "fpdlink/cy_fpdlink.h"
#include "mipicsi2/cy_mipicsi2.h"
#include "syspmic/cy_syspmic.h"
#include "ethernet/cy_ethif.h"
#include "sd_host/cy_sd_host.h"
#endif
#if defined (tviice4m)
#include "audioss/mixer/cy_mixer.h"
#include "audioss/i2s/cy_i2s.h"
#include "audioss/pwm/cy_pwm.h"
#include "audioss/tdm/cy_tdm.h"
#include "audioss/sg/cy_sg.h"
#include "audio/cy_cs42448.h"
#include "smif/ver3_1/cy_smif_ver_specific.h"
#include "smif/common/cy_smif_memslot.h"
#include "seglcd/cy_seglcd.h"
#endif
//=============================================
#if defined (tviibe512k)
#include "bb_bsp_tviibe512k.h"
#elif defined (tviibe1m)
#include "bb_bsp_tviibe1m.h"
#elif defined (tviibe2m)
#include "bb_bsp_tviibe2m.h"
#elif defined (tviibe4m)
#include "bb_bsp_tviibe4m.h"
#elif defined (tviibh4m)
#include "bb_bsp_tviibh4m.h"
#elif defined (tviibh8m)
#include "bb_bsp_tviibh8m.h"
#elif defined (tviibh16m)
#include "bb_bsp_tviibh16m.h"
#elif defined (tviic2d4m)
#include "bb_bsp_tviic2d4m.h"
#elif defined (tviic2d6m)
#include "bb_bsp_tviic2d6m.h"
#elif defined (tviic2d6mddr)
#include "bb_bsp_tviic2d6mddr.h"
#elif defined (tviice4m)
#include "bb_bsp_tviice4m.h"
#endif
//============ Middleware =====================
#include "button/cy_button.h"
#include "semihosting/cy_semihosting.h"
#include "sw_timer/cy_sw_tmr.h"
#include "flash/cy_mw_flash.h"
#if defined (tviibh4m) || defined (tviibh8m) || defined (tviibh16m)
#include "mw/eth_phy/cy_dp83867.h"
#include "mw/power/cy_power.h"
#include "mw/smif_mem/cy_smif_device_common.h"
#include "mw/smif_mem/cy_smif_hb_flash.h"
#include "mw/smif_mem/cy_smif_s25fl.h"
#include "mw/smif_mem/cy_smif_semp.h"
#include "mw/smif_mem/cy_smif_s25fs.h"
#include "aic261/cy_aic261.h"
#endif
#if defined (tviic2d4m) || defined (tviic2d6m) || defined (tviic2d6mddr)
#include "mw/smif_mem/cy_smif_device_common.h"
#include "mw/smif_mem/cy_smif_hb_flash.h"
#include "mw/smif_mem/cy_smif_s25fl.h"
#include "mw/smif_mem/cy_smif_semp.h"
#include "mw/smif_mem/cy_smif_s25fs.h"
#include "mw/power/cy_power.h"
#include "mw/gfx_env/cy_gfx_env.h"
#include "mw/mipi_sensor/cy_mipi_sensor.h"
#include "mw/audio/cy_cs42448.h"
#endif
#if defined (tviic2d4m)
#include "mw/eth_phy/cy_dp83848q_q1.h"
#endif
#if defined (tviic2d6m) || defined (tviic2d6mddr)
#include "mw/eth_phy/cy_dp83867.h"
#endif
#if defined (tviice4m)
#include "mw/smif_mem/cy_smif_device_common.h"
#include "mw/smif_mem/cy_smif_hb_flash.h"
#include "mw/smif_mem/cy_smif_s25fl.h"
#include "mw/smif_mem/cy_smif_semp.h"
#include "mw/smif_mem/cy_smif_s25fs.h"
#include "aic261/cy_aic261.h"
#endif
#if defined (tviic2d4m) || defined (tviice4m) || (defined (tviic2d6m) && defined (CY_MCU_rev_b)) || (defined (tviic2d6m) && defined (CY_MCU_rev_c))
#include "mw/audio/cy_cs42448.h"
#endif
#ifdef __cplusplus
}
#endif
#endif /* CY_PROJECT_H */
/* [] END OF FILE */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,524 @@
/***************************************************************************//**
* \file cy_adc.h
* \version 1.0
*
* \brief
* Provides an API declaration of the Adc driver
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
/**
* \defgroup group_adc Analog to Digital Converter (ADC)
* \{
* The ADC driver provides an API to configure the ADC.
* The ADC has upto 4 ADC and upto 32 channel for each ADC module. About actual
* supported # or modules and channels, please refer to the technical reference
* manual (TRM) and device datasheet.
*
* \section group_adc_configuration Configuration Considerations
* Typical usage as is follows:
* - Call Cy_Adc_Init to initialize ADC module
* - Call Cy_Adc_Channel_Init to initialize a channel
* - Call Cy_Adc_Channel_SetInterruptMask if you need to use interrupt.
* After initialize channel(s) call Cy_Adc_Channel_Enable function then
* trigger by software (calling Cy_Adc_Channel_SoftwareTrigger) or start
* a peripheral that is configured for HW trigger.
* Note that if you use interrupt for ADC, you have to make interrupt handler
* and register it by using sysint module. In the handler, you can check
* which interrupt is occurred by Cy_Adc_Channel_GetInterruptMaskedStatus
* and can get conversion result by Cy_Adc_Channel_GetResult.
* If you don't use interrupt, you can poll conversion status by
* Cy_Adc_Channel_GetInterruptStatus.
* For both of polling and interrupt, clear interrupt flags by
* Cy_Adc_Channel_ClearInterruptStatus after handling data.
*
* \section group_adc_more_information More Information
*
* Refer to the technical reference manual (TRM) and the device datasheet.
*
* \section group_adc_changelog Changelog
* <table class="doxtable">
* <tr><th>Version</th><th>Changes</th><th>Reason for Change</th></tr>
* <tr>
* <td>1.0</td>
* <td>Initial version</td>
* <td></td>
* </tr>
* </table>
*
* \defgroup group_adc_macro Macro
* \defgroup group_adc_interrupt_sources Interrupt Sources
* \defgroup group_adc_ch_status Channel Status
* \defgroup group_adc_grp_status Group Status
* \defgroup group_adc_functions Functions
* \defgroup group_adc_data_structures Data structures
* \defgroup group_adc_enums Enumerated Types
* \defgroup group_adc_globals Global variables
*/
#if !defined(CY_ADC_H)
#define CY_ADC_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "syslib/cy_syslib.h"
#include "syspm/cy_syspm.h"
#include "cy_device_headers.h"
#if defined(__cplusplus)
extern "C" {
#endif
/***************************************
* Macro definitions
***************************************/
/**
* \addtogroup group_adc_macro
* \{
*/
/** Driver major version */
#define CY_ADC_DRV_VERSION_MAJOR 1
/** Driver minor version */
#define CY_ADC_DRV_VERSION_MINOR 0
/** Adc driver ID */
#define CY_ADC_ID CY_PDL_DRV_ID(0x36u)
/** \} group_adc_macro */
/***************************************
* Enumeration
***************************************/
/**
* \addtogroup group_adc_enums
* \{
*/
/**
* Adc Driver error codes
*/
typedef enum {
CY_ADC_SUCCESS = 0x00u, /**< Returned successful */
CY_ADC_BAD_PARAM = CY_ADC_ID | CY_PDL_STATUS_ERROR | 0x01u, /**< Bad parameter was passed */
} cy_en_adc_status_t;
/** When set use 2 cycles for the Most Significant Bit (MSB) */
typedef enum {
CY_ADC_MSB_STRETCH_MODE_1CYCLE = 0u, /**< Use 1 clock cycles per conversion. */
CY_ADC_MSB_STRETCH_MODE_2CYCLE = 1u /**< Use 2 clock cycles per conversion. */
} cy_en_adc_msb_stretch_mode_t;
/** Select Diagnostic Reference function. */
typedef enum {
CY_ADC_DIAG_REFERENCE_SELECT_VREFL = 0u, /**< DiagOut = VrefL */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_1DIV8 = 1u, /**< DiagOut = VrefH * 1/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_2DIV8 = 2u, /**< DiagOut = VrefH * 2/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_3DIV8 = 3u, /**< DiagOut = VrefH * 3/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_4DIV8 = 4u, /**< DiagOut = VrefH * 4/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_5DIV8 = 5u, /**< DiagOut = VrefH * 5/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_6DIV8 = 6u, /**< DiagOut = VrefH * 6/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH_7DIV8 = 7u, /**< DiagOut = VrefH * 7/8 */
CY_ADC_DIAG_REFERENCE_SELECT_VREFH = 8u, /**< DiagOut = VrefH */
CY_ADC_DIAG_REFERENCE_SELECT_VREFX = 9u, /**< DiagOut = VrefX = VrefH * 199/200 */
CY_ADC_DIAG_REFERENCE_SELECT_VBG = 10u, /**< DiagOut = Vbg from SRSS */
CY_ADC_DIAG_REFERENCE_SELECT_VIN1 = 11u, /**< DiagOut = Vin1 */
CY_ADC_DIAG_REFERENCE_SELECT_VIN2 = 12u, /**< DiagOut = Vin2 */
CY_ADC_DIAG_REFERENCE_SELECT_VIN3 = 13u, /**< DiagOut = Vin3 */
CY_ADC_DIAG_REFERENCE_SELECT_I_SOURCE = 14u, /**< DiagOut = Isource (10uA) */
CY_ADC_DIAG_REFERENCE_SELECT_I_SINK = 15u /**< DiagOut = Isink (10uA) */
} cy_en_adc_diag_reference_select_t;
/** The ADC trigger mode selection. */
typedef enum {
CY_ADC_TRIGGER_OFF = 0u, /**< Use for channels in group, except the first channel */
CY_ADC_TRIGGER_TCPWM = 1u, /**< Trigger from corresponding TCPWM channel */
CY_ADC_TRIGGER_GENERIC0 = 2u, /**< Generic trigger input 0 */
CY_ADC_TRIGGER_GENERIC1 = 3u, /**< Generic trigger input 1 */
CY_ADC_TRIGGER_GENERIC2 = 4u, /**< Generic trigger input 2 */
CY_ADC_TRIGGER_GENERIC3 = 5u, /**< Generic trigger input 3 */
CY_ADC_TRIGGER_GENERIC4 = 6u, /**< Generic trigger input 4 */
CY_ADC_TRIGGER_CONTINUOUS = 7u /**< Always triggered (also called idle), can only be used for at most 1 channel */
} cy_en_adc_trigger_selection_t;
/** The ADC preemption type selection. */
typedef enum {
CY_ADC_PREEMPTION_ABORT_CANCEL = 0u, /**< Abort ongoing acquisition, do not return. */
CY_ADC_PREEMPTION_ABORT_RESTART = 1u, /**< Abort ongoing acquisition, up on return Restart group from first channel. */
CY_ADC_PREEMPTION_ABORT_RESUME = 2u, /**< Abort ongoing acquisition, up on return Resume group from aborted channel. */
CY_ADC_PREEMPTION_FINISH_RESUME = 3u /**< Complete ongoing acquisition (including averaging), up on return Resume group from next channel. */
} cy_en_adc_preemption_type_t;
/** Conversion done trigger output selection. */
typedef enum {
CY_ADC_DONE_LEVEL_PULSE = 0u, /**< Conversion done signal for other IPs is a 2 cycle pulse (clk_sys), no need to read the result regisgter.
Typically used for triggering another IPs other than DW. */
CY_ADC_DONE_LEVEL_LEVEL = 1u /**< Conversion done signal for other IPs is level output until result register is read.
Typically used for DW and also overflow detection. */
} cy_en_adc_done_level_t;
/** Address of the analog signal (pin) to be sampled by corresponding channel. */
typedef enum {
CY_ADC_PIN_ADDRESS_AN0 = 0u, /**< Vout = AN0, select corresponding analog input. */
CY_ADC_PIN_ADDRESS_AN1 = 1u,
CY_ADC_PIN_ADDRESS_AN2 = 2u,
CY_ADC_PIN_ADDRESS_AN3 = 3u,
CY_ADC_PIN_ADDRESS_AN4 = 4u,
CY_ADC_PIN_ADDRESS_AN5 = 5u,
CY_ADC_PIN_ADDRESS_AN6 = 6u,
CY_ADC_PIN_ADDRESS_AN7 = 7u,
CY_ADC_PIN_ADDRESS_AN8 = 8u,
CY_ADC_PIN_ADDRESS_AN9 = 9u,
CY_ADC_PIN_ADDRESS_AN10 = 10u,
CY_ADC_PIN_ADDRESS_AN11 = 11u,
CY_ADC_PIN_ADDRESS_AN12 = 12u,
CY_ADC_PIN_ADDRESS_AN13 = 13u,
CY_ADC_PIN_ADDRESS_AN14 = 14u,
CY_ADC_PIN_ADDRESS_AN15 = 15u,
CY_ADC_PIN_ADDRESS_AN16 = 16u,
CY_ADC_PIN_ADDRESS_AN17 = 17u,
CY_ADC_PIN_ADDRESS_AN18 = 18u,
CY_ADC_PIN_ADDRESS_AN19 = 19u,
CY_ADC_PIN_ADDRESS_AN20 = 20u,
CY_ADC_PIN_ADDRESS_AN21 = 21u,
CY_ADC_PIN_ADDRESS_AN22 = 22u,
CY_ADC_PIN_ADDRESS_AN23 = 23u,
CY_ADC_PIN_ADDRESS_AN24 = 24u,
CY_ADC_PIN_ADDRESS_AN25 = 25u,
CY_ADC_PIN_ADDRESS_AN26 = 26u,
CY_ADC_PIN_ADDRESS_AN27 = 27u,
CY_ADC_PIN_ADDRESS_AN28 = 28u,
CY_ADC_PIN_ADDRESS_AN29 = 29u,
CY_ADC_PIN_ADDRESS_AN30 = 30u,
CY_ADC_PIN_ADDRESS_AN31 = 31u,
CY_ADC_PIN_ADDRESS_VMOTOR = 32u, /**< Vout = Vmotor, select motor input. */
CY_ADC_PIN_ADDRESS_VAUX = 33u, /**< Vout = Vaux, select auxiliarly input. */
CY_ADC_PIN_ADDRESS_AMUXBUS_A = 34u, /**< Vout = AmuxbusA. */
CY_ADC_PIN_ADDRESS_AMUXBUS_B = 35u, /**< Vout = AmuxbusB. */
CY_ADC_PIN_ADDRESS_VCCD = 36u, /**< Vout = Vccd. */
CY_ADC_PIN_ADDRESS_VDDA = 37u, /**< Vout = Vdda. */
CY_ADC_PIN_ADDRESS_VBG = 38u, /**< Vout = Vbg, Bandgap voltage from SRSS. */
CY_ADC_PIN_ADDRESS_VTEMP = 39u, /**< Vout = Vtemp, select temperature sensor. Make sure that only 1 ADC is allowed to use this.. */
CY_ADC_PIN_ADDRESS_VREF_L = 62u, /**< Vout = VrefL (VrefL actually bypasses the SARMUX (XSL)). */
CY_ADC_PIN_ADDRESS_VREF_H = 63u /**< Vout = VrefH (VrefH actually bypasses the SARMUX (XSH)) */
} cy_en_adc_pin_address_t;
/** Select the physical port. This field is only valid for ADC0. */
typedef enum {
CY_ADC_PORT_ADDRESS_SARMUX0 = 0u, /**< ADC uses it's own SARMUX. */
CY_ADC_PORT_ADDRESS_SARMUX1 = 1u, /**< ADC0 uses SARMUX1 (only valid for ADC0, undefined result if used for ADC1-3). */
CY_ADC_PORT_ADDRESS_SARMUX2 = 2u, /**< ADC0 uses SARMUX2 (only valid for ADC0, undefined result if used for ADC1-3). */
CY_ADC_PORT_ADDRESS_SARMUX3 = 3u /**< ADC0 uses SARMUX3 (only valid for ADC0, undefined result if used for ADC1-3). */
} cy_en_adc_port_address_t;
/** Select preconditioning mode.
Preconditioning (dis)charges the SAR sample capacitor to the selected reference voltage for precondition time (global) cycles,
a break before make cycle will be inserted before sampling starts sample time.
See also \ref cy_stc_adc_config_t */
typedef enum {
CY_ADC_PRECONDITION_MODE_OFF = 0u, /**< No preconditioning. */
CY_ADC_PRECONDITION_MODE_VREFL = 1u, /**< Discharge to VREFL. */
CY_ADC_PRECONDITION_MODE_VREFH = 2u, /**< Charge to VREFH. */
CY_ADC_PRECONDITION_MODE_DIAG = 3u /**< Connect the Diagnostic reference output during preconditioning.
The Diagnostic reference should be configured to output a reference voltage. */
} cy_en_adc_precondition_mode_t;
/** Select Overlap mode or SARMUX Diagnostics, in both cases the Diagnostic reference is used. */
typedef enum {
CY_ADC_OVERLAP_DIAG_MODE_OFF = 0u, /**< No overlap or SARMUX Diagnostics. */
CY_ADC_OVERLAP_DIAG_MODE_HALF = 1u, /**< Sample the selected analog input for 2 sample time periods.
During the first period use overlap sampling,
i.e. connect both the analog input and Diagnostic reference.
During second period only connect the analog input */
CY_ADC_OVERLAP_DIAG_MODE_FULL = 2u, /**< Like normal sample the selected analog input for a single sample time period
but use overlap sampling, i.e. connect both the analog input and Diagnostic reference. */
CY_ADC_OVERLAP_DIAG_MODE_MUX_DIAG = 3u /**< Select Diagnostic reference instead of analog signal at the input of the SARMUX.
This enables a functional safety check of the SARMUX analog connections. */
} cy_en_adc_overlap_diag_mode_t;
/** Select calibration values. */
typedef enum {
CY_ADC_CALIBRATION_VALUE_REGULAR = 0u, /**< use regular calibration values */
CY_ADC_CALIBRATION_VALUE_ALTERNATE = 1u /**< use alternate calibration values */
} cy_en_adc_calibration_value_select_t;
/** Select post processing mode. */
typedef enum {
CY_ADC_POST_PROCESSING_MODE_NONE = 0u, /**< No postprocessing. */
CY_ADC_POST_PROCESSING_MODE_AVG = 1u, /**< Averaging. */
CY_ADC_POST_PROCESSING_MODE_AVG_RANGE = 2u, /**< Averaging followed by Range detect. */
CY_ADC_POST_PROCESSING_MODE_RANGE = 3u, /**< Range detect. */
CY_ADC_POST_PROCESSING_MODE_RANGE_PULSE = 4u, /**< Range detect followed by pulse detect. */
} cy_en_adc_post_processing_mode_t;
/** Select result data alignment. */
typedef enum {
CY_ADC_RESULT_ALIGNMENT_RIGHT = 0u, /**< the data is right aligned in result[11:0], with sign extension to 16 bits if enabled. */
CY_ADC_RESULT_ALIGNMENT_LEFT = 1u, /**< the data is left aligned in result[15:4] with the lower nibble 0.
Caveat if the result was more than 12 bits (e.g. after averaging)
then the bits above 12 will be discarded. */
} cy_en_adc_result_alignment_t;
/** Select whether result data is signed or unsigned. */
typedef enum {
CY_ADC_SIGN_EXTENTION_UNSIGNED = 0u, /**< Result data is unsigned (zero extended if needed). */
CY_ADC_SIGN_EXTENTION_SIGNED = 1u, /**< Result data is signed (sign extended if needed). */
} cy_en_adc_sign_extention_t;
/** Select range detection mode. */
typedef enum {
CY_ADC_RANGE_DETECTION_MODE_BELOW_LO = 0u, /**< Below Low threshold (result < Lo) */
CY_ADC_RANGE_DETECTION_MODE_INSIDE_RANGE = 1u, /**< Inside range (Lo <= result < Hi) */
CY_ADC_RANGE_DETECTION_MODE_ABOVE_HI = 2u, /**< Above high threshold (Hi <= result) */
CY_ADC_RANGE_DETECTION_MODE_OUTSIDE_RANGE = 3u /**< Outside range (result < Lo || Hi <= result) */
} cy_en_adc_range_detection_mode_t;
/** Select reference buffer mode. */
typedef enum {
CY_ADC_REF_BUF_MODE_OFF = 0u, /**< No reference mode selected */
CY_ADC_REF_BUF_MODE_ON = 1u, /**< Reference buffered Vbg from SRSS */
CY_ADC_REF_BUF_MODE_BYPASS = 3u, /**< Reference unbuffered Vbg from SRSS */
} cy_en_adc_ref_buf_mode_t;
/** \} group_adc_enums */
/***************************************
* Configuration Structure
***************************************/
/** Channel conversion status */
typedef struct
{
bool aboveHi; /**< If true out of range was detected and the value was above the Hi threshold */
bool rangeIntr; /**< If true range detected. */
bool pulseIntr; /**< If true pulse detected. */
bool valid; /**< If true result data is valid. */
} cy_stc_adc_ch_status_t;
/** Interrupt sources */
typedef struct
{
bool grpDone; /**< (Group) Done interrupt */
bool grpCancelled; /**< (Group) Cancelled Interrupt */
bool grpOverflow; /**< (Group) Overflow Interrupt */
bool chRange; /**< Range detect Interrupt */
bool chPulse; /**< Pulse detect Interrupt */
bool chOverflow; /**< Channel overflow Interrupt */
} cy_stc_adc_interrupt_source_t;
/** Group sources */
typedef struct
{
bool grpComplete; /**< Group acquisition complete. */
bool grpCancelled; /**< Group Cancelled. */
bool grpOverflow; /**< Group Overflow. */
bool chRangeComplete; /**< Channel Range complete. */
bool chPulseComplete; /**< Channel Pulse complete. */
bool chOverflow; /**< Channel Overflow. */
bool grpBusy; /**< Group acquisition busy. */
} cy_stc_adc_group_status_t;
/**
* \addtogroup group_adc_data_structures
* \{
*/
/** Configuration structure of a ADC */
typedef struct {
uint8_t preconditionTime; /**< Number ADC clock cycles that Preconditioning is done before the sample window starts. */
uint8_t powerupTime; /**< Number cycles to wait to power up after IDLE_PWRDWN. */
bool enableIdlePowerDown; /**< When idle automatically power down the analog if true. */
cy_en_adc_msb_stretch_mode_t msbStretchMode; /**< When set use 2 cycles for the Most Significant Bit (MSB), see \ref cy_en_adc_msb_stretch_mode_t */
bool enableHalfLsbConv; /**< When true take an extra cycle to convert the half LSB and add it to 12-bit result for Missing Code Recovery */
bool sarMuxEnable; /**< Enable the SARMUX (only valid if sarIpEnable = true). */
bool adcEnable; /**< Enable the SAR ADC and SAR sequencer (only valid if sarIpEnable = true). */
bool sarIpEnable; /**< Enable the SAR IP. */
} cy_stc_adc_config_t;
/** Configuration structure of a ADC channel */
typedef struct {
cy_en_adc_trigger_selection_t triggerSelection; /**< The ADC trigger mode selection, see \ref cy_en_adc_trigger_selection_t */
uint8_t channelPriority; /**< Channel priority. 0=highest, 7=lowest. Valid range = [0..7].*/
cy_en_adc_preemption_type_t preenptionType; /**< The ADC preemption type selection, see \ref cy_en_adc_preemption_type_t */
bool isGroupEnd; /**< This value indicates that this channel is the last channel of a group or not */
cy_en_adc_done_level_t doneLevel; /**< Conversion done trigger output selection, see \ref cy_en_adc_done_level_t */
cy_en_adc_pin_address_t pinAddress; /**< Address of the analog signal (pin) to be sampled by corresponding channel,
* see \ref cy_en_adc_pin_address_t
*/
cy_en_adc_port_address_t portAddress; /**< Select the physical port, see \ref cy_en_adc_port_address_t */
uint8_t extMuxSelect; /**< External analog mux select. */
bool extMuxEnable; /**< External analog mux enable. */
cy_en_adc_precondition_mode_t preconditionMode; /**< Select preconditioning mode, see \ref cy_en_adc_precondition_mode_t */
cy_en_adc_overlap_diag_mode_t overlapDiagMode; /**< Select Overlap mode or SARMUX Diagnostics, see \ref cy_en_adc_overlap_diag_mode_t */
uint16_t sampleTime; /**< Sample time (aperture) in ADC clock cycles. Minimum is 1 (0 gives the same result as 1),
* minimum time needed for proper settling is at least 300ns,
* i.e. 6 clock cycles at the max frequency of 20MHz.
*/
cy_en_adc_calibration_value_select_t calibrationValueSelect; /**< Select calibration values, see \ref cy_en_adc_calibration_value_select_t */
cy_en_adc_post_processing_mode_t postProcessingMode; /**< Select post processing mode, see \ref cy_en_adc_post_processing_mode_t */
cy_en_adc_result_alignment_t resultAlignment; /**< Select result data alignment, see \ref cy_en_adc_result_alignment_t */
cy_en_adc_sign_extention_t signExtention; /**< Select whether result data is signed or unsigned, see \ref cy_en_adc_sign_extention_t */
uint8_t averageCount; /**< Either averaging count (minus 1) or Pulse positive reload value. */
uint8_t rightShift; /**< Either Shift Right (no pulse detection) or Pulse nagative reload value (if pulse detection is enabled).
* When using other than pulse detection, setting value is used for right shift value of the
* conversion result. Typically it is used for the averaging. Averaging data may be over 12bits,
* therefore user must ensure conversion result not to over 12bits by using this value.
* This value is also used to fit the 12-bit result in 8bits.
* When using other than pulse detection, valid range is [0..12].
* In pulse detection mode this value is used as negative reload value.
*/
cy_en_adc_range_detection_mode_t rangeDetectionMode; /**< Select range detection mode, see \ref cy_en_adc_range_detection_mode_t */
uint16_t rangeDetectionLoThreshold; /**< Range detect low threshold (Lo) */
uint16_t rangeDetectionHiThreshold; /**< Range detect high threshold (Hi) */
cy_stc_adc_interrupt_source_t mask; /**< Select the interrupt sources. \ref cy_stc_adc_interrupt_source_t */
} cy_stc_adc_channel_config_t;
/** Digital calibration values. */
typedef struct {
uint16_t offset; /**< Digital offset correction. Valid range is [0..4096] */
uint8_t gain; /**< Digital gain correction. Valid range is [0..63] */
} cy_stc_adc_digital_calibration_conifg_t;
/** Analog calibration values. */
typedef struct {
int8_t offset; /**< Analog offset correction. Valid range is [0..255] */
int8_t gain; /**< Analog gain correction. Valid range is [0..31] */
} cy_stc_adc_analog_calibration_conifg_t;
/** Configuration structure of diagnosis function. */
typedef struct {
cy_en_adc_diag_reference_select_t referenceSelect; /**< Select Diagnostic Reference function, see \ref cy_en_adc_diag_reference_select_t */
} cy_stc_adc_diag_config_t;
/** Control freeze feature for debugging. */
typedef struct {
bool enableFreezeAdc0; /**< If true, freeze ADC0 in debug mode. */
bool enableFreezeAdc1; /**< If true, freeze ADC1 in debug mode. */
bool enableFreezeAdc2; /**< If true, freeze ADC2 in debug mode. */
bool enableFreezeAdc3; /**< If true, freeze ADC3 in debug mode. */
} cy_stc_adc_debug_freeze_config_t;
/** ADC temp sensor data values structure read form SFLASH */
typedef struct {
double hotValue; /**< Pass the hot temperature reading */
double roomValue; /**< Pass the room temperature reading */
double coldValue; /**< Pass the cold temperature reading */
} cy_en_adc_temp_sflash_data_t;
/** ADC ref data values structure read form SFLASH */
typedef struct {
cy_en_adc_temp_sflash_data_t adcVbgValues; /**< Pass the Vbg read value from SFLASH */
cy_en_adc_temp_sflash_data_t adcTempValues; /**< Pass the sort data values from SFLASH */
cy_en_adc_temp_sflash_data_t adcDiodeValues; /**< Pass the class data values from SFLASH */
} cy_stc_adc_temp_ref_t;
/** ADC raw data values structure of a temperature calculation */
typedef struct {
uint16_t adcVtempRawValue; /**< Enable the SAR Channel VTEMP, and update the value */
uint16_t adcVbgRawValue; /**< Enable the SAR Channel VBG, and update the value */
} cy_stc_adc_temp_raw_t;
/** \} group_adc_data_structures */
/***************************************
* Global Variable
***************************************/
/* N/A */
/***************************************
* Constants
***************************************/
/** \cond INTERNAL */
/** \endcond */
/***************************************
* Function Prototypes
***************************************/
/**
* \addtogroup group_adc_functions
* \{
*/
/* For each ADC */
cy_en_adc_status_t Cy_Adc_Init(volatile stc_PASS_SAR_t * base, const cy_stc_adc_config_t * config);
void Cy_Adc_DeInit(volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetPendingStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetWorkValidStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetWorkRangeStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetWorkRangeHiStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetWorkPulseStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetResultValidStatus(const volatile stc_PASS_SAR_t * base);
uint32_t Cy_Adc_GetResultRangeHiStatus(const volatile stc_PASS_SAR_t * base);
/* For each channels */
cy_en_adc_status_t Cy_Adc_Channel_Init(volatile stc_PASS_SAR_CH_t * base, const cy_stc_adc_channel_config_t * config);
void Cy_Adc_Channel_DeInit(volatile stc_PASS_SAR_CH_t * base);
void Cy_Adc_Channel_Enable(volatile stc_PASS_SAR_CH_t * base);
void Cy_Adc_Channel_Disable(volatile stc_PASS_SAR_CH_t * base);
void Cy_Adc_Channel_SoftwareTrigger(volatile stc_PASS_SAR_CH_t * base);
cy_en_adc_status_t Cy_Adc_Channel_GetResult(const volatile stc_PASS_SAR_CH_t * base, uint16_t * result, cy_stc_adc_ch_status_t * status);
cy_en_adc_status_t Cy_Adc_Channel_GetWorkingData(const volatile stc_PASS_SAR_CH_t * base, uint16_t * result, cy_stc_adc_ch_status_t * status);
cy_en_adc_status_t Cy_Adc_Channel_GetGroupStatus(const volatile stc_PASS_SAR_CH_t * base, cy_stc_adc_group_status_t * status);
cy_en_adc_status_t Cy_Adc_Channel_SetInterruptMask(volatile stc_PASS_SAR_CH_t * base, const cy_stc_adc_interrupt_source_t * mask);
cy_en_adc_status_t Cy_Adc_Channel_GetInterruptMask(const volatile stc_PASS_SAR_CH_t * base, cy_stc_adc_interrupt_source_t * mask);
cy_en_adc_status_t Cy_Adc_Channel_ClearInterruptStatus(volatile stc_PASS_SAR_CH_t * base, const cy_stc_adc_interrupt_source_t * source);
cy_en_adc_status_t Cy_Adc_Channel_GetInterruptStatus(const volatile stc_PASS_SAR_CH_t * base, cy_stc_adc_interrupt_source_t * status);
cy_en_adc_status_t Cy_Adc_Channel_GetInterruptMaskedStatus(const volatile stc_PASS_SAR_CH_t * base, cy_stc_adc_interrupt_source_t * status);
/* For diagnosis function */
cy_en_adc_status_t Cy_Adc_Diag_Init(volatile stc_PASS_SAR_t * base, const cy_stc_adc_diag_config_t * config);
void Cy_Adc_Diag_Enable(volatile stc_PASS_SAR_t * base);
void Cy_Adc_Diag_Disable(volatile stc_PASS_SAR_t * base);
/* For calibration */
void Cy_Adc_TriggerCalibrationUpdate(volatile stc_PASS_SAR_t * base);
bool Cy_Adc_IsCalibrationUpdateDone(const volatile stc_PASS_SAR_t * base);
cy_en_adc_status_t Cy_Adc_SetDigitalCalibrationValue(volatile stc_PASS_SAR_t * base, const cy_stc_adc_digital_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_GetDigitalCalibrationValue(const volatile stc_PASS_SAR_t * base, cy_stc_adc_digital_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_SetAltDigitalCalibrationValue(volatile stc_PASS_SAR_t * base, const cy_stc_adc_digital_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_GetAltDigitalCalibrationValue(const volatile stc_PASS_SAR_t * base, cy_stc_adc_digital_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_SetAnalogCalibrationValue(volatile stc_PASS_SAR_t * base, cy_stc_adc_analog_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_GetAnalogCalibrationValue(const volatile stc_PASS_SAR_t * base, cy_stc_adc_analog_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_SetAltAnalogCalibrationValue(volatile stc_PASS_SAR_t * base, cy_stc_adc_analog_calibration_conifg_t * config);
cy_en_adc_status_t Cy_Adc_GetAltAnalogCalibrationValue(const volatile stc_PASS_SAR_t * base, cy_stc_adc_analog_calibration_conifg_t * config);
/* For debugging */
cy_en_adc_ref_buf_mode_t Cy_Adc_GetReferenceBufferMode(volatile stc_PASS_EPASS_MMIO_t * base);
void Cy_Adc_SetReferenceBufferMode(volatile stc_PASS_EPASS_MMIO_t * base, cy_en_adc_ref_buf_mode_t mode);
cy_en_adc_status_t Cy_Adc_SetDebugFreezeMode(volatile stc_PASS_EPASS_MMIO_t * base, const cy_stc_adc_debug_freeze_config_t * config);
/* For SAR general trigger input / output trigger setting */
cy_en_adc_status_t Cy_Adc_SetGenericTriggerInput(volatile stc_PASS_EPASS_MMIO_t * base, uint8_t numOfAdc, uint8_t triggerInputNumber, uint8_t genericTriggerValue);
cy_en_adc_status_t Cy_Adc_SetGenericOutpuTrigger(volatile stc_PASS_EPASS_MMIO_t * base, uint8_t numOfAdc, uint8_t outputTriggerNumber, uint8_t genericTriggerValue);
/* For temperature measurements */
double Cy_Adc_CalculateDieTemperature(cy_stc_adc_temp_ref_t *refValue, cy_stc_adc_temp_raw_t *rawValue);
/** \} group_adc_functions */
#if defined(__cplusplus)
}
#endif
#endif /* CY_ADC_H */
/** \} group_adc */
/* [] END OF FILE */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,915 @@
/***************************************************************************//**
* \file cy_canfd.h
* \version 1.0
*
* \brief
* Provides an API declaration of the CAN FD driver
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
/**
* \defgroup CanFDGroup CAN with Flexible Data rate (CAN FD)
* \{
* \brief This section describes the interface for the CAN with Flexible Data rate.
*
* Provided functions of CAN FD module:
* - Cy_CANFD_Init() (Initialization of CAN FD module)
* - Cy_CANFD_UpdateAndTransmitMsgBuffer() (Used to update the data in a message buffer and to transmit a message)
* - Cy_CANFD_DeInit() (Deinitialization of CAN FD module)
* - Cy_CANFD_GetTxBufferStatus() (Used to retreive the Tx message buffer status)
*
* \brief How to use the CAN FD module:
* First, to initialise a CAN FD module, Cy_CANFD_Init() must be called.
* Initialize the CAN FD module with the configuration parameters, and start
* the CAN FD communication.
* The callback functions are optional, but recommended, otherwise there is no
* report to the API in case of any error and transmission/reception.
*
* For transmitting CAN FD messages the function CanFD_UpdateAndTransmitMsgBuffer()
* has to be used. The buffer status can be retreived by Cy_CANFD_GetTxBufferStatus().
* It is possible to set a callback function which will be notified whenever a
* message has been received.
*
* \description
* Specification of sample software is as follows:
* - Selectable CAN-FD mode or CAN mode.
* - Configurable CAN clock
* - Configurable CAN Bit rate
* - Configurable CAN-FD Bit rate
* - Configurable Standard ID filter/s
* - Configurable Extended Message ID Filter/s
* - Tx/Rx Element Size : 8 byte (CAN mode)
* Tx/Rx Element Size : 64 byte (CAN-FD mode)
* - Tx/Rx FIFO : Not used
* - Interrupt : DRXE=1 (Message stored to Dedicated Rx Buffer)
* (Inactive other)
*
* Send data of ID:0x200 periodically in the main().
* Echoback by incrementing the ID of the Rx data received by
* the receiving interrupt.(CanFDIrqHandler() --- CanFDCallbackRx())
*
* \note Following settings are not implemented.
* - Using Tx/warning/error interrupt
* - Using Tx/Rx FIFO
*
*/
#if !defined(CY_CANFD_H)
#define CY_CANFD_H
#include <stddef.h>
#include "syslib/cy_syslib.h"
#include "cy_device_headers.h"
#include "canfd/cy_canfd_config.h"
#if defined(__cplusplus)
extern "C" {
#endif
/** \addtogroup group_canfd_macro
* \{
*/
/** Driver major version */
#define CY_CANFD_DRV_VERSION_MAJOR 1
/** Driver minor version */
#define CY_CANFD_DRV_VERSION_MINOR 0
/** CANFD driver ID */
#define CY_CANFD_ID CY_PDL_DRV_ID(0x18u)
/** \} group_canfd_macro */
/***************************************
* Constants
***************************************/
/** Size of Word data bytes in a receive or transmit operation (16word = 64byte) */
#define CY_CANFD_MESSAGE_DATA_BUFFER_SIZE 16
/** Rx FIFO Numbers */
#define CY_CANFD_RX_FIFO0 0
#define CY_CANFD_RX_FIFO1 1
#define CY_CANFD_MSGRAM_START (0x00010000ul)
/**
******************************************************************************
** \brief The macro for configuration the standard ID filter to store RX buffer.
** If RxId fulfills the following condition, RxId matches in this filter.
** Id == RxId
** When the RxId matches the condition, the message will be store into a rx buffer
** that specified by RxBuffIdx.
******************************************************************************/
#define CANFD_CONFIG_STD_ID_FILTER_CLASSIC_RXBUFF(Id, RxBuffIdx) \
{ \
.sfid2 = (0u << 9u) | (uint32_t)(RxBuffIdx), \
.sfid1 = (uint32_t)(Id), \
.sfec = (uint32_t)CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXBUFF_OR_DEBUGMSG, \
.sft = (uint32_t)CY_CANFD_STD_ID_FILTER_TYPE_CLASSIC, \
}
/**
******************************************************************************
** \brief The macro for configuration the standard ID filter as the classic filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** (Id & IdMask) == (RxId & IdMask)
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_STD_ID_FILTER_CLASSIC(Id, IdMask, ElementConfig) \
{ \
.sfid2 = (uint32_t)(IdMask), \
.sfid1 = (uint32_t)(Id), \
.sfec = (uint32_t)(ElementConfig), \
.sft = (uint32_t)CY_CANFD_STD_ID_FILTER_TYPE_CLASSIC, \
}
/**
******************************************************************************
** \brief The macro for configuration the standard ID filter as the range filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** MinId <= RxId <= MaxId
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_STD_ID_FILTER_RANGE(MinId, MaxId, ElementConfig) \
{ \
.sfid2 = (uint32_t)(MaxId), \
.sfid1 = (uint32_t)(MinId), \
.sfec = (uint32_t)(ElementConfig), \
.sft = (uint32_t)CY_CANFD_STD_ID_FILTER_TYPE_RANGE, \
}
/**
******************************************************************************
** \brief The macro for configuration the standard ID filter as the dual filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** Id1 == RxId
** OR
** Id2 == RxId
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_STD_ID_FILTER_DUAL(Id1, Id2, ElementConfig) \
{ \
.sfid2 = (uint32_t)(Id2), \
.sfid1 = (uint32_t)(Id1), \
.sfec = (uint32_t)(ElementConfig), \
.sft = (uint32_t)CY_CANFD_STD_ID_FILTER_TYPE_DUAL, \
}
/**
******************************************************************************
** \brief The macro for configuration the extended ID filter to store RX buffer.
** If RxId fulfills the following condition, RxId matches in this filter.
** Id == (RxId & extIDANDMask)
** When the RxId matches the condition, the message will be store into a rx buffer
** that specified by RxBuffIdx.
******************************************************************************/
#define CANFD_CONFIG_EXT_ID_FILTER_CLASSIC_RXBUFF(Id, RxBuffIdx) \
{ \
.f0_f = \
{ \
.efid1 = (uint32_t)(Id), \
.efec = (uint32_t)CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXBUFF_OR_DEBUGMSG, \
}, \
.f1_f = \
{ \
.efid2 = (0u << 9u) | (uint32_t)(RxBuffIdx), \
.eft = (uint32_t)CY_CANFD_EXT_ID_FILTER_TYPE_CLASSIC, \
}, \
}
/**
******************************************************************************
** \brief The macro for configuration the extended ID filter as the classic filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** (Id & IdMask) == ((RxId & extIDANDMask) & IdMask)
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_EXT_ID_FILTER_CLASSIC(Id, IdMask, ElementConfig) \
{ \
.f0_f = \
{ \
.efid1 = (uint32_t)(Id), \
.efec = (uint32_t)(ElementConfig), \
}, \
.f1_f = \
{ \
.efid2 = (uint32_t)(IdMask), \
.eft = (uint32_t)CY_CANFD_EXT_ID_FILTER_TYPE_CLASSIC, \
}, \
}
/**
******************************************************************************
** \brief The macro for configuration the extended ID filter as the range filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** MinId <= RxId <= MaxId
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_EXT_ID_FILTER_RANGE(MinId, MaxId, ElementConfig) \
{ \
.f0_f = \
{ \
.efid1 = (uint32_t)(MinId), \
.efec = (uint32_t)(ElementConfig), \
}, \
.f1_f = \
{ \
.efid2 = (uint32_t)(MaxId), \
.eft = (uint32_t)CY_CANFD_EXT_ID_FILTER_TYPE_RANGE \
}, \
}
/**
******************************************************************************
** \brief The macro for configuration the extended ID filter as the range and mask filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** MinId <= (RxId & extIDANDMask) <= MaxId
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_EXT_ID_FILTER_RANGE_AND_MASK(MinId, MaxId, ElementConfig) \
{ \
.f0_f = \
{ \
.efid1 = (uint32_t)(MinId), \
.efec = (uint32_t)(ElementConfig), \
}, \
.f1_f = \
{ \
.efid2 = (uint32_t)(MaxId), \
.eft = (uint32_t)CY_CANFD_EXT_ID_FILTER_TYPE_RANGE_AND_MASK \
}, \
}
/**
******************************************************************************
** \brief The macro for configuration the extended ID filter as the dual filter.
** If RxId fulfills the following condition, RxId matches in this filter.
** Id1 == (RxId & extIDANDMask)
** OR
** Id2 == (RxId & extIDANDMask)
** When the RxId matches the condition, the message will be treated in accordance
** with ElementConfig.
** ElementConfig can be selected from following definitions:
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0
** - CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1
******************************************************************************/
#define CANFD_CONFIG_EXT_ID_FILTER_DUAL(Id1, Id2, ElementConfig) \
{ \
.f0_f = \
{ \
.efid1 = (uint32_t)(Id1), \
.efec = (uint32_t)(ElementConfig), \
}, \
.f1_f = \
{ \
.efid2 = (uint32_t)(Id2), \
.eft = (uint32_t)CY_CANFD_EXT_ID_FILTER_TYPE_DUAL \
}, \
}
/***************************************
* Enumerations
***************************************/
/**
* \addtogroup group_canfd_enums
* \{
*/
/**
*****************************************************************************
** \brief CAN FD driver error codes.
**
*****************************************************************************/
typedef enum
{
CY_CANFD_SUCCESS = 0x00u, /**< Returned successful */
CY_CANFD_BAD_PARAM = CY_CANFD_ID | CY_PDL_STATUS_ERROR | 0x01u, /**< Bad parameter was passed */
} cy_en_canfd_status_t;
/**
*****************************************************************************
** \brief CAN FD Tx Buffer status.
**
*****************************************************************************/
typedef enum
{
CY_CANFD_TX_BUFFER_IDLE = 0u,
CY_CANFD_TX_BUFFER_PENDING = 1u, /**< Pending transmission> */
CY_CANFD_TX_BUFFER_TRANSMIT_OCCURRED = 2u, /**< Transmission occurred> */
CY_CANFD_TX_BUFFER_CANCEL_REQUESTED = 3u, /**< Cancellation requested> */
CY_CANFD_TX_BUFFER_CANCEL_FINISHED = 4u, /**< Cancellation finished> */
} cy_en_canfd_tx_buffer_status_t;
/**
*****************************************************************************
** \brief CAN FD error.
**
** These error values are used to report any CAN FD related error to the API via
** the CAN FD error callback function (canfd_error_func_ptr_t).
*****************************************************************************/
typedef enum
{
CY_CANFD_BUSOFF = 0, /*!< The CAN FD module is in busoff state. */
CY_CANFD_WARNING = 1 /*!< At least one error counter has reached error warning. */
} cy_en_canfd_bus_error_t;
/**
*****************************************************************************
** \brief CAN FD instance Clock source.
**
** These values are used to specify the clock source for the CAN FD instance
*****************************************************************************/
typedef enum
{
CY_CANFD_CLOCK_SOURCE_MAIN = 0, // TODO: DENA. Clocks SAS still not finalized. Will be done later
} cy_en_canfd_clock_source_t;
/**
*****************************************************************************
** \brief CAN FD Bus status.
**
** These state values represent the current CAN FD state, and are used in the
** CAN FD status changed callback function (canfd_status_chg_func_ptr_t).
*****************************************************************************/
typedef enum
{
CY_CANFD_NO_ERROR = 0, /*!< No error pending. */
CY_CANFD_STUFF_ERROR = 1, /*!< More than 5 equal bits in a sequence have occurred in a part of a received message. */
CY_CANFD_FORM_ERROR = 2, /*!< A fixed format part of a received frame has the wrong format. */
CY_CANFD_ACK_ERROR = 3, /*!< The message this CAN FD Core transmitted was not acknowledged by another node. */
CY_CANFD_BIT1_ERROR = 4, /*!< While trying to send a recessive bit (1) a dominant level (0) was sampled. */
CY_CANFD_BIT0_ERROR = 5, /*!< While trying to send a dominant bit (0) a recessive level (1) was sampled. */
CY_CANFD_CRC_ERROR = 6 /*!< The CRC checksum was incorrect. */
} cy_en_canfd_bus_error_status_t;
/**
*****************************************************************************
** \brief CAN FD TX/RX buffer element size.
**
** The number of data bytes belonging to an Rx, Tx Buffer / Rx FIFO element.
*****************************************************************************/
typedef enum
{
CY_CANFD_BUFFER_DATA_SIZE_8 = 0, /*!< 8 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_12 = 1, /*!< 12 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_16 = 2, /*!< 16 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_20 = 3, /*!< 20 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_24 = 4, /*!< 24 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_32 = 5, /*!< 32 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_48 = 6, /*!< 48 byte data field. */
CY_CANFD_BUFFER_DATA_SIZE_64 = 7 /*!< 64 byte data field. */
} cy_en_canfd_buffer_data_size_t;
/**
*****************************************************************************
** \brief CAN FD RX FIFO operating mode.
*****************************************************************************/
typedef enum
{
CY_CANFD_FIFO_MODE_BLOCKING = 0, /*!< FIFO blocking mode. */
CY_CANFD_FIFO_MODE_OVERWRITE = 1 /*!< FIFO overwrite mode. */
} cy_en_canfd_fifo_mode_t;
/**
*****************************************************************************
** \brief CAN FD accept non matching frames.
*****************************************************************************/
typedef enum
{
CY_CANFD_ACCEPT_IN_RXFIFO_0 = 0,
CY_CANFD_ACCEPT_IN_RXFIFO_1 = 1,
CY_CANFD_REJECT_NON_MATCHING = 2
} cy_en_accept_non_matching_frames_t;
/**
******************************************************************************
** \brief ID Filter Element Configuration Type
******************************************************************************/
typedef enum
{
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_DISABLE_FILETER_ELEMENT = 0x00u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO0 = 0x01u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXFIFO1 = 0x02u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_REJECT_ID = 0x03u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PRIORIY = 0x04u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO0 = 0x05u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_SET_PIORITY_STORE_RXFIFO1 = 0x06u,
CY_CANFD_ID_FILTER_ELEMNT_CONFIG_STORE_RXBUFF_OR_DEBUGMSG = 0x07u,
}cy_en_canfd_id_filter_element_confifg_t;
/**
******************************************************************************
** \brief Standard ID Filter Type
******************************************************************************/
typedef enum
{
CY_CANFD_STD_ID_FILTER_TYPE_RANGE = 0x00u,
CY_CANFD_STD_ID_FILTER_TYPE_DUAL = 0x01u,
CY_CANFD_STD_ID_FILTER_TYPE_CLASSIC = 0x02u,
}cy_en_canfd_std_id_filter_type_t;
/**
******************************************************************************
** \brief Extended ID Filter Type
******************************************************************************/
typedef enum
{
CY_CANFD_EXT_ID_FILTER_TYPE_RANGE_AND_MASK = 0x00u,
CY_CANFD_EXT_ID_FILTER_TYPE_DUAL = 0x01u,
CY_CANFD_EXT_ID_FILTER_TYPE_CLASSIC = 0x02u,
CY_CANFD_EXT_ID_FILTER_TYPE_RANGE = 0x03u,
}cy_en_canfd_ext_id_filter_type_t;
/**
******************************************************************************
** \brief Method of receiving the message
******************************************************************************/
typedef enum
{
CY_CANFD_RECEIVED_IN_DEDICATED_BUFFER = 0x00u,
CY_CANFD_RECEIVED_IN_FIFO_TOP_POINTER_NOT_USED = 0x01u,
CY_CANFD_RECEIVED_IN_FIFO_TOP_POINTER_USED = 0x02u,
}cy_en_canfd_rx_received_type_t;
/** \} group_canfd_enums */
/***************************************
* Configuration Structures
***************************************/
/**
* \addtogroup group_canfd_data_structures
* \{
*/
/**
******************************************************************************
** \brief Redefinition of R0 register bitfields
******************************************************************************/
typedef struct
{
volatile uint32_t id :29; /*!< Identifier */
volatile uint32_t rtr : 1; /*!< Remote Transmission Request. 0:data frame, 1:remote frame */
volatile uint32_t xtd : 1; /*!< Extended Identifier. 0:11-bit standard identifier, 1:29-bit extended identifier */
volatile uint32_t esi : 1; /*!< Error State Indicator. 0:error active, 1:error passive */
} cy_stc_canfd_r0_t;
/**
******************************************************************************
** \brief Redefinition of R1 register bitfields
******************************************************************************/
typedef struct
{
volatile uint32_t rxts :16; /*!< Rx Timestamp */
volatile uint32_t dlc : 4; /*!< Data Length Code */
volatile uint32_t brs : 1; /*!< Bit Rate Switch. 0:without bit rate switching, 1:with bit rate switching */
volatile uint32_t fdf : 1; /*!< Extended Data Length. 0:Classic CAN frame format, 1:CAN FD frame format */
uint32_t reserved1 : 2; /*!< Reserved bits */
volatile uint32_t fidx : 7; /*!< Filter Index */
volatile uint32_t anmf : 1; /*!< Accepted Non-matching Frame. 0:matching filter index FIDX, 1:did not match any Rx filter element */
} cy_stc_canfd_r1_t;
/**
******************************************************************************
** \brief Redefinition of Rx Buffer Register
******************************************************************************/
typedef struct
{
cy_stc_canfd_r0_t r0_f; /*!< Rx buffer element for R0. See #cy_stc_canfd_r0_t. */
cy_stc_canfd_r1_t r1_f; /*!< Rx buffer element for R1. See #cy_stc_canfd_r1_t. */
uint32_t data_area_f[16]; /*!< Rx buffer element for Rn */
} cy_stc_canfd_rx_buffer_t;
/**
******************************************************************************
** \brief Redefinition of T0 register bitfields
******************************************************************************/
typedef struct
{
volatile uint32_t id :29; /*!< Identifier */
volatile uint32_t rtr : 1; /*!< Remote Transmission Request. 0:data frame, 1:remote frame */
volatile uint32_t xtd : 1; /*!< Extended Identifier. 0:11-bit standard identifier, 1:29-bit extended identifier */
volatile uint32_t esi : 1; /*!< Error State Indicator. 0:ESI bit in CAN FD format depends only on error passive flag, 1:ESI bit in CAN FD format transmitted recessive. */
} cy_stc_canfd_t0_t;
/**
******************************************************************************
** \brief Redefinition of T1 register bitfields
******************************************************************************/
typedef struct
{
uint32_t reserved1 :16; /*!< Reserved bits */
volatile uint32_t dlc : 4; /*!< Data Length Code */
volatile uint32_t brs : 1; /*!< Bit Rate Switching. 0:CAN FD frames transmitted without bit rate switching, CAN FD frames transmitted with bit rate switching */
volatile uint32_t fdf : 1; /*!< FD Format. 0:Frame transmitted in Classic CAN format, 1:Frame transmitted in CAN FD format */
volatile uint32_t reserved2 : 1; /*!< Reserved bit*/ // Added by DENA
volatile uint32_t efc : 1; /*!< Event FIFO Control. 0:Tx events not stored, 1:Store Tx events */
volatile uint32_t mm : 8; /*!< Message Marker */
} cy_stc_canfd_t1_t;
/**
******************************************************************************
** \brief Redefinition of Tx Buffer Register
******************************************************************************/
typedef struct
{
cy_stc_canfd_t0_t t0_f; /*!< Tx buffer element for T0. See #cy_stc_canfd_t0_t. */
cy_stc_canfd_t1_t t1_f; /*!< Tx buffer element for T1. See #cy_stc_canfd_t1_t. */
uint32_t data_area_f[16]; /*!< Tx buffer element for Tn */
} cy_stc_canfd_tx_buffer_t;
/**
******************************************************************************
** \brief Redefinition of Message ID Filter register
** This filter can be configured with CANFD_CONFIG_STD_ID_FILTER_* macros.
******************************************************************************/
typedef struct
{
volatile uint32_t sfid2 :11; /*!< Standard Filter ID 2 */
uint32_t reserved4 : 5; /*!< Reserved bits */
volatile uint32_t sfid1 :11; /*!< Standard Filter ID 1 */
volatile uint32_t sfec : 3; /*!< Standard Filter Element Configuration */
volatile uint32_t sft : 2; /*!< Standard Filter Type */
} cy_stc_id_filter_t;
/**
******************************************************************************
** \brief Redefinition of F0 register bitfields
******************************************************************************/
typedef struct
{
volatile uint32_t efid1 :29; /*!< Extended Filter ID 1 */
volatile uint32_t efec : 3; /*!< Extended Filter Element Configuration */
} cy_stc_canfd_f0_t;
/**
******************************************************************************
** \brief Redefinition of F1 register bitfields
******************************************************************************/
typedef struct
{
volatile uint32_t efid2 :29; /*!< Extended Filter ID 2 */
uint32_t reserved1 : 1; /*!< Reserved bits */
volatile uint32_t eft : 2; /*!< Extended Filter Type */
} cy_stc_canfd_f1_t;
/**
******************************************************************************
** \brief Redefinition of Extended Message ID Filter register
** This filter can be configured with CANFD_CONFIG_EXT_ID_FILTER_* macros.
******************************************************************************/
typedef struct
{
cy_stc_canfd_f0_t f0_f; /*!< Extended Message ID Filter Element for F0. See #stc_canfd_f0_t. */
cy_stc_canfd_f1_t f1_f; /*!< Extended Message ID Filter Element for F1. See #stc_canfd_f1_t. */
} cy_stc_extid_filter_t;
/**
*****************************************************************************
** \brief CAN FD bitrate.
**
** This structure is used to set the bitrate register.
*****************************************************************************/
typedef struct
{
uint16_t prescaler; /*!< Baud Rate Prescaler */
uint8_t timeSegment1; /*!< Time segment before sample point */
uint8_t timeSegment2; /*!< Time segment after sample point */
uint8_t syncJumpWidth; /*!< Synchronization Jump Width */
} cy_stc_canfd_bitrate_t;
/**
*****************************************************************************
** \brief CAN FD transceiver delay compensation offset configuration.
*****************************************************************************/
typedef struct
{
bool tdcEnabled;
uint8_t tdcOffset;
uint8_t tdcFilterWindow;
} cy_stc_canfd_transceiver_delay_compensation_t;
/**
*****************************************************************************
** \brief CAN FD message identifier
**
** This structure is used to set the CAN FD message identifier for transmit and
** receive operations.
*****************************************************************************/
typedef struct
{
uint32_t identifier; /*!< 11- or 29-bit identifier. The valid bit length depends on bExtended. */
bool extended; /*!< TRUE: 29-bit identifier, FALSE: 11-bit identifier. */
} cy_stc_canfd_msg_id_t;
/**
*****************************************************************************
** \brief CAN FD message data.
**
** Data structure for transmit and receive operations
*****************************************************************************/
typedef struct
{
uint32_t data[CY_CANFD_MESSAGE_DATA_BUFFER_SIZE]; /*!< Data of CAN FD message. */
uint8_t dataLengthCode; /*!< Number of valid bytes in data and DLC of CAN FD message. */
} cy_stc_canfd_msg_data_t;
/**
*****************************************************************************
** \brief CAN FD message.
**
** This structure stores a CAN FD message, including the identifier, data
** and data length code (DLC).
*****************************************************************************/
typedef struct
{
bool canFDFormat; /*!< TRUE:CAN FD format, FALSE:Classic CAN format. */
cy_stc_canfd_msg_id_t idConfig; /*!< 11- or 29-bit identifier (ID) */
cy_stc_canfd_msg_data_t dataConfig; /*!< Data and DLC */
} cy_stc_canfd_msg_t;
/**
*****************************************************************************
** \brief Standard ID filter configuration.
**
** Data structure for SID flter configuration.
*****************************************************************************/
typedef struct
{
uint8_t numberOfSIDFilters;
const cy_stc_id_filter_t* sidFilter;
} cy_stc_canfd_sid_filter_config_t;
/**
*****************************************************************************
** \brief Extended ID filter configuration.
**
** Data structure for EXTID flter configuration.
*****************************************************************************/
typedef struct
{
uint8_t numberOfEXTIDFilters;
const cy_stc_extid_filter_t* extidFilter;
uint32_t extIDANDMask;
} cy_stc_canfd_extid_filter_config_t;
/**
*****************************************************************************
** \brief Global filter configuration.
**
** Data structure for Global flter configuration.
*****************************************************************************/
typedef struct
{
cy_en_accept_non_matching_frames_t nonMatchingFramesStandard;
cy_en_accept_non_matching_frames_t nonMatchingFramesExtended;
bool rejectRemoteFramesStandard;
bool rejectRemoteFramesExtended;
} cy_stc_canfd_global_filter_config_t;
/**
*****************************************************************************
** \brief Rx Fifo configuration.
**
** Data structure for Rx Fifo configuration.
*****************************************************************************/
typedef struct
{
cy_en_canfd_fifo_mode_t mode;
uint8_t watermark;
uint8_t numberOfFifoElements;
bool topPointerLogicEnabled;
}cy_en_canfd_fifo_config_t;
/**
*****************************************************************************
** \brief Message transmission complete callback function (cy_canfd_tx_msg_func_ptr_t).
**
** Signals a successful completed transmission.
*****************************************************************************/
typedef void (*cy_canfd_tx_msg_func_ptr_t)( void );
/**
*****************************************************************************
** \brief Message reception callback function for message received in dedicated
** Rx Buffer or in Rx Fifo when Rx Fifo top pointer is not used (cy_canfd_rx_msg_func_ptr_t)
**
** Signals that CAN FD has received a new message.
** -- If Message was received in dedicated Rx Buffer (0 - 63) then bRxFifoMsg is False
** and u8MsgBufOrFifoNum indicates the Rx Buffer number
** -- If Message was received in Rx Fifo (0/1 and Rx Fifo top pointer is not used)
** then bRxFifoMsg is True and u8MsgBufOrFifoNum indicates the FIFO number.
*****************************************************************************/
typedef void (*cy_canfd_rx_msg_func_ptr_t)(bool bRxFifoMsg,
uint8_t u8MsgBufOrRxFifoNum,
cy_stc_canfd_msg_t* pstcCanFDmsg
);
/**
*****************************************************************************
** \brief Message reception callback function for message received in Rx Fifo
** when Rx Fifo Top pointer is used (cy_canfd_rx_msg_func_ptr_t).
**
*****************************************************************************/
typedef void (*cy_canfd_rx_fifo_msg_with_top_func_ptr_t)(uint8_t u8FifoNum,
uint8_t u8BufferSizeInWord,
uint32_t* pu32RxBuf
);
/**
*****************************************************************************
** \brief Status changed callback function (cy_canfd_status_chg_func_ptr_t).
**
** Any status change will be reported to the API (see #cy_en_canfd_bus_error_status_t for
** possible status change codes).
*****************************************************************************/
typedef void (*cy_canfd_status_chg_func_ptr_t)(cy_en_canfd_bus_error_status_t enCanFDStatus);
/**
*****************************************************************************
** \brief Error callback function (cy_canfd_error_func_ptr_t).
**
** Any error will be reported to the API (see #cy_en_canfd_bus_error_t for
** possible error codes).
*****************************************************************************/
typedef void (*cy_canfd_error_func_ptr_t)(cy_en_canfd_bus_error_t enCanFDError);
/**
*****************************************************************************
** \brief CAN FD configuration.
**
** Is used to set the bitrate and select CAN mode or CAN FD mode.
** Optionally Callbacks notification can be set.
*****************************************************************************/
typedef struct
{
cy_canfd_tx_msg_func_ptr_t txCallback; /*!< Callback function for transmit completed, can be NULL. */
cy_canfd_rx_msg_func_ptr_t rxCallback; /*!< Callback function for receive completed, can be NULL. */
cy_canfd_rx_fifo_msg_with_top_func_ptr_t rxFifoWithTopCallback; /*!< Callback function for receive in Fifo completed with Fifo Top pointer logic enabled */
cy_canfd_status_chg_func_ptr_t statusCallback; /*!< Callback function for CAN status changes, can be NULL. */
cy_canfd_error_func_ptr_t errorCallback; /*!< Callback function for CAN related errors, can be NULL. */
bool canFDMode; /*!< TRUE:CAN FD mode, FALSE:Classic CAN mode. */
cy_stc_canfd_bitrate_t bitrate; /*!< CAN bitrate setting. */
cy_stc_canfd_bitrate_t fastBitrate; /*!< CAN Fast bitrate setting. */
cy_stc_canfd_transceiver_delay_compensation_t tdcConfig; /*!< CAN transceiver delay compensation setting. */
cy_stc_canfd_sid_filter_config_t sidFilterConfig; /*!< CAN Standard ID filter setting. */
cy_stc_canfd_extid_filter_config_t extidFilterConfig; /*!< CAN Extended ID filter setting. */
cy_stc_canfd_global_filter_config_t globalFilterConfig; /*!< CAN global filter setting. */
cy_en_canfd_buffer_data_size_t rxBufferDataSize;
cy_en_canfd_buffer_data_size_t rxFifo1DataSize;
cy_en_canfd_buffer_data_size_t rxFifo0DataSize;
cy_en_canfd_buffer_data_size_t txBufferDataSize;
cy_en_canfd_fifo_config_t rxFifo0Config;
cy_en_canfd_fifo_config_t rxFifo1Config;
uint8_t noOfRxBuffers; /*!< Max 64> */
uint8_t noOfTxBuffers; /*!< Max 32> */
} cy_stc_canfd_config_t;
/**
*****************************************************************************
** \brief CAN FD interrupt pointer structure
**
** Holds some pointers to callback functions and buffer
*****************************************************************************/
typedef struct
{
cy_canfd_tx_msg_func_ptr_t canFDTxInterruptFunction; /*!< pointer to transmit interrupt callback */
cy_canfd_rx_msg_func_ptr_t canFDRxInterruptFunction; /*!< pointer to receive interrupt callback (dedicated Rx Buffer or RxFifo without Top pointer logic)*/
cy_canfd_rx_fifo_msg_with_top_func_ptr_t canFDRxWithTopPtrInterruptFunction; /*!< pointer to receive interrupt callback (RxFifo when Top pointer logic used)*/
} cy_stc_canfd_interrupt_handling_t;
/**
*****************************************************************************
** \brief CAN FD notification pointer structure
**
** Holds some pointers to callback functions for status and error notification
*****************************************************************************/
typedef struct
{
cy_canfd_status_chg_func_ptr_t canFDStatusInterruptFunction ; /*!< pointer to status interrupt callback */
cy_canfd_error_func_ptr_t canFDErrorInterruptFunction ; /*!< pointer to error interrupt callback */
} cy_stc_canfd_notification_t;
/**
*****************************************************************************
** \brief CAN FD internal data structure
**
** Pointer to callback function interrupt handler
*****************************************************************************/
typedef struct
{
cy_stc_canfd_interrupt_handling_t canFDInterruptHandling; /*!< Interrupt callback. See #stc_canfd_interrupt_handling_t. */
cy_stc_canfd_notification_t canFDNotificationCb; /*!< Interrupt callback. See #stc_canfd_notification_t. */
} cy_stc_canfd_context_t;
/** \} group_canfd_data_structures */
/**
*****************************************************************************
** \brief CAN FD Unit Type
*****************************************************************************/
typedef volatile stc_CANFD_CH_t* cy_pstc_canfd_type_t;
/***************************************
* Function Prototypes
***************************************/
/**
* \addtogroup group_canfd_functions
* \{
*/
void Cy_CANFD_IrqHandler( cy_pstc_canfd_type_t pstcCanFD );
cy_en_canfd_status_t Cy_CANFD_Init( cy_pstc_canfd_type_t pstcCanFD, const cy_stc_canfd_config_t* pstcConfig) ;
cy_en_canfd_status_t Cy_CANFD_DeInit(cy_pstc_canfd_type_t pstcCanFD);
cy_en_canfd_status_t Cy_CANFD_UpdateAndTransmitMsgBuffer( cy_pstc_canfd_type_t pstcCanFD, uint8_t u8MsgBuf, cy_stc_canfd_msg_t* pstcMsg ) ;
cy_en_canfd_tx_buffer_status_t Cy_CANFD_GetTxBufferStatus( cy_pstc_canfd_type_t pstcCanFD, uint8_t u8MsgBuf);
void Cy_CANFD_ExtractMsgFromRXBuffer(cy_stc_canfd_rx_buffer_t *pstcRxBufferAddr, cy_stc_canfd_msg_t *pstcCanFDmsg);
cy_en_canfd_status_t Cy_CANFD_ClockInit( cy_pstc_canfd_type_t pstcCanFD,
uint8_t u8CanFDPrescaler, // TODO: DENA. Clock SAS still not finalized. Will be done later
cy_en_canfd_clock_source_t enCanFDClockSource // TODO: DENA. Clock SAS still not finalized. Will be done later
) ;
/** \} group_canfd_functions */
#if defined(__cplusplus)
}
#endif
#endif /* CY_CANFD_H */
/** \} group_canfd */
/* [] END OF FILE */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,717 @@
/***************************************************************************//**
* \file cy_crypto_common.h
* \version 1.0
*
* \brief
* This file provides common constants and parameters
* for the Crypto driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#if !defined(CY_CRYPTO_COMMON_H)
#define CY_CRYPTO_COMMON_H
#include <stddef.h>
#include <stdbool.h>
#include "cy_device_headers.h"
#include "sysint/cy_sysint.h"
#include "syslib/cy_syslib.h"
#ifndef CY_IP_MXCRYPTO
#error "The CRYPTO driver is not supported on this device"
#endif
#if (CPUSS_CRYPTO_PRESENT == 1)
/** Driver major version */
#define CY_CRYPTO_DRV_VERSION_MAJOR (1)
/** Driver minor version */
#define CY_CRYPTO_DRV_VERSION_MINOR (0)
/**
* \addtogroup group_crypto_macros
* \{
*/
/** Defines Crypto_Sync blocking execution type parameter */
#define CY_CRYPTO_SYNC_BLOCKING (true)
/** Defines Crypto_Sync non-blocking execution type parameter */
#define CY_CRYPTO_SYNC_NON_BLOCKING (false)
/** Defines the Crypto DES key size (in bytes) */
#define CY_CRYPTO_DES_KEY_SIZE (8u)
/** Defines the Crypto TDES key size (in bytes) */
#define CY_CRYPTO_TDES_KEY_SIZE (24u)
/** Defines the Crypto AES block size (in bytes) */
#define CY_CRYPTO_AES_BLOCK_SIZE (16u)
/** Defines the Crypto AES key maximum size (in bytes) */
#define CY_CRYPTO_AES_128_KEY_SIZE (16u)
/** Defines the Crypto AES key maximum size (in bytes) */
#define CY_CRYPTO_AES_192_KEY_SIZE (24u)
/** Defines the Crypto AES key maximum size (in bytes) */
#define CY_CRYPTO_AES_256_KEY_SIZE (32u)
/** Defines size of the AES block, in four-byte words */
#define CY_CRYPTO_AES_BLOCK_SIZE_U32 (4UL)
#if ((CPUSS_CRYPTO_SHA1 == 1) || \
(CPUSS_CRYPTO_SHA2 == 1) || \
(CPUSS_CRYPTO_SHA3 == 1))
#define CPUSS_CRYPTO_SHA (1u)
#endif
#if (CPUSS_CRYPTO_SHA == 1)
/** Hash size for the SHA1 mode (in bytes) */
#define CY_CRYPTO_SHA1_DIGEST_SIZE (20u)
/** Hash size for the SHA224 mode (in bytes) */
#define CY_CRYPTO_SHA224_DIGEST_SIZE (28u)
/** Hash size for the SHA256 mode (in bytes) */
#define CY_CRYPTO_SHA256_DIGEST_SIZE (32u)
/** Hash size for the SHA384 mode (in bytes) */
#define CY_CRYPTO_SHA384_DIGEST_SIZE (48u)
/** Hash size for the SHA512 mode (in bytes) */
#define CY_CRYPTO_SHA512_DIGEST_SIZE (64u)
/** Hash size for the SHA512_224 mode (in bytes) */
#define CY_CRYPTO_SHA512_224_DIGEST_SIZE (28u)
/** Hash size for the SHA512_256 mode (in bytes) */
#define CY_CRYPTO_SHA512_256_DIGEST_SIZE (32u)
/** Hash size for the SHA3_224 mode (in bytes) */
#define CY_CRYPTO_SHA3_224_DIGEST_SIZE (224u/8u)
/** Hash size for the SHA3_256 mode (in bytes) */
#define CY_CRYPTO_SHA3_256_DIGEST_SIZE (256u/8u)
/** Hash size for the SHA3_384 mode (in bytes) */
#define CY_CRYPTO_SHA3_384_DIGEST_SIZE (384u/8u)
/** Hash size for the SHA3_512 mode (in bytes) */
#define CY_CRYPTO_SHA3_512_DIGEST_SIZE (512u/8u)
/** Hash size for the SHAKE128 mode (in bytes)
* SHAKE128 can generate variable size of the digest, but in the driver implementation
* only fixed length 256bits is supported.
*/
#define CY_CRYPTO_SHAKE128_DIGEST_SIZE (256u/8u)
/** Hash size for the SHAKE256 mode (in bytes)
* SHAKE256 can generate variable size of the digest, but in the driver implementation
* only fixed length 512bits is supported.
*/
#define CY_CRYPTO_SHAKE256_DIGEST_SIZE (512u/8u)
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
#if (CPUSS_CRYPTO_VU == 1)
/** Processed message size for the RSA 1024Bit mode (in bytes) */
#define CY_CRYPTO_RSA1024_MESSAGE_SIZE (128)
/** Processed message size for the RSA 1536Bit mode (in bytes) */
#define CY_CRYPTO_RSA1536_MESSAGE_SIZE (192)
/** Processed message size for the RSA 2048Bit mode (in bytes) */
#define CY_CRYPTO_RSA2048_MESSAGE_SIZE (256)
#endif /* #if (CPUSS_CRYPTO_VU == 1) */
/** Crypto Driver PDL ID */
#define CY_CRYPTO_ID CY_PDL_DRV_ID(0x0Cu)
/** \} group_crypto_macros */
/**
* \addtogroup group_crypto_config_structure
* \{
*/
/** The Crypto user callback function type.
Callback is called at the end of Crypto calculation. */
typedef void (*cy_crypto_callback_ptr_t)(void);
/** The Crypto configuration structure. */
typedef struct
{
/** Defines the IPC channel used for client-server data exchange */
uint32_t ipcChannel;
/** Specifies the IPC notifier channel (IPC interrupt structure number)
to notify server that data for the operation is prepared */
uint32_t acquireNotifierChannel;
/** Specifies the IPC notifier channel (IPC interrupt structure number)
to notify client that operation is complete and data is valid */
uint32_t releaseNotifierChannel;
/** Specifies the release notifier interrupt configuration. */
cy_stc_sysint_irq_t releaseNotifierConfig;
/** User callback function.
If this field is NOT NULL, it called when Crypto operation
is complete. */
cy_crypto_callback_ptr_t userCompleteCallback;
#if (CY_CPU_CORTEX_M0P)
/** Server-side user IRQ handler function, called when data for the
operation is prepared to process.
- This field cannot work because IRQ handler cannot change in operation.
If needed user IRQ handler, please update the Cy_Crypto_Server_GetDataHandler.
- If this field is NULL, server will use own interrupt handler
to get data.
- If this field is not NULL, server will call this interrupt handler.
This interrupt handler must call the
\ref Cy_Crypto_Server_GetDataHandler to get data to process.
Note: In the second case user should process data separately and
clear interrupt by calling \ref Cy_Crypto_Server_Process.
This model is used in the
multitasking environment (e.g. CyOS). */
cy_israddress userGetDataHandler;
/** Server-side user IRQ handler function, called when a Crypto hardware
error occurs (interrupt was raised).
- This field cannot work because IRQ handler cannot change in operation.
If needed user IRQ handler, please update the Cy_Crypto_Server_ErrorHandler.
- If this field is NULL - server will use own interrupt handler
for error processing.
- If this field is not NULL - server will call this interrupt handler.
This interrupt handler must call the
\ref Cy_Crypto_Server_ErrorHandler to clear the interrupt. */
cy_israddress userErrorHandler;
/** Specifies the prepared data notifier interrupt configuration. */
cy_stc_sysint_irq_t acquireNotifierConfig;
/** Specifies the hardware error processing interrupt configuration. */
cy_stc_sysint_irq_t cryptoErrorIntrConfig;
#endif
} cy_stc_crypto_config_t;
/** \} group_crypto_config_structure */
/**
* \addtogroup group_crypto_cli_data_structures
* \{
*/
#if (CPUSS_CRYPTO_VU == 1)
/** Structure for storing the RSA public key and additional coefficients to
accelerate RSA calculation.
RSA key contained from two fields:
- n - modulus part of the key
- e - exponent part of the key.
Other fields are accelerating coefficients and can be calculated by
\ref Cy_Crypto_Rsa_CalcCoefs.
*/
typedef struct
{
/** The pointer to the modulus part of public key. */
void const *moduloPtr;
/** The modulus length, in bits, maximum supported size is 2048Bit */
uint32_t moduloLength;
/** The pointer to the exponent part of public key */
void const *pubExpPtr;
/** The exponent length, in bits, maximum supported size is 256Bit */
uint32_t pubExpLength;
/** The pointer to the Barrett coefficient. Memory for it should be
allocated by user with size moduloLength + 1. */
void *barretCoefPtr;
/** The pointer to the binary inverse of the modulo. Memory for it
should be allocated by user with size moduloLength. */
void *inverseModuloPtr;
/** The pointer to the (2^moduloLength mod modulo). Memory for it should
be allocated by user with size moduloLength */
void *rBarPtr;
} cy_stc_crypto_rsa_pub_key_t;
#endif /* #if (CPUSS_CRYPTO_VU == 1) */
/** Structure for storing a description of a Crypto hardware error */
typedef struct
{
/**
Captures error description information:
for INSTR_OPC_ERROR: - Violating instruction (from instruction FIFO).
for INSTR_CC_ERROR : - Violating instruction (from instruction FIFO).
for BUS_ERROR : - violating the transfer address. */
uint32_t errorStatus0;
/**
[31] - Specifies if ERROR_STATUS0 and ERROR_STATUS1 capture valid
error-information.
[26..24] - The error source:
"0": INSTR_OPC_ERROR (instuction FIFO decoder error).
"1": INSTR_CC_ERROR (instruction FIFO decoder, VU CC error).
"2": BUS_ERROR (bus master interface AHB-Lite bus error).
"3": TR_AP_DETECT_ERROR.
"4": TR_RC_DETECT_ERROR.
[23..0] - Captures error description information.
For BUS_ERROR:
- violating the transfer, read the attribute (DATA23[0]).
- violating the transfer, the size attribute (DATA23[5:4]).
"0": an 8-bit transfer;
"1": 16 bits transfer;
"2": 32-bit transfer. */
uint32_t errorStatus1;
} cy_stc_crypto_hw_error_t;
/** \} group_crypto_cli_data_structures */
/** The Crypto library functionality level. */
typedef enum
{
CY_CRYPTO_NO_LIBRARY = 0x00u,
CY_CRYPTO_BASE_LIBRARY = 0x01u,
CY_CRYPTO_EXTRA_LIBRARY = 0x02u,
CY_CRYPTO_FULL_LIBRARY = 0x03u,
} cy_en_crypto_lib_info_t;
/**
* \addtogroup group_crypto_enums
* \{
*/
#if (CPUSS_CRYPTO_AES == 1)
/** The key length options for the AES method. */
typedef enum
{
CY_CRYPTO_KEY_AES_128 = 0x00u, /**< The AES key size is 128 bits */
CY_CRYPTO_KEY_AES_192 = 0x01u, /**< The AES key size is 192 bits */
CY_CRYPTO_KEY_AES_256 = 0x02u /**< The AES key size is 256 bits */
} cy_en_crypto_aes_key_length_t;
#endif /* #if (CPUSS_CRYPTO_AES == 1) */
/** Defines the direction of the Crypto methods */
typedef enum
{
/** The forward mode, plain text will be encrypted into cipher text */
CY_CRYPTO_ENCRYPT = 0x00u,
/** The reverse mode, cipher text will be decrypted into plain text */
CY_CRYPTO_DECRYPT = 0x01u
} cy_en_crypto_dir_mode_t;
#if (CPUSS_CRYPTO_SHA == 1)
/** Defines modes of SHA method */
typedef enum
{
#if (CPUSS_CRYPTO_SHA1 == 1)
CY_CRYPTO_MODE_SHA1 = 0x00u, /**< Sets the SHA1 mode */
#endif /* #if (CPUSS_CRYPTO_SHA1 == 1) */
#if (CPUSS_CRYPTO_SHA2 == 1)
CY_CRYPTO_MODE_SHA224 = 0x11u, /**< Sets the SHA224 mode */
CY_CRYPTO_MODE_SHA256 = 0x01u, /**< Sets the SHA256 mode */
CY_CRYPTO_MODE_SHA384 = 0x12u, /**< Sets the SHA384 mode */
CY_CRYPTO_MODE_SHA512 = 0x02u, /**< Sets the SHA512 mode */
CY_CRYPTO_MODE_SHA512_256 = 0x22u, /**< Sets the SHA512/256 mode */
CY_CRYPTO_MODE_SHA512_224 = 0x32u, /**< Sets the SHA512/224 mode */
#endif /* #if (CPUSS_CRYPTO_SHA2 == 1) */
#if (CPUSS_CRYPTO_SHA3 == 1)
CY_CRYPTO_MODE_SHA3_224 = 0x81u, /**< Sets the SHA3_224 mode */
CY_CRYPTO_MODE_SHA3_256 = 0x82u, /**< Sets the SHA3_256 mode */
CY_CRYPTO_MODE_SHA3_384 = 0x83u, /**< Sets the SHA3_384 mode */
CY_CRYPTO_MODE_SHA3_512 = 0x84u, /**< Sets the SHA3_512 mode */
CY_CRYPTO_MODE_SHAKE128 = 0x85u, /**< Sets the SHAKE128 mode */
CY_CRYPTO_MODE_SHAKE256 = 0x86u /**< Sets the SHAKE256 mode */
#endif /* #if (CPUSS_CRYPTO_SHA3 == 1) */
} cy_en_crypto_sha_mode_t;
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
/** Signature verification status */
typedef enum
{
CY_CRYPTO_RSA_VERIFY_SUCCESS = 0x00u, /**< PKCS1-v1.5 verify SUCCESS */
CY_CRYPTO_RSA_VERIFY_FAIL = 0x01u /**< PKCS1-v1.5 verify FAILED */
} cy_en_crypto_rsa_ver_result_t;
#if (CPUSS_CRYPTO_CHACHA == 1)
/** ChaCha round number selection */
typedef enum
{
CY_CRYPTO_CHACHA8 = 8u / 2u, /**< CHACHA8 */
CY_CRYPTO_CHACHA12 = 12u / 2u, /**< CHACHA12 */
CY_CRYPTO_CHACHA20 = 20u / 2u /**< CHACHA20 */
} cy_en_crypto_chacha_round_t;
#endif /* #if (CPUSS_CRYPTO_CHACHA == 1) */
/** Errors of the Crypto block */
typedef enum
{
/** Operation completed successfully. */
CY_CRYPTO_SUCCESS = 0x00u,
/** A hardware error occurred, detailed information is in stc_crypto_hw_error_t. */
CY_CRYPTO_HW_ERROR = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x01u,
/** The size of input data is not multiple of 16. */
CY_CRYPTO_SIZE_NOT_X16 = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x02u,
/** The key for the DES method is weak. */
CY_CRYPTO_DES_WEAK_KEY = CY_CRYPTO_ID | CY_PDL_STATUS_WARNING | 0x03u,
/** Communication between the client and server via IPC is broken. */
CY_CRYPTO_COMM_FAIL = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x04u,
/** The Crypto server is not started. */
CY_CRYPTO_SERVER_NOT_STARTED = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x06u,
/** The Crypto server in process state. */
CY_CRYPTO_SERVER_BUSY = CY_CRYPTO_ID | CY_PDL_STATUS_INFO | 0x07u,
/** The Crypto driver is not initialized. */
CY_CRYPTO_NOT_INITIALIZED = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x08u,
/** The Crypto hardware is not enabled. */
CY_CRYPTO_HW_NOT_ENABLED = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x09u,
/** The Crypto operation is not supported. */
CY_CRYPTO_NOT_SUPPORTED = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x0Au,
/** Invalid parameter is passed */
CY_CRYPTO_BAD_PARAM = CY_CRYPTO_ID | CY_PDL_STATUS_ERROR | 0x0Bu
} cy_en_crypto_status_t;
/** \} group_crypto_enums */
/** \cond INTERNAL */
/** Instruction to communicate between Client and Server */
typedef enum
{
CY_CRYPTO_INSTR_UNKNOWN = 0x00u,
CY_CRYPTO_INSTR_ENABLE = 0x01u,
CY_CRYPTO_INSTR_DISABLE = 0x02u,
#if (CPUSS_CRYPTO_PR == 1)
CY_CRYPTO_INSTR_PRNG_INIT = 0x03u,
CY_CRYPTO_INSTR_PRNG = 0x04u,
#endif /* #if (CPUSS_CRYPTO_PR == 1) */
#if (CPUSS_CRYPTO_TR == 1)
CY_CRYPTO_INSTR_TRNG_INIT = 0x05u,
CY_CRYPTO_INSTR_TRNG = 0x06u,
#endif /* #if (CPUSS_CRYPTO_PR == 1) */
#if (CPUSS_CRYPTO_AES == 1)
CY_CRYPTO_INSTR_AES_ECB = 0x08u,
CY_CRYPTO_INSTR_AES_CBC = 0x09u,
CY_CRYPTO_INSTR_AES_CFB = 0x0Au,
CY_CRYPTO_INSTR_AES_CTR = 0x0Bu,
CY_CRYPTO_INSTR_CMAC = 0x0Cu,
#endif /* #if (CPUSS_CRYPTO_AES == 1) */
#if (CPUSS_CRYPTO_SHA == 1)
CY_CRYPTO_INSTR_SHA = 0x0Du,
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
#if (CPUSS_CRYPTO_SHA == 1)
CY_CRYPTO_INSTR_HMAC = 0x0Eu,
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
#if (CPUSS_CRYPTO_CRC == 1)
CY_CRYPTO_INSTR_CRC_INIT = 0x13u,
CY_CRYPTO_INSTR_CRC = 0x14u,
#endif /* #if (CPUSS_CRYPTO_CRC == 1) */
#if (CPUSS_CRYPTO_DES == 1)
CY_CRYPTO_INSTR_DES = 0x15u,
CY_CRYPTO_INSTR_3DES = 0x16u,
#endif /* #if (CPUSS_CRYPTO_DES == 1) */
#if (CPUSS_CRYPTO_VU == 1)
CY_CRYPTO_INSTR_RSA_PROC = 0x17u,
CY_CRYPTO_INSTR_RSA_COEF = 0x18u,
#endif /* #if (CPUSS_CRYPTO_VU == 1)*/
#if (CPUSS_CRYPTO_SHA == 1)
CY_CRYPTO_INSTR_RSA_VER = 0x19u,
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
#if (CPUSS_CRYPTO_CHACHA == 1)
CY_CRYPTO_INSTR_CHACHA = 0x1Au,
#endif /* #if (CPUSS_CRYPTO_CHACHA == 1) */
CY_CRYPTO_INSTR_SRV_INFO = 0x55u
} cy_en_crypto_comm_instr_t;
/** \endcond */
/**
* \addtogroup group_crypto_cli_data_structures
* \{
*/
#if (CPUSS_CRYPTO_AES == 1)
/** Structure for storing the AES state */
typedef struct
{
/** Pointer to AES key */
void const *keyPtr;
/** AES key length */
cy_en_crypto_aes_key_length_t keyLength;
/** AES processed block index (for CMAC operations) */
uint32_t blockIdx;
} cy_stc_crypto_aes_state_t;
#endif /* #if (CPUSS_CRYPTO_AES == 1) */
/** \} group_crypto_cli_data_structures */
/*************************************************************
* Structures used for communication between Client and Server
***************************************************************/
/**
* \addtogroup group_crypto_srv_data_structures
* \{
*/
/** Structure for storing the server context */
typedef struct
{
/** IPC communication channel number */
uint32_t ipcChannel;
/** IPC acquire interrupt channel number */
uint32_t acquireNotifierChannel;
/** IPC release interrupt channel number */
uint32_t releaseNotifierChannel;
/** Interrupt handler to get processing data */
cy_israddress getDataHandlerPtr;
/** Crypto hardware errors interrupt handler */
cy_israddress errorHandlerPtr;
/** Acquire notifier interrupt configuration */
cy_stc_sysint_irq_t acquireNotifierConfig;
/** Crypto hardware errors interrupt configuration */
cy_stc_sysint_irq_t cryptoErrorIntrConfig;
/** Hardware error occurrence flag */
bool isHwErrorOccured;
} cy_stc_crypto_server_context_t;
/** \} group_crypto_srv_data_structures */
/**
* \addtogroup group_crypto_cli_data_structures
* \{
*/
/** Structure for storing the global context */
typedef struct
{
/** Operation instruction code */
cy_en_crypto_comm_instr_t instr;
/** Response from executed crypto function */
cy_en_crypto_status_t resp;
/** Hardware processing errors */
cy_stc_crypto_hw_error_t hwErrorStatus;
/** IPC communication channel number */
uint32_t ipcChannel;
/** IPC acquire interrupt channel number */
uint32_t acquireNotifierChannel;
/** IPC release interrupt channel number */
uint32_t releaseNotifierChannel;
/** User callback for Crypto HW calculation complete event */
cy_crypto_callback_ptr_t userCompleteCallback;
/** Release notifier interrupt configuration */
cy_stc_sysint_irq_t releaseNotifierConfig;
/** Pointer to the crypto function specific context data */
void *xdata;
} cy_stc_crypto_context_t;
#if (CPUSS_CRYPTO_DES == 1)
/** Structure for storing the DES context */
typedef struct
{
/** Operation direction (Encrypt / Decrypt) */
cy_en_crypto_dir_mode_t dirMode;
/** Pointer to key data */
void const *keyPtr;
/** Pointer to data destination block */
void *dstPtr;
/** Pointer to data source block */
void const *srcPtr;
} cy_stc_crypto_context_des_t;
#endif /* #if (CPUSS_CRYPTO_DES == 1) */
#if (CPUSS_CRYPTO_AES == 1)
/** Structure for storing the AES context */
typedef struct
{
/** AES state data */
cy_stc_crypto_aes_state_t aesState;
/** Operation direction (Encrypt / Decrypt) */
cy_en_crypto_dir_mode_t dirMode;
/** Operation data size */
uint32_t srcSize;
/** Size of the last non-complete block (for CTR mode only)
* Other than CTR mode can be set to NULL.
* For the first sequence, *srcOffset value should be 0.
* This parameter is updated by CRYPTO module.
* For the first call for a sequence, this field shall be initialized
* to '0'. For the trailing call for the sequence,
* it shall not be initialized.
*/
uint32_t *srcOffset;
/** Initialization vector, in the CTR mode is used as nonceCounter
* In the CTR mode, counter value is incremented by the API.
* For the first call for a sequence, this field shall be initialized
* required nonceCounter value. For the trailing call for the sequence,
* it shall not be initialized.
*/
void *ivPtr;
/** Pointer to data destination block */
void *dstPtr;
/** Pointer to data source block */
void const *srcPtr;
} cy_stc_crypto_context_aes_t;
#endif /* #if (CPUSS_CRYPTO_AES == 1) */
#if (CPUSS_CRYPTO_SHA == 1)
/** Structure for storing the SHA context */
typedef struct
{
/** Pointer to data source block */
void const *messagePtr;
/** Operation data size */
uint32_t messageSize;
/** Pointer to data destination block */
void *dstPtr;
/** SHA mode */
cy_en_crypto_sha_mode_t mode;
/** Pointer to key data (for HMAC only) */
void const *keyPtr;
/** Key data length (for HMAC only) */
uint32_t keyLength;
} cy_stc_crypto_context_sha_t;
#endif /* #if (CPUSS_CRYPTO_SHA == 1) */
#if (CPUSS_CRYPTO_PR == 1)
/** Structure for storing the PRNG context */
typedef struct
{
uint32_t lfsr32InitState; /**< lfsr32 initialization data (must be none zero) */
uint32_t lfsr31InitState; /**< lfsr31 initialization data (must be none zero) */
uint32_t lfsr29InitState; /**< lfsr29 initialization data (must be none zero) */
uint32_t max; /**< Maximum of the generated value */
uint32_t *prngNumPtr; /**< Pointer to generated value */
} cy_stc_crypto_context_prng_t;
#endif /* #if (CPUSS_CRYPTO_PR == 1) */
#if (CPUSS_CRYPTO_TR == 1)
/** Structure for storing the TRNG context */
typedef struct
{
/**
The polynomial for the programmable Galois ring oscillator (TR_GARO_CTL).
The polynomial is represented WITHOUT the high order bit (this bit is
always assumed '1').
The polynomial should be aligned so that more significant bits
(bit 30 and down) contain the polynomial and less significant bits
(bit 0 and up) contain padding '0's. */
uint32_t GAROPol;
/**
The polynomial for the programmable Fibonacci ring oscillator(TR_FIRO_CTL).
The polynomial is represented WITHOUT the high order bit (this bit is
always assumed '1').
The polynomial should be aligned so that more significant bits
(bit 30 and down) contain the polynomial and less significant bits
(bit 0 and up) contain padding '0's. */
uint32_t FIROPol;
/** Bit size of the generated value. Valid range is [0,32] */
uint8_t bitSize;
/** Pointer to generated value */
uint32_t *trngNumPtr;
} cy_stc_crypto_context_trng_t;
#endif /* #if (CPUSS_CRYPTO_TR == 1) */
#if (CPUSS_CRYPTO_CRC == 1)
/** Structure for storing the CRC context */
typedef struct
{
void const *srcDataPtr; /**< Pointer to data source block */
uint32_t dataSize; /**< operation data size */
uint32_t *crcPtr; /**< Pointer to CRC destination variable */
uint32_t polynomial; /**< Polynomial for CRC calculate */
uint32_t lfsrInitState; /**< CRC calculation initial value */
uint32_t dataReverse; /**< Input data reverse flag */
uint32_t dataXor; /**< Input data XOR flag */
uint32_t remReverse; /**< Output data reverse flag */
uint32_t remXor; /**< Output data XOR flag */
} cy_stc_crypto_context_crc_t;
#endif /* #if (CPUSS_CRYPTO_CRC == 1) */
/** Structure for storing the RSA verify context */
typedef struct
{
/** Pointer to verification result /ref cy_en_crypto_rsa_ver_result_t */
cy_en_crypto_rsa_ver_result_t *verResult;
/** SHA digest type, used with SHA calculation of the message */
cy_en_crypto_sha_mode_t digestType;
/** SHA digest of the message, calculated with digestType */
void const *hashPtr;
/** Previously decrypted RSA signature */
void const *decryptedSignaturePtr;
/** Length of the decrypted RSA signature */
uint32_t decryptedSignatureLength;
} cy_stc_crypto_context_rsa_ver_t;
#if (CPUSS_CRYPTO_VU == 1)
/** Structure for storing the RSA process context */
typedef struct
{
/** Pointer to key data */
cy_stc_crypto_rsa_pub_key_t const *keyPtr;
/** Pointer to data source block */
void const *messagePtr;
/** Operation data size */
uint32_t messageLength;
/** Pointer to data destination block */
void *resultPtr;
} cy_stc_crypto_context_rsa_t;
#endif /* #if (CPUSS_CRYPTO_VU == 1)*/
#if (CPUSS_CRYPTO_CHACHA == 1)
/** Structure for storing the CHACHA context */
typedef struct
{
/** Operation data size */
uint32_t srcSize;
/** Pointer to data destination block */
void *dstPtr;
/** Pointer to data source block */
void const *srcPtr;
/** Initial nonce value (only 96bits is supported in the driver.) */
void const *noncePtr;
/** Initial counter value */
uint32_t counter;
/** Pointer to CHACHA key (Only 256bits is supported in the driver). */
void const *keyPtr;
/** half of a round number. Refer \ref cy_en_crypto_chacha_round_t */
cy_en_crypto_chacha_round_t roundNum;
} cy_stc_crypto_context_chacha_t;
#endif /* #if (CPUSS_CRYPTO_CHACHA == 1) */
/** \} group_crypto_data_structures */
/** \} group_crypto_cli_data_structures */
#endif /* (CPUSS_CRYPTO_PRESENT == 1) */
#endif /* #if !defined(CY_CRYPTO_COMMON_H) */
/* [] END OF FILE */
@@ -0,0 +1,59 @@
/***************************************************************************//**
* \file
* \version `$CY_MAJOR_VERSION`.`$CY_MINOR_VERSION`
*
* Description:
* This C file is not intended to be part of the Crypto driver. It is the code
* required to configure the crypto driver by user.
*
********************************************************************************
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#include "cy_crypto_config.h"
#include "ipc/cy_ipc_drv.h"
#include "sysint/cy_sysint.h"
/** The Crypto configuration structure. (default example)*/
const cy_stc_crypto_config_t cryptoConfig =
{
.ipcChannel = CY_IPC_CHAN_CRYPTO,
.acquireNotifierChannel = CY_CRYPTO_IPC_INTR_NOTIFY_NUM,
.releaseNotifierChannel = CY_CRYPTO_IPC_INTR_RELEASE_NUM,
.userCompleteCallback = NULL,
.releaseNotifierConfig = {
.sysIntSrc = (cy_en_intr_t)((uint32_t)cpuss_interrupts_ipc_0_IRQn + CY_CRYPTO_IPC_INTR_RELEASE_NUM),
.intIdx = CY_CRYPTO_RELEASE_CPU_INT_IDX,
.isEnabled = true
}
#if (CY_CPU_CORTEX_M0P)
,
.userGetDataHandler = NULL,
.userErrorHandler = NULL,
.acquireNotifierConfig = {
.sysIntSrc = (cy_en_intr_t)((uint32_t)cpuss_interrupts_ipc_0_IRQn + CY_CRYPTO_IPC_INTR_NOTIFY_NUM),
.intIdx = CY_CRYPTO_NOTIFY_CPU_INT_IDX,
.isEnabled = true
},
.cryptoErrorIntrConfig = {
.sysIntSrc = cpuss_interrupt_crypto_IRQn,
.intIdx = CY_CRYPTO_ERROR_CPU_INT_IDX,
.isEnabled = true
}
#endif
};
/* [] END OF FILE */
@@ -0,0 +1,59 @@
/***************************************************************************//**
* \file cy_crypto_config.h
* \version 1.0
*
* \brief
* This file provides user parameters for the Crypto driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#if !defined(CY_CRYPTO_CONFIG_H)
#define CY_CRYPTO_CONFIG_H
#include <stddef.h>
#include <stdbool.h>
#include "cy_device_headers.h"
#include "crypto/cy_crypto_common.h"
/* Defines to configure Crypto driver */
extern const cy_stc_crypto_config_t cryptoConfig;
/**
* \addtogroup group_crypto_config_macros
* \{
*
* These constants defines an interrupts settings for IPC channel used for the
* client-server communication, and the Crypto Error IRQ
*
* Both the CM4/CM7_x and CM0+ uses IPC hardware depended interrupt sources
* through SYSINT structure.
*/
/** Defines the Crypto notify IPC interrupt number */
#define CY_CRYPTO_IPC_INTR_NOTIFY_NUM CY_IPC_INTR_CRYPTO_SRV
/** Defines the Crypto release IPC interrupt number */
#define CY_CRYPTO_IPC_INTR_RELEASE_NUM CY_IPC_INTR_CRYPTO_CLI
/** Defines the CPU Interrupt Index for the Crypto IPC server notify interrupt */
#define CY_CRYPTO_NOTIFY_CPU_INT_IDX (CPUIntIdx3_IRQn)
/** Defines the CPU Interrupt Index for the Crypto IPC client release interrupt */
#define CY_CRYPTO_RELEASE_CPU_INT_IDX (CPUIntIdx3_IRQn)
/** Defines the CPU Interrupt Index for the Crypto Error interrupt */
#define CY_CRYPTO_ERROR_CPU_INT_IDX (CPUIntIdx3_IRQn)
/** \} group_crypto_config_macros */
#endif /* #if !defined(CY_CRYPTO_CONFIG_H) */
/* [] END OF FILE */
@@ -0,0 +1,117 @@
/***************************************************************************//**
* \file cy_crypto_server.h
* \version 1.0
*
* \brief
* This file provides the prototypes for common API
* in the Crypto driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#if !defined(CY_CRYPTO_SERVER_H)
#define CY_CRYPTO_SERVER_H
#include "crypto/cy_crypto_common.h"
#include "syslib/cy_syslib.h"
#if (CY_CPU_CORTEX_M0P)
#if (CPUSS_CRYPTO_PRESENT == 1)
/**
* \addtogroup group_crypto_srv_functions
* \{
*/
/*******************************************************************************
* Function Name: Cy_Crypto_Server_Start
****************************************************************************//**
*
* This function starts the Crypto server on the CM0+ core, sets up an interrupt
* for the IPC Crypto channel on the CM0+ core, sets up an interrupt
* to catch Crypto HW errors. Should be invoked only on CM0.
*
* This function available for CM0+ core only.
*
* \param config
* The Crypto configuration structure.
*
* \param context
* The pointer to the \ref cy_stc_crypto_server_context_t structure that stores
* the Crypto server context.
*
* \return
* A Crypto status \ref cy_en_crypto_status_t.
*
*******************************************************************************/
cy_en_crypto_status_t Cy_Crypto_Server_Start(cy_stc_crypto_config_t const *config,
cy_stc_crypto_server_context_t *context);
/*******************************************************************************
* Function Name: Cy_Crypto_Server_Stop
****************************************************************************//**
*
* This function stops the Crypto server by disabling the IPC notify interrupt
* and Crypto error interrupt. Should be invoked only on CM0.
*
* This function available for CM0+ core only.
*
* \return
* A Crypto status \ref cy_en_crypto_status_t.
*
*******************************************************************************/
cy_en_crypto_status_t Cy_Crypto_Server_Stop(void);
/*******************************************************************************
* Function Name: Cy_Crypto_Server_Process
****************************************************************************//**
*
* This function parses input data received from the Crypto Client,
* runs the appropriate Crypto function and releases the Crypto IPC channel.
*
* This function available for CM0+ core only.
*
*******************************************************************************/
void Cy_Crypto_Server_Process(void);
/*******************************************************************************
* Function Name: Cy_Crypto_Server_GetDataHandler
****************************************************************************//**
*
* This function is a IPC Crypto channel notify interrupt-routine.
* It receives information from the Crypto client,
* runs the process if user not setup own handler.
*
* This function available for CM0+ core only.
*
*******************************************************************************/
void Cy_Crypto_Server_GetDataHandler(void);
/*******************************************************************************
* Function Name: Cy_Crypto_Server_ErrorHandler
****************************************************************************//**
*
* This function is a routine to handle an interrupt caused by the Crypto hardware error.
*
* This function available for CM0+ core only.
*
*******************************************************************************/
void Cy_Crypto_Server_ErrorHandler(void);
/** \} group_crypto_srv_functions */
#endif /* #if (CPUSS_CRYPTO_PRESENT == 1) */
#endif /* #if (CY_CPU_CORTEX_M0P) */
#endif /* #if !defined(CY_CRYPTO_SERVER_H) */
/* [] END OF FILE */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,521 @@
/***************************************************************************//**
* \file cxpi.h
* \version 1.0
*
* \brief
* Provides an API declaration of the CXPI driver
*
* \note
* Generator version: draft verision
* Database revision: https://svn.design.cypress.com:18080/svn/chips/trunk/s40/MXS40/Platform/MXS40-SAS/Configuration/MXS40-IP-CXPI.xlsm
[rev.185188]
*
********************************************************************************
* \copyright
* Copyright 2017-2018, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
/**
* \defgroup group_cxpi Clock eXtension Peripheral Interface (CXPI)
* \{
* This driver provides global CXPI defines and API function definitions.
*
* \defgroup group_cxpi_macros Macro
* \defgroup group_cxpi_functions Functions
* \defgroup group_cxpi_data_structures Data structures
* \defgroup group_cxpi_enums Enumerated types
*/
#if !defined(CY_CXPI_H)
#define CY_CXPI_H
/*****************************************************************************/
/* Include files */
/*****************************************************************************/
#include <stddef.h>
#include "syslib/cy_syslib.h"
#include "cy_device_headers.h"
#if defined(__cplusplus)
extern "C" {
#endif
/**
* \addtogroup group_cxpi_macros
* \{
*/
/*****************************************************************************/
/* Global pre-processor symbols/macros ('#define') */
/*****************************************************************************/
/** Driver major version */
#define CY_CXPI_DRV_VERSION_MAJOR 1
/** Driver minor version */
#define CY_CXPI_DRV_VERSION_MINOR 0
/** CXPI driver ID */
#define CY_CXPI_ID CY_PDL_DRV_ID(0x37u) //need modified
/** \} group_cxpi_macros */
/**
* \addtogroup group_cxpi_enums
* \{
*/
/** CXPI API status definition */
typedef enum
{
CY_CXPI_SUCCESS = 0x00u, /**< Returned successful */
//CY_CXPI_BAD_PARAM = CY_CXPI_ID | CY_PDL_STATUS_ERROR | 0x01u, /**< Bad parameter was passed */
CY_CXPI_BAD_PARAM = 0x01u,
//CY_CXPI_BUSY = CY_CXPI_ID | CY_PDL_STATUS_ERROR | 0x02u, /**< Change settings while tx/rx on-going */
CY_CXPI_BUSY = 0x02u,
//CY_CXPI_FRAME_NOT_RECEIVED = CY_CXPI_ID | CY_PDL_STATUS_ERROR | 0x03u, /**< No frame received */
CY_CXPI_FRAME_NOT_RECEIVED = 0x03u,
} cy_en_cxpi_status_t;
/** \} group_cxpi_enums */
/**
* \addtogroup group_cxpi_macros
* \{
*/
/** Maximum reversed ID */
#define CXPI_REV_ID_MAX (0x79)
/** Maximum data length */
#define CXPI_DATA_LENGTH_MAX (255u)
/** Minimum data length */
#define CXPI_DATA_LENGTH_MIN (1u)
/** Maximum counter = 3 */
#define CXPI_COUNTER_MAX (3u)
/** Maximum time out = 10Tbits*/
#define CXPI_TIMEOUT_MAX (10u)
/** Maximum wake up pulse low period = 50Tbits*/
#define CXPI_T_WAKEUP_LENGTH_MAX (50u)
/** Minimum wake up pulse low period = 1Tbits*/
#define CXPI_T_WAKEUP_LENGTH_MIN (1u)
/** Maximum low count for logic "1" = 400 Tbits*/
#define CXPI_T_LOW1_MAX (400u)
/** Minimum low count for logic "1" = 1 Tbits*/
#define CXPI_T_LOW1_MIN (1u)
/** Maximim low count for logic "0" = 400 Tbits*/
#define CXPI_T_LOW0_MAX (400u)
/** Minimum low count for logic "0" = 1 Tbits*/
#define CXPI_T_LOW0_MIN (1u)
/** Maximim offset value = 400 Tbits*/
#define CXPI_T_OFFSET_MAX (400u)
/** Minimum offset value = 1 Tbits*/
#define CXPI_T_OFFSET_MIN (1u)
/** Maximum retries after arbitration lost = 3 times */
#define CXPI_RETRY_MAX (3u)
/**Maximum number of entries of TX_FIFO */
#define CXPI_TX_FIFO_ENTRIESNUM_MAX (16u)
/**Maximum number of entries of RX_FIFO */
#define CXPI_RX_FIFO_ENTRIESNUM_MAX (16u)
/** Hardware TX_FIFO size */
#define CXPI_TX_FIFO_SIZE (16u)
/** Hardware RX_FIFO size */
#define CXPI_RX_FIFO_SIZE (16u)
#define CXPI_PTYPE (0x0)
#define CXPI_SLEEP_MODE (0x0)
#define CXPI_STANDBY_MODE (0x1)
#define CXPI_NORMAL_MODE (0x2)
/**
* Specifies the parameter values passed to CXPI command API
*/
#define CXPI_CMD_TX_HEADER (CXPI_CH_CMD_TX_HEADER_Msk)
#define CXPI_CMD_RX_HEADER (CXPI_CH_CMD_RX_HEADER_Msk)
#define CXPI_CMD_TX_HEADER_RX_HEADER (CXPI_CH_CMD_TX_HEADER_Msk | CXPI_CH_CMD_RX_HEADER_Msk)
#define CXPI_CMD_TX_RESPONSE (CXPI_CH_CMD_TX_RESPONSE_Msk)
#define CXPI_CMD_RX_RESPONSE (CXPI_CH_CMD_RX_RESPONSE_Msk)
#define CXPI_CMD_TX_HEADER_TX_RESPONSE (CXPI_CH_CMD_TX_HEADER_Msk | CXPI_CH_CMD_TX_RESPONSE_Msk)
#define CXPI_CMD_TX_HEADER_RX_RESPONSE (CXPI_CH_CMD_TX_HEADER_Msk | CXPI_CH_CMD_RX_RESPONSE_Msk)
#define CXPI_CMD_RX_HEADER_RX_RESPONSE (CXPI_CH_CMD_RX_HEADER_Msk | CXPI_CH_CMD_RX_RESPONSE_Msk)
#define CXPI_CMD_TX_WAKEUP_PULSE (CXPI_CH_CMD_TX_WAKEUP_PULSE_Msk)
#define CXPI_CMD_SLEEP (CXPI_CH_CMD_SLEEP_Msk)
#define CXPI_CMD_WAKE_TO_STANDBY (CXPI_CH_CMD_WAKE_TO_STANDBY_Msk)
#define CXPI_CMD_IFS_WAIT (CXPI_CH_CMD_IFS_WAIT_Msk)
/** \} group_cxpi_cmd_macro */
/**
* Specifies the mask value for interrupt status/mask
*/
#define CXPI_INTR_TX_HEADER_DONE (CXPI_CH_INTR_TX_HEADER_DONE_Msk)
#define CXPI_INTR_TX_RESPONSE_DONE (CXPI_CH_INTR_TX_RESPONSE_DONE_Msk)
#define CXPI_INTR_TX_WAKEUP_DONE (CXPI_CH_INTR_TX_WAKEUP_DONE_Msk)
#define CXPI_INTR_TX_FIFO_TRIGGER (CXPI_CH_INTR_TX_FIFO_TRIGGER_Msk)
#define CXPI_INTR_RX_HEADER_DONE (CXPI_CH_INTR_RX_HEADER_DONE_Msk)
#define CXPI_INTR_RX_RESPONSE_DONE (CXPI_CH_INTR_RX_RESPONSE_DONE_Msk)
#define CXPI_INTR_RX_WAKEUP_DETECT (CXPI_CH_INTR_RX_WAKEUP_DETECT_Msk)
#define CXPI_INTR_RX_FIFO_TRIGGER (CXPI_CH_INTR_RX_FIFO_TRIGGER_Msk)
#define CXPI_INTR_RX_HEADER_PID_DONE (CXPI_CH_INTR_RX_HEADER_PID_DONE_Msk)
#define CXPI_INTR_TXRX_COMPLETE (CXPI_CH_INTR_TXRX_COMPLETE_Msk)
#define CXPI_INTR_TIMEOUT (CXPI_CH_INTR_TIMEOUT_Msk)
#define CXPI_INTR_TX_HEADER_ARB_LOST (CXPI_CH_INTR_TX_HEADER_ARB_LOST_Msk)
#define CXPI_INTR_TX_BIT_ERROR (CXPI_CH_INTR_TX_BIT_ERROR_Msk)
#define CXPI_INTR_RX_CRC_ERROR (CXPI_CH_INTR_RX_CRC_ERROR_Msk)
#define CXPI_INTR_RX_HEADER_PARITY_ERROR (CXPI_CH_INTR_RX_HEADER_PARITY_ERROR_Msk)
#define CXPI_INTR_RX_DATA_LENGTH_ERROR (CXPI_CH_INTR_RX_DATA_LENGTH_ERROR_Msk)
#define CXPI_INTR_TX_DATA_LENGTH_ERROR (CXPI_CH_INTR_TX_DATA_LENGTH_ERROR_Msk)
#define CXPI_INTR_RX_OVERFLOW_ERROR (CXPI_CH_INTR_RX_OVERFLOW_ERROR_Msk)
#define CXPI_INTR_TX_OVERFLOW_ERROR (CXPI_CH_INTR_TX_OVERFLOW_ERROR_Msk)
#define CXPI_INTR_RX_UNDERFLOW_ERROR (CXPI_CH_INTR_RX_UNDERFLOW_ERROR_Msk)
#define CXPI_INTR_TX_UNDERFLOW_ERROR (CXPI_CH_INTR_TX_UNDERFLOW_ERROR_Msk)
#define CXPI_INTR_RX_FRAME_ERROR (CXPI_CH_INTR_RX_FRAME_ERROR_Msk)
#define CXPI_INTR_TX_FRAME_ERROR (CXPI_CH_INTR_TX_FRAME_ERROR_Msk)
#define CXPI_INTR_ALL_ERROR_MASK (CXPI_CH_INTR_TX_BIT_ERROR_Msk |\
CXPI_CH_INTR_RX_CRC_ERROR_Msk |\
CXPI_CH_INTR_RX_HEADER_PARITY_ERROR_Msk |\
CXPI_CH_INTR_RX_DATA_LENGTH_ERROR_Msk |\
CXPI_CH_INTR_TX_DATA_LENGTH_ERROR_Msk |\
CXPI_CH_INTR_RX_OVERFLOW_ERROR_Msk |\
CXPI_CH_INTR_TX_OVERFLOW_ERROR_Msk |\
CXPI_CH_INTR_RX_UNDERFLOW_ERROR_Msk |\
CXPI_CH_INTR_TX_UNDERFLOW_ERROR_Msk |\
CXPI_CH_INTR_RX_FRAME_ERROR_Msk |\
CXPI_CH_INTR_TX_FRAME_ERROR_Msk)
/** \} group_cxpi_macros */
/*****************************************************************************/
/* Global type definitions ('typedef') */
/*****************************************************************************/
/**
* \addtogroup group_cxpi_enums
* \{
*/
/**
*****************************************************************************
** \brief CXPI mode of operation
**
** This configuration is effective only when corresponding channel = master mode.
*****************************************************************************/
typedef enum en_cxpi_mode{
CXPI_NRZ_Mode = 0, /*!< NRZ mode */
CXPI_PWM_Mode = 1, /*!< PWM mode */
}en_cxpi_mode_t;
typedef enum en_bit_error_ignore{
MESSAGE_TRANFER_ABORTED = 0, /*!< Message transfer is aborted. */
MESSAGE_TRANFER_NOT_ABORTED = 1, /*!< Message transfer is NOT aborted. */
}en_bit_error_ignore_t;
typedef enum en_master_mode{
CXPI_SLAVE_MODE = 0, /*!< Indicates CXPI as slave node. */
CXPI_MASTER_MODE = 1, /*!< Indicates CXPI as master node. */
}en_master_mode_t;
/**
*****************************************************************************
** \brief number of retries after arbitration lost
**
*****************************************************************************/
typedef enum en_cxpi_retries
{
CxpiNoRetries = 0, /*!< no retries */
CxpiRetriesOneTime = 1, /*!< 1 retries */
CxpiRetriesTwoTime = 2, /*!< 2 retries */
CxpiRetriesThreeTime = 3 /*!< 3 retries */
}en_cxpi_retries_t;
/**
*****************************************************************************
** \brief timeout selection.
**
*****************************************************************************/
typedef enum en_cxpi_timeout_sel
{
CxpiTimeoutDisabled = 0, /*!< Timeout check is disabled */
CxpiTimeoutCheckHdrRes = 1, /*!< Timeout check is enabled and HW will refer to TIMEOUT_LENGTH as number of Tbits allowed between header and response */
CxpiTimeoutCheckAllByte = 2, /*!< Timeout check is enabled to check all bytes within a message frame to be space within TIMEOUT_LENGTH bit time */
}en_cxpi_timeout_sel_t;
/* CXPI state type */
typedef enum
{
CXPI_STATE_TXRX_PENDING,
CXPI_STATE_TX_PTYPE,
CXPI_STATE_TX_HDR_RX_RESP,
CXPI_STATE_TX_HDR_TX_RESP,
CXPI_STATE_RX_HDR_TX_RESP,
CXPI_STATE_RX_HDR_RX_RESP,
} en_cxpi_state_t;
/* Arbitration state type */
typedef enum
{
CXPI_ARB_PENDING,
CXPI_ARB_LOST_TX_RESP,
CXPI_ARB_LOST_RX_RESP,
} en_cxpi_arb_state_t;
/** \} group_cxpi_enums */
/**
* \addtogroup group_cxpi_data_structures
* \{
*/
/**
*****************************************************************************
** \brief CXPI_CH_CLT0 configuration for modes, offset, or master/slave
**
*****************************************************************************/
typedef struct stc_cxpi_ch_ctl0_config
{
/** Mode of operation */
en_cxpi_mode_t enMode;
/** If TRUE, corresponding CXPI channel transceiver is enabled automatically,
* the TX_RX_STATUS.EN_OUT field is controlled by HW.
* If FALSE, firmware has to handle the transceiver enable signal manually
*/
bool bCxpiTransceiverAutoEnable;
/** Receive PID Zero Check Enable
* If TRUE, If received PID[6:0] = 0 and PID[7]=1'b1, HW does not clear CMD.RX_HEADER
* and will anticipate receiving header again. Should be TRUE where polling is enabled
* and CXPI controller is configured as slave.
* If FALSE, no action if received PID[6:0] = 0 and PID[7]=1'b1.
*/
bool bRxPIDZeroAutoCheckEnable;
/** If TRUE, cxpi_rx_in filter operates.
* Median 3 operates on the last three "cxpi_rx_in" values.
* The sequences '000', '001', '010' and '100' result in a filtered value '0'.
* The sequences '111', '110', '101' and '011' result in a filtered value '1'.
*/
bool bFilterEnable;
/** Inter Frame Space in bit periods */
uint8_t u8IFS;
/** Inter Byte Space in bit periods */
uint8_t u8IBS;
/** Specifies behavior on a detected bit error during header or response transmission.
* If TRUE, message transfer is NOT aborted.
* If FALSE, message transfer is NOT aborted.
*/
en_bit_error_ignore_t enBitErrorIgnore;
/** CXPI master mode.
* If TRUE, use CXPI as master node.
* If FALSE, use CXPI as slave node.
*/
en_master_mode_t enCxpiMasterMode;
}stc_cxpi_ch_ctl0_config_t;
/**
*****************************************************************************
** \brief CXPI_CH_CLT1 configuration for timing controls at Data link layer.
**
*****************************************************************************/
typedef struct stc_cxpi_ch_ctl1_config
{
/** Low count for logic 1.
* The count value here indicates the number of clocks per clk_cxpi_ch
* to drive a '0' at CXPI bus before releasing it to indicate a logical '1'. */
uint16_t u16TLow1;
/** Low count for logic 0.
* The count value here indicates the number of clocks per clk_cxpi_ch
* to drive a '0' at CXPI bus before releasing it to indicate a logical '1'. */
uint16_t u16TLow0;
/** The value of offset that is used for sampling the 'rx'. */
uint16_t u16TOffset;
}stc_cxpi_ch_ctl1_config_t;
/**
*****************************************************************************
** \brief CXPI_CH_CLT2 configuration for retry, wakeup pulse length, time out.
**
*****************************************************************************/
typedef struct stc_cxpi_ch_ctl2_config
{
/** Number of retries after arbitration lost. */
en_cxpi_retries_t enRetriesNumber;
/** Specifies the wake up pulse low period in Tbits that is transmitted during Standby mode. */
uint8_t u8TWakeUpLength;
/** Timeout Length (in Tbits). Specifies the number of Tbits to exceed timeout between frame bytes
* within a message frame. CXPI spec states that the maximum allowed inter byte space (IBS) is 9Tbits
*/
uint8_t u8TimeoutLength;
/** Timeout Select */
en_cxpi_timeout_sel_t enTimeoutSel;
}stc_cxpi_ch_ctl2_config_t;
typedef struct stc_cxpi_frame_info{
uint8_t datalength;
uint8_t SleepInd;
uint8_t WakeupInd;
uint8_t counter;
} stc_cxpi_frame_info_t;
/* CXPI frame configuration */
typedef struct cxpi_response
{
stc_cxpi_frame_info_t frameIF;
uint8_t dataBuffer[CXPI_DATA_LENGTH_MAX];
} cxpi_response_t;
typedef struct cxpi_frame_context
{
uint8_t id;
cxpi_response_t response;
} cxpi_frame_context_t;
typedef struct stc_CXPI_context
{
volatile stc_CXPI_CH_t *pstcInstanceCxpi;
uint8_t* pAcceptedPIDList;
uint32_t numberOfAcceptedPID;
uint8_t *rxBuf; /**< The pointer to the receive buffer */
uint8_t volatile rxBuffCount; /**< The receive buffer size */
uint8_t volatile rxBufIdx; /**< The current location in the receive buffer */
uint8_t *txBuf; /**< The pointer to the transmit buffer */
uint8_t volatile txBuffCount; /**< The transmit buffer size */
uint8_t volatile txBufIdx; /**< The current location in the transmit buffer */
en_cxpi_state_t volatile state;
en_cxpi_arb_state_t volatile arbState;
uint8_t maximumRetries;
uint8_t volatile retriesCount;
uint8_t transmittingFrameId;
cxpi_response_t* pTransmittingFrameResponse;
cxpi_frame_context_t transmittingFrame;
cxpi_frame_context_t receivedFrame;
void (*pNotifyTrasmittedWakeupPulse)(void);
void (*pNotifyRXFallingEdgeDetected)(void);
void (*pNotifyError)(uint32_t);
void (*pNotifySentPTYPE)(void);
void (*pNotifyReceivedPTYPE)(void);
void (*pNotifyReceivedPID)(uint8_t);
void (*pNotifyReceivedResponse)(cxpi_frame_context_t*);
void (*pNotifyArbitrationLost)(void);
}stc_CXPI_context_t;
/**
*****************************************************************************
** \brief CXPI configuration
** This settings are per CXPI instance.
*****************************************************************************/
typedef struct stc_cxpi_config
{
stc_cxpi_ch_ctl0_config_t stcCxpiChCtl0Config;
stc_cxpi_ch_ctl1_config_t stcCxpiChCtl1Config;
stc_cxpi_ch_ctl2_config_t stcCxpiChCtl2Config;
uint8_t* pAcceptedPIDList;
uint32_t numberOfAcceptedPID;
uint32_t maximumRetries;
void (*pNotifyTrasmittedWakeupPulse)(void);
void (*pNotifyRXFallingEdgeDetected)(void);
void (*pNotifyError)(uint32_t);
void (*pNotifySentPTYPE)(void);
void (*pNotifyReceivedPTYPE)(void);
void (*pNotifyReceivedPID)(uint8_t);
void (*pNotifyReceivedResponse)(cxpi_frame_context_t*);
void (*pNotifyArbitrationLost)(void);
}stc_cxpi_config_t;
/** \} group_cxpi_data_structures */
/**
*****************************************************************************
** \brief CXPI context structure
**
*****************************************************************************/
/*****************************************************************************/
/* Global variable declarations ('extern', definition in C source) */
/*****************************************************************************/
/*****************************************************************************/
/* Global function prototypes ('extern', definition in C source) */
/*****************************************************************************/
/**
* \addtogroup group_cxpi_functions
* \{
*/
cy_en_cxpi_status_t Cy_CXPI_DeInit( volatile stc_CXPI_CH_t *pstcCXPI ); //done
cy_en_cxpi_status_t Cy_CXPI_Init( volatile stc_CXPI_CH_t *pstcCXPI, const stc_cxpi_config_t *pstcConfig, stc_CXPI_context_t *pCxpiCtx); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_Enable(volatile stc_CXPI_CH_t *pstcCXPI); //done
cy_en_cxpi_status_t Cy_CXPI_Disable(volatile stc_CXPI_CH_t *pstcCXPI); //done
cy_en_cxpi_status_t Cy_CXPI_SetCmd(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t command);
cy_en_cxpi_status_t Cy_CXPI_GetStatus(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *status); //done
cy_en_cxpi_status_t Cy_CXPI_SetPID(volatile stc_CXPI_CH_t *pstcCXPI, uint8_t id); //done
cy_en_cxpi_status_t Cy_CXPI_GetPID(volatile stc_CXPI_CH_t *pstcCXPI, uint8_t* id, uint8_t* parity); //done
cy_en_cxpi_status_t Cy_CXPI_SetFI(volatile stc_CXPI_CH_t *pstcCXPI, stc_cxpi_frame_info_t fi); //done
cy_en_cxpi_status_t Cy_CXPI_GetFI(volatile stc_CXPI_CH_t *pstcCXPI, stc_cxpi_frame_info_t *fi); //done
cy_en_cxpi_status_t Cy_CXPI_GetCRC(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *CRCdata); //done
cy_en_cxpi_status_t Cy_CXPI_SetTriggerLevelTxFIFO(volatile stc_CXPI_CH_t *pstcCXPI, uint8_t triggerLevel); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_ClearTxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_FreezeTxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_UnfreezeTxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_GetTxFIFOStatus(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *usedEnntries, uint32_t *availEntries); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_WriteTxFIFO( volatile stc_CXPI_CH_t *pstcCXPI, const uint8_t data);
cy_en_cxpi_status_t Cy_CXPI_SetTriggerLevelRxFIFO(volatile stc_CXPI_CH_t *pstcCXPI, uint8_t triggerLevel); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_ClearRxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_FreezeRxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_UnfreezeRxFIFO(volatile stc_CXPI_CH_t *pstcCXPI); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_GetRxFIFOStatus(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *usedEnntries, uint32_t *availEntries); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_ReadRxFIFO( volatile stc_CXPI_CH_t *pstcCXPI, uint8_t *data); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_SilentReadRxFIFO(volatile stc_CXPI_CH_t *pstcCXPI, uint8_t *data); //done w/o comment
cy_en_cxpi_status_t Cy_CXPI_SetInterruptMask(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t mask); //done
cy_en_cxpi_status_t Cy_CXPI_GetInterruptMask(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *mask); //done
cy_en_cxpi_status_t Cy_CXPI_GetInterruptMaskedStatus(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *status); //done
cy_en_cxpi_status_t Cy_CXPI_GetInterruptStatus(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t *status); //done
cy_en_cxpi_status_t Cy_CXPI_ClearInterrupt(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t mask); //done
cy_en_cxpi_status_t Cy_CXPI_SendWakeupPulse(volatile stc_CXPI_CH_t *pstcCXPI, uint32_t tWakeupLength);
cy_en_cxpi_status_t Cy_CXPI_IFSWait(volatile stc_CXPI_CH_t *pstcCXPI);
cy_en_cxpi_status_t Cy_CXPI_SetMode(volatile stc_CXPI_CH_t *pstcCXPI, stc_CXPI_context_t *cxpiCtx, uint32_t mode);
cy_en_cxpi_status_t Cy_CXPI_SendPTYPE(volatile stc_CXPI_CH_t *pstcCXPI, stc_CXPI_context_t *cxpiCtx);
cy_en_cxpi_status_t Cy_CXPI_SendPID(volatile stc_CXPI_CH_t *pstcCXPI, stc_CXPI_context_t *cxpiCtx, uint8_t frameID, bool withResponse, cxpi_response_t *pResponse, bool IFSwait);
cy_en_cxpi_status_t Cy_CXPI_SendResp(volatile stc_CXPI_CH_t *pstcCXPI, stc_CXPI_context_t *cxpiCtx, cxpi_response_t *pResponse);
cy_en_cxpi_status_t Cy_CXPI_Interrupt(volatile stc_CXPI_CH_t *pstcCXPI, stc_CXPI_context_t *cxpiCtx);
void HeaderReceptionOperation(volatile stc_CXPI_CH_t* pstcCXPI, stc_CXPI_context_t* pCxpiCtx);
void TXRXCompletionOperation(volatile stc_CXPI_CH_t* pstcCXPI, stc_CXPI_context_t* pCxpiCtx);
void TXHandler(volatile stc_CXPI_CH_t* pstcCXPI, stc_CXPI_context_t* pCxpiCtx);
void RXHandler(volatile stc_CXPI_CH_t* pstcCXPI, stc_CXPI_context_t* pCxpiCtx);
void RXResponseDoneHandler(volatile stc_CXPI_CH_t* pstcCXPI, stc_CXPI_context_t* pCxpiCtx);
/** \} group_cxpi_functions */
#ifdef __cplusplus
}
#endif
#endif /* __CY_CXPI_H__ */
/*****************************************************************************/
/* EOF (not truncated) */
/*****************************************************************************/
@@ -0,0 +1,428 @@
/***************************************************************************//**
* \file cy_mdma.c
* \version 1.0
*
* \brief
* The MDMA driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#include "cy_mdma.h"
#if defined(__cplusplus)
extern "C" {
#endif
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_Init
****************************************************************************//**
*
* This function initializes descriptor structure in SRAM from a pre-initialized
* configuration structure.
* This function initializes only the descriptor and not the channel.
* This function will operate on the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
*
* \param descriptor
* This is an instance of the descriptor structure declared by the user and
* initialized by this function.
*
* \param config
* This is a configuration structure that has all initialization information for
* the descriptor.
*
* \return
* This function returns /ref cy_en_mdma_status_t.
*
*******************************************************************************/
cy_en_mdma_status_t Cy_MDMA_Descr_Init(cy_stc_mdma_descr_t* descriptor, const cy_stc_mdma_descr_config_t* config)
{
cy_en_mdma_status_t retVal = CY_MDMA_ERR_UNC;
if ((descriptor != NULL) && (config != NULL))
{
/* Descriptor[0] */
descriptor->unMDMA_DESCR_CTL.stcField.u2WAIT_FOR_DEACT = config->deact;
descriptor->unMDMA_DESCR_CTL.stcField.u2INTR_TYPE = config->intrType;
descriptor->unMDMA_DESCR_CTL.stcField.u2TR_OUT_TYPE = config->trigoutType;
descriptor->unMDMA_DESCR_CTL.stcField.u2TR_IN_TYPE = config->triginType;
descriptor->unMDMA_DESCR_CTL.stcField.u1DATA_PREFETCH = config->dataPrefetch;
descriptor->unMDMA_DESCR_CTL.stcField.u2DATA_SIZE = config->dataSize;
descriptor->unMDMA_DESCR_CTL.stcField.u1CH_DISABLE = config->chStateAtCmplt;
descriptor->unMDMA_DESCR_CTL.stcField.u1SRC_TRANSFER_SIZE = config->srcTxfrSize;
descriptor->unMDMA_DESCR_CTL.stcField.u1DST_TRANSFER_SIZE = config->destTxfrSize;
descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE = config->descrType;
/* Descriptor[1] */
descriptor->u32MDMA_DESCR_SRC = (uint32_t)config->srcAddr;
/* after 3rd word of descriptor depends on descriptor type */
switch(config->descrType)
{
case (uint32_t)CY_MDMA_SINGLE_TRANSFER:
{
/* Descriptor[2] */
descriptor->u32MDMA_DESCR_DST = (uint32_t)config->destAddr;
/* Descriptor[3] -> NEXT_PTR */
descriptor->unMDMA_DESCR_X_SIZE.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_MDMA_1D_TRANSFER:
{
/* Descriptor[2] */
descriptor->u32MDMA_DESCR_DST = (uint32_t)config->destAddr;
/* Descriptor[3] */
descriptor->unMDMA_DESCR_X_SIZE.stcField.u16X_COUNT = (uint32_t)((config->xCount) - 1ul);
/* Descriptor[4] */
descriptor->unMDMA_DESCR_X_INCR.stcField.u16SRC_X_INCR = (uint32_t)config->srcXincr;
descriptor->unMDMA_DESCR_X_INCR.stcField.u16DST_X_INCR = (uint32_t)config->destXincr;
/* Descriptor[5] -> NEXT_PTR */
descriptor->unMDMA_DESCR_Y_SIZE.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_MDMA_2D_TRANSFER:
{
/* Descriptor[2] */
descriptor->u32MDMA_DESCR_DST = (uint32_t)config->destAddr;
/* Descriptor[3] */
descriptor->unMDMA_DESCR_X_SIZE.stcField.u16X_COUNT = (uint32_t)((config->xCount) - 1ul);
/* Descriptor[4] */
descriptor->unMDMA_DESCR_X_INCR.stcField.u16SRC_X_INCR = (uint32_t)config->srcXincr;
descriptor->unMDMA_DESCR_X_INCR.stcField.u16DST_X_INCR = (uint32_t)config->destXincr;
/* Descriptor[5] */
descriptor->unMDMA_DESCR_Y_SIZE.stcField.u16Y_COUNT = (uint32_t)((config->yCount) - 1ul);
/* Descriptor[6] */
descriptor->unMDMA_DESCR_Y_INCR.stcField.u16SRC_Y_INCR = (uint32_t)config->srcYincr;
descriptor->unMDMA_DESCR_Y_INCR.stcField.u16DST_Y_INCR = (uint32_t)config->destYincr;
/* Descriptor[7] */
descriptor->u32MDMA_DESCR_NEXT_PTR = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_MDMA_MEMORY_COPY_TRANSFER:
{
/* Descriptor[2] */
descriptor->u32MDMA_DESCR_DST = (uint32_t)config->destAddr;
/* Descriptor[3] */
descriptor->unMDMA_DESCR_X_SIZE.stcField.u16X_COUNT = (uint32_t)((config->xCount) - 1ul);
/* Descriptor[4] -> NEXT_PTR */
descriptor->unMDMA_DESCR_X_INCR.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_MDMA_SCATTER_TRANSFER:
{
/* Descriptor[2] -> X_SIZE */
descriptor->u32MDMA_DESCR_DST = (uint32_t)((config->xCount) - 1ul);
/* Descriptor[3] -> NEXT_PTR */
descriptor->unMDMA_DESCR_X_SIZE.u32Register = (uint32_t)config->descrNext;
break;
}
default:
{
/* Unsupported type of descriptor */
break;
}
}
retVal = CY_MDMA_SUCCESS;
}
else
{
retVal = CY_MDMA_INVALID_INPUT_PARAMETERS;
}
return retVal;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_DeInit
****************************************************************************//**
*
* This function clears all the content of the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user can't use this
* function to deinitialize it.
*
* \param descriptor
* This is an instance of the descriptor structure to be cleared.
*
*******************************************************************************/
void Cy_MDMA_Descr_DeInit(cy_stc_mdma_descr_t* descriptor)
{
descriptor->unMDMA_DESCR_CTL.u32Register = 0ul;
descriptor->u32MDMA_DESCR_SRC = 0ul;
descriptor->u32MDMA_DESCR_DST = 0ul;
descriptor->unMDMA_DESCR_X_SIZE.u32Register = 0ul;
descriptor->unMDMA_DESCR_X_INCR.u32Register = 0ul;
descriptor->unMDMA_DESCR_Y_SIZE.u32Register = 0ul;
descriptor->unMDMA_DESCR_Y_INCR.u32Register = 0ul;
descriptor->u32MDMA_DESCR_NEXT_PTR = 0ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetTxfrWidth
****************************************************************************//**
*
* This function sets the transfer widths for for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor-structure instance declared by the user. This is the
* descriptor being modified.
*
* \param dataElementSize
* This parameter provides the data element size of the descriptor.
* \ref cy_en_mdma_datasize_t
*
* \param srcTxfrWidth
* This parameter provides the source transfer size parameters of the descriptor.
* \ref cy_en_mdma_txfrsize_t
*
* \param dstTxfrWidth
* This parameter provides the destination transfer size parameters of the
* descriptor.
* \ref cy_en_mdma_txfrsize_t
*
*******************************************************************************/
void Cy_MDMA_Descr_SetTxfrWidth(cy_stc_mdma_descr_t* descriptor,
uint32_t dataElementSize,
uint32_t srcTxfrWidth,
uint32_t dstTxfrWidth)
{
descriptor->unMDMA_DESCR_CTL.stcField.u2DATA_SIZE = dataElementSize;
descriptor->unMDMA_DESCR_CTL.stcField.u1SRC_TRANSFER_SIZE = srcTxfrWidth;
descriptor->unMDMA_DESCR_CTL.stcField.u1DST_TRANSFER_SIZE = dstTxfrWidth;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_Init
****************************************************************************//**
*
* Initializes the MDMA channel with a descriptor and other parameters.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \param chnlConfig
* This parameter is a structure that has the initialization information for the
* channel.
*
* \return
* This function returns /ref cy_en_mdma_status_t.
*
*******************************************************************************/
cy_en_mdma_status_t Cy_MDMA_Chnl_Init(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum, const cy_stc_mdma_chnl_config_t* chnlConfig)
{
cy_en_mdma_status_t retVal = CY_MDMA_ERR_UNC;
if ((pstcMDMA != NULL) && (chnlConfig != NULL))
{
/* Initialize the X/Y indices (which are undefined after reset) */
pstcMDMA->CH[chNum].unIDX.u32Register = 0;
/* Set current descriptor */
pstcMDMA->CH[chNum].unCURR.u32Register = (uint32_t)chnlConfig->MDMA_Descriptor;
/* Set if the channel is preemptable */
/* There is no the parameter in MDMA */
/* Set channel priority */
pstcMDMA->CH[chNum].unCTL.stcField.u2PRIO = chnlConfig->priority;
/* Set enabled status */
pstcMDMA->CH[chNum].unCTL.stcField.u1ENABLED = chnlConfig->enable;
retVal = CY_MDMA_SUCCESS;
}
else
{
retVal = CY_MDMA_INVALID_INPUT_PARAMETERS;
}
return (retVal);
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_DeInit
****************************************************************************//**
*
* Clears all the content of registers corresponding to the channel.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
*******************************************************************************/
void Cy_MDMA_Chnl_DeInit(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
pstcMDMA->CH[chNum].unCTL.u32Register = 0ul;
pstcMDMA->CH[chNum].unIDX.u32Register = 0ul;
pstcMDMA->CH[chNum].unCURR.u32Register = 0ul;
pstcMDMA->CH[chNum].unINTR_MASK.u32Register = 0ul;
pstcMDMA->CH[chNum].unINTR_SET.u32Register = 0ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Enable
****************************************************************************//**
*
* This function enables the MDMA block.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
*******************************************************************************/
void Cy_MDMA_Enable(volatile stc_DMAC_t *pstcMDMA)
{
pstcMDMA->unCTL.stcField.u1ENABLED = 1ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Disable
****************************************************************************//**
*
* This function disables the MDMA block.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
*******************************************************************************/
void Cy_MDMA_Disable(volatile stc_DMAC_t *pstcMDMA)
{
pstcMDMA->unCTL.stcField.u1ENABLED = 0ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_GetActiveChnl
****************************************************************************//**
*
* This function used to determine the channels that are active/pending in
* the MDMA block.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
* \return
* Returns a bit-field with all of the currently active/pending channels in the
* MDMA block.
*
*******************************************************************************/
uint32_t Cy_MDMA_GetActiveChnl(const volatile stc_DMAC_t *pstcMDMA)
{
return pstcMDMA->unACTIVE.u32Register;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_ClearInterrupt
****************************************************************************//**
*
* This function clears the interrupt status.
*
* \param ptscMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
*******************************************************************************/
void Cy_MDMA_Chnl_ClearInterrupt(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
pstcMDMA->CH[chNum].unINTR.u32Register = CY_MDMA_INTR_BIT_MASK;
/* Readback of the register is required by hardware. */
(void) pstcMDMA->CH[chNum].unINTR.u32Register;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetNxtDescr
****************************************************************************//**
*
* Sets a next descriptor parameter for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
* Based on descriptor type the offset of address for the next descriptor may
* vary. For a single transfer descriptor type, this register is at offset 0x0c.
* For a 1D transfer descriptor type, this register is at offset 0x10. For a 2D
* transfer descriptor type, this register is at offset 0x14.
*
* \param descriptor
* This is the descriptor-structure instance declared by the user. The function
* modifies the destination-address parameter in this descriptor.
*
* \param nxtDescriptor
* The pointer to the next descriptor.
*
*******************************************************************************/
void Cy_MDMA_Descr_SetNxtDescr(cy_stc_mdma_descr_t* descriptor, const cy_stc_mdma_descr_t* nxtDescriptor)
{
if (descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE == (uint32_t)CY_MDMA_SINGLE_TRANSFER)
{
descriptor->unMDMA_DESCR_X_SIZE.u32Register = (uint32_t)nxtDescriptor;
}
else if (descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE == (uint32_t)CY_MDMA_1D_TRANSFER)
{
descriptor->unMDMA_DESCR_Y_SIZE.u32Register = (uint32_t)nxtDescriptor;
}
else if (descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE == (uint32_t)CY_MDMA_2D_TRANSFER)
{
descriptor->u32MDMA_DESCR_NEXT_PTR = (uint32_t)nxtDescriptor;
}
else if (descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE == (uint32_t)CY_MDMA_MEMORY_COPY_TRANSFER)
{
descriptor->unMDMA_DESCR_X_INCR.u32Register = (uint32_t)nxtDescriptor;
}
else if (descriptor->unMDMA_DESCR_CTL.stcField.u3DESCR_TYPE == (uint32_t)CY_MDMA_SCATTER_TRANSFER)
{
descriptor->unMDMA_DESCR_X_SIZE.u32Register = (uint32_t)nxtDescriptor;
}
else
{
/* Unsupported type of descriptor */
}
}
#if defined(__cplusplus)
}
#endif
/* [] END OF FILE */
@@ -0,0 +1,866 @@
/***************************************************************************//**
* \file cy_mdma.h
* \version 1.0
*
* \brief
* The header file of the MDMA driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
/**
* \defgroup group_mdma Memory DMA (M-DMA)
* \{
* Configures a MDMA channel and its descriptor(s).
*
* The MDMA Channel component can be used in any project that needs to transfer data
* without CPU intervention, based on a hardware trigger signal from another component.
*
* A device may support more than one MDMA hardware block. Each block has a set of
* registers, and a base hardware address. Each block also supports multiple channels.
* Many API functions for the MDMA driver require the base hardware address and the
* channel number. Ensure that you use the correct hardware address for the MDMA block in use.
*
* Features:
* * Devices support one MDMA hardware block
* * Each MDMA block supports up to 16 MDMA channels
* * Supports channel descriptors in SRAM
* * Four Priority Levels for each channel
* * Byte, Half-word (2 byte), and Word (4 byte) transfers
* * Configurable source and destination addresses
*
* A simplified diagram of the M-DMA block is shown below:
* \image html dma.png
*
* \section group_mdma_configuration Configuration Considerations
*
* To set up a MDMA driver you initialize a descriptor,
* initialize and enable a channel, and enable the MDMA block.
*
* To set up the descriptor, provide the configuration parameters for the
* descriptor in the \ref cy_stc_mdma_descr_config_t structure. Then call the
* \ref Cy_MDMA_Descr_Init function to initialize the descriptor in SRAM. You can
* modify the source and destination addresses dynamically by calling
* \ref Cy_MDMA_Descr_SetSrcAddr and \ref Cy_MDMA_Descr_SetDestAddr.
*
* To set up the MDMA channel, provide a filled \ref cy_stc_mdma_chnl_config_t
* structure. Call the \ref Cy_MDMA_Chnl_Init function, specifying the channel
* number. Use \ref Cy_MDMA_Chnl_Enable to enable the configured MDMA channel.
*
* Call \ref Cy_MDMA_Chnl_Enable for each MDMA channel in use.
*
* <B>NOTE:</B> Even if a MDMA channel is enabled, it is not operational until
* the MDMA block is enabled using function \ref Cy_MDMA_Enable.\n
* <B>NOTE:</B> If the MDMA descriptor is configured to generate an interrupt,
* the interrupt must be enabled using the \ref Cy_MDMA_Chnl_SetInterruptMask
* function for each MDMA channel.
*
* \section group_mdma_more_information More Information.
*
* See the MDMA Component datasheet.
* See also the MDMA chapter of the device technical reference manual (TRM).
*
*
* \section group_mdma_changelog Changelog
*
* <table class="doxtable">
* <tr><th>Version</th><th>Changes</th><th>Reason for Change</th></tr>
* <tr>
* <td>1.0</td>
* <td>Initial version</td>
* <td></td>
* </tr>
* </table>
*
* \defgroup group_mdma_macro Macro
* \{
* \defgroup group_mdma_common_macro_MDMAIntrCauses MDMA Interrupt Causes
* \}
*
* \defgroup group_mdma_functions Functions
* \defgroup group_mdma_data_structures Data structures
* \defgroup group_mdma_enums Enumerated types
*/
#if !defined(CY_MDMA_H)
#define CY_MDMA_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "syslib/cy_syslib.h"
#include "cy_device_headers.h"
/*
#ifndef CY_IP_M4CPUSS_MDMA
#error "The MDMA driver is not supported on this device"
#endif
*/
#if defined(__cplusplus)
extern "C" {
#endif
/******************************************************************************
* Macro definitions *
******************************************************************************/
/**
* \addtogroup group_mdma_macro
* \{
*/
/** Driver major version */
#define CY_MDMA_DRV_VERSION_MAJOR 1
/** Driver minor version */
#define CY_MDMA_DRV_VERSION_MINOR 0
/** \cond INTERNAL */
#define CY_MDMA_INTR_BIT_MASK (0x01UL)
/** \endcond */
/**
* \addtogroup group_mdma_common_macro_MDMAIntrCauses
* \{
*/
#define CY_MDMA_INTRCAUSE_NO_INTR (0x00u) /**< No interrupt */
#define CY_MDMA_INTRCAUSE_COMPLETION (0x01u) /**< Completion */
#define CY_MDMA_INTRCAUSE_SRC_BUS_ERROR (0x02u) /**< Source bus error */
#define CY_MDMA_INTRCAUSE_DST_BUS_ERROR (0x04u) /**< Destination bus error */
#define CY_MDMA_INTRCAUSE_SRC_MISAL (0x08u) /**< Source address is not aligned */
#define CY_MDMA_INTRCAUSE_DST_MISAL (0x10u) /**< Destination address is not aligned */
#define CY_MDMA_INTRCAUSE_CURR_PTR_NULL (0x20u) /**< Current descriptor pointer is NULL */
#define CY_MDMA_INTRCAUSE_ACTIVE_CH_DISABLED (0x40u) /**< Active channel is disabled */
#define CY_MDMA_INTRCAUSE_DESCR_BUS_ERROR (0x80u) /**< Descriptor bus error */
/** \} group_mdma_common_macro_MDMAIntrCauses */
#define CY_MDMA_ID (CY_PDL_DRV_ID(0x13u)) /**< MDMA PDL ID */
#define CY_MDMA_ID_INFO (uint32_t)( CY_MDMA_ID | CY_PDL_STATUS_INFO ) /**< Return prefix for MDMA driver function status codes */
#define CY_MDMA_ID_WARNING (uint32_t)( CY_MDMA_ID | CY_PDL_STATUS_WARNING) /**< Return prefix for MDMA driver function warning return values */
#define CY_MDMA_ID_ERROR (uint32_t)( CY_MDMA_ID | CY_PDL_STATUS_ERROR) /**< Return prefix for MDMA driver function error return values */
/** \} group_mdma_macro */
/**
* \addtogroup group_mdma_data_structures
* \{
*/
/* MDMA descriptor structure type. This is a redefinition of the descriptor structure in the driver.
* The user will use this descriptor type in his descriptor configuration functions.
*/
/**
* \brief MDMA descriptor structure
*/
typedef struct stc_MDMA_DESCR_CTL_field {
uint32_t u2WAIT_FOR_DEACT:2;
uint32_t u2INTR_TYPE:2;
uint32_t u2TR_OUT_TYPE:2;
uint32_t u2TR_IN_TYPE:2;
uint32_t u1DATA_PREFETCH:1;
uint32_t :7;
uint32_t u2DATA_SIZE:2;
uint32_t :6;
uint32_t u1CH_DISABLE:1;
uint32_t :1;
uint32_t u1SRC_TRANSFER_SIZE:1;
uint32_t u1DST_TRANSFER_SIZE:1;
uint32_t u3DESCR_TYPE:3;
uint32_t :1;
} stc_MDMA_DESCR_CTL_field_t;
typedef union un_MDMA_DESCR_CTL {
uint32_t u32Register;
stc_MDMA_DESCR_CTL_field_t stcField;
uint8_t au8Byte[4];
uint16_t au16Halfword[2];
} un_MDMA_DESCR_CTL_t;
typedef struct stc_MDMA_DESCR_X_SIZE_field {
uint32_t u16X_COUNT:16;
uint32_t :16;
} stc_MDMA_DESCR_X_SIZE_field_t;
typedef union un_MDMA_DESCR_X_SIZE {
uint32_t u32Register;
stc_MDMA_DESCR_X_SIZE_field_t stcField;
uint8_t au8Byte[4];
uint16_t au16Halfword[2];
} un_MDMA_DESCR_X_SIZE_t;
typedef struct stc_MDMA_DESCR_X_INCR_field {
uint32_t u16SRC_X_INCR:16;
uint32_t u16DST_X_INCR:16;
} stc_MDMA_DESCR_X_INCR_field_t;
typedef union un_MDMA_DESCR_X_INCR {
uint32_t u32Register;
stc_MDMA_DESCR_X_INCR_field_t stcField;
uint8_t au8Byte[4];
uint16_t au16Halfword[2];
} un_MDMA_DESCR_X_INCR_t;
typedef struct stc_MDMA_DESCR_Y_SIZE_field {
uint32_t u16Y_COUNT:16;
uint32_t :16;
} stc_MDMA_DESCR_Y_SIZE_field_t;
typedef union un_MDMA_DESCR_Y_SIZE {
uint32_t u32Register;
stc_MDMA_DESCR_Y_SIZE_field_t stcField;
uint8_t au8Byte[4];
uint16_t au16Halfword[2];
} un_MDMA_DESCR_Y_SIZE_t;
typedef struct stc_MDMA_DESCR_Y_INCR_field {
uint32_t u16SRC_Y_INCR:16;
uint32_t u16DST_Y_INCR:16;
} stc_MDMA_DESCR_Y_INCR_field_t;
typedef union un_MDMA_DESCR_Y_INCR {
uint32_t u32Register;
stc_MDMA_DESCR_Y_INCR_field_t stcField;
uint8_t au8Byte[4];
uint16_t au16Halfword[2];
} un_MDMA_DESCR_Y_INCR_t;
typedef struct {
un_MDMA_DESCR_CTL_t unMDMA_DESCR_CTL;
uint32_t u32MDMA_DESCR_SRC;
uint32_t u32MDMA_DESCR_DST;
un_MDMA_DESCR_X_SIZE_t unMDMA_DESCR_X_SIZE;
un_MDMA_DESCR_X_INCR_t unMDMA_DESCR_X_INCR;
un_MDMA_DESCR_Y_SIZE_t unMDMA_DESCR_Y_SIZE;
un_MDMA_DESCR_Y_INCR_t unMDMA_DESCR_Y_INCR;
uint32_t u32MDMA_DESCR_NEXT_PTR;
} cy_stc_mdma_descr_t;
/**
* This structure is a configuration structure pre-initialized by the user and
* passed as a parameter to the Cy_MDMA_InitDescr(). This structure has all the parameters
* of the descriptor as separate parameters. Some of these parameters are represented in the
* stc_Descr_t data structure as bit fields.
*/
typedef struct {
uint32_t deact; /**< Specifies whether the MDMA controller should wait for the input trigger to be deactivated */
uint32_t intrType; /**< Sets the event that triggers an interrupt, see \ref cy_en_mdma_inttype_t */
uint32_t trigoutType; /**< Sets the event that triggers an output, see \ref cy_en_mdma_trigouttype_t */
uint32_t chStateAtCmplt; /**< Specifies if the channel is enabled or disabled on completion of descriptor see \ref cy_en_mdma_chdesccmpl_t */
uint32_t triginType; /**< Sets what type of transfer is triggered, see \ref cy_en_mdma_trigintype_t */
uint32_t dataPrefetch; /**< Source data prefetch */
uint32_t dataSize; /**< The size of the data bus for transfer, see \ref cy_en_mdma_datasize_t */
uint32_t srcTxfrSize; /**< The source transfer size */
uint32_t destTxfrSize; /**< The destination transfer size */
uint32_t descrType; /**< The type of the descriptor see \ref cy_en_mdma_descrtype_t*/
void * srcAddr; /**< The source address of the transfer */
void * destAddr; /**< The destination address of the transfer */
int16_t srcXincr; /**< The address increment of the source after each X-loop transfer */
int16_t destXincr; /**< The address increment of the destination after each X-loop transfer */
uint32_t xCount; /**< The number of transfers in an X-loop */
int16_t srcYincr; /**< The address increment of the source after each Y-loop transfer */
int16_t destYincr; /**< The address increment of the destination after each Y-loop transfer */
uint32_t yCount; /**< The number of transfers in the Y-loop */
cy_stc_mdma_descr_t* descrNext; /**< The next descriptor to chain after completion, a NULL value will signify no chaining */
} cy_stc_mdma_descr_config_t;
/** This structure holds the initialization values for the MDMA channel */
typedef struct {
cy_stc_mdma_descr_t* MDMA_Descriptor; /**< The MDMA descriptor associated with the channel being initialized */
uint32_t preemptable; /**< Specifies if the channel is preemptable by another higher-priority channel */
uint32_t priority; /**< This parameter specifies the channel's priority */
uint32_t enable; /**< This parameter specifies if the channel is enabled after initializing */
uint32_t priviledge;
uint32_t non_secure;
uint32_t bufferable;
uint32_t protection_context;
} cy_stc_mdma_chnl_config_t;
/** \} group_mdma_data_structures */
/**
* \addtogroup group_mdma_enums
* \{
*/
/** Contains the options for the trigger input deactivation parameter type */
enum cy_en_mdma_deactivate_t {
CY_MDMA_TRIG_DEACT_NO_WAIT, /**< Don't wait for deactivation (for pulse sensitive triggers)*/
CY_MDMA_TRIG_DEACT_4CYC, /**< Wait for up to 4 cycles */
CY_MDMA_TRIG_DEACT_16CYC, /**< Wait for up to 16 cycles */
CY_MDMA_TRIG_DEACT_WAIT_INDEFINITELY /**< Wait indefinitely. This option may result in controller lockup if the trigger is not de-activated */
} ;
/** Contains the options for the descriptor type */
enum cy_en_mdma_descrtype_t {
CY_MDMA_SINGLE_TRANSFER, /**< Single transfer */
CY_MDMA_1D_TRANSFER, /**< 1D transfer */
CY_MDMA_2D_TRANSFER, /**< 2D transfer */
CY_MDMA_MEMORY_COPY_TRANSFER, /**< Memory copy transfer */
CY_MDMA_SCATTER_TRANSFER /**< Scatter transfer */
} ;
/** Contains the options for the interrupt-type parameter of the descriptor */
enum cy_en_mdma_inttype_t {
CY_MDMA_INTR_1ELEMENT_CMPLT, /**< Triggers on every element transfer completion */
CY_MDMA_INTR_X_LOOP_CMPLT, /**< Triggers on every X loop transfer completion */
CY_MDMA_INTR_DESCR_CMPLT, /**< Triggers on descriptor completion */
CY_MDMA_INTR_DESCRCHAIN_CMPLT /**< Triggers on completion of the entire descriptor chain */
} ;
/** This enum has the options for the trigger in the type parameter of the descriptor */
enum cy_en_mdma_trigouttype_t {
CY_MDMA_TRIGOUT_1ELEMENT_CMPLT, /**< Triggers on every element transfer completion */
CY_MDMA_TRIGOUT_X_LOOP_CMPLT, /**< Triggers on every X loop transfer completion */
CY_MDMA_TRIGOUT_DESCR_CMPLT, /**< Triggers on descriptor completion */
CY_MDMA_TRIGOUT_DESCRCHAIN_CMPLT /**< Triggers on completion of the entire descriptor chain */
} ;
/** This enum has the options for the trigger-in-type parameter of the descriptor */
enum cy_en_mdma_trigintype_t {
CY_MDMA_TRIGIN_1ELEMENT, /**< One transfer per trigger */
CY_MDMA_TRIGIN_XLOOP, /**< One X loop transfer per trigger */
CY_MDMA_TRIGIN_DESCR, /**< The entire descriptor transfer per trigger */
CY_MDMA_TRIGIN_DESCRCHAIN /**< The entire descriptor chain per trigger */
} ;
/** This enum has the options for the data size */
enum cy_en_mdma_datasize_t {
CY_MDMA_BYTE, /**< One byte */
CY_MDMA_HALFWORD, /**< Half word (two bytes) */
CY_MDMA_WORD /**< Full word (four bytes) */
} ;
/** This enum has the options for descriptor retriggering */
enum cy_en_mdma_retrigger_t {
CY_MDMA_RETDIG_IM, /**< Retrigger immediately */
CY_MDMA_RETDIG_4CYC, /**< Retrigger after 4 SYSCLK */
CY_MDMA_RETDIG_16CYC, /**< Retrigger after 16 SYSCLK */
CY_MDMA_WAIT_FOR_REACT /**< Wait for trigger reactivation */
};
/** This enum has the options for the transfer size */
enum cy_en_mdma_txfrsize_t {
CY_MDMA_TXFR_SIZE_DATA_SIZE, /**< As per data size register */
CY_MDMA_TXFR_SIZE_WORD /**< A full word (four bytes) */
};
/** This enum has the options for the state of the channel when the descriptor is completed */
enum cy_en_mdma_chdesccmpl_t {
CY_MDMA_CH_ENABLED, /**< Channel stays enabled */
CY_MDMA_CH_DISABLED /**< Channel is disabled */
};
/** This enum has the return values of the MDMA driver */
typedef enum
{
CY_MDMA_SUCCESS = 0x00ul, /**< Success */
CY_MDMA_INVALID_INPUT_PARAMETERS = ( CY_MDMA_ID_ERROR + 1ul), /**< Input parameters passed to MDMA API are not valid */
CY_MDMA_ERR_UNC = ( CY_MDMA_ID_ERROR + 0xFul),/**< Unknown error */
} cy_en_mdma_status_t;
/** \} group_mdma_enums */
/**
* \addtogroup group_mdma_functions
* \{
*/
//IP functions
void Cy_MDMA_Enable ( volatile stc_DMAC_t * pstcMDMA);
void Cy_MDMA_Disable ( volatile stc_DMAC_t * pstcMDMA);
uint32_t Cy_MDMA_GetActiveChnl ( const volatile stc_DMAC_t * pstcMDMA);
//Descriptor functions
cy_en_mdma_status_t Cy_MDMA_Descr_Init ( cy_stc_mdma_descr_t* descriptor,
const cy_stc_mdma_descr_config_t* config);
void Cy_MDMA_Descr_DeInit ( cy_stc_mdma_descr_t* descriptor);
__STATIC_INLINE void Cy_MDMA_Descr_SetSrcAddr ( cy_stc_mdma_descr_t* descriptor,
const void * srcAddr);
__STATIC_INLINE void Cy_MDMA_Descr_SetDestAddr ( cy_stc_mdma_descr_t* descriptor,
const void * dstAddr);
void Cy_MDMA_Descr_SetNxtDescr ( cy_stc_mdma_descr_t* descriptor,
const cy_stc_mdma_descr_t* nxtDescriptor);
__STATIC_INLINE void Cy_MDMA_Descr_SetIntrType ( cy_stc_mdma_descr_t* descriptor,
uint32_t interruptType);
__STATIC_INLINE void Cy_MDMA_Descr_SetTrigInType ( cy_stc_mdma_descr_t* descriptor,
uint32_t trigInType);
__STATIC_INLINE void Cy_MDMA_Descr_SetTrigOutType ( cy_stc_mdma_descr_t* descriptor,
uint32_t trigOutType);
void Cy_MDMA_Descr_SetTxfrWidth ( cy_stc_mdma_descr_t* descriptor,
uint32_t dataElementSize,
uint32_t srcTxfrWidth,
uint32_t dstTxfrWidth);
//Channel functions
cy_en_mdma_status_t Cy_MDMA_Chnl_Init ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum,
const cy_stc_mdma_chnl_config_t* chnlConfig);
void Cy_MDMA_Chnl_DeInit ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE void Cy_MDMA_Chnl_SetDescr ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum,
const cy_stc_mdma_descr_t* descriptor);
__STATIC_INLINE void Cy_MDMA_Chnl_Enable ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE void Cy_MDMA_Chnl_Disable ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE void Cy_MDMA_Chnl_SetPriority ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum,
uint32_t priority);
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetPriority ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE cy_stc_mdma_descr_t* Cy_MDMA_Chnl_GetCurrentDescr ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptStatus ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptCause ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
void Cy_MDMA_Chnl_ClearInterrupt ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE void Cy_MDMA_Chnl_SetInterrupt ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum,
uint32_t intMask);
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptMask ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
__STATIC_INLINE void Cy_MDMA_Chnl_SetInterruptMask ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum,
uint32_t intMask);
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptStatusMasked ( const volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
#if defined(CPUSS_SW_TR_PRESENT) && (CPUSS_SW_TR_PRESENT == 1)
__STATIC_INLINE void Cy_MDMA_Chnl_SetSwTrigger ( volatile stc_DMAC_t * pstcMDMA,
uint32_t chNum);
#endif
/***************************************
* In-line Function Implementation
***************************************/
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetSrcAddr
****************************************************************************//**
*
* \brief Sets the source address parameter for the descriptor structure.
*
* This function will operate on the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user has to use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor-structure instance declared by the user. The function
* modifies the source-address parameter in this descriptor.
*
* \param srcAddr
* The source address value for the descriptor.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Descr_SetSrcAddr(cy_stc_mdma_descr_t* descriptor, const void * srcAddr)
{
descriptor->u32MDMA_DESCR_SRC = (uint32_t)srcAddr;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetDestAddr
****************************************************************************//**
*
* \brief Sets the destination address parameter for the descriptor structure.
*
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user has to use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor-structure instance declared by the user. The function
* modifies the destination-address parameter in this descriptor.
*
* \param dstAddr
* The destination address value for the descriptor.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Descr_SetDestAddr(cy_stc_mdma_descr_t* descriptor, const void * dstAddr)
{
descriptor->u32MDMA_DESCR_DST = (uint32_t)dstAddr;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_SetDescr
****************************************************************************//**
*
* \brief Sets a descriptor as current for specified MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \param descriptor
* This is the descriptor which will be associated with the channel.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_SetDescr(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum, const cy_stc_mdma_descr_t* descriptor)
{
pstcMDMA->CH[chNum].unCURR.u32Register = (uint32_t)descriptor;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_Enable
****************************************************************************//**
*
* The function is used to enable a MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_Enable(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
pstcMDMA->CH[chNum].unCTL.stcField.u1ENABLED = 1ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_Disable
****************************************************************************//**
*
* The function is used to disable a MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_Disable(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
pstcMDMA->CH[chNum].unCTL.stcField.u1ENABLED = 0ul;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_SetPriority
****************************************************************************//**
*
* The function is used to set a priority for the MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \param priority
* The priority to be set for the MDMA channel. The allowed values are 0,1,2,3.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_SetPriority(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum, uint32_t priority)
{
pstcMDMA->CH[chNum].unCTL.stcField.u2PRIO = priority;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetPriority
****************************************************************************//**
*
* Returns the priority of MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* The priority of the channel.
*
*******************************************************************************/
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetPriority(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return pstcMDMA->CH[chNum].unCTL.stcField.u2PRIO;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetCurrentDescr
****************************************************************************//**
*
* This function returns the descriptor that is active in the channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* The pointer to the descriptor associated with the channel.
*
*******************************************************************************/
__STATIC_INLINE cy_stc_mdma_descr_t* Cy_MDMA_Chnl_GetCurrentDescr(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return (cy_stc_mdma_descr_t*) pstcMDMA->CH[chNum].unCURR.u32Register;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetIntrType
****************************************************************************//**
*
* Sets the interrupt type parameter for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user has to use the
* configuration defines to initialize it.
*
* \param descriptor
* This is the descriptor-structure instance declared by the user. This is the
* descriptor being modified.
*
* \param interruptType
* The interrupt type set for the descriptor.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Descr_SetIntrType(cy_stc_mdma_descr_t* descriptor, uint32_t interruptType)
{
descriptor->unMDMA_DESCR_CTL.stcField.u2INTR_TYPE = interruptType;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetTrigInType
****************************************************************************//**
*
* Sets the trigger-in-type parameter for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user has to use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor structure instance declared by the user. This is the
* descriptor being modified.
*
* \param trigInType
* The trigger-in-type set for the descriptor. \ref cy_en_pdma_trigintype_t
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Descr_SetTrigInType(cy_stc_mdma_descr_t* descriptor, uint32_t trigInType)
{
descriptor->unMDMA_DESCR_CTL.stcField.u2TR_IN_TYPE = trigInType;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Descr_SetTrigOutType
****************************************************************************//**
*
* This function sets the trigger-out-type parameter for for the descriptor
* structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user has to use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor-structure instance declared by the user. This is the
* descriptor being modified.
*
* \param trigOutType
* The trigger-out-type set for the descriptor. \ref cy_en_pdma_trigouttype_t
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Descr_SetTrigOutType(cy_stc_mdma_descr_t* descriptor, uint32_t trigOutType)
{
descriptor->unMDMA_DESCR_CTL.stcField.u2TR_OUT_TYPE = trigOutType;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetInterruptStatus
****************************************************************************//**
*
* This function can be used to determine the interrupt status of the
* MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* Returns a status of an interrupt for specified channel.
*
*******************************************************************************/
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptStatus(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return (pstcMDMA->CH[chNum].unINTR.u32Register);
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetInterruptCause
****************************************************************************//**
*
* This function can be used to determine the interrupt reason of the
* MDMA channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* Returns a status of an interrupt for specified channel.
* (see \ref group_pdma_common_macro_MDMAIntrCauses for return values).
* For error related interrupt causes (INTR_CAUSE is "1", "2", "3", ..., "8"),
* the channel is disabled (HW sets CH_CTL.ENABLED to '0').
*
*******************************************************************************/
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptCause(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return (pstcMDMA->CH[chNum].unINTR.u32Register & 0xFFul);
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_SetInterrupt
****************************************************************************//**
*
* This function sets the interrupt.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
* intMask Interrupt to be enabled
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_SetInterrupt(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum, uint32_t intMask)
{
pstcMDMA->CH[chNum].unINTR_SET.u32Register = intMask;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetInterruptMask
****************************************************************************//**
*
* This function gets interrupt mask value.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* Returns an interrupt mask value.
*
*******************************************************************************/
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptMask(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return (pstcMDMA->CH[chNum].unINTR_MASK.u32Register);
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_SetInterruptMask
****************************************************************************//**
*
* This function sets interrupt mask value.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
* intMask Interrupt mask to be enabled
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_SetInterruptMask(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum, uint32_t intMask)
{
pstcMDMA->CH[chNum].unINTR_MASK.u32Register = intMask;
}
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_GetInterruptStatusMasked
****************************************************************************//**
*
* This function returns logical and of corresponding INTR and INTR_MASK fields
* in a single load operation.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
*******************************************************************************/
__STATIC_INLINE uint32_t Cy_MDMA_Chnl_GetInterruptStatusMasked(const volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
return (pstcMDMA->CH[chNum].unINTR_MASKED.u32Register);
}
#if defined(CPUSS_SW_TR_PRESENT) && (CPUSS_SW_TR_PRESENT == 1)
/*******************************************************************************
* Function Name: Cy_MDMA_Chnl_SetSwTrigger
****************************************************************************//**
*
* This function sets the SW trigger for a channel.
*
* \param pstcMDMA
* Pointer to the hardware MDMA block.
*
* \param chNum
* Channel number of the MDMA.
*
* \return
* Returns an interrupt mask value.
*
*******************************************************************************/
__STATIC_INLINE void Cy_MDMA_Chnl_SetSwTrigger(volatile stc_DMAC_t *pstcMDMA, uint32_t chNum)
{
pstcMDMA->CH[chNum].unTR_CMD.stcField.u1ACTIVATE = 1ul;
}
#endif
/** \} group_mdma_functions */
#if defined(__cplusplus)
}
#endif
#endif /* (CY_MDMA_H) */
/** \} group_mdma */
/* [] END OF FILE */
@@ -0,0 +1,454 @@
/***************************************************************************//**
* \file cy_pdma.c
* \version 1.0
*
* \brief
* The PDMA driver.
*
********************************************************************************
* \copyright
* Copyright 2016-2017, Cypress Semiconductor Corporation. All rights reserved.
* You may use this file only in accordance with the license, terms, conditions,
* disclaimers, and limitations in the end user license agreement accompanying
* the software package with which this file was provided.
*******************************************************************************/
#include "cy_pdma.h"
#if defined(__cplusplus)
extern "C" {
#endif
/*******************************************************************************
* Function Name: Cy_PDMA_Descr_Init
****************************************************************************//**
*
* This function initializes descriptor structure in SRAM from a pre-initialized
* configuration structure.
* This function initializes only the descriptor and not the channel.
* This function will operate on the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
*
* \param descriptor
* This is an instance of the descriptor structure declared by the user and
* initialized by this function.
*
* \param config
* This is a configuration structure that has all initialization information for
* the descriptor.
*
* \return
* This function returns /ref cy_en_pdma_status_t.
*
*******************************************************************************/
cy_en_pdma_status_t Cy_PDMA_Descr_Init(cy_stc_pdma_descr_t* descriptor, const cy_stc_pdma_descr_config_t* config)
{
cy_en_pdma_status_t retVal = CY_PDMA_ERR_UNC;
if ((descriptor != NULL) && (config != NULL))
{
descriptor->unPDMA_DESCR_CTL.stcField.u2WAIT_FOR_DEACT = config->deact;
descriptor->unPDMA_DESCR_CTL.stcField.u2INTR_TYPE = config->intrType;
descriptor->unPDMA_DESCR_CTL.stcField.u2TR_OUT_TYPE = config->trigoutType;
descriptor->unPDMA_DESCR_CTL.stcField.u2TR_IN_TYPE = config->triginType;
descriptor->unPDMA_DESCR_CTL.stcField.u1SRC_TRANSFER_SIZE = config->srcTxfrSize;
descriptor->unPDMA_DESCR_CTL.stcField.u1DST_TRANSFER_SIZE = config->destTxfrSize;
descriptor->unPDMA_DESCR_CTL.stcField.u1CH_DISABLE = config->chStateAtCmplt;
descriptor->unPDMA_DESCR_CTL.stcField.u2DATA_SIZE = config->dataSize;
descriptor->unPDMA_DESCR_CTL.stcField.u2DESCR_TYPE = config->descrType;
descriptor->u32PDMA_DESCR_SRC = (uint32_t)config->srcAddr;
descriptor->u32PDMA_DESCR_DST = (uint32_t)config->destAddr;
switch(config->descrType)
{
case (uint32_t)CY_PDMA_SINGLE_TRANSFER:
{
descriptor->unPDMA_DESCR_X_CTL.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_PDMA_1D_TRANSFER:
{
descriptor->unPDMA_DESCR_X_CTL.stcField.u12SRC_X_INCR = (uint32_t)config->srcXincr;
descriptor->unPDMA_DESCR_X_CTL.stcField.u12DST_X_INCR = (uint32_t)config->destXincr;
descriptor->unPDMA_DESCR_X_CTL.stcField.u8X_COUNT = (uint32_t)((config->xCount) - 1ul);
descriptor->unPDMA_DESCR_Y_CTL.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_PDMA_CRC_TRANSFER:
{
descriptor->unPDMA_DESCR_X_CTL.stcField.u12SRC_X_INCR = (uint32_t)config->srcXincr;
descriptor->unPDMA_DESCR_X_CTL.stcField.u12DST_X_INCR = 0ul;
descriptor->unPDMA_DESCR_X_CTL.stcField.u8X_COUNT = (uint32_t)((config->xCount) - 1ul);
descriptor->unPDMA_DESCR_Y_CTL.u32Register = (uint32_t)config->descrNext;
break;
}
case (uint32_t)CY_PDMA_2D_TRANSFER:
{
descriptor->unPDMA_DESCR_X_CTL.stcField.u12SRC_X_INCR = (uint32_t)config->srcXincr;
descriptor->unPDMA_DESCR_X_CTL.stcField.u12DST_X_INCR = (uint32_t)config->destXincr;
descriptor->unPDMA_DESCR_X_CTL.stcField.u8X_COUNT = (uint32_t)((config->xCount) - 1ul);
descriptor->unPDMA_DESCR_Y_CTL.stcField.u12SRC_Y_INCR = (uint32_t)config->srcYincr;
descriptor->unPDMA_DESCR_Y_CTL.stcField.u12DST_Y_INCR = (uint32_t)config->destYincr;
descriptor->unPDMA_DESCR_Y_CTL.stcField.u8Y_COUNT = (uint32_t)((config->yCount) - 1ul);
descriptor->u32PDMA_DESCR_NEXT_PTR = (uint32_t)config->descrNext;
break;
}
default:
{
/* Unsupported type of descriptor */
break;
}
}
retVal = CY_PDMA_SUCCESS;
}
else
{
retVal = CY_PDMA_INVALID_INPUT_PARAMETERS;
}
return retVal;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Descr_DeInit
****************************************************************************//**
*
* This function clears all the content of the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is being defined in Flash, then the user can't use this
* function to deinitialize it.
*
* \param descriptor
* This is an instance of the descriptor structure to be cleared.
*
*******************************************************************************/
void Cy_PDMA_Descr_DeInit(cy_stc_pdma_descr_t* descriptor)
{
descriptor->unPDMA_DESCR_CTL.u32Register = 0ul;
descriptor->u32PDMA_DESCR_SRC = 0ul;
descriptor->u32PDMA_DESCR_DST = 0ul;
descriptor->unPDMA_DESCR_X_CTL.u32Register = 0ul;
descriptor->unPDMA_DESCR_Y_CTL.u32Register = 0ul;
descriptor->u32PDMA_DESCR_NEXT_PTR = 0ul;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Descr_SetTxfrWidth
****************************************************************************//**
*
* This function sets the transfer widths for for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
*
* \param descriptor
* This is a descriptor-structure instance declared by the user. This is the
* descriptor being modified.
*
* \param dataElementSize
* This parameter provides the data element size of the descriptor.
* \ref cy_en_pdma_datasize_t
*
* \param srcTxfrWidth
* This parameter provides the source transfer size parameters of the descriptor.
* \ref cy_en_pdma_txfrsize_t
*
* \param dstTxfrWidth
* This parameter provides the destination transfer size parameters of the
* descriptor.
* \ref cy_en_pdma_txfrsize_t
*
*******************************************************************************/
void Cy_PDMA_Descr_SetTxfrWidth(cy_stc_pdma_descr_t* descriptor,
uint32_t dataElementSize,
uint32_t srcTxfrWidth,
uint32_t dstTxfrWidth)
{
descriptor->unPDMA_DESCR_CTL.stcField.u2DATA_SIZE = dataElementSize;
descriptor->unPDMA_DESCR_CTL.stcField.u1SRC_TRANSFER_SIZE = srcTxfrWidth;
descriptor->unPDMA_DESCR_CTL.stcField.u1DST_TRANSFER_SIZE = dstTxfrWidth;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Chnl_Init
****************************************************************************//**
*
* Initializes the PDMA channel with a descriptor and other parameters.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \param chNum
* Channel number of the PDMA.
*
* \param chnlConfig
* This parameter is a structure that has the initialization information for the
* channel.
*
* \return
* This function returns /ref cy_en_pdma_status_t.
*
*******************************************************************************/
cy_en_pdma_status_t Cy_PDMA_Chnl_Init(volatile stc_DW_t *pstcPDMA, uint32_t chNum, const cy_stc_pdma_chnl_config_t* chnlConfig)
{
cy_en_pdma_status_t retVal = CY_PDMA_ERR_UNC;
if ((pstcPDMA != NULL) && (chnlConfig != NULL))
{
/* Write this channel's SRAM data to initialize ECC properly */
pstcPDMA->CH_STRUCT[chNum].unSRAM_DATA0.u32Register = 0;
pstcPDMA->CH_STRUCT[chNum].unSRAM_DATA1.u32Register = 0;
/* Initialize the X/Y indices (which are undefined after reset) */
pstcPDMA->CH_STRUCT[chNum].unCH_IDX.u32Register = 0;
/* Set current descriptor */
pstcPDMA->CH_STRUCT[chNum].unCH_CURR_PTR.u32Register = (uint32_t)chnlConfig->PDMA_Descriptor;
/* Set if the channel is preemtable */
pstcPDMA->CH_STRUCT[chNum].unCH_CTL.stcField.u1PREEMPTABLE = chnlConfig->preemptable;
/* Set channel priority */
pstcPDMA->CH_STRUCT[chNum].unCH_CTL.stcField.u2PRIO = chnlConfig->priority;
/* Set enabled status */
pstcPDMA->CH_STRUCT[chNum].unCH_CTL.stcField.u1ENABLED = chnlConfig->enable;
retVal = CY_PDMA_SUCCESS;
}
else
{
retVal = CY_PDMA_INVALID_INPUT_PARAMETERS;
}
return (retVal);
}
/*******************************************************************************
* Function Name: Cy_PDMA_Chnl_DeInit
****************************************************************************//**
*
* Clears all the content of registers corresponding to the channel.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \param chNum
* Channel number of the PDMA.
*
*******************************************************************************/
void Cy_PDMA_Chnl_DeInit(volatile stc_DW_t *pstcPDMA, uint32_t chNum)
{
pstcPDMA->CH_STRUCT[chNum].unCH_CTL.u32Register = 0ul;
pstcPDMA->CH_STRUCT[chNum].unCH_IDX.u32Register = 0ul;
pstcPDMA->CH_STRUCT[chNum].unCH_CURR_PTR.u32Register = 0ul;
pstcPDMA->CH_STRUCT[chNum].unINTR_MASK.u32Register = 0ul;
pstcPDMA->CH_STRUCT[chNum].unINTR_SET.u32Register = 0ul;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Enable
****************************************************************************//**
*
* This function enables the PDMA block.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
*******************************************************************************/
void Cy_PDMA_Enable(volatile stc_DW_t *pstcPDMA)
{
pstcPDMA->unCTL.stcField.u1ENABLED = 1ul;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Disable
****************************************************************************//**
*
* This function disables the PDMA block.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
*******************************************************************************/
void Cy_PDMA_Disable(volatile stc_DW_t *pstcPDMA)
{
pstcPDMA->unCTL.stcField.u1ENABLED = 0ul;
}
/*******************************************************************************
* Function Name: Cy_PDMA_GetActiveChnl
****************************************************************************//**
*
* This function used to determine the channels that are active/pending in
* the PDMA block.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \return
* Returns a bit-field with all of the currently active/pending channels in the
* PDMA block.
*
*******************************************************************************/
uint32_t Cy_PDMA_GetActiveChnl(const volatile stc_DW_t *pstcPDMA)
{
return pstcPDMA->unSTATUS.stcField.u9CH_IDX;
}
/*******************************************************************************
* Function Name: Cy_PDMA_GetActiveSrcAddr
****************************************************************************//**
*
* Returns the source address that is being used for current transfer.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \return
* Returns a pointer to the source of transfer.
*
*******************************************************************************/
void * Cy_PDMA_GetActiveSrcAddr(const volatile stc_DW_t *pstcPDMA)
{
return ((void *)pstcPDMA->unACT_DESCR_SRC.u32Register);
}
/*******************************************************************************
* Function Name: Cy_PDMA_GetActiveDstAddr
****************************************************************************//**
*
* Returns the destination address that is being used for the current transfer.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \return
* Returns a pointer to the destination of transfer.
*
*******************************************************************************/
void * Cy_PDMA_GetActiveDstAddr(const volatile stc_DW_t *pstcPDMA)
{
return ((void *)pstcPDMA->unACT_DESCR_DST.u32Register);
}
/*******************************************************************************
* Function Name: Cy_PDMA_Chnl_ClearInterrupt
****************************************************************************//**
*
* This function clears the interrupt status.
*
* \param ptscPDMA
* Pointer to the hardware PDMA block.
*
* \param chNum
* Channel number of the PDMA.
*
*******************************************************************************/
void Cy_PDMA_Chnl_ClearInterrupt(volatile stc_DW_t *pstcPDMA, uint32_t chNum)
{
pstcPDMA->CH_STRUCT[chNum].unINTR.u32Register = CY_PDMA_INTR_BIT_MASK;
/* Readback of the register is required by hardware. */
(void) pstcPDMA->CH_STRUCT[chNum].unINTR.u32Register;
}
/*******************************************************************************
* Function Name: Cy_PDMA_Descr_SetNxtDescr
****************************************************************************//**
*
* Sets a next descriptor parameter for the descriptor structure.
* This function will operate in the descriptor only if it was defined as a RAM
* variable.
* If the descriptor is defined in Flash, then the user must use the
* configuration defines to initialize it.
* Based on descriptor type the offset of address for the next descriptor may
* vary. For a single transfer descriptor type, this register is at offset 0x0c.
* For a 1D transfer descriptor type, this register is at offset 0x10. For a 2D
* transfer descriptor type, this register is at offset 0x14.
*
* \param descriptor
* This is the descriptor-structure instance declared by the user. The function
* modifies the destination-address parameter in this descriptor.
*
* \param nxtDescriptor
* The pointer to the next descriptor.
*
*******************************************************************************/
void Cy_PDMA_Descr_SetNxtDescr(cy_stc_pdma_descr_t* descriptor, const cy_stc_pdma_descr_t* nxtDescriptor)
{
switch(descriptor->unPDMA_DESCR_CTL.stcField.u2DESCR_TYPE)
{
case (uint32_t)CY_PDMA_SINGLE_TRANSFER:
{
descriptor->unPDMA_DESCR_X_CTL.u32Register = (uint32_t)nxtDescriptor;
break;
}
case (uint32_t)CY_PDMA_1D_TRANSFER:
case (uint32_t)CY_PDMA_CRC_TRANSFER:
{
descriptor->unPDMA_DESCR_Y_CTL.u32Register = (uint32_t)nxtDescriptor;
break;
}
case (uint32_t)CY_PDMA_2D_TRANSFER:
{
descriptor->u32PDMA_DESCR_NEXT_PTR = (uint32_t)nxtDescriptor;
break;
}
default:
{
/* Unsupported type of descriptor */
break;
}
}
}
/*******************************************************************************
* Function Name: Cy_PDMA_CRC_Config
****************************************************************************//**
*******************************************************************************/
void Cy_PDMA_CRC_Config ( volatile stc_DW_t * pstcPDMA,
const cy_stc_pdma_crc_config_t* pstcCrcConfig)
{
pstcPDMA->unCRC_CTL.stcField.u1DATA_REVERSE = pstcCrcConfig->data_reverse;
pstcPDMA->unCRC_CTL.stcField.u1REM_REVERSE = pstcCrcConfig->rem_reverse;
pstcPDMA->unCRC_DATA_CTL.stcField.u8DATA_XOR = pstcCrcConfig->data_xor;
pstcPDMA->unCRC_LFSR_CTL.stcField.u32LFSR32 = pstcCrcConfig->lfsr32;
pstcPDMA->unCRC_POL_CTL.stcField.u32POLYNOMIAL = pstcCrcConfig->polynomial;
pstcPDMA->unCRC_REM_CTL.stcField.u32REM_XOR = pstcCrcConfig->rem_xor;
}
/*******************************************************************************
* Function Name: Cy_PDMA_CRC_Config
****************************************************************************//**
*******************************************************************************/
uint32_t Cy_PDMA_GetCrcRemainderResult ( volatile stc_DW_t * pstcPDMA)
{
return pstcPDMA->unCRC_REM_RESULT.u32Register;
}
#if defined(__cplusplus)
}
#endif
/* [] END OF FILE */

Some files were not shown because too many files have changed in this diff Show More