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>
This commit is contained in:
Kevin O'Connor
2018-05-04 14:16:21 -04:00
parent 940db6bd70
commit eba252d3fd
8 changed files with 583 additions and 4 deletions

View File

@@ -3,6 +3,6 @@
src-y += sched.c command.c basecmd.c debugcmds.c
src-$(CONFIG_HAVE_GPIO) += gpiocmds.c stepper.c endstop.c
src-$(CONFIG_HAVE_GPIO_ADC) += adccmds.c
src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c
src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
src-$(CONFIG_HAVE_USER_INTERFACE) += lcd_st7920.c lcd_hd44780.c buttons.c

View File

@@ -9,6 +9,7 @@
#include "basecmd.h" // oid_alloc
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_SHUTDOWN
#include "spicmds.h" // spidev_transfer
struct spidev_s {
struct spi_config spi_config;
@@ -53,7 +54,13 @@ DECL_COMMAND(command_config_spi_without_cs,
"config_spi_without_cs oid=%c bus=%u mode=%u rate=%u"
" shutdown_msg=%*s");
static void
struct spidev_s *
spidev_oid_lookup(uint8_t oid)
{
return oid_lookup(oid, command_config_spi);
}
void
spidev_transfer(struct spidev_s *spi, uint8_t receive_data
, uint8_t data_len, uint8_t *data)
{

10
src/spicmds.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef __SPICMDS_H
#define __SPICMDS_H
#include <stdint.h> // uint8_t
struct spidev_s *spidev_oid_lookup(uint8_t oid);
void spidev_transfer(struct spidev_s *spi, uint8_t receive_data
, uint8_t data_len, uint8_t *data);
#endif // stepper.h

178
src/thermocouple.c Normal file
View File

@@ -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);