1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
// 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, TS_CHIP_MAX31856, TS_CHIP_MAX31865, TS_CHIP_MAX6675
};
DECL_ENUMERATION("thermocouple_type", "MAX31855", TS_CHIP_MAX31855);
DECL_ENUMERATION("thermocouple_type", "MAX31856", TS_CHIP_MAX31856);
DECL_ENUMERATION("thermocouple_type", "MAX31865", TS_CHIP_MAX31865);
DECL_ENUMERATION("thermocouple_type", "MAX6675", TS_CHIP_MAX6675);
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_MAX6675)
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 thermocouple_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];
spi->rest_time = args[2];
if (! spi->rest_time)
return;
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)
{
sendf("thermocouple_result oid=%c next_clock=%u value=%u fault=%c",
oid, next_begin_time, value, fault);
/* 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");
}
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");
}
static void
thermocouple_handle_max6675(struct thermocouple_spi *spi
, uint32_t next_begin_time, uint8_t oid)
{
uint8_t msg[2] = { 0x00, 0x00};
spidev_transfer(spi->spi, 1, sizeof(msg), msg);
uint16_t value;
memcpy(&value, msg, sizeof(msg));
value = be16_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");
}
// 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;
case TS_CHIP_MAX6675:
thermocouple_handle_max6675(spi, next_begin_time, oid);
break;
}
}
}
DECL_TASK(thermocouple_task);
|