From f7114aad22dc01234a39d1eef805006a97d67d69 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 26 Aug 2024 11:46:03 +0200 Subject: [PATCH 01/61] Command interface buffer size changes --- Firmware/FFBoard/Inc/CmdParser.h | 2 ++ Firmware/FFBoard/Inc/CommandInterface.h | 4 ++-- Firmware/FFBoard/Src/CmdParser.cpp | 8 ++++---- Firmware/FFBoard/Src/CommandInterface.cpp | 2 +- .../FFBoard/UserExtensions/Src/FFBoardMain.cpp | 2 +- .../UserExtensions/Src/usb_descriptors.cpp | 15 +++++++-------- 6 files changed, 17 insertions(+), 16 deletions(-) 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/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/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/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/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/usb_descriptors.cpp b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp index 442758d04..ff31cf8ba 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" @@ -51,7 +50,7 @@ const uint8_t usb_cdc_conf[] = TUD_CONFIG_DESCRIPTOR(1, 2, 0, (TUD_CONFIG_DESC_LEN + TUD_CDC_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), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, (TUD_OPT_HIGH_SPEED ? 512 : 64)), }; // Composite CDC and HID @@ -62,7 +61,7 @@ const uint8_t usb_cdc_hid_conf_1axis[] = 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), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, (TUD_OPT_HIGH_SPEED ? 512 : 64)), // 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), @@ -77,10 +76,10 @@ const uint8_t usb_cdc_hid_conf_2axis[] = 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), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, (TUD_OPT_HIGH_SPEED ? 512 : 64)), // 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), + TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, USB_HID_2FFB_REPORT_DESC_SIZE, 0x83, 0x02,64, HID_BINTERVAL), }; #endif @@ -92,10 +91,10 @@ const uint8_t usb_cdc_hid_conf_gamepad[] = 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), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, (TUD_OPT_HIGH_SPEED ? 512 : 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), + TUD_HID_INOUT_DESCRIPTOR(2, 5, HID_ITF_PROTOCOL_NONE, USB_HID_GAMEPAD_REPORT_DESC_SIZE, 0x83, 0x02, (TUD_OPT_HIGH_SPEED ? 512 : 64), HID_BINTERVAL), }; #endif @@ -105,7 +104,7 @@ 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), // 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), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, (TUD_OPT_HIGH_SPEED ? 512 : 64)), // Interface number, string index, EP Out & EP In address, EP size TUD_MIDI_DESCRIPTOR(2, 6, 0x02, 0x83, 64) }; From b33edde1e03d0bb60091f684842579392dbe0205 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 28 Sep 2023 13:38:47 +0200 Subject: [PATCH 02/61] SPI allow speed range --- Firmware/FFBoard/Inc/SPI.h | 3 ++- Firmware/FFBoard/Src/SPI.cpp | 4 ++-- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) 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/Src/SPI.cpp b/Firmware/FFBoard/Src/SPI.cpp index 37185ea27..206ab2693 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; } diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 3b331632a..14b482e09 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -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; @@ -261,7 +261,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; } @@ -3222,7 +3222,7 @@ void TMC4671::setUpExtEncTimer(){ // 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->PSC = ((TIM_TMC_BCLK)/1000000) +1; // 1µs ticks this->externalEncoderTimer->Instance->CR1 = 1; HAL_TIM_Base_Start_IT(this->externalEncoderTimer); #endif From fd7c1b3c923605d002917b653932ea23ae856e6d Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sat, 21 Oct 2023 10:41:41 +0200 Subject: [PATCH 03/61] Task delay in i2c eeprom --- Firmware/FFBoard/Src/flash_helpers.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Firmware/FFBoard/Src/flash_helpers.cpp b/Firmware/FFBoard/Src/flash_helpers.cpp index cc8256ec0..4b0c2d051 100644 --- a/Firmware/FFBoard/Src/flash_helpers.cpp +++ b/Firmware/FFBoard/Src/flash_helpers.cpp @@ -121,7 +121,7 @@ bool Flash_Write(uint16_t adr,uint16_t dat){ 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); + vTaskDelay(1); } uint16_t wLen = std::min(dataLength,I2C_EEPROM_PAGEWRITE_SIZE - (adr % I2C_EEPROM_PAGEWRITE_SIZE)); @@ -147,7 +147,8 @@ 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); +// HAL_Delay(1); + 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; @@ -180,7 +181,7 @@ bool Flash_Format(){ flag = false; }else{ while(!HAL_I2C_IsDeviceReady(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT) == HAL_OK){ - HAL_Delay(1); + vTaskDelay(1); } } } From a6aa0e198da81f9b5329ec3a9baf4997b9b2d24f Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 23 Oct 2023 18:18:19 +0200 Subject: [PATCH 04/61] Dynamic biss-c speed presets --- Firmware/FFBoard/UserExtensions/Src/EncoderBissC.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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; } From 7b994440be4522cdb520be0d686a7f2551e3fed2 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Fri, 8 Dec 2023 18:38:25 +0100 Subject: [PATCH 05/61] Add force erase flag for debugging --- Firmware/FFBoard/Src/cppmain.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firmware/FFBoard/Src/cppmain.cpp b/Firmware/FFBoard/Src/cppmain.cpp index aa0f9a76d..cc760ff07 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,10 @@ void cppmain() { Error_Handler(); } + if(forceErase){ + Flash_Format(); + } + // // Check if flash is initialized // uint16_t lastVersion = 0; // if(!Flash_Read(ADR_SW_VERSION, &lastVersion)){ // Version never written From ca8468fa14e850cdd2d97a9cf1320286bdd5ef4c Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 23 Jan 2024 19:45:35 +0100 Subject: [PATCH 06/61] Set initial spi device config to actual current port config --- Firmware/FFBoard/Src/SPI.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/FFBoard/Src/SPI.cpp b/Firmware/FFBoard/Src/SPI.cpp index 206ab2693..937cae4a8 100644 --- a/Firmware/FFBoard/Src/SPI.cpp +++ b/Firmware/FFBoard/Src/SPI.cpp @@ -328,9 +328,11 @@ std::pair SPIPort::getClosestPrescaler(float clock,float min,flo 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() { From 67fb5fd829edc04291e3f75b1ec3b90311e762e4 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 30 Jan 2024 13:47:41 +0100 Subject: [PATCH 07/61] Add analog encoder cal skip option --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 3 ++- .../FFBoard/UserExtensions/Src/TMC4671.cpp | 26 +++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index dd19e51fb..6afbd9114 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -97,6 +97,7 @@ struct TMC4671HardwareTypeConf{ uint8_t bbm = 20; float fluxDissipationScaler = 0.5; bool allowFluxDissipationDeactivation = true; + bool analogEncoderSkipCal = false; // Todo restrict allowed motor and encoder types }; @@ -237,7 +238,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 diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 14b482e09..8d9afb32d 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -651,7 +651,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.analogEncoderSkipCal){ + calibrateAenc(); + } }else if(conf.motconf.enctype == EncoderType_TMC::ext){ estimateExtEnc(); } @@ -1447,7 +1449,9 @@ void TMC4671::encoderInit(){ setPosSel(PosSelection::PhiM_aenc); // Mechanical Angle setVelSel(VelSelection::PhiM_aenc); // Mechanical Angle (RPM) //setup_AENC(aencconf); - calibrateAenc(); + if(!conf.hwconf.analogEncoderSkipCal){ + calibrateAenc(); + } } // find index @@ -2707,7 +2711,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 50900, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 50 // DMTH8003SPS need longer deadtime + .bbm = 50, // DMTH8003SPS need longer deadtime + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; break; @@ -2727,7 +2732,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 50900, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 40 + .bbm = 40, + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; break; @@ -2748,7 +2754,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 50900, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 + .bbm = 20, + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; break; @@ -2769,7 +2776,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 50900, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 + .bbm = 20, + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; break; @@ -2790,7 +2798,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 50900, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 + .bbm = 20, + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; // Activates around 60V as last resort failsave. Check offsets from tmc leakage. ~ 1.426V @@ -2813,7 +2822,8 @@ void TMC4671::setHwType(TMC_HW_Ver type){ .brakeLimHigh = 52800, .vmScaler = (2.5 / 0x7fff) * ((1.5+71.5)/1.5), .vSenseMult = VOLTAGE_MULT_DEFAULT, - .bbm = 20 + .bbm = 20, + .analogEncoderSkipCal = false }; this->conf.hwconf = newHwConf; From f940e34ddb0c8aebcda4d46a8236bdebfc658b34 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 7 Feb 2024 17:37:28 +0100 Subject: [PATCH 08/61] .gitignore added Debug --- Firmware/.gitignore | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 From 4dd799e2608aaaeb166c60c481ac07c3ab2d294e Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 20 Feb 2024 17:18:29 +0100 Subject: [PATCH 09/61] TMC4671 restructured hw conf constants --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 112 +++++--- .../FFBoard/UserExtensions/Src/TMC4671.cpp | 245 +++++------------- .../UserExtensions/Src/TMC4671_configs.cpp | 117 +++++++++ 3 files changed, 268 insertions(+), 206 deletions(-) create mode 100644 Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index 6afbd9114..fb5563b33 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,20 @@ 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 + + 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 +60,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 +68,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,24 +85,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 = 0; + uint16_t brakeLimHigh = 0; 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; - bool analogEncoderSkipCal = false; - // 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; + } + } }; @@ -158,7 +208,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; @@ -347,7 +396,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); @@ -571,6 +620,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"); @@ -601,6 +652,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; @@ -636,6 +688,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/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 8d9afb32d..f59aa92b0 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) { @@ -347,7 +347,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 +358,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 +549,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(); } @@ -651,7 +651,7 @@ 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){ - if(!conf.hwconf.analogEncoderSkipCal){ + if(!conf.hwconf.flags.analog_enc_skip_cal){ calibrateAenc(); } }else if(conf.motconf.enctype == EncoderType_TMC::ext){ @@ -1364,8 +1364,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(); @@ -1404,7 +1404,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; @@ -1415,7 +1415,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; } @@ -1449,7 +1449,7 @@ void TMC4671::encoderInit(){ setPosSel(PosSelection::PhiM_aenc); // Mechanical Angle setVelSel(VelSelection::PhiM_aenc); // Mechanical Angle (RPM) //setup_AENC(aencconf); - if(!conf.hwconf.analogEncoderSkipCal){ + if(!conf.hwconf.flags.analog_enc_skip_cal){ calibrateAenc(); } } @@ -1516,9 +1516,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; @@ -2011,14 +2012,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); @@ -2686,167 +2692,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 - .analogEncoderSkipCal = false - }; - 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, - .analogEncoderSkipCal = false - }; - 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, - .analogEncoderSkipCal = false - }; - 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, - .analogEncoderSkipCal = false - }; - 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, - .analogEncoderSkipCal = false - }; - 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, - .analogEncoderSkipCal = false - }; - 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(){ @@ -2933,10 +2819,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; @@ -2946,11 +2838,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; @@ -2958,16 +2854,11 @@ CommandStatus TMC4671::command(const ParsedCommand& cmd,std::vector({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 From 6fbe1a510c1be06dd79bfb2918ff7feb7f967364 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 22 Feb 2024 12:05:40 +0100 Subject: [PATCH 10/61] TMC allow autotuning I term definition --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 4 ++++ Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index fb5563b33..425f76ae3 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -50,6 +50,10 @@ extern TIM_HandleTypeDef TIM_TMC; #ifndef TMC4671_DEFAULT_ANALOGENC_SKIPCAL #define TMC4671_DEFAULT_ANALOGENC_SKIPCAL 0 #endif +#ifndef TMC4671_ITUNE_CUTOFF +#define TMC4671_ITUNE_CUTOFF 0.04 +#endif + enum class TMC_ControlState : uint32_t {uninitialized,waitPower,Shutdown,Running,EncoderInit,EncoderFinished,HardError,OverTemp,IndexSearch,FullCalibration,ExternalEncoderInit,Pidautotune}; diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index f59aa92b0..38ef00d46 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -726,7 +726,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; From ed6567666e55c1e7883a2ac25ab8dc2ecefef98a Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 22 Feb 2024 12:06:23 +0100 Subject: [PATCH 11/61] Commandhandler split register command template and prepared overriding Saves memory on smaller template function --- Firmware/FFBoard/Inc/CommandHandler.h | 27 ++++++++++++------- Firmware/FFBoard/Src/CommandHandler.cpp | 36 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 9 deletions(-) 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/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 +} From 5febed31c7119233a5529c806b6cc38dab8599e3 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 26 Feb 2024 16:46:00 +0100 Subject: [PATCH 12/61] Make FFB capable axes optionally report 32b gamepad values --- Firmware/FFBoard/Inc/AxesManager.h | 2 -- Firmware/FFBoard/Inc/Axis.h | 2 +- Firmware/FFBoard/Inc/EffectsCalculator.h | 2 +- Firmware/FFBoard/Inc/ffb_defs.h | 17 +++++++++-- Firmware/FFBoard/Src/Axis.cpp | 26 +++++++++-------- Firmware/FFBoard/Src/EffectsCalculator.cpp | 6 ++-- .../UserExtensions/Inc/usb_hid_ffb_desc.h | 28 ++++++++++++++++++- .../FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 4 +++ .../UserExtensions/Src/usb_hid_1ffb_desc.c | 13 +++++++++ .../UserExtensions/Src/usb_hid_2ffb_desc.c | 13 +++++++++ .../UserExtensions/Src/usb_hid_gamepad.c | 13 +++++++++ 11 files changed, 104 insertions(+), 22 deletions(-) diff --git a/Firmware/FFBoard/Inc/AxesManager.h b/Firmware/FFBoard/Inc/AxesManager.h index f1b9e3762..46527d225 100644 --- a/Firmware/FFBoard/Inc/AxesManager.h +++ b/Firmware/FFBoard/Inc/AxesManager.h @@ -49,8 +49,6 @@ class AxesManager 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..0fdfcb7bc 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 diff --git a/Firmware/FFBoard/Inc/EffectsCalculator.h b/Firmware/FFBoard/Inc/EffectsCalculator.h index e065c0335..3728f6cca 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); diff --git a/Firmware/FFBoard/Inc/ffb_defs.h b/Firmware/FFBoard/Inc/ffb_defs.h index e78b1e6fd..c5596b073 100644 --- a/Firmware/FFBoard/Inc/ffb_defs.h +++ b/Firmware/FFBoard/Inc/ffb_defs.h @@ -96,15 +96,25 @@ // Only include these for cpp #ifdef __cplusplus -// HID gamepad report - struct __attribute__((__packed__)) reportHID_t { uint8_t id = 1; uint64_t buttons = 0; +#if defined(HIDAXISRES_32B) && MAX_AXIS >= 1 + int32_t X = 0; +#else int16_t X = 0; +#endif +#if defined(HIDAXISRES_32B) && MAX_AXIS >= 2 + int32_t Y = 0; +#else int16_t Y = 0; +#endif +#if defined(HIDAXISRES_32B) && MAX_AXIS >= 3 + int32_t Z = 0; +#else int16_t Z = 0; +#endif int16_t RX = 0; int16_t RY = 0; int16_t RZ = 0; @@ -115,7 +125,8 @@ struct __attribute__((__packed__)) reportHID_t { /* * Helper function to access analog axes in packed HID report struct */ -inline void setHidReportAxis(reportHID_t *report, uint8_t idx, int16_t val){ +inline void setHidReportAxis(reportHID_t *report, uint8_t idx, uint32_t val){ + switch(idx){ case 0: report->X = val; diff --git a/Firmware/FFBoard/Src/Axis.cpp b/Firmware/FFBoard/Src/Axis.cpp index 2a61616b4..f3855525e 100644 --- a/Firmware/FFBoard/Src/Axis.cpp +++ b/Firmware/FFBoard/Src/Axis.cpp @@ -553,17 +553,21 @@ 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); +#if defined(HIDAXISRES_32B) + return clip(metric.current.pos_f * 0x7fffffff,-0x7fffffff,0x7fffffff); // Calc from float pos +#else + return clip(metric.current.pos_scaled_16b,-0x7fff,0x7fff); +#endif } /** * 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 +642,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,7 +657,7 @@ 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 @@ -692,7 +696,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 +706,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; } @@ -975,16 +979,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: diff --git a/Firmware/FFBoard/Src/EffectsCalculator.cpp b/Firmware/FFBoard/Src/EffectsCalculator.cpp index 688afb25f..a75d745c1 100644 --- a/Firmware/FFBoard/Src/EffectsCalculator.cpp +++ b/Firmware/FFBoard/Src/EffectsCalculator.cpp @@ -313,7 +313,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 +669,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)); } } diff --git a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h index d6067effe..ea9aee670 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h @@ -9,18 +9,44 @@ #define USB_INC_USB_HID_FFB_DESC_H_ #include "constants.h" +#if defined(HIDAXISRES_32B) +#define HIDDESC_32B_ENTRY(count) /* LOGICAL_MINIMUM (-7FFFFFFF)*/\ + 0x17, 0x01, 0x00, 0x00, 0x80, \ + /* LOGICAL_MAXIMUM (7FFFFFFF)*/\ + 0x27, 0xff, 0xff, 0xff, 0x7f,\ + /* REPORT_SIZE (16)*/\ + 0x75, 0x20,\ + /* REPORT_COUNT */\ + 0x95, count,\ +/* INPUT (Data,Var,Abs)*/\ + 0x81, 0x02, +#define HIDDESC_32B_ENTRY_SIZE 12 // 12 bytes extra for additional 16b definition 2B count +#endif +#if defined(HIDAXISRES_32B) +#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 + 16 +#else #define USB_HID_1FFB_REPORT_DESC_SIZE 1196 +#endif #ifdef AXIS1_FFB_HID_DESC extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; #endif -#define USB_HID_2FFB_REPORT_DESC_SIZE 1215//1213 +#if defined(HIDAXISRES_32B) +#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 + 16 +#else +#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 +#endif + #ifdef AXIS2_FFB_HID_DESC extern const uint8_t hid_2ffb_desc[USB_HID_2FFB_REPORT_DESC_SIZE]; #endif +#if defined(HIDAXISRES_32B) +#define USB_HID_GAMEPAD_REPORT_DESC_SIZE 176 + 16 +#else #define USB_HID_GAMEPAD_REPORT_DESC_SIZE 176 +#endif #ifdef FFB_HID_DESC_GAMEPAD extern const uint8_t hid_gamepad_desc[USB_HID_GAMEPAD_REPORT_DESC_SIZE]; #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 3fd91228c..0ef76ac3f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -175,8 +175,12 @@ void FFBHIDMain::send_report(){ for(int32_t val : *axes){ if(count >= analogAxisCount) break; +#ifdef HIDAXISRES_32B + if(count <= MAX_AXIS) val = val << 16; // Shift up 16 bit to fill 32b value. Primary axis is 32b +#endif setHidReportAxis(&reportHID,count++,val); } + // sourcesSem.Give(); // Fill rest for(;count Date: Mon, 4 Mar 2024 16:10:13 +0100 Subject: [PATCH 13/61] Allow changing FFB update rates Forwards new samplerate to filters. Needs presets, non timer handling, better FX samplerate resolutions --- Firmware/FFBoard/Inc/AxesManager.h | 2 + Firmware/FFBoard/Inc/Axis.h | 5 +- Firmware/FFBoard/Inc/EffectsCalculator.h | 9 +- Firmware/FFBoard/Inc/HidFFB.h | 1 + Firmware/FFBoard/Inc/SerialFFB.h | 1 + Firmware/FFBoard/Src/AxesManager.cpp | 24 +++-- Firmware/FFBoard/Src/Axis.cpp | 24 +++-- Firmware/FFBoard/Src/EffectsCalculator.cpp | 25 ++++-- Firmware/FFBoard/Src/Filters.cpp | 2 + Firmware/FFBoard/Src/HidFFB.cpp | 4 + Firmware/FFBoard/Src/SerialFFB.cpp | 4 + .../FFBoard/UserExtensions/Inc/FFBHIDMain.h | 34 +++++++- .../FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 87 ++++++++++++++++--- 13 files changed, 178 insertions(+), 44 deletions(-) diff --git a/Firmware/FFBoard/Inc/AxesManager.h b/Firmware/FFBoard/Inc/AxesManager.h index 46527d225..fb361e127 100644 --- a/Firmware/FFBoard/Inc/AxesManager.h +++ b/Firmware/FFBoard/Inc/AxesManager.h @@ -47,6 +47,8 @@ class AxesManager void emergencyStop(bool reset); void resetPosZero(); + void updateSamplerate(float newSamplerate); + private: volatile Control_t* control; std::shared_ptr effects_calc; diff --git a/Firmware/FFBoard/Inc/Axis.h b/Firmware/FFBoard/Inc/Axis.h index 0fdfcb7bc..5b044c187 100644 --- a/Firmware/FFBoard/Inc/Axis.h +++ b/Firmware/FFBoard/Inc/Axis.h @@ -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/EffectsCalculator.h b/Firmware/FFBoard/Inc/EffectsCalculator.h index 3728f6cca..feff5f0a0 100644 --- a/Firmware/FFBoard/Inc/EffectsCalculator.h +++ b/Firmware/FFBoard/Inc/EffectsCalculator.h @@ -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/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/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 f3855525e..cc9c717bc 100644 --- a/Firmware/FFBoard/Src/Axis.cpp +++ b/Firmware/FFBoard/Src/Axis.cpp @@ -661,9 +661,9 @@ void Axis::updateMetrics(float new_pos) { // pos is degrees // 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; } @@ -796,6 +796,20 @@ 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); +} + /** * Starts fading in force from start to 1 over fadeTime */ @@ -1007,11 +1021,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/EffectsCalculator.cpp b/Firmware/FFBoard/Src/EffectsCalculator.cpp index a75d745c1..6c8947919 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: @@ -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/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/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/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index 72855f793..b383b95d7 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -33,7 +33,11 @@ #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 }; @@ -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,28 @@ 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 = 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 = 0; - const uint8_t usb_report_rates[4] = {1,2,4,8}; // Maps stored hid speed to report rates +#ifndef TIM_FFB + uint8_t ffb_rate_divider = 0; +#endif + + + struct FFB_update_rates{ + struct FFB_update_rate_divider{ + uint8_t basediv; + uint8_t hiddiv; + }; +#if TUD_OPT_HIGH_SPEED // divider pair + 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 + uint32_t basefreq = 1000; + std::array dividers = {{{1,1},{2,1},{4,1},{8,1}}}; // 8 entries max. 1khz to 125hz +#endif + }; + //const uint8_t usb_report_rates[4] = {1,2,4,8}; // Maps stored hid speed to report rates + const static FFB_update_rates ffbrates; std::string usb_report_rates_names(); diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 0ef76ac3f..28ac4e58f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -14,6 +14,14 @@ #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 + ////////////////////////////////////////////// @@ -76,7 +84,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 +98,16 @@ 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){ +#ifndef TIM_FFB Delay(1); +#else + WaitForNotification(); +#endif updateControl(); } } @@ -203,37 +218,74 @@ void FFBHIDMain::send_report(){ } +/** + * 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 @@ -257,6 +309,7 @@ void FFBHIDMain::usbResume(){ control.emergency = false; } #endif + setReportRate(this->usb_report_rate_idx); control.usb_disabled = false; axes_manager->usbResume(); } @@ -305,3 +358,11 @@ void FFBHIDMain::errorCallback(const Error &error, bool cleared){ } } +#ifdef TIM_FFB +void FFBHIDMain::timerElapsed(TIM_HandleTypeDef* htim){ + if(htim == &TIM_FFB){ + NotifyFromISR(); + } +} +#endif + From c2f784e0e2b01bcca7b2c4612a6835a797ec7946 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 6 Mar 2024 11:57:37 +0100 Subject: [PATCH 14/61] Added tasklist --- Firmware/FFBoard/Inc/SystemCommands.h | 2 +- Firmware/FFBoard/Src/SystemCommands.cpp | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) 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/Src/SystemCommands.cpp b/Firmware/FFBoard/Src/SystemCommands.cpp index c42b0918a..9874be82f 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,14 +241,23 @@ 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]; + char repl[uxTaskGetNumberOfTasks()*64]; vTaskGetRunTimeStats(repl); replies.emplace_back("\n"+std::string(repl)); break; } +#endif +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + case FFBoardMain_commands::tasklist: + { + char repl[uxTaskGetNumberOfTasks()*64]; + vTaskList(repl); + replies.emplace_back("\n"+std::string(repl)); + break; + } #endif case FFBoardMain_commands::lsactive: { From 862b383b7cf2fb2df6328da67a0bd67f2830d805 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 6 Mar 2024 15:07:38 +0100 Subject: [PATCH 15/61] Periodic effect sampling using microsecond counter --- Firmware/FFBoard/Src/EffectsCalculator.cpp | 32 +++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Firmware/FFBoard/Src/EffectsCalculator.cpp b/Firmware/FFBoard/Src/EffectsCalculator.cpp index 6c8947919..5684fd975 100644 --- a/Firmware/FFBoard/Src/EffectsCalculator.cpp +++ b/Firmware/FFBoard/Src/EffectsCalculator.cpp @@ -188,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; @@ -206,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); @@ -229,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; @@ -247,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; @@ -264,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; From ffde94f1673ea2a480f35b53b5c30f7898a136e1 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 6 Mar 2024 15:22:03 +0100 Subject: [PATCH 16/61] Corrected tmc hw config defines --- Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp index 8dfd8c4c0..1062d05f6 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp @@ -4,10 +4,12 @@ * Created on: Feb 20, 2024 * Author: Yannick */ + #include "TMC4671.h" #include "constants.h" +#include "span" -#ifndef TMC4671_OVERRIDE_HWCONFS +#if !defined(TMC4671_OVERRIDE_HWCONFS) && defined(TMC4671DRIVER) // Default configs for officially supported hardware From b4497d537306bb0cf2d36d68fcebd480d6d03532 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 12 Mar 2024 15:34:35 +0100 Subject: [PATCH 17/61] TMC4671 corrected brake pin settings. Must be 0xffff to keep pin off. --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 4 ++-- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index 425f76ae3..e418f4377 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -104,8 +104,8 @@ struct TMC4671HardwareTypeConf{ float currentScaler = TMC4671_DEFAULT_CURRENT_SCALER; // Converts from adc counts to current in Amps - uint16_t brakeLimLow = 0; - uint16_t brakeLimHigh = 0; + 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 = TMC4671_DEFAULT_CLOCKFREQ; diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 38ef00d46..5dc16bf74 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -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)){ @@ -2060,7 +2063,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){ @@ -2272,7 +2279,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); From b9aeefebbbabac63f7b11e7e23a487da0d7997a5 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 19 Mar 2024 12:20:39 +0100 Subject: [PATCH 18/61] Removed micros timer from canbridge --- Firmware/FFBoard/UserExtensions/Src/CanBridge.cpp | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) 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), From 2e368c3ef122e1574b4fc280e0966131661a310a Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 19 Mar 2024 12:23:50 +0100 Subject: [PATCH 19/61] Default ffb rate option --- Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index b383b95d7..d3e4fa6a0 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -101,9 +101,9 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto */ void setReportRate(uint8_t rateidx); 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 = 0; + uint8_t usb_report_rate_idx = ffbrates.defaultmode; #ifndef TIM_FFB - uint8_t ffb_rate_divider = 0; + uint8_t ffb_rate_divider = 0; // TODO support ffb without timers again #endif @@ -112,15 +112,17 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto 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 uint8_t usb_report_rates[4] = {1,2,4,8}; // Maps stored hid speed to report rates const static FFB_update_rates ffbrates; std::string usb_report_rates_names(); From 8a4683fa86a7598358525d49020e36a7f72cca71 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 2 Apr 2024 12:14:43 +0200 Subject: [PATCH 20/61] Axis always returns a 32b value. mainclass does the scaling in 16b mode. --- Firmware/FFBoard/Src/Axis.cpp | 4 ---- Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Firmware/FFBoard/Src/Axis.cpp b/Firmware/FFBoard/Src/Axis.cpp index cc9c717bc..400421dee 100644 --- a/Firmware/FFBoard/Src/Axis.cpp +++ b/Firmware/FFBoard/Src/Axis.cpp @@ -556,11 +556,7 @@ metric_t* Axis::getMetrics() { * Returns position as int scaled to gamepad range */ int32_t Axis::getLastScaledEnc() { -#if defined(HIDAXISRES_32B) return clip(metric.current.pos_f * 0x7fffffff,-0x7fffffff,0x7fffffff); // Calc from float pos -#else - return clip(metric.current.pos_scaled_16b,-0x7fff,0x7fff); -#endif } /** diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 28ac4e58f..fe957e2a1 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -182,6 +182,9 @@ void FFBHIDMain::send_report(){ std::vector* axes = axes_manager->getAxisValues(); uint8_t count = 0; for(auto val : *axes){ +#if !defined(HIDAXISRES_32B) + val = val >> 16; // Scale to 16b +#endif setHidReportAxis(&reportHID,count++,val); } From 29ddbd3d6b795e1c33427fe249645ad24a9da4db Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 2 Apr 2024 12:15:13 +0200 Subject: [PATCH 21/61] Prepared usb descriptor splitting. # Conflicts: # Firmware/FFBoard/UserExtensions/Inc/usb_descriptors.h # Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp --- .../FFBoard/UserExtensions/Inc/FFBHIDMain.h | 4 +- .../UserExtensions/Inc/usb_descriptors.h | 14 +- .../UserExtensions/Inc/usb_hid_ffb_desc.h | 754 +++++++++- .../FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 15 +- .../UserExtensions/Src/usb_descriptors.cpp | 48 +- .../UserExtensions/Src/usb_hid_1ffb_desc.c | 697 +++------ .../UserExtensions/Src/usb_hid_2ffb_desc.c | 1283 +++++++++-------- .../UserExtensions/Src/usb_hid_gamepad.c | 131 +- 8 files changed, 1627 insertions(+), 1319 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index d3e4fa6a0..bdb0bd200 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -43,7 +43,7 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto }; 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); @@ -125,6 +125,8 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto }; const static FFB_update_rates ffbrates; + const bool hidAxis32b; + std::string usb_report_rates_names(); uint8_t report_rate_cnt = 0; 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 ea9aee670..61b3fccfd 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h @@ -8,8 +8,28 @@ #ifndef USB_INC_USB_HID_FFB_DESC_H_ #define USB_INC_USB_HID_FFB_DESC_H_ #include "constants.h" +#include "hid.h" -#if defined(HIDAXISRES_32B) +//#if defined(HIDAXISRES_32B) +//#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 + 16 +//#else +//#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 +//#endif +//#ifdef AXIS1_FFB_HID_DESC +//extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; +//#endif + +//#if defined(HIDAXISRES_32B) +//#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 + 16 +//#else +//#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 +//#endif + + + + + +// HID descriptor building blocks #define HIDDESC_32B_ENTRY(count) /* LOGICAL_MINIMUM (-7FFFFFFF)*/\ 0x17, 0x01, 0x00, 0x00, 0x80, \ /* LOGICAL_MAXIMUM (7FFFFFFF)*/\ @@ -21,34 +41,738 @@ /* INPUT (Data,Var,Abs)*/\ 0x81, 0x02, #define HIDDESC_32B_ENTRY_SIZE 12 // 12 bytes extra for additional 16b definition 2B count -#endif -#if defined(HIDAXISRES_32B) -#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 + 16 + +#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 USB_HID_1FFB_REPORT_DESC_SIZE 1196 +#define HIDDESC_GPENTRY_32B_1 0x09, HID_USAGE_DESKTOP_X, /* USAGE (X)*/ #endif -#ifdef AXIS1_FFB_HID_DESC -extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; +#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 defined(HIDAXISRES_32B) -#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 + 16 +#if MAX_AXIS == 3 +#define HIDDESC_GPENTRY_32B_3 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/\ + HIDDESC_32B_ENTRY(0x02) #else -#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 +#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 + + #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 -#if defined(HIDAXISRES_32B) -#define USB_HID_GAMEPAD_REPORT_DESC_SIZE 176 + 16 -#else -#define USB_HID_GAMEPAD_REPORT_DESC_SIZE 176 +#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 + + #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 + #endif /* USB_INC_USB_HID_FFB_DESC_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index fe957e2a1..c50cb9e5f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -28,10 +28,10 @@ const FFBHIDMain::FFB_update_rates FFBHIDMain::ffbrates; // Default rates /** * 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) { restoreFlashDelayed(); // Load parameters @@ -182,9 +182,9 @@ void FFBHIDMain::send_report(){ std::vector* axes = axes_manager->getAxisValues(); uint8_t count = 0; for(auto val : *axes){ -#if !defined(HIDAXISRES_32B) - val = val >> 16; // Scale to 16b -#endif + if(!hidAxis32b){ + val = val >> 16; // Scale to 16b + } setHidReportAxis(&reportHID,count++,val); } @@ -193,9 +193,8 @@ void FFBHIDMain::send_report(){ for(int32_t val : *axes){ if(count >= analogAxisCount) break; -#ifdef HIDAXISRES_32B - if(count <= MAX_AXIS) val = val << 16; // Shift up 16 bit to fill 32b value. Primary axis is 32b -#endif + if(count <= MAX_AXIS && hidAxis32b) + val = val << 16; // Shift up 16 bit to fill 32b value. Primary axis is 32b setHidReportAxis(&reportHID,count++,val); } diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp index ff31cf8ba..0b692e60c 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp @@ -47,64 +47,38 @@ 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, (TUD_OPT_HIGH_SPEED ? 512 : 64)), + 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, (TUD_OPT_HIGH_SPEED ? 512 : 64)), - - // 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), -}; + const uint8_t usb_cdc_hid_conf_1axis[] ={USB_CONF_DESC_HID_CDC(USB_HID_1FFB_REPORT_DESC_SIZE,64)}; #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, (TUD_OPT_HIGH_SPEED ? 512 : 64)), - - // 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), -}; + const uint8_t usb_cdc_hid_conf_2axis[] ={USB_CONF_DESC_HID_CDC(USB_HID_2FFB_REPORT_DESC_SIZE,64)}; +#endif +#ifdef AXIS1_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, (TUD_OPT_HIGH_SPEED ? 512 : 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, (TUD_OPT_HIGH_SPEED ? 512 : 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, (TUD_OPT_HIGH_SPEED ? 512 : 64)), + TUD_CDC_DESCRIPTOR(0, 4, 0x82, 8, 0x01, 0x81, 64), // Interface number, string index, EP Out & EP In address, EP size TUD_MIDI_DESCRIPTOR(2, 6, 0x02, 0x83, 64) }; diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c b/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c index c8432e410..afbc27caa 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_1ffb_desc.c @@ -13,244 +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) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 1 - HIDDESC_32B_ENTRY(0x01) -#endif - 0x09, HID_USAGE_Y, // USAGE (Y) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 2 - HIDDESC_32B_ENTRY(0x02) -#endif - 0x09, HID_USAGE_Z, // USAGE (Z) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 3 - HIDDESC_32B_ENTRY(0x03) -#endif - 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) -#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 - - - // 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 - - subcollection Effect Type - 12 effect types, 8bit each + HIDDESC_FFB_STATEREP, - */ - 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 @@ -302,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 @@ -391,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 @@ -527,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) @@ -637,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 427dba6c7..386e5a1aa 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c @@ -12,223 +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) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 1 - HIDDESC_32B_ENTRY(0x01) -#endif - 0x09, HID_USAGE_Y, // USAGE (Y) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 2 - HIDDESC_32B_ENTRY(0x02) -#endif - 0x09, HID_USAGE_Z, // USAGE (Z) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 3 - HIDDESC_32B_ENTRY(0x03) -#endif - 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) -#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 - - - // 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: @@ -241,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 @@ -357,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 @@ -446,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_CSTMREP+FFB_ID_OFFSET, // Report ID 7 +// 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_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) @@ -550,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 + HIDDESC_FFB_SETRAMPREP, +// 0x09,0x74, // Usage Set Ramp Force Report // 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 -// 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) @@ -671,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 6f9364491..bc56f3781 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_gamepad.c @@ -16,124 +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) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 1 - HIDDESC_32B_ENTRY(0x01) -#endif - 0x09, HID_USAGE_Y, // USAGE (Y) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 2 - HIDDESC_32B_ENTRY(0x02) -#endif - 0x09, HID_USAGE_Z, // USAGE (Z) -#if defined(HIDAXISRES_32B) && MAX_AXIS == 3 - HIDDESC_32B_ENTRY(0x03) -#endif - 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) -#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 - - - // 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) + 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ + 0x09, 0x04, /* USAGE (Joystick)*/ + 0xa1, 0x01, /* COLLECTION (Application)*/ + HIDDESC_GAMEPAD_16B, - 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) + HIDDESC_CTRL_REPORTS, // HID command report support - 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 */ }; From 2b474517fcec98d4f8c82669713dce2d8f6d5a27 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 4 Apr 2024 15:31:10 +0200 Subject: [PATCH 22/61] Split HID gamepad report buffer into helper class --- Firmware/FFBoard/Inc/ffb_defs.h | 108 +++++++++++------- .../FFBoard/UserExtensions/Inc/FFBHIDMain.h | 7 +- .../FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 39 ++++--- 3 files changed, 96 insertions(+), 58 deletions(-) diff --git a/Firmware/FFBoard/Inc/ffb_defs.h b/Firmware/FFBoard/Inc/ffb_defs.h index c5596b073..f6f0a8eef 100644 --- a/Firmware/FFBoard/Inc/ffb_defs.h +++ b/Firmware/FFBoard/Inc/ffb_defs.h @@ -97,21 +97,22 @@ #ifdef __cplusplus +template struct __attribute__((__packed__)) reportHID_t { uint8_t id = 1; uint64_t buttons = 0; -#if defined(HIDAXISRES_32B) && MAX_AXIS >= 1 - int32_t X = 0; +#if MAX_AXIS >= 1 + dtypeaxis X = 0; #else int16_t X = 0; #endif -#if defined(HIDAXISRES_32B) && MAX_AXIS >= 2 - int32_t Y = 0; +#if MAX_AXIS >= 2 + dtypeaxis Y = 0; #else int16_t Y = 0; #endif -#if defined(HIDAXISRES_32B) && MAX_AXIS >= 3 - int32_t Z = 0; +#if MAX_AXIS >= 3 + dtypeaxis Z = 0; #else int16_t Z = 0; #endif @@ -121,41 +122,70 @@ struct __attribute__((__packed__)) reportHID_t { 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, uint32_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/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index bdb0bd200..736b6c81b 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -103,7 +103,8 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto 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 = 0; // TODO support ffb without timers again + uint8_t ffb_rate_divider = 0; + uint8_t ffb_rate_counter = 0; #endif @@ -137,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/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index c50cb9e5f..90e8dcec8 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -33,7 +33,13 @@ FFBHIDMain::FFBHIDMain(uint8_t axisCount,bool hidAxis32b) : SelectableInputs(ButtonSource::all_buttonsources,AnalogSource::all_analogsources), 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(); @@ -104,7 +110,10 @@ void FFBHIDMain::Run(){ #endif while(true){ #ifndef TIM_FFB - Delay(1); + while(ffb_rate_counter++ < ffb_rate_divider){ + Delay(1); + } + ffb_rate_counter = 0; #else WaitForNotification(); #endif @@ -169,12 +178,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); @@ -185,7 +193,8 @@ void FFBHIDMain::send_report(){ if(!hidAxis32b){ val = val >> 16; // Scale to 16b } - setHidReportAxis(&reportHID,count++,val); + //setHidReportAxis(&reportHID,count++,val); + reportHID->setHidReportAxis(count++, val); } // Fill remaining values with analog inputs @@ -193,29 +202,27 @@ void FFBHIDMain::send_report(){ for(int32_t val : *axes){ if(count >= analogAxisCount) break; - if(count <= MAX_AXIS && hidAxis32b) + if((count <= MAX_AXIS) && hidAxis32b) val = val << 16; // Shift up 16 bit to fill 32b value. Primary axis is 32b - setHidReportAxis(&reportHID,count++,val); + 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; } } From 3c02223c23199bcb6a315b59b27a8f372a73e567 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 8 Apr 2024 11:49:35 +0200 Subject: [PATCH 23/61] Cleanup --- .../UserExtensions/Inc/usb_hid_ffb_desc.h | 22 +++++-------------- .../UserExtensions/Src/TMC4671_configs.cpp | 4 +--- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h index 61b3fccfd..9257ceaf6 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h @@ -10,22 +10,10 @@ #include "constants.h" #include "hid.h" -//#if defined(HIDAXISRES_32B) -//#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 + 16 -//#else -//#define USB_HID_1FFB_REPORT_DESC_SIZE 1196 -//#endif -//#ifdef AXIS1_FFB_HID_DESC -//extern const uint8_t hid_1ffb_desc[USB_HID_1FFB_REPORT_DESC_SIZE]; -//#endif - -//#if defined(HIDAXISRES_32B) -//#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 + 16 -//#else -//#define USB_HID_2FFB_REPORT_DESC_SIZE 1215 -//#endif - - +/** \defgroup HID Descriptors + * \details Contains definitions of different HID descriptor parts * + * @{ + */ @@ -774,5 +762,5 @@ extern const uint8_t hid_gamepad_desc[USB_HID_GAMEPAD_REPORT_DESC_SIZE]; extern const uint8_t hid_gamepad_desc_32b[USB_HID_GAMEPAD_REPORT_DESC_32B_SIZE]; #endif - +/** @}*/ #endif /* USB_INC_USB_HID_FFB_DESC_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp index 1062d05f6..1d5cb0d0d 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671_configs.cpp @@ -71,7 +71,7 @@ const TMC4671HardwareTypeConf conf1_2_2 = { .bbm = 20, }; const TMC4671HardwareTypeConf conf1_3 = { - .name = "v1.2.2 ACS724 (66mV/A)", + .name = "v1.3 ACS724 (66mV/A)", .hwVersion = (uint8_t)TMC_HW_Ver::v1_3_66mv, .adcOffset = 0, .thermistorSettings = { @@ -101,8 +101,6 @@ const TMC4671HardwareTypeConf conf1_3 = { .enc_hall = 1, .enc_ext = 1, - .analog_enc_skip_cal = 0, - .allowFluxDissipationDeactivation = 1 } }; From bca95eb09dd0c205dc7c9fd44eda416ac5e96773 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 16 Apr 2024 14:35:14 +0200 Subject: [PATCH 24/61] I2C eeprom using port class. Requires port definition and id >=1 --- Firmware/FFBoard/Inc/cpp_target_config.h | 4 ++++ Firmware/FFBoard/Src/I2C.cpp | 18 ----------------- Firmware/FFBoard/Src/flash_helpers.cpp | 25 +++++++++++++++--------- 3 files changed, 20 insertions(+), 27 deletions(-) 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/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/flash_helpers.cpp b/Firmware/FFBoard/Src/flash_helpers.cpp index 4b0c2d051..1008df97d 100644 --- a/Firmware/FFBoard/Src/flash_helpers.cpp +++ b/Firmware/FFBoard/Src/flash_helpers.cpp @@ -107,7 +107,10 @@ 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){ uint16_t dataLength = sizeof(dat); @@ -120,12 +123,14 @@ bool Flash_Write(uint16_t adr,uint16_t dat){ // 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){ +// i2cport_int.isDeviceReady(i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false) + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 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; + res = i2cport_int.writeMem(&i2cdeveeprom, I2C_EEPROM_ADR, curAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, wLen, I2C_EEPROM_TIMEOUT, false); + //res = HAL_I2C_Mem_Write(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, curAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, wLen,I2C_EEPROM_TIMEOUT) == HAL_OK; curAdr+=wLen; if(!res){ break; @@ -146,11 +151,12 @@ bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def){ 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); + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 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; + + //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; + bool res = i2cport_int.readMem(&i2cdeveeprom, I2C_EEPROM_ADR, I2C_EEPROM_OFS+adr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, 2, I2C_EEPROM_TIMEOUT, false); if(checkempty){ bool empty = true; @@ -176,11 +182,12 @@ bool Flash_Format(){ 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; + //bool res = HAL_I2C_Mem_Write(&I2C_PORT_EEPROM, I2C_EEPROM_ADR, I2C_EEPROM_OFS+i, I2C_EEPROM_ADR_SIZE, eraseBuf.data(), std::min(I2C_EEPROM_PAGEWRITE_SIZE,I2C_EEPROM_SIZE-i),I2C_EEPROM_TIMEOUT) == HAL_OK; + bool res = i2cport_int.writeMem(&i2cdeveeprom, I2C_EEPROM_ADR, I2C_EEPROM_OFS+i, 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){ + while(!i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false)){ vTaskDelay(1); } } @@ -188,7 +195,7 @@ bool Flash_Format(){ 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 From e67f11fcf7f77d4d9f111ba89677e799924f684c Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 23 Apr 2024 14:12:52 +0200 Subject: [PATCH 25/61] TMC check if motor is set before encoder init --- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 5dc16bf74..d60c37c2f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -580,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; @@ -1170,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; } From 9843a8fffa2154c7cd00ef03442e616f3e0176dd Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 29 Apr 2024 18:11:20 +0200 Subject: [PATCH 26/61] FFBWheel supporting variable range in 32b desc mode --- Firmware/FFBoard/Inc/constants.h | 8 ++++++ .../FFBoard/UserExtensions/Inc/FFBWheel.h | 6 ++++ .../UserExtensions/Inc/usb_hid_ffb_desc.h | 22 +++++++++++---- .../FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 4 ++- .../FFBoard/UserExtensions/Src/FFBWheel.cpp | 28 +++++++++++++------ .../UserExtensions/Src/usb_descriptors.cpp | 10 ++++++- .../UserExtensions/Src/usb_hid_2ffb_desc.c | 2 +- 7 files changed, 63 insertions(+), 17 deletions(-) diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index e18918af6..04b6f8b7f 100644 --- a/Firmware/FFBoard/Inc/constants.h +++ b/Firmware/FFBoard/Inc/constants.h @@ -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 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/usb_hid_ffb_desc.h b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h index 9257ceaf6..242536eba 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h +++ b/Firmware/FFBoard/UserExtensions/Inc/usb_hid_ffb_desc.h @@ -15,20 +15,26 @@ * @{ */ - +#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)*/\ - 0x17, 0x01, 0x00, 0x00, 0x80, \ + HID_LOGICAL_MIN_N(-HIDAXISRES_32B_MAXS,3), /*0x17, 0x01, 0x00, 0x00, 0x80,*/\ /* LOGICAL_MAXIMUM (7FFFFFFF)*/\ - 0x27, 0xff, 0xff, 0xff, 0x7f,\ - /* REPORT_SIZE (16)*/\ + 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 12 // 12 bytes extra for additional 16b definition 2B count +#define HIDDESC_32B_ENTRY_SIZE 16 // 16 bytes more for range definition #define HIDDESC_GAMEPAD_16B \ @@ -74,7 +80,7 @@ #endif #if MAX_AXIS == 3 #define HIDDESC_GPENTRY_32B_3 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/\ - HIDDESC_32B_ENTRY(0x02) + HIDDESC_32B_ENTRY(0x03) #else #define HIDDESC_GPENTRY_32B_3 0x09, HID_USAGE_DESKTOP_Z, /* USAGE (Z)*/ #endif @@ -762,5 +768,9 @@ extern const uint8_t hid_gamepad_desc[USB_HID_GAMEPAD_REPORT_DESC_SIZE]; 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/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 90e8dcec8..2eea7683d 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -192,6 +192,8 @@ void FFBHIDMain::send_report(){ for(auto val : *axes){ 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); @@ -203,7 +205,7 @@ void FFBHIDMain::send_report(){ if(count >= analogAxisCount) break; if((count <= MAX_AXIS) && hidAxis32b) - val = val << 16; // Shift up 16 bit to fill 32b value. Primary axis is 32b + val = val << (HIDAXISRES_32B_BITS-16); // Shift up 16 bit to fill 32b value. Primary axis is 32b reportHID->setHidReportAxis(count++, val); } 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/usb_descriptors.cpp b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp index 0b692e60c..d199462e9 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp @@ -59,11 +59,19 @@ const uint8_t usb_cdc_conf[] = const uint8_t usb_cdc_hid_conf_1axis[] ={USB_CONF_DESC_HID_CDC(USB_HID_1FFB_REPORT_DESC_SIZE,64)}; #endif +#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[] ={USB_CONF_DESC_HID_CDC(USB_HID_2FFB_REPORT_DESC_SIZE,64)}; #endif -#ifdef AXIS1_FFB_HID_DESC_32B + +#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 diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c b/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c index 386e5a1aa..6083e7e52 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c +++ b/Firmware/FFBoard/UserExtensions/Src/usb_hid_2ffb_desc.c @@ -691,7 +691,7 @@ __ALIGN_BEGIN const uint8_t hid_2ffb_desc_32b[USB_HID_2FFB_REPORT_DESC_32B_SIZE] 0x05, 0x01, /* USAGE_PAGE (Generic Desktop)*/ 0x09, 0x04, /* USAGE (Joystick)*/ 0xa1, 0x01, /* COLLECTION (Application)*/ - HIDDESC_GAMEPAD_32B, + HIDDESC_GAMEPAD_32B, HIDDESC_CTRL_REPORTS, // HID command report support From d396f62c106bbe0d9f55380c9860680ffb47e624 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 29 Apr 2024 18:13:53 +0200 Subject: [PATCH 27/61] HID command interface using reference in loop --- Firmware/FFBoard/Src/HidCommandInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 } From da0b4391c008b824c59db35f93c472cab0907bbe Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Fri, 3 May 2024 10:56:02 +0200 Subject: [PATCH 28/61] Fixed first 16b axis being scaled incorrectly --- Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 2eea7683d..428967168 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -204,7 +204,7 @@ void FFBHIDMain::send_report(){ for(int32_t val : *axes){ if(count >= analogAxisCount) break; - if((count <= MAX_AXIS) && hidAxis32b) + 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); } From f616395b4b036c171d5bba0ff090d91c2ca6d804 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Fri, 14 Jun 2024 11:13:10 +0200 Subject: [PATCH 29/61] Axis updates friction and inertia filters on samplerate change --- Firmware/FFBoard/Src/Axis.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/FFBoard/Src/Axis.cpp b/Firmware/FFBoard/Src/Axis.cpp index 400421dee..41f487b28 100644 --- a/Firmware/FFBoard/Src/Axis.cpp +++ b/Firmware/FFBoard/Src/Axis.cpp @@ -804,6 +804,8 @@ void Axis::updateFilters(uint8_t profileId){ 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); } /** From 1cda177fc1b949679e748a1eee2783defb3959e8 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 26 Aug 2024 10:39:49 +0200 Subject: [PATCH 30/61] Allow overriding flash defaults. Requires >CPP20 --- Firmware/FFBoard/Inc/flash_helpers.h | 4 ++++ Firmware/FFBoard/Src/SystemCommands.cpp | 1 + Firmware/FFBoard/Src/cppmain.cpp | 3 +++ Firmware/FFBoard/Src/flash_helpers.cpp | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+) 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/SystemCommands.cpp b/Firmware/FFBoard/Src/SystemCommands.cpp index 9874be82f..853f46ec6 100644 --- a/Firmware/FFBoard/Src/SystemCommands.cpp +++ b/Firmware/FFBoard/Src/SystemCommands.cpp @@ -279,6 +279,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 cc760ff07..412b75efb 100644 --- a/Firmware/FFBoard/Src/cppmain.cpp +++ b/Firmware/FFBoard/Src/cppmain.cpp @@ -49,6 +49,7 @@ void cppmain() { if(forceErase){ Flash_Format(); + Flash_Write_Defaults(); } // // Check if flash is initialized @@ -64,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 1008df97d..75e0f61e0 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 @@ -291,3 +292,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 + } +} \ No newline at end of file From 15ae944deec2213ceccf1baa0305500070da29bf Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 10 Feb 2025 10:47:25 +0100 Subject: [PATCH 31/61] Wait at least 1ms if no TIM_FFB defined --- Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 428967168..344ec0044 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -110,7 +110,7 @@ void FFBHIDMain::Run(){ #endif while(true){ #ifndef TIM_FFB - while(ffb_rate_counter++ < ffb_rate_divider){ + while(ffb_rate_counter++ <= ffb_rate_divider){ Delay(1); } ffb_rate_counter = 0; From ce15222c80aa0fed0bfcb93da2b9de1d7710654c Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 26 Feb 2025 17:20:10 +0100 Subject: [PATCH 32/61] TMC4671 fix ext encoder timer not starting --- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index d60c37c2f..5f7757826 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -1856,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); } } From 6026b4c45fe8574357ca069e059331a0c65ec628 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 26 Feb 2025 17:21:55 +0100 Subject: [PATCH 33/61] TMC4671 make ext enc timer ARR a define --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 4 +++- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index e418f4377..8d5cc8cbd 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -53,7 +53,9 @@ extern TIM_HandleTypeDef TIM_TMC; #ifndef TMC4671_ITUNE_CUTOFF #define TMC4671_ITUNE_CUTOFF 0.04 #endif - +#ifndef TIM_TMC_ARR +#define TIM_TMC_ARR 200 +#endif enum class TMC_ControlState : uint32_t {uninitialized,waitPower,Shutdown,Running,EncoderInit,EncoderFinished,HardError,OverTemp,IndexSearch,FullCalibration,ExternalEncoderInit,Pidautotune}; diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 5f7757826..8e996cba1 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -3131,7 +3131,7 @@ void TMC4671::setUpExtEncTimer(){ extEncUpdater = std::make_unique(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->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); From c1dc72cd81b3d352328c71444cb15a328d4835ff Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sat, 10 Jan 2026 15:05:55 +0100 Subject: [PATCH 34/61] Fixed timerless FFB updating waiting 1ms too long --- Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 344ec0044..428967168 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -110,7 +110,7 @@ void FFBHIDMain::Run(){ #endif while(true){ #ifndef TIM_FFB - while(ffb_rate_counter++ <= ffb_rate_divider){ + while(ffb_rate_counter++ < ffb_rate_divider){ Delay(1); } ffb_rate_counter = 0; From af7885f6018f05f0e36b39ff8ee50c3f38c7cc59 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sat, 10 Jan 2026 15:22:04 +0100 Subject: [PATCH 35/61] Enabled TIM_FFB for F407VG target --- Firmware/Targets/F407VG/Core/Inc/target_constants.h | 1 + Firmware/Targets/F407VG/Core/Src/main.c | 2 +- Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Firmware/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index 860f27682..6409a522b 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -69,6 +69,7 @@ #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_FFB htim13 extern UART_HandleTypeDef huart1; #define UART_PORT_EXT huart1 // main uart port diff --git a/Firmware/Targets/F407VG/Core/Src/main.c b/Firmware/Targets/F407VG/Core/Src/main.c index b2316a867..a23a8c8ee 100644 --- a/Firmware/Targets/F407VG/Core/Src/main.c +++ b/Firmware/Targets/F407VG/Core/Src/main.c @@ -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; diff --git a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc index d97d47989..e9aab43bb 100644 --- a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc +++ b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc @@ -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 From 8d494852d8fcf75b8ce30f06be78747da102afe7 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 15 Jan 2026 12:18:59 +0100 Subject: [PATCH 36/61] Enable freertos task formatting for F407VG --- Firmware/Targets/F407VG/.cproject | 24 +++++++++++++++++-- .../.settings/stm32cubeide.project.prefs | 6 ++--- .../Targets/F407VG/Core/Inc/FreeRTOSConfig.h | 12 ++++++++++ Firmware/Targets/F407VG/Core/Src/freertos.c | 15 ++++++++++++ .../F407VG/Core/ThreadSafe/newlib_lock_glue.c | 2 +- .../F407VG/Core/ThreadSafe/stm32_lock.h | 2 +- .../Targets/F407VG/OpenFFBoard_F407VG.ioc | 4 ++-- 7 files changed, 56 insertions(+), 9 deletions(-) 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..405ce1e69 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 +2F62501ED4689FB349E356AB974DBE57=2E846C232C86F8091E1D1C41786923FD +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/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/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 e9aab43bb..8857b1e93 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 From 0d9c851594870745d7e8878b14c1c9b8ef44e379 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 15 Jan 2026 12:19:33 +0100 Subject: [PATCH 37/61] Write task info directly into string buffer --- Firmware/FFBoard/Src/SystemCommands.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Firmware/FFBoard/Src/SystemCommands.cpp b/Firmware/FFBoard/Src/SystemCommands.cpp index 853f46ec6..49b50ebd0 100644 --- a/Firmware/FFBoard/Src/SystemCommands.cpp +++ b/Firmware/FFBoard/Src/SystemCommands.cpp @@ -244,18 +244,22 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect #if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) case FFBoardMain_commands::taskstats: { - char repl[uxTaskGetNumberOfTasks()*64]; - 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: { - char repl[uxTaskGetNumberOfTasks()*64]; - vTaskList(repl); - replies.emplace_back("\n"+std::string(repl)); + 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 From 4aaecc54af8ddf4d5adc1c61e2dc3a6cacefc080 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 16 Jan 2026 09:58:57 +0100 Subject: [PATCH 38/61] Fix SPI_Buttons constructor procedure (#164) Fix SPI_Buttons constructor procedure --- .../FFBoard/UserExtensions/Src/SPIButtons.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp index f4a0512a6..a82e0d710 100644 --- a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp @@ -51,13 +51,6 @@ SPI_Buttons::SPI_Buttons(uint16_t configuration_address, uint16_t configuration_ this->configuration_address_2 = configuration_address_2; restoreFlash(); - this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[this->conf.spi_speed]; - 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); ready = true; @@ -109,18 +102,17 @@ void SPI_Buttons::setConfig(ButtonSourceConfig config){ if(conf.mode == SPI_BtnMode::TM){ this->spiConfig.cspol = true; this->conf.cutRight = true; - this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; + this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; - + this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; }else if(conf.mode == SPI_BtnMode::PISOSR){ this->spiConfig.cspol = false; this->conf.cutRight = false; + this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; this->spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE; this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; // its actually shifting on the rising edge but 165 will have the first output set even before clocking. First clock cycle is actually second bit so we sample at the falling edge and skip the first bit with that. } -// spiPort.takeSemaphore(); -// spiPort.configurePort(&this->spiConfig.peripheral); -// spiPort.giveSemaphore(); + this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[this->conf.spi_speed]; initSPI(); if(config.numButtons == 64){ // Special case mask = 0xffffffffffffffff; From cf7dd4dbf76f9b0e62126eecde4207311a7bd134 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sun, 18 Jan 2026 20:08:33 +0100 Subject: [PATCH 39/61] Version bump 1.16.7 --- Firmware/FFBoard/Inc/constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index 04b6f8b7f..ac1f0fede 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,16,7}; // Version as array. 8 bit each! #ifndef MAX_AXIS #define MAX_AXIS 2 // ONLY USE 2 for now else screws HID Reports #endif From 1b07468c4d1105b3ae6dd2b6f3598114ab4c290e Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sun, 1 Feb 2026 13:53:17 +0100 Subject: [PATCH 40/61] MT6835 corrected saved offset shift --- .../FFBoard/UserExtensions/Src/MtEncoderSPI.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp index 54148bad5..fb1024c72 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); } From 9f4699d9fe1db81ccc1a3b17952f6d96b25f5da2 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 17 Feb 2026 21:59:02 +0100 Subject: [PATCH 41/61] Funding update --- .github/FUNDING.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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" From 9367886ebedb7412649d4d9cab309cb7ba0cec66 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 25 Feb 2026 09:50:46 +0100 Subject: [PATCH 42/61] F4 DISCO set encoder timer filter to 5 --- Firmware/Targets/F407VG_DISCO/.cproject | 24 +++++++++++++++++-- Firmware/Targets/F407VG_DISCO/Core/Src/main.c | 2 +- .../Core/ThreadSafe/newlib_lock_glue.c | 2 +- .../F407VG_DISCO/Core/ThreadSafe/stm32_lock.h | 2 +- .../F407VG_DISCO/OpenFFBoard_F407VG_DISCO.ioc | 6 +++-- 5 files changed, 29 insertions(+), 7 deletions(-) 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 From a827c68cfaa7b20099e1b20322a4e91a281c1e79 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 25 Feb 2026 09:51:59 +0100 Subject: [PATCH 43/61] F407 general set encoder filter to 5 --- Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs | 2 +- Firmware/Targets/F407VG/Core/Src/main.c | 2 +- Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs b/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs index 405ce1e69..5b3603498 100644 --- a/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs +++ b/Firmware/Targets/F407VG/.settings/stm32cubeide.project.prefs @@ -1,4 +1,4 @@ -2F62501ED4689FB349E356AB974DBE57=2E846C232C86F8091E1D1C41786923FD +2F62501ED4689FB349E356AB974DBE57=E2380525C25FCA23F64F38676B957D9B 8DF89ED150041C4CBC7CB9A9CAA90856=2E846C232C86F8091E1D1C41786923FD DC22A860405A8BF2F2C095E5B6529F12=948244613B091C43745821314F11043C eclipse.preferences.version=1 diff --git a/Firmware/Targets/F407VG/Core/Src/main.c b/Firmware/Targets/F407VG/Core/Src/main.c index a23a8c8ee..9026825ba 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; diff --git a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc index 8857b1e93..59e0251c3 100644 --- a/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc +++ b/Firmware/Targets/F407VG/OpenFFBoard_F407VG.ioc @@ -887,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 From 92c327f1225e06e0d9e600003115d3cfd7b78bb2 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Thu, 26 Feb 2026 09:15:53 +0100 Subject: [PATCH 44/61] Changelog update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d6fa0308..486601a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ### Changes this version: - 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. ### Changes in 1.16: From 0767c51ed5ea4f727db3cbe79c4dc1e48e3b4fc0 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 10 Mar 2026 13:20:49 +0100 Subject: [PATCH 45/61] TMC4671 fixed encoder forwarding force inversion logic --- Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp index 8e996cba1..1d7be6823 100644 --- a/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/TMC4671.cpp @@ -1737,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 } From 6405b90caebc95b85e61b1f74266a8db167b6f7f Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 10 Mar 2026 13:21:31 +0100 Subject: [PATCH 46/61] MTenc fixed excessive delays when forwarding/multithread access --- Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h | 2 ++ Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) 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/Src/MtEncoderSPI.cpp b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp index fb1024c72..1805232ec 100644 --- a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp @@ -102,6 +102,7 @@ void MtEncoderSPI::Run(){ }else{ errors++; } + lastUpdateTick = HAL_GetTick(); waitForUpdateSem.Give(); updateInProgress = false; } @@ -264,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; } From 2f061da7fb6b5df22bade3ab6bf7cf9b54bf2fcc Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 10 Mar 2026 13:23:05 +0100 Subject: [PATCH 47/61] Reduced default TMC forwarding rate to 4khz --- Firmware/FFBoard/UserExtensions/Inc/TMC4671.h | 2 +- Firmware/Targets/F407VG/Core/Inc/target_constants.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h index 8d5cc8cbd..fdde1fa2b 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h +++ b/Firmware/FFBoard/UserExtensions/Inc/TMC4671.h @@ -54,7 +54,7 @@ extern TIM_HandleTypeDef TIM_TMC; #define TMC4671_ITUNE_CUTOFF 0.04 #endif #ifndef TIM_TMC_ARR -#define TIM_TMC_ARR 200 +#define TIM_TMC_ARR 250 #endif diff --git a/Firmware/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index 6409a522b..0cc62e9f8 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -69,6 +69,7 @@ #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; From ab16fd425f7fe4822b1e9ac7436e9138f4a893a4 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 10 Mar 2026 13:25:31 +0100 Subject: [PATCH 48/61] Changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486601a04..55af5cbe1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ - 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: From 99171b08489a4788b5f418ee7fe1d7f0deb956f2 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Mon, 16 Mar 2026 22:14:32 +0100 Subject: [PATCH 49/61] Version bump v1.17.0 --- CHANGELOG.md | 23 +---------------------- Firmware/FFBoard/Inc/constants.h | 2 +- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55af5cbe1..403f95890 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,25 +3,4 @@ - 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: - -Internal changes: -- CAN port interfaces rewritten -- HAL updated -- Migrated to CPP20 -- Upgraded to tinyusb 0.17 -- Improved microsecond counter implementation -- 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 +- Fixed TMC4671 ext encoder having inverted forces if previously inverted by dual enc mode \ No newline at end of file diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index ac1f0fede..caf039d63 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,7}; // 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 From da99aba5815d792478cd00b5a8d4f376a935bd88 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Tue, 17 Mar 2026 21:03:20 +0100 Subject: [PATCH 50/61] Release update --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8a80ca7fb..c17cdb088 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 # Create release From 5fc00dbddd227af8151610ce8dcd5efe3df6af0b Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 18 Mar 2026 13:37:36 +0100 Subject: [PATCH 51/61] Filled Changelog with past changes --- CHANGELOG.md | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 181 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 403f95890..8f2df35ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +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 \ No newline at end of file +- Fixed TMC4671 ext encoder having inverted forces if previously inverted by dual enc mode + +### 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. + +### 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 +- 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 From 48e29f3a82ecaa3fcdb3c8642e14583fbac0ed67 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 1 Apr 2026 13:08:34 +0200 Subject: [PATCH 52/61] Added I2C EEPROM ID/OTP Support --- Firmware/FFBoard/Inc/constants.h | 2 +- Firmware/FFBoard/Src/flash_helpers.cpp | 111 ++++++++++++++++++------- 2 files changed, 83 insertions(+), 30 deletions(-) diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index caf039d63..ba11df6d5 100644 --- a/Firmware/FFBoard/Inc/constants.h +++ b/Firmware/FFBoard/Inc/constants.h @@ -112,7 +112,7 @@ static const uint8_t SW_VERSION_INT[3] = {1,17,0}; // 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/Src/flash_helpers.cpp b/Firmware/FFBoard/Src/flash_helpers.cpp index 75e0f61e0..76081260f 100644 --- a/Firmware/FFBoard/Src/flash_helpers.cpp +++ b/Firmware/FFBoard/Src/flash_helpers.cpp @@ -113,25 +113,33 @@ uint8_t i2cBufferEeprom[sizeof(uint16_t)] = {0}; 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(curAdr < adrAbs+dataLength){ // i2cport_int.isDeviceReady(i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false) - while(!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 = i2cport_int.writeMem(&i2cdeveeprom, I2C_EEPROM_ADR, curAdr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, wLen, I2C_EEPROM_TIMEOUT, false); - //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; @@ -140,24 +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(!i2cport_int.isDeviceReady(&i2cdeveeprom, I2C_EEPROM_ADR, 100, I2C_EEPROM_TIMEOUT,false)){ +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; - bool res = i2cport_int.readMem(&i2cdeveeprom, I2C_EEPROM_ADR, I2C_EEPROM_OFS+adr, I2C_EEPROM_ADR_SIZE, i2cBufferEeprom, 2, I2C_EEPROM_TIMEOUT, false); + 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; @@ -174,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 */ @@ -182,9 +204,12 @@ 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; - bool res = i2cport_int.writeMem(&i2cdeveeprom, I2C_EEPROM_ADR, I2C_EEPROM_OFS+i, I2C_EEPROM_ADR_SIZE, eraseBuf.data(), std::min(I2C_EEPROM_PAGEWRITE_SIZE,I2C_EEPROM_SIZE-i), I2C_EEPROM_TIMEOUT, false); + 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{ @@ -281,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; @@ -310,4 +363,4 @@ void Flash_Write_Defaults(){ for(const std::pair &kv : flash_factory_defaults){ Flash_Write(kv.first, kv.second); // Try to write values } -} \ No newline at end of file +} From 7485d657947d8d2f6022e8ede28aa7bac6ca7241 Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 1 Apr 2026 18:45:15 +0200 Subject: [PATCH 53/61] Configurator update --- Configurator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurator b/Configurator index 9eee2b5ff..1474e31c3 160000 --- a/Configurator +++ b/Configurator @@ -1 +1 @@ -Subproject commit 9eee2b5ffe44f2dec1c6d2eae4a0ad987d53b5c8 +Subproject commit 1474e31c35a45888587ae61a4ab730bae7c35e15 From 983caa49ecea0d91bd55d6a4989dcb32720b828a Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 1 Apr 2026 19:11:06 +0200 Subject: [PATCH 54/61] Fix changelog path typo --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c17cdb088..783721603 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -82,7 +82,7 @@ jobs: echo -e "### Firmware changes\n" > /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 - perl -0777 -ne 'print "$1\n$2" if /^(#+.+)\n((?:[^#\n]+\n)+)/m; exit' ${{ github.workspace }}/Configurator/CHANGELOG.md >> /tmp/CHANGELOG + perl -0777 -ne 'print "$1\n$2" if /^(#+.+)\n((?:[^#\n]+\n)+)/m; exit' ${{ github.workspace }}/Configurator/CHANGELOG.md >> /tmp/CHANGELOG.md # Create release From cbfe54786f60a6498c4ce23624e536b070dad59e Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Wed, 8 Apr 2026 11:50:00 +0200 Subject: [PATCH 55/61] Configurator update --- Configurator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurator b/Configurator index 1474e31c3..993f3cfbd 160000 --- a/Configurator +++ b/Configurator @@ -1 +1 @@ -Subproject commit 1474e31c35a45888587ae61a4ab730bae7c35e15 +Subproject commit 993f3cfbdc6d740d220f5013852649e8695f4fe2 From 59a13502995d874a7a9541c0c5b59d10d8af0e6e Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Fri, 10 Apr 2026 11:55:17 +0200 Subject: [PATCH 56/61] Configurator update --- Configurator | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurator b/Configurator index 993f3cfbd..7e1cbb442 160000 --- a/Configurator +++ b/Configurator @@ -1 +1 @@ -Subproject commit 993f3cfbdc6d740d220f5013852649e8695f4fe2 +Subproject commit 7e1cbb4421af872deacf84ee3ba0b584c2815aa8 From cafe50cecce5948c84a0e478cf7e666300ab54bc Mon Sep 17 00:00:00 2001 From: Yannick Richter Date: Sat, 11 Apr 2026 19:03:33 +0200 Subject: [PATCH 57/61] Initialize ffb rate dividers always Fixes DISCO infinite loop in clean state --- Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h | 2 +- Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h index 736b6c81b..fa9ae121f 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h +++ b/Firmware/FFBoard/UserExtensions/Inc/FFBHIDMain.h @@ -103,7 +103,7 @@ class FFBHIDMain: public FFBoardMain, public cpp_freertos::Thread, PersistentSto 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 = 0; + uint8_t ffb_rate_divider = 1; uint8_t ffb_rate_counter = 0; #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp index 428967168..13934f2e9 100644 --- a/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/FFBHIDMain.cpp @@ -78,6 +78,8 @@ void FFBHIDMain::restoreFlash(){ if(Flash_Read(ADR_FFBWHEEL_CONF1,&conf1)){ uint8_t rateidx = conf1 & 0x3; setReportRate(rateidx); + }else{ + setReportRate(0); // default } } From f2c8df3f2320c0e397597c2573bfa896f92090cf Mon Sep 17 00:00:00 2001 From: Orlando Eduardo Pereira Date: Wed, 15 Apr 2026 11:48:00 -0300 Subject: [PATCH 58/61] Add Logitech G27 shifter fix and SPI3 support for wheel rim buttons ## ShifterAnalog G27 Mode Fix - Fix SPI clock configuration for 74HC165 (CLKPhase, CLKPolarity) - Add CS polarity configuration for active-low operation - Change to synchronous SPI read for reliable button reading - Fix CS pin index off-by-one error in setMode() and setCSPin() - Add startRead() method to trigger SPI before processing data ## SPI Buttons 3 (SPI3 Support) - Add SPI_Buttons_3 class using SPI3 peripheral for separate button source - Required because 74HC165 lacks tri-state output (cannot share MISO) - Allows G27 shifter (SPI2) and wheel rim (SPI3) to work simultaneously - SPI3 pins: PC10 (SCK), PC11 (MISO), PA15 (CS1) ## Hardware Configuration (F407VG) - Reduce SPI3 baud rate for reliable 74HC165 communication - Initialize SPI CS pins HIGH (inactive) to prevent bus contention - Enable SPIBUTTONS3 feature flag ## Documentation - Add complete wiring guide for all G27 components - Add technical reports documenting bugs and solutions - Bilingual documentation (English/Portuguese) Tested with Logitech G27 on STM32F407VET6 board. Made-with: Cursor --- .../FFBoard/UserExtensions/Inc/SPIButtons.h | 46 +- .../UserExtensions/Inc/ShifterAnalog.h | 7 +- .../UserExtensions/Inc/eeprom_addresses.h | 48 +- .../UserExtensions/Src/ButtonSources.cpp | 3 + .../FFBoard/UserExtensions/Src/SPIButtons.cpp | 189 +++++++- .../UserExtensions/Src/ShifterAnalog.cpp | 80 +++- .../UserExtensions/Src/eeprom_addresses.c | 48 +- .../F407VG/Core/Inc/target_constants.h | 4 +- .../F407VG/Core/Src/cpp_target_config.cpp | 7 +- Firmware/Targets/F407VG/Core/Src/main.c | 15 +- doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md | 453 ++++++++++++++++++ doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md | 367 ++++++++++++++ .../G27_WHEEL_RIM_BUTTONS_REPORT.md | 405 ++++++++++++++++ doc/logitech g27/README.md | 161 +++++++ 14 files changed, 1736 insertions(+), 97 deletions(-) create mode 100644 doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md create mode 100644 doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md create mode 100644 doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md create mode 100644 doc/logitech g27/README.md diff --git a/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h b/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h index 9f8f061df..b131ee941 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,14 +96,24 @@ 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; + static bool isCreatable(); +}; + +class SPI_Buttons_3 : public SPI_Buttons { +public: + SPI_Buttons_3(); const ClassIdentifier getInfo() override; static ClassIdentifier info; static bool isCreatable(); + + void restoreFlash() override; // Override to set default values + uint8_t readButtons(uint64_t* buf) override; // Override to use synchronous read for SPI3 + std::string getHelpstring() override {return "SPI 3 Button (SPI3)";} }; #endif /* SPIBUTTONS_H_ */ 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/eeprom_addresses.h b/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h index d05638476..40755abb3 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 @@ -58,6 +58,8 @@ uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data) will return 1 if #define ADR_SPI_BTN_2_CONF 0x205 #define ADR_SPI_BTN_1_CONF_2 0x206 #define ADR_SPI_BTN_2_CONF_2 0x207 +#define ADR_SPI_BTN_3_CONF 0x209 +#define ADR_SPI_BTN_3_CONF_2 0x20A #define ADR_LOCAL_BTN_CONF_3 0x208 // Pulse mask // Local encoder #define ADR_ENCLOCAL_CPR 0x210 diff --git a/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp b/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp index 554560c7f..b189dc693 100644 --- a/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp @@ -24,6 +24,9 @@ const std::vector> ButtonSource::all_buttonsources = #ifdef SPIBUTTONS2 add_class(2), #endif +#ifdef SPIBUTTONS3 + add_class(6), +#endif #ifdef SHIFTERBUTTONS add_class(3), #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp index a82e0d710..5193ce70b 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,14 +47,99 @@ 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) +} + +ClassIdentifier SPI_Buttons_3::info = { + .name = "SPI Buttons 3" , + .id=CLSID_BTN_SPI, + }; +const ClassIdentifier SPI_Buttons_3::getInfo(){ + return info; +} + +bool SPI_Buttons_3::isCreatable(){ +#ifdef EXT3_SPI_PORT + return (ext3_spi.hasFreePins()); +#else + return false; +#endif +} + +SPI_Buttons_3::SPI_Buttons_3() + : SPI_Buttons{ADR_SPI_BTN_3_CONF, ADR_SPI_BTN_3_CONF_2, &ext3_spi, 2} { + // Constructor body runs AFTER base class is fully constructed + // Now we can safely call our overridden restoreFlash() + SPI_Buttons_3::restoreFlash(); // Explicitly call our version, not virtual dispatch +} + +void SPI_Buttons_3::restoreFlash(){ + // Always apply G27 wheel rim defaults for SPI_Buttons_3 (hardcoded) + // Force these values every time, regardless of flash contents + ButtonSourceConfig config; + config.numButtons = 8; // 8 buttons on wheel rim - HARDCODED + config.mode = SPI_BtnMode::PISOSR; // 74xx165 mode - HARDCODED + config.cs_num = 1; // CS pin 1 (PA15 - SPI3_SS1) - HARDCODED + config.spi_speed = 2; // Slow speed (prescaler 64) - HARDCODED + config.invert = false; + config.cutRight = false; + + // Always apply hardcoded configuration + setConfig(config); + + // Force CS to correct value after setConfig + this->conf.cs_num = 1; + + // Explicitly ensure btnnum is set (multiple attempts for debug) + this->btnnum = 8; + ButtonSource::btnnum = 8; +} + +// SPI_Buttons_3 uses synchronous read because DMA has timing issues with SPI3 +uint8_t SPI_Buttons_3::readButtons(uint64_t* buf){ + // Copy last buffer to output + memcpy(buf, this->spi_buf, std::min(this->bytes, 8)); + process(buf); + + // Check if SPI is available + if(spiPort.isTaken()) + return this->conf.numButtons; + + // Use direct HAL_SPI_Receive which works correctly for SPI3 + SPI_HandleTypeDef* hspi = spiPort.getPortHandle(); + + // Get CS pin for this device (cs_num is 1-indexed, array is 0-indexed) + OutputPin* cs_pin = spiPort.getCsPin(this->conf.cs_num > 0 ? this->conf.cs_num - 1 : 0); + + if(cs_pin != nullptr) { + // Pulse CS low then high to latch data into 74HC165 + cs_pin->write(false); // CS LOW - load parallel data + // Small delay for latch + for(volatile int i = 0; i < 10; i++) {} + cs_pin->write(true); // CS HIGH - enable shift + } + + // Read data via SPI + HAL_SPI_Receive(hspi, spi_buf, bytes, 10); + + return this->conf.numButtons; +} + // 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 + + // 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; registerCommands(); this->setCommandsEnabled(true); @@ -77,6 +167,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); } /** @@ -95,31 +187,41 @@ 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; this->conf.cutRight = true; - this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; - this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW; + this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE; + }else if(conf.mode == SPI_BtnMode::PISOSR){ this->spiConfig.cspol = false; this->conf.cutRight = false; - this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB; this->spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE; this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; // its actually shifting on the rising edge but 165 will have the first output set even before clocking. First clock cycle is actually second bit so we sample at the falling edge and skip the first bit with that. } - this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[this->conf.spi_speed]; +// spiPort.takeSemaphore(); +// spiPort.configurePort(&this->spiConfig.peripheral); +// spiPort.giveSemaphore(); initSPI(); if(config.numButtons == 64){ // Special case mask = 0xffffffffffffffff; }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){ @@ -128,6 +230,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; } @@ -135,6 +238,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; @@ -175,12 +284,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){ @@ -199,6 +308,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{ @@ -230,6 +340,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){ @@ -242,6 +353,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){ @@ -254,6 +366,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/eeprom_addresses.c b/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c index d111d712a..0026f3d48 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, @@ -40,6 +40,8 @@ const uint16_t VirtAddVarTab[NB_OF_VAR] = ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_1_CONF_2, ADR_SPI_BTN_2_CONF_2, + ADR_SPI_BTN_3_CONF, + ADR_SPI_BTN_3_CONF_2, ADR_LOCAL_BTN_CONF_3, // Pulse mask // Local encoder ADR_ENCLOCAL_CPR, @@ -212,10 +214,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, @@ -240,6 +242,8 @@ const uint16_t exportableFlashAddresses[NB_EXPORTABLE_ADR] = ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_1_CONF_2, ADR_SPI_BTN_2_CONF_2, + ADR_SPI_BTN_3_CONF, + ADR_SPI_BTN_3_CONF_2, ADR_LOCAL_BTN_CONF_3, // Pulse mask // Local encoder ADR_ENCLOCAL_CPR, @@ -410,4 +414,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/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index 0cc62e9f8..c43d5983f 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -40,6 +40,8 @@ // Extra features #define LOCALBUTTONS #define SPIBUTTONS +#define SPIBUTTONS2 +#define SPIBUTTONS3 #define SHIFTERBUTTONS #define PCF8574BUTTONS // Requires I2C #define ANALOGAXES @@ -69,8 +71,6 @@ #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/cpp_target_config.cpp b/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp index 5b481f7f8..fe41ac90c 100644 --- a/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp +++ b/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp @@ -11,7 +11,12 @@ extern SPI_HandleTypeDef hspi1; SPIPort motor_spi{hspi1,motor_spi_cspins,84000000,false}; #ifdef EXT3_SPI_PORT -static const std::vector ext3_spi_cspins{OutputPin(*SPI3_SS1_GPIO_Port, SPI3_SS1_Pin), OutputPin(*SPI3_SS2_GPIO_Port, SPI3_SS2_Pin),OutputPin(*SPI3_SS3_GPIO_Port, SPI3_SS3_Pin)}; +// SPI3 CS pins - native SPI3 pins only +static const std::vector ext3_spi_cspins{ + OutputPin(*SPI3_SS1_GPIO_Port, SPI3_SS1_Pin), // PA15 - CS1 (used for G27 wheel rim) + 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 diff --git a/Firmware/Targets/F407VG/Core/Src/main.c b/Firmware/Targets/F407VG/Core/Src/main.c index 9026825ba..5758fdcf9 100644 --- a/Firmware/Targets/F407VG/Core/Src/main.c +++ b/Firmware/Targets/F407VG/Core/Src/main.c @@ -677,7 +677,7 @@ static void MX_SPI3_Init(void) hspi3.Init.CLKPolarity = SPI_POLARITY_LOW; hspi3.Init.CLKPhase = SPI_PHASE_1EDGE; hspi3.Init.NSS = SPI_NSS_SOFT; - hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; + hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // Slower for reliable 74HC165 communication hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi3.Init.TIMode = SPI_TIMODE_DISABLE; hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; @@ -1374,18 +1374,23 @@ 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); + // SPI CS pins initialized HIGH (inactive) - important for 74HC165 shift registers + HAL_GPIO_WritePin(GPIOD, SPI2_SS2_Pin|SPI2_SS3_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOD, SPI3_SS2_Pin|SPI3_SS3_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(GPIOD, 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); + // SPI3 CS1 initialized HIGH (inactive) - used for G27 wheel rim buttons + HAL_GPIO_WritePin(SPI3_SS1_GPIO_Port, SPI3_SS1_Pin, GPIO_PIN_SET); /*Configure GPIO pins : DIN7_Pin DIN6_Pin DIN5_Pin DIN4_Pin DIN3_Pin */ 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..eed65cc89 --- /dev/null +++ b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md @@ -0,0 +1,453 @@ +# 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 (PA6) | - | 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 (PA6) | - | 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 | Analog In | 7 | Analog X position | +| Y Axis | Analog In | 3 | Analog Y position | +| GND | GND | 9 | Ground | + +#### Configuration / Configuração +- Mode: **G27 Shifter H-pattern** (ShifterAnalog mode 2) +- CS Pin: **1** (PB12) + +### 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 | Analog In | 7 | Posição analógica X | +| Eixo Y | Analog In | 3 | Posição analógica Y | +| GND | GND | 9 | Terra | + +--- + +## Wheel Rim Buttons / Botões do Aro + +### 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 74HC165 lacks tri-state output. A **separate SPI port (SPI3)** must be used. + +| 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 SPI3? / Por que SPI3? + +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. Using a separate SPI bus (SPI3) avoids this problem. + +#### Configuration / Configuração +- Class: **SPI Buttons 3** (custom, uses SPI3) +- Buttons: **8** +- Mode: **74HC165** (PISOSR) +- CS Pin: **1** (PA15) + +### PT - Ligação dos Botões do Aro + +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 74HC165 não tem saída tri-state. Um **porta SPI separada (SPI3)** deve ser usada. + +| Função | Pino OpenFFBoard | Pino do Aro | Notas | +|--------|------------------|-------------|-------| +| VCC | 3.3V | 1 | Alimentação (3.3V!) | +| SCK | PC10 | 4 | Clock SPI3 | +| MISO | PC11 | 6 | Dados SPI3 | +| CS/Latch | PA15 | 2 | SPI3_SS1 | +| GND | GND | 7 | Terra | +| MOSI | - | 3 | Para LEDs (não usado) | +| LED Latch | - | 5 | Para LEDs (não usado) | + +#### Por que SPI3? + +O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compartilham a mesma linha MISO, eles criam conflito elétrico - ambos tentam controlar a linha simultaneamente. Usar um barramento SPI separado (SPI3) evita esse problema. + +--- + +## 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 ─────── PA6 (Analog 3) │ +│ ├── VCC ────────── 3.3V │ +│ └── GND ────────── GND │ +│ │ +│ SHIFTER (SPI2) │ +│ ├── SCK ────────── PB13 │ +│ ├── MISO ───────── PB14 │ +│ ├── CS ─────────── PB12 (SPI2_SS1) │ +│ ├── X Axis ─────── Analog Input │ +│ ├── Y Axis ─────── Analog Input │ +│ ├── 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, PA6 | 3.3V | Standard wiki connection | +| Shifter | SPI2 | PB13, PB14, PB12 | 3.3V | G27 mode | +| 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**: The wheel rim buttons require custom firmware modifications (SPI_Buttons_3 class). + +### 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..70b3dd252 --- /dev/null +++ b/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md @@ -0,0 +1,367 @@ +# 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) | +| 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) + +--- + +## 🔴 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..15f752038 --- /dev/null +++ b/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md @@ -0,0 +1,405 @@ +# G27 Wheel Rim Buttons - SPI Bus Conflict Report & Solution + +## 🎯 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 solution implemented using a separate SPI port. + +--- + +## 📋 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..fd0269499 --- /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. This fork adds **SPI3 support** to allow both to work simultaneously. +> +> **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. Este fork adiciona **suporte a SPI3** para ambos funcionarem simultaneamente. + +--- + +## 📄 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 solution / Conflito SPI e solução | + +--- + +## 🔧 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, PA6 | +| Shifter | SPI2 | PB12, PB13, PB14 | +| Wheel Rim | SPI3 | 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 + +--- + +## 🎮 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* From 8947edf3611f926390f651117158edec8f70f03c Mon Sep 17 00:00:00 2001 From: Orlando Eduardo Pereira Date: Fri, 8 May 2026 16:37:04 -0300 Subject: [PATCH 59/61] Fix incorrect analog pin mapping in documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed Clutch pedal pin from PA6 to PC3 (AIN2) to match the actual F407VG hardware configuration in main.h: - AIN0 = PA3 (Brake) - AIN1 = PA2 (Throttle) - AIN2 = PC3 (Clutch) ← was incorrectly listed as PA6 Co-authored-by: Cursor --- doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md | 8 ++++---- doc/logitech g27/README.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md index eed65cc89..1843dfe16 100644 --- a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md +++ b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md @@ -163,7 +163,7 @@ The G27 pedals use **3 potentiometers** for throttle, brake, and clutch. This co |----------|-----------------|----------------|-------| | Throttle | Analog 1 (PA2) | - | Accelerator pedal | | Brake | Analog 2 (PA3) | - | Brake pedal | -| Clutch | Analog 3 (PA6) | - | Clutch pedal | +| Clutch | Analog 3 (PC3) | - | Clutch pedal | | VCC | 3.3V | Red | Potentiometer power | | GND | GND | Black | Ground | @@ -182,7 +182,7 @@ Os pedais do G27 usam **3 potenciômetros** para acelerador, freio e embreagem. |--------|------------------|---------------|-------| | Acelerador | Analog 1 (PA2) | - | Pedal do acelerador | | Freio | Analog 2 (PA3) | - | Pedal do freio | -| Embreagem | Analog 3 (PA6) | - | Pedal da embreagem | +| Embreagem | Analog 3 (PC3) | - | Pedal da embreagem | | VCC | 3.3V | Vermelho | Alimentação dos potenciômetros | | GND | GND | Preto | Terra | @@ -310,7 +310,7 @@ O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compart │ PEDALS (Standard Wiki Connection) │ │ ├── Throttle ───── PA2 (Analog 1) │ │ ├── Brake ──────── PA3 (Analog 2) │ -│ ├── Clutch ─────── PA6 (Analog 3) │ +│ ├── Clutch ─────── PC3 (Analog 3) │ │ ├── VCC ────────── 3.3V │ │ └── GND ────────── GND │ │ │ @@ -341,7 +341,7 @@ O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compart | 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, PA6 | 3.3V | Standard wiki connection | +| Pedals | Analog | PA2, PA3, PC3 | 3.3V | Standard wiki connection | | Shifter | SPI2 | PB13, PB14, PB12 | 3.3V | G27 mode | | Wheel Rim | SPI3 | PC10, PC11, PA15 | 3.3V | Custom firmware required | diff --git a/doc/logitech g27/README.md b/doc/logitech g27/README.md index fd0269499..5063ec86d 100644 --- a/doc/logitech g27/README.md +++ b/doc/logitech g27/README.md @@ -94,7 +94,7 @@ See [G27_COMPLETE_WIRING_GUIDE.md](G27_COMPLETE_WIRING_GUIDE.md) for detailed pi |-----------|-----------|-----------| | Motor | PWM | PE9, PE11 | | Encoder | ABN | PA0, PA1 | -| Pedals | Analog | PA2, PA3, PA6 | +| Pedals | Analog | PA2, PA3, PC3 | | Shifter | SPI2 | PB12, PB13, PB14 | | Wheel Rim | SPI3 | PA15, PC10, PC11 | From b9c4b9dd150826c20e26de39087a2eddf3465324 Mon Sep 17 00:00:00 2001 From: Orlando Eduardo Pereira Date: Fri, 8 May 2026 16:42:14 -0300 Subject: [PATCH 60/61] Add shifter analog pin mapping to documentation Documented the G27 shifter X/Y analog axis connections: - X Axis: PC0 (Analog 6) - configurable via shifter.xchan - Y Axis: PC1 (Analog 5) - configurable via shifter.ychan Updated files: - G27_COMPLETE_WIRING_GUIDE.md: Added pin info to tables, diagram - G27_SHIFTER_ISSUE_REPORT.md: Added analog pins to hardware setup - README.md: Updated shifter pin summary Co-authored-by: Cursor --- doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md | 24 ++++++++++++------- doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md | 4 +++- doc/logitech g27/README.md | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md index 1843dfe16..5e9e2f0e7 100644 --- a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md +++ b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md @@ -204,13 +204,15 @@ The G27 shifter has: | SCK | PB13 | 2 | SPI Clock | | MISO | PB14 | 4 | SPI Data | | CS/Latch | PB12 | 6 | Chip Select (SPI2_SS1) | -| X Axis | Analog In | 7 | Analog X position | -| Y Axis | Analog In | 3 | Analog Y position | +| 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 @@ -224,10 +226,16 @@ O câmbio G27 tem: | SCK | PB13 | 2 | Clock SPI | | MISO | PB14 | 4 | Dados SPI | | CS/Latch | PB12 | 6 | Chip Select (SPI2_SS1) | -| Eixo X | Analog In | 7 | Posição analógica X | -| Eixo Y | Analog In | 3 | Posição analógica Y | +| 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 @@ -314,12 +322,12 @@ O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compart │ ├── VCC ────────── 3.3V │ │ └── GND ────────── GND │ │ │ -│ SHIFTER (SPI2) │ +│ SHIFTER (SPI2 + Analog) │ │ ├── SCK ────────── PB13 │ │ ├── MISO ───────── PB14 │ │ ├── CS ─────────── PB12 (SPI2_SS1) │ -│ ├── X Axis ─────── Analog Input │ -│ ├── Y Axis ─────── Analog Input │ +│ ├── X Axis ─────── PC0 (Analog 6) │ +│ ├── Y Axis ─────── PC1 (Analog 5) │ │ ├── VCC ────────── 3.3V │ │ └── GND ────────── GND │ │ │ @@ -342,7 +350,7 @@ O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compart | 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 | PB13, PB14, PB12 | 3.3V | G27 mode | +| 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 | --- diff --git a/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md b/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md index 70b3dd252..ba582f662 100644 --- a/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md +++ b/doc/logitech g27/G27_SHIFTER_ISSUE_REPORT.md @@ -13,12 +13,14 @@ The **G27 Shifter buttons did not work** when using the official **ShifterAnalog | 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) +- **Analog axes** for X/Y position (gear detection via PC0/PC1) --- diff --git a/doc/logitech g27/README.md b/doc/logitech g27/README.md index 5063ec86d..ee03b207e 100644 --- a/doc/logitech g27/README.md +++ b/doc/logitech g27/README.md @@ -95,7 +95,7 @@ See [G27_COMPLETE_WIRING_GUIDE.md](G27_COMPLETE_WIRING_GUIDE.md) for detailed pi | Motor | PWM | PE9, PE11 | | Encoder | ABN | PA0, PA1 | | Pedals | Analog | PA2, PA3, PC3 | -| Shifter | SPI2 | PB12, PB13, PB14 | +| Shifter | SPI2 + Analog | PB12, PB13, PB14, PC0, PC1 | | Wheel Rim | SPI3 | PA15, PC10, PC11 | ### 3. Configure / Configurar From aa73c382446faf72d1436399f441d1729985c95d Mon Sep 17 00:00:00 2001 From: Orlando Eduardo Pereira Date: Sat, 16 May 2026 13:53:11 -0300 Subject: [PATCH 61/61] Remove SPI3 wheel rim changes per review; keep ShifterAnalog fix Per maintainer feedback on PR #182: SPI2 is the preferred bus for buttons/addons; SPI3 is reserved for high-speed encoders (BISS-C, SSI). Removed from this PR: - SPI_Buttons_3 class and SPIBUTTONS3 define - EEPROM addresses ADR_SPI_BTN_3_CONF / _CONF_2 - Slow SPI3 prescaler (reverted to upstream PRESCALER_4) - Restore TIM_TMC_ARR and TIM_FFB accidentally removed in initial commit - G27-specific SPI3 comments in cpp_target_config.cpp Kept: - ShifterAnalog G27 H-pattern fix (cs_pin indexing, DMA read, SPI config) - SPI2 CS pins initialized HIGH (PB12, PD8, PD9) for correct operation - SPIBUTTONS2 define SPI3 wheel rim workaround preserved in tag g27-full-working for personal use. Upstream preferred solution: SPI2 CS2 with tri-state buffer on MISO. --- .../FFBoard/UserExtensions/Inc/SPIButtons.h | 13 ---- .../UserExtensions/Inc/eeprom_addresses.h | 2 - .../UserExtensions/Src/ButtonSources.cpp | 3 - .../FFBoard/UserExtensions/Src/SPIButtons.cpp | 75 ------------------- .../UserExtensions/Src/eeprom_addresses.c | 6 -- .../F407VG/Core/Inc/target_constants.h | 3 +- .../F407VG/Core/Src/cpp_target_config.cpp | 7 +- Firmware/Targets/F407VG/Core/Src/main.c | 10 +-- doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md | 48 ++++++------ .../G27_WHEEL_RIM_BUTTONS_REPORT.md | 4 +- doc/logitech g27/README.md | 10 +-- 11 files changed, 42 insertions(+), 139 deletions(-) diff --git a/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h b/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h index b131ee941..7c5fe634a 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h +++ b/Firmware/FFBoard/UserExtensions/Inc/SPIButtons.h @@ -103,17 +103,4 @@ class SPI_Buttons_2 : public SPI_Buttons { static bool isCreatable(); }; -class SPI_Buttons_3 : public SPI_Buttons { -public: - SPI_Buttons_3(); - - const ClassIdentifier getInfo() override; - static ClassIdentifier info; - static bool isCreatable(); - - void restoreFlash() override; // Override to set default values - uint8_t readButtons(uint64_t* buf) override; // Override to use synchronous read for SPI3 - std::string getHelpstring() override {return "SPI 3 Button (SPI3)";} -}; - #endif /* SPIBUTTONS_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h b/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h index 40755abb3..ef533fa8a 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h +++ b/Firmware/FFBoard/UserExtensions/Inc/eeprom_addresses.h @@ -58,8 +58,6 @@ uint16_t EE_ReadVariable(uint16_t VirtAddress, uint16_t* Data) will return 1 if #define ADR_SPI_BTN_2_CONF 0x205 #define ADR_SPI_BTN_1_CONF_2 0x206 #define ADR_SPI_BTN_2_CONF_2 0x207 -#define ADR_SPI_BTN_3_CONF 0x209 -#define ADR_SPI_BTN_3_CONF_2 0x20A #define ADR_LOCAL_BTN_CONF_3 0x208 // Pulse mask // Local encoder #define ADR_ENCLOCAL_CPR 0x210 diff --git a/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp b/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp index b189dc693..554560c7f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/ButtonSources.cpp @@ -24,9 +24,6 @@ const std::vector> ButtonSource::all_buttonsources = #ifdef SPIBUTTONS2 add_class(2), #endif -#ifdef SPIBUTTONS3 - add_class(6), -#endif #ifdef SHIFTERBUTTONS add_class(3), #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp index 5193ce70b..7d15417ed 100644 --- a/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/SPIButtons.cpp @@ -52,81 +52,6 @@ SPI_Buttons_2::SPI_Buttons_2() restoreFlash(); // Call base class version (SPI_Buttons_2 doesn't override) } -ClassIdentifier SPI_Buttons_3::info = { - .name = "SPI Buttons 3" , - .id=CLSID_BTN_SPI, - }; -const ClassIdentifier SPI_Buttons_3::getInfo(){ - return info; -} - -bool SPI_Buttons_3::isCreatable(){ -#ifdef EXT3_SPI_PORT - return (ext3_spi.hasFreePins()); -#else - return false; -#endif -} - -SPI_Buttons_3::SPI_Buttons_3() - : SPI_Buttons{ADR_SPI_BTN_3_CONF, ADR_SPI_BTN_3_CONF_2, &ext3_spi, 2} { - // Constructor body runs AFTER base class is fully constructed - // Now we can safely call our overridden restoreFlash() - SPI_Buttons_3::restoreFlash(); // Explicitly call our version, not virtual dispatch -} - -void SPI_Buttons_3::restoreFlash(){ - // Always apply G27 wheel rim defaults for SPI_Buttons_3 (hardcoded) - // Force these values every time, regardless of flash contents - ButtonSourceConfig config; - config.numButtons = 8; // 8 buttons on wheel rim - HARDCODED - config.mode = SPI_BtnMode::PISOSR; // 74xx165 mode - HARDCODED - config.cs_num = 1; // CS pin 1 (PA15 - SPI3_SS1) - HARDCODED - config.spi_speed = 2; // Slow speed (prescaler 64) - HARDCODED - config.invert = false; - config.cutRight = false; - - // Always apply hardcoded configuration - setConfig(config); - - // Force CS to correct value after setConfig - this->conf.cs_num = 1; - - // Explicitly ensure btnnum is set (multiple attempts for debug) - this->btnnum = 8; - ButtonSource::btnnum = 8; -} - -// SPI_Buttons_3 uses synchronous read because DMA has timing issues with SPI3 -uint8_t SPI_Buttons_3::readButtons(uint64_t* buf){ - // Copy last buffer to output - memcpy(buf, this->spi_buf, std::min(this->bytes, 8)); - process(buf); - - // Check if SPI is available - if(spiPort.isTaken()) - return this->conf.numButtons; - - // Use direct HAL_SPI_Receive which works correctly for SPI3 - SPI_HandleTypeDef* hspi = spiPort.getPortHandle(); - - // Get CS pin for this device (cs_num is 1-indexed, array is 0-indexed) - OutputPin* cs_pin = spiPort.getCsPin(this->conf.cs_num > 0 ? this->conf.cs_num - 1 : 0); - - if(cs_pin != nullptr) { - // Pulse CS low then high to latch data into 74HC165 - cs_pin->write(false); // CS LOW - load parallel data - // Small delay for latch - for(volatile int i = 0; i < 10; i++) {} - cs_pin->write(true); // CS HIGH - enable shift - } - - // Read data via SPI - HAL_SPI_Receive(hspi, spi_buf, bytes, 10); - - return this->conf.numButtons; -} - // TODO check if pin is free SPI_Buttons::SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2, SPIPort* spiPort, uint8_t instance) diff --git a/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c b/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c index 0026f3d48..80257d632 100644 --- a/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c +++ b/Firmware/FFBoard/UserExtensions/Src/eeprom_addresses.c @@ -40,8 +40,6 @@ const uint16_t VirtAddVarTab[NB_OF_VAR] = ADR_SPI_BTN_2_CONF, ADR_SPI_BTN_1_CONF_2, ADR_SPI_BTN_2_CONF_2, - ADR_SPI_BTN_3_CONF, - ADR_SPI_BTN_3_CONF_2, ADR_LOCAL_BTN_CONF_3, // Pulse mask // Local encoder ADR_ENCLOCAL_CPR, @@ -238,12 +236,8 @@ 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_SPI_BTN_3_CONF, - ADR_SPI_BTN_3_CONF_2, ADR_LOCAL_BTN_CONF_3, // Pulse mask // Local encoder ADR_ENCLOCAL_CPR, diff --git a/Firmware/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index c43d5983f..6897827dc 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -41,7 +41,6 @@ #define LOCALBUTTONS #define SPIBUTTONS #define SPIBUTTONS2 -#define SPIBUTTONS3 #define SHIFTERBUTTONS #define PCF8574BUTTONS // Requires I2C #define ANALOGAXES @@ -71,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/cpp_target_config.cpp b/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp index fe41ac90c..5b481f7f8 100644 --- a/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp +++ b/Firmware/Targets/F407VG/Core/Src/cpp_target_config.cpp @@ -11,12 +11,7 @@ extern SPI_HandleTypeDef hspi1; SPIPort motor_spi{hspi1,motor_spi_cspins,84000000,false}; #ifdef EXT3_SPI_PORT -// SPI3 CS pins - native SPI3 pins only -static const std::vector ext3_spi_cspins{ - OutputPin(*SPI3_SS1_GPIO_Port, SPI3_SS1_Pin), // PA15 - CS1 (used for G27 wheel rim) - OutputPin(*SPI3_SS2_GPIO_Port, SPI3_SS2_Pin), // PD2 - CS2 - OutputPin(*SPI3_SS3_GPIO_Port, SPI3_SS3_Pin) // PD3 - CS3 -}; +static const std::vector ext3_spi_cspins{OutputPin(*SPI3_SS1_GPIO_Port, SPI3_SS1_Pin), OutputPin(*SPI3_SS2_GPIO_Port, SPI3_SS2_Pin),OutputPin(*SPI3_SS3_GPIO_Port, SPI3_SS3_Pin)}; extern SPI_HandleTypeDef EXT3_SPI_PORT; SPIPort ext3_spi{hspi3,ext3_spi_cspins,42000000,true}; #endif diff --git a/Firmware/Targets/F407VG/Core/Src/main.c b/Firmware/Targets/F407VG/Core/Src/main.c index 5758fdcf9..716996bd8 100644 --- a/Firmware/Targets/F407VG/Core/Src/main.c +++ b/Firmware/Targets/F407VG/Core/Src/main.c @@ -677,7 +677,7 @@ static void MX_SPI3_Init(void) hspi3.Init.CLKPolarity = SPI_POLARITY_LOW; hspi3.Init.CLKPhase = SPI_PHASE_1EDGE; hspi3.Init.NSS = SPI_NSS_SOFT; - hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; // Slower for reliable 74HC165 communication + hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi3.Init.TIMode = SPI_TIMODE_DISABLE; hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; @@ -1383,14 +1383,12 @@ static void MX_GPIO_Init(void) |LED_ERR_Pin, GPIO_PIN_RESET); /*Configure GPIO pin Output Level */ - // SPI CS pins initialized HIGH (inactive) - important for 74HC165 shift registers + // 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, GPIO_PIN_SET); - HAL_GPIO_WritePin(GPIOD, CAN_S_Pin|GP1_Pin|LED_SYS_Pin, GPIO_PIN_RESET); + 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 */ - // SPI3 CS1 initialized HIGH (inactive) - used for G27 wheel rim buttons - HAL_GPIO_WritePin(SPI3_SS1_GPIO_Port, SPI3_SS1_Pin, GPIO_PIN_SET); + HAL_GPIO_WritePin(SPI3_SS1_GPIO_Port, SPI3_SS1_Pin, GPIO_PIN_RESET); /*Configure GPIO pins : DIN7_Pin DIN6_Pin DIN5_Pin DIN4_Pin DIN3_Pin */ diff --git a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md index 5e9e2f0e7..9a5660b3c 100644 --- a/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md +++ b/doc/logitech g27/G27_COMPLETE_WIRING_GUIDE.md @@ -240,13 +240,29 @@ O câmbio G27 tem: ## 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 74HC165 lacks tri-state output. A **separate SPI port (SPI3)** must be used. +> **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 | |----------|-----------------|---------------|-------| @@ -258,37 +274,27 @@ The G27 wheel rim has: | MOSI | - | 3 | For LEDs (not used) | | LED Latch | - | 5 | For LEDs (not used) | -#### Why SPI3? / Por que SPI3? +#### 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. Using a separate SPI bus (SPI3) avoids this problem. +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 / Configuração -- Class: **SPI Buttons 3** (custom, uses SPI3) +#### 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 74HC165 não tem saída tri-state. Um **porta SPI separada (SPI3)** deve ser usada. - -| Função | Pino OpenFFBoard | Pino do Aro | Notas | -|--------|------------------|-------------|-------| -| VCC | 3.3V | 1 | Alimentação (3.3V!) | -| SCK | PC10 | 4 | Clock SPI3 | -| MISO | PC11 | 6 | Dados SPI3 | -| CS/Latch | PA15 | 2 | SPI3_SS1 | -| GND | GND | 7 | Terra | -| MOSI | - | 3 | Para LEDs (não usado) | -| LED Latch | - | 5 | Para LEDs (não usado) | - -#### Por que SPI3? - -O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compartilham a mesma linha MISO, eles criam conflito elétrico - ambos tentam controlar a linha simultaneamente. Usar um barramento SPI separado (SPI3) evita esse problema. +> **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`. --- @@ -422,7 +428,7 @@ O registrador 74HC165 **não tem saída tri-state**. Quando dois 74HC165 compart 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**: The wheel rim buttons require custom firmware modifications (SPI_Buttons_3 class). +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 diff --git a/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md b/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md index 15f752038..31d27ebf1 100644 --- a/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md +++ b/doc/logitech g27/G27_WHEEL_RIM_BUTTONS_REPORT.md @@ -1,8 +1,10 @@ # 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 solution implemented using a separate SPI port. +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). --- diff --git a/doc/logitech g27/README.md b/doc/logitech g27/README.md index ee03b207e..764ef1863 100644 --- a/doc/logitech g27/README.md +++ b/doc/logitech g27/README.md @@ -52,9 +52,9 @@ Esta pasta contém documentação e guias para integrar um **Volante Logitech G2 ### 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. This fork adds **SPI3 support** to allow both to work simultaneously. +> **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. Este fork adiciona **suporte a SPI3** para ambos funcionarem simultaneamente. +> **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. --- @@ -71,7 +71,7 @@ Esta pasta contém documentação e guias para integrar um **Volante Logitech G2 | 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 solution / Conflito SPI e solução | +| [**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) | --- @@ -96,7 +96,7 @@ See [G27_COMPLETE_WIRING_GUIDE.md](G27_COMPLETE_WIRING_GUIDE.md) for detailed pi | Encoder | ABN | PA0, PA1 | | Pedals | Analog | PA2, PA3, PC3 | | Shifter | SPI2 + Analog | PB12, PB13, PB14, PC0, PC1 | -| Wheel Rim | SPI3 | PA15, PC10, PC11 | +| Wheel Rim | SPI3 (fork/tag `g27-full-working` only) | PA15, PC10, PC11 | ### 3. Configure / Configurar @@ -106,7 +106,7 @@ Using OpenFFBoard Configurator: 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 +5. **Wheel Rim**: SPI Buttons 3, 8 buttons, Mode 74HC165 *(fork/tag `g27-full-working` only)* ---