Open FFBoard
Open source force feedback firmware
SPIButtons.cpp
Go to the documentation of this file.
1/*
2 * ButtonSourceSPI.cpp
3 *
4 * Created on: 11.02.2020
5 * Author: Yannick
6 */
7
8#include <math.h>
9#include <tuple>
10
11#include "constants.h"
12#include <SPIButtons.h>
13#include "eeprom_addresses.h"
14
15static ButtonSourceConfig decodeIntToConf(uint16_t config, uint16_t config_int_2);
16static std::tuple<uint16_t, uint16_t> encodeConfToInt(ButtonSourceConfig* c);
17
18const std::vector<std::string> SPI_Buttons::mode_names = {"Thrustmaster/HEF4021BT","74xx165"};
19const std::vector<std::string> SPI_Buttons::speed_names = {"Fast","Medium","Slow"};
20
22 .name = "SPI Buttons 1" ,
23 .id=CLSID_BTN_SPI,
24 };
26 return info;
27}
28
30 return (external_spi.hasFreePins());
31}
32
34 .name = "SPI Buttons 2" ,
35 .id=CLSID_BTN_SPI,
36 };
38 return info;
39}
40
42 return false;//(external_spi.hasFreePins();
43}
44
45
46// TODO check if pin is free
47SPI_Buttons::SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2)
48 : CommandHandler("spibtn",CLSID_BTN_SPI,0), SPIDevice(external_spi,external_spi.getFreeCsPins()[0]){
49
50 this->configuration_address = configuration_address;
51 this->configuration_address_2 = configuration_address_2;
53
54 this->spiConfig.peripheral.BaudRatePrescaler = speedPresets[this->conf.spi_speed];
55 this->spiConfig.peripheral.FirstBit = SPI_FIRSTBIT_LSB;
56 this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE;
57 this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW;
58
59 initSPI();
60
62 this->setCommandsEnabled(true);
63 ready = true;
64}
65
66
67
72}
73
75
76}
77
78
81 registerCommand("mode", SPIButtons_commands::mode, "SPI mode",CMDFLAG_INFOSTRING | CMDFLAG_GET | CMDFLAG_SET);
82 registerCommand("btncut", SPIButtons_commands::btncut, "Cut buttons right",CMDFLAG_GET | CMDFLAG_SET);
83 registerCommand("btnpol", SPIButtons_commands::btnpol, "Invert",CMDFLAG_GET | CMDFLAG_SET);
84 registerCommand("btnnum", SPIButtons_commands::btnnum, "Number of buttons",CMDFLAG_GET | CMDFLAG_SET);
85 registerCommand("cs", SPIButtons_commands::cs, "SPI CS pin",CMDFLAG_GET | CMDFLAG_SET);
86 registerCommand("spispeed", SPIButtons_commands::spispeed, "SPI speed preset",CMDFLAG_INFOSTRING | CMDFLAG_GET | CMDFLAG_SET);
87}
88
93 this->conf.mode = mode;
95}
96
98 config.numButtons = std::min<uint8_t>(this->maxButtons, config.numButtons);
99 this->conf = config;
100
102 OutputPin* newPin = spiPort.getCsPin(config.cs_num-1); // TODO update internal pin number if requested pin is blocked
103 if(newPin != nullptr){
104 this->spiConfig.cs = *newPin;
105
106 }
108 // Setup presets
110 this->spiConfig.cspol = true;
111 this->conf.cutRight = true;
112 this->spiConfig.peripheral.CLKPolarity = SPI_POLARITY_LOW;
113 this->spiConfig.peripheral.CLKPhase = SPI_PHASE_1EDGE;
114
115 }else if(conf.mode == SPI_BtnMode::PISOSR){
116 this->spiConfig.cspol = false;
117 this->conf.cutRight = false;
118 this->spiConfig.peripheral.CLKPhase = SPI_PHASE_2EDGE;
119 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.
120 }
121// spiPort.takeSemaphore();
122// spiPort.configurePort(&this->spiConfig.peripheral);
123// spiPort.giveSemaphore();
124 initSPI();
125 if(config.numButtons == 64){ // Special case
126 mask = 0xffffffffffffffff;
127 }else{
128 mask = (uint64_t)pow<uint64_t>(2,config.numButtons)-(uint64_t)1; // Must be done completely in 64 bit!
129 }
130 offset = 8 - (config.numButtons % 8);
131
132 // Thrustmaster uses extra bits for IDs
133 if(config.mode == SPI_BtnMode::TM){
134 bytes = 1+((config.numButtons+2)/8);
135 }else{
136 bytes = 1+((config.numButtons-1)/8);
137 }
138
139 this->btnnum = config.numButtons;
140}
141
143 return &this->conf;
144}
145
146void SPI_Buttons::setSpiSpeed(uint8_t speedPreset){
147 speedPreset = clip<uint8_t,uint8_t>(speedPreset,0,this->speedPresets.size());
148 this->conf.spi_speed = speedPreset;
149 this->spiConfig.peripheral.BaudRatePrescaler = this->speedPresets[speedPreset];
150 initSPI();
151}
152
154 auto [configuration_int, cs_num_int] = encodeConfToInt(this->getConfig());
155
156 Flash_Write(configuration_address, configuration_int);
158}
159
161 uint16_t conf_int = Flash_ReadDefault(configuration_address, 0);
162 uint16_t cs_num_int = Flash_ReadDefault(configuration_address_2, 1);
163
164 setConfig(decodeIntToConf(conf_int, cs_num_int));
165}
166
167void SPI_Buttons::process(uint64_t* buf){
168 if(offset){
169 if(this->conf.cutRight){
170 *buf = *buf >> offset;
171 }else{
172 *buf = *buf & this->mask;
173 }
174 }
175 if(conf.invert)
176 *buf = (~*buf);
177 *buf = *buf & mask;
178}
179
180__attribute__((optimize("-Ofast")))
181uint8_t SPI_Buttons::readButtons(uint64_t* buf){
182 memcpy(buf,this->spi_buf,std::min<uint8_t>(this->bytes,8));
183 process(buf); // give back last buffer
184
185 if(spiPort.isTaken() || !ready)
186 return this->btnnum; // Don't wait.
187
188 // CS pin and semaphore managed by spi port
189 spiPort.receive_DMA(spi_buf, bytes, this);
190
191 return this->btnnum;
192}
193
194std::string SPI_Buttons::printModes(const std::vector<std::string>& names){
195 std::string reply;
196 for(uint8_t i = 0; i<names.size();i++){
197 reply+= names[i] + ":" + std::to_string(i)+"\n";
198 }
199 return reply;
200}
201
202CommandStatus SPI_Buttons::command(const ParsedCommand& cmd,std::vector<CommandReply>& replies){
203
204 switch(static_cast<SPIButtons_commands>(cmd.cmdId)){
206 if(cmd.type == CMDtype::set){
207 ButtonSourceConfig* c = this->getConfig();
208 c->numButtons = cmd.val;
209 this->setConfig(*c);
210 }else if(cmd.type == CMDtype::get){
211 replies.emplace_back(this->getBtnNum());
212 }else{
213 return CommandStatus::ERR;
214 }
215 break;
217 if(cmd.type == CMDtype::set){
218 ButtonSourceConfig* c = this->getConfig();
219 c->invert = cmd.val != 0;
220 this->setConfig(*c);
221 }else if(cmd.type == CMDtype::get){
222 replies.emplace_back(this->getConfig()->invert ? 1 : 0);
223 }else{
224 return CommandStatus::ERR;
225 }
226 break;
228 if(cmd.type == CMDtype::set){
229 ButtonSourceConfig* c = this->getConfig();
230 c->cutRight = cmd.val != 0;
231 this->setConfig(*c);
232 }else if(cmd.type == CMDtype::get){
233 replies.emplace_back(this->getConfig()->cutRight ? 1 : 0);
234 }else{
235 return CommandStatus::ERR;
236 }
237 break;
239 if(cmd.type == CMDtype::set){
240 setMode((SPI_BtnMode)cmd.val);
241 }else if(cmd.type == CMDtype::get){
242 replies.emplace_back((uint8_t)this->conf.mode);
243 }else if(cmd.type == CMDtype::info){
244 replies.emplace_back(printModes(this->mode_names));
245 }else{
246 return CommandStatus::ERR;
247 }
248 break;
249
251 if(cmd.type == CMDtype::set){
252 setSpiSpeed(cmd.val);
253 }else if(cmd.type == CMDtype::get){
254 replies.emplace_back((uint8_t)this->conf.spi_speed);
255 }else if(cmd.type == CMDtype::info){
256 replies.emplace_back(printModes(this->speed_names));
257 }else{
258 return CommandStatus::ERR;
259 }
260 break;
261
263 if (handleGetSet(cmd, replies, this->conf.cs_num) == CommandStatus::OK ) {
264 setConfig(this->conf);
265 }
266 break;
267
268 default:
270 }
271
272 return CommandStatus::OK;
273}
274
275static ButtonSourceConfig decodeIntToConf(uint16_t config_int, uint16_t config_int_2){
277 c.numButtons = (config_int & 0x3F) + 1;
278 c.invert = (config_int >> 6) & 0x1;
279 c.cutRight = (config_int >> 7) & 0x1;
280 c.mode = SPI_BtnMode(config_int >> 8);
281 c.cs_num = (config_int_2 & 0x3);
282 c.spi_speed = (config_int_2 >> 3) & 0x3;
283 return c;
284}
285static std::tuple<uint16_t, uint16_t> encodeConfToInt(ButtonSourceConfig* c){
286 uint16_t val = (c->numButtons-1) & 0x3F; // 1-64
287 val |= c->invert << 6;
288 val |= c->cutRight << 7;
289 val |= (uint16_t)c->mode << 8;
290 uint16_t val2 = c->cs_num & 0x3;
291 val2 |= (c->spi_speed & 0x3) << 3;
292
293 return { val, val2 };
294}
295
CommandStatus
uint16_t val2
static ButtonSourceConfig decodeIntToConf(uint16_t config, uint16_t config_int_2)
Definition: SPIButtons.cpp:275
static std::tuple< uint16_t, uint16_t > encodeConfToInt(ButtonSourceConfig *c)
Definition: SPIButtons.cpp:285
__attribute__((optimize("-Ofast"))) uint8_t SPI_Buttons
Definition: SPIButtons.cpp:180
SPI_BtnMode
Definition: SPIButtons.h:22
uint16_t btnnum
Definition: ButtonSource.h:38
virtual uint16_t getBtnNum()
void registerCommand(const char *cmd, const ID cmdid, const char *help=nullptr, uint32_t flags=0)
virtual void setCommandsEnabled(bool enable)
static CommandStatus handleGetSet(const ParsedCommand &cmd, std::vector< CommandReply > &replies, TVal &value)
const ClassIdentifier getInfo() override
Definition: SPIButtons.cpp:25
static bool isCreatable()
Definition: SPIButtons.cpp:29
static ClassIdentifier info
Definition: SPIButtons.h:94
static ClassIdentifier info
Definition: SPIButtons.h:107
static bool isCreatable()
Definition: SPIButtons.cpp:41
const ClassIdentifier getInfo() override
Definition: SPIButtons.cpp:37
virtual ~SPI_Buttons()
Definition: SPIButtons.cpp:74
uint8_t bytes
Definition: SPIButtons.h:75
CommandStatus command(const ParsedCommand &cmd, std::vector< CommandReply > &replies) override
Definition: SPIButtons.cpp:202
void setSpiSpeed(uint8_t speedPreset)
Definition: SPIButtons.cpp:146
static const std::vector< std::string > speed_names
Definition: SPIButtons.h:43
void restoreFlash()
Definition: SPIButtons.cpp:160
void setConfig(ButtonSourceConfig config)
Definition: SPIButtons.cpp:97
ButtonSourceConfig conf
Definition: SPIButtons.h:79
uint16_t configuration_address
Definition: SPIButtons.h:69
virtual ButtonSourceConfig * getConfig()
Definition: SPIButtons.cpp:142
SPI_Buttons(uint16_t configuration_address, uint16_t configuration_address_2)
Definition: SPIButtons.cpp:47
static constexpr std::array< uint32_t, 3 > speedPresets
Definition: SPIButtons.h:83
uint64_t mask
Definition: SPIButtons.h:76
const uint8_t maxButtons
Definition: SPIButtons.h:56
static const std::vector< std::string > mode_names
Definition: SPIButtons.h:42
void saveFlash()
Definition: SPIButtons.cpp:153
uint16_t configuration_address_2
Definition: SPIButtons.h:70
void initSPI()
Definition: SPIButtons.cpp:68
bool ready
Definition: SPIButtons.h:71
void setMode(SPI_BtnMode mode)
Definition: SPIButtons.cpp:92
std::string printModes(const std::vector< std::string > &names)
Definition: SPIButtons.cpp:194
uint8_t offset
Definition: SPIButtons.h:77
void registerCommands()
Definition: SPIButtons.cpp:79
void process(uint64_t *buf)
Definition: SPIButtons.cpp:167
Definition: SPI.h:100
SPIPort & spiPort
Definition: SPI.h:124
SPIConfig spiConfig
Definition: SPI.h:125
bool hasFreePins()
Definition: SPI.cpp:220
void takeSemaphore()
Definition: SPI.cpp:186
bool reserveCsPin(OutputPin pin)
Definition: SPI.cpp:25
OutputPin * getCsPin(uint16_t idx)
Definition: SPI.cpp:65
void configurePort(SPI_InitTypeDef *config)
Definition: SPI.cpp:72
void giveSemaphore()
Definition: SPI.cpp:199
bool freeCsPin(OutputPin pin)
Definition: SPI.cpp:37
SPIPort external_spi
TVal Flash_ReadDefault(uint16_t adr, TVal def)
Definition: flash_helpers.h:36
bool Flash_Write(uint16_t adr, uint16_t dat)
static void * memcpy(void *dst, const void *src, size_t n)
Definition: ringbuffer.c:8
SPI_BtnMode mode
Definition: SPIButtons.h:29
uint8_t numButtons
Definition: SPIButtons.h:26
const char * name
uint32_t cmdId
SPI_InitTypeDef peripheral
Definition: SPI.h:38
OutputPin cs
Definition: SPI.h:34
bool cspol
CSPOL=true === active low.
Definition: SPI.h:37