diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-05-04 14:16:21 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2018-07-24 11:16:05 -0400 |
commit | eba252d3fd963ae10a51bea644f2b0c9e504e559 (patch) | |
tree | d472e0a01f04adedfe5a47a11e91133cd8166da1 /src/thermocouple.c | |
parent | 940db6bd70be981c2002905bbbbb7f41977b79e9 (diff) | |
download | kutter-eba252d3fd963ae10a51bea644f2b0c9e504e559.tar.gz kutter-eba252d3fd963ae10a51bea644f2b0c9e504e559.tar.xz kutter-eba252d3fd963ae10a51bea644f2b0c9e504e559.zip |
thermocouple: Add initial support for common SPI temperature sensing chips
Signed-off-by: Petri Honkala <cruwaller@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src/thermocouple.c')
-rw-r--r-- | src/thermocouple.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/thermocouple.c b/src/thermocouple.c new file mode 100644 index 00000000..5f4f7bb6 --- /dev/null +++ b/src/thermocouple.c @@ -0,0 +1,178 @@ +// Basic support for common SPI controlled thermocouple chips +// +// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com> +// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net> +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include <string.h> // memcpy +#include "board/irq.h" // irq_disable +#include "basecmd.h" // oid_alloc +#include "byteorder.h" // be32_to_cpu +#include "command.h" // DECL_COMMAND +#include "sched.h" // DECL_TASK +#include "spicmds.h" // spidev_transfer + +enum { + TS_CHIP_MAX31855 = 1 << 0, + TS_CHIP_MAX31856 = 1 << 1, + TS_CHIP_MAX31865 = 1 << 2 +}; + +struct thermocouple_spi { + struct timer timer; + uint32_t rest_time; + uint32_t min_value; // Min allowed ADC value + uint32_t max_value; // Max allowed ADC value + struct spidev_s *spi; + uint8_t chip_type, flags; +}; + +enum { + TS_PENDING = 1, +}; + +static struct task_wake thermocouple_wake; + +static uint_fast8_t thermocouple_event(struct timer *timer) { + struct thermocouple_spi *spi = container_of( + timer, struct thermocouple_spi, timer); + // Trigger task to read and send results + sched_wake_task(&thermocouple_wake); + spi->flags |= TS_PENDING; + spi->timer.waketime += spi->rest_time; + return SF_RESCHEDULE; +} + +void +command_config_thermocouple(uint32_t *args) +{ + uint8_t chip_type = args[2]; + if (chip_type > TS_CHIP_MAX31865 || !chip_type) + shutdown("Invalid thermocouple chip type"); + struct thermocouple_spi *spi = oid_alloc( + args[0], command_config_thermocouple, sizeof(*spi)); + spi->timer.func = thermocouple_event; + spi->spi = spidev_oid_lookup(args[1]); + spi->chip_type = chip_type; +} +DECL_COMMAND(command_config_thermocouple, + "config_thermocouple oid=%c spi_oid=%c chip_type=%c"); + +void +command_query_thermocouple(uint32_t *args) +{ + struct thermocouple_spi *spi = oid_lookup( + args[0], command_config_thermocouple); + + sched_del_timer(&spi->timer); + spi->timer.waketime = args[1]; + if (! spi->timer.waketime) + return; + spi->rest_time = args[2]; + spi->min_value = args[3]; + spi->max_value = args[4]; + sched_add_timer(&spi->timer); +} +DECL_COMMAND(command_query_thermocouple, + "query_thermocouple oid=%c clock=%u rest_ticks=%u" + " min_value=%u max_value=%u"); + +static void +thermocouple_respond(struct thermocouple_spi *spi, uint32_t next_begin_time + , uint32_t value, uint8_t fault, uint8_t oid) +{ + /* check the result and stop if below or above allowed range */ + if (value < spi->min_value || value > spi->max_value) { + try_shutdown("Thermocouple ADC out of range"); + } + sendf("thermocouple_result oid=%c next_clock=%u value=%u fault=%c", + oid, next_begin_time, value, fault); +} + +/* Logic of thermocouple K readers MAX6675 and MAX31855 are same */ +static void +thermocouple_handle_max31855(struct thermocouple_spi *spi + , uint32_t next_begin_time, uint8_t oid) +{ + uint8_t msg[4] = { 0x00, 0x00, 0x00, 0x00 }; + spidev_transfer(spi->spi, 1, sizeof(msg), msg); + uint32_t value; + memcpy(&value, msg, sizeof(value)); + value = be32_to_cpu(value); + thermocouple_respond(spi, next_begin_time, value, 0, oid); + // Kill after data send, host decode an error + if (value & 0x04) + try_shutdown("Thermocouple reader fault"); +} + +#define MAX31856_LTCBH_REG 0x0C +#define MAX31856_SR_REG 0x0F + +static void +thermocouple_handle_max31856(struct thermocouple_spi *spi + , uint32_t next_begin_time, uint8_t oid) +{ + uint8_t msg[4] = { MAX31856_LTCBH_REG, 0x00, 0x00, 0x00 }; + spidev_transfer(spi->spi, 1, sizeof(msg), msg); + uint32_t value; + memcpy(&value, msg, sizeof(value)); + value = be32_to_cpu(value) & 0x00ffffff; + // Read faults + msg[0] = MAX31856_SR_REG; + msg[1] = 0x00; + spidev_transfer(spi->spi, 1, 2, msg); + thermocouple_respond(spi, next_begin_time, value, msg[1], oid); +} + +#define MAX31865_RTDMSB_REG 0x01 +#define MAX31865_FAULTSTAT_REG 0x07 + +static void +thermocouple_handle_max31865(struct thermocouple_spi *spi + , uint32_t next_begin_time, uint8_t oid) +{ + uint8_t msg[4] = { MAX31865_RTDMSB_REG, 0x00, 0x00, 0x00 }; + spidev_transfer(spi->spi, 1, 3, msg); + uint32_t value; + memcpy(&value, msg, sizeof(value)); + value = (be32_to_cpu(value) >> 8) & 0xffff; + // Read faults + msg[0] = MAX31865_FAULTSTAT_REG; + msg[1] = 0x00; + spidev_transfer(spi->spi, 1, 2, msg); + thermocouple_respond(spi, next_begin_time, value, msg[1], oid); + // Kill after data send, host decode an error + if (value & 0x0001) + try_shutdown("Thermocouple reader fault"); +} + +// task to read thermocouple and send response +void +thermocouple_task(void) +{ + if (!sched_check_wake(&thermocouple_wake)) + return; + uint8_t oid; + struct thermocouple_spi *spi; + foreach_oid(oid, spi, command_config_thermocouple) { + if (!(spi->flags & TS_PENDING)) + continue; + irq_disable(); + uint32_t next_begin_time = spi->timer.waketime; + spi->flags &= ~TS_PENDING; + irq_enable(); + switch (spi->chip_type) { + case TS_CHIP_MAX31855: + thermocouple_handle_max31855(spi, next_begin_time, oid); + break; + case TS_CHIP_MAX31856: + thermocouple_handle_max31856(spi, next_begin_time, oid); + break; + case TS_CHIP_MAX31865: + thermocouple_handle_max31865(spi, next_begin_time, oid); + break; + } + } +} +DECL_TASK(thermocouple_task); |