diff --git a/CHANGELOG.md b/CHANGELOG.md index 94a1f90b..75d2ef41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ### Changes this version: -- 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) ### Changes in 1.16: @@ -15,4 +18,5 @@ Internal changes: - 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 \ No newline at end of file +- Added remote CAN button/analog source mainclass +- Added exponential torque postprocessing for game effects \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..afdbd21d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,42 @@ +# How to contribute + +## General terms + +1. By contributing to this project you grant the repository owner the full copyright to the contribution. +2. Generally small changes and fixes are more likely to be merged as large changes can easily lead to conflicts in the future. +3. A change in the common codebase must not cause additional issues in target subprojects. It must compile for all subprojects. +4. Changes to pins, interrupts, task priorities, heap/stack sizes must be carefully examined for side effects as they can have unintended consequences in seemingly unrelated modules. Generally do not change these unless absolutely required and tested. +5. Read the [wiki](https://github.com/Ultrawipf/OpenFFBoard/wiki) for additional information about [pinouts](https://github.com/Ultrawipf/OpenFFBoard/wiki/Pinouts-and-peripherals), features, [commands](https://github.com/Ultrawipf/OpenFFBoard/wiki/Commands) and contribution info + +### Simplified steps for contribution +1. Clone/Fork the projects master branch +2. Make your changes and create a commit (If it requires GUI features to be changed it is recommended to do the same with the [configurator repo](https://github.com/Ultrawipf/OpenFFBoard-configurator) or if its just a small change detail what the effect is and request a change) +3. Create a pull request for your fork against master and describe your contribution in a detailed way. What bug does it fix? Which feature is added? +4. Wait for approval and append requested changes to your branch +5. Your code gets squash/rebase merged if approved + +### Release workflow +Changes should be added in the CHANGELOG.md file. +This will be used to generate release notes. +A tagged commit (v1.x.x) will trigger an automatic release. A tag with a "-" (v1.x.x-beta) generates a prerelease. + +A release is triggered when changes are deemed ready for public use. + +### Submodules +The configurator is an important part of the project and should be kept up to date with any changes required to support a firmware and the submodule should if possible point to a commit that works with the corresponding firmware version. + +Pull the submodules with `git submodule update --init`. + +### Branches +If you directly push a branch to the project as a developer your branch name should contain your username or the type of feature. Like `ultrawipf/warpspeed` or `feature/pwmdriver`. +Set your git author name correctly before committing. + +### Code style +The code should follow the same style as the other parts of the project and you should use easy to understand comments for complex statements and new functions. + +### Flash storage +If you require a variable to be stored in flash check the eeprom emulation functions and choose an address according to `scripts/memory_map.csv` and add it to the list in [`scripts/memory_map.csv`](../../Firmware/scripts/memory_map.csv). + +The script `scripts/generate_memory.py` is used to generate `eeprom_addresses.c` and `eeprom_addresses.h` from the csv file to keep track of used addresses and automatically set the right defitions. + +In general you should use a block of addresses per class and use the space efficiently. If you have 5 bools and an 8 bit int to store don't use 6 full addresses but pack them into a single uint16_t. diff --git a/Configurator b/Configurator index e4d1e3c3..a3bc7f11 160000 --- a/Configurator +++ b/Configurator @@ -1 +1 @@ -Subproject commit e4d1e3c326f2db9efe113ed9bf4fa48b790fd6b3 +Subproject commit a3bc7f11aeb04853ae8bbc1446b6761d2a739588 diff --git a/Firmware/FFBoard/Inc/SystemCommands.h b/Firmware/FFBoard/Inc/SystemCommands.h index ffe5600b..46ef3969 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 + 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 }; class SystemCommands : public CommandHandler { diff --git a/Firmware/FFBoard/Inc/constants.h b/Firmware/FFBoard/Inc/constants.h index 45606908..8f6724a9 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,4}; // Version as array. 8 bit each! +static const uint8_t SW_VERSION_INT[3] = {1,16,5}; // Version as array. 8 bit each! #ifndef MAX_AXIS #define MAX_AXIS 2 // ONLY USE 2 for now else screws HID Reports #endif @@ -99,5 +99,14 @@ static const uint8_t SW_VERSION_INT[3] = {1,16,4}; // Version as array. 8 bit ea #error "Only TIM_MICROS_HALTICK OR TIM_MICROS may be defined as a microsecond timebase" #endif +#ifndef SIGNATURELEN +#define SIGNATURELEN 8 +#define SIGNATURE +#endif + +#if defined(FLASH_OTP_BASE) && defined(FLASH_OTP_END) +#define OTPMEMORY +#endif + #endif diff --git a/Firmware/FFBoard/Inc/flash_helpers.h b/Firmware/FFBoard/Inc/flash_helpers.h index d4f87532..a409a48f 100644 --- a/Firmware/FFBoard/Inc/flash_helpers.h +++ b/Firmware/FFBoard/Inc/flash_helpers.h @@ -31,6 +31,9 @@ bool Flash_ReadWriteDefault(uint16_t adr,uint16_t *buf,uint16_t def); // returns void Flash_Dump(std::vector> *result,bool includeAll = false); bool Flash_Format(); +bool OTP_Write(uint16_t adroffset,uint64_t dat); +bool OTP_Read(uint16_t adroffset,uint64_t* dat); + template inline TVal Flash_ReadDefault(uint16_t adr, TVal def) { diff --git a/Firmware/FFBoard/Src/SystemCommands.cpp b/Firmware/FFBoard/Src/SystemCommands.cpp index af8b293e..c42b0918 100644 --- a/Firmware/FFBoard/Src/SystemCommands.cpp +++ b/Firmware/FFBoard/Src/SystemCommands.cpp @@ -69,8 +69,14 @@ void SystemCommands::registerCommands(){ CommandHandler::registerCommand("devid", FFBoardMain_commands::devid, "Get chip dev id and rev id",CMDFLAG_GET); CommandHandler::registerCommand("name", CommandHandlerCommands::name, "name of class",CMDFLAG_GET|CMDFLAG_STR_ONLY); CommandHandler::registerCommand("cmdinfo", CommandHandlerCommands::cmdinfo, "Flags of a command id (adr). -1 if cmd id invalid",CMDFLAG_GETADR); - CommandHandler::registerCommand("uid", FFBoardMain_commands::uid, "Get 96b chip uid. Adr0-2 sel blk",CMDFLAG_GET | CMDFLAG_GETADR); + CommandHandler::registerCommand("uid", FFBoardMain_commands::uid, "Get 96b chip uid. Adr0-2 sel blk",CMDFLAG_GET | CMDFLAG_GETADR | CMDFLAG_INFOSTRING); CommandHandler::registerCommand("temp", FFBoardMain_commands::temp, "Chip temperature in C",CMDFLAG_GET); +#if defined(OTPMEMORY) + CommandHandler::registerCommand("otp", FFBoardMain_commands::otp, "Access OTP memory",CMDFLAG_GETADR | CMDFLAG_SETADR | CMDFLAG_DEBUG); +#endif +#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 } // Choose lower optimize level because the compiler likes to blow up this function @@ -279,6 +285,10 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect }else if(cmd.adr == 2){ replies.emplace_back(HAL_GetUIDw2()); } + }else if(cmd.type == CMDtype::info){ + char buf[32]; + std::snprintf(buf,32,"0x%08lx%08lx%08lx",HAL_GetUIDw2(),HAL_GetUIDw1(),HAL_GetUIDw0()); // Print as hex string + replies.emplace_back(std::string(buf)); } break; case FFBoardMain_commands::temp: @@ -286,6 +296,70 @@ CommandStatus SystemCommands::internalCommand(const ParsedCommand& cmd,std::vect replies.emplace_back(getChipTemp()); break; } +#if defined(OTPMEMORY) + case FFBoardMain_commands::otp: + if(cmd.type == CMDtype::setat){ + bool success = OTP_Write(cmd.adr, cmd.val); + if(!success){ + flag = CommandStatus::ERR; + } + }else if(cmd.type == CMDtype::getat){ + CommandReply reply; + reply.type = CommandReplyType::INT; + uint64_t val; + if(OTP_Read(cmd.adr,&val)){ + reply.val=val; + }else{ + flag = CommandStatus::ERR; + } + replies.push_back(reply); + } + break; +#endif +#if defined(SIGNATURE) + case FFBoardMain_commands::signature: +#if defined(SIGNATURELEN) && defined(SIGBNATUREBASEADR) + if(cmd.type == CMDtype::info){ + char buf[(SIGNATURELEN * 16) + 1]; + uint64_t dat; + for(uint8_t i = 0;i= SIGNATURELEN || cmd.adr < 0){ + flag = CommandStatus::ERR; + }else{ + uint64_t dat; + OTP_Read(SIGBNATUREBASEADR+cmd.adr, &dat); + replies.emplace_back(dat,cmd.adr); + } + }else if(cmd.type == CMDtype::setat){ + bool success = OTP_Write(cmd.adr+SIGBNATUREBASEADR, cmd.val); + if(!success){ + flag = CommandStatus::ERR; + } + }else{ + flag = CommandStatus::ERR; + } +#else + if(cmd.type == CMDtype::info){ + replies.emplace_back("0"); + }else if(cmd.type == CMDtype::get || cmd.type == CMDtype::getat){ + replies.emplace_back(0); + }else{ + flag = CommandStatus::ERR; + } +#endif +#endif + break; default: diff --git a/Firmware/FFBoard/Src/USBdevice.cpp b/Firmware/FFBoard/Src/USBdevice.cpp index acb3fb26..2580d8ab 100644 --- a/Firmware/FFBoard/Src/USBdevice.cpp +++ b/Firmware/FFBoard/Src/USBdevice.cpp @@ -11,7 +11,7 @@ uint16_t _desc_str[USB_STRING_DESC_BUF_SIZE]; // String buffer USBdevice::USBdevice(const tusb_desc_device_t* deviceDesc,const uint8_t (*confDesc),const usb_string_desc_t* strings, uint8_t appendSerial) : -Thread("USB", 256, 40), desc_device(deviceDesc), desc_conf(confDesc), string_desc(strings),appendSerial(appendSerial) { +Thread("USB", 330, 40), desc_device(deviceDesc), desc_conf(confDesc), string_desc(strings),appendSerial(appendSerial) { } @@ -41,7 +41,9 @@ void USBdevice::Run(){ * Generates a unique id string from the hardware id */ std::string USBdevice::getUsbSerial(){ - std::string serial = std::to_string(HAL_GetUIDw0()) + std::to_string(HAL_GetUIDw1()) + std::to_string(HAL_GetUIDw2()); + char buf[25]; + std::snprintf(buf,25,"%08lx%08lx%08lx",HAL_GetUIDw2(),HAL_GetUIDw1(),HAL_GetUIDw0()); // Print as hex string + std::string serial{buf}; //std::to_string(HAL_GetUIDw0()) + std::to_string(HAL_GetUIDw1()) + std::to_string(HAL_GetUIDw2()); return serial; } @@ -76,7 +78,7 @@ uint16_t* USBdevice::getUsbStringDesc(uint8_t index,uint16_t langid){ field = string_desc->interfaces[index-4]; if(appendSerial){ field += "("; - field.append(getUsbSerial().substr(0, appendSerial)); + field.append(std::to_string(HAL_GetUIDw0()).substr(0, appendSerial)); field += ")"; } }else{ diff --git a/Firmware/FFBoard/Src/flash_helpers.cpp b/Firmware/FFBoard/Src/flash_helpers.cpp index c1f8b384..cc8256ec 100644 --- a/Firmware/FFBoard/Src/flash_helpers.cpp +++ b/Firmware/FFBoard/Src/flash_helpers.cpp @@ -233,3 +233,53 @@ void Flash_Dump(std::vector> *result,bool includeA } } + + +/* + * OTP Helper functions. + * Some chips have internal OTP sections, others may need custom implementations + * Page size and address required + */ +#if defined(FLASH_OTP_BASE) && defined(FLASH_OTP_END) && defined(OTPMEMORY) +// This chip has an OTP section in main flash. + +__weak bool OTP_Write(uint16_t adroffset,uint64_t dat){ +// FLASH_OTP_BASE +// FLASH_OTP_END + uint32_t adr = (FLASH_OTP_BASE+adroffset*sizeof(uint64_t)); + if(adr > FLASH_OTP_END){ + return false; // Error + } + uint64_t curval = *(uint64_t*)adr; + if(curval != 0xffffffffffffffff){ + return false; + } + if(HAL_FLASH_Unlock()!=HAL_OK) return false; + //bool success = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, adr, dat) == HAL_OK; + bool success = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, adr, (uint32_t)(dat)) == HAL_OK; + success = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, adr+4, (uint32_t)(dat >> 32)) == HAL_OK && success; + HAL_FLASH_Lock(); + return success; +} + + +__weak bool OTP_Read(uint16_t adroffset,uint64_t* dat){ + uint32_t adr = (FLASH_OTP_BASE+adroffset*sizeof(uint64_t)); + if(adr > FLASH_OTP_END){ + return false; // Error + } + uint64_t curval = *(uint64_t*)adr; + *dat = curval; + return true; +} +#else +__weak bool OTP_Write(uint16_t adroffset,uint64_t dat){ + return false; +} + + +__weak bool OTP_Read(uint16_t adroffset,uint64_t* dat){ + return false; +} + +#endif diff --git a/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h b/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h index ef1b26a9..38163662 100644 --- a/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h +++ b/Firmware/FFBoard/UserExtensions/Inc/MtEncoderSPI.h @@ -16,11 +16,13 @@ #include "CommandHandler.h" #include "thread.hpp" -#define MAGNTEK_READ 0x80 class MtEncoderSPI: public Encoder, public SPIDevice, public PersistentStorage, public CommandHandler,cpp_freertos::Thread{ enum class MtEncoderSPI_commands : uint32_t{ - cspin,pos,errors + cspin,pos,errors,mode + }; + enum class MtEncoderSPI_mode : uint8_t{ + mt6825,mt6835 }; public: MtEncoderSPI(); @@ -51,11 +53,13 @@ class MtEncoderSPI: public Encoder, public SPIDevice, public PersistentStorage, CommandStatus command(const ParsedCommand& cmd,std::vector& replies); void setCsPin(uint8_t cspin); + void setMode(MtEncoderSPI_mode mode); + //bool useDMA = false; // if true uses DMA for angle updates instead of polling SPI. TODO when used with tmc external encoder using DMA will hang the interrupt randomly private: - uint8_t readSpi(uint8_t addr); - void writeSpi(uint8_t addr,uint8_t data); + uint8_t readSpi(uint16_t addr); + void writeSpi(uint16_t addr,uint8_t data); void spiTxRxCompleted(SPIPort* port); @@ -71,11 +75,16 @@ class MtEncoderSPI: public Encoder, public SPIDevice, public PersistentStorage, uint32_t errors = 0; - uint8_t txbuf[4] = {0x03 | MAGNTEK_READ,0,0,0}; - uint8_t rxbuf[4] = {0,0,0,0}; - uint8_t rxbuf_t[4] = {0,0,0,0}; + uint8_t txbuf[6] = {0}; + uint8_t rxbuf[6] = {0}; + uint8_t rxbuf_t[6] = {0}; cpp_freertos::BinarySemaphore requestNewDataSem = cpp_freertos::BinarySemaphore(false); cpp_freertos::BinarySemaphore waitForUpdateSem = cpp_freertos::BinarySemaphore(false); + + MtEncoderSPI_mode mode = MtEncoderSPI_mode::mt6825; + + static std::array tableCRC; + const uint8_t POLY = 0x07; }; #endif /* USEREXTENSIONS_SRC_MTENCODERSPI_H_ */ diff --git a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp index 20ce3613..b0ef5bf1 100644 --- a/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/MtEncoderSPI.cpp @@ -7,17 +7,20 @@ #include "MtEncoderSPI.h" #include "constants.h" +#include "CRC.h" #ifdef MTENCODERSPI bool MtEncoderSPI::inUse = false; ClassIdentifier MtEncoderSPI::info = { - .name = "MT6825 SPI3" , + .name = "MagnTek SPI" , .id=CLSID_ENCODER_MTSPI, }; const ClassIdentifier MtEncoderSPI::getInfo(){ return info; } +std::array MtEncoderSPI::tableCRC __attribute__((section (".ccmram"))); + MtEncoderSPI::MtEncoderSPI() : SPIDevice(ENCODER_SPI_PORT,ENCODER_SPI_PORT.getFreeCsPins()[0]), CommandHandler("mtenc",CLSID_ENCODER_MTSPI,0),cpp_freertos::Thread("MTENC",256,42) { MtEncoderSPI::inUse = true; this->spiConfig.peripheral.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; // 4 = 10MHz 8 = 5MHz @@ -26,6 +29,9 @@ MtEncoderSPI::MtEncoderSPI() : SPIDevice(ENCODER_SPI_PORT,ENCODER_SPI_PORT.getFr this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_HIGH; this->spiConfig.cspol = true; + //Init CRC-8 table + makeCrcTable(tableCRC,POLY,8); // Mt6825, Poly X8+X2+X (+1) + restoreFlash(); spiPort.reserveCsPin(this->spiConfig.cs); @@ -33,6 +39,7 @@ MtEncoderSPI::MtEncoderSPI() : SPIDevice(ENCODER_SPI_PORT,ENCODER_SPI_PORT.getFr registerCommand("cs", MtEncoderSPI_commands::cspin, "CS pin",CMDFLAG_GET | CMDFLAG_SET); registerCommand("pos", MtEncoderSPI_commands::pos, "Position",CMDFLAG_GET | CMDFLAG_SET); registerCommand("errors", MtEncoderSPI_commands::errors, "Parity error count",CMDFLAG_GET); + registerCommand("mode", MtEncoderSPI_commands::mode, "Encoder mode (MT6825=0;MT6835=1)",CMDFLAG_GET | CMDFLAG_SET | CMDFLAG_INFOSTRING); this->Start(); } @@ -45,11 +52,14 @@ 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 & 0xF; + MtEncoderSPI_mode mode = static_cast(conf_int >> 8); + setMode(mode); setCsPin(cspin); } void MtEncoderSPI::saveFlash(){ uint16_t conf_int = this->cspin & 0xF; + conf_int |= ((uint8_t)mode & 0xf) << 8; Flash_Write(ADR_MTENC_CONF1, conf_int); Flash_Write(ADR_MTENC_OFS, offset >> 2); } @@ -61,12 +71,13 @@ void MtEncoderSPI::Run(){ //spiPort.receive_DMA(spi_buf, bytes, this); // Receive next frame updateAngleStatus(); this->WaitForNotification(); // Wait until DMA is finished - if(updateAngleStatusCb()){ - if(curAngleInt-lastAngleInt > 0x20000){ // Underflowed + if(updateAngleStatusCb()){ + int overflowLim = getCpr() >> 1; + if(curAngleInt-lastAngleInt > overflowLim){ // Underflowed rotations--; } - else if(lastAngleInt-curAngleInt > 0x20000){ // Overflowed + else if(lastAngleInt-curAngleInt > overflowLim){ // Overflowed rotations++; } lastAngleInt = curAngleInt; @@ -98,18 +109,29 @@ void MtEncoderSPI::initSPI(){ /** * MT encoder reads 1 byte and transmits 1 byte back after that */ -uint8_t MtEncoderSPI::readSpi(uint8_t addr){ - - uint8_t txbuf[2] = {(uint8_t)(addr | MAGNTEK_READ),0}; - uint8_t rxbuf[2] = {0,0}; - spiPort.transmitReceive(txbuf, rxbuf, 2, this,100); +uint8_t MtEncoderSPI::readSpi(uint16_t addr){ + if(mode == MtEncoderSPI_mode::mt6825){ + uint8_t txbuf[2] = {(uint8_t)(addr | 0x80),0}; + uint8_t rxbuf[2] = {0,0}; + spiPort.transmitReceive(txbuf, rxbuf, 2, this,100); + }else if(mode == MtEncoderSPI_mode::mt6835){ + uint8_t txbuf[3] = {(uint8_t)((addr & 0xf00) | 0x30),(uint8_t)(addr & 0xff),0}; + uint8_t rxbuf[3] = {0,0,0}; + spiPort.transmitReceive(txbuf, rxbuf, 3, this,100); + } return rxbuf[1]; } -void MtEncoderSPI::writeSpi(uint8_t addr,uint8_t data){ - uint8_t txbuf[2] = {addr,data}; - spiPort.transmit(txbuf, 2, this,100); +void MtEncoderSPI::writeSpi(uint16_t addr,uint8_t data){ + if(mode == MtEncoderSPI_mode::mt6825){ + uint8_t txbuf[2] = {(uint8_t)(addr & 0xff),data}; + spiPort.transmit(txbuf, 2, this,100); + }else if(mode == MtEncoderSPI_mode::mt6835){ + uint8_t txbuf[3] = {(uint8_t)((addr & 0xf00) | 0x60),(uint8_t)(addr & 0xff),data}; + spiPort.transmit(txbuf, 3, this,100); + } + } void MtEncoderSPI::setPos(int32_t pos){ @@ -122,7 +144,7 @@ void MtEncoderSPI::spiTxRxCompleted(SPIPort* port){ if(updateInProgress){ NotifyFromISR(); //updateAngleStatusCb(); - memcpy(rxbuf,rxbuf_t,4); + memcpy(rxbuf,rxbuf_t,sizeof(rxbuf)); } } @@ -132,43 +154,68 @@ void MtEncoderSPI::spiTxRxCompleted(SPIPort* port){ */ void MtEncoderSPI::updateAngleStatus(){ - uint8_t txbufNew[4] = {0x03 | MAGNTEK_READ,0,0,0}; - memcpy(this->txbuf,txbufNew,4); - spiPort.transmitReceive_DMA(txbuf, rxbuf_t, 4, this); + + if(mode == MtEncoderSPI_mode::mt6825){ + uint8_t txbufNew[5] = {0x03 | 0x80,0,0,0,0}; + memcpy(this->txbuf,txbufNew,5); + spiPort.transmitReceive_DMA(txbuf, rxbuf_t, 4, this); + }else if(mode == MtEncoderSPI_mode::mt6835){ + uint8_t txbufNew[6] = {0xA0,0x03,0,0,0,0}; + memcpy(this->txbuf,txbufNew,6); + spiPort.transmitReceive_DMA(txbuf, rxbuf_t, 6, this); + } } bool MtEncoderSPI::updateAngleStatusCb(){ - uint32_t angle17_10 = rxbuf[1]; - uint32_t angle9_4 = rxbuf[2]; - uint32_t angle3_0 = rxbuf[3]; + bool parity_ok = false; + + if(mode == MtEncoderSPI_mode::mt6825){ + uint32_t angle17_10 = rxbuf[1]; + uint32_t angle9_4 = rxbuf[2]; + uint32_t angle3_0 = rxbuf[3]; + + // Parity check byte 2 + uint8_t pc1 = angle17_10 ^ angle17_10 >> 1; + pc1 = pc1 ^ pc1 >> 2; + pc1 = pc1 ^ pc1 >> 4; - // Parity check byte 2 - uint8_t pc1 = angle17_10 ^ angle17_10 >> 1; - pc1 = pc1 ^ pc1 >> 2; - pc1 = pc1 ^ pc1 >> 4; + uint8_t pc1_2 = angle9_4 ^ angle9_4 >> 1; + pc1_2 = pc1_2 ^ pc1_2 >> 2; + pc1_2 = pc1_2 ^ pc1_2 >> 4; - uint8_t pc1_2 = angle9_4 ^ angle9_4 >> 1; - pc1_2 = pc1_2 ^ pc1_2 >> 2; - pc1_2 = pc1_2 ^ pc1_2 >> 4; + // Parity check byte 1 + angle3_0 = angle3_0 >> 2; // shift 2 + uint8_t pc2 = (angle3_0) ^ (angle3_0) >> 1; + pc2 = pc2 ^ pc2 >> 2; + pc2 = pc2 ^ pc2 >> 4; - // Parity check byte 1 - angle3_0 = angle3_0 >> 2; // shift 2 - uint8_t pc2 = (angle3_0) ^ (angle3_0) >> 1; - pc2 = pc2 ^ pc2 >> 2; - pc2 = pc2 ^ pc2 >> 4; + nomag = (angle9_4 & 0x02) >> 1; + overspeed = (angle3_0 & 0x04) >> 2; + angle9_4 = (angle9_4 & 0xFC) >> 2; + angle3_0 = angle3_0 >> 2;//(angle3_0 & 0xF0) >> 4; - nomag = (angle9_4 & 0x02) >> 1; - overspeed = (angle3_0 & 0x04) >> 2; - angle9_4 = (angle9_4 & 0xFC) >> 2; - angle3_0 = angle3_0 >> 2;//(angle3_0 & 0xF0) >> 4; + parity_ok = !(pc2 & 1) && ((pc1 & 1) == (pc1_2 & 1)); - bool parity_ok = !(pc2 & 1) && ((pc1 & 1) == (pc1_2 & 1)); + curAngleInt = (angle17_10 << 10) | (angle9_4 << 4) | (angle3_0); - curAngleInt = (angle17_10 << 10) | (angle9_4 << 4) | (angle3_0); + }else if(mode == MtEncoderSPI_mode::mt6835){ + uint32_t angle20_13 = rxbuf[2]; + uint32_t angle12_5 = rxbuf[3]; + uint32_t angle4_0 = (rxbuf[4] & 0xF8) >> 3; + + uint8_t status = rxbuf[4] & 0x7; + nomag = (status & 0x02) >> 1; + overspeed = (status & 0x01); + uint8_t crc = rxbuf[5]; + + curAngleInt = (angle20_13 << 13) | (angle12_5 << 5) | (angle4_0); + uint8_t calccrc = calculateCrc8(tableCRC, rxbuf+2, 3, 0); + parity_ok = calccrc == crc; + } this->updateInProgress = false; @@ -193,9 +240,27 @@ int32_t MtEncoderSPI::getPosAbs(){ } uint32_t MtEncoderSPI::getCpr(){ - return 262144; + switch(mode){ + case MtEncoderSPI_mode::mt6825: + return 262144; + case MtEncoderSPI_mode::mt6835: + return 2097152; + default: + return 0; // Not possible + } } +void MtEncoderSPI::setMode(MtEncoderSPI::MtEncoderSPI_mode mode){ + this->mode = mode; + // Reset variables + this->curPos = 0; + this->curAngleInt = 0; + this->lastAngleInt = 0; + this->rotations = 0; + this->errors = 0; + this->nomag = false; + this->overspeed = false; +} CommandStatus MtEncoderSPI::command(const ParsedCommand& cmd,std::vector& replies){ @@ -222,6 +287,17 @@ CommandStatus MtEncoderSPI::command(const ParsedCommand& cmd,std::vectorsetMode((MtEncoderSPI_mode)cmd.val); + }else if(cmd.type==CMDtype::info){ + replies.emplace_back("MT6825:0\nMT6835:1"); + }else{ + return CommandStatus::ERR; + } + break; default: return CommandStatus::NOT_FOUND; } diff --git a/Firmware/FFBoard/UserExtensions/Src/mainclass_chooser.cpp b/Firmware/FFBoard/UserExtensions/Src/mainclass_chooser.cpp index 8bb41e02..bf3da88f 100644 --- a/Firmware/FFBoard/UserExtensions/Src/mainclass_chooser.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/mainclass_chooser.cpp @@ -66,7 +66,9 @@ const std::vector> class_registry = add_class(), #endif - add_class() +#ifdef CUSTOMMAINNAME + add_class() +#endif }; #endif diff --git a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp index df7cb284..442758d0 100644 --- a/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp +++ b/Firmware/FFBoard/UserExtensions/Src/usb_descriptors.cpp @@ -23,7 +23,7 @@ const tusb_desc_device_t usb_devdesc_ffboard_composite = .bcdUSB = 0x0200, // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) - .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceClass = TUSB_CLASS_UNSPECIFIED, .bDeviceSubClass = MISC_SUBCLASS_COMMON, .bDeviceProtocol = MISC_PROTOCOL_IAD, .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, @@ -118,7 +118,7 @@ uint8_t const usb_cdc_midi_conf[] = const usb_string_desc_t usb_ffboard_strings_default = { .langId = 0x0409, .manufacturer = "Open FFBoard", - .product = "FFBoard", + .product = "FFBoard " HW_TYPE, // Interfaces start at index 4 .interfaces = {"FFBoard CDC", "FFBoard HID","FFBoard MIDI"} }; diff --git a/Firmware/Targets/F407VG/Core/Inc/target_constants.h b/Firmware/Targets/F407VG/Core/Inc/target_constants.h index 7f7ea606..860f2768 100644 --- a/Firmware/Targets/F407VG/Core/Inc/target_constants.h +++ b/Firmware/Targets/F407VG/Core/Inc/target_constants.h @@ -138,4 +138,6 @@ extern CAN_HandleTypeDef hcan1; #define CCRAM_SEC ".ccmram" +#define SIGBNATUREBASEADR 0 // First block in OTP + #endif /* INC_TARGET_CONSTANTS_H_ */ diff --git a/README.md b/README.md index 78538952..36d4b17a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ The GUI for configuration is found at [OpenFFBoard-configurator](https://github. These git submodules can be pulled with `git submodule init` and `git submodule update` Updates often require matching firmware and GUI versions! +For binary releases check the [github release section](https://github.com/Ultrawipf/OpenFFBoard/releases). ## Documentation Documentation will be updated in the [GitHub Wiki](https://github.com/Ultrawipf/OpenFFBoard/wiki). @@ -44,6 +45,43 @@ Code summary and documentation of the latest stable version is available as a [D For discussion and progress updates we have a [Discord server](https://discord.com/invite/gHtnEcP). + +## Features +For more information, [game compatibility](https://github.com/Ultrawipf/OpenFFBoard/wiki/Games-setup#compatibility-list) and common setups check the [GitHub Wiki](https://github.com/Ultrawipf/OpenFFBoard/wiki). +### Main modes: +See [other mainclasses](https://github.com/Ultrawipf/OpenFFBoard/wiki/Commands#other-mainclasses) for full list. +* **FFB Wheel**: USB 1 Axis force feedback device with HID FFB support, multiple analog axis inputs (see analog sources) and digital buttons (see digital sources) +* **FFB Joystick**: USB 2 Axis force feedback device + +* **EXT FFB Gamepad**: USB 2 axis gamepad device without HID FFB. Use this for simple non FFB devices or send custom FFB data via commands (See [Ext FFB mode](https://github.com/Ultrawipf/OpenFFBoard/wiki/External-FFB-mode)) +* **CAN Remote Analog/Digital**: Send all supported digital and analog inputs as CAN packets to a main FFBoard (Receive using CAN Analog/Digital source) +--- +* **CAN Interface**: GVRET compatible CAN interface for CAN bus debugging + +### Supported motor drivers +|Name|Interface|1ch Wheel support|2ch Joystick support|Supported motors|Supported encoders|Enc forwarding (see [FFBoard supported encoders](#supported-encoders))|Dual interface encoder +|--|--|--|--|--|--|--|--| +**OpenFFBoard TMC4671**|SPI|yes✅|limited⚠️|3 phase servo (BLDC), 2ph stepper (w. encoder), (DC) | ABZ, SinCos, (Digital Hall), Analog hall | yes✅|yes✅ +ODrive|CAN|yes✅|yes✅|3 phase servo | ABZ, SPI, Internal (See ODrive docs) | no❌|no❌ +VESC|CAN|yes✅|yes✅|3 phase servo| ABZ | no❌|yes✅ +PWM|PWM pins|yes✅|no❌|Any external driver (PWM+Dir, Centered, RC PPM, 2x PWM)| Any [FFBoard encoder](#supported-encoders) | no❌|no❌ +MyActuator|CAN|yes✅|yes✅|Integrated in servo| Internal | no❌|no❌ +Simplemotion|RS432 w. adapter⚠️|yes✅|no❌|3 phase servo, Stepper| ABZ, BISS-C | no❌|no❌ + + +### Supported encoders +Encoders supported directly by FFBoard firmware and main board + +Can be forwarded to TMC4671 using external encoder forwarding + +|Name|Interface|Absolute| +|-|-|-| +|ABZ Quadrature|ABZ|no❌| +|BISS-C|SPI w. Adapter⚠️|yes✅| +|MagnTek (MT6825,MT6835)|SPI + ABZ|yes✅| +|SSI|SPI|yes✅| + + ### Extensions The modular structure means you are free to implement your own main classes. Take a look into the FFBoardMain and ExampleMain class files in the UserExtensions folder.