Welcome to mirror list, hosted at ThFree Co, Russian Federation.

ThermocoupleSensor31856.cpp « Sensors « Heating « src - github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: cc4402e7ff60dadf833e8fff85509492eaa4dad1 (plain)
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
/*
 * ThermocoupleSensor31856.cpp
 *
 *  Created on: 27 Jun 2017
 *      Author: David
 */

#include "ThermocoupleSensor31856.h"
#include "RepRap.h"
#include "Platform.h"
#include "GCodes/GCodeBuffer/GCodeBuffer.h"

const uint32_t MAX31856_Frequency = 4000000;	// maximum for MAX31865 is 5MHz

static const char * const TypeLetters = "BEJKNRST";		// MAX31856 mapping of AVGSEWL bits to thermocouple types
const uint8_t TypeK = 3;

// SPI modes:
// If the inactive state of SCL is LOW (CPOL = 0): (in the case of the MAX31865, this is sampled on the falling edge of CS):
// The MAX31856 changes data after the rising edge of CLK, and samples input data on the falling edge.
// This requires NCPHA = 0.
const uint8_t MAX31856_SpiMode = SPI_MODE_1;

// Define the minimum interval between readings
const uint32_t MinimumReadInterval = 100;		// minimum interval between reads, in milliseconds

// Default configuration registers.
// CR0:
//  CMODE=1		continuous conversion
//  1shot=0		no 1-shot conversion
//  OCFAULT=01	open circuit detection enabled, input resistance <5K
//  CJ=0		cold junction temperature sensing enabled
//  FAULT=1		fault bit and output remain set until cleared explicitly
//  FAULTCLR=1	clear any existing fault
//  50/60Hz=1	reject 50Hz (configurable via F parameter)
const uint8_t DefaultCr0 = 0b10010111;
const uint8_t Cr0ReadMask = 0b10111101;			// bits 1 and 6 auto clear, so ignore the value read

// CR1:
//  Reserved=0
//  AVGSEL=010	4 samples averaged, takes about 80ms
//  TCTYPE=0011	K type thermocouple
const uint8_t DefaultCr1 = 0b00100000;			// the thermocouple type needs to be or'd in
const uint8_t Cr1ReadMask = 0b01111111;			// ignore the reserved bits

// MASK:
//  reserved=00
//  CJhigh = 1	don't assert fault on cold junction temperature high
//  CJlow = 1	don't assert fault on cold junction temperature low
//  TChigh = 1	don't assert fault on thermocouple temperature high
//  TClow = 1	don't assert fault on thermocouple temperature low
//	OV/UV=0		assert fault on under/over voltage
//  Openfault=0	assert fault on open circuit condition
const uint8_t DefaultFaultMask = 0b00111100;

ThermocoupleSensor31856::ThermocoupleSensor31856(unsigned int sensorNum)
	: SpiTemperatureSensor(sensorNum, "Thermocouple (MAX31856)", MAX31856_SpiMode, MAX31856_Frequency),
	  cr0(DefaultCr0), thermocoupleType(TypeK)
{
}

// Configure this temperature sensor
GCodeResult ThermocoupleSensor31856::Configure(GCodeBuffer& gb, const StringRef& reply)
{
	bool seen = false;
	if (!ConfigurePort(gb, reply, seen))
	{
		return GCodeResult::error;
	}
	TryConfigureSensorName(gb, seen);
	if (gb.Seen('F'))
	{
		seen = true;
		if (gb.GetIValue() == 60)
		{
			cr0 &= ~0x01;		// set 60Hz rejection
		}
		else
		{
			cr0 |= 0x01;		// default to 50Hz rejection
		}
	}

	String<2> buf;
	if (gb.TryGetQuotedString('K', buf.GetRef(), seen))
	{
		const char *p;
		if (buf.strlen() == 1 && (p = strchr(TypeLetters, toupper(buf.c_str()[0]))) != nullptr)
		{
			thermocoupleType = p - TypeLetters;
		}
		else
		{
			reply.copy("Bad thermocouple type letter in M305 command");
			return GCodeResult::error;
		}
	}

	if (seen)
	{
		// Initialise the sensor
		InitSpi();

		TemperatureError rslt;
		for (unsigned int i = 0; i < 3; ++i)		// try 3 times
		{
			rslt = TryInitThermocouple();
			if (rslt == TemperatureError::success)
			{
				break;
			}
			delay(MinimumReadInterval);
		}

		lastReadingTime = millis();
		lastResult = rslt;
		lastTemperature = 0.0;

		if (rslt != TemperatureError::success)
		{
			reprap.GetPlatform().MessageF(ErrorMessage, "Failed to initialise thermocouple: %s\n", TemperatureErrorString(rslt));
		}

	}
	else
	{
		CopyBasicDetails(reply);
		reply.catf(", thermocouple type %c, reject %dHz", TypeLetters[thermocoupleType], (cr0 & 0x01) ? 50 : 60);
	}
	return GCodeResult::ok;
}

TemperatureError ThermocoupleSensor31856::TryInitThermocouple() const
{
	const uint8_t modeData[4] = { 0x80, cr0, (uint8_t)(DefaultCr1 | thermocoupleType), DefaultFaultMask };		// write registers 0, 1, 2
	uint32_t rawVal;
	TemperatureError sts = DoSpiTransaction(modeData, ARRAY_SIZE(modeData), rawVal);

	if (sts == TemperatureError::success)
	{
		static const uint8_t readData[4] = { 0x00, 0x00, 0x00, 0x00 };		// read registers 0, 1, 2
		sts = DoSpiTransaction(readData, ARRAY_SIZE(readData), rawVal);
	}

	//debugPrintf("Status %d data %04x\n", (int)sts, rawVal);
	if (sts == TemperatureError::success)
	{
		const uint32_t expectedResponseMask = (0b10111101 << 16)	// bits 1 and 5 of CR0 auto-clear
											| (0b01111111 << 8)		// ignore the reserved bit
											| (0b00111111);			// ignore the reserved bits
		if ((rawVal & expectedResponseMask) != (((modeData[1] << 16) | (modeData[2] << 8) | modeData[3]) & expectedResponseMask))
		{
//			debugPrintf("expected %08x got %08x\n", rawVal & expectedResponseMask, ((modeData[1] << 16) | (modeData[2] << 8) | modeData[3]) & expectedResponseMask);
			sts = TemperatureError::badResponse;
		}
	}

	return sts;
}

void ThermocoupleSensor31856::Poll()
{
	static const uint8_t dataOut[5] = {0x0C, 0x55, 0x55, 0x55, 0x55};	// read registers LTCB0, LTCB1, LTCB2, Fault status
	uint32_t rawVal;
	TemperatureError sts = DoSpiTransaction(dataOut, ARRAY_SIZE(dataOut), rawVal);

	if (sts != TemperatureError::success)
	{
		SetResult(sts);
	}
	else
	{
		lastReadingTime = millis();
		if ((rawVal & 0x00FF) != 0)
		{
			// One or more fault bits is set
			SetResult( (rawVal & 0x02) ? TemperatureError::overOrUnderVoltage
						: (rawVal & 0x01) ? TemperatureError::openCircuit
							: TemperatureError::hardwareError
					 );
			delayMicroseconds(1);										// MAX31856 requires CS to be high for 400ns minimum
			TryInitThermocouple();										// clear fault bits and re-initialise
		}
		else
		{
			const int16_t rawTemp = (int16_t)(rawVal >> 16);			// keep just the most significant 2 bytes and interpret them as signed
			SetResult((float)rawTemp / 16, TemperatureError::success);
		}
	}
}

// End