diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 96fde146e..4a1dda7cd 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,6 +1,6 @@ # These are supported funding model platforms -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: ultrawipf patreon: Gigawipf open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username @@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username -custom: "paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B23BD5FGD5CH8" +custom: "https://www.paypal.com/donate/?hosted_button_id=2SU5VLFRFZACG" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a80ca7fb..783721603 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -80,9 +80,9 @@ jobs: - name: Make changelog run: | echo -e "### Firmware changes\n" > /tmp/CHANGELOG.md - cat ${{ github.workspace }}/CHANGELOG.md >> /tmp/CHANGELOG.md + perl -0777 -ne 'print "$1\n$2" if /^(#+.+)\n((?:[^#\n]+\n)+)/m; exit' ${{ github.workspace }}/CHANGELOG.md >> /tmp/CHANGELOG.md echo -e "\n### Configurator changes\n" >> /tmp/CHANGELOG.md - cat ${{ github.workspace }}/Configurator/CHANGELOG.md >> /tmp/CHANGELOG.md + perl -0777 -ne 'print "$1\n$2" if /^(#+.+)\n((?:[^#\n]+\n)+)/m; exit' ${{ github.workspace }}/Configurator/CHANGELOG.md >> /tmp/CHANGELOG.md # Create release diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6fa0308..8f2df35ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,185 @@ -### Changes this version: +### v1.17.0 - Added SPI speed selector to MagnTek encoders - Added "reg" and "save" commands to MagnTek encoder. Allows programming MT6835 encoders (debug=1 mode required!) +- Set ABN encoder filter to 5 for F407 and F407_DISCO. Should improve encoder stability in noisy environments. +- Fixed Magntek encoder forwarding causing choppy FFB +- Fixed TMC4671 ext encoder having inverted forces if previously inverted by dual enc mode -### Changes in 1.16: +### v1.16.6 +- Added SPI speed selector to MagnTek encoders +- Added "reg" and "save" commands to MagnTek encoder. Allows programming MT6835 encoders (debug=1 mode required!) +- Set ABN encoder filter to 5 for F407 and F407_DISCO. Should improve encoder stability in noisy environments. -Internal changes: -- CAN port interfaces rewritten -- HAL updated -- Migrated to CPP20 -- Upgraded to tinyusb 0.17 -- Improved microsecond counter implementation +### v1.16.5 +- Reformatted USB serial string as hex and added command to request UID as hex string +- Added device name to USB Product name +- Added support for F407 OTP section +- Added support for MagnTek MT6835 via SPI (SPI3 port, MagnTek encoder class) + +### v1.16.4 +- Added exponential torque postprocessing for game effects + +### v1.16.3 +- Added remote CAN button/analog source mainclass + +### v1.16.2 +- Inverted Y axis direction vector magnitude + - Fixes 2 axis setups in XPforce and DCS and other flight sims +- Changed 2 axis conditional effects to ignore direction vectors (Fixes DCS) +- Modified HID 2 axis descriptor, added back second direction for compliance +- Fixed chip temp sometimes glitching +- TMC debug mode: Changed openloopspeed command to use torque mode instead of raw PWM. Added new openloopspeedpwm to control raw PWM. +- CAN bus corrected packet length when packet is sent as command +- Corrected CAN speed preset in can bridge GVRET mode (savvycan works again) +- Using interrupt transfer for TMC4671 encoder forwarding (Fixed again) + +### v1.16.0 - Added MyActuator RMD CAN support class. - Temporary implementation until CAN protocol changes. Usable but might be improved in the future - Fixed issues in CAN analog class for packet 2. Allow shorter frames - F407: ADC now triggered by timer to reduce interrupt frequency - Using analog VREF for voltage sensing (better accuracy with unstable 3.3V) - Added chip temperature readout -- Added remote CAN button/analog source mainclass -- Added exponential torque postprocessing for game effects -- Reformatted USB serial string as hex and added command to request UID as hex string -- Added device name to USB Product name -- Added support for F407 OTP section -- Added support for MagnTek MT6835 via SPI (SPI3 port, MagnTek encoder class) \ No newline at end of file +- CAN port interfaces rewritten +- HAL updated +- Migrated to CPP20 +- Upgraded to tinyusb 0.17 +- Improved microsecond counter implementation + +### v1.15.1 +- Fixed BISS-C encoder sometimes overflowing one rotation at startup +- Added BISS-C direction inversion function (Default true). Most BISS-C encoders count CW while most others and TMC count CCW. +- Standardized encoder counting direction counting up in CCW direction as a more common industrial standard +- Fixed idle spring effect not working before first save +- Retuned speed limiter function. Removed averaging. Should be more stable for high resolution encoders if high bandwidth speed filter preset is selected +- Force ramps up slowly on potential sharp position changes such as recentering +- FFB led now shows FFB state. On when FFB actuators enabled. Still blinks on clipping + +### v1.15.0 +- Added independend friction and inertia effects to axis +- ODrive class can save encoder position offset +- Reverted the forza fix for 2 axis setups. + - TODO: test and report if behaviour works for all games with the angle always being used for 1 axis modes (Games must send 90° on X axis effects instead of 0°). + +### v1.14.4 +- TMC E-Stop handled even during calibration by pausing and disabling driver +- E-Stop checked correctly after startup. You can now disable force and delay startup by setting E-Stop during startup. +- Digital and Analog sources are disabled by default +- Biss-C 1 rotation offset glitch at first packet fixed +- Reverted CAN retransmission to enabled temporarily. Fixes 2 axis ODrive issues. + +### v1.14.3 +- Only activate brake resistor if vint and vext are >6.5V. Prevents board from activating resistor if only usb powered and a fault reset loop +- Changed behaviour of direction enable and axis enable bits in set_effect report to always apply direction vector + - Fix for Forza Motorsport + +### v1.14.2 +- Added local button pulse mode + +### v1.14.0 +- Save TMC space vector PWM mode in flash. Should be usually on for BLDC motors if the star point is isolated. +- Allow using the motors flux component to dissipate energy with the TMC4671 instead of the brake resistor. May cause noticable braking in the motor but takes stress off the resistor. +- Axis speed limiter usable and saved in flash. +- Removed unused hall direction flash setting. + +### v1.13.3 +- Added uid command (`sys.uid?` returns first 64 bits as val and second 32 as adr) + +### v1.13.2 +- Added effect monitoring per axis + +### v1.13.1 +- Added PWM direction toggle + +### v1.13.0 +- Added basic iterative TMC PI autotuning +- Fixed issues with CAN transmission with multiple axes +- Added SSI encoder support (AMT232B) +- Fixed SPI buttons not working (SPI2 DMA on F407) +- Dynamic TMC encoder alignment current based on current limit + +### v1.12.1 +- Added part of unique serial number to usb interface names for easier identification of multiple devices +- Enabled Simplemotion for F407_DISCO build + +### v1.12.0 +- Added support for Simplemotion V2 (Ioni/Argon motor drivers) + +### v1.11.2 +- Fixed a possible crash if unparsable/too large numbers are sent in a command +- Removed unused direction field from descriptor in 2 axis desc +- Workarounds for 2 axis conditional effects giving condition blocks priority over direction angles + +### v1.11.1 +- Added new subproject for third party devkits (F407DISCO target) +- Added serialfx effect manager for a command based FFB mainclass (Instead of PID FFB) +- Added Serial FFB mainclass mode +- Added mosfet BBM time to TMC hardware selection +- TMC enable pin is set when TMC debug mode starts +- Added option to change SPI speed for buttons. Can be helpful if the connection is unreliable +- Added back second VESC instance for 2 axis vesc setups +- Separate motor driver selection lists per axis (No double odrive/vesc/tmc instance options in motor driver lists) + +### v1.10.1 +- Changed default power from 2000 to 5000 as 2000 is not enough to calibrate many motors +- Internal change moving effects into effectscalculator to simplify managing effects from different sources +- Effect intensity tuning value now only affects game effects. Fixes the effect intensity incorrectly affecting the endstop. + +### v1.10.0 +- Added local encoder index option to reload a previously stored offset +- Fixed an issue with 2 axis FFB effects on second axis +- Added TMC4671 biquad filter option + - Lowpass, notch and peak modes (fixed Q factor, saved frequency) +- Improved BISS performance when used with TMC +- Fixed an issue with live effects statistics jumping to 0 using double buffers +- Added missing command flags and help messages + +### v1.9.7 +- Fixed CDC serial port sometimes losing replies on with some USB ports + +### v1.9.6: +- Added analog filter option +- Main effect loop runs in higher priority thread than idle +- Added ADS111X analog source +- Added user configurable axis encoder ratios for setups with reductions +- Added effect filter option (Speed/accel filter presets for different encoders) +- Added effects monitoring +- Added some analog autorange margin +- Added min/max commands to analog processing for manual ranges +- Added analog processing functions to ADS111X +- Selecting a "none" encoder will remove the axis value. Allows analog inputs to be used as the primary axis. +- Added constant force rate command +- Highly improved uart command stability (default baud rate 115200) +- Added command to check command flags (cls.cmdinfo?cmdid) +- Added advanced filter mode to switch between custom and default conditional effect output filters ("fx.filterProfile_id") +- Automatic flash erase condition changed from major version change to separate flash version counter + +### v1.8.8 +- Rescaled endstop to encoder angle (makes strength feel the same at every range) +- Changed SPI button saved count from 0-63 to 1-64 (will invalidate your setting) +- Added CAN next frame length command to send frames with different headers +- Emergency stop can be reset and only disables torque +- Added estop command +- Optimized string based command interfaces +- Effects honor the gain setting (Makes Forza Horizon work) + +### v1.8.7 +- FFBWheel and FFBJoystick classes split for 1 and 2 axis FFB (Allows to use different HID descriptors) +- Added a single axis HID descriptor (currently not used for compatibility reasons. enable by defining FFBWHEEL_USE_1AXIS_DESC) +- Default SPI button speed increased to 1.3MHz +- Added CAN button source +- Added CAN analog source +- Moved CAN and i2c speed settings to port class +- Fixed CAN bridge RTR frames +- HID interface sends ACKs +- Improved help command formatting and added flags +- Added I2C fast mode 400kHz +- Added BISS-C encoder +- Fixed MT Encoder +- Reworked TMC external encoder system +- Digital and analog source readout command + + +# Persistent changelog +Append changes at the top. +The first section is used in release comments \ No newline at end of file diff --git a/Configurator b/Configurator index 9eee2b5ff..7e1cbb442 160000 --- a/Configurator +++ b/Configurator @@ -1 +1 @@ -Subproject commit 9eee2b5ffe44f2dec1c6d2eae4a0ad987d53b5c8 +Subproject commit 7e1cbb4421af872deacf84ee3ba0b584c2815aa8 diff --git a/Firmware/.gitignore b/Firmware/.gitignore index 1757ea432..f6faee73a 100644 --- a/Firmware/.gitignore +++ b/Firmware/.gitignore @@ -1,4 +1,4 @@ -/*Debug/ +**/Debug/ **/Release/ **/.settings/ *.launch @@ -7,5 +7,4 @@ .mxproject fixlang /build/ -/OpenFFBoard/ -/*Targets/F407VG/*.cfg +/OpenFFBoard/ \ No newline at end of file diff --git a/Firmware/FFBoard/Inc/AxesManager.h b/Firmware/FFBoard/Inc/AxesManager.h index f1b9e3762..fb361e127 100644 --- a/Firmware/FFBoard/Inc/AxesManager.h +++ b/Firmware/FFBoard/Inc/AxesManager.h @@ -47,10 +47,10 @@ class AxesManager void emergencyStop(bool reset); void resetPosZero(); + void updateSamplerate(float newSamplerate); + private: volatile Control_t* control; - volatile bool *p_usb_disabled; - volatile bool *p_emergency; std::shared_ptr effects_calc; uint16_t axis_count = 0; std::vector> axes; diff --git a/Firmware/FFBoard/Inc/Axis.h b/Firmware/FFBoard/Inc/Axis.h index 05e87fdc9..5b044c187 100644 --- a/Firmware/FFBoard/Inc/Axis.h +++ b/Firmware/FFBoard/Inc/Axis.h @@ -71,7 +71,7 @@ struct AxisConfig struct metric_t { float accel = 0; // in deg/s² float speed = 0; // in deg/s - int32_t pos = 0; // scaled position as 16b int -0x7fff to 0x7fff + int32_t pos_scaled_16b = 0; // scaled position as 16b int -0x7fff to 0x7fff matching FFB ranges float pos_f = 0; // scaled position as float. -1 to 1 range float posDegrees = 0; // Position in degrees. Not scaled to selected range int32_t torque = 0; // total of effect + endstop torque @@ -171,6 +171,9 @@ class Axis : public PersistentStorage, public CommandHandler, public ErrorHandle bool updateTorque(int32_t* totalTorque); + void updateSamplerate(float newSamplerate); + void updateFilters(uint8_t profileId); + void setGearRatio(uint8_t numerator,uint8_t denominator); static const std::vector> axis1_drivers; @@ -259,7 +262,7 @@ class Axis : public PersistentStorage, public CommandHandler, public ErrorHandle const biquad_constant_t filterFrictionCst = {50, 20}; const biquad_constant_t filterInertiaCst = {20, 20}; uint8_t filterProfileId = 1; // Default medium (1) as this is the most common encoder resolution and users can go lower or higher if required. - const float filter_f = 1000; // 1khz + float filter_f = 1000; // 1khz default. should be set at runtime once the actual rate is known const int32_t intFxClip = 20000; uint8_t damperIntensity = 30; diff --git a/Firmware/FFBoard/Inc/CmdParser.h b/Firmware/FFBoard/Inc/CmdParser.h index 48094d3c5..be3221884 100644 --- a/Firmware/FFBoard/Inc/CmdParser.h +++ b/Firmware/FFBoard/Inc/CmdParser.h @@ -17,7 +17,9 @@ #include "CommandHandler.h" #include "ringbufferwrapper.h" +#ifndef CMDPARSER_MAX_VALID_CAPACITY #define CMDPARSER_MAX_VALID_CAPACITY 2048 +#endif class CommandHandler; class CommandInterface; diff --git a/Firmware/FFBoard/Inc/CommandHandler.h b/Firmware/FFBoard/Inc/CommandHandler.h index 0a5cb92a7..71c005a53 100644 --- a/Firmware/FFBoard/Inc/CommandHandler.h +++ b/Firmware/FFBoard/Inc/CommandHandler.h @@ -13,7 +13,7 @@ #include "mutex.hpp" #include "ClassIDs.h" #include - +#include #define CMDFLAG_GET 0x01 #define CMDFLAG_SET 0x02 @@ -22,6 +22,7 @@ #define CMDFLAG_SETADR 0x20 #define CMDFLAG_HIDDEN 0x40 #define CMDFLAG_DEBUG 0x80 +#define CMDFLAG_EXTOVERRIDE 0x80000000 #define CMDFLAG_STR_ONLY 0x100 #define CMDFLAG_HID_ONLY 0x200 // Command not available for string based parsers @@ -50,8 +51,8 @@ class CmdHandlerCommanddef {}; const char* cmd = nullptr; const char* helpstring = nullptr; - const uint32_t cmdId; - const uint32_t flags; + uint32_t cmdId; + uint32_t flags; }; struct CmdHandlerInfo @@ -260,17 +261,22 @@ class CommandHandler { */ template void registerCommand(const char* cmd,const ID cmdid,const char* help=nullptr,uint32_t flags = 0){ - for(CmdHandlerCommanddef& cmdDef : registeredCommands){ - if(cmdDef.cmdId == static_cast(cmdid)) - return; //already present - } + registerCommand_INT(cmd, static_cast(cmdid), help, flags); + } - this->registeredCommands.emplace_back(cmd, help,static_cast(cmdid),flags); - this->registeredCommands.shrink_to_fit(); + + virtual void postCmdhandlerInit(){}; // Can implement in external file to override command flags + template + /** + * Can override command flags to make it read only + */ + void overrideCommandFlags(const ID cmdid,uint32_t flagmask = 0){ + overrideCommandFlags_INT(static_cast(cmdid), flagmask); } + protected: void setInstance(uint8_t instance); bool commandsEnabled = true; @@ -287,6 +293,9 @@ class CommandHandler { CmdHandlerInfo cmdHandlerInfo; + void registerCommand_INT(const char* cmd,const uint32_t cmdid,const char* help=nullptr,uint32_t flags = 0); + void overrideCommandFlags_INT(const uint32_t cmdid,uint32_t flagmask = CMDFLAG_GET | CMDFLAG_GETADR); + }; #endif /* COMMANDHANDLER_H_ */ diff --git a/Firmware/FFBoard/Inc/CommandInterface.h b/Firmware/FFBoard/Inc/CommandInterface.h index d99ec7b92..b70a57da6 100644 --- a/Firmware/FFBoard/Inc/CommandInterface.h +++ b/Firmware/FFBoard/Inc/CommandInterface.h @@ -60,7 +60,7 @@ class StringCommandInterface : public CommandInterface{ CmdParser parser; // String parser }; - +#define CDC_CMD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 4096 : 1024) //receives bytes from mainclass. calls its own parser instance, calls global parser thread, passes replies back to cdc port. class CDC_CommandInterface : public StringCommandInterface,public cpp_freertos::Thread{ public: @@ -83,7 +83,7 @@ class CDC_CommandInterface : public StringCommandInterface,public cpp_freertos:: bool nextFormat = false; std::string sendBuffer; uint32_t bufferLength = 0; - const uint32_t maxSendBuffer = 1024; // Max buffered command size before sending immediately + const uint32_t maxSendBuffer = CDC_CMD_BUFFER_SIZE; // Max buffered command size before sending immediately }; diff --git a/Firmware/FFBoard/Inc/EffectsCalculator.h b/Firmware/FFBoard/Inc/EffectsCalculator.h index e065c0335..feff5f0a0 100644 --- a/Firmware/FFBoard/Inc/EffectsCalculator.h +++ b/Firmware/FFBoard/Inc/EffectsCalculator.h @@ -84,7 +84,7 @@ class EffectsCalculator: public PersistentStorage, uint8_t getGain(); void logEffectType(uint8_t type,bool remove = false); //void setDirectionEnableMask(uint8_t mask); - void calcStatsEffectType(uint8_t type, int16_t force,uint8_t axis); + void calcStatsEffectType(uint8_t type, int32_t force,uint8_t axis); void logEffectState(uint8_t type,uint8_t state); void resetLoggedActiveEffects(bool reinit); @@ -100,6 +100,8 @@ class EffectsCalculator: public PersistentStorage, // Thread impl void Run(); + void updateSamplerate(float newSamplerate); // Must be called if update rate is changed to update filters and effects + protected: @@ -108,7 +110,7 @@ class EffectsCalculator: public PersistentStorage, // Filters effect_biquad_t filter[2]; // 0 is the default profile and the custom for CFFilter, CUSTOM_PROFILE_ID is the custom slot uint8_t filterProfileId = 0; - const uint32_t calcfrequency = 1000; // HID frequency 1khz + uint32_t calcfrequency = 1000; // HID frequency 1khz const float qfloatScaler = 0.01; // Rescale factor for conditional effect to boost or decrease the intensity @@ -150,10 +152,11 @@ class EffectsControlItf{ virtual void set_gain(uint8_t gain) = 0; virtual void cfUpdateEvent(); virtual void fxUpdateEvent(); + virtual void updateSamplerate(float newSamplerate) = 0; // Should be called when update loop rate is changed private: - FastMovingAverage fxPeriodAvg{20}; - FastMovingAverage cfUpdatePeriodAvg{20}; + FastMovingAverage fxPeriodAvg{5}; + FastMovingAverage cfUpdatePeriodAvg{5}; uint32_t lastFxUpdate = 0; uint32_t lastCfUpdate = 0; diff --git a/Firmware/FFBoard/Inc/HidFFB.h b/Firmware/FFBoard/Inc/HidFFB.h index 28ddc6707..7dae12394 100644 --- a/Firmware/FFBoard/Inc/HidFFB.h +++ b/Firmware/FFBoard/Inc/HidFFB.h @@ -39,6 +39,7 @@ class HidFFB: public UsbHidHandler, public EffectsControlItf { void sendStatusReport(uint8_t effect); void setDirectionEnableMask(uint8_t mask); + void updateSamplerate(float newSamplerate); private: // HID diff --git a/Firmware/FFBoard/Inc/SPI.h b/Firmware/FFBoard/Inc/SPI.h index b1a063f79..0f6afedfe 100644 --- a/Firmware/FFBoard/Inc/SPI.h +++ b/Firmware/FFBoard/Inc/SPI.h @@ -14,6 +14,7 @@ #include "SpiHandler.h" #include "semaphore.hpp" +#include struct SPIConfig { SPIConfig(OutputPin cs,bool cspol = true) @@ -77,7 +78,7 @@ class SPIPort: public SpiHandler { bool hasFreePins(); uint32_t getBaseClk(); - std::pair getClosestPrescaler(float clock); + std::pair getClosestPrescaler(float clock,float min = 0, float max = INFINITY); SPI_HandleTypeDef* getPortHandle(); diff --git a/Firmware/FFBoard/Inc/SerialFFB.h b/Firmware/FFBoard/Inc/SerialFFB.h index e16cfc1a7..9ec6e26be 100644 --- a/Firmware/FFBoard/Inc/SerialFFB.h +++ b/Firmware/FFBoard/Inc/SerialFFB.h @@ -37,6 +37,7 @@ class SerialFFB : public CommandHandler, public EffectsControlItf{ void setMagnitude(uint8_t idx,int16_t magnitude); void setEffectState(uint8_t id, bool state); + void updateSamplerate(float newSamplerate); private: static ClassIdentifier info; diff --git a/Firmware/FFBoard/Inc/SystemCommands.h b/Firmware/FFBoard/Inc/SystemCommands.h index 46ef39695..ec6118529 100644 --- a/Firmware/FFBoard/Inc/SystemCommands.h +++ b/Firmware/FFBoard/Inc/SystemCommands.h @@ -11,7 +11,7 @@ #include "CommandHandler.h" enum class FFBoardMain_commands : uint32_t{ - help=0,save=1,reboot=2,dfu=3,swver=4,hwtype=5,lsmain,main,lsactive,format,errors,errorsclr,flashdump,flashraw,vint,vext,mallinfo,heapfree,taskstats,debug,devid,uid,temp,otp,signature + help=0,save=1,reboot=2,dfu=3,swver=4,hwtype=5,lsmain,main,lsactive,format,errors,errorsclr,flashdump,flashraw,vint,vext,mallinfo,heapfree,taskstats,debug,devid,uid,temp,otp,signature,tasklist }; class SystemCommands : public CommandHandler { diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index e18918af6..ba11df6d5 100644 --- a/Firmware/FFBoard/Inc/constants.h +++ b/Firmware/FFBoard/Inc/constants.h @@ -8,7 +8,7 @@ * For more settings see target_constants.h in a target specific folder */ -static const uint8_t SW_VERSION_INT[3] = {1,16,6}; // Version as array. 8 bit each! +static const uint8_t SW_VERSION_INT[3] = {1,17,0}; // Version as array. 8 bit each! #ifndef MAX_AXIS #define MAX_AXIS 2 // ONLY USE 2 for now else screws HID Reports #endif @@ -31,11 +31,19 @@ static const uint8_t SW_VERSION_INT[3] = {1,16,6}; // Version as array. 8 bit ea #ifdef FFBWHEEL #ifdef FFBWHEEL_USE_1AXIS_DESC +#ifdef HIDAXISRES_USE_32B_DESC +#define AXIS1_FFB_HID_DESC_32B +#else #define AXIS1_FFB_HID_DESC +#endif +#else +#ifdef HIDAXISRES_USE_32B_DESC +#define AXIS2_FFB_HID_DESC_32B #else #define AXIS2_FFB_HID_DESC #endif #endif +#endif #ifdef FFBJOYSTICK #define AXIS2_FFB_HID_DESC @@ -104,7 +112,7 @@ static const uint8_t SW_VERSION_INT[3] = {1,16,6}; // Version as array. 8 bit ea #define SIGNATURE #endif -#if defined(FLASH_OTP_BASE) && defined(FLASH_OTP_END) +#if (defined(FLASH_OTP_BASE) && defined(FLASH_OTP_END)) || (defined(I2C_EEPROM_OTP_ADR) && defined(I2C_PORT_EEPROM)) #define OTPMEMORY #endif diff --git a/Firmware/FFBoard/Inc/cpp_target_config.h b/Firmware/FFBoard/Inc/cpp_target_config.h index fcf4f7ed6..20b898825 100644 --- a/Firmware/FFBoard/Inc/cpp_target_config.h +++ b/Firmware/FFBoard/Inc/cpp_target_config.h @@ -47,4 +47,8 @@ extern const OutputPin debugpin; extern const OutputPin gpMotor; #endif +#if defined(I2C_PORT_EEPROM) +extern I2CPort i2cport_int; +#endif + #endif diff --git a/Firmware/FFBoard/Inc/ffb_defs.h b/Firmware/FFBoard/Inc/ffb_defs.h index e78b1e6fd..f6f0a8eef 100644 --- a/Firmware/FFBoard/Inc/ffb_defs.h +++ b/Firmware/FFBoard/Inc/ffb_defs.h @@ -96,55 +96,96 @@ // Only include these for cpp #ifdef __cplusplus -// HID gamepad report - +template struct __attribute__((__packed__)) reportHID_t { uint8_t id = 1; uint64_t buttons = 0; +#if MAX_AXIS >= 1 + dtypeaxis X = 0; +#else int16_t X = 0; +#endif +#if MAX_AXIS >= 2 + dtypeaxis Y = 0; +#else int16_t Y = 0; +#endif +#if MAX_AXIS >= 3 + dtypeaxis Z = 0; +#else int16_t Z = 0; +#endif int16_t RX = 0; int16_t RY = 0; int16_t RZ = 0; int16_t Dial = 0; int16_t Slider = 0; }; - -/* - * Helper function to access analog axes in packed HID report struct +/** + * Helper class for double buffered HID gamepad reports to allow use of different datatypes for main axes + * Double buffer allows testing if data has changed before sending */ -inline void setHidReportAxis(reportHID_t *report, uint8_t idx, int16_t val){ - switch(idx){ - case 0: - report->X = val; - break; - case 1: - report->Y = val; - break; - case 2: - report->Z = val; - break; - case 3: - report->RX = val; - break; - case 4: - report->RY = val; - break; - case 5: - report->RZ = val; - break; - case 6: - report->Dial = val; - break; - case 7: - report->Slider = val; - break; - default: - return; +class HID_GamepadReport_base{ +public: + virtual void setHidReportAxis(uint8_t idx, uint32_t val) = 0; + virtual uint8_t* getBuffer() = 0; + virtual uint32_t getLength() = 0; + virtual uint64_t getButtons() = 0; + virtual void setButtons(uint64_t btn) = 0; //! Must use setter to prevent unaligned access + virtual bool changed() = 0; + virtual void swap() = 0; +}; + +template +class HID_GamepadReport : public HID_GamepadReport_base{ +private: + reportHID_t report1; + reportHID_t report2; + reportHID_t* report = &report1; + +public: + uint64_t getButtons() override {return report->buttons;} + void setButtons(uint64_t btn) override {report->buttons = btn;} + + void setHidReportAxis(uint8_t idx, uint32_t val) override { + switch(idx){ + case 0: + report->X = val; + break; + case 1: + report->Y = val; + break; + case 2: + report->Z = val; + break; + case 3: + report->RX = val; + break; + case 4: + report->RY = val; + break; + case 5: + report->RZ = val; + break; + case 6: + report->Dial = val; + break; + case 7: + report->Slider = val; + break; + default: + return; + } } -} + bool changed() override {return memcmp(&report1,&report2,sizeof(reportHID_t)) != 0;} + void swap() override {report = (report == &report1 ? &report2 : &report1);} // Swaps report buffers + + uint32_t getLength() override {return sizeof(reportHID_t);} + uint8_t* getBuffer() override {return reinterpret_cast(this->report);} + +}; + typedef struct { diff --git a/Firmware/FFBoard/Inc/flash_helpers.h b/Firmware/FFBoard/Inc/flash_helpers.h index a409a48fe..68961e711 100644 --- a/Firmware/FFBoard/Inc/flash_helpers.h +++ b/Firmware/FFBoard/Inc/flash_helpers.h @@ -23,6 +23,7 @@ extern "C" { #include #include +#include bool Flash_Init(); bool Flash_Write(uint16_t adr,uint16_t dat); // Writes or updates only if changed or missing @@ -58,4 +59,7 @@ inline std::tuple unpack(uint16_t v) { #endif +extern const std::span> flash_factory_defaults; // address,value pair +void Flash_Write_Defaults(); + #endif /* FLASH_HELPERS_H_ */ diff --git a/Firmware/FFBoard/Src/AxesManager.cpp b/Firmware/FFBoard/Src/AxesManager.cpp index dd1edd726..c2855fe11 100644 --- a/Firmware/FFBoard/Src/AxesManager.cpp +++ b/Firmware/FFBoard/Src/AxesManager.cpp @@ -102,19 +102,25 @@ bool AxesManager::setAxisCount(int8_t count) { } void AxesManager::usbSuspend() { - for (auto &axis : axes) { - axis->usbSuspend(); - } + for (auto &axis : axes) { + axis->usbSuspend(); + } } void AxesManager::usbResume() { - for (auto &axis : axes) { - axis->usbResume(); - } + for (auto &axis : axes) { + axis->usbResume(); + } } void AxesManager::resetPosZero() { - for (auto &axis : axes) { - axis->setPos(0); - } + for (auto &axis : axes) { + axis->setPos(0); + } +} + +void AxesManager::updateSamplerate(float newSamplerate){ + for (auto &axis : axes) { + axis->updateSamplerate(newSamplerate); + } } diff --git a/Firmware/FFBoard/Src/Axis.cpp b/Firmware/FFBoard/Src/Axis.cpp index 2a61616b4..41f487b28 100644 --- a/Firmware/FFBoard/Src/Axis.cpp +++ b/Firmware/FFBoard/Src/Axis.cpp @@ -553,17 +553,17 @@ metric_t* Axis::getMetrics() { } /** - * Returns position as 16b int scaled to gamepad range + * Returns position as int scaled to gamepad range */ int32_t Axis::getLastScaledEnc() { - return clip(metric.current.pos,-0x7fff,0x7fff); + return clip(metric.current.pos_f * 0x7fffffff,-0x7fffffff,0x7fffffff); // Calc from float pos } /** * Changes intensity of idle spring when FFB is off */ int32_t Axis::updateIdleSpringForce() { - return clip((int32_t)(-metric.current.pos*idlespringscale),-idlespringclip,idlespringclip); + return clip((int32_t)(-metric.current.pos_scaled_16b*idlespringscale),-idlespringclip,idlespringclip); } /* @@ -638,7 +638,7 @@ void Axis::setFxRatio(uint8_t val) { void Axis::resetMetrics(float new_pos= 0) { // pos is degrees metric.current = metric_t(); metric.current.posDegrees = new_pos; - std::tie(metric.current.pos,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation); + std::tie(metric.current.pos_scaled_16b,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation); metric.previous = metric_t(); // Reset filters speedFilter.calcBiquad(); @@ -653,13 +653,13 @@ void Axis::updateMetrics(float new_pos) { // pos is degrees metric.previous = metric.current; metric.current.posDegrees = new_pos; - std::tie(metric.current.pos,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation); + std::tie(metric.current.pos_scaled_16b,metric.current.pos_f) = scaleEncValue(new_pos, degreesOfRotation); // compute speed and accel from raw instant speed normalized - float currentSpeed = (new_pos - metric.previous.posDegrees) * 1000.0; // deg/s + float currentSpeed = (new_pos - metric.previous.posDegrees) * this->filter_f; // deg/s metric.current.speed = speedFilter.process(currentSpeed); - metric.current.accel = accelFilter.process((currentSpeed - _lastSpeed))* 1000.0; // deg/s/s + metric.current.accel = accelFilter.process((currentSpeed - _lastSpeed))* this->filter_f; // deg/s/s _lastSpeed = currentSpeed; } @@ -692,7 +692,7 @@ float Axis::getTorqueScaler(){ } -int32_t Axis::getTorque() { return metric.current.torque; } +int32_t Axis::getTorque() { return metric.previous.torque; } bool Axis::isInverted() { return invertAxis; @@ -702,7 +702,7 @@ bool Axis::isInverted() { * Calculate soft endstop effect */ int16_t Axis::updateEndstop(){ - int8_t clipdir = cliptest(metric.current.pos, -0x7fff, 0x7fff); + int8_t clipdir = cliptest(metric.current.pos_scaled_16b, -0x7fff, 0x7fff); if(clipdir == 0){ return 0; } @@ -792,6 +792,22 @@ bool Axis::updateTorque(int32_t* totalTorque) { return (torqueChanged); } +void Axis::updateSamplerate(float newSamplerate){ + this->filter_f = newSamplerate; + this->updateFilters(this->filterProfileId); // Recalculate filters +} + +void Axis::updateFilters(uint8_t profileId){ + this->filterProfileId = profileId; + speedFilter.setFc(filterSpeedCst[this->filterProfileId].freq / filter_f); + speedFilter.setQ(filterSpeedCst[this->filterProfileId].q / 100.0); + accelFilter.setFc(filterAccelCst[this->filterProfileId].freq / filter_f); + accelFilter.setQ(filterAccelCst[this->filterProfileId].q / 100.0); + damperFilter.setFc(filterDamperCst.freq/filter_f); + inertiaFilter.setFc(filterInertiaCst.freq/filter_f); + frictionFilter.setFc(filterFrictionCst.freq/filter_f); +} + /** * Starts fading in force from start to 1 over fadeTime */ @@ -975,16 +991,16 @@ CommandStatus Axis::command(const ParsedCommand& cmd,std::vector& break; case Axis_commands::curpos: - replies.emplace_back(this->metric.current.pos); + replies.emplace_back(this->metric.previous.pos_scaled_16b); break; case Axis_commands::curtorque: - replies.emplace_back(this->metric.current.torque); + replies.emplace_back(getTorque()); break; case Axis_commands::curspd: - replies.emplace_back(this->metric.current.speed); + replies.emplace_back(this->metric.previous.speed); break; case Axis_commands::curaccel: - replies.emplace_back(this->metric.current.accel); + replies.emplace_back(this->metric.previous.accel); break; case Axis_commands::reductionScaler: @@ -1003,11 +1019,7 @@ CommandStatus Axis::command(const ParsedCommand& cmd,std::vector& else if (cmd.type == CMDtype::set) { uint32_t value = clip(cmd.val, 0, filterSpeedCst.size()-1); - this->filterProfileId = value; - speedFilter.setFc(filterSpeedCst[this->filterProfileId].freq / filter_f); - speedFilter.setQ(filterSpeedCst[this->filterProfileId].q / 100.0); - accelFilter.setFc(filterAccelCst[this->filterProfileId].freq / filter_f); - accelFilter.setQ(filterAccelCst[this->filterProfileId].q / 100.0); + this->updateFilters(value); } break; case Axis_commands::filterSpeed: diff --git a/Firmware/FFBoard/Src/CmdParser.cpp b/Firmware/FFBoard/Src/CmdParser.cpp index 73640b9fd..bd61b1206 100644 --- a/Firmware/FFBoard/Src/CmdParser.cpp +++ b/Firmware/FFBoard/Src/CmdParser.cpp @@ -155,8 +155,8 @@ bool CmdParser::parse(std::vector& commands){ // Check if conversion is even possible bool validPqm = (pqm != std::string::npos && (std::isdigit(word[pqm+1]) || (std::isdigit(word[pqm+2]) && (word[pqm+1] == '-' || word[pqm+1] == '+')) || ( std::isxdigit(word[pqm+2]) && word[pqm+1] == 'x'))); bool validPeq = (peq != std::string::npos && (std::isdigit(word[peq+1]) || (std::isdigit(word[peq+2]) && (word[peq+1] == '-' || word[peq+1] == '+')) || ( std::isxdigit(word[peq+2]) && word[peq+1] == 'x'))); - - if(validPqm && validPeq && peq < pqm && (abs(pqm - peq) > 1)){ // =? + int32_t pqm_peq_dist = (pqm - peq); + if(validPqm && validPeq && peq < pqm && (abs(pqm_peq_dist) > 1)){ // =? // Dual int64_t val; int64_t val2; @@ -167,9 +167,9 @@ bool CmdParser::parse(std::vector& commands){ } if(word[peq+1] == 'x'){ - val = (int64_t)std::strtoll(word.substr(peq+2, pqm-peq).c_str(),0,16); + val = (int64_t)std::strtoll(word.substr(peq+2, pqm_peq_dist).c_str(),0,16); }else{ - val = (int64_t)std::strtoll(word.substr(peq+1, pqm-peq).c_str(),0,10); + val = (int64_t)std::strtoll(word.substr(peq+1, pqm_peq_dist).c_str(),0,10); } cmdstring = word.substr(cmd_start, peq-cmd_start); diff --git a/Firmware/FFBoard/Src/CommandHandler.cpp b/Firmware/FFBoard/Src/CommandHandler.cpp index 1c488a832..95cf4d823 100644 --- a/Firmware/FFBoard/Src/CommandHandler.cpp +++ b/Firmware/FFBoard/Src/CommandHandler.cpp @@ -523,3 +523,39 @@ void CommandHandler::removeCommandHandler(){ removeCallbackHandler(getCommandHandlers(), this); //cmdHandlerListMutex.Unlock(); } + +/** + * Registers a new command. Called by registerCommand template + */ +void CommandHandler::registerCommand_INT(const char* cmd,const uint32_t cmdid,const char* help,uint32_t flags){ + for(auto it = registeredCommands.begin();it!=registeredCommands.end();++it){ + CmdHandlerCommanddef& cmdDef = *it; + if(cmdDef.cmdId == cmdid){ + if(cmdDef.flags & CMDFLAG_EXTOVERRIDE){ + // Override dummy present. Only update other data if not present and honor flag mask + flags &= flags | CMDFLAG_EXTOVERRIDE; + registeredCommands.erase(it); // Remove old dummy + break; + }else{ + return; //already present + } + } + } + + this->registeredCommands.emplace_back(cmd, help,cmdid,flags); + this->registeredCommands.shrink_to_fit(); +} + +/** + * Adds an override dummy or changes flags of a command. Called by template. TODO untested + */ +void CommandHandler::overrideCommandFlags_INT(const uint32_t cmdid,uint32_t flagmask){ + for(CmdHandlerCommanddef& cmdDef : registeredCommands){ + if(cmdDef.cmdId == static_cast(cmdid)){ + cmdDef.flags &= flagmask; + cmdDef.flags |= CMDFLAG_EXTOVERRIDE; + return; //already present + } + } + this->registeredCommands.emplace_back(nullptr, nullptr,cmdid,flagmask); // Dummy +} diff --git a/Firmware/FFBoard/Src/CommandInterface.cpp b/Firmware/FFBoard/Src/CommandInterface.cpp index 9aa004c91..0427cd0d0 100644 --- a/Firmware/FFBoard/Src/CommandInterface.cpp +++ b/Firmware/FFBoard/Src/CommandInterface.cpp @@ -240,7 +240,7 @@ void StringCommandInterface::generateReplyFromCmd(std::string& replyPart,const P */ -CDC_CommandInterface::CDC_CommandInterface() : StringCommandInterface(1024), Thread("CDCCMD", 512, 37) { +CDC_CommandInterface::CDC_CommandInterface() : StringCommandInterface(CDC_CMD_BUFFER_SIZE), Thread("CDCCMD", 512, 37) { parser.setClearBufferTimeout(parserTimeout); this->Start(); } diff --git a/Firmware/FFBoard/Src/EffectsCalculator.cpp b/Firmware/FFBoard/Src/EffectsCalculator.cpp index 688afb25f..5684fd975 100644 --- a/Firmware/FFBoard/Src/EffectsCalculator.cpp +++ b/Firmware/FFBoard/Src/EffectsCalculator.cpp @@ -74,6 +74,15 @@ void EffectsCalculator::setActive(bool active) setClipLed(active); } +void EffectsCalculator::updateSamplerate(float newSamplerate){ + this->calcfrequency = newSamplerate; + for(FFB_Effect &effect : this->effects){ + if(effect.filter[0]){ // Update filters if effect has filters + setFilters(&effect); + } + } +} + /* If the metric is less than CP Offset - Dead Band, then the resulting force is given by the following formula: @@ -179,15 +188,15 @@ int32_t EffectsCalculator::calcNonConditionEffectForce(FFB_Effect *effect) { case FFB_EFFECT_RAMP: { - uint32_t elapsed_time = HAL_GetTick() - effect->startTime; + float elapsed_time = (micros()/1000.0) - (float)effect->startTime; int32_t duration = effect->duration; - force_vector = (int32_t)effect->startLevel + ((int32_t)elapsed_time * (effect->endLevel - effect->startLevel)) / duration; + force_vector = (int32_t)effect->startLevel + (elapsed_time * (effect->endLevel - effect->startLevel)) / duration; break; } case FFB_EFFECT_SQUARE: { - uint32_t elapsed_time = HAL_GetTick() - effect->startTime; + uint32_t elapsed_time = HAL_GetTick() - effect->startTime; // Square is ms aligned int32_t force = ((elapsed_time + effect->phase) % ((uint32_t)effect->period + 2)) < (uint32_t)(effect->period + 2) / 2 ? -magnitude : magnitude; force_vector = force + effect->offset; break; @@ -197,16 +206,16 @@ int32_t EffectsCalculator::calcNonConditionEffectForce(FFB_Effect *effect) { { int32_t force = 0; int32_t offset = effect->offset; - uint32_t elapsed_time = HAL_GetTick() - effect->startTime; + float elapsed_time = micros() - ((float)effect->startTime*1000.0); uint32_t phase = effect->phase; uint32_t period = effect->period; float periodF = period; int32_t maxMagnitude = offset + magnitude; int32_t minMagnitude = offset - magnitude; - uint32_t phasetime = (phase * period) / 35999; - uint32_t timeTemp = elapsed_time + phasetime; - float remainder = timeTemp % period; + float phasetime = (phase * period) / 35999.0; + uint32_t timeTemp = elapsed_time + (phasetime*1000); // timetemp in µs + float remainder = (timeTemp % (period*1000)) / 1000; float slope = ((maxMagnitude - minMagnitude) * 2) / periodF; if (remainder > (periodF / 2)) force = slope * (periodF - remainder); @@ -220,16 +229,16 @@ int32_t EffectsCalculator::calcNonConditionEffectForce(FFB_Effect *effect) { case FFB_EFFECT_SAWTOOTHUP: { float offset = effect->offset; - uint32_t elapsed_time = HAL_GetTick() - effect->startTime; + float elapsed_time = micros() - ((float)effect->startTime*1000.0); uint32_t phase = effect->phase; uint32_t period = effect->period; float periodF = effect->period; float maxMagnitude = offset + magnitude; float minMagnitude = offset - magnitude; - int32_t phasetime = (phase * period) / 35999; - uint32_t timeTemp = elapsed_time + phasetime; - float remainder = timeTemp % period; + float phasetime = (phase * period) / 35999.0; + uint32_t timeTemp = elapsed_time + (phasetime*1000); // timetemp in µs + float remainder = (timeTemp % (period*1000)) / 1000; float slope = (maxMagnitude - minMagnitude) / periodF; force_vector = (int32_t)(minMagnitude + slope * (period - remainder)); break; @@ -238,16 +247,16 @@ int32_t EffectsCalculator::calcNonConditionEffectForce(FFB_Effect *effect) { case FFB_EFFECT_SAWTOOTHDOWN: { float offset = effect->offset; - uint32_t elapsed_time = HAL_GetTick() - effect->startTime; + float elapsed_time = micros() - ((float)effect->startTime*1000.0); float phase = effect->phase; uint32_t period = effect->period; float periodF = effect->period; float maxMagnitude = offset + magnitude; float minMagnitude = offset - magnitude; - int32_t phasetime = (phase * period) / 35999; - uint32_t timeTemp = elapsed_time + phasetime; - float remainder = timeTemp % period; + float phasetime = (phase * period) / 35999.0; + uint32_t timeTemp = elapsed_time + (phasetime*1000); // timetemp in µs + float remainder = (timeTemp % (period*1000)) / 1000; float slope = (maxMagnitude - minMagnitude) / periodF; force_vector = (int32_t)(minMagnitude + slope * (remainder)); // reverse time break; @@ -255,7 +264,7 @@ int32_t EffectsCalculator::calcNonConditionEffectForce(FFB_Effect *effect) { case FFB_EFFECT_SINE: { - float t = HAL_GetTick() - effect->startTime; + float t = (micros()/1000.0) - (float)effect->startTime; float freq = 1.0f / (float)(std::max(effect->period, 2)); float phase = (float)effect->phase / (float)35999; //degrees float sine = sinf(2.0 * M_PI * (t * freq + phase)) * magnitude; @@ -313,7 +322,7 @@ int32_t EffectsCalculator::calcComponentForce(FFB_Effect *effect, int32_t forceV case FFB_EFFECT_SPRING: { - float pos = metrics->pos; + float pos = metrics->pos_scaled_16b; result_torque -= calcConditionEffectForce(effect, pos, gain.spring, con_idx, scaler.spring, angle_ratio); break; } @@ -669,12 +678,12 @@ void EffectsCalculator::logEffectState(uint8_t type,uint8_t state){ } -void EffectsCalculator::calcStatsEffectType(uint8_t type, int16_t force,uint8_t axis){ +void EffectsCalculator::calcStatsEffectType(uint8_t type, int32_t force,uint8_t axis){ if(axis >= MAX_AXIS) return; if(type > 0 && type < 13) { uint8_t arrayLocation = type - 1; - effects_stats[arrayLocation].current[axis] = clip(effects_stats[arrayLocation].current[axis] + force, -0x7fff, 0x7fff); + effects_stats[arrayLocation].current[axis] = clip(effects_stats[arrayLocation].current[axis] + force, -0x7fff, 0x7fff); effects_stats[arrayLocation].max[axis] = std::max(effects_stats[arrayLocation].max[axis], (int16_t)abs(force)); } } @@ -1001,12 +1010,12 @@ int32_t EffectsCalculator::find_free_effect(uint8_t type){ */ uint32_t EffectsControlItf::getRate(){ float periodAvg = fxPeriodAvg.getAverage(); - if((HAL_GetTick() - lastFxUpdate) > 1000 || periodAvg == 0){ + if((micros() - lastFxUpdate) > 1000000 || periodAvg == 0){ // Reset average fxPeriodAvg.clear(); return 0; }else{ - return (1000.0/periodAvg); + return (1000000.0/periodAvg); } } @@ -1015,22 +1024,22 @@ uint32_t EffectsControlItf::getRate(){ */ uint32_t EffectsControlItf::getConstantForceRate(){ float periodAvg = cfUpdatePeriodAvg.getAverage(); - if((HAL_GetTick() - lastCfUpdate) > 1000 || periodAvg == 0){ + if((micros() - lastCfUpdate) > 1000000 || periodAvg == 0){ // Reset average cfUpdatePeriodAvg.clear(); return 0; }else{ - return (1000.0/periodAvg); + return (1000000.0/periodAvg); } } void EffectsControlItf::cfUpdateEvent(){ - cfUpdatePeriodAvg.addValue((uint32_t)(HAL_GetTick() - lastCfUpdate)); - lastCfUpdate = HAL_GetTick(); + cfUpdatePeriodAvg.addValue((uint32_t)(micros() - lastCfUpdate)); + lastCfUpdate = micros(); } void EffectsControlItf::fxUpdateEvent(){ - fxPeriodAvg.addValue((uint32_t)(HAL_GetTick() - lastFxUpdate)); - lastFxUpdate = HAL_GetTick(); + fxPeriodAvg.addValue((uint32_t)(micros() - lastFxUpdate)); + lastFxUpdate = micros(); } diff --git a/Firmware/FFBoard/Src/Filters.cpp b/Firmware/FFBoard/Src/Filters.cpp index a62074783..a9c59330a 100644 --- a/Firmware/FFBoard/Src/Filters.cpp +++ b/Firmware/FFBoard/Src/Filters.cpp @@ -3,6 +3,8 @@ * * Created on: Feb 13, 2020 * Author: Yannick + * + * Based on http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ */ #include "Filters.h" diff --git a/Firmware/FFBoard/Src/HidCommandInterface.cpp b/Firmware/FFBoard/Src/HidCommandInterface.cpp index 9a73a4268..418c3a032 100644 --- a/Firmware/FFBoard/Src/HidCommandInterface.cpp +++ b/Firmware/FFBoard/Src/HidCommandInterface.cpp @@ -99,7 +99,7 @@ void HID_CommandInterface::sendReplies(const std::vector& results } - for(const CommandReply reply : replies){ + for(const CommandReply& reply : replies){ if(reply.type == CommandReplyType::STRING){ continue; // Ignore string only replies } diff --git a/Firmware/FFBoard/Src/HidFFB.cpp b/Firmware/FFBoard/Src/HidFFB.cpp index 21835fef3..5b12a41f7 100644 --- a/Firmware/FFBoard/Src/HidFFB.cpp +++ b/Firmware/FFBoard/Src/HidFFB.cpp @@ -39,6 +39,10 @@ void HidFFB::setDirectionEnableMask(uint8_t mask){ this->directionEnableMask = mask; } +void HidFFB::updateSamplerate(float newSamplerate){ + effects_calc->updateSamplerate(newSamplerate); +} + bool HidFFB::getFfbActive(){ return this->ffb_active; diff --git a/Firmware/FFBoard/Src/I2C.cpp b/Firmware/FFBoard/Src/I2C.cpp index decbe4192..33470ab8a 100644 --- a/Firmware/FFBoard/Src/I2C.cpp +++ b/Firmware/FFBoard/Src/I2C.cpp @@ -14,24 +14,6 @@ ClassIdentifier I2CPort::info = { -//static bool operator==(const I2C_InitTypeDef& lhs, const I2C_InitTypeDef& rhs) { -// return lhs.AddressingMode == rhs.AddressingMode -// && lhs.ClockSpeed == rhs.ClockSpeed -// && lhs.DualAddressMode == rhs.DualAddressMode -// && lhs.DutyCycle == rhs.DutyCycle -// && lhs.GeneralCallMode == rhs.GeneralCallMode -// && lhs.NoStretchMode == rhs.NoStretchMode -// && lhs.OwnAddress1 == rhs.OwnAddress1 -// && lhs.OwnAddress2 == rhs.OwnAddress2; -//} - -//static bool operator==(const I2C_InitTypeDef& lhs, const I2C_InitTypeDef& rhs) { -// return memcmp(&lhs,&rhs,sizeof(I2C_InitTypeDef)) == 0; -//} -// - - - I2CPort::I2CPort(I2C_HandleTypeDef &hi2c,const I2CPortHardwareConfig& presets,uint8_t instance) : CommandHandler("i2c", CLSID_I2CPORT, instance), hi2c(hi2c),presets(presets) { restoreFlashDelayed(); #ifdef I2C_COMMANDS_DISABLED_IF_NOT_USED diff --git a/Firmware/FFBoard/Src/SPI.cpp b/Firmware/FFBoard/Src/SPI.cpp index 37185ea27..937cae4a8 100644 --- a/Firmware/FFBoard/Src/SPI.cpp +++ b/Firmware/FFBoard/Src/SPI.cpp @@ -288,7 +288,7 @@ void SPIPort::SpiError(SPI_HandleTypeDef *hspi) { * Calculates the closest possible clock achievable with the current base clock and prescalers * Returns a pair of {prescaler,actual_clock} */ -std::pair SPIPort::getClosestPrescaler(float clock){ +std::pair SPIPort::getClosestPrescaler(float clock,float min,float max){ std::vector> distances; #if defined(SPI_BAUDRATEPRESCALER_2) distances.push_back({SPI_BAUDRATEPRESCALER_2,(baseclk/2.0)}); @@ -318,7 +318,7 @@ std::pair SPIPort::getClosestPrescaler(float clock){ std::pair bestVal = distances[0]; float bestDist = INFINITY; for(auto& val : distances){ - if(std::abs(clock-val.second) < bestDist){ + if(std::abs(clock-val.second) < bestDist && (val.second > min && val.second < max)){ bestDist = abs(clock-val.second); bestVal = val; } @@ -328,9 +328,11 @@ std::pair SPIPort::getClosestPrescaler(float clock){ SPIDevice::SPIDevice(SPIPort& port,SPIConfig& spiConfig) : spiPort{port},spiConfig{spiConfig}{ spiPort.reserveCsPin(spiConfig.cs); + this->spiConfig.peripheral = port.getPortHandle()->Init; } SPIDevice::SPIDevice(SPIPort& port,OutputPin csPin) : spiPort{port},spiConfig{csPin}{ this->spiConfig.cs = csPin; + this->spiConfig.peripheral = port.getPortHandle()->Init; spiPort.reserveCsPin(spiConfig.cs); } SPIDevice::~SPIDevice() { diff --git a/Firmware/FFBoard/Src/SerialFFB.cpp b/Firmware/FFBoard/Src/SerialFFB.cpp index a439850d4..6e00d25d0 100644 --- a/Firmware/FFBoard/Src/SerialFFB.cpp +++ b/Firmware/FFBoard/Src/SerialFFB.cpp @@ -121,6 +121,10 @@ void SerialFFB::setEffectState(uint8_t id, bool state){ effects[id].state = state ? 1 : 0; } +void SerialFFB::updateSamplerate(float newSamplerate){ + effects_calc->updateSamplerate(newSamplerate); +} + CommandStatus SerialFFB::command(const ParsedCommand& cmd,std::vector& replies){ CommandStatus status = CommandStatus::OK; EffectsControlItf::fxUpdateEvent(); diff --git a/Firmware/FFBoard/Src/SystemCommands.cpp b/Firmware/FFBoard/Src/SystemCommands.cpp index c42b0918a..49b50ebd0 100644 --- a/Firmware/FFBoard/Src/SystemCommands.cpp +++ b/Firmware/FFBoard/Src/SystemCommands.cpp @@ -61,7 +61,7 @@ void SystemCommands::registerCommands(){ CommandHandler::registerCommand("errors", FFBoardMain_commands::errors, "Read error states",CMDFLAG_GET); CommandHandler::registerCommand("errorsclr", FFBoardMain_commands::errorsclr, "Reset errors",CMDFLAG_GET); CommandHandler::registerCommand("heapfree", FFBoardMain_commands::heapfree, "Memory info",CMDFLAG_GET); -#if configUSE_STATS_FORMATTING_FUNCTIONS> 0 +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) CommandHandler::registerCommand("taskstats", FFBoardMain_commands::taskstats, "Task stats",CMDFLAG_GET); #endif CommandHandler::registerCommand("format", FFBoardMain_commands::format, "set format=1 to erase all stored values",CMDFLAG_SET); @@ -77,6 +77,9 @@ void SystemCommands::registerCommands(){ #if defined(SIGNATURE) CommandHandler::registerCommand("signature", FFBoardMain_commands::signature, "Chip signature in OTP. setadr to write data. set=1 to lock",CMDFLAG_GETADR | CMDFLAG_SETADR | CMDFLAG_GET | CMDFLAG_INFOSTRING); #endif +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + CommandHandler::registerCommand("tasklist", FFBoardMain_commands::tasklist, "Task list",CMDFLAG_GET); +#endif } // Choose lower optimize level because the compiler likes to blow up this function @@ -218,7 +221,7 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect } - case FFBoardMain_commands::mallinfo: // UNUSED since freertos + case FFBoardMain_commands::mallinfo: { CommandReply reply; struct mallinfo info = mallinfo(); @@ -238,12 +241,25 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect replies.emplace_back(xPortGetFreeHeapSize(),xPortGetMinimumEverFreeHeapSize()); break; } -#if configUSE_STATS_FORMATTING_FUNCTIONS>0 +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) case FFBoardMain_commands::taskstats: { - char repl[800]; - vTaskGetRunTimeStats(repl); - replies.emplace_back("\n"+std::string(repl)); + std::string repl; + repl.resize(uxTaskGetNumberOfTasks()*64,'\0'); + vTaskGetRunTimeStats(repl.data()); + repl.resize(repl.find_first_of('\0')); // Cut + replies.emplace_back("\n"+(repl)); + break; + } +#endif +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + case FFBoardMain_commands::tasklist: + { + std::string repl; + repl.resize(uxTaskGetNumberOfTasks()*64,'\0'); + vTaskList(repl.data()); + repl.resize(repl.find_first_of('\0')); // Cut + replies.emplace_back("\n"+(repl)); break; } #endif @@ -267,6 +283,7 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect if(cmd.type == CMDtype::set && cmd.val==1){ if(Flash_Format()){ + Flash_Write_Defaults(); // Restore default values if present flag = CommandStatus::OK; }else{ flag = CommandStatus::ERR; diff --git a/Firmware/FFBoard/Src/cppmain.cpp b/Firmware/FFBoard/Src/cppmain.cpp index aa0f9a76d..412b75efb 100644 --- a/Firmware/FFBoard/Src/cppmain.cpp +++ b/Firmware/FFBoard/Src/cppmain.cpp @@ -15,6 +15,7 @@ extern IWDG_HandleTypeDef hiwdg; // Watchdog bool running = true; bool mainclassChosen = false; +volatile bool forceErase = false; uint16_t main_id = 0; @@ -46,6 +47,11 @@ void cppmain() { Error_Handler(); } + if(forceErase){ + Flash_Format(); + Flash_Write_Defaults(); + } + // // Check if flash is initialized // uint16_t lastVersion = 0; // if(!Flash_Read(ADR_SW_VERSION, &lastVersion)){ // Version never written @@ -59,11 +65,13 @@ void cppmain() { // Check if flash is initialized uint16_t lastFlashVersion = 0; if(!Flash_Read(ADR_FLASH_VERSION, &lastFlashVersion)){ // Version never written + Flash_Write_Defaults(); Flash_Write(ADR_FLASH_VERSION, FLASH_VERSION); } Flash_Read(ADR_FLASH_VERSION,&lastFlashVersion); if(lastFlashVersion != FLASH_VERSION){ Flash_Format(); // Major version changed or could not write initial value. force a format + Flash_Write_Defaults(); Flash_Write(ADR_FLASH_VERSION, FLASH_VERSION); } diff --git a/Firmware/FFBoard/Src/flash_helpers.cpp b/Firmware/FFBoard/Src/flash_helpers.cpp index cc8256ec0..76081260f 100644 --- a/Firmware/FFBoard/Src/flash_helpers.cpp +++ b/Firmware/FFBoard/Src/flash_helpers.cpp @@ -8,6 +8,7 @@ #include "eeprom_addresses.h" #include #include "mutex.hpp" +#include cpp_freertos::MutexStandard flashMutex; // Flash helpers @@ -107,25 +108,38 @@ bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def){ uint8_t i2cBufferEeprom[sizeof(uint16_t)] = {0}; #include "string.h" // Memcpy #include "cassert" +#include +#include "I2C.h" +I2CDevice i2cdeveeprom; +extern I2CPort i2cport_int; - -bool Flash_Write(uint16_t adr,uint16_t dat){ +/** + * Helper function to write 16b data to i2c eeproms with different block sizes + */ +bool I2C_EEPROM_Write16(uint16_t devAdr,uint16_t adr, uint16_t dat){ uint16_t dataLength = sizeof(dat); memcpy(i2cBufferEeprom,&dat, dataLength); - adr *= sizeof(dat)/I2C_EEPROM_DATA_SIZE; - adr = I2C_EEPROM_OFS+adr; - uint16_t curAdr = adr; - assert(adr < I2C_EEPROM_SIZE); + uint32_t adrAbs = adr; + adrAbs *= sizeof(dat)/I2C_EEPROM_DATA_SIZE; + uint32_t curAdr = adrAbs; + assert(adrAbs < I2C_EEPROM_SIZE); // Do segmented writes + bool res = false; - while(curAdr < adr+dataLength){ - while(!HAL_I2C_IsDeviceReady(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT) == HAL_OK){ - HAL_Delay(1); + while(curAdr < adrAbs+dataLength){ +// i2cport_int.isDeviceReady(i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false) + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, devAdr, 100, I2C_EEPROM_TIMEOUT,false)){ + vTaskDelay(1); } - - uint16_t wLen = std::min(dataLength,I2C_EEPROM_PAGEWRITE_SIZE - (adr % I2C_EEPROM_PAGEWRITE_SIZE)); - res = HAL_I2C_Mem_Write(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, curAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, wLen,I2C_EEPROM_TIMEOUT) == HAL_OK; + uint16_t i2cAdr = devAdr; + uint16_t writeAdr = curAdr; + if(curAdr > 0xffff){ + writeAdr = curAdr & 0xffff; + i2cAdr |= 0x02; // A17 bit + } + uint16_t wLen = std::min(dataLength,I2C_EEPROM_PAGEWRITE_SIZE - (adrAbs % I2C_EEPROM_PAGEWRITE_SIZE)); + res = i2cport_int.writeMem(&i2cdeveeprom, i2cAdr, writeAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, wLen, I2C_EEPROM_TIMEOUT, false); curAdr+=wLen; if(!res){ break; @@ -134,22 +148,22 @@ bool Flash_Write(uint16_t adr,uint16_t dat){ return res; } -bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def){ - if(!Flash_Read(adr,buf)){ - return Flash_Write(adr, def); - } - return true; -} + /** - * Reads a value and if checkempty is true returns false if the read value is the erased value (0xffff) or not found + * Helper function to read 16b data from i2c eeproms with different block sizes */ -bool Flash_Read(uint16_t adr,uint16_t *buf, bool checkempty){ - adr *= sizeof(*buf)/I2C_EEPROM_DATA_SIZE; - assert(adr < I2C_EEPROM_SIZE); - while(!HAL_I2C_IsDeviceReady(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT) == HAL_OK){ - HAL_Delay(1); +bool I2C_EEPROM_Read16(uint16_t devAdr,uint16_t adr,uint16_t *buf, bool checkempty){ + uint32_t adrAbs = (adr * sizeof(*buf)/I2C_EEPROM_DATA_SIZE); + assert(adrAbs < I2C_EEPROM_SIZE); + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, devAdr, 100, I2C_EEPROM_TIMEOUT,false)){ + vTaskDelay(1); } - bool res = HAL_I2C_Mem_Read(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, I2C_EEPROM_OFS+adr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, 2, I2C_EEPROM_TIMEOUT) == HAL_OK; + uint16_t i2cAdr = devAdr; + uint16_t datAdr = adrAbs & 0xffff; + if(adrAbs > 0xffff){ + i2cAdr |= 0x02; // If curAdr > 0xffff set bit 1 of addr high + } + bool res = i2cport_int.readMem(&i2cdeveeprom, i2cAdr, datAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, 2, I2C_EEPROM_TIMEOUT, false); if(checkempty){ bool empty = true; @@ -166,6 +180,22 @@ bool Flash_Read(uint16_t adr,uint16_t *buf, bool checkempty){ return res; } +bool Flash_Write(uint16_t adr,uint16_t dat){ + return I2C_EEPROM_Write16(I2C_EEPROM_ADR,adr+I2C_EEPROM_OFS,dat); +} +bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def){ + if(!Flash_Read(adr,buf)){ + return Flash_Write(adr, def); + } + return true; +} +/** + * Reads a value and if checkempty is true returns false if the read value is the erased value (0xffff) or not found + */ +bool Flash_Read(uint16_t adr,uint16_t *buf, bool checkempty){ + return I2C_EEPROM_Read16(I2C_EEPROM_ADR,adr+I2C_EEPROM_OFS,buf,checkempty); +} + /** * Erases the whole EEPROM to its default value */ @@ -174,20 +204,24 @@ bool Flash_Format(){ std::array eraseBuf; eraseBuf.fill(I2C_EEPROM_ERASED); for(uint32_t i=I2C_EEPROM_OFS;i(I2C_EEPROM_PAGEWRITE_SIZE,I2C_EEPROM_SIZE-i),I2C_EEPROM_TIMEOUT) == HAL_OK; + uint16_t datAdr = i & 0xffff; + uint16_t devAdr = I2C_EEPROM_ADR; + if(i > 0xffff){ + devAdr |= 0x02; + } + bool res = i2cport_int.writeMem(&i2cdeveeprom, devAdr, datAdr, I2C_EEPROM_ADR_SIZE, eraseBuf.data(), std::min(I2C_EEPROM_PAGEWRITE_SIZE,I2C_EEPROM_SIZE-i), I2C_EEPROM_TIMEOUT, false); if(!res){ flag = false; }else{ - while(!HAL_I2C_IsDeviceReady(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT) == HAL_OK){ - HAL_Delay(1); + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false)){ + vTaskDelay(1); } } } return flag; } bool Flash_Init(){ - return HAL_I2C_IsDeviceReady(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, 10, I2C_EEPROM_TIMEOUT) == HAL_OK; + return i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 50, I2C_EEPROM_TIMEOUT,false); } #else @@ -272,6 +306,34 @@ __weak bool OTP_Read(uint16_t adroffset,uint64_t* dat){ *dat = curval; return true; } +#elif defined(I2C_EEPROM_OTP_ADR) && defined(I2C_PORT_EEPROM) +// I2C EEPROM OTP/ID memory +__weak bool OTP_Write(uint16_t adroffset,uint64_t dat){ + // Write 4 x 16b + for(uint8_t i = 0; i < 4; i++){ + bool res = I2C_EEPROM_Write16(I2C_EEPROM_OTP_ADR, 4*adroffset + (i), (dat >> (i*16)) & 0xffff); + if(!res){ + return false; + } + } + + return true; +} + + +__weak bool OTP_Read(uint16_t adroffset,uint64_t* dat){ + uint64_t val = 0; + for(uint8_t i = 0; i < 4; i++){ + uint16_t tdat = 0; + bool res = I2C_EEPROM_Read16(I2C_EEPROM_OTP_ADR, 4*adroffset + (i), &tdat,false); + val |= (uint64_t)tdat << (i*16); + if(!res){ + return false; + } + } + *dat = val; + return true; +} #else __weak bool OTP_Write(uint16_t adroffset,uint64_t dat){ return false; @@ -283,3 +345,22 @@ __weak bool OTP_Read(uint16_t adroffset,uint64_t* dat){ } #endif + +#ifndef FLASH_FACTORY_DEFAULTS_OVERRIDE + /** + * To define factory defaults create a span/array in a core folder defined for example as + * const auto flash_defaults = std::to_array>({ {ADR_CURRENT_CONFIG,1} }); // ADR,VALUE pairs + * const std::span> flash_factory_defaults = flash_defaults; + */ +const std::array,0> empty_flash_defaults; // Empty +const std::span> flash_factory_defaults = empty_flash_defaults; +#endif + +/** + * Writes factory default values to flash. Does nothing if no defaults are defined + */ +void Flash_Write_Defaults(){ + for(const std::pair &kv : flash_factory_defaults){ + Flash_Write(kv.first, kv.second); // Try to write values + } +} diff --git a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index 72855f793..fa9ae121f 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -33,13 +33,17 @@ #include "SelectableInputs.h" #include "thread.hpp" -class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentStorage,ExtiHandler,public UsbHidHandler, ErrorHandler, SelectableInputs{ +class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentStorage,ExtiHandler,public UsbHidHandler, ErrorHandler, SelectableInputs +#ifdef TIM_FFB +, TimerHandler // Adds timer handler +#endif +{ enum class FFBWheel_commands : uint32_t{ ffbactive,axes,btntypes,lsbtn,addbtn,aintypes,lsain,addain,hidrate,hidsendspd,estop,cfrate }; public: - FFBHIDMain(uint8_t axisCount); + FFBHIDMain(uint8_t axisCount,bool hidAxis32b = false); virtual ~FFBHIDMain(); void setFFBEffectsCalc(std::shared_ptr ffb,std::shared_ptr effects_calc); @@ -74,6 +78,11 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto void errorCallback(const Error &error, bool cleared); void systick(); +#ifdef TIM_FFB + void timerElapsed(TIM_HandleTypeDef* htim); +#endif + + float getCurFFBFreq(); protected: std::shared_ptr ffb; @@ -91,9 +100,33 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto * Warning: Report rate initialized by bInterval is overridden by saved speed preset at startup! */ void setReportRate(uint8_t rateidx); - uint8_t usb_report_rate = HID_BINTERVAL; //1 = 1000hz, 2 = 500hz, 3 = 333hz 4 = 250hz, 5 = 200hz 6 = 166hz, 8 = 125hz etc... - uint8_t usb_report_rate_idx = 0; - const uint8_t usb_report_rates[4] = {1,2,4,8}; // Maps stored hid speed to report rates + uint8_t usb_report_rate = HID_BINTERVAL; //for FS USB 1 = 1000hz, 2 = 500hz, 3 = 333hz 4 = 250hz, 5 = 200hz 6 = 166hz, 8 = 125hz etc... + uint8_t usb_report_rate_idx = ffbrates.defaultmode; +#ifndef TIM_FFB + uint8_t ffb_rate_divider = 1; + uint8_t ffb_rate_counter = 0; +#endif + + + struct FFB_update_rates{ + struct FFB_update_rate_divider{ + uint8_t basediv; + uint8_t hiddiv; + }; + +#if TUD_OPT_HIGH_SPEED // divider pair + const uint8_t defaultmode = 3; + uint32_t basefreq = 8000; + std::array dividers = {{{1,1},{2,1},{4,1},{8,1},{16,1},{32,1},{64,1}}}; // 8khz to 125hz +#else + const uint8_t defaultmode = 0; + uint32_t basefreq = 1000; + std::array dividers = {{{1,1},{2,1},{4,1},{8,1}}}; // 8 entries max. 1khz to 125hz +#endif + }; + const static FFB_update_rates ffbrates; + + const bool hidAxis32b; std::string usb_report_rates_names(); @@ -105,8 +138,8 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto std::vector> btns; std::vector> analog_inputs; - reportHID_t reportHID; - reportHID_t lastReportHID; + std::unique_ptr reportHID; + uint8_t reportSendCounter = 0; const uint8_t analogAxisCount = 8; diff --git a/Firmware/FFBoard/UserExtensions/Inc/FFBWheel.h b/Firmware/FFBoard/UserExtensions/Inc/FFBWheel.h index 0f7be1be1..274e93cc0 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBWheel.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBWheel.h @@ -12,6 +12,12 @@ #include "FFBHIDMain.h" +#ifdef HIDAXISRES_USE_32B_DESC +#define FFBWHEEL_32B_MODE true +#else +#define FFBWHEEL_32B_MODE false +#endif + class FFBWheel : public FFBHIDMain { public: FFBWheel(); diff --git a/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h b/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h index 981dea2de..e390b7ac7 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h +++ b/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h @@ -91,6 +91,8 @@ class MtEncoderSPI: public Encoder, public SPIDevice, public PersistentStorage, const uint8_t POLY = 0x07; uint8_t spiSpeedPreset = 0; + static const uint32_t waitThresh = 2; // If last sample older than x ms use wait semaphore. Else skip and use last value to speed up processing + uint32_t lastUpdateTick = 0; }; #endif /* USEREXTENSIONS_SRC_MTENCODERSPI_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h b/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h index 9f8f061df..7c5fe634a 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h +++ b/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h @@ -35,7 +35,7 @@ struct ButtonSourceConfig{ class SPI_Buttons: public ButtonSource,public CommandHandler,public SPIDevice { enum class SPIButtons_commands : uint32_t { - mode,btncut,btnpol,btnnum,cs,spispeed + mode,btncut,btnpol,btnnum,cs,spispeed,debug,syncread }; public: @@ -45,13 +45,14 @@ class SPI_Buttons: public ButtonSource,public CommandHandler,public SPIDevice { virtual ~SPI_Buttons(); uint8_t readButtons(uint64_t* buf); + uint16_t getBtnNum() override; // Override to return conf.numButtons CommandStatus command(const ParsedCommand& cmd,std::vector& replies) override; void registerCommands(); virtual std::string getHelpstring(){return "SPI 2 Button";} void saveFlash(); - void restoreFlash(); + virtual void restoreFlash(); const uint8_t maxButtons = 64; std::string printModes(const std::vector& names); @@ -63,32 +64,29 @@ class SPI_Buttons: public ButtonSource,public CommandHandler,public SPIDevice { void setSpiSpeed(uint8_t speedPreset); protected: - SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2); - -private: + SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2, SPIPort* spiPort = &external_spi, uint8_t instance = 0); uint16_t configuration_address; uint16_t configuration_address_2; - bool ready = false; void setConfig(ButtonSourceConfig config); virtual ButtonSourceConfig* getConfig(); - void process(uint64_t* buf); - uint8_t bytes = 4; + +private: + bool ready = false; uint64_t mask = 0xff; uint8_t offset = 0; - ButtonSourceConfig conf; + static constexpr std::array speedPresets= {SPI_BAUDRATEPRESCALER_16,SPI_BAUDRATEPRESCALER_32,SPI_BAUDRATEPRESCALER_64}; +protected: + void process(uint64_t* buf); + uint8_t bytes = 4; uint8_t spi_buf[4] = {0}; - - static constexpr std::array speedPresets= {SPI_BAUDRATEPRESCALER_16,SPI_BAUDRATEPRESCALER_32,SPI_BAUDRATEPRESCALER_64}; + ButtonSourceConfig conf; }; class SPI_Buttons_1 : public SPI_Buttons { public: - SPI_Buttons_1() - : SPI_Buttons{ADR_SPI_BTN_1_CONF, ADR_SPI_BTN_1_CONF_2} { - setInstance(0); - } + SPI_Buttons_1(); const ClassIdentifier getInfo() override; static ClassIdentifier info; @@ -98,10 +96,7 @@ class SPI_Buttons_1 : public SPI_Buttons { class SPI_Buttons_2 : public SPI_Buttons { public: - SPI_Buttons_2() - : SPI_Buttons{ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_2_CONF_2} { - setInstance(1); - } + SPI_Buttons_2(); const ClassIdentifier getInfo() override; static ClassIdentifier info; diff --git a/Firmware/FFBoard/UserExtensions/Inc/ShifterAnalog.h b/Firmware/FFBoard/UserExtensions/Inc/ShifterAnalog.h index 39a0931fb..3f0638fc5 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/ShifterAnalog.h +++ b/Firmware/FFBoard/UserExtensions/Inc/ShifterAnalog.h @@ -63,11 +63,14 @@ enum class ShifterAnalog_commands : uint32_t{ G27ShifterButtonClient(OutputPin& csPin); static constexpr int numUserButtons{12}; + static constexpr int bytesToRead{2}; // 16 buttons = 2 bytes (same as SPI Buttons with 16 buttons) uint16_t getUserButtons(); bool getReverseButton(); - private: - uint16_t buttonStates{0}; + void startRead(); // Start a new DMA read (like SPI_Buttons does) + void spiRxCompleted(SPIPort* port) override; // Called when DMA completes + + uint8_t spi_buf[2]{0}; // Buffer for SPI read (2 bytes for 16 buttons) - DMA writes directly here }; diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index dd19e51fb..fdde1fa2b 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -21,7 +21,7 @@ #include "ExtiHandler.h" #include "SPI.h" #include "TimerHandler.h" - +#include #include "semaphore.hpp" #include #include "cpp_target_config.h" @@ -38,6 +38,26 @@ extern SPI_HandleTypeDef HSPIDRV; extern TIM_HandleTypeDef TIM_TMC; #endif +#ifndef TMC4671_DEFAULT_CURRENT_SCALER +#define TMC4671_DEFAULT_CURRENT_SCALER 0 +#endif +#ifndef TMC4671_DEFAULT_CLOCKFREQ +#define TMC4671_DEFAULT_CLOCKFREQ 25e6 +#endif +#ifndef TMC4671_DEFAULT_BBM +#define TMC4671_DEFAULT_BBM 20 +#endif +#ifndef TMC4671_DEFAULT_ANALOGENC_SKIPCAL +#define TMC4671_DEFAULT_ANALOGENC_SKIPCAL 0 +#endif +#ifndef TMC4671_ITUNE_CUTOFF +#define TMC4671_ITUNE_CUTOFF 0.04 +#endif +#ifndef TIM_TMC_ARR +#define TIM_TMC_ARR 250 +#endif + + enum class TMC_ControlState : uint32_t {uninitialized,waitPower,Shutdown,Running,EncoderInit,EncoderFinished,HardError,OverTemp,IndexSearch,FullCalibration,ExternalEncoderInit,Pidautotune}; enum class TMC_PwmMode : uint8_t {off = 0,HSlow_LShigh = 1, HShigh_LSlow = 2, res2 = 3, res3 = 4, PWM_LS = 5, PWM_HS = 6, PWM_FOC = 7}; @@ -46,7 +66,7 @@ enum class TMC_StartupType{NONE,coldStart,warmStart}; enum class TMC_GpioMode{DebugSpi,DSAdcClkOut,DSAdcClkIn,Aout_Bin,Ain_Bout,Aout_Bout,Ain_Bin}; -enum class MotorType : uint8_t {NONE=0,DC=1,STEPPER=2,BLDC=3,ERR}; +enum class MotorType : uint8_t {NONE=0,DC=1,STEPPER=2,BLDC=3}; enum class PhiE : uint8_t {ext=1,openloop=2,abn=3,hall=5,aenc=6,aencE=7,NONE,extEncoder}; enum class MotionMode : uint8_t {stop=0,torque=1,velocity=2,position=3,prbsflux=4,prbstorque=5,prbsvelocity=6,uqudext=8,encminimove=9,NONE}; enum class FFMode : uint8_t {none=0,velocity=1,torque=2}; @@ -54,18 +74,8 @@ enum class PosSelection : uint8_t {PhiE=0, PhiE_ext=1, PhiE_openloop=2, PhiE_abn enum class VelSelection : uint8_t {PhiE=0, PhiE_ext=1, PhiE_openloop=2, PhiE_abn=3, res1=4, PhiE_hal=5, PhiE_aenc=6, PhiA_aenc=7, res2=8, PhiM_abn=9, PhiM_abn2=10, PhiM_aenc=11, PhiM_hal=12}; enum class EncoderType_TMC : uint8_t {NONE=0,abn=1,sincos=2,uvw=3,hall=4,ext=5}; // max 7 -// Hardware versions for identifying different types. 31 versions valid +//// Hardware versions for identifying different types. 31 versions valid enum class TMC_HW_Ver : uint8_t {NONE=0,v1_0,v1_2,v1_2_2,v1_2_2_LEM20,v1_2_2_100mv,v1_3_66mv}; -// Selectable version names to be listed in commands -const std::vector> tmcHwVersionNames{ - std::make_pair(TMC_HW_Ver::NONE,"Undefined"), // Do not select. Default but disables some safety features - std::make_pair(TMC_HW_Ver::v1_0,"v1.0 AD8417"), - std::make_pair(TMC_HW_Ver::v1_2,"v1.2 AD8417"), - std::make_pair(TMC_HW_Ver::v1_2_2,"v1.2.2 LEM GO 10 (80mV/A)"), - std::make_pair(TMC_HW_Ver::v1_2_2_LEM20,"v1.2.2 LEM GO 20 (40mV/A)"), - std::make_pair(TMC_HW_Ver::v1_2_2_100mv,"v1.2/3 100mV/A"), - std::make_pair(TMC_HW_Ver::v1_3_66mv,"v1.3 ACS724 30A (66mV/A)") -}; struct TMC4671MotConf{ MotorType motor_type = MotorType::NONE; //saved @@ -81,23 +91,70 @@ struct TMC4671MotConf{ * Settings that depend on the hardware version */ struct TMC4671HardwareTypeConf{ - TMC_HW_Ver hwVersion = TMC_HW_Ver::NONE; + const char* name="CUSTOM"; + uint8_t hwVersion = 0; int adcOffset = 0; - float thermistor_R2 = 1500; - float thermistor_R = 22000; - float thermistor_Beta = 4300; - bool temperatureEnabled = false; // Enables temperature readings - float temp_limit = 90; - float currentScaler = 2.5 / (0x7fff * 60.0 * 0.0015); // Converts from adc counts to current in Amps - uint16_t brakeLimLow = 50700; - uint16_t brakeLimHigh = 50900; + + struct ThermistorSettings{ + float thermistor_R2 = 1500; + float thermistor_R = 22000; + float thermistor_Beta = 4300; + float temp_limit = 90; + bool temperatureEnabled = false; // Enables temperature readings + }; + ThermistorSettings thermistorSettings; + + + float currentScaler = TMC4671_DEFAULT_CURRENT_SCALER; // Converts from adc counts to current in Amps + uint16_t brakeLimLow = 0xffff; + uint16_t brakeLimHigh = 0xffff; float vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5); float vSenseMult = VOLTAGE_MULT_DEFAULT; - float clockfreq = 25e6; - uint8_t bbm = 20; + float clockfreq = TMC4671_DEFAULT_CLOCKFREQ; + uint8_t bbm = TMC4671_DEFAULT_BBM; float fluxDissipationScaler = 0.5; - bool allowFluxDissipationDeactivation = true; - // Todo restrict allowed motor and encoder types + + /** + * Flags which features are enabled in this hardware type + */ + struct SupportedModes_s { + uint32_t mot_none: 1 = 1, + mot_dc : 1 = 1, + mot_bldc: 1 = 1, + mot_stepper:1 = 1, + + enc_none:1 = 1, + enc_abn:1 = 1, + enc_sincos:1 = 1, + enc_uvw:1 = 1, + enc_hall:1 = 1, + enc_ext:1 = 1, + + analog_enc_skip_cal:1 = TMC4671_DEFAULT_ANALOGENC_SKIPCAL, + + allowFluxDissipationDeactivation:1 = 1; + }; + SupportedModes_s flags; + bool isEncSupported(EncoderType_TMC type){ + switch(type){ + case EncoderType_TMC::NONE: return flags.enc_none; + case EncoderType_TMC::abn: return flags.enc_abn; + case EncoderType_TMC::ext: return flags.enc_ext; + case EncoderType_TMC::sincos: return flags.enc_sincos; + case EncoderType_TMC::hall: return flags.enc_hall; + case EncoderType_TMC::uvw: return flags.enc_uvw; + default: return false; + } + } + bool isMotSupported(MotorType type){ + switch(type){ + case MotorType::NONE: return flags.mot_none; + case MotorType::DC: return flags.mot_dc; + case MotorType::STEPPER: return flags.mot_stepper; + case MotorType::BLDC: return flags.mot_bldc; + default: return false; + } + } }; @@ -157,7 +214,6 @@ struct TMC4671MainConfig{ uint16_t adc_I1_offset = 33415; uint16_t adc_I0_scale = 256; uint16_t adc_I1_scale = 256; - bool canChangeHwType = true; // Allows changing the hardware version by commands bool encoderReversed = false; bool combineEncoder = false; bool invertForce = false; @@ -237,7 +293,7 @@ struct TMC4671AENCConf{ int16_t AENC0_scale = 256; uint16_t AENC1_offset = 0x7fff; int16_t AENC1_scale = 256; - uint16_t AENC2_offset = 20000; + uint16_t AENC2_offset = 0x7fff; int16_t AENC2_scale = 256; int16_t nMask = 0; // 0x3c & 0xffff0000 @@ -346,7 +402,7 @@ friend class TMCDebugBridge; TMC4671(SPIPort& spiport,OutputPin cspin,uint8_t address=1); - void setHwType(TMC_HW_Ver type); + void setHwType(uint8_t type); void setAddress(uint8_t address); @@ -570,6 +626,8 @@ friend class TMCDebugBridge; TMC4671* tmc; }; + static std::span tmc4671_hw_configs; // Can override in external target file + private: OutputPin enablePin = OutputPin(*DRV_ENABLE_GPIO_Port,DRV_ENABLE_Pin); const Error indexNotHitError = Error(ErrorCode::encoderIndexMissed,ErrorType::critical,"Encoder index missed"); @@ -600,6 +658,7 @@ friend class TMCDebugBridge; bool fullCalibrationInProgress = false; bool phiErestored = false; bool encHallRestored = false; + bool canChangeHwType = true; // Allows changing the hardware version by commands //int32_t phiEOffsetRestored = 0; //-0x8000 to 0x7fff uint8_t calibrationFailCount = 2; @@ -635,6 +694,8 @@ friend class TMCDebugBridge; void errorCallback(const Error &error, bool cleared); bool pidAutoTune(); + void replyHardwareVersions(const std::span& versions,std::vector& replies); + uint32_t initTime = 0; bool manualEncAlign = false; bool spiActive = false; // Flag for tx interrupt that the transfer was started by this instance diff --git a/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h b/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h index d05638476..ef533fa8a 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h +++ b/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h @@ -1,13 +1,13 @@ -/* - * eeprom_addresses.h - * - * Created on: 24.01.2020 - * Author: Yannick - * - * /!\ Generated from the file memory_map.csv - / ! \ DO NOT EDIT THIS DIRECTLY !!! - */ - +/* + * eeprom_addresses.h + * + * Created on: 24.01.2020 + * Author: Yannick + * + * /!\ Generated from the file memory_map.csv + / ! \ DO NOT EDIT THIS DIRECTLY !!! + */ + #ifndef EEPROM_ADDRESSES_H_ #define EEPROM_ADDRESSES_H_ @@ -21,19 +21,19 @@ extern const uint16_t VirtAddVarTab[NB_OF_VAR]; extern const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR]; - -/* Add your addresses here. 0xffff is invalid as it marks an erased field. -Anything below 0x00ff is reserved for system variables. - -Use ranges that are clear to distinguish between configurations. Address ranges can have gaps. -Label the names clearly. -Example: 0x0100 - 0x01ff for one class and 0x0200-0x02ff for another class would be reasonable even if they each need only 3 variables - - -Important: Add your variable to the VirtAddVarTab[NB_OF_VAR] array in eeprom_addresses.c! - -Tip to check if a cell is intialized: -uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data) will return 1 if the address is not found or 0 if it was found. + +/* Add your addresses here. 0xffff is invalid as it marks an erased field. +Anything below 0x00ff is reserved for system variables. + +Use ranges that are clear to distinguish between configurations. Address ranges can have gaps. +Label the names clearly. +Example: 0x0100 - 0x01ff for one class and 0x0200-0x02ff for another class would be reasonable even if they each need only 3 variables + + +Important: Add your variable to the VirtAddVarTab[NB_OF_VAR] array in eeprom_addresses.c! + +Tip to check if a cell is intialized: +uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data) will return 1 if the address is not found or 0 if it was found. */ // System variables #define ADR_HW_VERSION 1 diff --git a/Firmware/FFBoard/UserExtensions/Inc/usb_descriptors.h b/Firmware/FFBoard/UserExtensions/Inc/usb_descriptors.h index b4d6d8ee8..ef8507300 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_descriptors.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_descriptors.h @@ -24,6 +24,16 @@ typedef struct usb_string_desc const std::vector interfaces; } usb_string_desc_t; +/* + * Base combined usb config descriptor for HID and CDC composite device + */ +#define USB_CONF_DESC_HID_CDC(HIDREPSIZE,EPSIZE) \ + /* Config number, interface count, string index, total length, attribute, power in mA*/\ + TUD_CONFIG_DESCRIPTOR(1, 3, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP | TUSB_DESC_CONFIG_ATT_SELF_POWERED, 100),\ + /* 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size.*/\ + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, EPSIZE),\ + /* HID Descriptor. EP 83 and 2*/\ + TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, HIDREPSIZE, 0x83, 0x02,EPSIZE, HID_BINTERVAL) /* * Device descriptors @@ -42,7 +52,9 @@ extern const uint8_t usb_cdc_hid_conf_1axis[]; #ifdef AXIS2_FFB_HID_DESC extern const uint8_t usb_cdc_hid_conf_2axis[]; #endif - +#ifdef AXIS2_FFB_HID_DESC_32B +extern const uint8_t usb_cdc_hid_conf_2axis_32b[]; +#endif #ifdef FFB_HID_DESC_GAMEPAD extern const uint8_t usb_cdc_hid_conf_gamepad[]; #endif diff --git a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h index d6067effe..242536eba 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h @@ -8,21 +8,769 @@ #ifndef USB_INC_USB_HID_FFB_DESC_H_ #define USB_INC_USB_HID_FFB_DESC_H_ #include "constants.h" +#include "hid.h" +/** \defgroup HID Descriptors + * \details Contains definitions of different HID descriptor parts * + * @{ + */ -#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 -#ifdef AXIS1_FFB_HID_DESC -extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; +#if defined(HIDAXISRES_32B_BITS) && HIDAXISRES_32B_BITS > 2 && HIDAXISRES_32B_BITS < 32 +// 7FFFFFFF for 32 +#define HIDAXISRES_32B_MAXS (1 << (HIDAXISRES_32B_BITS-1))-1 +#else +#define HIDAXISRES_32B_MAXS 0x7FFFFFFF +#define HIDAXISRES_32B_BITS 32 +#endif + +// HID descriptor building blocks +#define HIDDESC_32B_ENTRY(count) /* LOGICAL_MINIMUM (-7FFFFFFF)*/\ + HID_LOGICAL_MIN_N(-HIDAXISRES_32B_MAXS,3), /*0x17, 0x01, 0x00, 0x00, 0x80,*/\ + /* LOGICAL_MAXIMUM (7FFFFFFF)*/\ + HID_LOGICAL_MAX_N(HIDAXISRES_32B_MAXS,3), /* 0x27, 0xff, 0xff, 0xff, 0x7f, */ \ + /* REPORT_SIZE (32)*/\ + 0x75, 0x20,\ + /* REPORT_COUNT */\ + 0x95, count,\ +/* INPUT (Data,Var,Abs)*/\ + 0x81, 0x02, +#define HIDDESC_32B_ENTRY_SIZE 16 // 16 bytes more for range definition + + +#define HIDDESC_GAMEPAD_16B \ + 0xa1, 0x00, /* COLLECTION (Physical)*/\ + 0x85, 0x01, /* REPORT_ID (1)*/\ + 0x05, 0x09, /* USAGE_PAGE (Button)*/\ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1)*/\ + 0x29, 0x40, /* USAGE_MAXIMUM (Button 64)*/\ + 0x15, 0x00, /* LOGICAL_MINIMUM (0)*/\ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1)*/\ + 0x95, 0x40, /* REPORT_COUNT (64)*/\ + 0x75, 0x01, /* REPORT_SIZE (1)*/\ + 0x81, 0x02, /* INPUT (Data,Var,Abs)*/\ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/\ + 0x09, HID_USAGE_DESKTOP_X, /* USAGE (X)*/\ + 0x09, HID_USAGE_DESKTOP_Y, /* USAGE (Y)*/\ + 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/\ + 0x09, HID_USAGE_DESKTOP_RX, /* USAGE (Rx)*/\ + 0x09, HID_USAGE_DESKTOP_RY, /* USAGE (Ry)*/\ + 0x09, HID_USAGE_DESKTOP_RZ, /* USAGE (Rz)*/\ + 0x09, HID_USAGE_DESKTOP_DIAL, /* USAGE (Dial)*/\ + 0x09, HID_USAGE_DESKTOP_SLIDER, /* USAGE (Slider)*/\ + 0x16, 0x01, 0x80, /* LOGICAL_MINIMUM (-32767)*/\ + 0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (32767)*/\ + 0x75, 0x10, /* REPORT_SIZE (16)*/\ + 0x95, 0x08, /* REPORT_COUNT (8)*/\ + 0x81, 0x02, /* INPUT (Data,Var,Abs)*/\ + 0xc0 +#define HIDDESC_GAMEPAD_16B_SIZE 51 + +// Define workaround because we can't have conditionals in macros +#if MAX_AXIS == 1 +#define HIDDESC_GPENTRY_32B_1 0x09, HID_USAGE_DESKTOP_X, /* USAGE (X)*/\ + HIDDESC_32B_ENTRY(0x01) +#else +#define HIDDESC_GPENTRY_32B_1 0x09, HID_USAGE_DESKTOP_X, /* USAGE (X)*/ +#endif +#if MAX_AXIS == 2 +#define HIDDESC_GPENTRY_32B_2 0x09, HID_USAGE_DESKTOP_Y, /* USAGE (Y)*/\ + HIDDESC_32B_ENTRY(0x02) +#else +#define HIDDESC_GPENTRY_32B_2 0x09, HID_USAGE_DESKTOP_Y, /* USAGE (Y)*/ +#endif +#if MAX_AXIS == 3 +#define HIDDESC_GPENTRY_32B_3 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/\ + HIDDESC_32B_ENTRY(0x03) +#else +#define HIDDESC_GPENTRY_32B_3 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/ #endif +#define HIDDESC_GAMEPAD_32B \ + 0xa1, 0x00, /* COLLECTION (Physical)*/\ + 0x85, 0x01, /* REPORT_ID (1)*/\ + 0x05, 0x09, /* USAGE_PAGE (Button)*/\ + 0x19, 0x01, /* USAGE_MINIMUM (Button 1)*/\ + 0x29, 0x40, /* USAGE_MAXIMUM (Button 64)*/\ + 0x15, 0x00, /* LOGICAL_MINIMUM (0)*/\ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1)*/\ + 0x95, 0x40, /* REPORT_COUNT (64)*/\ + 0x75, 0x01, /* REPORT_SIZE (1)*/\ + 0x81, 0x02, /* INPUT (Data,Var,Abs)*/\ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/\ + HIDDESC_GPENTRY_32B_1 \ + HIDDESC_GPENTRY_32B_2 \ + HIDDESC_GPENTRY_32B_3 \ + 0x09, HID_USAGE_DESKTOP_RX, /* USAGE (Rx)*/\ + 0x09, HID_USAGE_DESKTOP_RY, /* USAGE (Ry)*/\ + 0x09, HID_USAGE_DESKTOP_RZ, /* USAGE (Rz)*/\ + 0x09, HID_USAGE_DESKTOP_DIAL, /* USAGE (Dial)*/\ + 0x09, HID_USAGE_DESKTOP_SLIDER, /* USAGE (Slider)*/\ + 0x16, 0x01, 0x80, /* LOGICAL_MINIMUM (-32767)*/\ + 0x26, 0xff, 0x7f, /* LOGICAL_MAXIMUM (32767)*/\ + 0x75, 0x10, /* REPORT_SIZE (16)*/\ + 0x95, 0x08-MAX_AXIS, /* REPORT_COUNT (8-32baxes)*/\ + 0x81, 0x02, /* INPUT (Data,Var,Abs)*/\ + 0xc0 +#define HIDDESC_GAMEPAD_32B_SIZE HIDDESC_GAMEPAD_16B_SIZE + HIDDESC_32B_ENTRY_SIZE +// Control reports for HID command interface +#define HIDDESC_CTRL_REP(DIR) /* INPUT or OUTPUT*/\ + 0x85,HID_ID_HIDCMD, /* Report ID*/\ + 0x09, 0x01, /* USAGE (Vendor)*/\ + 0x15, 0x00, /* LOGICAL_MINIMUM (0)*/\ + 0x26, 0x04, 0x00, /* Logical Maximum 4*/\ + 0x75, 0x08, /* REPORT_SIZE (8)*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1), /* INPUT (Data,Var,Abs)*/\ + \ + 0x09, 0x02, /* USAGE (Vendor) class address*/\ + 0x75, 0x10, /* REPORT_SIZE (16)*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1), /* INPUT (Data,Var,Abs)*/\ + \ + 0x09, 0x03, /* USAGE (Vendor) class instance*/\ + 0x75, 0x08, /* REPORT_SIZE (8)*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1), /* INPUT (Data,Var,Abs)*/\ + \ + 0x09, 0x04, /* USAGE (Vendor) cmd*/\ + 0x75, 0x20, /* REPORT_SIZE (32)*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1), /* INPUT (Data,Var,Abs)*/\ + \ + 0x09, 0x05, /* USAGE (Vendor)*/\ + 0x75, 0x40, /* REPORT_SIZE (64) value*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1), /* INPUT (Data,Var,Abs)*/\ + \ + 0x09, 0x06, /* USAGE (Vendor) address*/\ + 0x75, 0x40, /* REPORT_SIZE (64)*/\ + 0x95, 0x01, /* REPORT_COUNT (1)*/\ + HID_##DIR(1) /* INPUT (Data,Var,Abs)*/ +#define HIDDESC_CTRL_REPORTS \ + 0x06, 0x00, 0xFF, /* USAGE_PAGE (Vendor)*/\ + 0x09, 0x00, /* USAGE (Vendor)*/\ + 0xA1, 0x01, /* Collection (Application)*/\ + HIDDESC_CTRL_REP(OUTPUT),\ + HIDDESC_CTRL_REP(INPUT),\ + 0xc0 /* END_COLLECTION*/ +#define HIDDESC_CTRL_REPORTS_SIZE 118 + +#define HIDDESC_FFB_STATEREP \ + 0x05,0x0F, /* Usage Page Physical Interface*/ \ + 0x09,0x92, /* Usage PID State report*/\ + 0xA1,0x02, /* Collection Datalink (logical)*/\ + 0x85,HID_ID_STATE+FFB_ID_OFFSET, /* Report ID 2*/\ +/* 0x09,0x22, Usage Effect Block Index*/\ +/* 0x15,0x01, Logical Minimum 1*/\ +/* 0x25,MAX_EFFECTS, Logical Maximum 28h (40d)*/\ +/* 0x35,0x01, Physical Minimum 1*/\ +/* 0x45,MAX_EFFECTS, Physical Maximum 28h (40d)*/\ +/* 0x75,0x08, Report Size 8*/\ +/* 0x95,0x01, Report Count 1*/\ +/* 0x81,0x02, Input (Variable)*/\ + 0x09,0x9F, /* Usage Device is Pause*/\ + 0x09,0xA0, /* Usage Actuators Enabled*/\ + 0x09,0xA4, /* Usage Safety Switch*/\ + 0x09,0xA6, /* Usage Actuator Power*/\ + 0x09,0x94, /* Usage Effect Playing*/\ + /* 0x15,0x00, Logical Minimum 0*/\ + /* 0x25,0x01, Logical Maximum 1*/\ + /* 0x35,0x00, Physical Minimum 0*/\ + /* 0x45,0x01, Physical Maximum 1*/\ + /* 0x75,0x01, Report Size 1*/\ + /* 0x95,0x01, Report Count 1*/\ + /* 0x81,0x02, Input (Variable)*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x25,0x01, /* Logical Maximum 1*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x45,0x01, /* Physical Maximum 1*/\ + 0x75,0x01, /* Report Size 1*/\ + 0x95,0x05, /* Report Count 4*/\ + 0x81,0x02, /* Input (Variable)*/\ + 0x95,0x03, /* Report Count 3*/\ + 0x81,0x03, /* Input (Constant, Variable)*/\ + 0xC0 // End Collection +#define HIDDESC_FFB_STATEREP_SIZE 37 + +/* +Output +Collection Datalink: +Usage Set Effect Report + +ID:1 +Effect Block Index: 8bit + +subcollection Effect Type +12 effect types, 8bit each + +*/ +#define HIDDESC_FFB_SETEFREP \ + 0x09,0x21, /* Usage Set Effect Report*/\ + 0xA1,0x02, /* Collection Datalink (Logical)*/\ + 0x85,HID_ID_EFFREP+FFB_ID_OFFSET, /* Report ID 1*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x25, /* Usage Effect Type*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x09, HID_USAGE_CONST, /* Usage ET Constant Force*/\ + 0x09, HID_USAGE_RAMP, /* Usage ET Ramp*/\ + 0x09, HID_USAGE_SQUR, /* Usage ET Square*/\ + 0x09, HID_USAGE_SINE, /* Usage ET Sine*/\ + 0x09, HID_USAGE_TRNG, /* Usage ET Triangle*/\ + 0x09, HID_USAGE_STUP, /* Usage ET Sawtooth Up*/\ + 0x09, HID_USAGE_STDN, /* Usage ET Sawtooth Down*/\ + 0x09, HID_USAGE_SPRNG, /* Usage ET Spring*/\ + 0x09, HID_USAGE_DMPR, /* Usage ET Damper*/\ + 0x09, HID_USAGE_INRT, /* Usage ET Inertia*/\ + 0x09, HID_USAGE_FRIC, /* Usage ET Friction*/\ + /* 0x09, 0x28, // Usage ET Custom Force Data*/\ + 0x25,0x0B, /* Logical Maximum Bh (11d)*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,0x0B, /* Physical Maximum Bh (11d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x00, /* Output*/\ + 0xC0 , /* End Collection*/\ + 0x09,0x50, /* Usage Duration*/\ + 0x09,0x54, /* Usage Trigger Repeat Interval*/\ + 0x09,0x51, /* Usage Sample Period*/\ + 0x09,0xA7, /* Usage Start Delay*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x7F, /* Logical Maximum 7FFFh (32767d)*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x46,0xFF,0x7F, /* Physical Maximum 7FFFh (32767d)*/\ + 0x66,0x03,0x10, /* Unit 1003h (4099d)*/\ + 0x55,0xFD, /* Unit Exponent FDh (253d)*/\ + 0x75,0x10, /* Report Size 10h (16d)*/\ + 0x95,0x04, /* Report Count 4*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x55,0x00, /* Unit Exponent 0*/\ + 0x66,0x00,0x00, /* Unit 0*/\ + 0x09,0x52, /* Usage Gain*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x00, /* Logical Maximum FFh (255d) // TODO scaling?*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x46,0x10,0x27, /* Physical Maximum 2710h (10000d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x53, /* Usage Trigger Button*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,0x08, /* Logical Maximum 8*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,0x08, /* Physical Maximum 8*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02 // Output (Variable) +#define HIDDESC_FFB_SETEFREP_SIZE 131 + +#define HIDDESC_FFB_SETENVREP \ + /* Envelope Report Definition*/\ + 0x09,0x5A, /* Usage Set Envelope Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_ENVREP+FFB_ID_OFFSET, /* Report ID 2*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x5B, /* Usage Attack Level*/\ + 0x09,0x5D, /* Usage Fade Level*/\ + 0x16,0x00,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x7F, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x00,0x00, /* Physical Minimum 0*/\ + 0x46,0xFF,0x7F, /* Physical Maximum 7FFFh (32767d)*/\ + 0x75,0x10, /* Report Size 16*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09, 0x5C, /* Usage Attack Time*/\ + 0x09, 0x5E, /* Usage Fade Time*/\ + 0x66, 0x03, 0x10, /* Unit 1003h (English Linear, Seconds)*/\ + 0x55, 0xFD, /* Unit Exponent FDh (X10^-3 ==> Milisecond)*/\ + 0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum FFFFFFFFh (4294967295)*/\ + 0x47, 0xFF, 0x7F, 0x00, 0x00, /* Physical Maximum FFFFFFFFh (4294967295)*/\ + 0x75, 0x20, /* Report Size 20h (32d)*/\ + 0x91, 0x02, /* Output (Variable)*/\ + 0x45, 0x00, /* Physical Maximum 0*/\ + 0x66,0x00,0x00, /* Unit 0*/\ + 0x55,0x00, /* Unit Exponent 0*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_SETENVREP_SIZE 75 + +// Contains axis dependant parts. not used +#define HIDDESC_FFB_SETCONDREP \ + 0x09,0x5F, /* Usage Set Condition Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_CONDREP+FFB_ID_OFFSET, /* Report ID 3*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x23, /* Usage Parameter Block Offset*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x25,0x03, /* Logical Maximum 3*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x45,0x03, /* Physical Maximum 3*/\ + 0x75,0x04, /* Report Size 4*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x58, /* Usage Type Specific Block Off...*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x0B,0x01,0x00,0x0A,0x00, /* Usage Ordinals: Instance 1*/\ + 0x0B,0x02,0x00,0x0A,0x00, /* Usage Ordinals: Instance 2*/\ + 0x75,0x02, /* Report Size 2*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 , /* End Collection*/\ + 0x16,0x00, 0x80, /* Logical Minimum 7FFFh (-32767d)*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x00, 0x80, /* Physical Minimum 7FFFh (-32767d)*/\ + 0x46,0xff, 0x7f, /* Physical Maximum 7FFFh (32767d)*/\ +\ + 0x09,0x60, /* Usage CP Offset*/\ + 0x75,0x10, /* Report Size 16*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x36,0x00, 0x80, /* Physical Minimum (-32768)*/\ + 0x46,0xff, 0x7f, /* Physical Maximum (32767)*/\ + 0x09,0x61, /* Usage Positive Coefficient*/\ + 0x09,0x62, /* Usage Negative Coefficient*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x16,0x00,0x00, /* Logical Minimum 0*/\ + 0x26,0xff, 0x7f, /* Logical Maximum (32767)*/\ + 0x36,0x00,0x00, /* Physical Minimum 0*/\ + 0x46,0xff, 0x7f, /* Physical Maximum (32767)*/\ + 0x09,0x63, /* Usage Positive Saturation*/\ + 0x09,0x64, /* Usage Negative Saturation*/\ + 0x75,0x10, /* Report Size 16*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x65, /* Usage Dead Band*/\ + 0x46,0xff, 0x7f, /* Physical Maximum (32767)*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_SETCONDREP_SIZE 125 + +#define HIDDESC_FFB_SETPERIODICREP \ + 0x09,0x6E, /* Usage Set Periodic Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_PRIDREP+FFB_ID_OFFSET, /* Report ID 4*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x70, /* Usage Magnitude*/\ + 0x16,0x00,0x00, /* Logical Minimum 0*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x00,0x00, /* Physical Minimum 0*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x75,0x10, /* Report Size 16*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09, 0x6F, /* Usage Offset*/\ + 0x16,0x00, 0x80, /* Logical Minimum 7FFFh (-32767d)*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x00, 0x80, /* Physical Minimum 7FFFh (-32767d)*/\ + 0x46,0xff, 0x7f, /* Physical Maximum 7FFFh (32767d)*/\ + 0x95, 0x01, /* Report Count 1*/\ + 0x75, 0x10, /* Report Size 16*/\ + 0x91, 0x02, /* Output (Variable)*/\ + 0x09, 0x71, /* Usage Phase*/\ + 0x66, 0x14, 0x00, /* Unit 14h (Eng Rotation, Degrees)*/\ + 0x55, 0xFE, /* Unit Exponent FEh (X10^-2)*/\ + 0x15, 0x00, /* Logical Minimum 0*/\ + 0x27, 0x9F, 0x8C, 0x00, 0x00, /* Logical Maximum 8C9Fh (35999d)*/\ + 0x35, 0x00, /* Physical Minimum 0*/\ + 0x47, 0x9F, 0x8C, 0x00, 0x00, /* Physical Maximum 8C9Fh (35999d)*/\ + 0x75, 0x10, /* Report Size 16*/\ + 0x95, 0x01, /* Report Count 1*/\ + 0x91, 0x02, /* Output (Variable)*/\ + 0x09, 0x72, /* Usage Period*/\ + 0x15, 0x01, /* Logical Minimum 1*/\ + 0x27, 0xFF, 0x7F, 0x00, 0x00, /* Logical Maximum 7FFFh (32K)*/\ + 0x35, 0x01, /* Physical Minimum 1*/\ + 0x47, 0xFF, 0x7F, 0x00, 0x00, /* Physical Maximum 7FFFh (32K)*/\ + 0x66, 0x03, 0x10, /* Unit 1003h (English Linear, Seconds)*/\ + 0x55, 0xFD, /* Unit Exponent FDh (X10^-3 ==> Millisecond)*/\ + 0x75, 0x20, /* Report Size 20h (32)*/\ + 0x95, 0x01, /* Report Count 1*/\ + 0x91, 0x02, /* Output (Variable)*/\ + 0x66, 0x00, 0x00, /* Unit 0*/\ + 0x55,0x00, /* Unit Exponent 0*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_SETPERIODICREP_SIZE 122 + +#define HIDDESC_FFB_SETCFREP \ + 0x09,0x73, /* Usage Set Constant Force Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_CONSTREP+FFB_ID_OFFSET, /* Report ID 5*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x70, /* Usage Magnitude*/\ + 0x16,0x01, 0x80, /* Logical Minimum -7FFFh (-32767d)*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x01, 0x80, /* Physical Minimum -7FFFh (-32767d)*/\ + 0x46,0xff, 0x7f, /* Physical Maximum 7FFFh (32767d)*/\ + 0x75, 0x10, /* Report Size 10h (16d)*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_SETCFREP_SIZE 43 + +#define HIDDESC_FFB_SETRAMPREP \ + 0x09,0x74, /* Usage Set Ramp Force Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_RAMPREP+FFB_ID_OFFSET, /* Report ID 6*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x75, /* Usage Ramp Start*/\ + 0x09,0x76, /* Usage Ramp End*/\ + 0x16,0x00, 0x80, /* Logical Minimum 7FFFh (-32767d)*/\ + 0x26,0xff, 0x7f, /* Logical Maximum 7FFFh (32767d)*/\ + 0x36,0x00, 0x80, /* Physical Minimum 7FFFh (-32767d)*/\ + 0x46,0xff, 0x7f, /* Physical Maximum 7FFFh (32767d)*/\ + 0x75,0x10, /* Report Size 16*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_SETRAMPREP_SIZE 45 +// not to be used yet +#define HIDDESC_FFB_CUSTOMFORCEREP \ +/* 0x09,0x68, // Usage Custom Force Data Report*/\ +/* 0xA1,0x02, // Collection Datalink*/\ +/* 0x85,HID_ID_CSTMREP+FFB_ID_OFFSET, // Report ID 7*/\ +/* 0x09,0x22, // Usage Effect Block Index*/\ +/* 0x15,0x01, // Logical Minimum 1*/\ +/* 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d)*/\ +/* 0x35,0x01, // Physical Minimum 1*/\ +/* 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d)*/\ +/* 0x75,0x08, // Report Size 8*/\ +/* 0x95,0x01, // Report Count 1*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0x09,0x6C, // Usage Custom Force Data Offset*/\ +/* 0x15,0x00, // Logical Minimum 0*/\ +/* 0x26,0x10,0x27, // Logical Maximum 2710h (10000d)*/\ +/* 0x35,0x00, // Physical Minimum 0*/\ +/* 0x46,0x10,0x27, // Physical Maximum 2710h (10000d)*/\ +/* 0x75,0x10, // Report Size 10h (16d)*/\ +/* 0x95,0x01, // Report Count 1*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0x09,0x69, // Usage Custom Force Data*/\ +/* 0x15,0x81, // Logical Minimum 81h (-127d)*/\ +/* 0x25,0x7F, // Logical Maximum 7Fh (127d)*/\ +/* 0x35,0x00, // Physical Minimum 0*/\ +/* 0x46,0xFF,0x00, // Physical Maximum FFh (255d)*/\ +/* 0x75,0x08, // Report Size 8*/\ +/* 0x95,0x0C, // Report Count Ch (12d)*/\ +/* 0x92,0x02,0x01, // Output (Variable, Buffered)*/\ +/* 0xC0 , // End Collection*/\ +/* 0x09,0x66, // Usage Download Force Sample*/\ +/* 0xA1,0x02, // Collection Datalink*/\ +/* 0x85,HID_ID_SMPLREP+FFB_ID_OFFSET, // Report ID 8*/\ +/* 0x05,0x01, // Usage Page Generic Desktop*/\ +/* 0x09,0x30, // Usage X*/\ +/* 0x09,0x31, // Usage Y*/\ +/* 0x15,0x81, // Logical Minimum 81h (-127d)*/\ +/* 0x25,0x7F, // Logical Maximum 7Fh (127d)*/\ +/* 0x35,0x00, // Physical Minimum 0*/\ +/* 0x46,0xFF,0x00, // Physical Maximum FFh (255d)*/\ +/* 0x75,0x08, // Report Size 8*/\ +/* 0x95,0x02, // Report Count 2*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0xC0 , // End Collection*/ +#define HIDDESC_FFB_EFOPREP \ + 0x05,0x0F, /* Usage Page Physical Interface*/\ + 0x09,0x77, /* Usage Effect Operation Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_EFOPREP+FFB_ID_OFFSET, /* Report ID Ah (10d)*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0x09,0x78, /* Usage Effect Operation*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x09,0x79, /* Usage Op Effect Start*/\ + 0x09,0x7A, /* Usage Op Effect Start Solo*/\ + 0x09,0x7B, /* Usage Op Effect Stop*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,0x03, /* Logical Maximum 3*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x00, /* Output*/\ + 0xC0 , /* End Collection*/\ + 0x09,0x7C, /* Usage Loop Count*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x00, /* Logical Maximum FFh (255d)*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x46,0xFF,0x00, /* Physical Maximum FFh (255d)*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_EFOPREP_SIZE 60 +#define HIDDESC_FFB_BLOCKFREEREP \ + 0x09,0x90, /* Usage PID Block Free Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_BLKFRREP+FFB_ID_OFFSET, /* Report ID Bh (11d)*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_BLOCKFREEREP_SIZE 23 +#define HIDDESC_FFB_DEVCTRLREP \ + 0x09,0x95, /* Usage PID Device Control (0x96?)*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_CTRLREP+FFB_ID_OFFSET, /* Report ID Ch (12d)*/\ + 0x09,0x96, /* Usage PID Device Control (0x96?)*/\ + 0xA1,0x02, /* Collection Datalink*/\ +\ + 0x09,0x97, /* Usage DC Enable Actuators*/\ + 0x09,0x98, /* Usage DC Disable Actuators*/\ + 0x09,0x99, /* Usage DC Stop All Effects*/\ + 0x09,0x9A, /* Usage DC Device Reset*/\ + 0x09,0x9B, /* Usage DC Device Pause*/\ + 0x09,0x9C, /* Usage DC Device Continue*/\ +\ +\ +\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,0x06, /* Logical Maximum 6*/\ + 0x75,0x01, /* Report Size 1*/\ + 0x95,0x08, /* Report Count 8*/\ + 0x91,0x02, /* Output*/\ +\ + 0xC0 , /* End Collection*/\ + 0xC0 , /* End Collection*/\ + 0x09,0x7D, /* Usage Device Gain Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_GAINREP+FFB_ID_OFFSET, /* Report ID Dh (13d)*/\ + 0x09,0x7E, /* Usage Device Gain*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x00, /* Logical Maximum FFh (255d)*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x46,0x10,0x27, /* Physical Maximum 2710h (10000d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x91,0x02, /* Output (Variable)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_DEVCTRLREP_SIZE 59 +// Do not use yet +#define HIDDESC_FFB_SETCUSTFORCEREP \ +/* 0x09,0x6B, // Usage Set Custom Force Report*/\ +/* 0xA1,0x02, // Collection Datalink*/\ +/* 0x85,HID_ID_SETCREP+FFB_ID_OFFSET, // Report ID Eh (14d)*/\ +/* 0x09,0x22, // Usage Effect Block Index*/\ +/* 0x15,0x01, // Logical Minimum 1*/\ +/* 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d)*/\ +/* 0x35,0x01, // Physical Minimum 1*/\ +/* 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d)*/\ +/* 0x75,0x08, // Report Size 8*/\ +/* 0x95,0x01, // Report Count 1*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0x09,0x6D, // Usage Sample Count*/\ +/* 0x15,0x00, // Logical Minimum 0*/\ +/* 0x26,0xFF,0x00, // Logical Maximum FFh (255d)*/\ +/* 0x35,0x00, // Physical Minimum 0*/\ +/* 0x46,0xFF,0x00, // Physical Maximum FFh (255d)*/\ +/* 0x75,0x08, // Report Size 8*/\ +/* 0x95,0x01, // Report Count 1*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0x09,0x51, // Usage Sample Period*/\ +/* 0x66,0x03,0x10, // Unit 1003h (4099d)*/\ +/* 0x55,0xFD, // Unit Exponent FDh (253d)*/\ +/* 0x15,0x00, // Logical Minimum 0*/\ +/* 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)*/\ +/* 0x35,0x00, // Physical Minimum 0*/\ +/* 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)*/\ +/* 0x75,0x10, // Report Size 10h (16d)*/\ +/* 0x95,0x01, // Report Count 1*/\ +/* 0x91,0x02, // Output (Variable)*/\ +/* 0x55,0x00, // Unit Exponent 0*/\ +/* 0x66,0x00,0x00, // Unit 0*/\ +/* 0xC0 // End Collection*/ +#define HIDDESC_FFB_NEWEFREP \ + 0x09,0xAB, /* Usage Create New Effect Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_NEWEFREP+FFB_ID_OFFSET, /* Report ID 1*/\ + 0x09,0x25, /* Usage Effect Type*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x09, HID_USAGE_CONST, /* Usage ET Constant Force*/\ + 0x09, HID_USAGE_RAMP, /* Usage ET Ramp*/\ + 0x09, HID_USAGE_SQUR, /* Usage ET Square*/\ + 0x09, HID_USAGE_SINE, /* Usage ET Sine*/\ + 0x09, HID_USAGE_TRNG, /* Usage ET Triangle*/\ + 0x09, HID_USAGE_STUP, /* Usage ET Sawtooth Up*/\ + 0x09, HID_USAGE_STDN, /* Usage ET Sawtooth Down*/\ + 0x09, HID_USAGE_SPRNG, /* Usage ET Spring*/\ + 0x09, HID_USAGE_DMPR, /* Usage ET Damper*/\ + 0x09, HID_USAGE_INRT, /* Usage ET Inertia*/\ + 0x09, HID_USAGE_FRIC, /* Usage ET Friction*/\ +/* 0x09, 0x28, // Usage ET Custom Force Data*/\ + 0x25,0x0B, /* Logical Maximum Ch (11d)*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,0x0B, /* Physical Maximum Ch (11d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x00, /* Feature*/\ + 0xC0 , /* End Collection*/\ + 0x05,0x01, /* Usage Page Generic Desktop*/\ + 0x09,0x3B, /* Usage Reserved (Byte count)*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x26,0xFF,0x01, /* Logical Maximum 1FFh (511d)*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x46,0xFF,0x01, /* Physical Maximum 1FFh (511d)*/\ + 0x75,0x0A, /* Report Size Ah (10d)*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x02, /* Feature (Variable)*/\ + 0x75,0x06, /* Report Size 6*/\ + 0xB1,0x01, /* Feature (Constant)*/\ + 0xC0 /* End Collection*/ +#define HIDDESC_FFB_NEWEFREP_SIZE 72 + +#define HIDDESC_FFB_BLOCKLOADREP \ + 0x05,0x0F, /* Usage Page Physical Interface*/\ + 0x09,0x89, /* Usage Block Load Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_BLKLDREP+FFB_ID_OFFSET, /* Report ID 0x12*/\ + 0x09,0x22, /* Usage Effect Block Index*/\ + 0x25,MAX_EFFECTS, /* Logical Maximum 28h (40d)*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,MAX_EFFECTS, /* Physical Maximum 28h (40d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x02, /* Feature (Variable)*/\ + 0x09,0x8B, /* Usage Block Load Status*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x09,0x8C, /* Usage Block Load Success*/\ + 0x09,0x8D, /* Usage Block Load Full*/\ + 0x09,0x8E, /* Usage Block Load Error*/\ + 0x15,0x01, /* Logical Minimum 1*/\ + 0x25,0x03, /* Logical Maximum 3*/\ + 0x35,0x01, /* Physical Minimum 1*/\ + 0x45,0x03, /* Physical Maximum 3*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x00, /* Feature*/\ + 0xC0 , /* End Collection*/\ + 0x09,0xAC, /* Usage Pool available*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x27,0xFF,0xFF,0x00,0x00, /* Logical Maximum FFFFh (65535d)*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x47,0xFF,0xFF,0x00,0x00, /* Physical Maximum FFFFh (65535d)*/\ + 0x75,0x10, /* Report Size 10h (16d)*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x00, /* Feature*/\ + 0xC0 // End Collection +#define HIDDESC_FFB_BLOCKLOADREP_SIZE 72 + +#define HIDDESC_FFB_POOLREP \ + 0x09,0x7F, /* Usage PID Pool Report*/\ + 0xA1,0x02, /* Collection Datalink*/\ + 0x85,HID_ID_POOLREP+FFB_ID_OFFSET, /* Report ID 0x13*/\ + 0x09,0x80, /* Usage RAM Pool size*/\ + 0x75,0x10, /* Report Size 10h (16d)*/\ + 0x95,0x01, /* Report Count 1*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x27,0xFF,0xFF,0x00,0x00, /* Logical Maximum FFFFh (65535d)*/\ + 0x47,0xFF,0xFF,0x00,0x00, /* Physical Maximum FFFFh (65535d)*/\ + 0xB1,0x02, /* Feature (Variable)*/\ + 0x09,0x83, /* Usage Simultaneous Effects Max*/\ + 0x26,0xFF,0x00, /* Logical Maximum FFh (255d)*/\ + 0x46,0xFF,0x00, /* Physical Maximum FFh (255d)*/\ + 0x75,0x08, /* Report Size 8*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x02, /* Feature (Variable)*/\ + 0x09,0xA9, /* Usage Device Managed Pool*/\ + 0x09,0xAA, /* Usage Shared Parameter Blocks*/\ + 0x75,0x01, /* Report Size 1*/\ + 0x95,0x02, /* Report Count 2*/\ + 0x15,0x00, /* Logical Minimum 0*/\ + 0x25,0x01, /* Logical Maximum 1*/\ + 0x35,0x00, /* Physical Minimum 0*/\ + 0x45,0x01, /* Physical Maximum 1*/\ + 0xB1,0x02, /* Feature (Variable)*/\ + 0x75,0x06, /* Report Size 6*/\ + 0x95,0x01, /* Report Count 1*/\ + 0xB1,0x03, /* Feature (Constant, Variable)*/\ + 0xC0 // End Collection +#define HIDDESC_FFB_POOLREP_SIZE 67 + +#define HIDDESC_FFB_DESCSIZE HIDDESC_FFB_STATEREP_SIZE + HIDDESC_FFB_POOLREP_SIZE + HIDDESC_FFB_BLOCKLOADREP_SIZE + HIDDESC_FFB_NEWEFREP_SIZE + HIDDESC_FFB_DEVCTRLREP_SIZE + HIDDESC_FFB_BLOCKFREEREP_SIZE + HIDDESC_FFB_EFOPREP_SIZE + HIDDESC_FFB_SETEFREP_SIZE + HIDDESC_FFB_SETRAMPREP_SIZE + HIDDESC_FFB_SETCFREP_SIZE + HIDDESC_FFB_SETPERIODICREP_SIZE + HIDDESC_FFB_SETENVREP_SIZE + + -#define USB_HID_2FFB_REPORT_DESC_SIZE 1215//1213 #ifdef AXIS2_FFB_HID_DESC +#define USB_HID_2FFB_REPORT_DESC_SIZE HIDDESC_GAMEPAD_16B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + HIDDESC_FFB_DESCSIZE + 7 + 108 + 125 extern const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE]; #endif +#ifdef AXIS2_FFB_HID_DESC_32B +#define USB_HID_2FFB_REPORT_DESC_32B_SIZE HIDDESC_GAMEPAD_32B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + HIDDESC_FFB_DESCSIZE + 7 + 108 + 125 +extern const uint8_t hid_2ffb_desc_32b[USB_HID_2FFB_REPORT_DESC_32B_SIZE]; +#endif + +#ifdef AXIS1_FFB_HID_DESC +#define USB_HID_1FFB_REPORT_DESC_SIZE HIDDESC_GAMEPAD_16B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + HIDDESC_FFB_DESCSIZE + 7 + 94 + 120 +extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; +#endif +#ifdef AXIS1_FFB_HID_DESC_32B +#define USB_HID_1FFB_REPORT_DESC_SIZE HIDDESC_GAMEPAD_32B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + HIDDESC_FFB_DESCSIZE + 7 + 94 + 120 +extern const uint8_t hid_1ffb_desc_32b[USB_HID_1FFB_REPORT_DESC_32B_SIZE]; +#endif + -#define USB_HID_GAMEPAD_REPORT_DESC_SIZE 176 #ifdef FFB_HID_DESC_GAMEPAD +#define USB_HID_GAMEPAD_REPORT_DESC_SIZE HIDDESC_GAMEPAD_16B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + 7 extern const uint8_t hid_gamepad_desc[USB_HID_GAMEPAD_REPORT_DESC_SIZE]; +#endif +#ifdef FFB_HID_DESC_GAMEPAD_32B +#define USB_HID_GAMEPAD_REPORT_DESC_32B_SIZE HIDDESC_GAMEPAD_32B_SIZE + HIDDESC_CTRL_REPORTS_SIZE + 7 +extern const uint8_t hid_gamepad_desc_32b[USB_HID_GAMEPAD_REPORT_DESC_32B_SIZE]; +#endif + +#ifdef HIDAXISRES_USE_32B_DESC + #endif +/** @}*/ #endif /* USB_INC_USB_HID_FFB_DESC_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Src/CanBridge.cpp b/Firmware/FFBoard/UserExtensions/Src/CanBridge.cpp index 9708e8908..5b013463c 100644 --- a/Firmware/FFBoard/UserExtensions/Src/CanBridge.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/CanBridge.cpp @@ -12,8 +12,6 @@ #include "ledEffects.h" #include "cdc_device.h" -extern TIM_TypeDef TIM_MICROS; - ClassIdentifier CanBridge::info = { .name = "CAN Bridge (GVRET)" , .id=CLSID_MAIN_CAN, @@ -63,9 +61,9 @@ void CanBridge::sendMessage(uint32_t id, uint64_t msg,uint8_t len = 8,bool rtr = txHeader.id = id; txHeader.length = len; txHeader.rtr = rtr; - if(id & 1 << 31){ + if(id & 0x80000000){ txHeader.extId = true; - id &= 0x7FFFFFFF; + txHeader.id &= 0x7FFFFFFF; }else{ txHeader.extId = false; } @@ -102,10 +100,9 @@ void CanBridge::update(){ CAN_msg_header_rx rxHeader = msg.header; uint32_t time = rxHeader.timestamp; uint32_t id = rxHeader.id; -// if(rxHeader.ExtId != 0){ -// id = rxHeader.ExtId; -// id |= 0x80000000; -// } + if(rxHeader.extId){ + id |= 0x80000000; + } std::vector reply = { 0xF1,0,(char)(time & 0xff), (char)((time >> 8) & 0xff), (char)((time >> 16) & 0xff), (char)((time >> 24) & 0xff), (char)(id & 0xff), (char)((id >> 8) & 0xff), (char)((id >> 16) & 0xff), (char)((id >> 24) & 0xff), diff --git a/Firmware/FFBoard/UserExtensions/Src/EncoderBissC.cpp b/Firmware/FFBoard/UserExtensions/Src/EncoderBissC.cpp index 218e3e03a..5f36b13b1 100644 --- a/Firmware/FFBoard/UserExtensions/Src/EncoderBissC.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/EncoderBissC.cpp @@ -101,16 +101,16 @@ void EncoderBissC::configSPI() { uint32_t prescale; switch (spiSpeed) { case 1 : - prescale = SPI_BAUDRATEPRESCALER_64; + prescale = spiPort.getClosestPrescaler(600000).first; break; case 2 : - prescale = SPI_BAUDRATEPRESCALER_32; + prescale = spiPort.getClosestPrescaler(1300000).first; break; case 3 : - prescale = SPI_BAUDRATEPRESCALER_16; + prescale = spiPort.getClosestPrescaler(2600000).first; break; default : - prescale = SPI_BAUDRATEPRESCALER_16; + prescale = spiPort.getClosestPrescaler(2600000).first; break; } diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 3fd91228c..13934f2e9 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -14,18 +14,32 @@ #include "cmsis_os.h" extern osThreadId_t defaultTaskHandle; +#ifdef TIM_FFB +extern TIM_HandleTypeDef TIM_FFB; +#endif + +#ifndef OVERRIDE_FFBRATES +const FFBHIDMain::FFB_update_rates FFBHIDMain::ffbrates; // Default rates +#endif + ////////////////////////////////////////////// /** * setFFBEffectsCalc must be called in constructor of derived class to finish the setup */ -FFBHIDMain::FFBHIDMain(uint8_t axisCount) : +FFBHIDMain::FFBHIDMain(uint8_t axisCount,bool hidAxis32b) : Thread("FFBMAIN", 256, 30), SelectableInputs(ButtonSource::all_buttonsources,AnalogSource::all_analogsources), - axisCount(axisCount) + axisCount(axisCount),hidAxis32b(hidAxis32b) { - + if(hidAxis32b){ + reportHID = std::make_unique>(); + }else{ + reportHID = std::make_unique>(); + } +// reportHID((hidAxis32b ? HID_GamepadReport() : HID_GamepadReport())), +// lastReportHID((hidAxis32b ? HID_GamepadReport() : HID_GamepadReport())), restoreFlashDelayed(); // Load parameters registerCommands(); @@ -64,6 +78,8 @@ void FFBHIDMain::restoreFlash(){ if(Flash_Read(ADR_FFBWHEEL_CONF1,&conf1)){ uint8_t rateidx = conf1 & 0x3; setReportRate(rateidx); + }else{ + setReportRate(0); // default } } @@ -76,7 +92,7 @@ void FFBHIDMain::saveFlash(){ Flash_Write(ADR_FFBWHEEL_ANALOGCONF,this->ainsources); uint8_t conf1 = 0; - conf1 |= usb_report_rate_idx & 0x3; + conf1 |= usb_report_rate_idx & 0x7; Flash_Write(ADR_FFBWHEEL_CONF1,conf1); } @@ -90,9 +106,19 @@ void FFBHIDMain::Run(){ // control.emergency = true; // Immediately enter emergency state but without notifying other classes yet lastEstop = HAL_GetTick(); } +#endif +#ifdef TIM_FFB + HAL_TIM_Base_Start_IT(&TIM_FFB); // Start generating updates #endif while(true){ - Delay(1); +#ifndef TIM_FFB + while(ffb_rate_counter++ < ffb_rate_divider){ + Delay(1); + } + ffb_rate_counter = 0; +#else + WaitForNotification(); +#endif updateControl(); } } @@ -154,12 +180,11 @@ void FFBHIDMain::send_report(){ // if(!sourcesSem.Take(10)){ // return; // } - // Read buttons - reportHID.buttons = 0; // Reset buttons + // Read buttons uint64_t b = 0; SelectableInputs::getButtonValues(b); - reportHID.buttons = b; + reportHID->setButtons(b); // Encoder //axes_manager->addAxesToReport(analogAxesReport, &count); @@ -167,7 +192,13 @@ void FFBHIDMain::send_report(){ std::vector* axes = axes_manager->getAxisValues(); uint8_t count = 0; for(auto val : *axes){ - setHidReportAxis(&reportHID,count++,val); + if(!hidAxis32b){ + val = val >> 16; // Scale to 16b + }else{ + val = val >> (32-HIDAXISRES_32B_BITS); // Scale if less than 32b + } + //setHidReportAxis(&reportHID,count++,val); + reportHID->setHidReportAxis(count++, val); } // Fill remaining values with analog inputs @@ -175,61 +206,99 @@ void FFBHIDMain::send_report(){ for(int32_t val : *axes){ if(count >= analogAxisCount) break; - setHidReportAxis(&reportHID,count++,val); + if((count < MAX_AXIS) && hidAxis32b) + val = val << (HIDAXISRES_32B_BITS-16); // Shift up 16 bit to fill 32b value. Primary axis is 32b + reportHID->setHidReportAxis(count++, val); } + // sourcesSem.Give(); // Fill rest for(;countsetHidReportAxis(count, 0); } /* * Only send a new report if actually changed since last time or timeout and hid is ready */ - if( (reportSendCounter > 100/usb_report_rate || (memcmp(&lastReportHID,&reportHID,sizeof(reportHID_t)) != 0) )) + if( (reportSendCounter > 100/usb_report_rate || reportHID->changed()) ) { - - - tud_hid_report(0, reinterpret_cast(&reportHID), sizeof(reportHID_t)); - lastReportHID = reportHID; - reportSendCounter = 0; - + tud_hid_report(0, reportHID->getBuffer(), reportHID->getLength()); + reportHID->swap(); // Report has changed and was sent. Swap buffers. + reportSendCounter = 0; } } +/** + * Returns current FFB update loop frequency in Hz + */ +float FFBHIDMain::getCurFFBFreq(){ + return ffbrates.basefreq/((uint32_t)ffbrates.dividers[usb_report_rate_idx].basediv); +} + /** * Changes the hid report rate based on the index for usb_report_rates */ void FFBHIDMain::setReportRate(uint8_t rateidx){ - rateidx = clip(rateidx, 0,sizeof(usb_report_rates)); + uint32_t usbrate_base = TUD_OPT_HIGH_SPEED ? 8000 : 1000; + if(tud_connected()){ // Get either actual rate or max supported rate if not connected + usbrate_base = tud_speed_get() == TUSB_SPEED_HIGH ? 8000 : 1000; // Only FS and HS supported + } + + rateidx = clip(rateidx, 0,ffbrates.dividers.size()); usb_report_rate_idx = rateidx; - usb_report_rate = usb_report_rates[rateidx]*HID_BINTERVAL; + + + // Either limit using rate counter or HW timer if present. +#ifdef TIM_FFB + TIM_FFB.Instance->ARR = ((1000000*(uint32_t)ffbrates.dividers[rateidx].basediv)/ffbrates.basefreq); // Assumes 1µs timer steps +#else + ffb_rate_divider = ffbrates.dividers[rateidx].basediv; +#endif + usb_report_rate = ffbrates.dividers[rateidx].hiddiv*HID_BINTERVAL; + // Divide report rate down if above actual usb rate + while(((ffbrates.basefreq / (uint32_t)ffbrates.dividers[rateidx].basediv) / usb_report_rate) > usbrate_base){ + usb_report_rate++; + } + + // Pass updated rate to other classes to update filters + float newRate = getCurFFBFreq(); + if(ffb) + ffb->updateSamplerate(newRate); + if(axes_manager) + axes_manager->updateSamplerate(newRate); } /** * Generates the speed strings to display to the user */ std::string FFBHIDMain::usb_report_rates_names() { - std::string s = ""; - for(uint8_t i = 0 ; i < sizeof(usb_report_rates);i++){ - s += std::to_string(1000/(HID_BINTERVAL*usb_report_rates[i])) + "Hz:"+std::to_string(i); - if(i < sizeof(usb_report_rates)-1) - s += ","; + std::string s = ""; + uint32_t usbrate_base = TUD_OPT_HIGH_SPEED ? 8000 : 1000; + if(tud_connected()){ // Get either actual rate or max supported rate if not connected + usbrate_base = tud_speed_get() == TUSB_SPEED_HIGH ? 8000 : 1000; // Only FS and HS supported + } + for(uint8_t i = 0 ; i < ffbrates.dividers.size();i++){ + uint32_t updatefreq = ffbrates.basefreq/((uint32_t)ffbrates.dividers[i].basediv); + uint32_t hidrate = (HID_BINTERVAL*ffbrates.dividers[i].hiddiv); + while((updatefreq/hidrate) > usbrate_base){ // Fall back if usb rate is still higher than supported + hidrate++; } - return s; + uint32_t hidfreq = updatefreq/hidrate; + s += "FFB "+std::to_string(updatefreq) + "Hz\nUSB " + std::to_string(hidfreq) + "Hz:"+std::to_string(i); + if(i < ffbrates.dividers.size()-1) + s += ","; } + return s; +} void FFBHIDMain::emergencyStop(bool reset){ control.emergency = !reset; axes_manager->emergencyStop(reset); } -//void FFBHIDMain::timerElapsed(TIM_HandleTypeDef* htim){ -// -//} - /** * USB unplugged @@ -253,6 +322,7 @@ void FFBHIDMain::usbResume(){ control.emergency = false; } #endif + setReportRate(this->usb_report_rate_idx); control.usb_disabled = false; axes_manager->usbResume(); } @@ -301,3 +371,11 @@ void FFBHIDMain::errorCallback(const Error &error, bool cleared){ } } +#ifdef TIM_FFB +void FFBHIDMain::timerElapsed(TIM_HandleTypeDef* htim){ + if(htim == &TIM_FFB){ + NotifyFromISR(); + } +} +#endif + diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBWheel.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBWheel.cpp index b77f5c37d..1e94dd4cc 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBWheel.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBWheel.cpp @@ -24,7 +24,7 @@ const ClassIdentifier FFBWheel::getInfo(){ FFBWheel::FFBWheel() : - FFBHIDMain(1) + FFBHIDMain(1,FFBWHEEL_32B_MODE) { FFBHIDMain::setFFBEffectsCalc(ffb, effects_calc); } @@ -34,17 +34,29 @@ FFBWheel::~FFBWheel() { } - void FFBWheel::usbInit(){ +#ifdef HIDAXISRES_USE_32B_DESC #ifdef FFBWHEEL_USE_1AXIS_DESC - this->usbdev = std::make_unique(&usb_devdesc_ffboard_composite,usb_cdc_hid_conf_1axis,&usb_ffboard_strings_default); - FFBHIDMain::UsbHidHandler::setHidDesc(hid_1ffb_desc); - static_cast(ffb.get())->setDirectionEnableMask(0x02); +const uint8_t* usbconf = usb_cdc_hid_conf_1axis_32b; +const uint8_t* ffbdesc = hid_1ffb_desc_32b; #else - this->usbdev = std::make_unique(&usb_devdesc_ffboard_composite,usb_cdc_hid_conf_2axis,&usb_ffboard_strings_default); - FFBHIDMain::UsbHidHandler::setHidDesc(hid_2ffb_desc); - static_cast(ffb.get())->setDirectionEnableMask(0x04); +const uint8_t* ffbdesc = hid_2ffb_desc_32b; +const uint8_t* usbconf = usb_cdc_hid_conf_2axis_32b; +#endif +#else // ELSE 32B +#ifdef FFBWHEEL_USE_1AXIS_DESC +const uint8_t* usbconf = usb_cdc_hid_conf_1axis; +const uint8_t* ffbdesc = hid_1ffb_desc; +#else +const uint8_t* ffbdesc = hid_2ffb_desc; +const uint8_t* usbconf = usb_cdc_hid_conf_2axis; #endif +#endif + + this->usbdev = std::make_unique(&usb_devdesc_ffboard_composite,usbconf,&usb_ffboard_strings_default); + + FFBHIDMain::UsbHidHandler::setHidDesc(ffbdesc); + static_cast(ffb.get())->setDirectionEnableMask(0x04); usbdev->registerUsb(); } diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBoardMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBoardMain.cpp index da0b8da10..83c42dd0e 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBoardMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBoardMain.cpp @@ -14,7 +14,7 @@ ClassIdentifier FFBoardMain::info ={.name = "Basic (Failsafe)" ,.id=0}; -char FFBoardMain::cdcbuf[64]; +char FFBoardMain::cdcbuf[(TUD_OPT_HIGH_SPEED ? 512 : 64)]; diff --git a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp index 54148bad5..1805232ec 100644 --- a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp @@ -52,21 +52,32 @@ MtEncoderSPI::~MtEncoderSPI() { } void MtEncoderSPI::restoreFlash(){ + uint16_t conf_int = Flash_ReadDefault(ADR_MTENC_CONF1, 0); - offset = Flash_ReadDefault(ADR_MTENC_OFS, 0) << 2; + uint8_t cspin = conf_int & 0x3; MtEncoderSPI_mode mode = static_cast(conf_int >> 8); + + uint8_t offsetShift = 2; + if(mode == MtEncoderSPI_mode::mt6835){ + offsetShift = 5; + } + offset = Flash_ReadDefault(ADR_MTENC_OFS, 0) << offsetShift; setMode(mode); setCsPin(cspin); setSpiSpeed((conf_int >> 2) & 0x3); } void MtEncoderSPI::saveFlash(){ + uint8_t offsetShift = 2; + if(mode == MtEncoderSPI_mode::mt6835){ + offsetShift = 5; + } uint16_t conf_int = this->cspin & 0x3; conf_int |= (this->spiSpeedPreset & 0x3) << 2; conf_int |= ((uint8_t)mode & 0xf) << 8; Flash_Write(ADR_MTENC_CONF1, conf_int); - Flash_Write(ADR_MTENC_OFS, offset >> 2); + Flash_Write(ADR_MTENC_OFS, offset >> offsetShift); } @@ -91,6 +102,7 @@ void MtEncoderSPI::Run(){ }else{ errors++; } + lastUpdateTick = HAL_GetTick(); waitForUpdateSem.Give(); updateInProgress = false; } @@ -253,7 +265,8 @@ int32_t MtEncoderSPI::getPosAbs(){ } updateInProgress = true; requestNewDataSem.Give(); // Start transfer - waitForUpdateSem.Take(10); // Wait a bit + if(HAL_GetTick() - lastUpdateTick > waitThresh) + waitForUpdateSem.Take(waitThresh); // Wait a bit return curPos; } diff --git a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp index f4a0512a6..7d15417ed 100644 --- a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp @@ -30,6 +30,11 @@ bool SPI_Buttons_1::isCreatable(){ return (external_spi.hasFreePins()); } +SPI_Buttons_1::SPI_Buttons_1() + : SPI_Buttons{ADR_SPI_BTN_1_CONF, ADR_SPI_BTN_1_CONF_2, &external_spi, 0} { + restoreFlash(); // Call base class version (SPI_Buttons_1 doesn't override) +} + ClassIdentifier SPI_Buttons_2::info = { .name = "SPI Buttons 2" , .id=CLSID_BTN_SPI, @@ -42,21 +47,24 @@ bool SPI_Buttons_2::isCreatable(){ return false;//(external_spi.hasFreePins(); } +SPI_Buttons_2::SPI_Buttons_2() + : SPI_Buttons{ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_2_CONF_2, &external_spi, 1} { + restoreFlash(); // Call base class version (SPI_Buttons_2 doesn't override) +} + // TODO check if pin is free -SPI_Buttons::SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2) - : CommandHandler("spibtn",CLSID_BTN_SPI,0), SPIDevice(external_spi,external_spi.getFreeCsPins()[0]){ +SPI_Buttons::SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2, SPIPort* spiPort, uint8_t instance) + : CommandHandler("spibtn",CLSID_BTN_SPI,instance), SPIDevice(*spiPort,spiPort->getFreeCsPins()[0]){ this->configuration_address = configuration_address; this->configuration_address_2 = configuration_address_2; - restoreFlash(); + // NOTE: restoreFlash() is NOT called here - it must be called by derived class constructors + // This is because calling virtual functions in base class constructor doesn't dispatch to derived class - this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[this->conf.spi_speed]; + // Initialize with default settings - will be overwritten by restoreFlash() in derived class + this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[1]; // Medium speed default this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; - this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; - this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; - - initSPI(); registerCommands(); this->setCommandsEnabled(true); @@ -84,6 +92,8 @@ void SPI_Buttons::registerCommands(){ registerCommand("btnnum", SPIButtons_commands::btnnum, "Number of buttons",CMDFLAG_GET | CMDFLAG_SET); registerCommand("cs", SPIButtons_commands::cs, "SPI CS pin",CMDFLAG_GET | CMDFLAG_SET); registerCommand("spispeed", SPIButtons_commands::spispeed, "SPI speed preset",CMDFLAG_INFOSTRING | CMDFLAG_GET | CMDFLAG_SET); + registerCommand("debug", SPIButtons_commands::debug, "Debug raw SPI data",CMDFLAG_GET); + registerCommand("syncread", SPIButtons_commands::syncread, "Test synchronous read",CMDFLAG_GET); } /** @@ -102,9 +112,16 @@ void SPI_Buttons::setConfig(ButtonSourceConfig config){ OutputPin* newPin = spiPort.getCsPin(config.cs_num-1); // TODO update internal pin number if requested pin is blocked if(newPin != nullptr){ this->spiConfig.cs = *newPin; - + spiPort.reserveCsPin(this->spiConfig.cs); + }else{ + // CS pin not found - this is an error condition + // Try to use first free pin as fallback + auto& freePins = spiPort.getFreeCsPins(); + if(!freePins.empty()){ + this->spiConfig.cs = freePins[0]; + spiPort.reserveCsPin(this->spiConfig.cs); + } } - spiPort.reserveCsPin(this->spiConfig.cs); // Setup presets if(conf.mode == SPI_BtnMode::TM){ this->spiConfig.cspol = true; @@ -127,7 +144,9 @@ void SPI_Buttons::setConfig(ButtonSourceConfig config){ }else{ mask = (uint64_t)pow(2,config.numButtons)-(uint64_t)1; // Must be done completely in 64 bit! } - offset = 8 - (config.numButtons % 8); + // Calculate offset: if numButtons is multiple of 8, offset is 0 + // Otherwise, offset is 8 - (numButtons % 8) + offset = (config.numButtons % 8 == 0) ? 0 : (8 - (config.numButtons % 8)); // Thrustmaster uses extra bits for IDs if(config.mode == SPI_BtnMode::TM){ @@ -136,6 +155,7 @@ void SPI_Buttons::setConfig(ButtonSourceConfig config){ bytes = 1+((config.numButtons-1)/8); } + // Update ButtonSource::btnnum so getBtnNum() returns correct value this->btnnum = config.numButtons; } @@ -143,6 +163,12 @@ ButtonSourceConfig* SPI_Buttons::getConfig(){ return &this->conf; } +uint16_t SPI_Buttons::getBtnNum(){ + // Always return conf.numButtons as the source of truth + // btnnum inheritance issue causes it to not update correctly + return this->conf.numButtons; +} + void SPI_Buttons::setSpiSpeed(uint8_t speedPreset){ speedPreset = clip(speedPreset,0,this->speedPresets.size()); this->conf.spi_speed = speedPreset; @@ -183,12 +209,12 @@ uint8_t SPI_Buttons::readButtons(uint64_t* buf){ process(buf); // give back last buffer if(spiPort.isTaken() || !ready) - return this->btnnum; // Don't wait. + return this->conf.numButtons; // Return conf.numButtons instead of btnnum // CS pin and semaphore managed by spi port spiPort.receive_DMA(spi_buf, bytes, this); - return this->btnnum; + return this->conf.numButtons; // Return conf.numButtons instead of btnnum } std::string SPI_Buttons::printModes(const std::vector& names){ @@ -207,6 +233,7 @@ CommandStatus SPI_Buttons::command(const ParsedCommand& cmd,std::vectorgetConfig(); c->numButtons = cmd.val; this->setConfig(*c); + this->saveFlash(); // Save to flash immediately }else if(cmd.type == CMDtype::get){ replies.emplace_back(this->getBtnNum()); }else{ @@ -238,6 +265,7 @@ CommandStatus SPI_Buttons::command(const ParsedCommand& cmd,std::vectorsaveFlash(); // Save to flash immediately }else if(cmd.type == CMDtype::get){ replies.emplace_back((uint8_t)this->conf.mode); }else if(cmd.type == CMDtype::info){ @@ -250,6 +278,7 @@ CommandStatus SPI_Buttons::command(const ParsedCommand& cmd,std::vectorsaveFlash(); // Save to flash immediately }else if(cmd.type == CMDtype::get){ replies.emplace_back((uint8_t)this->conf.spi_speed); }else if(cmd.type == CMDtype::info){ @@ -262,6 +291,59 @@ CommandStatus SPI_Buttons::command(const ParsedCommand& cmd,std::vectorconf.cs_num) == CommandStatus::OK ) { setConfig(this->conf); + this->saveFlash(); // Save to flash immediately + } + break; + + case SPIButtons_commands::debug: + if(cmd.type == CMDtype::get){ + // Return raw SPI buffer data in hex format for debugging + std::string debug_data = "Raw:"; + for(uint8_t i = 0; i < this->bytes; i++){ + char hex[4]; + sprintf(hex, "%02X", this->spi_buf[i]); + debug_data += hex; + if(i < this->bytes - 1) debug_data += " "; + } + debug_data += " offset:" + std::to_string(this->offset); + debug_data += " mask:0x" + std::to_string(this->mask); + debug_data += " inv:" + std::to_string(this->conf.invert); + debug_data += " cut:" + std::to_string(this->conf.cutRight); + replies.emplace_back(debug_data); + }else{ + return CommandStatus::ERR; + } + break; + + case SPIButtons_commands::syncread: + if(cmd.type == CMDtype::get){ + // Detailed diagnostic of SPI communication + std::string result = ""; + + // Show which SPI port is being used + SPI_HandleTypeDef* hspi = spiPort.getPortHandle(); + if(hspi->Instance == SPI2) result += "SPI2 "; + else if(hspi->Instance == SPI3) result += "SPI3 "; + else result += "SPI? "; + + // Show CS pin info + result += "CS:" + std::to_string(this->conf.cs_num) + " "; + + // Force a synchronous read + uint8_t test_buf[4] = {0xAA, 0xAA, 0xAA, 0xAA}; // Pre-fill with known pattern + HAL_StatusTypeDef status = HAL_SPI_Receive(hspi, test_buf, this->bytes, 100); + + result += "HAL:" + std::to_string(status) + " "; + result += "Data:"; + for(uint8_t i = 0; i < this->bytes; i++){ + char hex[4]; + sprintf(hex, "%02X", test_buf[i]); + result += hex; + if(i < this->bytes - 1) result += " "; + } + replies.emplace_back(result); + }else{ + return CommandStatus::ERR; } break; diff --git a/Firmware/FFBoard/UserExtensions/Src/ShifterAnalog.cpp b/Firmware/FFBoard/UserExtensions/Src/ShifterAnalog.cpp index 4334f7653..3bf153b16 100644 --- a/Firmware/FFBoard/UserExtensions/Src/ShifterAnalog.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/ShifterAnalog.cpp @@ -121,15 +121,24 @@ int ShifterAnalog::getUserButtons(uint64_t* buf) { uint8_t ShifterAnalog::readButtons(uint64_t* buf){ updateAdc(); - updateReverseState(); + if (g27ShifterButtonClient) { + // Synchronous read - waits for bus and reads immediately + g27ShifterButtonClient->startRead(); + + // Now process the freshly read data + updateReverseState(); + getUserButtons(buf); + } else { + updateReverseState(); + *buf = 0; + } + calculateGear(); - *buf = 0; // User buttons go first so that switching between sequential and H-pattern // doesn't affect user button assignments. - auto numUserButtons{getUserButtons(buf)}; - if(gear > 0){ + uint64_t numUserButtons = g27ShifterButtonClient ? g27ShifterButtonClient->numUserButtons : 0; *buf |= 1 << (gear - 1 + numUserButtons); } @@ -194,7 +203,19 @@ void ShifterAnalog::setMode(ShifterMode newMode) { g27ShifterButtonClient.reset(); g27ShifterButtonClient = nullptr; } else if (!g27ShifterButtonClient && isG27Mode(newMode)) { - g27ShifterButtonClient = std::make_unique(external_spi.getFreeCsPins()[0]); + // Use the configured CS pin (cs_pin_num is 1-indexed, getCsPin uses 0-indexed) + OutputPin* csPin = external_spi.getCsPin(cs_pin_num - 1); + if (csPin == nullptr) { + // Fallback to first free pin if configured pin is invalid + auto& freePins = external_spi.getFreeCsPins(); + if (!freePins.empty()) { + csPin = &freePins[0]; + } + } + if (csPin != nullptr) { + g27ShifterButtonClient = std::make_unique(*csPin); + // Initial read will happen on first readButtons() call + } } mode = newMode; @@ -209,7 +230,11 @@ void ShifterAnalog::setCSPin(uint8_t new_cs_pin_num) { cs_pin_num = new_cs_pin_num; if (g27ShifterButtonClient) { - g27ShifterButtonClient->updateCSPin(*external_spi.getCsPin(cs_pin_num)); + // cs_pin_num is 1-indexed, getCsPin uses 0-indexed + OutputPin* newPin = external_spi.getCsPin(cs_pin_num - 1); + if (newPin != nullptr) { + g27ShifterButtonClient->updateCSPin(*newPin); + } } } @@ -239,7 +264,14 @@ CommandStatus ShifterAnalog::command(const ParsedCommand& cmd,std::vector(&buttonStates), sizeof(buttonStates),this); + // Read directly from spi_buf (DMA writes directly here, like SPI_Buttons) + // Combine the 2 bytes into uint16_t (LSB first) + uint16_t buttonStates = (uint16_t)spi_buf[0] | ((uint16_t)spi_buf[1] << 8); return buttonStates & 0x02; } uint16_t ShifterAnalog::G27ShifterButtonClient::getUserButtons() { + // Read directly from spi_buf (DMA writes directly here, like SPI_Buttons) + // Combine the 2 bytes into uint16_t (LSB first) + uint16_t buttonStates = (uint16_t)spi_buf[0] | ((uint16_t)spi_buf[1] << 8); return buttonStates >> 4; } +void ShifterAnalog::G27ShifterButtonClient::spiRxCompleted(SPIPort* port) { + // DMA completed - data is now in spi_buf + // Nothing else needed - data will be read on next readButtons() call +} diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 3b331632a..1d7be6823 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -9,7 +9,6 @@ #ifdef TMC4671DRIVER #include "ledEffects.h" #include "voltagesense.h" -//#include "stm32f4xx_hal_spi.h" #include #include #include "ErrorHandler.h" @@ -46,6 +45,7 @@ ClassIdentifier TMC4671::info = { }; + TMC4671::TMC4671(SPIPort& spiport,OutputPin cspin,uint8_t address) : CommandHandler("tmc", CLSID_MOT_TMC0,address-1), SPIDevice{motor_spi,cspin},Thread("TMC", TMC_THREAD_MEM, TMC_THREAD_PRIO) { @@ -59,7 +59,7 @@ TMC4671::TMC4671(SPIPort& spiport,OutputPin cspin,uint8_t address) : spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE; spiConfig.peripheral.NSS = SPI_NSS_SOFT; - spiConfig.peripheral.BaudRatePrescaler = spiPort.getClosestPrescaler(10e6).first; // 10MHz + spiConfig.peripheral.BaudRatePrescaler = spiPort.getClosestPrescaler(8e6,0,10e6).first; // 8 target, 10MHz max spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_MSB; spiConfig.peripheral.TIMode = SPI_TIMODE_DISABLE; spiConfig.peripheral.CRCCalculation = SPI_CRCCALCULATION_DISABLE; @@ -180,6 +180,9 @@ void TMC4671::restoreFlash(){ if(Flash_Read(flashAddrs.encA, &miscval)){ restoreEncHallMisc(miscval); encHallRestored = true; + }else{ + // set first hwconf if we can't restore + this->setHwType(TMC4671::tmc4671_hw_configs[0].hwVersion); } uint16_t filterval; if(Flash_Read(flashAddrs.torqueFilter, &filterval)){ @@ -261,7 +264,7 @@ bool TMC4671::initialize(){ */ pulseClipLed(); - this->spiConfig.peripheral.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; + this->spiConfig.peripheral.BaudRatePrescaler = spiPort.getClosestPrescaler(1e6).first; // 1MHz target spiPort.configurePort(&this->spiConfig.peripheral); ES_TMCdetected = true; } @@ -347,7 +350,7 @@ bool TMC4671::initialize(){ * Not calibrated perfectly! */ float TMC4671::getTemp(){ - if(!this->conf.hwconf.temperatureEnabled){ + if(!this->conf.hwconf.thermistorSettings.temperatureEnabled){ return 0; } TMC4671HardwareTypeConf* hwconf = &conf.hwconf; @@ -358,10 +361,10 @@ float TMC4671::getTemp(){ if(adcval <= 0){ return 0.0; } - float r = hwconf->thermistor_R2 * (((float)43252 / (float)adcval)); //43252 equivalent ADC count if it was 3.3V and not 2.5V + float r = hwconf->thermistorSettings.thermistor_R2 * (((float)43252 / (float)adcval)); //43252 equivalent ADC count if it was 3.3V and not 2.5V // Beta - r = (1.0 / 298.15) + log(r / hwconf->thermistor_R) / hwconf->thermistor_Beta; + r = (1.0 / 298.15) + log(r / hwconf->thermistorSettings.thermistor_R) / hwconf->thermistorSettings.thermistor_Beta; r = 1.0 / r; r -= 273.15; return r; @@ -549,9 +552,9 @@ void TMC4671::Run(){ } // Temperature sense - if(conf.hwconf.temperatureEnabled){ + if(conf.hwconf.thermistorSettings.temperatureEnabled){ float temp = getTemp(); - if(temp > conf.hwconf.temp_limit){ + if(temp > conf.hwconf.thermistorSettings.temp_limit){ changeState(TMC_ControlState::OverTemp); pulseErrLed(); } @@ -577,12 +580,12 @@ void TMC4671::Run(){ break; case TMC_ControlState::EncoderInit: - if(powerInitialized && hasPower()) + if(powerInitialized && hasPower() && conf.motconf.motor_type != MotorType::NONE) encoderInit(); break; case TMC_ControlState::ExternalEncoderInit: - if(powerInitialized && hasPower() && drvEncoder != nullptr) + if(powerInitialized && hasPower() && drvEncoder != nullptr && conf.motconf.motor_type != MotorType::NONE) encoderInit(); break; @@ -651,7 +654,9 @@ void TMC4671::calibrateEncoder(){ // Report changes CommandHandler::broadcastCommandReply(CommandReply(abnconf.npol ? 1 : 0), (uint32_t)TMC4671_commands::encpol, CMDtype::get); }else if(conf.motconf.enctype == EncoderType_TMC::sincos || conf.motconf.enctype == EncoderType_TMC::uvw){ - calibrateAenc(); + if(!conf.hwconf.flags.analog_enc_skip_cal){ + calibrateAenc(); + } }else if(conf.motconf.enctype == EncoderType_TMC::ext){ estimateExtEnc(); } @@ -724,7 +729,7 @@ bool TMC4671::pidAutoTune(){ flux = getActualFlux(); } - if(peakflux > (targetflux + ( targetflux * 0.03))) // Overshoot target by 3% + if(peakflux > (targetflux + ( targetflux * TMC4671_ITUNE_CUTOFF))) // Overshoot target by 4% default { fluxI -= step_i; // Revert last step break; @@ -1165,8 +1170,8 @@ int16_t TMC4671::getPhiE_Enc(){ * Steps the motor a few times to check if the encoder follows correctly */ bool TMC4671::checkEncoder(){ - if(this->conf.motconf.motor_type != MotorType::STEPPER && this->conf.motconf.motor_type != MotorType::BLDC && - conf.motconf.enctype != EncoderType_TMC::uvw && conf.motconf.enctype != EncoderType_TMC::sincos && conf.motconf.enctype != EncoderType_TMC::abn && conf.motconf.enctype != EncoderType_TMC::ext) + if((this->conf.motconf.motor_type != MotorType::STEPPER && this->conf.motconf.motor_type != MotorType::BLDC) || ( + conf.motconf.enctype != EncoderType_TMC::uvw && conf.motconf.enctype != EncoderType_TMC::sincos && conf.motconf.enctype != EncoderType_TMC::abn && conf.motconf.enctype != EncoderType_TMC::ext)) { // If not stepper or bldc return return true; } @@ -1362,8 +1367,8 @@ bool TMC4671::calibrateAdcOffset(uint16_t time){ uint32_t measurements_idle = 0; uint64_t totalA=0; uint64_t totalB=0; - bool allowTemp = conf.hwconf.temperatureEnabled; - conf.hwconf.temperatureEnabled = false; // Temp check interrupts adc + bool allowTemp = conf.hwconf.thermistorSettings.temperatureEnabled; + conf.hwconf.thermistorSettings.temperatureEnabled = false; // Temp check interrupts adc writeReg(0x03, 0); // Read raw adc PhiE lastphie = getPhiEtype(); MotionMode lastmode = getMotionMode(); @@ -1402,7 +1407,7 @@ bool TMC4671::calibrateAdcOffset(uint16_t time){ // setPwm(TMC_PwmMode::off); //Disable pwm // this->changeState(TMC_ControlState::HardError); adcCalibrated = false; - conf.hwconf.temperatureEnabled = allowTemp; + conf.hwconf.thermistorSettings.temperatureEnabled = allowTemp; return false; // An adc or shunt amp is likely broken. do not proceed. } conf.adc_I0_offset = offsetAidle; @@ -1413,7 +1418,7 @@ bool TMC4671::calibrateAdcOffset(uint16_t time){ setPhiEtype(lastphie); setMotionMode(lastmode,true); adcCalibrated = true; - conf.hwconf.temperatureEnabled = allowTemp; + conf.hwconf.thermistorSettings.temperatureEnabled = allowTemp; return true; } @@ -1447,7 +1452,9 @@ void TMC4671::encoderInit(){ setPosSel(PosSelection::PhiM_aenc); // Mechanical Angle setVelSel(VelSelection::PhiM_aenc); // Mechanical Angle (RPM) //setup_AENC(aencconf); - calibrateAenc(); + if(!conf.hwconf.flags.analog_enc_skip_cal){ + calibrateAenc(); + } } // find index @@ -1512,9 +1519,10 @@ void TMC4671::encoderInit(){ */ void TMC4671::setEncoderType(EncoderType_TMC type){ // If no external timer is set external encoder is not valid - if((!externalEncoderTimer || !externalEncoderAllowed()) && type == EncoderType_TMC::ext){ + if( !conf.hwconf.isEncSupported(type) || ((!externalEncoderTimer || !externalEncoderAllowed()) && type == EncoderType_TMC::ext)){ type = EncoderType_TMC::NONE; } + this->conf.motconf.enctype = type; this->statusMask.flags.AENC_N = 0; this->statusMask.flags.ENC_N = 0; @@ -1729,7 +1737,7 @@ void TMC4671::turn(int16_t power){ // Flux offset for field weakening flux = idleFlux-clip(abs(power),0,maxOffsetFlux); - if((this->conf.encoderReversed && conf.motconf.enctype == EncoderType_TMC::ext) ^ conf.invertForce){ + if((this->conf.encoderReversed && conf.motconf.enctype == EncoderType_TMC::ext) || conf.invertForce){ power = -power; // Encoder does not match } @@ -1848,7 +1856,9 @@ Encoder* TMC4671::getEncoder(){ void TMC4671::setEncoder(std::shared_ptr& encoder){ MotorDriver::drvEncoder = encoder; if(conf.motconf.enctype == EncoderType_TMC::ext && externalEncoderTimer){ - // TODO Calibrate and align external encoder + if(!extEncUpdater){ // If updater has not been set up because the encoder mode was changed before the external encoder passed force it now + setUpExtEncTimer(); + } changeState(TMC_ControlState::ExternalEncoderInit); } } @@ -2007,14 +2017,19 @@ bool TMC4671::externalEncoderAllowed(){ #ifndef TIM_TMC return false; #else - return allowExternalEncoder; + return allowExternalEncoder && conf.hwconf.flags.enc_ext; #endif } void TMC4671::setMotorType(MotorType motor,uint16_t poles){ + + if(!conf.hwconf.isMotSupported(motor)){ + motor = MotorType::NONE; + } if(motor == MotorType::DC){ poles = 1; } + conf.motconf.motor_type = motor; conf.motconf.pole_pairs = poles; uint32_t mtype = poles | ( ((uint8_t)motor&0xff) << 16); @@ -2050,7 +2065,11 @@ void TMC4671::setFluxTorque(int16_t flux, int16_t torque){ if(curMotionMode != MotionMode::torque && !emergency){ setMotionMode(MotionMode::torque,true); } +#ifdef TMC4671_TORQUE_USE_ASYNC + writeRegAsync(0x64, (flux & 0xffff) | (torque << 16)); +#else writeReg(0x64, (flux & 0xffff) | (torque << 16)); +#endif } void TMC4671::setFluxTorqueFF(int16_t flux, int16_t torque){ @@ -2262,7 +2281,7 @@ void TMC4671::setTorqueFilter(TMC4671Biquad_conf& conf){ /** * Sets the raw brake resistor limits. * Centered at 0x7fff - * Set both 0 to deactivate + * Set both 0xffff to deactivate */ void TMC4671::setBrakeLimits(uint16_t low,uint16_t high){ uint32_t val = low | (high << 16); @@ -2682,161 +2701,47 @@ void TMC4671::restoreEncHallMisc(uint16_t val){ this->hallconf.interpolation = (val>>9) & 0x01; this->curPids.sequentialPI = (val>>10) & 0x01; - setHwType((TMC_HW_Ver)((val >> 11) & 0x1F)); + setHwType((uint8_t)((val >> 11) & 0x1F)); } + + /** * Sets some constants and features depending on the hardware version of the driver */ -void TMC4671::setHwType(TMC_HW_Ver type){ - //TMC4671HardwareTypeConf newHwConf; - switch(type){ - case TMC_HW_Ver::v1_3_66mv: - { - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_3_66mv, - .adcOffset = 0, - .thermistor_R2 = 1500, - .thermistor_R = 10000, - .thermistor_Beta = 4300, - .temperatureEnabled = true, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 0.066), // sensor 66mV/A - .brakeLimLow = 50700, - .brakeLimHigh = 50900, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 50 // DMTH8003SPS need longer deadtime - }; - this->conf.hwconf = newHwConf; - break; - } - case TMC_HW_Ver::v1_2_2_100mv: - { - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_2_2_100mv, - .adcOffset = 0, - .thermistor_R2 = 1500, - .thermistor_R = 10000, - .thermistor_Beta = 4300, - .temperatureEnabled = true, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 0.1), // w. TMCS1100A2 sensor 100mV/A - .brakeLimLow = 50700, - .brakeLimHigh = 50900, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 40 - }; - this->conf.hwconf = newHwConf; - break; - } - case TMC_HW_Ver::v1_2_2_LEM20: - { - // TODO possibly lower PWM limit because of lower valid sensor range - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_2_2, - .adcOffset = 0, - .thermistor_R2 = 1500, - .thermistor_R = 10000, - .thermistor_Beta = 4300, - .temperatureEnabled = true, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 0.04), // w. LEM 20 sensor 40mV/A - .brakeLimLow = 50700, - .brakeLimHigh = 50900, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 - }; - this->conf.hwconf = newHwConf; - break; - } - case TMC_HW_Ver::v1_2_2: - { - // TODO possibly lower PWM limit because of lower valid sensor range - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_2_2, - .adcOffset = 0, - .thermistor_R2 = 1500, - .thermistor_R = 10000, - .thermistor_Beta = 4300, - .temperatureEnabled = true, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 0.08), // w. LEM 10 sensor 80mV/A - .brakeLimLow = 50700, - .brakeLimHigh = 50900, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 - }; - this->conf.hwconf = newHwConf; - break; - } - - case TMC_HW_Ver::v1_2: - { - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_2, - .adcOffset = 1000, - .thermistor_R2 = 1500, - .thermistor_R = 22000, - .thermistor_Beta = 4300, - .temperatureEnabled = true, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 60.0 * 0.0015), // w. 60x 1.5mOhm sensor - .brakeLimLow = 50700, - .brakeLimHigh = 50900, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 - }; - this->conf.hwconf = newHwConf; - // Activates around 60V as last resort failsave. Check offsets from tmc leakage. ~ 1.426V - break; - } - - - case TMC_HW_Ver::v1_0: - { - TMC4671HardwareTypeConf newHwConf = { - .hwVersion = TMC_HW_Ver::v1_0, - .adcOffset = 1000, - .thermistor_R2 = 0, - .thermistor_R = 0, - .thermistor_Beta = 0, - .temperatureEnabled = false, - .temp_limit = 90, - .currentScaler = 2.5 / (0x7fff * 60.0 * 0.0015), // w. 60x 1.5mOhm sensor - .brakeLimLow = 52400, - .brakeLimHigh = 52800, - .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), - .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 - }; - this->conf.hwconf = newHwConf; - - break; +void TMC4671::setHwType(uint8_t type){ + // If only one config is valid use this regardless of requested type + if(TMC4671::tmc4671_hw_configs.size() == 1){ + this->conf.hwconf = TMC4671::tmc4671_hw_configs[0]; + }else{ // Search for config matching requested type + for(const TMC4671HardwareTypeConf& newConf : TMC4671::tmc4671_hw_configs){ + if(type == newConf.hwVersion){ + this->conf.hwconf = newConf; + break; + } + } } - case TMC_HW_Ver::NONE: - { - default: - TMC4671HardwareTypeConf newHwConf; - newHwConf.temperatureEnabled = false; - newHwConf.hwVersion = TMC_HW_Ver::NONE; - newHwConf.currentScaler = 0; - this->conf.hwconf = newHwConf; - setBrakeLimits(0,0); // Disables internal brake resistor activation. DANGER! - break; - } - } setVSenseMult(this->conf.hwconf.vSenseMult); // Update vsense multiplier //setupBrakePin(vdiffAct, vdiffDeact, vMax); // TODO if required setBrakeLimits(this->conf.hwconf.brakeLimLow,this->conf.hwconf.brakeLimHigh); setBBM(this->conf.hwconf.bbm,this->conf.hwconf.bbm); + // Force changing motor and encoder types to prevent invalid types being selected if new hw type does not support them + setMotorType(this->conf.motconf.motor_type, this->conf.motconf.pole_pairs); + setEncoderType(this->conf.motconf.enctype); +} +/** + * Appends a formatted reply with currently available hardware version configs + */ +void TMC4671::replyHardwareVersions(const std::span& versions,std::vector& replies){ +// uint8_t idx = 0; + for(const TMC4671HardwareTypeConf& c : versions){ + if(this->canChangeHwType || c.hwVersion == this->conf.hwconf.hwVersion){ + replies.emplace_back( std::to_string((uint8_t)c.hwVersion) + ":" + c.name,(uint8_t)c.hwVersion); + } + } } void TMC4671::registerCommands(){ @@ -2923,10 +2828,16 @@ CommandStatus TMC4671::command(const ParsedCommand& cmd,std::vectorconf.motconf.motor_type); - }else if(cmd.type == CMDtype::set && (uint8_t)cmd.type < (uint8_t)MotorType::ERR){ + }else if(cmd.type == CMDtype::set && (uint8_t)cmd.type <= (uint8_t)MotorType::BLDC){ this->setMotorType((MotorType)cmd.val, this->conf.motconf.pole_pairs); }else{ - replies.emplace_back("NONE=0,DC=1,2Ph Stepper=2,3Ph BLDC=3"); + std::string rplstr = ""; + TMC4671HardwareTypeConf::SupportedModes_s* confflags = &conf.hwconf.flags; + if(confflags->mot_none) rplstr += "NONE=0,"; + if(confflags->mot_dc) rplstr += "DC=1,"; + if(confflags->mot_stepper) rplstr += "Stepper 2Ph=2,"; + if(confflags->mot_bldc) rplstr += "BLDC 3Ph=3"; + replies.emplace_back(rplstr); } break; @@ -2936,11 +2847,15 @@ CommandStatus TMC4671::command(const ParsedCommand& cmd,std::vectorsetEncoderType((EncoderType_TMC)cmd.val); }else{ - if(externalEncoderAllowed()) - replies.emplace_back("NONE=0,ABN=1,SinCos=2,Analog UVW=3,Hall=4,External=5"); - else - replies.emplace_back("NONE=0,ABN=1,SinCos=2,Analog UVW=3,Hall=4"); - + std::string rplstr = ""; + TMC4671HardwareTypeConf::SupportedModes_s* confflags = &conf.hwconf.flags; + if(confflags->enc_none) rplstr += "NONE=0,"; + if(confflags->enc_abn) rplstr += "ABN=1,"; + if(confflags->enc_sincos) rplstr += "SinCos=2,"; + if(confflags->enc_uvw) rplstr += "UVW=3,"; + if(confflags->enc_hall) rplstr += "HALL=4,"; + if(confflags->enc_ext && externalEncoderAllowed()) rplstr += "External=5"; + replies.emplace_back(rplstr); } break; @@ -2948,16 +2863,11 @@ CommandStatus TMC4671::command(const ParsedCommand& cmd,std::vector(this); // Setup timer this->externalEncoderTimer = &TIM_TMC; - this->externalEncoderTimer->Instance->ARR = 200; // 200 = 5khz = 5 tmc cycles, 250 = 4khz, 240 = 6 tmc cycles - this->externalEncoderTimer->Instance->PSC = (SystemCoreClock / 2000000)+1; // timer running at half clock speed. 1µs ticks + this->externalEncoderTimer->Instance->ARR = TIM_TMC_ARR; // 200 = 5khz = 5 tmc cycles, 250 = 4khz, 240 = 6 tmc cycles + this->externalEncoderTimer->Instance->PSC = ((TIM_TMC_BCLK)/1000000) +1; // 1µs ticks this->externalEncoderTimer->Instance->CR1 = 1; HAL_TIM_Base_Start_IT(this->externalEncoderTimer); #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp new file mode 100644 index 000000000..1d5cb0d0d --- /dev/null +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp @@ -0,0 +1,117 @@ +/* + * TMC4671_configs.cpp + * + * Created on: Feb 20, 2024 + * Author: Yannick + */ + +#include "TMC4671.h" +#include "constants.h" +#include "span" + +#if !defined(TMC4671_OVERRIDE_HWCONFS) && defined(TMC4671DRIVER) +// Default configs for officially supported hardware + + +const TMC4671HardwareTypeConf conf1_0 = { + .name = "v1.0 AD8417 (1.5mOhm)", + .hwVersion = (uint8_t)TMC_HW_Ver::v1_0, + .adcOffset = 1000, + .thermistorSettings = { + .thermistor_R2 = 0, + .thermistor_R = 0, + .thermistor_Beta = 0, + .temp_limit = 90, + .temperatureEnabled = false, + }, + .currentScaler = 2.5 / (0x7fff * 60.0 * 0.0015), // w. 60x 1.5mOhm sensor + .brakeLimLow = 52400, + .brakeLimHigh = 52800, + .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), + .vSenseMult = VOLTAGE_MULT_DEFAULT, + .clockfreq = 25e6, + .bbm = 20, +}; +const TMC4671HardwareTypeConf conf1_2 = { + .name = "v1.2 AD8417 (1.5mOhm)", + .hwVersion = (uint8_t)TMC_HW_Ver::v1_2, + .adcOffset = 1000, + .thermistorSettings = { + .thermistor_R2 = 1500, + .thermistor_R = 22000, + .thermistor_Beta = 4300, + .temp_limit = 90, + .temperatureEnabled = true, + }, + .currentScaler = 2.5 / (0x7fff * 60.0 * 0.0015), // w. 60x 1.5mOhm sensor + .brakeLimLow = 50700, + .brakeLimHigh = 50900, + .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), + .vSenseMult = VOLTAGE_MULT_DEFAULT, + .clockfreq = 25e6, + .bbm = 20, +}; +const TMC4671HardwareTypeConf conf1_2_2 = { + .name = "v1.2.2 LEM 10 (80mV/A)", + .hwVersion = (uint8_t)TMC_HW_Ver::v1_2_2, + .adcOffset = 0, + .thermistorSettings = { + .thermistor_R2 = 1500, + .thermistor_R = 10000, + .thermistor_Beta = 4300, + .temp_limit = 90, + .temperatureEnabled = true, + }, + .currentScaler = 2.5 / (0x7fff * 0.08), // w. LEM 10 sensor 80mV/A + .brakeLimLow = 50700, + .brakeLimHigh = 50900, + .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), + .vSenseMult = VOLTAGE_MULT_DEFAULT, + .clockfreq = 25e6, + .bbm = 20, +}; +const TMC4671HardwareTypeConf conf1_3 = { + .name = "v1.3 ACS724 (66mV/A)", + .hwVersion = (uint8_t)TMC_HW_Ver::v1_3_66mv, + .adcOffset = 0, + .thermistorSettings = { + .thermistor_R2 = 1500, + .thermistor_R = 10000, + .thermistor_Beta = 4300, + .temp_limit = 90, + .temperatureEnabled = true, + }, + .currentScaler = 2.5 / (0x7fff * 0.066), // sensor 66mV/A + .brakeLimLow = 50700, + .brakeLimHigh = 50900, + .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), + .vSenseMult = VOLTAGE_MULT_DEFAULT, + .clockfreq = 25e6, + .bbm = 40, // May need longer deadtime + .flags{ + .mot_none = 1, + .mot_dc = 1, + .mot_bldc = 1, + .mot_stepper = 1, + + .enc_none = 1, + .enc_abn = 1, + .enc_sincos = 1, + .enc_uvw = 1, + .enc_hall = 1, + .enc_ext = 1, + + .allowFluxDissipationDeactivation = 1 + } +}; +const auto tmc4671_hw_configs_array = std::to_array({conf1_3,conf1_2_2,conf1_2,conf1_0}); +std::span TMC4671::tmc4671_hw_configs = tmc4671_hw_configs_array; +#endif + +// Only a single config with default settings. Some defaults can be overridden by defines +#ifdef TMC4671_CUSTOM_DEFAULT_HWCONF +const TMC4671HardwareTypeConf defaultconf; +const auto tmc4671_hw_configs_array = std::to_array({defaultconf}); +std::span TMC4671::tmc4671_hw_configs = tmc4671_hw_configs_array; + +#endif diff --git a/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c b/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c index d111d712a..80257d632 100644 --- a/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c +++ b/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c @@ -1,21 +1,21 @@ -/* - * eeprom_addresses.c - * - * Created on: 24.01.2020 - * Author: Yannick - * - * /!\ Generated from the file memory_map.csv - / ! \ DO NOT EDIT THIS DIRECTLY !!! - */ - -#include "eeprom_addresses.h" +/* + * eeprom_addresses.c + * + * Created on: 24.01.2020 + * Author: Yannick + * + * /!\ Generated from the file memory_map.csv + / ! \ DO NOT EDIT THIS DIRECTLY !!! + */ -/* -Add all used addresses to the VirtAddVarTab[] array. This is important for the eeprom emulation to correctly transfer between pages. -This ensures that addresses that were once used are not copied again in a page transfer if they are not in this array. -*/ - -const uint16_t VirtAddVarTab[NB_OF_VAR] = +#include "eeprom_addresses.h" + +/* +Add all used addresses to the VirtAddVarTab[] array. This is important for the eeprom emulation to correctly transfer between pages. +This ensures that addresses that were once used are not copied again in a page transfer if they are not in this array. +*/ + +const uint16_t VirtAddVarTab[NB_OF_VAR] = { // System variables ADR_HW_VERSION, @@ -212,10 +212,10 @@ const uint16_t VirtAddVarTab[NB_OF_VAR] = ADR_ADS111X_MAX_3, }; -/** - * Variables to be included in a flash dump - */ -const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR] = +/** + * Variables to be included in a flash dump + */ +const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR] = { // System variables // ADR_HW_VERSION, @@ -236,8 +236,6 @@ const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR] = ADR_SPI_BTN_1_CONF, ADR_SHIFTERANALOG_CONF, ADR_LOCAL_BTN_CONF, // Pin mask - ADR_LOCAL_BTN_CONF_2, // Misc settings - ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_1_CONF_2, ADR_SPI_BTN_2_CONF_2, ADR_LOCAL_BTN_CONF_3, // Pulse mask @@ -410,4 +408,4 @@ const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR] = ADR_ADS111X_MAX_2, ADR_ADS111X_MIN_3, ADR_ADS111X_MAX_3, -}; \ No newline at end of file +}; diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp index 442758d04..d199462e9 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp @@ -7,7 +7,6 @@ #include "tusb.h" #include "usb_descriptors.h" #include "usbd.h" -#include "stm32f4xx_hal.h" #include "main.h" #include "usb_hid_ffb_desc.h" @@ -48,62 +47,44 @@ const tusb_desc_device_t usb_devdesc_ffboard_composite = const uint8_t usb_cdc_conf[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, 2, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, 2, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP | TUSB_DESC_CONFIG_ATT_SELF_POWERED, 100), // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), }; + // Composite CDC and HID #ifdef AXIS1_FFB_HID_DESC -const uint8_t usb_cdc_hid_conf_1axis[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, 3, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), + const uint8_t usb_cdc_hid_conf_1axis[] ={USB_CONF_DESC_HID_CDC(USB_HID_1FFB_REPORT_DESC_SIZE,64)}; +#endif - // HID Descriptor. EP 83 and 2 - TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, USB_HID_1FFB_REPORT_DESC_SIZE, 0x83, 0x02, 64, HID_BINTERVAL), -}; +#ifdef AXIS1_FFB_HID_DESC_32B + const uint8_t usb_cdc_hid_conf_1axis_32b[] ={USB_CONF_DESC_HID_CDC(USB_HID_1FFB_REPORT_DESC_32B_SIZE,64)}; +#if TUD_OPT_HIGH_SPEED + const uint8_t usb_cdc_hid_conf_1axis_32b_hs[] ={USB_CONF_DESC_HID_CDC(USB_HID_1FFB_REPORT_DESC_32B_SIZE,512)}; +#endif #endif // Composite CDC and HID #ifdef AXIS2_FFB_HID_DESC -const uint8_t usb_cdc_hid_conf_2axis[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, 3, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), + const uint8_t usb_cdc_hid_conf_2axis[] ={USB_CONF_DESC_HID_CDC(USB_HID_2FFB_REPORT_DESC_SIZE,64)}; +#endif - // HID Descriptor. EP 83 and 2 - TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, USB_HID_2FFB_REPORT_DESC_SIZE, 0x83, 0x02, 64, HID_BINTERVAL), -}; +#ifdef AXIS2_FFB_HID_DESC_32B + const uint8_t usb_cdc_hid_conf_2axis_32b[] ={USB_CONF_DESC_HID_CDC(USB_HID_2FFB_REPORT_DESC_32B_SIZE,64)}; #endif // Composite CDC and HID #ifdef FFB_HID_DESC_GAMEPAD -const uint8_t usb_cdc_hid_conf_gamepad[] = -{ - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, 3, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_HID_INOUT_DESC_LEN), TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. - TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), - - // HID Descriptor. EP 83 and 2 - TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, USB_HID_GAMEPAD_REPORT_DESC_SIZE, 0x83, 0x02, 64, HID_BINTERVAL), -}; + const uint8_t usb_cdc_hid_conf_gamepad[] ={USB_CONF_DESC_HID_CDC(USB_HID_GAMEPAD_REPORT_DESC_SIZE,64)}; #endif // Composite CDC and MIDI uint8_t const usb_cdc_midi_conf[] = { // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, 4, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MIDI_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CONFIG_DESCRIPTOR(1, 4, 0, TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MIDI_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP | TUSB_DESC_CONFIG_ATT_SELF_POWERED, 100), // 1st CDC: Interface number, string index, EP notification address and size, EP data address (out, in) and size. TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), // Interface number, string index, EP Out & EP In address, EP size diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c b/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c index 3d1e30b26..afbc27caa 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c @@ -13,231 +13,17 @@ __ALIGN_BEGIN const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE] __ALIGN_END = { - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x04, // USAGE (Joystick) - 0xa1, 0x01, // COLLECTION (Application) - 0xa1, 0x00, // COLLECTION (Physical) - 0x85, 0x01, // REPORT_ID (1) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x40, // USAGE_MAXIMUM (Button 64) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x40, // REPORT_COUNT (64) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, HID_USAGE_X, // USAGE (X) - 0x09, HID_USAGE_Y, // USAGE (Y) - 0x09, HID_USAGE_Z, // USAGE (Z) - 0x09, HID_USAGE_RX, // USAGE (Rx) - 0x09, HID_USAGE_RY, // USAGE (Ry) - 0x09, HID_USAGE_RZ, // USAGE (Rz) - 0x09, HID_USAGE_SL1, // USAGE (Dial) - 0x09, HID_USAGE_SL0, // USAGE (Slider) - 0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767) - 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x08, // REPORT_COUNT (8) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0xc0, // END_COLLECTION - - - // Control reports - 0x06, 0x00, 0xFF, // USAGE_PAGE (Vendor) - 0x09, 0x00, // USAGE (Vendor) - 0xA1, 0x01, // Collection (Application) - - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - - - 0xc0, // END_COLLECTION - - - // BEGIN PID effects - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x92, // Usage PID State report - 0xA1,0x02, // Collection Datalink (logical) - 0x85,HID_ID_STATE+FFB_ID_OFFSET, // Report ID 2 - -// 0x09,0x22, // Usage Effect Block Index -// 0x15,0x01, // Logical Minimum 1 -// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) -// 0x35,0x01, // Physical Minimum 1 -// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) -// 0x75,0x08, // Report Size 8 -// 0x95,0x01, // Report Count 1 -// 0x81,0x02, // Input (Variable) - - - - 0x09,0x9F, // Usage Device is Pause - 0x09,0xA0, // Usage Actuators Enabled - 0x09,0xA4, // Usage Safety Switch - 0x09,0xA6, // Usage Actuator Power - - 0x09,0x94, // Usage Effect Playing - /* - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0x75,0x01, // Report Size 1 - 0x95,0x01, // Report Count 1 - 0x81,0x02, // Input (Variable)*/ //14 + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_16B, - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0x75,0x01, // Report Size 1 - 0x95,0x05, // Report Count 4 - 0x81,0x02, // Input (Variable) - 0x95,0x03, // Report Count 3 - 0x81,0x03, // Input (Constant, Variable) - 0xC0 , // End Collection + HIDDESC_CTRL_REPORTS, // HID command report support - /* - Output - Collection Datalink: - Usage Set Effect Report - ID:1 - Effect Block Index: 8bit + HIDDESC_FFB_STATEREP, - subcollection Effect Type - 12 effect types, 8bit each - - */ - 0x09,0x21, // Usage Set Effect Report - 0xA1,0x02, // Collection Datalink (Logical) - 0x85,HID_ID_EFFREP+FFB_ID_OFFSET, // Report ID 1 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x25, // Usage Effect Type - 0xA1,0x02, // Collection Datalink - 0x09, HID_USAGE_CONST, // Usage ET Constant Force - 0x09, HID_USAGE_RAMP, // Usage ET Ramp - 0x09, HID_USAGE_SQUR, // Usage ET Square - 0x09, HID_USAGE_SINE, // Usage ET Sine - 0x09, HID_USAGE_TRNG, // Usage ET Triangle - 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up - 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down - 0x09, HID_USAGE_SPRNG, // Usage ET Spring - 0x09, HID_USAGE_DMPR, // Usage ET Damper - 0x09, HID_USAGE_INRT, // Usage ET Inertia - 0x09, HID_USAGE_FRIC, // Usage ET Friction - // 0x09, 0x28, // Usage ET Custom Force Data - 0x25,0x0B, // Logical Maximum Bh (11d) - 0x15,0x01, // Logical Minimum 1 - 0x35,0x01, // Physical Minimum 1 - 0x45,0x0B, // Physical Maximum Bh (11d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x00, // Output - 0xC0 , // End Collection - 0x09,0x50, // Usage Duration - 0x09,0x54, // Usage Trigger Repeat Interval - 0x09,0x51, // Usage Sample Period - 0x09,0xA7, // Usage Start Delay - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) - 0x66,0x03,0x10, // Unit 1003h (4099d) - 0x55,0xFD, // Unit Exponent FDh (253d) - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x04, // Report Count 4 - 0x91,0x02, // Output (Variable) - 0x55,0x00, // Unit Exponent 0 - 0x66,0x00,0x00, // Unit 0 - 0x09,0x52, // Usage Gain - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) // TODO scaling? - 0x35,0x00, // Physical Minimum 0 - 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x53, // Usage Trigger Button - 0x15,0x01, // Logical Minimum 1 - 0x25,0x08, // Logical Maximum 8 - 0x35,0x01, // Physical Minimum 1 - 0x45,0x08, // Physical Maximum 8 - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) + HIDDESC_FFB_SETEFREP, 0x09,0x55, // Usage Axes Enable TODO multi axis 0xA1,0x02, // Collection Datalink @@ -289,39 +75,8 @@ __ALIGN_BEGIN const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE] __ALIGN 0xC0, // END_COLLECTION 0xC0, // END_COLLECTION - // Envelope Report Definition - 0x09,0x5A, // Usage Set Envelope Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_ENVREP+FFB_ID_OFFSET, // Report ID 2 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x5B, // Usage Attack Level - 0x09,0x5D, // Usage Fade Level - 0x16,0x00,0x00, // Logical Minimum 0 - 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) - 0x36,0x00,0x00, // Physical Minimum 0 - 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x02, // Report Count 2 - 0x91,0x02, // Output (Variable) - 0x09, 0x5C, // Usage Attack Time - 0x09, 0x5E, // Usage Fade Time - 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) - 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) - 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum FFFFFFFFh (4294967295) - 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum FFFFFFFFh (4294967295) - 0x75, 0x20, // Report Size 20h (32d) - 0x91, 0x02, // Output (Variable) - 0x45, 0x00, // Physical Maximum 0 - 0x66,0x00,0x00, // Unit 0 - 0x55,0x00, // Unit Exponent 0 - 0xC0 , // End Collection + HIDDESC_FFB_SETENVREP, + 0x09,0x5F, // Usage Set Condition Report 0xA1,0x02, // Collection Datalink 0x85,HID_ID_CONDREP+FFB_ID_OFFSET, // Report ID 3 @@ -378,97 +133,12 @@ __ALIGN_BEGIN const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE] __ALIGN 0x95,0x01, // Report Count 1 0x91,0x02, // Output (Variable) 0xC0 , // End Collection - 0x09,0x6E, // Usage Set Periodic Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_PRIDREP+FFB_ID_OFFSET, // Report ID 4 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x70, // Usage Magnitude - 0x16,0x00,0x00, // Logical Minimum 0 - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00,0x00, // Physical Minimum 0 - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09, 0x6F, // Usage Offset - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x95, 0x01, // Report Count 1 - 0x75, 0x10, // Report Size 16 - 0x91, 0x02, // Output (Variable) - 0x09, 0x71, // Usage Phase - 0x66, 0x14, 0x00, // Unit 14h (Eng Rotation, Degrees) - 0x55, 0xFE, // Unit Exponent FEh (X10^-2) - 0x15, 0x00, // Logical Minimum 0 - 0x27, 0x9F, 0x8C, 0x00, 0x00, // Logical Maximum 8C9Fh (35999d) - 0x35, 0x00, // Physical Minimum 0 - 0x47, 0x9F, 0x8C, 0x00, 0x00, // Physical Maximum 8C9Fh (35999d) - 0x75, 0x10, // Report Size 16 - 0x95, 0x01, // Report Count 1 - 0x91, 0x02, // Output (Variable) - 0x09, 0x72, // Usage Period - 0x15, 0x01, // Logical Minimum 1 - 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum 7FFFh (32K) - 0x35, 0x01, // Physical Minimum 1 - 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum 7FFFh (32K) - 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) - 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) - 0x75, 0x20, // Report Size 20h (32) - 0x95, 0x01, // Report Count 1 - 0x91, 0x02, // Output (Variable) - 0x66, 0x00, 0x00, // Unit 0 - 0x55,0x00, // Unit Exponent 0 - 0xC0 , // End Collection - 0x09,0x73, // Usage Set Constant Force Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_CONSTREP+FFB_ID_OFFSET, // Report ID 5 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x70, // Usage Magnitude - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x75, 0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - 0x09,0x74, // Usage Set Ramp Force Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_RAMPREP+FFB_ID_OFFSET, // Report ID 6 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x75, // Usage Ramp Start - 0x09,0x76, // Usage Ramp End - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x02, // Report Count 2 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection + + HIDDESC_FFB_SETPERIODICREP, + + HIDDESC_FFB_SETCFREP, + + HIDDESC_FFB_SETRAMPREP, // 0x09,0x68, // Usage Custom Force Data Report @@ -514,84 +184,10 @@ __ALIGN_BEGIN const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE] __ALIGN // 0x91,0x02, // Output (Variable) // 0xC0 , // End Collection - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x77, // Usage Effect Operation Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_EFOPREP+FFB_ID_OFFSET, // Report ID Ah (10d) - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x78, // Usage Effect Operation - 0xA1,0x02, // Collection Datalink - 0x09,0x79, // Usage Op Effect Start - 0x09,0x7A, // Usage Op Effect Start Solo - 0x09,0x7B, // Usage Op Effect Stop - 0x15,0x01, // Logical Minimum 1 - 0x25,0x03, // Logical Maximum 3 - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x00, // Output - 0xC0 , // End Collection - 0x09,0x7C, // Usage Loop Count - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x00, // Physical Maximum FFh (255d) - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - 0x09,0x90, // Usage PID Block Free Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_BLKFRREP+FFB_ID_OFFSET, // Report ID Bh (11d) - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - - 0x09,0x95, // Usage PID Device Control (0x96?) - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_CTRLREP+FFB_ID_OFFSET, // Report ID Ch (12d) - 0x09,0x96, // Usage PID Device Control (0x96?) - 0xA1,0x02, // Collection Datalink - - 0x09,0x97, // Usage DC Enable Actuators - 0x09,0x98, // Usage DC Disable Actuators - 0x09,0x99, // Usage DC Stop All Effects - 0x09,0x9A, // Usage DC Device Reset - 0x09,0x9B, // Usage DC Device Pause - 0x09,0x9C, // Usage DC Device Continue - - + HIDDESC_FFB_EFOPREP, + HIDDESC_FFB_BLOCKFREEREP, - 0x15,0x01, // Logical Minimum 1 - 0x25,0x06, // Logical Maximum 6 - 0x75,0x01, // Report Size 1 - 0x95,0x08, // Report Count 8 - 0x91,0x02, // Output - - 0xC0 , // End Collection - 0xC0 , // End Collection - 0x09,0x7D, // Usage Device Gain Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_GAINREP+FFB_ID_OFFSET, // Report ID Dh (13d) - 0x09,0x7E, // Usage Device Gain - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection + HIDDESC_FFB_DEVCTRLREP, // 0x09,0x6B, // Usage Set Custom Force Report // 0xA1,0x02, // Collection Datalink // 0x85,HID_ID_SETCREP+FFB_ID_OFFSET, // Report ID Eh (14d) @@ -624,110 +220,168 @@ __ALIGN_BEGIN const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE] __ALIGN // 0x55,0x00, // Unit Exponent 0 // 0x66,0x00,0x00, // Unit 0 // 0xC0 , // End Collection - 0x09,0xAB, // Usage Create New Effect Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_NEWEFREP+FFB_ID_OFFSET, // Report ID 1 - 0x09,0x25, // Usage Effect Type + HIDDESC_FFB_NEWEFREP, + HIDDESC_FFB_BLOCKLOADREP, + + HIDDESC_FFB_POOLREP, + + 0xC0 /* END_COLLECTION */ +}; +#endif + +#ifdef AXIS1_FFB_HID_DESC_32B +__ALIGN_BEGIN const uint8_t hid_1ffb_desc_32b[USB_HID_1FFB_REPORT_DESC_32B_SIZE] __ALIGN_END = +{ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_32B, + + HIDDESC_CTRL_REPORTS, // HID command report support + + HIDDESC_FFB_STATEREP, + /* + Output + Collection Datalink: + Usage Set Effect Report + + ID:1 + Effect Block Index: 8bit + + subcollection Effect Type + 12 effect types, 8bit each + + */ + HIDDESC_FFB_SETEFREP, + + 0x09,0x55, // Usage Axes Enable TODO multi axis + 0xA1,0x02, // Collection Datalink + 0x05,0x01, // Usage Page Generic Desktop + 0x09,0x30, // Usage X + //0x09,0x31, // Usage Y + 0x15,0x00, // Logical Minimum 0 + 0x25,0x00, // Logical Maximum 0 + 0x75,0x01, // Report Size 1 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x56, // Usage Direction Enable + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x95,0x06, // Report Count 6 + 0x91,0x03, // Output (Constant, Variable) + + 0x09,0x57, // Usage Direction 0xA1,0x02, // Collection Datalink - 0x09, HID_USAGE_CONST, // Usage ET Constant Force - 0x09, HID_USAGE_RAMP, // Usage ET Ramp - 0x09, HID_USAGE_SQUR, // Usage ET Square - 0x09, HID_USAGE_SINE, // Usage ET Sine - 0x09, HID_USAGE_TRNG, // Usage ET Triangle - 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up - 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down - 0x09, HID_USAGE_SPRNG, // Usage ET Spring - 0x09, HID_USAGE_DMPR, // Usage ET Damper - 0x09, HID_USAGE_INRT, // Usage ET Inertia - 0x09, HID_USAGE_FRIC, // Usage ET Friction -// 0x09, 0x28, // Usage ET Custom Force Data - 0x25,0x0B, // Logical Maximum Ch (11d) + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 +// 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x66,0x14,0x00, // Unit 14h (20d) +// 0x55,0xFE, // Unit Exponent FEh (254d) +// 0x15,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x15,0x00, // Logical Minimum 0 + 0x27,0xA0,0x8C,0x00,0x00, // Logical Maximum 8CA0h (36000d) + 0x35,0x00, // Physical Minimum 0 + 0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d) + 0x66,0x00,0x00, // Unit 0 + 0x75,0x10, // Report Size 16 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x55,0x00, // Unit Exponent 0 + 0x66,0x00,0x00, // Unit 0 + 0xC0, // End Collection + + 0x05, 0x0F, // USAGE_PAGE (Physical Interface) + 0x09, 0x58, // USAGE (Type Specific Block Offset) + 0xA1, 0x02, // COLLECTION (Logical) + 0x0B, 0x01, 0x00, 0x0A, 0x00, //USAGE (Ordinals:Instance 1 + //0x0B, 0x02, 0x00, 0x0A, 0x00, //USAGE (Ordinals:Instance 2) + 0x26, 0xFD, 0x7F, // LOGICAL_MAXIMUM (32765) ; 32K RAM or ROM max. + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x01, // REPORT_COUNT (1) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0xC0, // END_COLLECTION + 0xC0, // END_COLLECTION + + HIDDESC_FFB_SETENVREP, + + 0x09,0x5F, // Usage Set Condition Report + 0xA1,0x02, // Collection Datalink + 0x85,HID_ID_CONDREP+FFB_ID_OFFSET, // Report ID 3 + 0x09,0x22, // Usage Effect Block Index 0x15,0x01, // Logical Minimum 1 + 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) 0x35,0x01, // Physical Minimum 1 - 0x45,0x0B, // Physical Maximum Ch (11d) + 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) 0x75,0x08, // Report Size 8 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature - 0xC0 , // End Collection - 0x05,0x01, // Usage Page Generic Desktop - 0x09,0x3B, // Usage Reserved (Byte count) - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x01, // Logical Maximum 1FFh (511d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x01, // Physical Maximum 1FFh (511d) - 0x75,0x0A, // Report Size Ah (10d) - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x75,0x06, // Report Size 6 - 0xB1,0x01, // Feature (Constant) - 0xC0 , // End Collection - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x89, // Usage Block Load Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_BLKLDREP+FFB_ID_OFFSET, // Report ID 0x12 - 0x09,0x22, // Usage Effect Block Index - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x15,0x01, // Logical Minimum 1 - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x09,0x8B, // Usage Block Load Status - 0xA1,0x02, // Collection Datalink - 0x09,0x8C, // Usage Block Load Success - 0x09,0x8D, // Usage Block Load Full - 0x09,0x8E, // Usage Block Load Error - 0x15,0x01, // Logical Minimum 1 + 0x91,0x02, // Output (Variable) + 0x09,0x23, // Usage Parameter Block Offset + 0x15,0x00, // Logical Minimum 0 0x25,0x03, // Logical Maximum 3 - 0x35,0x01, // Physical Minimum 1 + 0x35,0x00, // Physical Minimum 0 0x45,0x03, // Physical Maximum 3 - 0x75,0x08, // Report Size 8 + 0x75,0x06, // Report Size 6 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature - 0xC0 , // End Collection - 0x09,0xAC, // Usage Pool available - 0x15,0x00, // Logical Minimum 0 - 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) - 0x35,0x00, // Physical Minimum 0 - 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature + 0x91,0x02, // Output (Variable) + 0x09,0x58, // Usage Type Specific Block Off... + 0xA1,0x02, // Collection Datalink + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 +// 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x75,0x02, // Report Size 2 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) + 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) + 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) + 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) + + 0x09,0x60, // Usage CP Offset + 0x75,0x10, // Report Size 16 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x36,0x00, 0x80, // Physical Minimum (-32768) + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x09,0x61, // Usage Positive Coefficient + 0x09,0x62, // Usage Negative Coefficient + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x16,0x00,0x00, // Logical Minimum 0 + 0x26,0xff, 0x7f, // Logical Maximum (32767) + 0x36,0x00,0x00, // Physical Minimum 0 + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x09,0x63, // Usage Positive Saturation + 0x09,0x64, // Usage Negative Saturation + 0x75,0x10, // Report Size 16 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x09,0x65, // Usage Dead Band + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) 0xC0 , // End Collection - 0x09,0x7F, // Usage PID Pool Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_POOLREP+FFB_ID_OFFSET, // Report ID 0x13 - 0x09,0x80, // Usage RAM Pool size - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0x15,0x00, // Logical Minimum 0 - 0x35,0x00, // Physical Minimum 0 - 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) - 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) - 0xB1,0x02, // Feature (Variable) - 0x09,0x83, // Usage Simultaneous Effects Max - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x46,0xFF,0x00, // Physical Maximum FFh (255d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x09,0xA9, // Usage Device Managed Pool - 0x09,0xAA, // Usage Shared Parameter Blocks - 0x75,0x01, // Report Size 1 - 0x95,0x02, // Report Count 2 - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0xB1,0x02, // Feature (Variable) - 0x75,0x06, // Report Size 6 - 0x95,0x01, // Report Count 1 - 0xB1,0x03, // Feature (Constant, Variable) - 0xC0, // End Collection + HIDDESC_FFB_SETPERIODICREP, + + HIDDESC_FFB_SETCFREP, + + HIDDESC_FFB_SETRAMPREP, + + HIDDESC_FFB_EFOPREP, + + HIDDESC_FFB_BLOCKFREEREP, + + HIDDESC_FFB_DEVCTRLREP, + + HIDDESC_FFB_NEWEFREP, + + HIDDESC_FFB_BLOCKLOADREP, + + HIDDESC_FFB_POOLREP, 0xC0 /* END_COLLECTION */ }; - #endif + diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c b/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c index 79e98bcb0..6083e7e52 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c @@ -12,210 +12,95 @@ #ifdef AXIS2_FFB_HID_DESC __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN_END = { - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x04, // USAGE (Joystick) - 0xa1, 0x01, // COLLECTION (Application) - 0xa1, 0x00, // COLLECTION (Physical) - 0x85, 0x01, // REPORT_ID (1) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x40, // USAGE_MAXIMUM (Button 64) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x40, // REPORT_COUNT (64) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, HID_USAGE_X, // USAGE (X) - 0x09, HID_USAGE_Y, // USAGE (Y) - 0x09, HID_USAGE_Z, // USAGE (Z) - 0x09, HID_USAGE_RX, // USAGE (Rx) - 0x09, HID_USAGE_RY, // USAGE (Ry) - 0x09, HID_USAGE_RZ, // USAGE (Rz) - 0x09, HID_USAGE_SL1, // USAGE (Dial) - 0x09, HID_USAGE_SL0, // USAGE (Slider) - 0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767) - 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x08, // REPORT_COUNT (8) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0xc0, // END_COLLECTION - - - // Control reports - 0x06, 0x00, 0xFF, // USAGE_PAGE (Vendor) - 0x09, 0x00, // USAGE (Vendor) - 0xA1, 0x01, // Collection (Application) -// 0x85,HID_ID_CUSTOMCMD, // Report ID -// 0x09, 0x01, // USAGE (Vendor) type -// 0x15, 0x00, // LOGICAL_MINIMUM (0) -// 0x26, 0x04, 0x00, // Logical Maximum 4 -// 0x75, 0x08, // REPORT_SIZE (8) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0xb1, 0x02, // FEATURE (Data,Var,Abs) -// -// 0x09, 0x02, // USAGE (Vendor) cmd -// 0x09, 0x03, // USAGE (Vendor) addr -// 0x75, 0x20, // REPORT_SIZE (32) -// 0x95, 0x02, // REPORT_COUNT (2) -// 0xb1, 0x02, // FEATURE (Data,Var,Abs) -// -// 0x09, 0x04, // USAGE (Vendor) data -// 0x75, 0x40, // REPORT_SIZE (64) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0xb1, 0x02, // FEATURE (Data,Var,Abs) -// -// 0x85,HID_ID_CUSTOMCMD, // Report ID -// 0x09, 0x01, // USAGE (Vendor) -// 0x15, 0x00, // LOGICAL_MINIMUM (0) -// 0x26, 0x04, 0x00, // Logical Maximum 4 -// 0x75, 0x08, // REPORT_SIZE (8) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0x91, 0x02, // OUTPUT (Data,Var,Abs) -// -// 0x09, 0x02, // USAGE (Vendor) -// 0x09, 0x03, // USAGE (Vendor) -// 0x75, 0x20, // REPORT_SIZE (32) -// 0x95, 0x02, // REPORT_COUNT (2) -// 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_16B, +// 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +// 0x09, 0x04, // USAGE (Joystick) +// 0xa1, 0x01, // COLLECTION (Application) +// 0xa1, 0x00, // COLLECTION (Physical) +// 0x85, 0x01, // REPORT_ID (1) +// 0x05, 0x09, // USAGE_PAGE (Button) +// 0x19, 0x01, // USAGE_MINIMUM (Button 1) +// 0x29, 0x40, // USAGE_MAXIMUM (Button 64) +// 0x15, 0x00, // LOGICAL_MINIMUM (0) +// 0x25, 0x01, // LOGICAL_MAXIMUM (1) +// 0x95, 0x40, // REPORT_COUNT (64) +// 0x75, 0x01, // REPORT_SIZE (1) +// 0x81, 0x02, // INPUT (Data,Var,Abs) +// 0x05, 0x01, // USAGE_PAGE (Generic Desktop) +// 0x09, HID_USAGE_DESKTOP_X, // USAGE (X) +//#if defined(HIDAXISRES_32B) && MAX_AXIS == 1 +// HIDDESC_32B_ENTRY(0x01) +//#endif +// 0x09, HID_USAGE_DESKTOP_Y, // USAGE (Y) +//#if defined(HIDAXISRES_32B) && MAX_AXIS == 2 +// HIDDESC_32B_ENTRY(0x02) +//#endif +// 0x09, HID_USAGE_DESKTOP_Z, // USAGE (Z) +//#if defined(HIDAXISRES_32B) && MAX_AXIS == 3 +// HIDDESC_32B_ENTRY(0x03) +//#endif +// 0x09, HID_USAGE_DESKTOP_RX, // USAGE (Rx) +// 0x09, HID_USAGE_DESKTOP_RY, // USAGE (Ry) +// 0x09, HID_USAGE_DESKTOP_RZ, // USAGE (Rz) +// 0x09, HID_USAGE_DESKTOP_DIAL, // USAGE (Dial) +// 0x09, HID_USAGE_DESKTOP_SLIDER, // USAGE (Slider) +// 0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767) +// 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) +// 0x75, 0x10, // REPORT_SIZE (16) +//#if defined(HIDAXISRES_32B) +// 0x95, 0x08-MAX_AXIS, // REPORT_COUNT (8- amount of 32b axes) +//#else +// 0x95, 0x08, // REPORT_COUNT (8) +//#endif +// 0x81, 0x02, // INPUT (Data,Var,Abs) +// 0xc0, // END_COLLECTION + + HIDDESC_CTRL_REPORTS, // HID command report support + + // BEGIN PID effects +// 0x05,0x0F, // Usage Page Physical Interface +// 0x09,0x92, // Usage PID State report +// 0xA1,0x02, // Collection Datalink (logical) +// 0x85,HID_ID_STATE+FFB_ID_OFFSET, // Report ID 2 // -// 0x09, 0x04, // USAGE (Vendor) -// 0x75, 0x40, // REPORT_SIZE (64) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0x91, 0x02, // OUTPUT (Data,Var,Abs) +//// 0x09,0x22, // Usage Effect Block Index +//// 0x15,0x01, // Logical Minimum 1 +//// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +//// 0x35,0x01, // Physical Minimum 1 +//// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x01, // Report Count 1 +//// 0x81,0x02, // Input (Variable) // -// 0x85,HID_ID_CUSTOMCMD, // Report ID -// 0x09, 0x01, // USAGE (Vendor) -// 0x15, 0x00, // LOGICAL_MINIMUM (0) -// 0x26, 0x04, 0x00, // Logical Maximum 4 -// 0x75, 0x08, // REPORT_SIZE (8) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0x81, 0x02, // INPUT (Data,Var,Abs) +// 0x09,0x9F, // Usage Device is Pause +// 0x09,0xA0, // Usage Actuators Enabled +// 0x09,0xA4, // Usage Safety Switch +// 0x09,0xA6, // Usage Actuator Power // -// 0x09, 0x02, // USAGE (Vendor) -// 0x09, 0x03, // USAGE (Vendor) -// 0x75, 0x20, // REPORT_SIZE (32) -// 0x95, 0x02, // REPORT_COUNT (2) -// 0x81, 0x02, // INPUT (Data,Var,Abs) +// 0x09,0x94, // Usage Effect Playing +// /* +// 0x15,0x00, // Logical Minimum 0 +// 0x25,0x01, // Logical Maximum 1 +// 0x35,0x00, // Physical Minimum 0 +// 0x45,0x01, // Physical Maximum 1 +// 0x75,0x01, // Report Size 1 +// 0x95,0x01, // Report Count 1 +// 0x81,0x02, // Input (Variable)*/ //14 // -// 0x09, 0x04, // USAGE (Vendor) -// 0x75, 0x40, // REPORT_SIZE (64) -// 0x95, 0x01, // REPORT_COUNT (1) -// 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - - - 0xc0, // END_COLLECTION - - - // BEGIN PID effects - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x92, // Usage PID State report - 0xA1,0x02, // Collection Datalink (logical) - 0x85,HID_ID_STATE+FFB_ID_OFFSET, // Report ID 2 - -// 0x09,0x22, // Usage Effect Block Index -// 0x15,0x01, // Logical Minimum 1 -// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) -// 0x35,0x01, // Physical Minimum 1 -// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) -// 0x75,0x08, // Report Size 8 -// 0x95,0x01, // Report Count 1 +// 0x15,0x00, // Logical Minimum 0 +// 0x25,0x01, // Logical Maximum 1 +// 0x35,0x00, // Physical Minimum 0 +// 0x45,0x01, // Physical Maximum 1 +// 0x75,0x01, // Report Size 1 +// 0x95,0x05, // Report Count 4 // 0x81,0x02, // Input (Variable) - - - - 0x09,0x9F, // Usage Device is Pause - 0x09,0xA0, // Usage Actuators Enabled - 0x09,0xA4, // Usage Safety Switch - 0x09,0xA6, // Usage Actuator Power - - 0x09,0x94, // Usage Effect Playing - /* - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0x75,0x01, // Report Size 1 - 0x95,0x01, // Report Count 1 - 0x81,0x02, // Input (Variable)*/ //14 - - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0x75,0x01, // Report Size 1 - 0x95,0x05, // Report Count 4 - 0x81,0x02, // Input (Variable) - 0x95,0x03, // Report Count 3 - 0x81,0x03, // Input (Constant, Variable) - 0xC0 , // End Collection - +// 0x95,0x03, // Report Count 3 +// 0x81,0x03, // Input (Constant, Variable) +// 0xC0 , // End Collection + HIDDESC_FFB_STATEREP, /* Output Collection Datalink: @@ -228,71 +113,72 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN 12 effect types, 8bit each */ - 0x09,0x21, // Usage Set Effect Report - 0xA1,0x02, // Collection Datalink (Logical) - 0x85,HID_ID_EFFREP+FFB_ID_OFFSET, // Report ID 1 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x25, // Usage Effect Type - 0xA1,0x02, // Collection Datalink - 0x09, HID_USAGE_CONST, // Usage ET Constant Force - 0x09, HID_USAGE_RAMP, // Usage ET Ramp - 0x09, HID_USAGE_SQUR, // Usage ET Square - 0x09, HID_USAGE_SINE, // Usage ET Sine - 0x09, HID_USAGE_TRNG, // Usage ET Triangle - 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up - 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down - 0x09, HID_USAGE_SPRNG, // Usage ET Spring - 0x09, HID_USAGE_DMPR, // Usage ET Damper - 0x09, HID_USAGE_INRT, // Usage ET Inertia - 0x09, HID_USAGE_FRIC, // Usage ET Friction - // 0x09, 0x28, // Usage ET Custom Force Data - 0x25,0x0B, // Logical Maximum Bh (11d) - 0x15,0x01, // Logical Minimum 1 - 0x35,0x01, // Physical Minimum 1 - 0x45,0x0B, // Physical Maximum Bh (11d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x00, // Output - 0xC0 , // End Collection - 0x09,0x50, // Usage Duration - 0x09,0x54, // Usage Trigger Repeat Interval - 0x09,0x51, // Usage Sample Period - 0x09,0xA7, // Usage Start Delay - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) - 0x66,0x03,0x10, // Unit 1003h (4099d) - 0x55,0xFD, // Unit Exponent FDh (253d) - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x04, // Report Count 4 - 0x91,0x02, // Output (Variable) - 0x55,0x00, // Unit Exponent 0 - 0x66,0x00,0x00, // Unit 0 - 0x09,0x52, // Usage Gain - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) // TODO scaling? - 0x35,0x00, // Physical Minimum 0 - 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x53, // Usage Trigger Button - 0x15,0x01, // Logical Minimum 1 - 0x25,0x08, // Logical Maximum 8 - 0x35,0x01, // Physical Minimum 1 - 0x45,0x08, // Physical Maximum 8 - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - + HIDDESC_FFB_SETEFREP, +// 0x09,0x21, // Usage Set Effect Report +// 0xA1,0x02, // Collection Datalink (Logical) +// 0x85,HID_ID_EFFREP+FFB_ID_OFFSET, // Report ID 1 +// 0x09,0x22, // Usage Effect Block Index +// 0x15,0x01, // Logical Minimum 1 +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09,0x25, // Usage Effect Type +// 0xA1,0x02, // Collection Datalink +// 0x09, HID_USAGE_CONST, // Usage ET Constant Force +// 0x09, HID_USAGE_RAMP, // Usage ET Ramp +// 0x09, HID_USAGE_SQUR, // Usage ET Square +// 0x09, HID_USAGE_SINE, // Usage ET Sine +// 0x09, HID_USAGE_TRNG, // Usage ET Triangle +// 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up +// 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down +// 0x09, HID_USAGE_SPRNG, // Usage ET Spring +// 0x09, HID_USAGE_DMPR, // Usage ET Damper +// 0x09, HID_USAGE_INRT, // Usage ET Inertia +// 0x09, HID_USAGE_FRIC, // Usage ET Friction +// // 0x09, 0x28, // Usage ET Custom Force Data +// 0x25,0x0B, // Logical Maximum Bh (11d) +// 0x15,0x01, // Logical Minimum 1 +// 0x35,0x01, // Physical Minimum 1 +// 0x45,0x0B, // Physical Maximum Bh (11d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x00, // Output +// 0xC0 , // End Collection +// 0x09,0x50, // Usage Duration +// 0x09,0x54, // Usage Trigger Repeat Interval +// 0x09,0x51, // Usage Sample Period +// 0x09,0xA7, // Usage Start Delay +// 0x15,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) +// 0x35,0x00, // Physical Minimum 0 +// 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) +// 0x66,0x03,0x10, // Unit 1003h (4099d) +// 0x55,0xFD, // Unit Exponent FDh (253d) +// 0x75,0x10, // Report Size 10h (16d) +// 0x95,0x04, // Report Count 4 +// 0x91,0x02, // Output (Variable) +// 0x55,0x00, // Unit Exponent 0 +// 0x66,0x00,0x00, // Unit 0 +// 0x09,0x52, // Usage Gain +// 0x15,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) // TODO scaling? +// 0x35,0x00, // Physical Minimum 0 +// 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09,0x53, // Usage Trigger Button +// 0x15,0x01, // Logical Minimum 1 +// 0x25,0x08, // Logical Maximum 8 +// 0x35,0x01, // Physical Minimum 1 +// 0x45,0x08, // Physical Maximum 8 +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// Len 108 bytes following 0x09,0x55, // Usage Axes Enable TODO multi axis 0xA1,0x02, // Collection Datalink 0x05,0x01, // Usage Page Generic Desktop @@ -344,39 +230,44 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN 0xC0, // END_COLLECTION 0xC0, // END_COLLECTION - // Envelope Report Definition - 0x09,0x5A, // Usage Set Envelope Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_ENVREP+FFB_ID_OFFSET, // Report ID 2 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x5B, // Usage Attack Level - 0x09,0x5D, // Usage Fade Level - 0x16,0x00,0x00, // Logical Minimum 0 - 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) - 0x36,0x00,0x00, // Physical Minimum 0 - 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x02, // Report Count 2 - 0x91,0x02, // Output (Variable) - 0x09, 0x5C, // Usage Attack Time - 0x09, 0x5E, // Usage Fade Time - 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) - 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) - 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum FFFFFFFFh (4294967295) - 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum FFFFFFFFh (4294967295) - 0x75, 0x20, // Report Size 20h (32d) - 0x91, 0x02, // Output (Variable) - 0x45, 0x00, // Physical Maximum 0 - 0x66,0x00,0x00, // Unit 0 - 0x55,0x00, // Unit Exponent 0 - 0xC0 , // End Collection + HIDDESC_FFB_SETENVREP, +// // Envelope Report Definition +// 0x09,0x5A, // Usage Set Envelope Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_ENVREP+FFB_ID_OFFSET, // Report ID 2 +// 0x09,0x22, // Usage Effect Block Index +// 0x15,0x01, // Logical Minimum 1 +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09,0x5B, // Usage Attack Level +// 0x09,0x5D, // Usage Fade Level +// 0x16,0x00,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) +// 0x36,0x00,0x00, // Physical Minimum 0 +// 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) +// 0x75,0x10, // Report Size 16 +// 0x95,0x02, // Report Count 2 +// 0x91,0x02, // Output (Variable) +// 0x09, 0x5C, // Usage Attack Time +// 0x09, 0x5E, // Usage Fade Time +// 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) +// 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) +// 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum FFFFFFFFh (4294967295) +// 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum FFFFFFFFh (4294967295) +// 0x75, 0x20, // Report Size 20h (32d) +// 0x91, 0x02, // Output (Variable) +// 0x45, 0x00, // Physical Maximum 0 +// 0x66,0x00,0x00, // Unit 0 +// 0x55,0x00, // Unit Exponent 0 +// 0xC0 , // End Collection +// HIDDESC_FFB_SETCONDREP, + + // Condition report depends on ffb axis count + // 125 bytes following 0x09,0x5F, // Usage Set Condition Report 0xA1,0x02, // Collection Datalink 0x85,HID_ID_CONDREP+FFB_ID_OFFSET, // Report ID 3 @@ -433,102 +324,62 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN 0x95,0x01, // Report Count 1 0x91,0x02, // Output (Variable) 0xC0 , // End Collection - 0x09,0x6E, // Usage Set Periodic Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_PRIDREP+FFB_ID_OFFSET, // Report ID 4 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x70, // Usage Magnitude - 0x16,0x00,0x00, // Logical Minimum 0 - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00,0x00, // Physical Minimum 0 - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09, 0x6F, // Usage Offset - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x95, 0x01, // Report Count 1 - 0x75, 0x10, // Report Size 16 - 0x91, 0x02, // Output (Variable) - 0x09, 0x71, // Usage Phase - 0x66, 0x14, 0x00, // Unit 14h (Eng Rotation, Degrees) - 0x55, 0xFE, // Unit Exponent FEh (X10^-2) - 0x15, 0x00, // Logical Minimum 0 - 0x27, 0x9F, 0x8C, 0x00, 0x00, // Logical Maximum 8C9Fh (35999d) - 0x35, 0x00, // Physical Minimum 0 - 0x47, 0x9F, 0x8C, 0x00, 0x00, // Physical Maximum 8C9Fh (35999d) - 0x75, 0x10, // Report Size 16 - 0x95, 0x01, // Report Count 1 - 0x91, 0x02, // Output (Variable) - 0x09, 0x72, // Usage Period - 0x15, 0x01, // Logical Minimum 1 - 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum 7FFFh (32K) - 0x35, 0x01, // Physical Minimum 1 - 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum 7FFFh (32K) - 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) - 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) - 0x75, 0x20, // Report Size 20h (32) - 0x95, 0x01, // Report Count 1 - 0x91, 0x02, // Output (Variable) - 0x66, 0x00, 0x00, // Unit 0 - 0x55,0x00, // Unit Exponent 0 - 0xC0 , // End Collection - 0x09,0x73, // Usage Set Constant Force Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_CONSTREP+FFB_ID_OFFSET, // Report ID 5 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x70, // Usage Magnitude - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x75, 0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - 0x09,0x74, // Usage Set Ramp Force Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_RAMPREP+FFB_ID_OFFSET, // Report ID 6 - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x75, // Usage Ramp Start - 0x09,0x76, // Usage Ramp End - 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) - 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) - 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) - 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) - 0x75,0x10, // Report Size 16 - 0x95,0x02, // Report Count 2 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - -// 0x09,0x68, // Usage Custom Force Data Report + HIDDESC_FFB_SETPERIODICREP, +// 0x09,0x6E, // Usage Set Periodic Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_PRIDREP+FFB_ID_OFFSET, // Report ID 4 +// 0x09,0x22, // Usage Effect Block Index +// 0x15,0x01, // Logical Minimum 1 +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09,0x70, // Usage Magnitude +// 0x16,0x00,0x00, // Logical Minimum 0 +// 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) +// 0x36,0x00,0x00, // Physical Minimum 0 +// 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) +// 0x75,0x10, // Report Size 16 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09, 0x6F, // Usage Offset +// 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) +// 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) +// 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) +// 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) +// 0x95, 0x01, // Report Count 1 +// 0x75, 0x10, // Report Size 16 +// 0x91, 0x02, // Output (Variable) +// 0x09, 0x71, // Usage Phase +// 0x66, 0x14, 0x00, // Unit 14h (Eng Rotation, Degrees) +// 0x55, 0xFE, // Unit Exponent FEh (X10^-2) +// 0x15, 0x00, // Logical Minimum 0 +// 0x27, 0x9F, 0x8C, 0x00, 0x00, // Logical Maximum 8C9Fh (35999d) +// 0x35, 0x00, // Physical Minimum 0 +// 0x47, 0x9F, 0x8C, 0x00, 0x00, // Physical Maximum 8C9Fh (35999d) +// 0x75, 0x10, // Report Size 16 +// 0x95, 0x01, // Report Count 1 +// 0x91, 0x02, // Output (Variable) +// 0x09, 0x72, // Usage Period +// 0x15, 0x01, // Logical Minimum 1 +// 0x27, 0xFF, 0x7F, 0x00, 0x00, // Logical Maximum 7FFFh (32K) +// 0x35, 0x01, // Physical Minimum 1 +// 0x47, 0xFF, 0x7F, 0x00, 0x00, // Physical Maximum 7FFFh (32K) +// 0x66, 0x03, 0x10, // Unit 1003h (English Linear, Seconds) +// 0x55, 0xFD, // Unit Exponent FDh (X10^-3 ==> Milisecond) +// 0x75, 0x20, // Report Size 20h (32) +// 0x95, 0x01, // Report Count 1 +// 0x91, 0x02, // Output (Variable) +// 0x66, 0x00, 0x00, // Unit 0 +// 0x55,0x00, // Unit Exponent 0 +// 0xC0 , // End Collection + HIDDESC_FFB_SETCFREP, +// 0x09,0x73, // Usage Set Constant Force Report // 0xA1,0x02, // Collection Datalink -// 0x85,HID_ID_CSTMREP+FFB_ID_OFFSET, // Report ID 7 +// 0x85,HID_ID_CONSTREP+FFB_ID_OFFSET, // Report ID 5 // 0x09,0x22, // Usage Effect Block Index // 0x15,0x01, // Logical Minimum 1 // 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) @@ -537,119 +388,19 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN // 0x75,0x08, // Report Size 8 // 0x95,0x01, // Report Count 1 // 0x91,0x02, // Output (Variable) -// 0x09,0x6C, // Usage Custom Force Data Offset -// 0x15,0x00, // Logical Minimum 0 -// 0x26,0x10,0x27, // Logical Maximum 2710h (10000d) -// 0x35,0x00, // Physical Minimum 0 -// 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) -// 0x75,0x10, // Report Size 10h (16d) +// 0x09,0x70, // Usage Magnitude +// 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) +// 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) +// 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) +// 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) +// 0x75, 0x10, // Report Size 10h (16d) // 0x95,0x01, // Report Count 1 // 0x91,0x02, // Output (Variable) -// 0x09,0x69, // Usage Custom Force Data -// 0x15,0x81, // Logical Minimum 81h (-127d) -// 0x25,0x7F, // Logical Maximum 7Fh (127d) -// 0x35,0x00, // Physical Minimum 0 -// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) -// 0x75,0x08, // Report Size 8 -// 0x95,0x0C, // Report Count Ch (12d) -// 0x92,0x02,0x01, // Output (Variable, Buffered) // 0xC0 , // End Collection -// 0x09,0x66, // Usage Download Force Sample -// 0xA1,0x02, // Collection Datalink -// 0x85,HID_ID_SMPLREP+FFB_ID_OFFSET, // Report ID 8 -// 0x05,0x01, // Usage Page Generic Desktop -// 0x09,0x30, // Usage X -// 0x09,0x31, // Usage Y -// 0x15,0x81, // Logical Minimum 81h (-127d) -// 0x25,0x7F, // Logical Maximum 7Fh (127d) -// 0x35,0x00, // Physical Minimum 0 -// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) -// 0x75,0x08, // Report Size 8 -// 0x95,0x02, // Report Count 2 -// 0x91,0x02, // Output (Variable) -// 0xC0 , // End Collection - - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x77, // Usage Effect Operation Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_EFOPREP+FFB_ID_OFFSET, // Report ID Ah (10d) - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0x09,0x78, // Usage Effect Operation - 0xA1,0x02, // Collection Datalink - 0x09,0x79, // Usage Op Effect Start - 0x09,0x7A, // Usage Op Effect Start Solo - 0x09,0x7B, // Usage Op Effect Stop - 0x15,0x01, // Logical Minimum 1 - 0x25,0x03, // Logical Maximum 3 - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x00, // Output - 0xC0 , // End Collection - 0x09,0x7C, // Usage Loop Count - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x00, // Physical Maximum FFh (255d) - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - 0x09,0x90, // Usage PID Block Free Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_BLKFRREP+FFB_ID_OFFSET, // Report ID Bh (11d) - 0x09,0x22, // Usage Effect Block Index - 0x15,0x01, // Logical Minimum 1 - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection - - 0x09,0x95, // Usage PID Device Control (0x96?) - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_CTRLREP+FFB_ID_OFFSET, // Report ID Ch (12d) - 0x09,0x96, // Usage PID Device Control (0x96?) - 0xA1,0x02, // Collection Datalink - - 0x09,0x97, // Usage DC Enable Actuators - 0x09,0x98, // Usage DC Disable Actuators - 0x09,0x99, // Usage DC Stop All Effects - 0x09,0x9A, // Usage DC Device Reset - 0x09,0x9B, // Usage DC Device Pause - 0x09,0x9C, // Usage DC Device Continue - - - - 0x15,0x01, // Logical Minimum 1 - 0x25,0x06, // Logical Maximum 6 - 0x75,0x01, // Report Size 1 - 0x95,0x08, // Report Count 8 - 0x91,0x02, // Output - - 0xC0 , // End Collection - 0xC0 , // End Collection - 0x09,0x7D, // Usage Device Gain Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_GAINREP+FFB_ID_OFFSET, // Report ID Dh (13d) - 0x09,0x7E, // Usage Device Gain - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0x91,0x02, // Output (Variable) - 0xC0 , // End Collection -// 0x09,0x6B, // Usage Set Custom Force Report + HIDDESC_FFB_SETRAMPREP, +// 0x09,0x74, // Usage Set Ramp Force Report // 0xA1,0x02, // Collection Datalink -// 0x85,HID_ID_SETCREP+FFB_ID_OFFSET, // Report ID Eh (14d) +// 0x85,HID_ID_RAMPREP+FFB_ID_OFFSET, // Report ID 6 // 0x09,0x22, // Usage Effect Block Index // 0x15,0x01, // Logical Minimum 1 // 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) @@ -658,131 +409,442 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE] __ALIGN // 0x75,0x08, // Report Size 8 // 0x95,0x01, // Report Count 1 // 0x91,0x02, // Output (Variable) -// 0x09,0x6D, // Usage Sample Count +// 0x09,0x75, // Usage Ramp Start +// 0x09,0x76, // Usage Ramp End +// 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) +// 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) +// 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) +// 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) +// 0x75,0x10, // Report Size 16 +// 0x95,0x02, // Report Count 2 +// 0x91,0x02, // Output (Variable) +// 0xC0 , // End Collection +// +// +//// 0x09,0x68, // Usage Custom Force Data Report +//// 0xA1,0x02, // Collection Datalink +//// 0x85,HID_ID_CSTMREP+FFB_ID_OFFSET, // Report ID 7 +//// 0x09,0x22, // Usage Effect Block Index +//// 0x15,0x01, // Logical Minimum 1 +//// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +//// 0x35,0x01, // Physical Minimum 1 +//// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x01, // Report Count 1 +//// 0x91,0x02, // Output (Variable) +//// 0x09,0x6C, // Usage Custom Force Data Offset +//// 0x15,0x00, // Logical Minimum 0 +//// 0x26,0x10,0x27, // Logical Maximum 2710h (10000d) +//// 0x35,0x00, // Physical Minimum 0 +//// 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) +//// 0x75,0x10, // Report Size 10h (16d) +//// 0x95,0x01, // Report Count 1 +//// 0x91,0x02, // Output (Variable) +//// 0x09,0x69, // Usage Custom Force Data +//// 0x15,0x81, // Logical Minimum 81h (-127d) +//// 0x25,0x7F, // Logical Maximum 7Fh (127d) +//// 0x35,0x00, // Physical Minimum 0 +//// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x0C, // Report Count Ch (12d) +//// 0x92,0x02,0x01, // Output (Variable, Buffered) +//// 0xC0 , // End Collection +//// 0x09,0x66, // Usage Download Force Sample +//// 0xA1,0x02, // Collection Datalink +//// 0x85,HID_ID_SMPLREP+FFB_ID_OFFSET, // Report ID 8 +//// 0x05,0x01, // Usage Page Generic Desktop +//// 0x09,0x30, // Usage X +//// 0x09,0x31, // Usage Y +//// 0x15,0x81, // Logical Minimum 81h (-127d) +//// 0x25,0x7F, // Logical Maximum 7Fh (127d) +//// 0x35,0x00, // Physical Minimum 0 +//// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x02, // Report Count 2 +//// 0x91,0x02, // Output (Variable) +//// 0xC0 , // End Collection + HIDDESC_FFB_EFOPREP, +// 0x05,0x0F, // Usage Page Physical Interface +// 0x09,0x77, // Usage Effect Operation Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_EFOPREP+FFB_ID_OFFSET, // Report ID Ah (10d) +// 0x09,0x22, // Usage Effect Block Index +// 0x15,0x01, // Logical Minimum 1 +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0x09,0x78, // Usage Effect Operation +// 0xA1,0x02, // Collection Datalink +// 0x09,0x79, // Usage Op Effect Start +// 0x09,0x7A, // Usage Op Effect Start Solo +// 0x09,0x7B, // Usage Op Effect Stop +// 0x15,0x01, // Logical Minimum 1 +// 0x25,0x03, // Logical Maximum 3 +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x00, // Output +// 0xC0 , // End Collection +// 0x09,0x7C, // Usage Loop Count // 0x15,0x00, // Logical Minimum 0 // 0x26,0xFF,0x00, // Logical Maximum FFh (255d) // 0x35,0x00, // Physical Minimum 0 // 0x46,0xFF,0x00, // Physical Maximum FFh (255d) -// 0x75,0x08, // Report Size 8 -// 0x95,0x01, // Report Count 1 // 0x91,0x02, // Output (Variable) -// 0x09,0x51, // Usage Sample Period -// 0x66,0x03,0x10, // Unit 1003h (4099d) -// 0x55,0xFD, // Unit Exponent FDh (253d) +// 0xC0 , // End Collection + HIDDESC_FFB_BLOCKFREEREP, +// 0x09,0x90, // Usage PID Block Free Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_BLKFRREP+FFB_ID_OFFSET, // Report ID Bh (11d) +// 0x09,0x22, // Usage Effect Block Index +// 0x15,0x01, // Logical Minimum 1 +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0x91,0x02, // Output (Variable) +// 0xC0 , // End Collection + HIDDESC_FFB_DEVCTRLREP, +// 0x09,0x95, // Usage PID Device Control (0x96?) +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_CTRLREP+FFB_ID_OFFSET, // Report ID Ch (12d) +// 0x09,0x96, // Usage PID Device Control (0x96?) +// 0xA1,0x02, // Collection Datalink +// +// 0x09,0x97, // Usage DC Enable Actuators +// 0x09,0x98, // Usage DC Disable Actuators +// 0x09,0x99, // Usage DC Stop All Effects +// 0x09,0x9A, // Usage DC Device Reset +// 0x09,0x9B, // Usage DC Device Pause +// 0x09,0x9C, // Usage DC Device Continue +// +// +// +// 0x15,0x01, // Logical Minimum 1 +// 0x25,0x06, // Logical Maximum 6 +// 0x75,0x01, // Report Size 1 +// 0x95,0x08, // Report Count 8 +// 0x91,0x02, // Output +// +// 0xC0 , // End Collection +// 0xC0 , // End Collection +// 0x09,0x7D, // Usage Device Gain Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_GAINREP+FFB_ID_OFFSET, // Report ID Dh (13d) +// 0x09,0x7E, // Usage Device Gain // 0x15,0x00, // Logical Minimum 0 -// 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) +// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) // 0x35,0x00, // Physical Minimum 0 -// 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) -// 0x75,0x10, // Report Size 10h (16d) +// 0x46,0x10,0x27, // Physical Maximum 2710h (10000d) +// 0x75,0x08, // Report Size 8 // 0x95,0x01, // Report Count 1 // 0x91,0x02, // Output (Variable) -// 0x55,0x00, // Unit Exponent 0 -// 0x66,0x00,0x00, // Unit 0 +// 0xC0 , // End Collection + +//// 0x09,0x6B, // Usage Set Custom Force Report +//// 0xA1,0x02, // Collection Datalink +//// 0x85,HID_ID_SETCREP+FFB_ID_OFFSET, // Report ID Eh (14d) +//// 0x09,0x22, // Usage Effect Block Index +//// 0x15,0x01, // Logical Minimum 1 +//// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +//// 0x35,0x01, // Physical Minimum 1 +//// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x01, // Report Count 1 +//// 0x91,0x02, // Output (Variable) +//// 0x09,0x6D, // Usage Sample Count +//// 0x15,0x00, // Logical Minimum 0 +//// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) +//// 0x35,0x00, // Physical Minimum 0 +//// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) +//// 0x75,0x08, // Report Size 8 +//// 0x95,0x01, // Report Count 1 +//// 0x91,0x02, // Output (Variable) +//// 0x09,0x51, // Usage Sample Period +//// 0x66,0x03,0x10, // Unit 1003h (4099d) +//// 0x55,0xFD, // Unit Exponent FDh (253d) +//// 0x15,0x00, // Logical Minimum 0 +//// 0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d) +//// 0x35,0x00, // Physical Minimum 0 +//// 0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d) +//// 0x75,0x10, // Report Size 10h (16d) +//// 0x95,0x01, // Report Count 1 +//// 0x91,0x02, // Output (Variable) +//// 0x55,0x00, // Unit Exponent 0 +//// 0x66,0x00,0x00, // Unit 0 +//// 0xC0 , // End Collection + HIDDESC_FFB_NEWEFREP, +// 0x09,0xAB, // Usage Create New Effect Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_NEWEFREP+FFB_ID_OFFSET, // Report ID 1 +// 0x09,0x25, // Usage Effect Type +// 0xA1,0x02, // Collection Datalink +// 0x09, HID_USAGE_CONST, // Usage ET Constant Force +// 0x09, HID_USAGE_RAMP, // Usage ET Ramp +// 0x09, HID_USAGE_SQUR, // Usage ET Square +// 0x09, HID_USAGE_SINE, // Usage ET Sine +// 0x09, HID_USAGE_TRNG, // Usage ET Triangle +// 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up +// 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down +// 0x09, HID_USAGE_SPRNG, // Usage ET Spring +// 0x09, HID_USAGE_DMPR, // Usage ET Damper +// 0x09, HID_USAGE_INRT, // Usage ET Inertia +// 0x09, HID_USAGE_FRIC, // Usage ET Friction +//// 0x09, 0x28, // Usage ET Custom Force Data +// 0x25,0x0B, // Logical Maximum Ch (11d) +// 0x15,0x01, // Logical Minimum 1 +// 0x35,0x01, // Physical Minimum 1 +// 0x45,0x0B, // Physical Maximum Ch (11d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0xB1,0x00, // Feature +// 0xC0 , // End Collection +// 0x05,0x01, // Usage Page Generic Desktop +// 0x09,0x3B, // Usage Reserved (Byte count) +// 0x15,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x01, // Logical Maximum 1FFh (511d) +// 0x35,0x00, // Physical Minimum 0 +// 0x46,0xFF,0x01, // Physical Maximum 1FFh (511d) +// 0x75,0x0A, // Report Size Ah (10d) +// 0x95,0x01, // Report Count 1 +// 0xB1,0x02, // Feature (Variable) +// 0x75,0x06, // Report Size 6 +// 0xB1,0x01, // Feature (Constant) // 0xC0 , // End Collection - 0x09,0xAB, // Usage Create New Effect Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_NEWEFREP+FFB_ID_OFFSET, // Report ID 1 - 0x09,0x25, // Usage Effect Type +// 0x05,0x0F, // Usage Page Physical Interface + HIDDESC_FFB_BLOCKLOADREP, +// 0x09,0x89, // Usage Block Load Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_BLKLDREP+FFB_ID_OFFSET, // Report ID 0x12 +// 0x09,0x22, // Usage Effect Block Index +// 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) +// 0x15,0x01, // Logical Minimum 1 +// 0x35,0x01, // Physical Minimum 1 +// 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0xB1,0x02, // Feature (Variable) +// 0x09,0x8B, // Usage Block Load Status +// 0xA1,0x02, // Collection Datalink +// 0x09,0x8C, // Usage Block Load Success +// 0x09,0x8D, // Usage Block Load Full +// 0x09,0x8E, // Usage Block Load Error +// 0x15,0x01, // Logical Minimum 1 +// 0x25,0x03, // Logical Maximum 3 +// 0x35,0x01, // Physical Minimum 1 +// 0x45,0x03, // Physical Maximum 3 +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0xB1,0x00, // Feature +// 0xC0 , // End Collection +// 0x09,0xAC, // Usage Pool available +// 0x15,0x00, // Logical Minimum 0 +// 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) +// 0x35,0x00, // Physical Minimum 0 +// 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) +// 0x75,0x10, // Report Size 10h (16d) +// 0x95,0x01, // Report Count 1 +// 0xB1,0x00, // Feature +// 0xC0 , // End Collection + HIDDESC_FFB_POOLREP, +// 0x09,0x7F, // Usage PID Pool Report +// 0xA1,0x02, // Collection Datalink +// 0x85,HID_ID_POOLREP+FFB_ID_OFFSET, // Report ID 0x13 +// 0x09,0x80, // Usage RAM Pool size +// 0x75,0x10, // Report Size 10h (16d) +// 0x95,0x01, // Report Count 1 +// 0x15,0x00, // Logical Minimum 0 +// 0x35,0x00, // Physical Minimum 0 +// 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) +// 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) +// 0xB1,0x02, // Feature (Variable) +// 0x09,0x83, // Usage Simultaneous Effects Max +// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) +// 0x46,0xFF,0x00, // Physical Maximum FFh (255d) +// 0x75,0x08, // Report Size 8 +// 0x95,0x01, // Report Count 1 +// 0xB1,0x02, // Feature (Variable) +// 0x09,0xA9, // Usage Device Managed Pool +// 0x09,0xAA, // Usage Shared Parameter Blocks +// 0x75,0x01, // Report Size 1 +// 0x95,0x02, // Report Count 2 +// 0x15,0x00, // Logical Minimum 0 +// 0x25,0x01, // Logical Maximum 1 +// 0x35,0x00, // Physical Minimum 0 +// 0x45,0x01, // Physical Maximum 1 +// 0xB1,0x02, // Feature (Variable) +// 0x75,0x06, // Report Size 6 +// 0x95,0x01, // Report Count 1 +// 0xB1,0x03, // Feature (Constant, Variable) +// 0xC0, // End Collection + + 0xC0 /* END_COLLECTION */ +}; +#endif + +#ifdef AXIS2_FFB_HID_DESC_32B +__ALIGN_BEGIN const uint8_t hid_2ffb_desc_32b[USB_HID_2FFB_REPORT_DESC_32B_SIZE] __ALIGN_END = +{ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_32B, + + HIDDESC_CTRL_REPORTS, // HID command report support + + HIDDESC_FFB_STATEREP, + /* + Output + Collection Datalink: + Usage Set Effect Report + + ID:1 + Effect Block Index: 8bit + + subcollection Effect Type + 12 effect types, 8bit each + + */ + HIDDESC_FFB_SETEFREP, + +// Len 108 bytes following + 0x09,0x55, // Usage Axes Enable TODO multi axis + 0xA1,0x02, // Collection Datalink + 0x05,0x01, // Usage Page Generic Desktop + 0x09,0x30, // Usage X + 0x09,0x31, // Usage Y + 0x15,0x00, // Logical Minimum 0 + 0x25,0x01, // Logical Maximum 1 + 0x75,0x01, // Report Size 1 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x05,0x0F, // Usage Page Physical Interface + 0x09,0x56, // Usage Direction Enable + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x95,0x05, // Report Count 5 + 0x91,0x03, // Output (Constant, Variable) + + 0x09,0x57, // Usage Direction 0xA1,0x02, // Collection Datalink - 0x09, HID_USAGE_CONST, // Usage ET Constant Force - 0x09, HID_USAGE_RAMP, // Usage ET Ramp - 0x09, HID_USAGE_SQUR, // Usage ET Square - 0x09, HID_USAGE_SINE, // Usage ET Sine - 0x09, HID_USAGE_TRNG, // Usage ET Triangle - 0x09, HID_USAGE_STUP, // Usage ET Sawtooth Up - 0x09, HID_USAGE_STDN, // Usage ET Sawtooth Down - 0x09, HID_USAGE_SPRNG, // Usage ET Spring - 0x09, HID_USAGE_DMPR, // Usage ET Damper - 0x09, HID_USAGE_INRT, // Usage ET Inertia - 0x09, HID_USAGE_FRIC, // Usage ET Friction -// 0x09, 0x28, // Usage ET Custom Force Data - 0x25,0x0B, // Logical Maximum Ch (11d) + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 + 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x66,0x14,0x00, // Unit 14h (20d) Angular position + 0x55,0xFE, // Unit Exponent FEh (254d) +// 0x15,0x00, // Logical Minimum 0 +// 0x26,0xFF,0x00, // Logical Maximum FFh (255d) + 0x15,0x00, // Logical Minimum 0 + 0x27,0xA0,0x8C,0x00,0x00, // Logical Maximum 8CA0h (36000d) + 0x35,0x00, // Physical Minimum 0 + 0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d) + 0x66,0x00,0x00, // Unit 0 + 0x75,0x10, // Report Size 16 +// 0x95,0x01, // Report Count 1 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x55,0x00, // Unit Exponent 0 + 0x66,0x00,0x00, // Unit 0 + 0xC0, // End Collection + + 0x05, 0x0F, // USAGE_PAGE (Physical Interface) + 0x09, 0x58, // USAGE (Type Specific Block Offset) + 0xA1, 0x02, // COLLECTION (Logical) + 0x0B, 0x01, 0x00, 0x0A, 0x00, //USAGE (Ordinals:Instance 1 + 0x0B, 0x02, 0x00, 0x0A, 0x00, //USAGE (Ordinals:Instance 2) + 0x26, 0xFD, 0x7F, // LOGICAL_MAXIMUM (32765) ; 32K RAM or ROM max. + 0x75, 0x10, // REPORT_SIZE (16) + 0x95, 0x02, // REPORT_COUNT (2) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0xC0, // END_COLLECTION + 0xC0, // END_COLLECTION + + HIDDESC_FFB_SETENVREP, + +// HIDDESC_FFB_SETCONDREP, + + // Condition report depends on ffb axis count + // 125 bytes following + 0x09,0x5F, // Usage Set Condition Report + 0xA1,0x02, // Collection Datalink + 0x85,HID_ID_CONDREP+FFB_ID_OFFSET, // Report ID 3 + 0x09,0x22, // Usage Effect Block Index 0x15,0x01, // Logical Minimum 1 + 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) 0x35,0x01, // Physical Minimum 1 - 0x45,0x0B, // Physical Maximum Ch (11d) + 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) 0x75,0x08, // Report Size 8 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature - 0xC0 , // End Collection - 0x05,0x01, // Usage Page Generic Desktop - 0x09,0x3B, // Usage Reserved (Byte count) - 0x15,0x00, // Logical Minimum 0 - 0x26,0xFF,0x01, // Logical Maximum 1FFh (511d) - 0x35,0x00, // Physical Minimum 0 - 0x46,0xFF,0x01, // Physical Maximum 1FFh (511d) - 0x75,0x0A, // Report Size Ah (10d) - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x75,0x06, // Report Size 6 - 0xB1,0x01, // Feature (Constant) - 0xC0 , // End Collection - 0x05,0x0F, // Usage Page Physical Interface - 0x09,0x89, // Usage Block Load Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_BLKLDREP+FFB_ID_OFFSET, // Report ID 0x12 - 0x09,0x22, // Usage Effect Block Index - 0x25,MAX_EFFECTS, // Logical Maximum 28h (40d) - 0x15,0x01, // Logical Minimum 1 - 0x35,0x01, // Physical Minimum 1 - 0x45,MAX_EFFECTS, // Physical Maximum 28h (40d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x09,0x8B, // Usage Block Load Status - 0xA1,0x02, // Collection Datalink - 0x09,0x8C, // Usage Block Load Success - 0x09,0x8D, // Usage Block Load Full - 0x09,0x8E, // Usage Block Load Error - 0x15,0x01, // Logical Minimum 1 + 0x91,0x02, // Output (Variable) + 0x09,0x23, // Usage Parameter Block Offset + 0x15,0x00, // Logical Minimum 0 0x25,0x03, // Logical Maximum 3 - 0x35,0x01, // Physical Minimum 1 + 0x35,0x00, // Physical Minimum 0 0x45,0x03, // Physical Maximum 3 - 0x75,0x08, // Report Size 8 + 0x75,0x04, // Report Size 4 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature - 0xC0 , // End Collection - 0x09,0xAC, // Usage Pool available - 0x15,0x00, // Logical Minimum 0 - 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) - 0x35,0x00, // Physical Minimum 0 - 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0xB1,0x00, // Feature + 0x91,0x02, // Output (Variable) + 0x09,0x58, // Usage Type Specific Block Off... + 0xA1,0x02, // Collection Datalink + 0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1 + 0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2 + 0x75,0x02, // Report Size 2 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0xC0 , // End Collection + 0x16,0x00, 0x80, // Logical Minimum 7FFFh (-32767d) + 0x26,0xff, 0x7f, // Logical Maximum 7FFFh (32767d) + 0x36,0x00, 0x80, // Physical Minimum 7FFFh (-32767d) + 0x46,0xff, 0x7f, // Physical Maximum 7FFFh (32767d) + + 0x09,0x60, // Usage CP Offset + 0x75,0x10, // Report Size 16 + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) + 0x36,0x00, 0x80, // Physical Minimum (-32768) + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x09,0x61, // Usage Positive Coefficient + 0x09,0x62, // Usage Negative Coefficient + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x16,0x00,0x00, // Logical Minimum 0 + 0x26,0xff, 0x7f, // Logical Maximum (32767) + 0x36,0x00,0x00, // Physical Minimum 0 + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x09,0x63, // Usage Positive Saturation + 0x09,0x64, // Usage Negative Saturation + 0x75,0x10, // Report Size 16 + 0x95,0x02, // Report Count 2 + 0x91,0x02, // Output (Variable) + 0x09,0x65, // Usage Dead Band + 0x46,0xff, 0x7f, // Physical Maximum (32767) + 0x95,0x01, // Report Count 1 + 0x91,0x02, // Output (Variable) 0xC0 , // End Collection - 0x09,0x7F, // Usage PID Pool Report - 0xA1,0x02, // Collection Datalink - 0x85,HID_ID_POOLREP+FFB_ID_OFFSET, // Report ID 0x13 - 0x09,0x80, // Usage RAM Pool size - 0x75,0x10, // Report Size 10h (16d) - 0x95,0x01, // Report Count 1 - 0x15,0x00, // Logical Minimum 0 - 0x35,0x00, // Physical Minimum 0 - 0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d) - 0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d) - 0xB1,0x02, // Feature (Variable) - 0x09,0x83, // Usage Simultaneous Effects Max - 0x26,0xFF,0x00, // Logical Maximum FFh (255d) - 0x46,0xFF,0x00, // Physical Maximum FFh (255d) - 0x75,0x08, // Report Size 8 - 0x95,0x01, // Report Count 1 - 0xB1,0x02, // Feature (Variable) - 0x09,0xA9, // Usage Device Managed Pool - 0x09,0xAA, // Usage Shared Parameter Blocks - 0x75,0x01, // Report Size 1 - 0x95,0x02, // Report Count 2 - 0x15,0x00, // Logical Minimum 0 - 0x25,0x01, // Logical Maximum 1 - 0x35,0x00, // Physical Minimum 0 - 0x45,0x01, // Physical Maximum 1 - 0xB1,0x02, // Feature (Variable) - 0x75,0x06, // Report Size 6 - 0x95,0x01, // Report Count 1 - 0xB1,0x03, // Feature (Constant, Variable) - 0xC0, // End Collection + HIDDESC_FFB_SETPERIODICREP, + + HIDDESC_FFB_SETCFREP, + + HIDDESC_FFB_SETRAMPREP, + + HIDDESC_FFB_EFOPREP, + + HIDDESC_FFB_BLOCKFREEREP, + + HIDDESC_FFB_DEVCTRLREP, + + HIDDESC_FFB_NEWEFREP, + + HIDDESC_FFB_BLOCKLOADREP, + + HIDDESC_FFB_POOLREP, 0xC0 /* END_COLLECTION */ }; - #endif + + diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c b/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c index 474fd7bc5..bc56f3781 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c @@ -16,111 +16,31 @@ */ __ALIGN_BEGIN const uint8_t hid_gamepad_desc[USB_HID_GAMEPAD_REPORT_DESC_SIZE] __ALIGN_END = { - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x04, // USAGE (Joystick) - 0xa1, 0x01, // COLLECTION (Application) - 0xa1, 0x00, // COLLECTION (Physical) - 0x85, 0x01, // REPORT_ID (1) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x40, // USAGE_MAXIMUM (Button 64) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x40, // REPORT_COUNT (64) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, HID_USAGE_X, // USAGE (X) - 0x09, HID_USAGE_Y, // USAGE (Y) - 0x09, HID_USAGE_Z, // USAGE (Z) - 0x09, HID_USAGE_RX, // USAGE (Rx) - 0x09, HID_USAGE_RY, // USAGE (Ry) - 0x09, HID_USAGE_RZ, // USAGE (Rz) - 0x09, HID_USAGE_SL1, // USAGE (Dial) - 0x09, HID_USAGE_SL0, // USAGE (Slider) - 0x16, 0x01, 0x80, // LOGICAL_MINIMUM (-32767) - 0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x08, // REPORT_COUNT (8) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0xc0, // END_COLLECTION + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_16B, + HIDDESC_CTRL_REPORTS, // HID command report support - // Control reports - 0x06, 0x00, 0xFF, // USAGE_PAGE (Vendor) - 0x09, 0x00, // USAGE (Vendor) - 0xA1, 0x01, // Collection (Application) - - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - - 0x85,HID_ID_HIDCMD, // Report ID - 0x09, 0x01, // USAGE (Vendor) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0x04, 0x00, // Logical Maximum 4 - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x02, // USAGE (Vendor) class address - 0x75, 0x10, // REPORT_SIZE (16) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x03, // USAGE (Vendor) class instance - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x04, // USAGE (Vendor) cmd - 0x75, 0x20, // REPORT_SIZE (32) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x05, // USAGE (Vendor) - 0x75, 0x40, // REPORT_SIZE (64) value - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - - 0x09, 0x06, // USAGE (Vendor) address - 0x75, 0x40, // REPORT_SIZE (64) - 0x95, 0x01, // REPORT_COUNT (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) + 0xC0 /* END_COLLECTION */ +}; +#endif +#ifdef FFB_HID_DESC_GAMEPAD_32B - 0xc0, // END_COLLECTION +/** + * USB HID descriptor containing a gamepad definition and the vendor defined reports but no PID FFB + */ +__ALIGN_BEGIN const uint8_t hid_gamepad_desc_32b[USB_HID_GAMEPAD_REPORT_DESC_32B_SIZE] __ALIGN_END = +{ + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_32B, + HIDDESC_CTRL_REPORTS, // HID command report support 0xC0 /* END_COLLECTION */ }; diff --git a/Firmware/Targets/F407VG/.cproject b/Firmware/Targets/F407VG/.cproject index f645a46aa..81af2569f 100644 --- a/Firmware/Targets/F407VG/.cproject +++ b/Firmware/Targets/F407VG/.cproject @@ -25,7 +25,7 @@ + @@ -241,7 +251,7 @@ + diff --git a/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs b/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs index be0603234..5b3603498 100644 --- a/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs +++ b/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs @@ -1,4 +1,4 @@ 2F62501ED4689FB349E356AB974DBE57=E2380525C25FCA23F64F38676B957D9B -8DF89ED150041C4CBC7CB9A9CAA90856=E2380525C25FCA23F64F38676B957D9B -DC22A860405A8BF2F2C095E5B6529F12=609A53BFF0492F10C829ECAB4963903C +8DF89ED150041C4CBC7CB9A9CAA90856=2E846C232C86F8091E1D1C41786923FD +DC22A860405A8BF2F2C095E5B6529F12=948244613B091C43745821314F11043C eclipse.preferences.version=1 diff --git a/Firmware/Targets/F407VG/Core/Inc/FreeRTOSConfig.h b/Firmware/Targets/F407VG/Core/Inc/FreeRTOSConfig.h index df33746ef..3d718b1e3 100644 --- a/Firmware/Targets/F407VG/Core/Inc/FreeRTOSConfig.h +++ b/Firmware/Targets/F407VG/Core/Inc/FreeRTOSConfig.h @@ -51,6 +51,10 @@ #if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) #include extern uint32_t SystemCoreClock; +/* USER CODE BEGIN 0 */ + extern void configureTimerForRunTimeStats(void); + extern unsigned long getRunTimeCounterValue(void); +/* USER CODE END 0 */ #endif #ifndef CMSIS_device_header #define CMSIS_device_header "stm32f4xx.h" @@ -70,7 +74,9 @@ #define configMINIMAL_STACK_SIZE ((uint16_t)64) #define configTOTAL_HEAP_SIZE ((size_t)0xffff) #define configMAX_TASK_NAME_LEN ( 16 ) +#define configGENERATE_RUN_TIME_STATS 1 #define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 #define configUSE_16_BIT_TICKS 0 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 8 @@ -169,6 +175,12 @@ standard names. */ #define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0 +/* USER CODE BEGIN 2 */ +/* Definitions needed when configGENERATE_RUN_TIME_STATS is on */ +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS configureTimerForRunTimeStats +#define portGET_RUN_TIME_COUNTER_VALUE getRunTimeCounterValue +/* USER CODE END 2 */ + /* USER CODE BEGIN Defines */ #define CPP_FREERTOS_NO_EXCEPTIONS // For freertos cpp addons #define configAPPLICATION_ALLOCATED_HEAP 1 // ucHeap allocated in freertos.c diff --git a/Firmware/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index 860f27682..6897827dc 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -40,6 +40,7 @@ // Extra features #define LOCALBUTTONS #define SPIBUTTONS +#define SPIBUTTONS2 #define SHIFTERBUTTONS #define PCF8574BUTTONS // Requires I2C #define ANALOGAXES @@ -69,6 +70,8 @@ #define TIM_USER htim9 // Timer with full core clock speed available for the mainclass #define TIM_TMC htim6 // Timer running at half clock speed #define TIM_TMC_BCLK SystemCoreClock / 2 +#define TIM_TMC_ARR 250 // 4khz +#define TIM_FFB htim13 extern UART_HandleTypeDef huart1; #define UART_PORT_EXT huart1 // main uart port diff --git a/Firmware/Targets/F407VG/Core/Src/freertos.c b/Firmware/Targets/F407VG/Core/Src/freertos.c index 6eadca3d4..6f24b2c27 100644 --- a/Firmware/Targets/F407VG/Core/Src/freertos.c +++ b/Firmware/Targets/F407VG/Core/Src/freertos.c @@ -55,10 +55,25 @@ uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; // rtos heap // __attribute__((section /* USER CODE END FunctionPrototypes */ /* Hook prototypes */ +void configureTimerForRunTimeStats(void); +unsigned long getRunTimeCounterValue(void); void vApplicationIdleHook(void); void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName); void vApplicationMallocFailedHook(void); +/* USER CODE BEGIN 1 */ +/* Functions needed when configGENERATE_RUN_TIME_STATS is on */ +__weak void configureTimerForRunTimeStats(void) +{ + +} + +__weak unsigned long getRunTimeCounterValue(void) +{ +return 0; +} +/* USER CODE END 1 */ + /* USER CODE BEGIN 2 */ void vApplicationIdleHook( void ) { diff --git a/Firmware/Targets/F407VG/Core/Src/main.c b/Firmware/Targets/F407VG/Core/Src/main.c index b2316a867..716996bd8 100644 --- a/Firmware/Targets/F407VG/Core/Src/main.c +++ b/Firmware/Targets/F407VG/Core/Src/main.c @@ -881,7 +881,7 @@ static void MX_TIM3_Init(void) sConfig.IC1Polarity = TIM_ICPOLARITY_FALLING; sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler = TIM_ICPSC_DIV1; - sConfig.IC1Filter = 0; + sConfig.IC1Filter = 5; sConfig.IC2Polarity = TIM_ICPOLARITY_FALLING; sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV1; @@ -1200,7 +1200,7 @@ static void MX_TIM13_Init(void) /* USER CODE END TIM13_Init 1 */ htim13.Instance = TIM13; - htim13.Init.Prescaler = 167; + htim13.Init.Prescaler = 84-1; htim13.Init.CounterMode = TIM_COUNTERMODE_UP; htim13.Init.Period = 100; htim13.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; @@ -1374,15 +1374,18 @@ static void MX_GPIO_Init(void) HAL_GPIO_WritePin(SPI1_SS1_GPIO_Port, SPI1_SS1_Pin, GPIO_PIN_SET); /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOB, SPI1_SS2_Pin|SPI1_SS3_Pin|SPI2_NSS_Pin, GPIO_PIN_RESET); + // SPI CS pins initialized HIGH (inactive) - important for 74HC165 shift registers + HAL_GPIO_WritePin(SPI2_NSS_GPIO_Port, SPI2_NSS_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOB, SPI1_SS2_Pin|SPI1_SS3_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOE, DRV_ENABLE_Pin|DRV_BRAKE_Pin|DRV_GP1_Pin|LED_CLIP_Pin |LED_ERR_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ - HAL_GPIO_WritePin(GPIOD, SPI2_SS2_Pin|SPI2_SS3_Pin|SPI3_SS2_Pin|SPI3_SS3_Pin - |CAN_S_Pin|GP1_Pin|LED_SYS_Pin, GPIO_PIN_RESET); + // SPI2 CS pins initialized HIGH (inactive) - required for correct SPI_Buttons operation + HAL_GPIO_WritePin(GPIOD, SPI2_SS2_Pin|SPI2_SS3_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOD, SPI3_SS2_Pin|SPI3_SS3_Pin|CAN_S_Pin|GP1_Pin|LED_SYS_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(SPI3_SS1_GPIO_Port, SPI3_SS1_Pin, GPIO_PIN_RESET); diff --git a/Firmware/Targets/F407VG/Core/ThreadSafe/newlib_lock_glue.c b/Firmware/Targets/F407VG/Core/ThreadSafe/newlib_lock_glue.c index 8d956e401..4ed4b9ba0 100644 --- a/Firmware/Targets/F407VG/Core/ThreadSafe/newlib_lock_glue.c +++ b/Firmware/Targets/F407VG/Core/ThreadSafe/newlib_lock_glue.c @@ -19,7 +19,7 @@ ****************************************************************************** * @attention * - * Copyright (c) 2023 STMicroelectronics. + * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file diff --git a/Firmware/Targets/F407VG/Core/ThreadSafe/stm32_lock.h b/Firmware/Targets/F407VG/Core/ThreadSafe/stm32_lock.h index 372c5ded9..b661efdda 100644 --- a/Firmware/Targets/F407VG/Core/ThreadSafe/stm32_lock.h +++ b/Firmware/Targets/F407VG/Core/ThreadSafe/stm32_lock.h @@ -48,7 +48,7 @@ ****************************************************************************** * @attention * - * Copyright (c) 2023 STMicroelectronics. + * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file diff --git a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc index d97d47989..59e0251c3 100644 --- a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc +++ b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc @@ -176,7 +176,7 @@ FREERTOS.IPParameters=Tasks01,configENABLE_FPU,configTOTAL_HEAP_SIZE,FootprintOK FREERTOS.Tasks01=defaultTask,16,512,StartDefaultTask,As weak,NULL,Dynamic,NULL,NULL FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2 FREERTOS.configENABLE_FPU=1 -FREERTOS.configGENERATE_RUN_TIME_STATS=0 +FREERTOS.configGENERATE_RUN_TIME_STATS=1 FREERTOS.configMINIMAL_STACK_SIZE=64 FREERTOS.configRECORD_STACK_HIGH_ADDRESS=1 FREERTOS.configTIMER_TASK_STACK_DEPTH=128 @@ -184,7 +184,7 @@ FREERTOS.configTOTAL_HEAP_SIZE=0xffff FREERTOS.configUSE_IDLE_HOOK=1 FREERTOS.configUSE_MALLOC_FAILED_HOOK=1 FREERTOS.configUSE_NEWLIB_REENTRANT=1 -FREERTOS.configUSE_STATS_FORMATTING_FUNCTIONS=0 +FREERTOS.configUSE_STATS_FORMATTING_FUNCTIONS=1 FREERTOS.configUSE_TRACE_FACILITY=1 File.Version=6 GPIO.groupedBy=Show All @@ -721,6 +721,7 @@ PH1-OSC_OUT.Signal=RCC_OSC_OUT PinOutPanel.RotationAngle=-180 ProjectManager.AskForMigrate=true ProjectManager.BackupPrevious=false +ProjectManager.CompilerLinker=GCC ProjectManager.CompilerOptimize=6 ProjectManager.ComputerToolchain=false ProjectManager.CoupleFile=false @@ -733,7 +734,7 @@ ProjectManager.FreePins=false ProjectManager.HalAssertFull=false ProjectManager.HeapSize=0x400 ProjectManager.KeepUserCode=true -ProjectManager.LastFirmware=false +ProjectManager.LastFirmware=true ProjectManager.LibraryCopy=1 ProjectManager.MainLocation=Core/Src ProjectManager.MultiThreaded=true @@ -873,7 +874,7 @@ TIM1.TIM_MasterOutputTrigger=TIM_TRGO_RESET TIM1.TIM_MasterSlaveMode=TIM_MASTERSLAVEMODE_DISABLE TIM13.IPParameters=Prescaler,Period TIM13.Period=100 -TIM13.Prescaler=167 +TIM13.Prescaler=84-1 TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1 TIM2.CounterMode=TIM_COUNTERMODE_UP @@ -886,7 +887,7 @@ TIM3.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE TIM3.ClockDivision=TIM_CLOCKDIVISION_DIV1 TIM3.CounterMode=TIM_COUNTERMODE_CENTERALIGNED1 TIM3.EncoderMode=TIM_ENCODERMODE_TI12 -TIM3.IC1Filter=0 +TIM3.IC1Filter=5 TIM3.IC1Polarity=TIM_ICPOLARITY_FALLING TIM3.IC1Prescaler=TIM_ICPSC_DIV1 TIM3.IC1Selection=TIM_ICSELECTION_DIRECTTI diff --git a/Firmware/Targets/F407VG_DISCO/.cproject b/Firmware/Targets/F407VG_DISCO/.cproject index 8edba0284..d2e451124 100644 --- a/Firmware/Targets/F407VG_DISCO/.cproject +++ b/Firmware/Targets/F407VG_DISCO/.cproject @@ -25,7 +25,7 @@ + @@ -243,7 +253,7 @@ + diff --git a/Firmware/Targets/F407VG_DISCO/Core/Src/main.c b/Firmware/Targets/F407VG_DISCO/Core/Src/main.c index df400e81f..9b673777d 100644 --- a/Firmware/Targets/F407VG_DISCO/Core/Src/main.c +++ b/Firmware/Targets/F407VG_DISCO/Core/Src/main.c @@ -771,7 +771,7 @@ static void MX_TIM2_Init(void) sConfig.IC1Polarity = TIM_ICPOLARITY_RISING; sConfig.IC1Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC1Prescaler = TIM_ICPSC_DIV1; - sConfig.IC1Filter = 0; + sConfig.IC1Filter = 5; sConfig.IC2Polarity = TIM_ICPOLARITY_RISING; sConfig.IC2Selection = TIM_ICSELECTION_DIRECTTI; sConfig.IC2Prescaler = TIM_ICPSC_DIV1; diff --git a/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/newlib_lock_glue.c b/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/newlib_lock_glue.c index 8d956e401..4ed4b9ba0 100644 --- a/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/newlib_lock_glue.c +++ b/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/newlib_lock_glue.c @@ -19,7 +19,7 @@ ****************************************************************************** * @attention * - * Copyright (c) 2023 STMicroelectronics. + * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file diff --git a/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/stm32_lock.h b/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/stm32_lock.h index 372c5ded9..b661efdda 100644 --- a/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/stm32_lock.h +++ b/Firmware/Targets/F407VG_DISCO/Core/ThreadSafe/stm32_lock.h @@ -48,7 +48,7 @@ ****************************************************************************** * @attention * - * Copyright (c) 2023 STMicroelectronics. + * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file diff --git a/Firmware/Targets/F407VG_DISCO/OpenFFBoard_F407VG_DISCO.ioc b/Firmware/Targets/F407VG_DISCO/OpenFFBoard_F407VG_DISCO.ioc index e34994654..b2a72a03e 100644 --- a/Firmware/Targets/F407VG_DISCO/OpenFFBoard_F407VG_DISCO.ioc +++ b/Firmware/Targets/F407VG_DISCO/OpenFFBoard_F407VG_DISCO.ioc @@ -668,6 +668,7 @@ PH1-OSC_OUT.Signal=RCC_OSC_OUT PinOutPanel.RotationAngle=180 ProjectManager.AskForMigrate=true ProjectManager.BackupPrevious=false +ProjectManager.CompilerLinker=GCC ProjectManager.CompilerOptimize=6 ProjectManager.ComputerToolchain=false ProjectManager.CoupleFile=false @@ -680,7 +681,7 @@ ProjectManager.FreePins=false ProjectManager.HalAssertFull=false ProjectManager.HeapSize=0x400 ProjectManager.KeepUserCode=true -ProjectManager.LastFirmware=false +ProjectManager.LastFirmware=true ProjectManager.LibraryCopy=1 ProjectManager.MainLocation=Core/Src ProjectManager.MultiThreaded=true @@ -813,8 +814,9 @@ TIM2.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE TIM2.ClockDivision=TIM_CLOCKDIVISION_DIV1 TIM2.CounterMode=TIM_COUNTERMODE_UP TIM2.EncoderMode=TIM_ENCODERMODE_TI12 +TIM2.IC1Filter=5 TIM2.IC1Prescaler=TIM_ICPSC_DIV1 -TIM2.IPParameters=Prescaler,CounterMode,Period,ClockDivision,AutoReloadPreload,TIM_MasterSlaveMode,TIM_MasterOutputTrigger,EncoderMode,IC1Prescaler +TIM2.IPParameters=Prescaler,CounterMode,Period,ClockDivision,AutoReloadPreload,TIM_MasterSlaveMode,TIM_MasterOutputTrigger,EncoderMode,IC1Prescaler,IC1Filter TIM2.Period=0xffffffff TIM2.Prescaler=0 TIM2.TIM_MasterOutputTrigger=TIM_TRGO_RESET diff --git a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md new file mode 100644 index 000000000..9a5660b3c --- /dev/null +++ b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md @@ -0,0 +1,467 @@ +# Logitech G27 Complete Wiring Guide for OpenFFBoard STM32F407VET6 + +# Guia Completo de Ligações do Logitech G27 para OpenFFBoard STM32F407VET6 + +--- + +## ⚡ Required: PA9 Pull-up Resistor / Resistor Pull-up no PA9 + +> **EN:** On the generic **STM32F407VET6 board**, a **10kΩ pull-up resistor must be soldered between PA9 and 5V** for the main firmware (F407VG) to boot correctly. Without this resistor, only the F407VG_DISCO firmware works. This is required due to VBUS sensing differences. +> +> **PT:** Na placa genérica **STM32F407VET6**, um **resistor pull-up de 10kΩ deve ser soldado entre PA9 e 5V** para o firmware principal (F407VG) iniciar corretamente. Sem esse resistor, apenas o firmware F407VG_DISCO funciona. Isso é necessário devido a diferenças na detecção VBUS. + +| Connection | Description | +|------------|-------------| +| **PA9** ↔ **5V** | 10kΩ resistor (pull-up for VBUS sensing) | + +--- + +## 📋 Table of Contents / Índice + +1. [Overview / Visão Geral](#overview--visão-geral) +2. [⚡ PA9 Pull-up Resistor (Required)](#-required-pa9-pull-up-resistor--resistor-pull-up-no-pa9) +3. [Motor Connection / Conexão do Motor](#motor-connection--conexão-do-motor) +4. [Encoder Connection / Conexão do Encoder](#encoder-connection--conexão-do-encoder) +5. [Pedals Connection / Conexão dos Pedais](#pedals-connection--conexão-dos-pedais) +6. [Shifter Connection / Conexão do Câmbio](#shifter-connection--conexão-do-câmbio) +7. [Wheel Rim Buttons / Botões do Aro](#wheel-rim-buttons--botões-do-aro) +8. [Complete Pinout Summary / Resumo Completo da Pinagem](#complete-pinout-summary--resumo-completo-da-pinagem) + +--- + +## Overview / Visão Geral + +### EN - English + +This guide documents all connections made to integrate a **Logitech G27 Racing Wheel** with a generic **[STM32F407VET6 board](http://pt.aliexpress.com/item/1005006882009420.html)** running OpenFFBoard firmware. The G27 consists of: + +- **Wheel Base**: DC motor with optical encoder +- **Shifter**: H-pattern with analog axes + 16 SPI buttons (2x 74HC165) +- **Wheel Rim**: 8 buttons (1x 74HC165) + LEDs (1x HC595) +- **Pedals**: 3 analog axes (throttle, brake, clutch) + +### PT - Português + +Este guia documenta todas as conexões feitas para integrar um **Volante Logitech G27** com uma placa genérica **[STM32F407VET6](http://pt.aliexpress.com/item/1005006882009420.html)** rodando firmware OpenFFBoard. O G27 consiste em: + +- **Base do Volante**: Motor DC com encoder óptico +- **Câmbio**: H-pattern com eixos analógicos + 16 botões SPI (2x 74HC165) +- **Aro do Volante**: 8 botões (1x 74HC165) + LEDs (1x HC595) +- **Pedais**: 3 eixos analógicos (acelerador, freio, embreagem) + +--- + +## Motor Connection / Conexão do Motor + +### EN - Motor Wiring + +The G27 uses a **DC motor** controlled via PWM through an H-bridge driver (like BTS7960). + +| Function | OpenFFBoard Pin | Wire Color (typical) | Notes | +|----------|-----------------|---------------------|-------| +| PWM | PE11 | - | TIM1_CH2 PWM output | +| Direction | PE9 | - | Motor direction control | +| Enable | PE10 | - | Motor enable (DRV_BRAKE) | +| Motor + | BTS7960 M+ | Red/Orange | To H-bridge output | +| Motor - | BTS7960 M- | Black/Brown | To H-bridge output | + +#### 🎮 G27 Motor Torque Curve Tuning + +The G27 motors have specific characteristics that benefit from torque curve adjustment. For good force feedback response with minimal dead zone/backlash: + +| Parameter | Recommended Value | Notes | +|-----------|-------------------|-------| +| Torque Curve | **0.60 ~ 0.70** | Reduces dead zone, improves response | + +> **EN:** Setting the torque curve to 0.60-0.70 provides good FFB response with the G27 motors, reducing the "slack" feeling at center position. +> +> **PT:** Configurar a curva de torque em 0.60-0.70 proporciona boa resposta do FFB com os motores G27, reduzindo a sensação de "folga" na posição central. + +### PT - Ligação do Motor + +O G27 usa um **motor DC** controlado via PWM através de um driver H-bridge (como BTS7960). + +| Função | Pino OpenFFBoard | Cor do Fio (típico) | Notas | +|--------|------------------|---------------------|-------| +| PWM | PE11 | - | Saída PWM TIM1_CH2 | +| Direção | PE9 | - | Controle de direção | +| Enable | PE10 | - | Habilitação (DRV_BRAKE) | +| Motor + | BTS7960 M+ | Vermelho/Laranja | Saída do H-bridge | +| Motor - | BTS7960 M- | Preto/Marrom | Saída do H-bridge | + +--- + +## Encoder Connection / Conexão do Encoder + +### EN - Encoder Wiring + +The G27 originally uses an **optical quadrature encoder** (600 PPR) for position feedback. + +| Function | OpenFFBoard Pin | G27 Wire | Notes | +|----------|-----------------|----------|-------| +| Encoder A | PA0 | Green | Quadrature signal A | +| Encoder B | PA1 | White | Quadrature signal B | +| Index (Z) | - | - | Not used on G27 | +| VCC | 5V | Red | Encoder power | +| GND | GND | Black | Ground | + +#### Option A: Original G27 Encoder (600 PPR) +- Encoder Type: **ABN** (Quadrature) +- CPR (Counts Per Revolution): **2400** (600 PPR x4) + +#### Option B: Magnetic Encoder Upgrade (MT6835) ⭐ Recommended + +I replaced the original encoder with a **MT6835 ABZ magnetic encoder** mounted on the motor shaft. This provides much higher resolution and better FFB precision. + +| Parameter | Value | Notes | +|-----------|-------|-------| +| Encoder | MT6835 ABZ | Magnetic, high resolution | +| CPR | **65535** | Maximum resolution | +| Gear Ratio | **11:180** | Motor gear:Wheel gear teeth | +| Mounting | [Thingiverse 1270001](https://www.thingiverse.com/thing:1270001) | 3D printed adapter (works with 555 motors too) | + +The gear ratio 11:180 represents the relationship between the motor pinion (11 teeth) and the main wheel gear (180 teeth). + +### PT - Ligação do Encoder + +O G27 originalmente usa um **encoder óptico em quadratura** (600 PPR) para feedback de posição. + +| Função | Pino OpenFFBoard | Fio G27 | Notas | +|--------|------------------|---------|-------| +| Encoder A | PA0 | Verde | Sinal de quadratura A | +| Encoder B | PA1 | Branco | Sinal de quadratura B | +| Index (Z) | - | - | Não usado no G27 | +| VCC | 5V | Vermelho | Alimentação do encoder | +| GND | GND | Preto | Terra | + +#### Opção A: Encoder Original do G27 (600 PPR) +- Tipo do Encoder: **ABN** (Quadratura) +- CPR (Contagens Por Revolução): **2400** (600 PPR x4) + +#### Opção B: Upgrade com Encoder Magnético (MT6835) ⭐ Recomendado + +Substituí o encoder original por um **encoder magnético MT6835 ABZ** montado no eixo do motor. Isso proporciona resolução muito maior e melhor precisão do FFB. + +| Parâmetro | Valor | Notas | +|-----------|-------|-------| +| Encoder | MT6835 ABZ | Magnético, alta resolução | +| CPR | **65535** | Resolução máxima | +| Relação de Engrenagens | **11:180** | Dentes engrenagem motor:aro | +| Montagem | [Thingiverse 1270001](https://www.thingiverse.com/thing:1270001) | Adaptador impresso 3D (funciona com motores 555 também) | + +A relação 11:180 representa a proporção entre o pinhão do motor (11 dentes) e a engrenagem principal do aro (180 dentes). + +--- + +## Pedals Connection / Conexão dos Pedais + +### EN - Pedals Wiring + +The G27 pedals use **3 potentiometers** for throttle, brake, and clutch. This connection follows the **standard OpenFFBoard wiki guide** - no modifications needed. + +| Function | OpenFFBoard Pin | G27 Pedal Wire | Notes | +|----------|-----------------|----------------|-------| +| Throttle | Analog 1 (PA2) | - | Accelerator pedal | +| Brake | Analog 2 (PA3) | - | Brake pedal | +| Clutch | Analog 3 (PC3) | - | Clutch pedal | +| VCC | 3.3V | Red | Potentiometer power | +| GND | GND | Black | Ground | + +> **Note:** This is the **standard connection** as documented in the [OpenFFBoard Wiki - Pinouts and Peripherals](https://github.com/Ultrawipf/OpenFFBoard/wiki/Pinouts-and-peripherals). No custom modifications were required. + +#### Configuration / Configuração +- Analog Source: **Local Analog** +- Number of axes: **3** +- Calibrate each axis in the OpenFFBoard Configurator + +### PT - Ligação dos Pedais + +Os pedais do G27 usam **3 potenciômetros** para acelerador, freio e embreagem. Esta conexão segue o **guia padrão da wiki do OpenFFBoard** - nenhuma modificação necessária. + +| Função | Pino OpenFFBoard | Fio Pedal G27 | Notas | +|--------|------------------|---------------|-------| +| Acelerador | Analog 1 (PA2) | - | Pedal do acelerador | +| Freio | Analog 2 (PA3) | - | Pedal do freio | +| Embreagem | Analog 3 (PC3) | - | Pedal da embreagem | +| VCC | 3.3V | Vermelho | Alimentação dos potenciômetros | +| GND | GND | Preto | Terra | + +> **Nota:** Esta é a **conexão padrão** conforme documentado na [Wiki do OpenFFBoard - Pinouts and Peripherals](https://github.com/Ultrawipf/OpenFFBoard/wiki/Pinouts-and-peripherals). Nenhuma modificação customizada foi necessária. + +--- + +## Shifter Connection / Conexão do Câmbio + +### EN - Shifter Wiring + +The G27 shifter has: +- **Analog axes** (X/Y) for gear position detection +- **2x 74HC165** shift registers for 16 buttons + +| Function | OpenFFBoard Pin | G27 Connector Pin | Notes | +|----------|-----------------|-------------------|-------| +| VCC | 3.3V | 1 | Power (3.3V!) | +| SCK | PB13 | 2 | SPI Clock | +| MISO | PB14 | 4 | SPI Data | +| CS/Latch | PB12 | 6 | Chip Select (SPI2_SS1) | +| X Axis | PC0 (Analog 6) | 7 | Analog X position | +| Y Axis | PC1 (Analog 5) | 3 | Analog Y position | +| GND | GND | 9 | Ground | + +#### Configuration / Configuração +- Mode: **G27 Shifter H-pattern** (ShifterAnalog mode 2) +- CS Pin: **1** (PB12) +- X Channel: **6** (PC0) - configurable via `shifter.xchan` +- Y Channel: **5** (PC1) - configurable via `shifter.ychan` + +### PT - Ligação do Câmbio + +O câmbio G27 tem: +- **Eixos analógicos** (X/Y) para detecção da marcha +- **2x 74HC165** registradores de deslocamento para 16 botões + +| Função | Pino OpenFFBoard | Pino Conector G27 | Notas | +|--------|------------------|-------------------|-------| +| VCC | 3.3V | 1 | Alimentação (3.3V!) | +| SCK | PB13 | 2 | Clock SPI | +| MISO | PB14 | 4 | Dados SPI | +| CS/Latch | PB12 | 6 | Chip Select (SPI2_SS1) | +| Eixo X | PC0 (Analog 6) | 7 | Posição analógica X | +| Eixo Y | PC1 (Analog 5) | 3 | Posição analógica Y | +| GND | GND | 9 | Terra | + +#### Configuração +- Modo: **G27 Shifter H-pattern** (ShifterAnalog modo 2) +- CS Pin: **1** (PB12) +- X Channel: **6** (PC0) - configurável via `shifter.xchan` +- Y Channel: **5** (PC1) - configurável via `shifter.ychan` + +--- + +## Wheel Rim Buttons / Botões do Aro + +> **Upstream PR vs Fork note:** +> - **PR #182 (upstream):** Contains only the ShifterAnalog G27 H-pattern fix. Wheel rim via SPI2 CS2 is the preferred upstream approach, but requires a **tri-state buffer** on MISO because the stock G27 74HC165 has no tri-state output. +> - **Tag `g27-full-working` (fork):** Contains SPI3 support for the wheel rim. Use this tag to build and flash firmware if you need both shifter + wheel rim working on stock G27 hardware today. + +### EN - Wheel Rim Wiring + +The G27 wheel rim has: +- **1x 74HC165** for 8 buttons +- **1x HC595AG** for LEDs (optional, not implemented) + +> **Important:** The wheel rim buttons **cannot share the same SPI bus** with the shifter because the **stock G27 74HC165 lacks tri-state output**. The upstream preferred approach (SPI2 CS2) requires adding a tri-state buffer (e.g. 74HC125) on the MISO line. The fork workaround uses a **separate SPI port (SPI3)** — available in tag `g27-full-working`. + +#### Option A: Upstream approach (SPI2 CS2, requires tri-state buffer) + +| Function | OpenFFBoard Pin | Notes | +|----------|-----------------|-------| +| SCK | PB13 | Shared with shifter | +| MISO | PB14 | Via 74HC125 tri-state buffer | +| CS/Latch | PD8 | SPI2_SS2 | +| VCC | 3.3V | Power | +| GND | GND | Ground | + +#### Option B: Fork workaround (SPI3, tag `g27-full-working`) + +| Function | OpenFFBoard Pin | Wheel Rim Pin | Notes | +|----------|-----------------|---------------|-------| +| VCC | 3.3V | 1 | Power (3.3V!) | +| SCK | PC10 | 4 | SPI3 Clock | +| MISO | PC11 | 6 | SPI3 Data | +| CS/Latch | PA15 | 2 | SPI3_SS1 | +| GND | GND | 7 | Ground | +| MOSI | - | 3 | For LEDs (not used) | +| LED Latch | - | 5 | For LEDs (not used) | + +#### Why the conflict? / Por que há conflito? + +The 74HC165 shift register does **not have tri-state output**. When two 74HC165 chips share the same MISO line, they create electrical contention - both try to drive the line simultaneously. The upstream solution requires a buffer IC; the fork solution uses a separate SPI bus. + +#### Configuration for Option B / Configuração para Opção B +- Class: **SPI Buttons 3** (fork only, uses SPI3) +- Buttons: **8** +- Mode: **74HC165** (PISOSR) +- CS Pin: **1** (PA15) + +### PT - Ligação dos Botões do Aro + +> **PR upstream vs Fork:** +> - **PR #182 (upstream):** Contém apenas o fix do câmbio ShifterAnalog. Aro via SPI2 CS2 é a abordagem preferida upstream, mas exige **buffer tri-state** no MISO porque o G27 stock não tem saída tri-state. +> - **Tag `g27-full-working` (fork):** Contém suporte a SPI3 para o aro. Use essa tag para compilar e gravar se precisar de câmbio + aro funcionando no hardware G27 stock hoje. + +O aro do G27 tem: +- **1x 74HC165** para 8 botões +- **1x HC595AG** para LEDs (opcional, não implementado) + +> **Importante:** Os botões do aro **não podem compartilhar o mesmo barramento SPI** com o câmbio porque o **G27 stock 74HC165 não tem saída tri-state**. A abordagem upstream (SPI2 CS2) requer adicionar um buffer tri-state (ex: 74HC125). O workaround fork usa **SPI3** — disponível na tag `g27-full-working`. + +--- + +## Complete Pinout Summary / Resumo Completo da Pinagem + +### STM32F407VET6 Pin Assignment / Atribuição de Pinos + +``` +┌─────────────────────────────────────────────────────────────┐ +│ STM32F407VET6 - G27 CONNECTIONS │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ ⚡ REQUIRED FOR F407VG FIRMWARE TO BOOT │ +│ └── PA9 ────────── 5V (via 10kΩ resistor) │ +│ │ +│ MOTOR (via BTS7960) │ +│ ├── PWM ────────── PE11 │ +│ ├── Direction ──── PE9 │ +│ └── Enable ─────── PE10 (DRV_BRAKE) │ +│ │ +│ ENCODER │ +│ ├── Encoder A ──── PA0 │ +│ ├── Encoder B ──── PA1 │ +│ ├── VCC ────────── 5V │ +│ └── GND ────────── GND │ +│ │ +│ PEDALS (Standard Wiki Connection) │ +│ ├── Throttle ───── PA2 (Analog 1) │ +│ ├── Brake ──────── PA3 (Analog 2) │ +│ ├── Clutch ─────── PC3 (Analog 3) │ +│ ├── VCC ────────── 3.3V │ +│ └── GND ────────── GND │ +│ │ +│ SHIFTER (SPI2 + Analog) │ +│ ├── SCK ────────── PB13 │ +│ ├── MISO ───────── PB14 │ +│ ├── CS ─────────── PB12 (SPI2_SS1) │ +│ ├── X Axis ─────── PC0 (Analog 6) │ +│ ├── Y Axis ─────── PC1 (Analog 5) │ +│ ├── VCC ────────── 3.3V │ +│ └── GND ────────── GND │ +│ │ +│ WHEEL RIM BUTTONS (SPI3) - Separate bus required! │ +│ ├── SCK ────────── PC10 │ +│ ├── MISO ───────── PC11 │ +│ ├── CS ─────────── PA15 (SPI3_SS1) │ +│ ├── VCC ────────── 3.3V │ +│ └── GND ────────── GND │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Summary Table / Tabela Resumo + +| Component | Interface | Pins | Power | Notes | +|-----------|-----------|------|-------|-------| +| **VBUS Fix** | Resistor | PA9 → 5V | - | ⚡ **10kΩ required for F407VG firmware** | +| Motor | PWM | PE11 | External PSU | H-bridge required, torque curve 0.60-0.70 | +| Encoder (Original) | Quadrature | PA0, PA1 | 5V | ABN type, CPR 2400 | +| Encoder (MT6835) | Quadrature | PA0, PA1 | 5V | ABN type, CPR 65535, ratio 11:180 ⭐ | +| Pedals | Analog | PA2, PA3, PC3 | 3.3V | Standard wiki connection | +| Shifter | SPI2 + Analog | PB13, PB14, PB12, PC0, PC1 | 3.3V | G27 mode, X/Y analog axes | +| Wheel Rim | SPI3 | PC10, PC11, PA15 | 3.3V | Custom firmware required | + +--- + +## 🔧 OpenFFBoard Configurator Settings / Configurações + +### EN - Configuration Steps + +1. **Motor Driver** + - Type: PWM + - PWM Pin: PE11 + - **Torque Curve: 0.60 ~ 0.70** (reduces dead zone) + +2. **Encoder** + - Type: Local ABN + - **Option A (Original):** CPR: 2400 + - **Option B (MT6835):** CPR: 65535, Gear Ratio: 11:180 + +3. **Pedals (Local Analog)** + - Analog Source: Local Analog + - Axes: 3 (Throttle, Brake, Clutch) + - Calibrate in configurator + - ✅ Standard wiki connection - no modifications + +4. **Shifter (Analog Shifter)** + - Mode: G27 Shifter H-pattern (mode 2) + - CS Pin: 1 + +5. **Wheel Rim (SPI Buttons 3)** + - Buttons: 8 + - Mode: 74HC165 + - CS: 1 + - (Requires custom firmware with SPI_Buttons_3 class) + +### PT - Passos de Configuração + +1. **Driver do Motor** + - Tipo: PWM + - Pino PWM: PE11 + - **Curva de Torque: 0.60 ~ 0.70** (reduz zona morta) + +2. **Encoder** + - Tipo: Local ABN + - **Opção A (Original):** CPR: 2400 + - **Opção B (MT6835):** CPR: 65535, Relação: 11:180 + +3. **Pedais (Local Analog)** + - Fonte Analógica: Local Analog + - Eixos: 3 (Acelerador, Freio, Embreagem) + - Calibrar no configurador + - ✅ Conexão padrão da wiki - sem modificações + +4. **Câmbio (Analog Shifter)** + - Modo: G27 Shifter H-pattern (modo 2) + - CS Pin: 1 + +5. **Aro (SPI Buttons 3)** + - Botões: 8 + - Modo: 74HC165 + - CS: 1 + - (Requer firmware customizado com classe SPI_Buttons_3) + +--- + +## ⚠️ Warnings / Avisos + +### EN - Important Warnings + +1. **PA9 Resistor (STM32F407VET6)**: A **10kΩ pull-up resistor between PA9 and 5V is required** for the F407VG firmware to boot on generic STM32F407VET6 boards. Without it, only the DISCO firmware works. +2. **Voltage**: The shifter and wheel rim electronics work with **3.3V**. Do not apply 5V! +3. **SPI Bus Separation**: The wheel rim **must** use a separate SPI bus (SPI3) due to 74HC165 limitations. +4. **Firmware**: Wheel rim buttons via SPI3 require the fork firmware (tag `g27-full-working`). The upstream PR includes only the ShifterAnalog fix. + +### PT - Avisos Importantes + +1. **Resistor PA9 (STM32F407VET6)**: Um **resistor pull-up de 10kΩ entre PA9 e 5V é obrigatório** para o firmware F407VG iniciar em placas genéricas STM32F407VET6. Sem ele, apenas o firmware DISCO funciona. +2. **Tensão**: A eletrônica do câmbio e aro funciona com **3.3V**. Não aplique 5V! +3. **Separação do Barramento SPI**: O aro **deve** usar um barramento SPI separado (SPI3) devido às limitações do 74HC165. +4. **Firmware**: Os botões do aro requerem modificações customizadas no firmware (classe SPI_Buttons_3). + +--- + +## 📊 Final Result / Resultado Final + +| Component / Componente | Status | Windows Axes/Buttons | +|------------------------|--------|----------------------| +| Motor / Motor | ✅ Working / Funcionando | FFB enabled (torque curve 0.60-0.70) | +| Encoder / Encoder | ✅ Working / Funcionando | Steering axis (MT6835 65535 CPR) | +| Pedals / Pedais | ✅ Working / Funcionando | 3 axes (throttle, brake, clutch) | +| Shifter Gears / Marchas | ✅ Working / Funcionando | Buttons 1-7 (6 gears + reverse) | +| Shifter Buttons / Botões Câmbio | ✅ Working / Funcionando | Buttons 8-19 (12 buttons) | +| Wheel Rim / Aro | ✅ Working / Funcionando | Buttons 20-27 (8 buttons) | + +**Total: 27 buttons + 7 gears (including reverse) + 3 pedal axes + FFB motor** + +### Encoder Upgrade Notes / Notas do Upgrade do Encoder + +> **EN:** The magnetic encoder MT6835 mounted with [this 3D printed adapter](https://www.thingiverse.com/thing:1270001) provides much better resolution and FFB feel compared to the original 600 PPR encoder. The adapter also works with 555 motors. +> +> **PT:** O encoder magnético MT6835 montado com [este adaptador impresso 3D](https://www.thingiverse.com/thing:1270001) proporciona resolução muito melhor e sensação do FFB comparado ao encoder original de 600 PPR. O adaptador também funciona com motores 555. + +--- + +*Document Version: 1.1* +*Date: January 2026* +*Board: Generic STM32F407VET6* +*Hardware: Logitech G27 Racing Wheel* +*Encoder Upgrade: MT6835 ABZ Magnetic Encoder* diff --git a/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md b/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md new file mode 100644 index 000000000..ba582f662 --- /dev/null +++ b/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md @@ -0,0 +1,369 @@ +# G27 Shifter - ShifterAnalog G27 Mode Bug Report & Fix + +## 🎯 Summary + +The **G27 Shifter buttons did not work** when using the official **ShifterAnalog "G27 Shifter H-pattern" mode** on a generic [STM32F407VET6 board](http://pt.aliexpress.com/item/1005006882009420.html) running OpenFFBoard firmware. After code analysis and modifications, the issue was identified and fixed. + +--- + +## 📋 Hardware Setup + +| Function | Pin | Description | +|----------|-----|-------------| +| SCK | PB13 | SPI2 Clock | +| MISO | PB14 | SPI2 Data In | +| CS/Latch | PB12 | SPI2_SS1 (Shifter Chip Select) | +| X Axis | PC0 | Analog 6 (configurable) | +| Y Axis | PC1 | Analog 5 (configurable) | +| VCC | 3.3V | Power | +| GND | GND | Ground | + +The G27 shifter contains: +- **2x 74HC165** shift registers (16 buttons total) +- **Analog axes** for X/Y position (gear detection via PC0/PC1) + +--- + +## 🔴 Original Problem + +### Symptoms +- ✅ **Analog axes worked** - Gear positions detected correctly +- ❌ **Buttons did NOT work** - No button presses registered in G27 mode +- ✅ **Same hardware worked with SPI Buttons** - When configured as standalone "SPI Buttons" with 16 buttons, mode 74HC165, CS 1, all buttons worked + +--- + +## 🔍 Root Cause Analysis + +After analyzing the original code, **5 bugs** were identified in `ShifterAnalog.cpp`: + +### Bug 1: Wrong SPI Clock Configuration + +**Original Code:** +```cpp +spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; +spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; +``` + +**Problem:** The 74HC165 shift register requires specific SPI timing. The original configuration didn't match the chip's requirements. + +**Fix:** +```cpp +// 74HC165 configuration: sample on falling edge (phase 2, polarity high) +spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE; +spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; +``` + +--- + +### Bug 2: Missing CS Polarity Configuration + +**Original Code:** +```cpp +// No cspol configuration +``` + +**Problem:** The CS (chip select) polarity was not explicitly set, causing incorrect latch behavior for the 74HC165. + +**Fix:** +```cpp +// CS polarity LOW (active low) for 74HC165 mode +spiConfig.cspol = false; +``` + +--- + +### Bug 3: Asynchronous Read Without Waiting for Data + +**Original Code:** +```cpp +bool ShifterAnalog::G27ShifterButtonClient::getReverseButton() { + external_spi.receive_DMA(reinterpret_cast(&buttonStates), sizeof(buttonStates), this); + return buttonStates & 0x02; // Returns BEFORE DMA completes! +} +``` + +**Problem:** The function returned the button state **immediately after starting** the DMA transfer, not after it completed. This meant it always read stale/zero data. + +**Fix:** Created a separate `startRead()` function and read from buffer after completion: +```cpp +void ShifterAnalog::G27ShifterButtonClient::startRead() { + // Use synchronous read - waits for completion + external_spi.receive(spi_buf, bytesToRead, this, 2); // 2ms timeout +} + +bool ShifterAnalog::G27ShifterButtonClient::getReverseButton() { + // Read from buffer AFTER DMA completed + uint16_t buttonStates = (uint16_t)spi_buf[0] | ((uint16_t)spi_buf[1] << 8); + return buttonStates & 0x02; +} +``` + +--- + +### Bug 4: readButtons() Didn't Trigger SPI Read + +**Original Code:** +```cpp +uint8_t ShifterAnalog::readButtons(uint64_t* buf){ + updateAdc(); + updateReverseState(); // This triggered DMA but didn't wait + calculateGear(); + *buf = 0; + auto numUserButtons{getUserButtons(buf)}; // Read stale data + // ... +} +``` + +**Problem:** The read sequence was wrong - it didn't wait for SPI data to be ready before processing. + +**Fix:** +```cpp +uint8_t ShifterAnalog::readButtons(uint64_t* buf){ + updateAdc(); + + if (g27ShifterButtonClient) { + // Synchronous read - waits for bus and reads immediately + g27ShifterButtonClient->startRead(); + + // Now process the freshly read data + updateReverseState(); + getUserButtons(buf); + } else { + updateReverseState(); + *buf = 0; + } + + calculateGear(); + // ... +} +``` + +--- + +### Bug 5: CS Pin Index Off-by-One Error + +**Original Code:** +```cpp +void ShifterAnalog::setMode(ShifterMode newMode) { + // ... + g27ShifterButtonClient = std::make_unique(external_spi.getFreeCsPins()[0]); +} + +void ShifterAnalog::setCSPin(uint8_t new_cs_pin_num) { + // ... + g27ShifterButtonClient->updateCSPin(*external_spi.getCsPin(cs_pin_num)); // Wrong index! +} +``` + +**Problem:** +1. `setMode()` always used the first free CS pin instead of the configured `cs_pin_num` +2. `setCSPin()` used `cs_pin_num` directly, but `getCsPin()` is 0-indexed while `cs_pin_num` is 1-indexed + +**Fix:** +```cpp +void ShifterAnalog::setMode(ShifterMode newMode) { + // ... + // Use the configured CS pin (cs_pin_num is 1-indexed, getCsPin uses 0-indexed) + OutputPin* csPin = external_spi.getCsPin(cs_pin_num - 1); + if (csPin == nullptr) { + // Fallback to first free pin if configured pin is invalid + auto& freePins = external_spi.getFreeCsPins(); + if (!freePins.empty()) { + csPin = &freePins[0]; + } + } + if (csPin != nullptr) { + g27ShifterButtonClient = std::make_unique(*csPin); + } +} + +void ShifterAnalog::setCSPin(uint8_t new_cs_pin_num) { + // ... + // cs_pin_num is 1-indexed, getCsPin uses 0-indexed + OutputPin* newPin = external_spi.getCsPin(cs_pin_num - 1); + if (newPin != nullptr) { + g27ShifterButtonClient->updateCSPin(*newPin); + } +} +``` + +--- + +## 📁 Modified Files + +### ShifterAnalog.h + +```diff + class G27ShifterButtonClient : public SPIDevice { + G27ShifterButtonClient(OutputPin& csPin); + + static constexpr int numUserButtons{12}; ++ static constexpr int bytesToRead{2}; // 16 buttons = 2 bytes + + uint16_t getUserButtons(); + bool getReverseButton(); +- private: +- uint16_t buttonStates{0}; ++ void startRead(); ++ void spiRxCompleted(SPIPort* port) override; ++ ++ uint8_t spi_buf[2]{0}; // Buffer for SPI read + }; +``` + +### ShifterAnalog.cpp + +**G27ShifterButtonClient Constructor:** +```diff + ShifterAnalog::G27ShifterButtonClient::G27ShifterButtonClient(OutputPin& csPin) + :SPIDevice(external_spi,csPin) { + spiConfig.peripheral.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; + spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; +- spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; +- spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; ++ spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE; ++ spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; ++ spiConfig.cspol = false; + } +``` + +**New startRead() function:** +```cpp +void ShifterAnalog::G27ShifterButtonClient::startRead() { + external_spi.receive(spi_buf, bytesToRead, this, 2); // 2ms timeout +} +``` + +**getReverseButton() - Read from buffer instead of triggering DMA:** +```diff + bool ShifterAnalog::G27ShifterButtonClient::getReverseButton() { +- external_spi.receive_DMA(reinterpret_cast(&buttonStates), sizeof(buttonStates),this); ++ uint16_t buttonStates = (uint16_t)spi_buf[0] | ((uint16_t)spi_buf[1] << 8); + return buttonStates & 0x02; + } +``` + +**getUserButtons() - Read from buffer:** +```diff + uint16_t ShifterAnalog::G27ShifterButtonClient::getUserButtons() { ++ uint16_t buttonStates = (uint16_t)spi_buf[0] | ((uint16_t)spi_buf[1] << 8); + return buttonStates >> 4; + } +``` + +**readButtons() - Proper read sequence:** +```diff + uint8_t ShifterAnalog::readButtons(uint64_t* buf){ + updateAdc(); + +- updateReverseState(); ++ if (g27ShifterButtonClient) { ++ g27ShifterButtonClient->startRead(); ++ updateReverseState(); ++ getUserButtons(buf); ++ } else { ++ updateReverseState(); ++ *buf = 0; ++ } ++ + calculateGear(); + +- *buf = 0; +- auto numUserButtons{getUserButtons(buf)}; +- + if(gear > 0){ ++ uint64_t numUserButtons = g27ShifterButtonClient ? g27ShifterButtonClient->numUserButtons : 0; + *buf |= 1 << (gear - 1 + numUserButtons); + } + + return this->btnnum; + } +``` + +**setMode() - Use configured CS pin:** +```diff + void ShifterAnalog::setMode(ShifterMode newMode) { + // ... +- g27ShifterButtonClient = std::make_unique(external_spi.getFreeCsPins()[0]); ++ OutputPin* csPin = external_spi.getCsPin(cs_pin_num - 1); ++ if (csPin == nullptr) { ++ auto& freePins = external_spi.getFreeCsPins(); ++ if (!freePins.empty()) { ++ csPin = &freePins[0]; ++ } ++ } ++ if (csPin != nullptr) { ++ g27ShifterButtonClient = std::make_unique(*csPin); ++ } + } +``` + +**setCSPin() - Fix off-by-one error:** +```diff + void ShifterAnalog::setCSPin(uint8_t new_cs_pin_num) { + // ... + if (g27ShifterButtonClient) { +- g27ShifterButtonClient->updateCSPin(*external_spi.getCsPin(cs_pin_num)); ++ OutputPin* newPin = external_spi.getCsPin(cs_pin_num - 1); ++ if (newPin != nullptr) { ++ g27ShifterButtonClient->updateCSPin(*newPin); ++ } + } + } +``` + +--- + +## ✅ Result After Fix + +| Feature | Status | +|---------|--------| +| Gear 1-6 detection | ✅ Working | +| Reverse gear | ✅ Working | +| 12 user buttons | ✅ Working | +| CS pin configuration | ✅ Working | + +--- + +## 📊 Test Environment + +| Parameter | Value | +|-----------|-------| +| Board | Generic STM32F407VET6 | +| Shifter | Logitech G27 H-Pattern | +| Connection | PB12 (CS), PB13 (SCK), PB14 (MISO) | +| Voltage | 3.3V | + +--- + +## 🇧🇷 Resumo em Português + +### Problema +Os **botões do câmbio G27 não funcionavam** no modo oficial "G27 Shifter H-pattern" do ShifterAnalog. + +### Causa (5 bugs encontrados) +1. **Configuração SPI errada** - CLKPhase e CLKPolarity incorretos para o 74HC165 +2. **Polaridade do CS não definida** - cspol não estava configurado +3. **Leitura assíncrona sem esperar dados** - getReverseButton() retornava antes do DMA completar +4. **readButtons() não disparava leitura SPI** - sequência de leitura incorreta +5. **Erro de índice no CS pin** - off-by-one error (1-indexed vs 0-indexed) + +### Solução +Foram feitas correções em `ShifterAnalog.cpp` e `ShifterAnalog.h`: +- Corrigida configuração SPI (CLKPhase, CLKPolarity, cspol) +- Criada função `startRead()` com leitura síncrona +- Corrigida sequência de leitura em `readButtons()` +- Corrigido índice do CS pin (cs_pin_num - 1) + +### Resultado +Após as correções, o câmbio G27 funciona perfeitamente: +- ✅ 6 marchas + ré +- ✅ 12 botões extras +- ✅ Configuração do CS pin funcional + +--- + +*Report Date: January 2026* +*Board: Generic STM32F407VET6* +*Affected Feature: ShifterAnalog - G27 Shifter H-pattern mode* diff --git a/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md b/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md new file mode 100644 index 000000000..31d27ebf1 --- /dev/null +++ b/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md @@ -0,0 +1,407 @@ +# G27 Wheel Rim Buttons - SPI Bus Conflict Report & Solution + +> **Upstream PR note:** The upstream maintainer prefers using **SPI2 with a second CS pin** (e.g. PD8) for the wheel rim, which is the correct approach when a tri-state buffer is present on the MISO line. The **stock G27 wheel rim has no tri-state output**, so SPI2 CS2 fails when both devices are connected simultaneously. The SPI3 workaround documented here is available in the tag **`g27-full-working`** (fork only). **PR #182 contains only the ShifterAnalog fix** — the SPI3 wheel rim code is not included upstream. + +## 🎯 Summary + +The **G27 steering wheel rim buttons** (8 buttons via 74HC165) worked correctly when used **alone** on SPI2, but **failed when used simultaneously** with the G27 shifter on the same SPI bus. This document explains the root cause and the workaround implemented using a separate SPI port (SPI3, fork/tag only). + +--- + +## 📋 Hardware Setup + +### G27 Wheel Rim +| Function | Pin (Original) | Description | +|----------|----------------|-------------| +| SCK | PB13 | SPI2 Clock | +| MISO | PB14 | SPI2 Data In | +| CS/Latch | PD8 | SPI2_SS2 (Wheel Rim CS) | +| VCC | 3.3V | Power | +| GND | GND | Ground | + +The wheel rim contains: +- **1x 74HC165** shift register (8 buttons) +- **1x HC595AG** shift register (LEDs - not used) + +### G27 Shifter (for reference) +| Function | Pin | Description | +|----------|-----|-------------| +| SCK | PB13 | SPI2 Clock (shared) | +| MISO | PB14 | SPI2 Data In (shared) | +| CS/Latch | PB12 | SPI2_SS1 (Shifter CS) | + +--- + +## ✅ What Worked + +### Wheel Rim Alone on CS2 +When the wheel rim was connected **without the shifter**, using SPI Buttons with: +- `btnnum = 8` +- `mode = 1` (74HC165) +- `cs = 2` (PD8) + +**Result:** ✅ All 8 buttons worked perfectly + +### Shifter Alone on CS1 +When the shifter was connected **without the wheel rim**, using ShifterAnalog G27 mode: +- `mode = 2` (G27 H-pattern) +- `cspin = 1` (PB12) + +**Result:** ✅ All gears + reverse + 12 buttons worked perfectly + +--- + +## ❌ What Didn't Work + +### Both Devices on Same SPI Bus +When both devices were connected simultaneously: + +| Configuration | Shifter | Wheel Rim | +|--------------|---------|-----------| +| ShifterAnalog CS1 + SPI Buttons CS2 | ❌ Buttons stopped working | ❌ Buttons stopped working | +| Only ShifterAnalog active | ❌ Buttons not reading | - | +| Only SPI Buttons active | - | ✅ Working | +| Wheel rim physically disconnected | ✅ Working | - | + +**Key observation:** Simply having the wheel rim **physically connected** to the MISO line caused the shifter buttons to stop working, even with SPI Buttons disabled! + +--- + +## 🔍 Root Cause: 74HC165 Has No Tri-State Output + +### The Problem + +The **74HC165 shift register does NOT have a tri-state (high-impedance) output**. This is a critical hardware limitation. + +#### Normal SPI Device Behavior +Most SPI devices have tri-state outputs: +- When CS is **inactive** → Output goes **high-impedance** (disconnected) +- When CS is **active** → Output drives the MISO line + +#### 74HC165 Behavior +The 74HC165 is different: +- When CS (SH/LD) is **inactive** → Output **still drives** the MISO line +- When CS is **active** → Output drives the MISO line + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SPI BUS CONTENTION │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 74HC165 #1 (Shifter) 74HC165 #2 (Wheel Rim) │ +│ ┌───┐ ┌───┐ │ +│ Q7 ──┤ ├──┐ Q7 ──┤ ├──┐ │ +│ └───┘ │ └───┘ │ │ +│ │ │ │ +│ └────────┬───────────────┘ │ +│ │ │ +│ ▼ │ +│ MISO (PB14) ← CONFLICT! │ +│ │ +│ Both chips ALWAYS drive MISO, regardless of CS state. │ +│ This creates electrical contention and corrupted data. │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Why It Seemed to Work Sometimes + +When only one device was actively being read, the other device's output might coincidentally not interfere. But as soon as both are polled (even at different times), the constant output driving causes: +- Data corruption +- Unpredictable readings +- One device "winning" over the other + +--- + +## ✅ Solution: Use Separate SPI Bus + +Since the 74HC165 chips cannot share a MISO line, the solution is to use a **completely separate SPI peripheral** for the wheel rim. + +### New Hardware Configuration + +| Device | Function | Pin | SPI Bus | +|--------|----------|-----|---------| +| **Shifter** | SCK | PB13 | SPI2 | +| **Shifter** | MISO | PB14 | SPI2 | +| **Shifter** | CS | PB12 | SPI2_SS1 | +| **Wheel Rim** | SCK | PC10 | SPI3 | +| **Wheel Rim** | MISO | PC11 | SPI3 | +| **Wheel Rim** | CS | PA15 | SPI3_SS1 | +| **Wheel Rim** | VCC | 3.3V | - | +| **Wheel Rim** | GND | GND | - | + +``` +┌─────────────────────────────────────────────────────────────┐ +│ SEPARATE SPI BUSES (SOLUTION) │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ SPI2 Bus (Shifter) SPI3 Bus (Wheel Rim) │ +│ ┌─────────────┐ ┌─────────────┐ │ +│ │ 74HC165 │ │ 74HC165 │ │ +│ │ (x2) │ │ (x1) │ │ +│ └──────┬──────┘ └──────┬──────┘ │ +│ │ │ │ +│ SCK ───┼─── PB13 SCK ────┼─── PC10 │ +│ MISO ──┼─── PB14 MISO ───┼─── PC11 │ +│ CS ────┼─── PB12 CS ─────┼─── PA15 │ +│ │ │ │ +│ ▼ ▼ │ +│ ShifterAnalog SPI_Buttons_3 │ +│ (G27 Mode) (8 buttons) │ +│ │ +│ ✅ No contention - completely separate MISO lines │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 🛠️ Firmware Modifications + +### 1. Created `SPI_Buttons_3` Class + +**File:** `SPIButtons.h` +```cpp +class SPI_Buttons_3 : public SPI_Buttons { +public: + SPI_Buttons_3(); + + const ClassIdentifier getInfo() override; + static ClassIdentifier info; + static bool isCreatable(); + + void restoreFlash() override; + uint8_t readButtons(uint64_t* buf) override; // Custom read for SPI3 + std::string getHelpstring() override {return "SPI 3 Button (SPI3)";} +}; +``` + +### 2. Custom `readButtons()` for SPI3 + +The standard DMA-based read had timing issues with SPI3. Implemented synchronous read with manual CS pulse: + +**File:** `SPIButtons.cpp` +```cpp +uint8_t SPI_Buttons_3::readButtons(uint64_t* buf){ + // Return last buffer immediately (non-blocking) + memcpy(buf, this->spi_buf, std::min(this->bytes, 8)); + process(buf); + + if(spiPort.isTaken()) + return this->conf.numButtons; + + SPI_HandleTypeDef* hspi = spiPort.getPortHandle(); + + // Get CS pin (PA15) + OutputPin* cs_pin = spiPort.getCsPin(this->conf.cs_num > 0 ? this->conf.cs_num - 1 : 0); + + if(cs_pin != nullptr) { + // Pulse CS to latch parallel data into 74HC165 + cs_pin->write(false); // CS LOW - load parallel data + for(volatile int i = 0; i < 10; i++) {} // Small delay + cs_pin->write(true); // CS HIGH - enable shift + } + + // Synchronous SPI read + HAL_SPI_Receive(hspi, spi_buf, bytes, 10); + + return this->conf.numButtons; +} +``` + +### 3. Hardcoded Configuration for Wheel Rim + +```cpp +void SPI_Buttons_3::restoreFlash(){ + ButtonSourceConfig config; + config.numButtons = 8; // 8 buttons on wheel rim + config.mode = SPI_BtnMode::PISOSR; // 74HC165 mode + config.cs_num = 1; // CS pin 1 (PA15) + config.spi_speed = 2; // Slow speed + config.invert = false; + config.cutRight = false; + + setConfig(config); + this->conf.cs_num = 1; + this->btnnum = 8; +} +``` + +### 4. SPI3 Port Configuration + +**File:** `cpp_target_config.cpp` +```cpp +#ifdef EXT3_SPI_PORT +static const std::vector ext3_spi_cspins{ + OutputPin(*SPI3_SS1_GPIO_Port, SPI3_SS1_Pin), // PA15 - CS1 + OutputPin(*SPI3_SS2_GPIO_Port, SPI3_SS2_Pin), // PD2 - CS2 + OutputPin(*SPI3_SS3_GPIO_Port, SPI3_SS3_Pin) // PD3 - CS3 +}; +extern SPI_HandleTypeDef EXT3_SPI_PORT; +SPIPort ext3_spi{hspi3, ext3_spi_cspins, 42000000, true}; +#endif +``` + +### 5. SPI3 Hardware Init + +**File:** `main.c` +```c +static void MX_SPI3_Init(void) +{ + hspi3.Instance = SPI3; + hspi3.Init.Mode = SPI_MODE_MASTER; + hspi3.Init.Direction = SPI_DIRECTION_2LINES; + hspi3.Init.DataSize = SPI_DATASIZE_8BIT; + hspi3.Init.CLKPolarity = SPI_POLARITY_LOW; + hspi3.Init.CLKPhase = SPI_PHASE_1EDGE; + hspi3.Init.NSS = SPI_NSS_SOFT; + hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; + hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB; + // ... +} +``` + +--- + +## 📁 Modified Files Summary + +| File | Changes | +|------|---------| +| `SPIButtons.h` | Added `SPI_Buttons_3` class with custom `readButtons()` override | +| `SPIButtons.cpp` | Implemented `SPI_Buttons_3` with synchronous read + manual CS pulse | +| `cpp_target_config.cpp` | Defined SPI3 CS pins (PA15, PD2, PD3) | +| `main.c` | Configured SPI3 hardware (CLKPolarity, CLKPhase, BaudRate) | +| `stm32f4xx_hal_msp.c` | Adjusted SPI3 DMA/IRQ priorities | +| `OpenFFBoard_F407VG.ioc` | Updated SPI3 CubeMX configuration | + +--- + +## ✅ Final Result + +| Component | Status | Button Range | +|-----------|--------|--------------| +| G27 Shifter (SPI2) | ✅ Working | Buttons 1-19 (gears + 12 buttons) | +| G27 Wheel Rim (SPI3) | ✅ Working | Buttons 20-27 (8 buttons) | + +--- + +## 💡 Alternative Solutions (Not Implemented) + +If SPI3 pins are not available, other options include: + +1. **External Tri-State Buffer** (e.g., 74HC125) + - Add a buffer IC between each 74HC165 and the shared MISO line + - Enable/disable buffer with CS signal + - Allows sharing single MISO line + +2. **Directly daisy-chain the 74HC165s** + - Connect Q7' (serial out) of shifter's last 74HC165 to DS (serial in) of wheel rim's 74HC165 + - Read all buttons in one long SPI transaction + - Requires firmware changes to handle 24 bits (3 bytes) + +--- + +## 🔑 Key Takeaway + +**74HC165 shift registers cannot share an SPI MISO line** due to lack of tri-state output. Each 74HC165 (or group of daisy-chained 74HC165s) requires either: +- A **dedicated SPI bus**, or +- An **external tri-state buffer** on the MISO line + +--- + +# 🇧🇷 Relatório em Português + +## Resumo + +Os **botões do aro do volante G27** (8 botões via 74HC165) funcionavam corretamente quando usados **sozinhos** no SPI2, mas **falhavam quando usados simultaneamente** com o câmbio G27 no mesmo barramento SPI. + +--- + +## O Que Funcionou + +### Aro Sozinho no CS2 +- Configuração: SPI Buttons, 8 botões, modo 74HC165, CS 2 (PD8) +- **Resultado:** ✅ Todos os 8 botões funcionaram + +### Câmbio Sozinho no CS1 +- Configuração: ShifterAnalog modo G27, CS 1 (PB12) +- **Resultado:** ✅ Todas as marchas + ré + 12 botões funcionaram + +--- + +## O Que Não Funcionou + +### Ambos no Mesmo Barramento SPI +Quando ambos estavam conectados: +- Apenas conectar fisicamente o aro já fazia os botões do câmbio pararem +- Os dois dispositivos não conseguiam coexistir no mesmo MISO + +--- + +## Causa Raiz: 74HC165 Não Tem Saída Tri-State + +O **74HC165 sempre mantém sua saída ativa**, mesmo quando o CS está inativo. Isso causa conflito elétrico quando dois 74HC165 compartilham a mesma linha MISO. + +``` +Dispositivo SPI normal: + CS inativo → Saída em alta impedância (desconectada) + CS ativo → Saída conectada + +74HC165: + CS inativo → Saída CONTINUA conectada ❌ + CS ativo → Saída conectada +``` + +Quando dois 74HC165 estão na mesma linha MISO, ambos tentam controlar a linha simultaneamente, causando: +- Corrupção de dados +- Leituras imprevisíveis +- Um dispositivo "vencendo" sobre o outro + +--- + +## Solução: Usar Barramento SPI Separado + +### Nova Configuração de Hardware + +| Dispositivo | Função | Pino | Barramento | +|-------------|--------|------|------------| +| **Câmbio** | SCK | PB13 | SPI2 | +| **Câmbio** | MISO | PB14 | SPI2 | +| **Câmbio** | CS | PB12 | SPI2_SS1 | +| **Aro** | SCK | PC10 | SPI3 | +| **Aro** | MISO | PC11 | SPI3 | +| **Aro** | CS | PA15 | SPI3_SS1 | +| **Aro** | VCC | 3.3V | - | +| **Aro** | GND | GND | - | + +--- + +## Modificações no Firmware + +1. **Criada classe `SPI_Buttons_3`** para usar o SPI3 +2. **Implementada leitura síncrona** com pulso manual do CS (o DMA tinha problemas de timing no SPI3) +3. **Configuração hardcoded** para 8 botões, modo 74HC165, CS 1 (PA15) +4. **Configurado SPI3** com os parâmetros corretos (CLKPolarity, CLKPhase, BaudRate) + +--- + +## Resultado Final + +| Componente | Status | Botões no Windows | +|------------|--------|-------------------| +| Câmbio G27 (SPI2) | ✅ Funcionando | Botões 1-19 | +| Aro G27 (SPI3) | ✅ Funcionando | Botões 20-27 | + +--- + +## Lição Aprendida + +**Registradores 74HC165 não podem compartilhar a linha MISO do SPI** devido à falta de saída tri-state. Cada 74HC165 (ou grupo em cadeia) precisa de: +- Um **barramento SPI dedicado**, ou +- Um **buffer tri-state externo** (como 74HC125) na linha MISO + +--- + +*Report Date: January 2026* +*Board: Generic STM32F407VET6* +*Components: G27 Wheel Rim (8 buttons) + G27 Shifter* diff --git a/doc/logitech g27/README.md b/doc/logitech g27/README.md new file mode 100644 index 000000000..764ef1863 --- /dev/null +++ b/doc/logitech g27/README.md @@ -0,0 +1,161 @@ +# Logitech G27 Support for OpenFFBoard + +# Suporte ao Logitech G27 para OpenFFBoard + +--- + +## 📋 Overview / Visão Geral + +This folder contains documentation and guides for integrating a **Logitech G27 Racing Wheel** with **OpenFFBoard** firmware. + +Esta pasta contém documentação e guias para integrar um **Volante Logitech G27** com o firmware **OpenFFBoard**. + +--- + +## 🛒 Hardware Requirements / Requisitos de Hardware + +### Essential / Essencial + +| Component | Description | Example | +|-----------|-------------|---------| +| **STM32F407 Board** | Main controller | [STM32F407VET6 (AliExpress)](http://pt.aliexpress.com/item/1005006882009420.html) | +| **H-Bridge Driver** | Motor driver for FFB | BTS7960 43A | +| **Power Supply** | 12-24V for motor | 12V 5A minimum | +| **USB Cable** | Micro USB or USB-C | For STM32 connection | + +### From G27 / Do G27 + +| Component | What to use | +|-----------|-------------| +| **Motor** | Original G27 DC motor | +| **Encoder** | Original optical encoder (600 PPR) or upgrade to MT6835 | +| **Pedals** | Original 3-axis potentiometers | +| **Shifter** | Original H-pattern with 2x 74HC165 | +| **Wheel Rim** | Original buttons with 1x 74HC165 | + +### Optional Upgrades / Upgrades Opcionais + +| Component | Description | +|-----------|-------------| +| **MT6835 Encoder** | High resolution magnetic encoder (65535 CPR) | +| **3D Printed Adapter** | [Thingiverse 1270001](https://www.thingiverse.com/thing:1270001) | + +--- + +## ⚠️ Important Notes / Notas Importantes + +### STM32F407VET6 Board Fix + +> **EN:** Generic STM32F407VET6 boards require a **10kΩ pull-up resistor between PA9 and 5V** for the F407VG firmware to boot correctly. +> +> **PT:** Placas genéricas STM32F407VET6 requerem um **resistor pull-up de 10kΩ entre PA9 e 5V** para o firmware F407VG iniciar corretamente. + +### Wheel Rim + Shifter Together + +> **EN:** The G27 wheel rim and shifter both use 74HC165 shift registers which **cannot share the same SPI bus** due to lack of tri-state output. **PR #182 (upstream)** includes only the G27 shifter fix in ShifterAnalog. The wheel rim via SPI3 is a fork-only workaround available in tag **`g27-full-working`** — use it to flash if you need both shifter + wheel rim working today. +> +> **PT:** O aro e o câmbio do G27 usam 74HC165 que **não podem compartilhar o mesmo barramento SPI** devido à falta de saída tri-state. **O PR #182 (upstream)** inclui apenas o fix do câmbio no ShifterAnalog. O aro via SPI3 é um workaround de fork disponível na tag **`g27-full-working`** — use-a para gravar se você precisa do câmbio + aro funcionando hoje. + +--- + +## 📄 Documentation / Documentação + +### For Users / Para Usuários + +| Document | Description | +|----------|-------------| +| [**G27_COMPLETE_WIRING_GUIDE.md**](G27_COMPLETE_WIRING_GUIDE.md) | Complete wiring guide for all G27 components / Guia completo de ligações | + +### Technical Reports / Relatórios Técnicos + +| Document | Description | +|----------|-------------| +| [**G27_SHIFTER_ISSUE_REPORT.md**](G27_SHIFTER_ISSUE_REPORT.md) | Analysis of ShifterAnalog bugs and fixes / Análise dos bugs do câmbio | +| [**G27_WHEEL_RIM_BUTTONS_REPORT.md**](G27_WHEEL_RIM_BUTTONS_REPORT.md) | SPI bus conflict analysis and SPI3 workaround (fork/tag `g27-full-working` only) | + +--- + +## 🔧 Quick Start / Início Rápido + +### 1. Flash Firmware / Gravar Firmware + +```bash +# Using DFU mode (USB) +# 1. Hold BOOT0 button while connecting USB +# 2. Use STM32CubeProgrammer or dfu-util +dfu-util -a 0 -s 0x08000000 -D OpenFFBoard_F407VG.bin +``` + +### 2. Wire Components / Conectar Componentes + +See [G27_COMPLETE_WIRING_GUIDE.md](G27_COMPLETE_WIRING_GUIDE.md) for detailed pinout. + +| Component | Interface | Main Pins | +|-----------|-----------|-----------| +| Motor | PWM | PE9, PE11 | +| Encoder | ABN | PA0, PA1 | +| Pedals | Analog | PA2, PA3, PC3 | +| Shifter | SPI2 + Analog | PB12, PB13, PB14, PC0, PC1 | +| Wheel Rim | SPI3 (fork/tag `g27-full-working` only) | PA15, PC10, PC11 | + +### 3. Configure / Configurar + +Using OpenFFBoard Configurator: + +1. **Motor Driver**: PWM mode, Torque Curve 0.60-0.70 +2. **Encoder**: Local ABN, CPR 2400 (or 65535 for MT6835) +3. **Analog**: 3 axes for pedals +4. **Shifter**: ShifterAnalog, Mode G27 H-pattern +5. **Wheel Rim**: SPI Buttons 3, 8 buttons, Mode 74HC165 *(fork/tag `g27-full-working` only)* + +--- + +## 🎮 Final Result / Resultado Final + +| Component | Status | Windows Output | +|-----------|--------|----------------| +| Motor | ✅ Working | Force Feedback enabled | +| Encoder | ✅ Working | Steering axis | +| Pedals | ✅ Working | 3 axes (throttle, brake, clutch) | +| Shifter Gears | ✅ Working | Buttons 1-7 | +| Shifter Buttons | ✅ Working | Buttons 8-19 | +| Wheel Rim | ✅ Working | Buttons 20-27 | + +**Total: 27 buttons + 7 gears + 3 pedal axes + FFB** + +--- + +## 🐛 Troubleshooting / Solução de Problemas + +### Firmware doesn't boot / Firmware não inicia + +- **Cause**: Missing PA9 pull-up resistor on generic boards +- **Solution**: Solder 10kΩ between PA9 and 5V + +### Shifter buttons not working / Botões do câmbio não funcionam + +- **Cause**: Original firmware SPI timing bug +- **Solution**: Use this fork with the ShifterAnalog fix + +### Wheel rim buttons not working with shifter / Botões do aro não funcionam com câmbio + +- **Cause**: 74HC165 bus contention on shared SPI +- **Solution**: Use SPI3 for wheel rim (this fork) + +### Motor doesn't move / Motor não gira + +- Check H-bridge connections and power supply +- Verify PWM pin configuration in Configurator +- Check encoder is reading position correctly + +--- + +## 📚 References / Referências + +- [OpenFFBoard Wiki](https://github.com/Ultrawipf/OpenFFBoard/wiki) +- [OpenFFBoard Discord](https://discord.gg/gHtnEcP) +- [Original Project](https://github.com/Ultrawipf/OpenFFBoard) + +--- + +*Last updated: April 2026*