mirror of
https://github.com/MarlinFirmware/Marlin.git
synced 2026-03-05 17:44:48 -07:00
Merge f5eeea318a into a6ebfe14cc
This commit is contained in:
commit
99bd46347c
10 changed files with 767 additions and 3 deletions
|
|
@ -562,6 +562,7 @@
|
|||
* ================================================================
|
||||
* Analog Thermocouple Boards
|
||||
* ================================================================
|
||||
* -18 : ADS1118 with Thermocouple, e.g., Mightyboard rev G/H
|
||||
* -4 : AD8495 with Thermocouple
|
||||
* -1 : AD595 with Thermocouple
|
||||
*
|
||||
|
|
|
|||
|
|
@ -483,6 +483,11 @@
|
|||
#define TEMP_SENSOR_0_IS_AD8495 1
|
||||
#elif TEMP_SENSOR_0 == -1
|
||||
#define TEMP_SENSOR_0_IS_AD595 1
|
||||
#elif TEMP_SENSOR_0 == -18
|
||||
#define HAS_ADS1118 1
|
||||
#define TEMP_SENSOR_0_IS_ADS1118 1
|
||||
#define TEMP_SENSOR_0_ADS_TMIN 0
|
||||
#define TEMP_SENSOR_0_ADS_TMAX 1024
|
||||
#elif TEMP_SENSOR_0 > 0
|
||||
#define TEMP_SENSOR_0_IS_THERMISTOR 1
|
||||
#if TEMP_SENSOR_0 == 1000
|
||||
|
|
@ -526,6 +531,11 @@
|
|||
#define TEMP_SENSOR_1_IS_AD8495 1
|
||||
#elif TEMP_SENSOR_1 == -1
|
||||
#define TEMP_SENSOR_1_IS_AD595 1
|
||||
#elif TEMP_SENSOR_1 == -18
|
||||
#define HAS_ADS1118 1
|
||||
#define TEMP_SENSOR_1_IS_ADS1118 1
|
||||
#define TEMP_SENSOR_1_ADS_TMIN 0
|
||||
#define TEMP_SENSOR_1_ADS_TMAX 1024
|
||||
#elif TEMP_SENSOR_1 > 0
|
||||
#define TEMP_SENSOR_1_IS_THERMISTOR 1
|
||||
#if TEMP_SENSOR_1 == 1000
|
||||
|
|
|
|||
|
|
@ -118,6 +118,10 @@
|
|||
#warning "Warning! Don't use dummy thermistors (998/999) for final build!"
|
||||
#endif
|
||||
|
||||
#if ANY_THERMISTOR_IS(-18)
|
||||
#warning "ADS1118 support (-18) is in development"
|
||||
#endif
|
||||
|
||||
#if NONE(HAS_RESUME_CONTINUE, HOST_PROMPT_SUPPORT, UNIT_TEST, NO_USER_FEEDBACK_WARNING)
|
||||
#warning "Your Configuration provides no method to acquire user feedback! (Define NO_USER_FEEDBACK_WARNING to suppress this warning.)"
|
||||
#endif
|
||||
|
|
|
|||
339
Marlin/src/libs/adc/adc_ads1118.cpp
Normal file
339
Marlin/src/libs/adc/adc_ads1118.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* adc_ads1118.cpp - library for Texas Instruments ADS1118 - 16-Bit Analog-to-Digital Converter
|
||||
* based in the sailfish code for ADS1118, ThermocoupleReader, TemperatureTable
|
||||
* For implementation details, please take a look at the datasheet:
|
||||
* https://www.ti.com/product/ADS1118
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfig.h"
|
||||
|
||||
#if HAS_ADS1118
|
||||
|
||||
#include "adc_ads1118.h"
|
||||
|
||||
#include "../../HAL/shared/Delay.h"
|
||||
#include "../../core/macros.h"
|
||||
|
||||
#define ADS1118_CONV_MS 10
|
||||
#define ADS1118_CH_MASK 12
|
||||
|
||||
// Constructor
|
||||
void ADS1118::init(uint8_t cs, uint8_t mosi, uint8_t miso, uint8_t sck) {
|
||||
cs_pin = cs; mosi_pin = mosi; miso_pin = miso; sck_pin = sck;
|
||||
|
||||
//#define TEMP_0_CS_PIN 79 // E6
|
||||
//#define TEMP_0_SCK_PIN 78 // E2
|
||||
//#define TEMP_0_MISO_PIN 80 // E7
|
||||
//#define TEMP_0_MOSI_PIN 84 // H2
|
||||
|
||||
pinMode(cs_pin, OUTPUT);
|
||||
pinMode(mosi_pin, OUTPUT);
|
||||
pinMode(sck_pin, OUTPUT);
|
||||
pinMode(miso_pin, INPUT);
|
||||
|
||||
deselect();
|
||||
sckLow();
|
||||
}
|
||||
|
||||
// Sets ADS to start a single shot conversion. It will be read async when ready (after ADS1118_CONV_MS), non blocking
|
||||
void ADS1118::startConversion(const uint8_t pair) {
|
||||
if (isBusy) return;
|
||||
|
||||
uint16_t config = 0x858B; // 0b 1000 0101 1000 1011 : SS start, single-ended off, gain ±2.048V, single-shot mode, 128SPS, ADC mode, Pullup enable, Write config
|
||||
switch (pair) {
|
||||
default:
|
||||
case 0: config |= (0x0 << ADS1118_CH_MASK); currentchannel = 0; break; // AIN0-AIN1
|
||||
case 1: config |= (0x3 << ADS1118_CH_MASK); currentchannel = 1; break; // AIN2-AIN3
|
||||
}
|
||||
|
||||
digitalWrite(cs_pin, LOW);
|
||||
transfer16(config);
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
|
||||
isBusy = true;
|
||||
startTime = millis();
|
||||
}
|
||||
|
||||
// Determine that a conversion is ready by its elapsed time, if true, reads data and stores in _lastValue
|
||||
bool ADS1118::ready() {
|
||||
if (!isBusy) return true;
|
||||
if (millis() - startTime >= ADS1118_CONV_MS) {
|
||||
digitalWrite(cs_pin, LOW);
|
||||
uint16_t raw = transfer16(0);
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
lastValue = (int16_t)raw;
|
||||
isBusy = false;
|
||||
}
|
||||
return !isBusy;
|
||||
}
|
||||
|
||||
int16_t ADS1118::read() { return lastValue; }
|
||||
|
||||
bool ADS1118::busy() { return isBusy; }
|
||||
|
||||
void ADS1118::loop() { ready(); } // updates status and value
|
||||
|
||||
// Sets ADS to start Continuous conversion mode
|
||||
void ADS1118::startContinuousConversion(const uint8_t channel_pair) {
|
||||
SERIAL_ECHOLNPGM("ADS1118 Set to start conv");
|
||||
if (isBusy) return;
|
||||
// 0x048B b 0000 0100 1000 1011 : SS start, single-ended off, gain ±2.048V, Continuous conversion mode, 128SPS, ADC mode, Pullup enable, Write config
|
||||
// 0x049B: read internal temp
|
||||
uint16_t config = 0x048B;
|
||||
switch (channel_pair) {
|
||||
default:
|
||||
case 0: config |= (0x0 << ADS1118_CH_MASK); currentchannel = 0; break; // AIN0-AIN1
|
||||
case 1: config |= (0x3 << ADS1118_CH_MASK); currentchannel = 1; break; // AIN2-AIN3
|
||||
}
|
||||
|
||||
digitalWrite(cs_pin, LOW);
|
||||
transfer16(config);
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
|
||||
isBusy = true;
|
||||
startTime = millis();
|
||||
SERIAL_ECHOLNPGM("ADS1118 Leaving start conv");
|
||||
}
|
||||
|
||||
// Check if ADS has a complete conversion
|
||||
bool ADS1118::checkDataReady() {
|
||||
digitalWrite(cs_pin, LOW);
|
||||
const uint8_t isReady = !digitalRead(miso_pin); // Read MISO, is low when ready
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
return isReady;
|
||||
}
|
||||
|
||||
// Read ADS data
|
||||
uint16_t ADS1118::readData() {
|
||||
digitalWrite(cs_pin, LOW);
|
||||
const uint16_t data = transfer16(0);
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Read and write ADS data
|
||||
uint16_t ADS1118::readWriteData(const uint16_t config) {
|
||||
digitalWrite(cs_pin, LOW);
|
||||
const uint16_t data = transfer16(config);
|
||||
digitalWrite(cs_pin, HIGH);
|
||||
return data;
|
||||
}
|
||||
|
||||
// Reads and returns a single channel inmediately with delay (blocking)
|
||||
int16_t ADS1118::readChannel(const uint8_t channel) {
|
||||
const uint16_t config = configChannel(channel);
|
||||
transfer16(config);
|
||||
|
||||
// ADS1118 takes ~8 ms to convert
|
||||
delay(10);
|
||||
|
||||
const int16_t value = (int16_t)transfer16(config);
|
||||
return value;
|
||||
}
|
||||
|
||||
float ADS1118::readVoltage(const uint8_t channel, const float vref) {
|
||||
const int16_t raw = readChannel(channel);
|
||||
return (raw / 32768.0f) * vref; // scale 16 bits to voltage
|
||||
}
|
||||
|
||||
float ADS1118::readInternalTemp() {
|
||||
const uint16_t config = config_ADC_SS_TEMP; // Config internal temp read 0x8F80; // Config internal temp read & start SS conversion
|
||||
select();
|
||||
transfer16(config);
|
||||
deselect();
|
||||
delay(10);
|
||||
select();
|
||||
int16_t raw = (int16_t)transfer16(config);
|
||||
deselect();
|
||||
return convertInternalTemp(raw);
|
||||
}
|
||||
|
||||
// Convert raw internal temperature data to °C
|
||||
float ADS1118::convertInternalTemp(const int16_t data) {
|
||||
return float(data >> 2) * 0.03125f; // 14 bit left aligned, 0.03125 °C per LSB as datasheet
|
||||
}
|
||||
|
||||
uint16_t ADS1118::configChannel(const uint8_t channel) {
|
||||
uint16_t config = 0;
|
||||
|
||||
// Config single-ended inputs PGA ±2.048V, SS mode, 128SPS no pull up
|
||||
switch (channel) {
|
||||
case 0: config = 0xC583; break; // 0b 1100 0101 1000 0011 Ch1 & start SS conversion
|
||||
case 1: config = 0xD583; break; // 0b 1101 0101 1000 0011 Ch2 & start SS conversion
|
||||
case 2: config = 0xE583; break; // 0b 1110 0101 1000 0011 Ch3 & start SS conversion
|
||||
case 3: config = 0xF583; break; // 0b 1111 0101 1000 0011 Ch4 & start SS conversion
|
||||
default: config = 0x8583; break;
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
uint32_t ADS1118::transfer32(uint16_t data) {
|
||||
uint16_t result_prev = transfer16(data); // send config, receive previous result
|
||||
uint16_t config_echo = transfer16(0x0000); // send dummy, receive echo of configuration
|
||||
|
||||
// Combine both into a single 32-bit value
|
||||
return ((uint32_t)result_prev << 16) | config_echo;
|
||||
}
|
||||
|
||||
uint16_t ADS1118::readConfig() {
|
||||
uint16_t config_echo;
|
||||
select();
|
||||
transfer16(0x0000); // send 16‑bit dummy, ignore reply; configuration is in the following 16‑bit transfer
|
||||
config_echo = transfer16(0x0000); // send dummy, receive echo of configuration
|
||||
SERIAL_ECHOPGM("ADS1118 Configuration: "); SERIAL_ECHOLN(config_echo);
|
||||
deselect();
|
||||
return config_echo;
|
||||
}
|
||||
|
||||
uint16_t ADS1118::transfer16(uint16_t data) {
|
||||
uint8_t high = transfer8((uint8_t)(data >> 8));
|
||||
uint8_t low = transfer8((uint8_t)(data & 0xFF));
|
||||
return (uint16_t)(high << 8) | low;
|
||||
}
|
||||
|
||||
uint8_t ADS1118::transfer8(uint8_t data) {
|
||||
uint8_t recv = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
// Send MSB first
|
||||
if (data & 0x80) digitalWrite(mosi_pin, HIGH); else digitalWrite(mosi_pin, LOW);
|
||||
data <<= 1;
|
||||
|
||||
sckHigh();
|
||||
DELAY_NS(100); // small delay for stability
|
||||
|
||||
recv <<= 1;
|
||||
if (digitalRead(miso_pin)) recv |= 0x01;
|
||||
|
||||
sckLow();
|
||||
DELAY_NS(100);
|
||||
}
|
||||
return recv;
|
||||
}
|
||||
|
||||
void ADS1118::select() { digitalWrite(cs_pin, LOW); }
|
||||
void ADS1118::deselect() { digitalWrite(cs_pin, HIGH); }
|
||||
|
||||
void ADS1118::sckHigh() { digitalWrite(sck_pin, HIGH); }
|
||||
void ADS1118::sckLow() { digitalWrite(sck_pin, LOW); }
|
||||
|
||||
// Static variable definitions
|
||||
uint8_t ADS1118::cs_pin = 0;
|
||||
uint8_t ADS1118::mosi_pin = 0;
|
||||
uint8_t ADS1118::miso_pin = 0;
|
||||
uint8_t ADS1118::sck_pin = 0;
|
||||
unsigned long ADS1118::startTime = 0;
|
||||
int16_t ADS1118::lastValue = 0;
|
||||
int16_t ADS1118::config = 0;
|
||||
uint16_t ADS1118::current_config = 0;
|
||||
uint16_t ADS1118::previous_config = 0;
|
||||
|
||||
bool ADS1118::isBusy = false;
|
||||
uint8_t ADS1118::currentchannel = 0;
|
||||
|
||||
// ADS1118, global instance
|
||||
ADS1118 ads1118;
|
||||
ThermocoupleK thck_0;
|
||||
ThermocoupleK thck_1;
|
||||
|
||||
//#if TEMP_SENSOR_0_IS_ADS1118
|
||||
// #warning "ThcK 0 is enabled"
|
||||
// ThermocoupleK thck_0;
|
||||
//#endif
|
||||
|
||||
//#if TEMP_SENSOR_1_IS_ADS1118
|
||||
// #warning "ThcK 1 is enabled"
|
||||
// ThermocoupleK thck_1;
|
||||
//#endif
|
||||
|
||||
void ThermocoupleK::init() {}
|
||||
|
||||
// --- Convert ADC reading to °C ---
|
||||
float ThermocoupleK:: tempReadtoCelsius(const int16_t rawADC) {
|
||||
//Serial.println((int16_t)pgm_read_word(&ThermocoupleK_Lookup[TEMP_TABLE_SIZE - 1]));
|
||||
|
||||
if (rawADC > (int16_t) pgm_read_word(&table_thermocouple_k[TEMP_TABLE_SIZE - 1].adc))
|
||||
return TEMP_MAX_TEMP;
|
||||
if (rawADC < (int16_t) pgm_read_word(&table_thermocouple_k[0].adc))
|
||||
return TEMP_MIN_TEMP;
|
||||
|
||||
// Linear search in the table (from lowest to highest ADC value)
|
||||
for (uint16_t i = 0; i < TEMP_TABLE_SIZE - 1; i++) {
|
||||
|
||||
int16_t adc1 = pgm_read_word(&table_thermocouple_k[i].adc);
|
||||
int16_t adc2 = pgm_read_word(&table_thermocouple_k[i + 1].adc);
|
||||
// Serial.print(i); Serial.print(" "); Serial.print(adc1); Serial.print(" "); Serial.print(adc2);
|
||||
|
||||
if (rawADC >= adc1 && rawADC < adc2) { // in-between tableValue and nextValue
|
||||
// Approximate temperature via linear interpolation
|
||||
//float frac = float(rawADC - adc2) / float(adc1 - adc2);
|
||||
int16_t t1 = pgm_read_word(&table_thermocouple_k[i].temp);
|
||||
int16_t t2 = pgm_read_word(&table_thermocouple_k[i+1].temp);
|
||||
// Serial.print(" "); Serial.print(t1); Serial.print(" "); Serial.print(t2);
|
||||
float tempC = t1 + (float) (rawADC - adc1) * (float(t2 - t1) / float(adc2 - adc1)); // TEMP_TABLE_OFFSET + i + frac;
|
||||
return tempC;
|
||||
}
|
||||
// Serial.println("");
|
||||
}
|
||||
|
||||
return TEMP_MIN_TEMP;
|
||||
}
|
||||
|
||||
float ThermocoupleK:: calcTempCelsius() {
|
||||
_Tcold = ads1118.convertInternalTemp(_raw_cold);
|
||||
_Thot = tempReadtoCelsius(_raw_hot);
|
||||
//SERIAL_ECHOPGM("ADS1118 TCold "); SERIAL_ECHOLN(_Tcold);
|
||||
//SERIAL_ECHOPGM("ADS1118 THot "); SERIAL_ECHOLN(_Thot);
|
||||
return _Thot + _Tcold;
|
||||
}
|
||||
|
||||
void ThermocoupleK::setRawCold(const int16_t raw_cold) {
|
||||
_raw_cold = raw_cold;
|
||||
}
|
||||
|
||||
void ThermocoupleK::setRawHot(const int16_t raw_hot) {
|
||||
_raw_hot = raw_hot;
|
||||
}
|
||||
void ThermocoupleK::setTcold(const float tcold) {
|
||||
_Tcold = tcold;
|
||||
}
|
||||
|
||||
float ThermocoupleK::getTcold() {
|
||||
return _Tcold;
|
||||
}
|
||||
|
||||
void ThermocoupleK::setThot(const float thot) {
|
||||
_Thot = thot;
|
||||
}
|
||||
|
||||
float ThermocoupleK::getThot() {
|
||||
return _Thot;
|
||||
}
|
||||
|
||||
float ThermocoupleK:: getTempCelsius() {
|
||||
return _Thot + _Tcold;
|
||||
}
|
||||
|
||||
#endif // HAS_ADS1118
|
||||
213
Marlin/src/libs/adc/adc_ads1118.h
Normal file
213
Marlin/src/libs/adc/adc_ads1118.h
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Based on Arduino Library for Texas Instruments ADS1118 - 16-Bit Analog-to-Digital Converter with internal Reference and Temperature Sensor
|
||||
* https://www.ti.com/product/ADS1118
|
||||
* https://github.com/ADS1xxx-Series-ADC-Libraries/ADS1118
|
||||
*/
|
||||
|
||||
#include "../../inc/MarlinConfigPre.h"
|
||||
|
||||
// ADS config register bits and values:
|
||||
// Bit 15: Single-shot conversion start
|
||||
#define ADS_SS_NOP 0x0000
|
||||
#define ADS_SS_START 0x8000
|
||||
// Bits 14-12 Mux
|
||||
#define INPUT_CHAN_0_1 0x0000 // *Default*
|
||||
#define INPUT_CHAN_0_3 0x1000
|
||||
#define INPUT_CHAN_1_3 0x2000
|
||||
#define INPUT_CHAN_2_3 0x3000
|
||||
#define INPUT_CHAN_0_G 0x4000
|
||||
#define INPUT_CHAN_1_G 0x5000
|
||||
#define INPUT_CHAN_2_G 0x6000
|
||||
#define INPUT_CHAN_3_G 0x7000
|
||||
|
||||
/// Bits 11-9 ADC PGA gain select bits
|
||||
/// the gain setting sets the voltage range for the ADC. Full Scale Range
|
||||
/// voltage is the read value at 0x7FFF (the ADC returns a 16bit integer integer value)
|
||||
/// we use the highest possible gain setting - k-Type thermocouples have a voltage
|
||||
/// difference of ~12mV at 300C
|
||||
#define PGA_0_6_14 0x0000 // Gain = 1, Full Scale Voltage is 6.14V
|
||||
#define PGA_1_4_09 0x0200 // Gain = 1.5, Full Scale Voltage is 4.09V
|
||||
#define PGA_2_2_04 0x0400 // Gain = 3, Full Scale Voltage is 2.04V *Default*
|
||||
#define PGA_3_1_02 0x0600 // Gain = 6, Full Scale Voltage is 1.02V
|
||||
#define PGA_4_0_512 0x0800 // Gain = 12, Full Scale Voltage is 0.512V
|
||||
#define PGA_5_0_256 0x0A00 // Gain = 24, Full Scale Voltage is 0.256V
|
||||
|
||||
/// Bit 8: operating mode: single sample or continous conversion
|
||||
#define CONTINUOUS_CONVERSION_MODE 0x0000 // continous conversion
|
||||
#define SINGLE_SHOT_MODE 0x0100 // single sample
|
||||
|
||||
/// Bit 7-5: Data Rate, Sample Frequency select bits (Hz)
|
||||
#define SAMPLE_FREQ_860 0x00E0
|
||||
#define SAMPLE_FREQ_475 0x00C0
|
||||
#define SAMPLE_FREQ_250 0x00A0
|
||||
#define SAMPLE_FREQ_128 0x0080 //* default
|
||||
#define SAMPLE_FREQ_64 0x0060
|
||||
#define SAMPLE_FREQ_32 0x0040
|
||||
#define SAMPLE_FREQ_16 0x0020
|
||||
#define SAMPLE_FREQ_08 0x0000
|
||||
|
||||
/// Bit 4: ADC mode (thermocouples) vs temperature sensor (on-board cold_junction temp sensor)
|
||||
#define ADC_MODE 0x0000
|
||||
#define TEMP_MODE 0x0010
|
||||
|
||||
/// Bit 3: Pull up enable
|
||||
#define PULL_UP_DISABLE 0x0000
|
||||
#define PULL_UP_ENABLE 0x0008
|
||||
|
||||
/// write new data to the config register ( if bits <2:1> are not <01> the config bytes are ignored)
|
||||
#define ADS_NOP 0x0000
|
||||
#define WRITE_CONFIG 0x0002
|
||||
|
||||
/// Number of read cycles between cold junction temperature reads
|
||||
/// we don't need to read the cold junction temperature every cycle
|
||||
/// because we don't expect it to change much
|
||||
#define TEMP_CHECK_COUNT 120
|
||||
|
||||
#define THERM_CHANNEL_ONE 0
|
||||
#define THERM_CHANNEL_TWO 1
|
||||
#define THERM_CHANNEL_HBP 2
|
||||
#define THERM_COLD_JUNCTION 3
|
||||
|
||||
typedef struct {
|
||||
int16_t adc;
|
||||
int16_t temp;
|
||||
} ADC_Lookup;
|
||||
|
||||
const static ADC_Lookup table_thermocouple_k[] PROGMEM = {
|
||||
{ -304, -64},
|
||||
{ -232, -48},
|
||||
{ -157, -32},
|
||||
{ -79, -16},
|
||||
{ 0, 0},
|
||||
{ 82, 16},
|
||||
{ 164, 32},
|
||||
{ 248, 48},
|
||||
{ 333, 64},
|
||||
{ 418, 80},
|
||||
{ 503, 96},
|
||||
{ 588, 112},
|
||||
{ 672, 128},
|
||||
{ 755, 144},
|
||||
{ 837, 160},
|
||||
{ 919, 176},
|
||||
{ 1001, 192},
|
||||
{ 1083, 208},
|
||||
{ 1165, 224},
|
||||
{ 1248, 240},
|
||||
{ 1331, 256},
|
||||
{ 1415, 272},
|
||||
{ 1499, 288},
|
||||
{ 1584, 304},
|
||||
{ 1754, 336},
|
||||
{ 1840, 352},
|
||||
{ 1926, 368},
|
||||
{ 2012, 384},
|
||||
{ 2099, 400}
|
||||
};
|
||||
|
||||
#define TEMP_TABLE_SIZE (sizeof(table_thermocouple_k) / sizeof(table_thermocouple_k[0]))
|
||||
#define TEMP_MIN_TEMP 0
|
||||
#define TEMP_MAX_TEMP 300
|
||||
#define TEMP_TABLE_OFFSET 0 // grados Celsius por índice
|
||||
|
||||
class ADS1118 {
|
||||
public:
|
||||
static void init(uint8_t cs, uint8_t mosi, uint8_t miso, uint8_t sck);
|
||||
static void startConversion(const uint8_t pair); // start conversion
|
||||
static bool ready(); // indicates whether it is already ready to be read
|
||||
static int16_t read(); // read converted value
|
||||
static bool busy(); // conversion status
|
||||
static void loop(); // non‑blocking cycle
|
||||
|
||||
static bool checkDataReady();
|
||||
static void startContinuousConversion(const uint8_t channel_pair);
|
||||
static uint16_t readData();
|
||||
static int16_t readChannel(const uint8_t channel);
|
||||
static uint16_t readConfig ();
|
||||
static uint16_t readWriteData(const uint16_t config);
|
||||
|
||||
float convertInternalTemp(const int16_t data);
|
||||
float readInternalTemp();
|
||||
|
||||
uint16_t config_ADC_SS_CH0 = ADS_SS_START | INPUT_CHAN_0_1| PGA_5_0_256 | SINGLE_SHOT_MODE| SAMPLE_FREQ_128 | ADC_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
|
||||
uint16_t config_ADC_SS_CH1 = ADS_SS_START | INPUT_CHAN_2_3 | PGA_5_0_256 | SINGLE_SHOT_MODE| SAMPLE_FREQ_128 | ADC_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
|
||||
uint16_t config_ADC_SS_TEMP = ADS_SS_START | PGA_5_0_256 | SINGLE_SHOT_MODE | SAMPLE_FREQ_128 | TEMP_MODE | PULL_UP_ENABLE | WRITE_CONFIG;
|
||||
|
||||
static uint16_t current_config;
|
||||
static uint16_t previous_config;
|
||||
|
||||
private:
|
||||
static void spiTransfer(uint8_t data, uint8_t &resp);
|
||||
static void writeWord(uint16_t word);
|
||||
static void readWord(uint16_t &word);
|
||||
|
||||
static uint32_t transfer32 (uint16_t data);
|
||||
static uint16_t transfer16 (uint16_t data);
|
||||
static uint8_t transfer8 (uint8_t data);
|
||||
|
||||
|
||||
static void select();
|
||||
static void deselect();
|
||||
|
||||
static void sckHigh();
|
||||
static void sckLow();
|
||||
|
||||
static uint8_t cs_pin, mosi_pin, miso_pin, sck_pin;
|
||||
static unsigned long startTime;
|
||||
static int16_t lastValue;
|
||||
static int16_t config;
|
||||
|
||||
static bool isBusy;
|
||||
static uint8_t currentchannel;
|
||||
|
||||
//uint16_t configForChannel(const uint8_t channel);
|
||||
static uint16_t configChannel(const uint8_t channel);
|
||||
float readVoltage(const uint8_t channel, const float vref);
|
||||
};
|
||||
|
||||
class ThermocoupleK {
|
||||
public:
|
||||
void init();
|
||||
float tempReadtoCelsius(int16_t rawADC);
|
||||
float calcTempCelsius();
|
||||
float getTempCelsius();
|
||||
float _Tcold, _Thot;
|
||||
int16_t _raw_cold, _raw_hot;
|
||||
|
||||
void setRawCold(int16_t raw_cold);
|
||||
void setRawHot(int16_t raw_hot);
|
||||
|
||||
void setTcold(float tcold);
|
||||
float getTcold();
|
||||
|
||||
void setThot(float thot);
|
||||
float getThot();
|
||||
};
|
||||
|
||||
extern ADS1118 ads1118;
|
||||
|
||||
extern ThermocoupleK thck_0;
|
||||
extern ThermocoupleK thck_1;
|
||||
|
|
@ -207,6 +207,13 @@
|
|||
#include "stepper.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define TEMP_SENSOR_IS_ADS(n, M) (ENABLED(TEMP_SENSOR_##n##_IS_ADS##M) || (ENABLED(TEMP_SENSOR_REDUNDANT_IS_ADS##M) && REDUNDANT_TEMP_MATCH(SOURCE, E##n)))
|
||||
|
||||
#if HAS_ADS1118
|
||||
#include "../libs/adc/adc_ads1118.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(FILAMENT_WIDTH_SENSOR)
|
||||
#include "../feature/filwidth.h"
|
||||
#endif
|
||||
|
|
@ -2576,10 +2583,37 @@ void Temperature::task() {
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ANY_THERMISTOR_IS(-18)
|
||||
|
||||
// Conversion for ADS1118 in differential mode (K-type)
|
||||
// Each LSB bit ≈ 62.5 µV → ~1.5 °C (no calibration).
|
||||
// Adjustable with GAIN and OFFSET from Configuration_adv.h
|
||||
|
||||
static constexpr celsius_float_t temp_ads1118(const uint8_t e) {
|
||||
celsius_float_t temp = 0;
|
||||
switch (e) {
|
||||
case 0:
|
||||
temp = thck_0.calcTempCelsius();
|
||||
//SERIAL_ECHO("temp ads1118: "); SERIAL_ECHOLN(temp);
|
||||
break;
|
||||
case 1:
|
||||
temp = thck_1.calcTempCelsius();
|
||||
break;
|
||||
default:
|
||||
temp = -14.0f; // Fallback to error temperature
|
||||
break;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
#endif // ANY_THERMISTOR_IS(-18)
|
||||
|
||||
#if HAS_HOTEND
|
||||
// Derived from RepRap FiveD extruder::getTemperature()
|
||||
// For hot end temperature measurement.
|
||||
celsius_float_t Temperature::analog_to_celsius_hotend(const raw_adc_t raw, const uint8_t e) {
|
||||
//SERIAL_ECHOLN(e);
|
||||
if (e >= HOTENDS) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ECHO(e);
|
||||
|
|
@ -2605,6 +2639,8 @@ void Temperature::task() {
|
|||
return temp_ad595(raw);
|
||||
#elif TEMP_SENSOR_0_IS_AD8495
|
||||
return temp_ad8495(raw);
|
||||
#elif TEMP_SENSOR_0_IS_ADS1118
|
||||
return temp_ads1118(e);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
|
@ -2624,6 +2660,8 @@ void Temperature::task() {
|
|||
return temp_ad595(raw);
|
||||
#elif TEMP_SENSOR_1_IS_AD8495
|
||||
return temp_ad8495(raw);
|
||||
#elif TEMP_SENSOR_1_IS_ADS1118
|
||||
return temp_ads1118(e);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
|
@ -2875,6 +2913,31 @@ void Temperature::updateTemperaturesFromRawValues() {
|
|||
temp_bed.setraw(read_max_tc_bed());
|
||||
#endif
|
||||
|
||||
// Read ADC ADS1118
|
||||
// Note: For ADS1118, we don't call setraw() because read_ads1118() returns int16_t
|
||||
// (differential measurement can be negative) but raw_adc_t is uint16_t.
|
||||
// Instead, the ThermocoupleK object (thck_0/thck_1) handles the conversion
|
||||
// internally, and analog_to_celsius_hotend() will retrieve the computed
|
||||
// temperature via thck_0.getThot(). This avoids type overflow and keeps
|
||||
// the conversion logic centralized and ISR-light.
|
||||
#if TEMP_SENSOR_IS_ADS(0, 1118)
|
||||
#warning "ADS1118 is selected for hotend 0"
|
||||
temp_hotend[0].setraw(READ_ADS(0));
|
||||
//SERIAL_ECHOPGM("ADS1118 Tcold=");
|
||||
//SERIAL_ECHO(thck_0.getTcold());
|
||||
//SERIAL_ECHOPGM(" Thot=");
|
||||
//SERIAL_ECHOLN(thck_0.getThot());
|
||||
#endif
|
||||
|
||||
#if TEMP_SENSOR_IS_ADS(1, 1118)
|
||||
#warning "ADS1118 is selected for hotend 1"
|
||||
temp_hotend[1].setraw(READ_ADS(1));
|
||||
//SERIAL_ECHOPGM("ADS1118 Tcold=");
|
||||
//SERIAL_ECHO(thck_1.getTcold());
|
||||
//SERIAL_ECHOPGM(" Thot=");
|
||||
//SERIAL_ECHOLN(thck_1.getThot());
|
||||
#endif
|
||||
|
||||
#if HAS_HOTEND
|
||||
HOTEND_LOOP() temp_hotend[e].celsius = analog_to_celsius_hotend(temp_hotend[e].getraw(), e);
|
||||
#endif
|
||||
|
|
@ -2891,7 +2954,8 @@ void Temperature::updateTemperaturesFromRawValues() {
|
|||
TERN_(HAS_POWER_MONITOR, power_monitor.capture_values());
|
||||
|
||||
#if HAS_HOTEND
|
||||
#define _TEMPDIR(N) TEMP_SENSOR_IS_ANY_MAX_TC(N) ? 0 : TEMPDIR(N),
|
||||
|
||||
#define _TEMPDIR(N) (TEMP_SENSOR_IS_ANY_MAX_TC(N) || TEMP_SENSOR_IS_ADS(N,1118)) ? 0 : TEMPDIR(N),
|
||||
static constexpr int8_t temp_dir[HOTENDS] = { REPEAT(HOTENDS, _TEMPDIR) };
|
||||
|
||||
HOTEND_LOOP() {
|
||||
|
|
@ -2975,6 +3039,30 @@ void Temperature::init() {
|
|||
|
||||
TERN_(PROBING_HEATERS_OFF, paused_for_probing = false);
|
||||
|
||||
//#define TEMP_0_CS_PIN 79 // E6
|
||||
//#define TEMP_0_SCK_PIN 78 // E2
|
||||
//#define TEMP_0_MISO_PIN 80 // E7
|
||||
//#define TEMP_0_MOSI_PIN 84 // H2
|
||||
|
||||
#if HAS_ADS1118
|
||||
ads1118.init(TEMP_0_CS_PIN, TEMP_0_MOSI_PIN, TEMP_0_MISO_PIN, TEMP_0_SCK_PIN); // Initialize the ADS1118, global instance
|
||||
ads1118.readConfig();
|
||||
#endif
|
||||
|
||||
// ADS TC related macros
|
||||
#if TEMP_SENSOR_IS_ADS(0, 1118)
|
||||
#warning "ADS1118 is selected for temp 0"
|
||||
thck_0.init();
|
||||
//SERIAL_ECHOLNPGM("ADS1118 start initial conversion for Tcold...");
|
||||
thck_0.setTcold (ads1118.readInternalTemp());
|
||||
|
||||
ads1118.current_config = ads1118.config_ADC_SS_TEMP;
|
||||
ads1118.previous_config = ads1118.current_config;
|
||||
//SERIAL_ECHOPGM("ADS1118 Tcold: ");
|
||||
//SERIAL_ECHOLN(thck_0.getTcold());
|
||||
//ads1118.readConfig();
|
||||
#endif
|
||||
|
||||
// Init (and disable) SPI thermocouples
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(0) && PIN_EXISTS(TEMP_0_CS)
|
||||
OUT_WRITE(TEMP_0_CS_PIN, HIGH);
|
||||
|
|
@ -3727,7 +3815,7 @@ void Temperature::disable_all_heaters() {
|
|||
return max_tc_temp;
|
||||
}
|
||||
|
||||
#endif // HAS_MAX_TC
|
||||
#endif // TEMP_SENSOR_IS_MAX_TC(0) || TEMP_SENSOR_IS_MAX_TC(1) || TEMP_SENSOR_IS_MAX_TC(2)
|
||||
|
||||
#if TEMP_SENSOR_IS_MAX_TC(BED)
|
||||
/**
|
||||
|
|
@ -3847,6 +3935,106 @@ void Temperature::disable_all_heaters() {
|
|||
|
||||
#endif // TEMP_SENSOR_IS_MAX_TC(BED)
|
||||
|
||||
#if HAS_ADS1118
|
||||
|
||||
/**
|
||||
* @brief Read ADS Thermocouple temperature.
|
||||
*
|
||||
* Reads the thermocouple board via HW or SW SPI, using a library (LIB_USR_x) or raw SPI reads.
|
||||
* Doesn't strictly return a temperature; returns an "ADC Value" (i.e. raw register content).
|
||||
* Currently only supports channel 0 (single extruder)
|
||||
*
|
||||
* @param hindex the hotend we're referencing (different channel in ADS1118)
|
||||
* @return integer representing the board's buffer, to be converted later if needed
|
||||
*/
|
||||
raw_adc_t Temperature::read_ads1118(const uint8_t hindex/*=0*/) {
|
||||
#define ADS1118_HEAT_INTERVAL 250UL // 250 ms
|
||||
|
||||
//static raw_adc_t ads1118_coldJ_temp_current[2] = { 0, 0 };
|
||||
//static raw_adc_t ads1118_hotJ_temp_current[2] = { 0, 0 };
|
||||
|
||||
static raw_adc_t ads1118_temp_previous[2] = { 0, 0 };
|
||||
static uint8_t ads1118_errors[2] = { 0, 0 };
|
||||
static millis_t next_ads1118_ms[2] = { 0, 0 };
|
||||
|
||||
static raw_adc_t ads_val = TEMP_SENSOR_0_ADS_TMAX;
|
||||
|
||||
static uint8_t sampleCount;
|
||||
|
||||
//static millis_t lastmillis;
|
||||
|
||||
const millis_t ms = millis();
|
||||
//SERIAL_ECHOPGM("ADS1118 elapsed: "); SERIAL_ECHOLN(ms- lastmillis);
|
||||
//lastmillis = ms;
|
||||
if (PENDING(ms, next_ads1118_ms[hindex]) ) // || !ads1118.checkDataReady()
|
||||
return ads1118_temp_previous[hindex]; // return cached value
|
||||
|
||||
next_ads1118_ms[hindex] = ms + ADS1118_HEAT_INTERVAL;
|
||||
|
||||
// To do: If there are more hotends enabled, cycle through different channels
|
||||
int16_t raw;
|
||||
|
||||
if (sampleCount < 1) {
|
||||
ads1118.previous_config = ads1118.current_config;
|
||||
ads1118.current_config = ads1118.config_ADC_SS_TEMP;
|
||||
sampleCount++;
|
||||
}
|
||||
else if (hindex == 0) {
|
||||
ads1118.previous_config = ads1118.current_config;
|
||||
ads1118.current_config = ads1118.config_ADC_SS_CH0;
|
||||
sampleCount = 0;
|
||||
}
|
||||
else if (hindex == 1) {
|
||||
ads1118.previous_config = ads1118.current_config;
|
||||
ads1118.current_config = ads1118.config_ADC_SS_CH1;
|
||||
sampleCount = 0;
|
||||
}
|
||||
|
||||
raw = (int16_t) ads1118.readWriteData(ads1118.current_config);
|
||||
|
||||
if (ads1118.previous_config == ads1118.config_ADC_SS_TEMP) {
|
||||
//thck_0.setTcold(ads1118.convertInternalTemp(raw));
|
||||
thck_0.setRawCold(raw);
|
||||
//SERIAL_ECHOPGM("Last read Raw cold: "); SERIAL_ECHOLN(raw);
|
||||
//SERIAL_ECHOPGM("TCold "); SERIAL_ECHOLN(thck_0.getTcold());
|
||||
|
||||
}
|
||||
else if (ads1118.previous_config == ads1118.config_ADC_SS_CH0) {
|
||||
//ads1118_hotJ_temp_current[hindex] = raw;
|
||||
//thck_0.setThot(thck_0.tempReadtoCelsius(raw));
|
||||
thck_0.setRawHot(raw);
|
||||
//SERIAL_ECHOPGM("Last read Raw hot: "); SERIAL_ECHOLN(raw);
|
||||
//SERIAL_ECHOPGM("ADS1118 THot "); SERIAL_ECHOLN(thck_0.getThot());
|
||||
}
|
||||
|
||||
//SERIAL_ECHOPGM("ADS1118 State:Read "); SERIAL_ECHOLN(curr_state); SERIAL_ECHOPGM(":"); SERIAL_ECHOLN(raw);
|
||||
|
||||
// Handle read error or disconnection : raw = 0x7FFF or 0x8000 (-32768)
|
||||
if (raw == 0x7FFF || raw == -32768) {
|
||||
ads1118_errors[hindex]++;
|
||||
if (ads1118_errors[hindex] > 3) {
|
||||
SERIAL_ERROR_START();
|
||||
SERIAL_ECHOLNPGM("ADS1118 Fault: Conversion error!");
|
||||
ads_val = (raw_adc_t)(TEMP_SENSOR_0_ADS_TMAX << 4); // force error
|
||||
}
|
||||
}
|
||||
else if (raw < 32767){
|
||||
ads1118_errors[hindex] = 0; // reset errors if ok
|
||||
ads_val = (raw_adc_t) (((int16_t)raw) + 32768); // raw shift to unsigned int;
|
||||
} else { // if we add 32767 to raw it will overflow
|
||||
ads1118_errors[hindex] = 0; // reset errors if ok
|
||||
SERIAL_ECHOLNPGM("ADS1118 Warn: Cannot shift adc read from signed to unsigned!");
|
||||
ads_val = raw_adc_t(raw); // as is
|
||||
}
|
||||
//ads_val = raw_adc_t(3000); // raw;
|
||||
|
||||
ads1118_temp_previous[hindex] = ads_val; // cache value
|
||||
//SERIAL_ECHOPGM("ADS1118 ads_val: "); SERIAL_ECHOLN(ads_val);
|
||||
return ads_val; // return the raw value, it will not be used directly for conversion but for errors, (raw values are stored in thermocouple class)
|
||||
}
|
||||
|
||||
#endif // HAS_ADS1118
|
||||
|
||||
/**
|
||||
* Update raw temperatures
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1413,6 +1413,12 @@ class Temperature {
|
|||
static raw_adc_t read_max_tc_bed();
|
||||
#endif
|
||||
|
||||
// ADS Thermocouples
|
||||
#if HAS_ADS1118
|
||||
#define READ_ADS(N) read_ads1118(N)
|
||||
static raw_adc_t read_ads1118(const uint8_t hindex=0);
|
||||
#endif
|
||||
|
||||
#if HAS_AUTO_FAN
|
||||
#if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN)
|
||||
static bool autofans_on;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ set -e
|
|||
|
||||
restore_configs
|
||||
opt_set MOTHERBOARD BOARD_BTT_GTR_V1_0 SERIAL_PORT -1 \
|
||||
EXTRUDERS 8 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 TEMP_SENSOR_3 1 TEMP_SENSOR_4 1 TEMP_SENSOR_5 1 TEMP_SENSOR_6 1 TEMP_SENSOR_7 1
|
||||
EXTRUDERS 8 TEMP_SENSOR_1 1 TEMP_SENSOR_2 1 TEMP_SENSOR_3 1 TEMP_SENSOR_4 1 TEMP_SENSOR_5 1 TEMP_SENSOR_6 1 TEMP_SENSOR_7 1 \
|
||||
TEMP_SENSOR_0 -18 TEMP_0_MOSI_PIN PG15
|
||||
# Not necessary to enable auto-fan for all extruders to hit problematic code paths
|
||||
opt_set E0_AUTO_FAN_PIN PC10 E1_AUTO_FAN_PIN PC11 E2_AUTO_FAN_PIN PC12 NEOPIXEL_PIN PF13 \
|
||||
X_DRIVER_TYPE TMC2208 Y_DRIVER_TYPE TMC2130 \
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ HAS_MOTOR_CURRENT_I2C = SlowSoftI2CMaster
|
|||
build_src_filter=+<src/feature/digipot>
|
||||
LIB_MAX31855 = GadgetAngel MAX31855=https://github.com/GadgetAngel/Adafruit-MAX31855-V1.0.3-Mod-M/archive/dc9cc10ac2.zip
|
||||
LIB_INTERNAL_MAX31865 = build_src_filter=+<src/libs/MAX31865.cpp>
|
||||
HAS_ADS1118 = build_src_filter=+<src/libs/adc>
|
||||
NEOPIXEL_LED = adafruit/Adafruit NeoPixel@~1.12.3
|
||||
build_src_filter=+<src/feature/leds/neopixel.cpp>
|
||||
I2C_AMMETER = peterus/INA226Lib@1.1.2
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ default_src_filter = +<src/*> -<src/config> -<src/tests>
|
|||
-<src/gcode/temp>
|
||||
-<src/gcode/units>
|
||||
; Library Code
|
||||
-<src/libs/adc>
|
||||
-<src/libs/heatshrink>
|
||||
-<src/libs/BL24CXX.cpp> -<src/libs/W25Qxx.cpp>
|
||||
-<src/libs/MAX31865.cpp>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue