This commit is contained in:
Axel Sepúlveda 2026-02-27 18:51:54 -06:00 committed by GitHub
commit 99bd46347c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 767 additions and 3 deletions

View file

@ -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
*

View file

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

View file

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

View 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 16bit dummy, ignore reply; configuration is in the following 16bit 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

View 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(); // nonblocking 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;

View file

@ -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
*

View file

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

View file

@ -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 \

View file

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

View file

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