rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
max3185x.cpp
Go to the documentation of this file.
1/**
2 * @file max3185x.cpp
3 * @brief MAX31855 and MAX31856 Thermocouple-to-Digital Converter driver
4 *
5 *
6 * http://datasheets.maximintegrated.com/en/ds/MAX31855.pdf
7 * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31856.pdf
8 *
9 *
10 * Read-only (MAX31855), RW (MAX31956) communication over 5MHz SPI
11 *
12 * @date Sep 17, 2014
13 * @author Andrey Belomutskiy, (c) 2012-2020
14 *
15 * @author Andrey Gusakov, 2024
16 *
17 */
18
19#include "pch.h"
20#include "max3185x.h"
21
22#include "hardware.h"
23
24#if EFI_PROD_CODE
25#include "mpu_util.h"
26#endif /* EFI_PROD_CODE */
27
28#if EFI_MAX_31855
29
30#include "thread_controller.h"
31#include "stored_value_sensor.h"
32
33#ifndef MAX3185X_REFRESH_TIME
34#define MAX3185X_REFRESH_TIME 100
35// MAX6675 needs 0.22 S for convertion in worst case
36#define MAX6675_REFRESH_TIME 250
37#endif
38
39/* TODO: move all stuff to Max3185xRead class */
40class Max3185xRead final : public ThreadController<UTILITY_THREAD_STACK_SIZE> {
41public:
42 Max3185xRead()
43 : ThreadController("MAX3185X", MAX31855_PRIO)
44 {
45 }
46
47 typedef enum {
48 UNKNOWN_TYPE = 0,
49 MAX31855_TYPE = 1,
50 MAX31856_TYPE = 2,
51 MAX6675_TYPE = 3
52 } Max3185xType;
53
54 typedef enum {
55 MAX3185X_OK = 0,
56 MAX3185X_OPEN_CIRCUIT = 1,
57 MAX3185X_SHORT_TO_GND = 2,
58 MAX3185X_SHORT_TO_VCC = 3,
59 MAX3185X_NO_REPLY = 4,
60 MAX3185X_NOT_ENABLED = 5,
61 } Max3185xState;
62
64 driver = getSpiDevice(device);
65
66 if (driver) {
67 /* WARN: this will clear all other bits in cr1 */
68 //spiConfig.cr1 = getSpiPrescaler(_5MHz, device);
69 for (size_t i = 0; i < EGT_CHANNEL_COUNT; i++) {
70 auto& sensor = egtSensors[i];
71
72 m_cs[i] = Gpio::Invalid;
73 types[i] = UNKNOWN_TYPE;
74
75 // If there's already another (CAN?) EGT sensor configured,
76 // don't configure this one.
78 continue;
79
80 // get CS pin and mark used!
81 if (isBrainPinValid(cs[i])) {
82 initSpiCs(&spiConfig, cs[i]);
83 m_cs[i] = cs[i];
84
86 }
87 }
89 return 0;
90 }
91
92 efiPrintf("EGT not configured");
93 return -1;
94 }
95
96 void stop(void) {
98
99 for (size_t i = 0; i < EGT_CHANNEL_COUNT; i++) {
100 if (!isBrainPinValid(m_cs[i])) {
101 continue;
102 }
103
104 auto& sensor = egtSensors[i];
105
106 brain_pin_markUnused(m_cs[i]);
108 }
109 }
110
111 void ThreadTask() override {
112 while (!chThdShouldTerminateX()) {
113 sysinterval_t refreshTime = MAX3185X_REFRESH_TIME;
114 for (int i = 0; i < EGT_CHANNEL_COUNT; i++) {
115 float value;
116
117 Max3185xState ret = getMax3185xEgtValues(i, &value, NULL);
118 if (ret == MAX3185X_OK) {
119 auto& sensor = egtSensors[i];
120
122
123 // this one is slow
124 if (types[i] == MAX6675_TYPE) {
125 refreshTime = MAX6675_REFRESH_TIME;
126 }
127 } else {
128 /* TODO: report error code? */
129 }
130 }
131
132 chThdSleepMilliseconds(refreshTime);
133 }
134
135 chThdExit((msg_t)0x0);
136 }
137
138 /* Debug stuff */
139 void showEgtInfo() {
140 #if EFI_PROD_CODE
142
143 efiPrintf("EGT spi: %d", engineConfiguration->max31855spiDevice);
144
145 for (int i = 0; i < EGT_CHANNEL_COUNT; i++) {
146 if (isBrainPinValid(m_cs[i])) {
147 efiPrintf("EGT CS %d @%s", i + 1, hwPortname(m_cs[i]));
148 }
149 }
150 #endif
151 }
152
153 void egtRead() {
154 if (driver == NULL) {
155 efiPrintf("No SPI selected for EGT");
156 return;
157 }
158
159 efiPrintf("Reading egt(s)");
160
161 for (size_t i = 0; i < EGT_CHANNEL_COUNT; i++) {
162 float temp, refTemp;
163 Max3185xState code = getMax3185xEgtValues(i, &temp, &refTemp);
164
165 efiPrintf("egt%d: type %s, code=%d (%s)", i + 1, getMax3185xTypeName(types[i]), code, getMax3185xErrorCodeName(code));
166
167 if (code == MAX3185X_OK) {
168 efiPrintf(" temperature %.4f reference temperature %.2f", temp, refTemp);
169 }
170 }
171 }
172
173private:
174 // bits D17 and D3 are always expected to be zero
175 #define MAX31855_RESERVED_BITS 0x20008
176 // bit D3 is always expected to be zero, D15 is dummy sign bit also always zero
177 #define MAX6675_RESERVED_BITS 0x8002
178
179 brain_pin_e m_cs[EGT_CHANNEL_COUNT];
180
181 SPIDriver *driver;
182
183 /* TODO: validate */
184 SPIConfig spiConfig = {
185 .circular = false,
186#ifdef _CHIBIOS_RT_CONF_VER_6_1_
187 .end_cb = NULL,
188#else
189 .slave = false,
190 .data_cb = NULL,
191 .error_cb = NULL,
192#endif
193 .ssport = NULL,
194 .sspad = 0,
195 .cr1 =
196 SPI_CR1_8BIT_MODE |
197 SPI_CR1_SSM |
198 SPI_CR1_SSI |
199 ((5 << SPI_CR1_BR_Pos) & SPI_CR1_BR) | /* div = 64 */
200 SPI_CR1_MSTR |
201 /* SPI_CR1_CPOL | */ // = 0
202 SPI_CR1_CPHA | // = 1
203 0,
204 .cr2 = SPI_CR2_8BIT_MODE
205 };
206
207 const char * getMax3185xErrorCodeName(Max3185xState code) {
208 switch (code) {
209 case MAX3185X_OK:
210 return "Ok";
211 case MAX3185X_OPEN_CIRCUIT:
212 return "Open";
213 case MAX3185X_SHORT_TO_GND:
214 return "short gnd";
215 case MAX3185X_SHORT_TO_VCC:
216 return "short VCC";
217 case MAX3185X_NO_REPLY:
218 return "no reply";
219 case MAX3185X_NOT_ENABLED:
220 return "not enabled";
221 default:
222 return "invalid";
223 }
224 }
225
226 const char *getMax3185xTypeName(Max3185xType type) {
227 switch (type) {
228 case MAX31855_TYPE:
229 return "max31855";
230 case MAX31856_TYPE:
231 return "max31856";
232 case MAX6675_TYPE:
233 return "max6675";
234 default:
235 return "unknown";
236 }
237 }
238
239 int spi_txrx(size_t channel, uint8_t tx[], uint8_t rx[], size_t n)
240 {
241 brain_pin_e cs = m_cs[channel];
242
243 if ((!isBrainPinValid(cs)) || (driver == NULL)) {
244 return -1;
245 }
246
247 /* Set proper CS gpio */
248 initSpiCsNoOccupy(&spiConfig, cs);
249
250 /* Acquire ownership of the bus. */
251 spiAcquireBus(driver);
252 /* Setup transfer parameters. */
253 spiStart(driver, &spiConfig);
254 /* Slave Select assertion. */
255 spiSelect(driver);
256 spiExchange(driver, n, tx, rx);
257 /* Slave Select de-assertion. */
258 spiUnselect(driver);
259 /* Ownership release. */
260 spiReleaseBus(driver);
261
262 /* no errors for now */
263 return 0;
264 }
265
266 int spi_rx16(size_t channel, uint16_t *data)
267 {
268 int ret;
269 /* dummy */
270 uint8_t tx[2] = {0};
271 uint8_t rx[2];
272
273 ret = spi_txrx(channel, tx, rx, 2);
274 if (ret) {
275 return ret;
276 }
277 if (data) {
278 *data = (rx[0] << 8) |
279 (rx[1] << 0);
280 }
281
282 return 0;
283 }
284
285 int spi_rx32(size_t channel, uint32_t *data)
286 {
287 int ret;
288 /* dummy */
289 uint8_t tx[4] = {0};
290 uint8_t rx[4];
291
292 ret = spi_txrx(channel, tx, rx, 4);
293 if (ret) {
294 return ret;
295 }
296 if (data) {
297 *data = (rx[0] << 24) |
298 (rx[1] << 16) |
299 (rx[2] << 8) |
300 (rx[3] << 0);
301 }
302
303 return 0;
304 }
305
306 Max3185xType detect(size_t channel)
307 {
308 int ret;
309 uint8_t rx[4];
310 uint8_t tx[4];
311
312 /* try to apply settings to max31956 and then read back settings */
313 // Wr, register 0x00
314 tx[0] = 0x00 | BIT(7);
315 // CR0: 50Hz mode
316 // Change the notch frequency only while in the "Normally Off" mode - not in the Automatic
317 tx[1] = 0x01;
318 ret = spi_txrx(channel, tx, rx, 2);
319 if (ret) {
320 return UNKNOWN_TYPE;
321 }
322
323 // CR0: Automatic Conversion mode, OCFAULT = 2, 50Hz mode
324 tx[1] = BIT(7) | BIT(0) | (2 << 4);
325 // CR1: 4 samples average, K type
326 tx[2] = (2 << 4) | (3 << 0);
327 ret = spi_txrx(channel, tx, rx, 3);
328 if (ret) {
329 return UNKNOWN_TYPE;
330 }
331
332 /* Now readback settings */
333 tx[0] = 0x00;
334 ret = spi_txrx(channel, tx, rx, 4);
335 if ((rx[1] == tx[1]) && (rx[2] == tx[2])) {
336 return MAX31856_TYPE;
337 }
338
339 /* in case of max31855 we get standart reply with few reserved, always zero bits */
340 uint32_t data = (rx[0] << 24) |
341 (rx[1] << 16) |
342 (rx[2] << 8) |
343 (rx[3] << 0);
344
345 /* MISO is constantly low or high */
346 if ((data == 0xffffffff) || (data == 0x0)) {
347 return UNKNOWN_TYPE;
348 }
349
350 /* MAX6675 replyes with 16 bit. */
351 if ((data >> 16) == (data & 0xffff)) {
352 return MAX6675_TYPE;
353 }
354
355 if ((data & MAX31855_RESERVED_BITS) == 0x0) {
356 return MAX31855_TYPE;
357 }
358
359 return UNKNOWN_TYPE;
360 }
361
362 Max3185xState getMax31855ErrorCode(uint32_t egtPacket) {
363 #define MAX33855_FAULT_BIT BIT(16)
364 #define MAX33855_OPEN_BIT BIT(0)
365 #define MAX33855_GND_BIT BIT(1)
366 #define MAX33855_VCC_BIT BIT(2)
367
368 if (((egtPacket & MAX31855_RESERVED_BITS) != 0) ||
369 (egtPacket == 0x0) ||
370 (egtPacket == 0xffffffff)) {
371 return MAX3185X_NO_REPLY;
372 } else if ((egtPacket & MAX33855_OPEN_BIT) != 0) {
373 return MAX3185X_OPEN_CIRCUIT;
374 } else if ((egtPacket & MAX33855_GND_BIT) != 0) {
375 return MAX3185X_SHORT_TO_GND;
376 } else if ((egtPacket & MAX33855_VCC_BIT) != 0) {
377 return MAX3185X_SHORT_TO_VCC;
378 } else {
379 return MAX3185X_OK;
380 }
381 }
382
383 Max3185xState getMax31855EgtValues(size_t channel, float *temp, float *coldJunctionTemp) {
384 uint32_t packet;
385 Max3185xState code = MAX3185X_NO_REPLY;
386 int ret;
387
388 ret = spi_rx32(channel, &packet);
389 if (ret == 0) {
390 code = getMax31855ErrorCode(packet);
391 }
392
393 if (code != MAX3185X_OK) {
394 return code;
395 }
396
397 if (temp) {
398 // bits 31:18, 0.25C resolution (1/4 C)
399 int16_t tmp = (packet >> 18) & 0x3fff;
400 /* extend sign */
401 tmp = tmp << 2;
402 tmp = tmp >> 2; /* shifting right signed is not a good idea */
403 *temp = (float) tmp * 0.25;
404 }
405 if (coldJunctionTemp) {
406 // bits 15:4, 0.0625C resolution (1/16 C)
407 int16_t tmp = (packet >> 4) & 0xfff;
408 /* extend sign */
409 tmp = tmp << 4;
410 tmp = tmp >> 4; /* shifting right signed is not a good idea */
411 *coldJunctionTemp = (float)tmp * 0.0625;
412 }
413
414 return code;
415 }
416
417 Max3185xState getMax6675ErrorCode(uint16_t egtPacket) {
418 #define MAX6675_OPEN_BIT BIT(2)
419
420 if (((egtPacket & MAX6675_RESERVED_BITS) != 0) ||
421 (egtPacket == 0x0) ||
422 (egtPacket == 0xffff)) {
423 return MAX3185X_NO_REPLY;
424 } else if ((egtPacket & MAX6675_OPEN_BIT) != 0) {
425 return MAX3185X_OPEN_CIRCUIT;
426 } else {
427 return MAX3185X_OK;
428 }
429 }
430
431 Max3185xState getMax31856EgtValues(size_t channel, float *temp, float *coldJunctionTemp)
432 {
433 uint8_t rx[7];
434 /* read Cold-Junction temperature MSB, LSB, Linearized TC temperature 3 bytes and Fault Status */
435 uint8_t tx[7] = {0x0a};
436
437 int ret = spi_txrx(channel, tx, rx, 7);
438 if (ret) {
439 return MAX3185X_NO_REPLY;
440 }
441
442 if (rx[6] & BIT(0)) {
443 return MAX3185X_OPEN_CIRCUIT;
444 } else if (rx[6] & BIT(1)) {
445 return MAX3185X_SHORT_TO_VCC;
446 }
447
448 if (temp) {
449 // 10 bit before point and 7 bits after
450 int32_t tmp = (rx[3] << 11) | (rx[4] << 3) | (rx[5] >> 5);
451 /* extend sign: move top bit 18 to 31 */
452 tmp = tmp << 13;
453 tmp = tmp >> 13; /* shifting right signed is not a good idea */
454 *temp = ((float)tmp) / 128.0;
455 }
456
457 /* convert float to int */
458 if (coldJunctionTemp) {
459 int16_t tmp = (rx[1] << 6) | (rx[2] >> 2);
460 /* extend sign */
461 tmp = tmp << 2;
462 tmp = tmp >> 2; /* shifting right signed is not a good idea */
463 *coldJunctionTemp = ((float)tmp) / 64.0;
464 }
465
466 return MAX3185X_OK;
467 }
468
469 Max3185xState getMax6675EgtValues(size_t channel, float *temp, float *coldJunctionTemp) {
470 uint16_t packet;
471 Max3185xState code = MAX3185X_NO_REPLY;
472 int ret;
473
474 ret = spi_rx16(channel, &packet);
475 if (ret == 0) {
476 code = getMax6675ErrorCode(packet);
477 }
478
479 if (code != MAX3185X_OK) {
480 return code;
481 }
482
483 if (temp) {
484 // bits 14:3, 0.25C resolution
485 uint16_t tmp = (packet >> 3) & 0x0fff;
486 *temp = (float) tmp * 0.25;
487 }
488 if (coldJunctionTemp) {
489 // this chip does not provide
490 *coldJunctionTemp = -273.15;
491 }
492
493 return code;
494 }
495
496 Max3185xState getMax3185xEgtValues(size_t channel, float *temp, float *coldJunctionTemp) {
497 Max3185xState ret;
498
499 if ((!isBrainPinValid(m_cs[channel])) || (!driver)) {
500 return MAX3185X_NOT_ENABLED;
501 }
502
503 /* if chip type is not detected yet try to detect */
504 if (types[channel] == UNKNOWN_TYPE) {
505 types[channel] = detect(channel);
506 }
507
508 /* failed? bail out */
509 if (types[channel] == UNKNOWN_TYPE) {
510 return MAX3185X_NO_REPLY;
511 }
512
513 if (types[channel] == MAX31855_TYPE) {
514 ret = getMax31855EgtValues(channel, temp, coldJunctionTemp);
515 } else if (types[channel] == MAX31856_TYPE) {
516 ret = getMax31856EgtValues(channel, temp, coldJunctionTemp);
517 } else if (types[channel] == MAX6675_TYPE) {
518 ret = getMax6675EgtValues(channel, temp, coldJunctionTemp);
519 }
520
521 if (ret == MAX3185X_NO_REPLY) {
522 types[channel] = UNKNOWN_TYPE;
523 }
524
525 return ret;
526 }
527
528 Max3185xType types[EGT_CHANNEL_COUNT];
529
530 StoredValueSensor egtSensors[EGT_CHANNEL_COUNT] = {
531 { SensorType::EGT1, MS2NT(MAX6675_REFRESH_TIME * 3) },
532 { SensorType::EGT2, MS2NT(MAX6675_REFRESH_TIME * 3) },
533 { SensorType::EGT3, MS2NT(MAX6675_REFRESH_TIME * 3) },
534 { SensorType::EGT4, MS2NT(MAX6675_REFRESH_TIME * 3) },
535 { SensorType::EGT5, MS2NT(MAX6675_REFRESH_TIME * 3) },
536 { SensorType::EGT6, MS2NT(MAX6675_REFRESH_TIME * 3) },
537 { SensorType::EGT7, MS2NT(MAX6675_REFRESH_TIME * 3) },
538 { SensorType::EGT8, MS2NT(MAX6675_REFRESH_TIME * 3) }
539 };
540};
541
542static Max3185xRead instance;
543
544static void showEgtInfo() {
545 instance.showEgtInfo();
546}
547
548static void egtRead() {
549 instance.egtRead();
550}
551
553 addConsoleAction("egtinfo", (Void) showEgtInfo);
554 addConsoleAction("egtread", (Void) egtRead);
555
556 startMax3185x(device, max31855_cs);
557}
558
560 instance.stop();
561}
562
564{
565 instance.start(device, max31855_cs);
566}
567
568#endif /* EFI_MAX_31855 */
uint16_t channel
Definition adc_inputs.h:104
void initSpiCsNoOccupy(SPIConfig *spiConfig, brain_pin_e csPin)
Definition at32_spi.cpp:234
void initSpiCs(SPIConfig *spiConfig, brain_pin_e csPin)
Definition at32_spi.cpp:241
uint8_t code
Definition bluetooth.cpp:40
bool Register()
Definition sensor.cpp:131
virtual bool hasSensor() const
Definition sensor.h:141
SensorType type() const
Definition sensor.h:162
void unregister()
Definition sensor.cpp:135
Base class for sensors that compute a value on one thread, and want to make it available to consumers...
void setValidValue(float value, efitick_t timestamp)
A base class for a controller that requires its own thread.
virtual void ThreadTask()=0
void start()
Start the thread.
void stop()
Request thread termination and waits for termination.
void addConsoleAction(const char *token, Void callback)
Register console action without parameters.
void(* Void)(void)
@ Invalid
efitick_t getTimeNowNt()
Definition efitime.cpp:19
static constexpr engine_configuration_s * engineConfiguration
SPIDriver * getSpiDevice(spi_device_e spiDevice)
Definition hardware.cpp:149
static Lps25 device
Definition init_baro.cpp:4
static Lps25Sensor sensor(device)
void stopMax3185x()
Definition max3185x.cpp:559
static void showEgtInfo()
Definition max3185x.cpp:544
void initMax3185x(spi_device_e device, egt_cs_array_t max31855_cs)
Definition max3185x.cpp:552
void startMax3185x(spi_device_e device, egt_cs_array_t max31855_cs)
Definition max3185x.cpp:563
static void egtRead()
Definition max3185x.cpp:548
static Max3185xRead instance
Definition max3185x.cpp:542
const char * hwPortname(brain_pin_e brainPin)
void brain_pin_markUnused(brain_pin_e brainPin)
bool isBrainPinValid(brain_pin_e brainPin)
spi_device_e
brain_pin_e[EGT_CHANNEL_COUNT] egt_cs_array_t
void printSpiState()
Definition settings.cpp:46
Base class for a sensor that has its value asynchronously set, then later retrieved by a consumer.