Merge upstream changes from Marlin 2.1.1

This commit is contained in:
Stefan Kalscheuer
2022-09-03 09:23:32 +02:00
parent 626283aadb
commit 986e416c7f
1610 changed files with 73839 additions and 40857 deletions

View File

@@ -23,6 +23,7 @@
#include "../../inc/MarlinConfig.h"
#include "HAL.h"
#include <avr/wdt.h>
#ifdef USBCON
DefaultSerial1 MSerial0(false, Serial);
@@ -35,13 +36,32 @@
// Public Variables
// ------------------------
//uint8_t MCUSR;
// Don't initialize/override variable (which would happen in .init4)
uint8_t MarlinHAL::reset_reason __attribute__((section(".noinit")));
// ------------------------
// Public functions
// ------------------------
void HAL_init() {
__attribute__((naked)) // Don't output function pro- and epilogue
__attribute__((used)) // Output the function, even if "not used"
__attribute__((section(".init3"))) // Put in an early user definable section
void save_reset_reason() {
#if ENABLED(OPTIBOOT_RESET_REASON)
__asm__ __volatile__(
A("STS %0, r2")
: "=m"(hal.reset_reason)
);
#else
hal.reset_reason = MCUSR;
#endif
// Clear within 16ms since WDRF bit enables a 16ms watchdog timer -> Boot loop
hal.clear_reset_source();
wdt_disable();
}
void MarlinHAL::init() {
// Init Servo Pins
#define INIT_SERVO(N) OUT_WRITE(SERVO##N##_PIN, LOW)
#if HAS_SERVO_0
@@ -56,9 +76,11 @@ void HAL_init() {
#if HAS_SERVO_3
INIT_SERVO(3);
#endif
init_pwm_timers(); // Init user timers to default frequency - 1000HZ
}
void HAL_reboot() {
void MarlinHAL::reboot() {
#if ENABLED(USE_WATCHDOG)
while (1) { /* run out the watchdog */ }
#else
@@ -67,6 +89,62 @@ void HAL_reboot() {
#endif
}
// ------------------------
// Watchdog Timer
// ------------------------
#if ENABLED(USE_WATCHDOG)
#include <avr/wdt.h>
#include "../../MarlinCore.h"
// Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s.
void MarlinHAL::watchdog_init() {
#if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S)
#define WDTO_NS WDTO_8S
#else
#define WDTO_NS WDTO_4S
#endif
#if ENABLED(WATCHDOG_RESET_MANUAL)
// Enable the watchdog timer, but only for the interrupt.
// Take care, as this requires the correct order of operation, with interrupts disabled.
// See the datasheet of any AVR chip for details.
wdt_reset();
cli();
_WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE);
_WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5
// So worked for up to WDTO_2S
sei();
wdt_reset();
#else
wdt_enable(WDTO_NS); // The function handles the upper bit correct.
#endif
//delay(10000); // test it!
}
//===========================================================================
//=================================== ISR ===================================
//===========================================================================
// Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled.
#if ENABLED(WATCHDOG_RESET_MANUAL)
ISR(WDT_vect) {
sei(); // With the interrupt driven serial we need to allow interrupts.
SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED);
minkill(); // interrupt-safe final kill and infinite loop
}
#endif
// Reset watchdog. MUST be called at least every 4 seconds after the
// first watchdog_init or AVR will go into emergency procedures.
void MarlinHAL::watchdog_refresh() { wdt_reset(); }
#endif // USE_WATCHDOG
// ------------------------
// Free Memory Accessor
// ------------------------
#if ENABLED(SDSUPPORT)
#include "../../sd/SdFatUtil.h"
@@ -74,20 +152,20 @@ void HAL_reboot() {
#else // !SDSUPPORT
extern "C" {
extern char __bss_end;
extern char __heap_start;
extern void* __brkval;
extern "C" {
extern char __bss_end;
extern char __heap_start;
extern void* __brkval;
int freeMemory() {
int free_memory;
if ((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end);
else
free_memory = ((int)&free_memory) - ((int)__brkval);
return free_memory;
int freeMemory() {
int free_memory;
if ((int)__brkval == 0)
free_memory = ((int)&free_memory) - ((int)&__bss_end);
else
free_memory = ((int)&free_memory) - ((int)__brkval);
return free_memory;
}
}
}
#endif // !SDSUPPORT

View File

@@ -19,16 +19,18 @@
*/
#pragma once
/**
* HAL for Arduino AVR
*/
#include "../shared/Marduino.h"
#include "../shared/HAL_SPI.h"
#include "fastio.h"
#include "watchdog.h"
#include "math.h"
#ifdef USBCON
#include <HardwareSerial.h>
#else
#define HardwareSerial_h // Hack to prevent HardwareSerial.h header inclusion
#include "MarlinSerial.h"
#endif
@@ -74,9 +76,9 @@
#define CRITICAL_SECTION_START() unsigned char _sreg = SREG; cli()
#define CRITICAL_SECTION_END() SREG = _sreg
#endif
#define ISRS_ENABLED() TEST(SREG, SREG_I)
#define ENABLE_ISRS() sei()
#define DISABLE_ISRS() cli()
#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
#define PWM_FREQUENCY 1000 // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency()
// ------------------------
// Types
@@ -84,16 +86,15 @@
typedef int8_t pin_t;
#define SHARED_SERVOS HAS_SERVOS
#define HAL_SERVO_LIB Servo
#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp
class Servo;
typedef Servo hal_servo_t;
// ------------------------
// Public Variables
// ------------------------
//extern uint8_t MCUSR;
// Serial ports
// ------------------------
#ifdef USBCON
#include "../../core/serial_hook.h"
typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1;
@@ -142,59 +143,15 @@ typedef int8_t pin_t;
#endif
#endif
// ------------------------
// Public functions
// ------------------------
void HAL_init();
//void cli();
//void _delay_ms(const int delay);
inline void HAL_clear_reset_source() { MCUSR = 0; }
inline uint8_t HAL_get_reset_source() { return MCUSR; }
void HAL_reboot();
#if GCC_VERSION <= 50000
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
extern "C" int freeMemory();
#if GCC_VERSION <= 50000
#pragma GCC diagnostic pop
#endif
//
// ADC
#ifdef DIDR2
#define HAL_ANALOG_SELECT(ind) do{ if (ind < 8) SBI(DIDR0, ind); else SBI(DIDR2, ind & 0x07); }while(0)
#else
#define HAL_ANALOG_SELECT(ind) SBI(DIDR0, ind);
#endif
inline void HAL_adc_init() {
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07;
DIDR0 = 0;
#ifdef DIDR2
DIDR2 = 0;
#endif
}
#define SET_ADMUX_ADCSRA(ch) ADMUX = _BV(REFS0) | (ch & 0x07); SBI(ADCSRA, ADSC)
#ifdef MUX5
#define HAL_START_ADC(ch) if (ch > 7) ADCSRB = _BV(MUX5); else ADCSRB = 0; SET_ADMUX_ADCSRA(ch)
#else
#define HAL_START_ADC(ch) ADCSRB = 0; SET_ADMUX_ADCSRA(ch)
#endif
//
#define HAL_ADC_VREF 5.0
#define HAL_ADC_RESOLUTION 10
#define HAL_READ_ADC() ADC
#define HAL_ADC_READY() !TEST(ADCSRA, ADSC)
//
// Pin Mapping for M42, M43, M226
//
#define GET_PIN_MAP_PIN(index) index
#define GET_PIN_MAP_INDEX(pin) pin
#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval)
@@ -208,23 +165,113 @@ inline void HAL_adc_init() {
// AVR compatibility
#define strtof strtod
#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment
// ------------------------
// Free Memory Accessor
// ------------------------
/**
* set_pwm_frequency
* Sets the frequency of the timer corresponding to the provided pin
* as close as possible to the provided desired frequency. Internally
* calculates the required waveform generation mode, prescaler and
* resolution values required and sets the timer registers accordingly.
* NOTE that the frequency is applied to all pins on the timer (Ex OC3A, OC3B and OC3B)
* NOTE that there are limitations, particularly if using TIMER2. (see Configuration_adv.h -> FAST FAN PWM Settings)
*/
void set_pwm_frequency(const pin_t pin, int f_desired);
#pragma GCC diagnostic push
#if GCC_VERSION <= 50000
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
/**
* set_pwm_duty
* Sets the PWM duty cycle of the provided pin to the provided value
* Optionally allows inverting the duty cycle [default = false]
* Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255]
*/
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
extern "C" int freeMemory();
#pragma GCC diagnostic pop
// ------------------------
// MarlinHAL Class
// ------------------------
class MarlinHAL {
public:
// Earliest possible init, before setup()
MarlinHAL() {}
// Watchdog
static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {});
static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {});
static void init(); // Called early in setup()
static void init_board() {} // Called less early in setup()
static void reboot(); // Restart the firmware from 0x0
// Interrupts
static bool isr_state() { return TEST(SREG, SREG_I); }
static void isr_on() { sei(); }
static void isr_off() { cli(); }
static void delay_ms(const int ms) { _delay_ms(ms); }
// Tasks, called from idle()
static void idletask() {}
// Reset
static uint8_t reset_reason;
static uint8_t get_reset_source() { return reset_reason; }
static void clear_reset_source() { MCUSR = 0; }
// Free SRAM
static int freeMemory() { return ::freeMemory(); }
//
// ADC Methods
//
// Called by Temperature::init once at startup
static void adc_init() {
ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07;
DIDR0 = 0;
#ifdef DIDR2
DIDR2 = 0;
#endif
}
// Called by Temperature::init for each sensor at startup
static void adc_enable(const uint8_t ch) {
#ifdef DIDR2
if (ch > 7) { SBI(DIDR2, ch & 0x07); return; }
#endif
SBI(DIDR0, ch);
}
// Begin ADC sampling on the given channel. Called from Temperature::isr!
static void adc_start(const uint8_t ch) {
#ifdef MUX5
ADCSRB = ch > 7 ? _BV(MUX5) : 0;
#else
ADCSRB = 0;
#endif
ADMUX = _BV(REFS0) | (ch & 0x07);
SBI(ADCSRA, ADSC);
}
// Is the ADC ready for reading?
static bool adc_ready() { return !TEST(ADCSRA, ADSC); }
// The current value of the ADC register
static __typeof__(ADC) adc_value() { return ADC; }
/**
* init_pwm_timers
* Set the default frequency for timers 2-5 to 1000HZ
*/
static void init_pwm_timers();
/**
* Set the PWM duty cycle for the pin to the given value.
* Optionally invert the duty cycle [default = false]
* Optionally change the scale of the provided value to enable finer PWM duty control [default = 255]
*/
static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
/**
* Set the frequency of the timer for the given pin as close as
* possible to the provided desired frequency. Internally calculate
* the required waveform generation mode, prescaler, and resolution
* values and set timer registers accordingly.
* NOTE that the frequency is applied to all pins on the timer (Ex OC3A, OC3B and OC3B)
* NOTE that there are limitations, particularly if using TIMER2. (see Configuration_adv.h -> FAST_PWM_FAN Settings)
*/
static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired);
};

View File

@@ -34,21 +34,21 @@
#include "../../inc/MarlinConfig.h"
void spiBegin() {
OUT_WRITE(SD_SS_PIN, HIGH);
#if PIN_EXISTS(SD_SS)
// Do not init HIGH for boards with pin 4 used as Fans or Heaters or otherwise, not likely to have multiple SPI devices anyway.
#if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__)
// SS must be in output mode even it is not chip select
SET_OUTPUT(SD_SS_PIN);
#else
// set SS high - may be chip select for another SPI device
OUT_WRITE(SD_SS_PIN, HIGH);
#endif
#endif
SET_OUTPUT(SD_SCK_PIN);
SET_INPUT(SD_MISO_PIN);
SET_OUTPUT(SD_MOSI_PIN);
#if DISABLED(SOFTWARE_SPI)
// SS must be in output mode even it is not chip select
//SET_OUTPUT(SD_SS_PIN);
// set SS high - may be chip select for another SPI device
//#if SET_SPI_SS_HIGH
//WRITE(SD_SS_PIN, HIGH);
//#endif
// set a default rate
spiInit(1);
#endif
IF_DISABLED(SOFTWARE_SPI, spiInit(SPI_HALF_SPEED));
}
#if NONE(SOFTWARE_SPI, FORCE_SOFT_SPI)

View File

@@ -486,7 +486,7 @@ void MarlinSerial<Cfg>::write(const uint8_t c) {
const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1);
// If global interrupts are disabled (as the result of being called from an ISR)...
if (!ISRS_ENABLED()) {
if (!hal.isr_state()) {
// Make room by polling if it is possible to transmit, and do so!
while (i == tx_buffer.tail) {
@@ -534,7 +534,7 @@ void MarlinSerial<Cfg>::flushTX() {
if (!_written) return;
// If global interrupts are disabled (as the result of being called from an ISR)...
if (!ISRS_ENABLED()) {
if (!hal.isr_state()) {
// Wait until everything was transmitted - We must do polling, as interrupts are disabled
while (tx_buffer.head != tx_buffer.tail || !B_TXC) {

View File

@@ -191,13 +191,13 @@
rx_framing_errors;
static ring_buffer_pos_t rx_max_enqueued;
static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_head();
FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_head();
static volatile bool rx_tail_value_not_stable;
static volatile uint16_t rx_tail_value_backup;
static FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value);
static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail();
FORCE_INLINE static void atomic_set_rx_tail(ring_buffer_pos_t value);
FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_tail();
public:
FORCE_INLINE static void store_rxd_char();
@@ -217,7 +217,7 @@
#endif
enum { HasEmergencyParser = Cfg::EMERGENCYPARSER };
static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
static bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; }
FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; }
FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; }

View File

@@ -66,27 +66,26 @@ static volatile int8_t Channel[_Nbr_16timers]; // counter for the s
/************ static functions common to all instances ***********************/
static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) {
if (Channel[timer] < 0)
*TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer
else {
if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && SERVO(timer, Channel[timer]).Pin.isActive)
extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated
}
static inline void handle_interrupts(const timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) {
int8_t cho = Channel[timer]; // Handle the prior Channel[timer] first
if (cho < 0) // Channel -1 indicates the refresh interval completed...
*TCNTn = 0; // ...so reset the timer
else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled?
extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW
Channel[timer]++; // increment to the next channel
if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) {
*OCRnA = *TCNTn + SERVO(timer, Channel[timer]).ticks;
if (SERVO(timer, Channel[timer]).Pin.isActive) // check if activated
extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high
Channel[timer] = ++cho; // Handle the next channel (or 0)
if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) {
*OCRnA = *TCNTn + SERVO(timer, cho).ticks; // set compare to current ticks plus duration
if (SERVO(timer, cho).Pin.isActive) // activated?
extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH
}
else {
// finished all channels so wait for the refresh period to expire before starting over
if (((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL)) // allow a few ticks to ensure the next OCR1A not missed
*OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL);
else
*OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed
Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel
const unsigned int cval = ((unsigned)*TCNTn) + 32 / (SERVO_TIMER_PRESCALER), // allow 32 cycles to ensure the next OCR1A not missed
ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed
*OCRnA = max(cval, ival);
Channel[timer] = -1; // reset the timer counter to 0 on the next call
}
}
@@ -123,91 +122,102 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t
/****************** end of static functions ******************************/
void initISR(timer16_Sequence_t timer) {
#ifdef _useTimer1
if (timer == _timer1) {
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
SBI(TIFR, OCF1A); // clear any pending interrupts;
SBI(TIMSK, OCIE1A); // enable the output compare interrupt
#else
// here if not ATmega8 or ATmega128
SBI(TIFR1, OCF1A); // clear any pending interrupts;
SBI(TIMSK1, OCIE1A); // enable the output compare interrupt
#endif
#ifdef WIRING
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
#endif
}
#endif
void initISR(const timer16_Sequence_t timer_index) {
switch (timer_index) {
default: break;
#ifdef _useTimer3
if (timer == _timer3) {
TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count
#ifdef __AVR_ATmega128__
SBI(TIFR, OCF3A); // clear any pending interrupts;
SBI(ETIMSK, OCIE3A); // enable the output compare interrupt
#else
SBI(TIFR3, OCF3A); // clear any pending interrupts;
SBI(TIMSK3, OCIE3A); // enable the output compare interrupt
#endif
#ifdef WIRING
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
#endif
}
#endif
#ifdef _useTimer1
case _timer1:
TCCR1A = 0; // normal counting mode
TCCR1B = _BV(CS11); // set prescaler of 8
TCNT1 = 0; // clear the timer count
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
SBI(TIFR, OCF1A); // clear any pending interrupts;
SBI(TIMSK, OCIE1A); // enable the output compare interrupt
#else
// here if not ATmega8 or ATmega128
SBI(TIFR1, OCF1A); // clear any pending interrupts;
SBI(TIMSK1, OCIE1A); // enable the output compare interrupt
#endif
#ifdef WIRING
timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service);
#endif
break;
#endif
#ifdef _useTimer4
if (timer == _timer4) {
TCCR4A = 0; // normal counting mode
TCCR4B = _BV(CS41); // set prescaler of 8
TCNT4 = 0; // clear the timer count
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
TIMSK4 = _BV(OCIE4A); // enable the output compare interrupt
}
#endif
#ifdef _useTimer3
case _timer3:
TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count
#ifdef __AVR_ATmega128__
SBI(TIFR, OCF3A); // clear any pending interrupts;
SBI(ETIMSK, OCIE3A); // enable the output compare interrupt
#else
SBI(TIFR3, OCF3A); // clear any pending interrupts;
SBI(TIMSK3, OCIE3A); // enable the output compare interrupt
#endif
#ifdef WIRING
timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only
#endif
break;
#endif
#ifdef _useTimer5
if (timer == _timer5) {
TCCR5A = 0; // normal counting mode
TCCR5B = _BV(CS51); // set prescaler of 8
TCNT5 = 0; // clear the timer count
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
TIMSK5 = _BV(OCIE5A); // enable the output compare interrupt
}
#endif
#ifdef _useTimer4
case _timer4:
TCCR4A = 0; // normal counting mode
TCCR4B = _BV(CS41); // set prescaler of 8
TCNT4 = 0; // clear the timer count
TIFR4 = _BV(OCF4A); // clear any pending interrupts;
TIMSK4 = _BV(OCIE4A); // enable the output compare interrupt
break;
#endif
#ifdef _useTimer5
case _timer5:
TCCR5A = 0; // normal counting mode
TCCR5B = _BV(CS51); // set prescaler of 8
TCNT5 = 0; // clear the timer count
TIFR5 = _BV(OCF5A); // clear any pending interrupts;
TIMSK5 = _BV(OCIE5A); // enable the output compare interrupt
break;
#endif
}
}
void finISR(timer16_Sequence_t timer) {
void finISR(const timer16_Sequence_t timer_index) {
// Disable use of the given timer
#ifdef WIRING
if (timer == _timer1) {
CBI(
#if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
TIMSK1
#else
TIMSK
#endif
, OCIE1A); // disable timer 1 output compare interrupt
timerDetach(TIMER1OUTCOMPAREA_INT);
}
else if (timer == _timer3) {
CBI(
#if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
TIMSK3
#else
ETIMSK
#endif
, OCIE3A); // disable the timer3 output compare A interrupt
timerDetach(TIMER3OUTCOMPAREA_INT);
switch (timer_index) {
default: break;
case _timer1:
CBI(
#if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
TIMSK1
#else
TIMSK
#endif
, OCIE1A // disable timer 1 output compare interrupt
);
timerDetach(TIMER1OUTCOMPAREA_INT);
break;
case _timer3:
CBI(
#if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__)
TIMSK3
#else
ETIMSK
#endif
, OCIE3A // disable the timer3 output compare A interrupt
);
timerDetach(TIMER3OUTCOMPAREA_INT);
break;
}
#else // !WIRING
// For arduino - in future: call here to a currently undefined function to reset the timer
UNUSED(timer);
UNUSED(timer_index);
#endif
}

View File

@@ -213,6 +213,51 @@ void setup_endstop_interrupts() {
pciSetup(K_MIN_PIN);
#endif
#endif
#if HAS_U_MAX
#if (digitalPinToInterrupt(U_MAX_PIN) != NOT_AN_INTERRUPT)
_ATTACH(U_MAX_PIN);
#else
static_assert(digitalPinHasPCICR(U_MAX_PIN), "U_MAX_PIN is not interrupt-capable");
pciSetup(U_MAX_PIN);
#endif
#elif HAS_U_MIN
#if (digitalPinToInterrupt(U_MIN_PIN) != NOT_AN_INTERRUPT)
_ATTACH(U_MIN_PIN);
#else
static_assert(digitalPinHasPCICR(U_MIN_PIN), "U_MIN_PIN is not interrupt-capable");
pciSetup(U_MIN_PIN);
#endif
#endif
#if HAS_V_MAX
#if (digitalPinToInterrupt(V_MAX_PIN) != NOT_AN_INTERRUPT)
_ATTACH(V_MAX_PIN);
#else
static_assert(digitalPinHasPCICR(V_MAX_PIN), "V_MAX_PIN is not interrupt-capable");
pciSetup(V_MAX_PIN);
#endif
#elif HAS_V_MIN
#if (digitalPinToInterrupt(V_MIN_PIN) != NOT_AN_INTERRUPT)
_ATTACH(V_MIN_PIN);
#else
static_assert(digitalPinHasPCICR(V_MIN_PIN), "V_MIN_PIN is not interrupt-capable");
pciSetup(V_MIN_PIN);
#endif
#endif
#if HAS_W_MAX
#if (digitalPinToInterrupt(W_MAX_PIN) != NOT_AN_INTERRUPT)
_ATTACH(W_MAX_PIN);
#else
static_assert(digitalPinHasPCICR(W_MAX_PIN), "W_MAX_PIN is not interrupt-capable");
pciSetup(W_MAX_PIN);
#endif
#elif HAS_W_MIN
#if (digitalPinToInterrupt(W_MIN_PIN) != NOT_AN_INTERRUPT)
_ATTACH(W_MIN_PIN);
#else
static_assert(digitalPinHasPCICR(W_MIN_PIN), "W_MIN_PIN is not interrupt-capable");
pciSetup(W_MIN_PIN);
#endif
#endif
#if HAS_X2_MAX
#if (digitalPinToInterrupt(X2_MAX_PIN) != NOT_AN_INTERRUPT)
_ATTACH(X2_MAX_PIN);
@@ -301,5 +346,6 @@ void setup_endstop_interrupts() {
pciSetup(Z_MIN_PROBE_PIN);
#endif
#endif
// If we arrive here without raising an assertion, each pin has either an EXT-interrupt or a PCI.
}

View File

@@ -21,11 +21,7 @@
*/
#ifdef __AVR__
#include "../../inc/MarlinConfigPre.h"
#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM
#include "HAL.h"
#include "../../inc/MarlinConfig.h"
struct Timer {
volatile uint8_t* TCCRnQ[3]; // max 3 TCCR registers per timer
@@ -33,250 +29,194 @@ struct Timer {
volatile uint16_t* ICRn; // max 1 ICR register per timer
uint8_t n; // the timer number [0->5]
uint8_t q; // the timer output [0->2] (A->C)
bool isPWM; // True if pin is a "hardware timer"
bool isProtected; // True if timer is protected
};
// Macros for the Timer structure
#define _SET_WGMnQ(T, V) do{ \
*(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V) & 0x3) << 0); \
*(T.TCCRnQ)[1] = (*(T.TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \
}while(0)
// Set TCCR CS bits
#define _SET_CSn(T, V) (*(T.TCCRnQ)[1] = (*(T.TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0))
// Set TCCR COM bits
#define _SET_COMnQ(T, Q, V) (*(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q))))
// Set OCRnQ register
#define _SET_OCRnQ(T, Q, V) (*(T.OCRnQ)[Q] = int(V) & 0xFFFF)
// Set ICRn register (one per timer)
#define _SET_ICRn(T, V) (*(T.ICRn) = int(V) & 0xFFFF)
/**
* get_pwm_timer
* Get the timer information and register of the provided pin.
* Return a Timer struct containing this information.
* Used by set_pwm_frequency, set_pwm_duty
* Return a Timer struct describing a pin's timer.
*/
Timer get_pwm_timer(const pin_t pin) {
const Timer get_pwm_timer(const pin_t pin) {
uint8_t q = 0;
switch (digitalPinToTimer(pin)) {
// Protect reserved timers (TIMER0 & TIMER1)
#ifdef TCCR0A
#if !AVR_AT90USB1286_FAMILY
case TIMER0A:
#endif
case TIMER0B:
IF_DISABLED(AVR_AT90USB1286_FAMILY, case TIMER0A:)
#endif
#ifdef TCCR1A
case TIMER1A: case TIMER1B:
#endif
break;
#if defined(TCCR2) || defined(TCCR2A)
#ifdef TCCR2
case TIMER2: {
Timer timer = {
/*TCCRnQ*/ { &TCCR2, nullptr, nullptr },
/*OCRnQ*/ { (uint16_t*)&OCR2, nullptr, nullptr },
/*ICRn*/ nullptr,
/*n, q*/ 2, 0
};
}
#elif defined(TCCR2A)
#if ENABLED(USE_OCR2A_AS_TOP)
case TIMER2A: break; // protect TIMER2A
case TIMER2B: {
Timer timer = {
/*TCCRnQ*/ { &TCCR2A, &TCCR2B, nullptr },
/*OCRnQ*/ { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr },
/*ICRn*/ nullptr,
/*n, q*/ 2, 1
};
return timer;
}
#else
case TIMER2B: ++q;
case TIMER2A: {
Timer timer = {
/*TCCRnQ*/ { &TCCR2A, &TCCR2B, nullptr },
/*OCRnQ*/ { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr },
/*ICRn*/ nullptr,
2, q
};
return timer;
}
#endif
#endif
break; // Protect reserved timers (TIMER0 & TIMER1)
#ifdef TCCR0A
case TIMER0B: // Protected timer, but allow setting the duty cycle on OCR0B for pin D4 only
return Timer({ { &TCCR0A, nullptr, nullptr }, { (uint16_t*)&OCR0A, (uint16_t*)&OCR0B, nullptr }, nullptr, 0, 1, true, true });
#endif
#if HAS_TCCR2
case TIMER2:
return Timer({ { &TCCR2, nullptr, nullptr }, { (uint16_t*)&OCR2, nullptr, nullptr }, nullptr, 2, 0, true, false });
#elif ENABLED(USE_OCR2A_AS_TOP)
case TIMER2A: break; // Protect TIMER2A since its OCR is used by TIMER2B
case TIMER2B:
return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, 1, true, false });
#elif defined(TCCR2A)
case TIMER2B: ++q; case TIMER2A:
return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, q, true, false });
#endif
#ifdef OCR3C
case TIMER3C: ++q;
case TIMER3B: ++q;
case TIMER3A: {
Timer timer = {
/*TCCRnQ*/ { &TCCR3A, &TCCR3B, &TCCR3C },
/*OCRnQ*/ { &OCR3A, &OCR3B, &OCR3C },
/*ICRn*/ &ICR3,
/*n, q*/ 3, q
};
return timer;
}
case TIMER3C: ++q; case TIMER3B: ++q; case TIMER3A:
return Timer({ { &TCCR3A, &TCCR3B, &TCCR3C }, { &OCR3A, &OCR3B, &OCR3C }, &ICR3, 3, q, true, false });
#elif defined(OCR3B)
case TIMER3B: ++q;
case TIMER3A: {
Timer timer = {
/*TCCRnQ*/ { &TCCR3A, &TCCR3B, nullptr },
/*OCRnQ*/ { &OCR3A, &OCR3B, nullptr },
/*ICRn*/ &ICR3,
/*n, q*/ 3, q
};
return timer;
}
case TIMER3B: ++q; case TIMER3A:
return Timer({ { &TCCR3A, &TCCR3B, nullptr }, { &OCR3A, &OCR3B, nullptr }, &ICR3, 3, q, true, false });
#endif
#ifdef TCCR4A
case TIMER4C: ++q;
case TIMER4B: ++q;
case TIMER4A: {
Timer timer = {
/*TCCRnQ*/ { &TCCR4A, &TCCR4B, &TCCR4C },
/*OCRnQ*/ { &OCR4A, &OCR4B, &OCR4C },
/*ICRn*/ &ICR4,
/*n, q*/ 4, q
};
return timer;
}
case TIMER4C: ++q; case TIMER4B: ++q; case TIMER4A:
return Timer({ { &TCCR4A, &TCCR4B, &TCCR4C }, { &OCR4A, &OCR4B, &OCR4C }, &ICR4, 4, q, true, false });
#endif
#ifdef TCCR5A
case TIMER5C: ++q;
case TIMER5B: ++q;
case TIMER5A: {
Timer timer = {
/*TCCRnQ*/ { &TCCR5A, &TCCR5B, &TCCR5C },
/*OCRnQ*/ { &OCR5A, &OCR5B, &OCR5C },
/*ICRn*/ &ICR5,
/*n, q*/ 5, q
};
return timer;
}
case TIMER5C: ++q; case TIMER5B: ++q; case TIMER5A:
return Timer({ { &TCCR5A, &TCCR5B, &TCCR5C }, { &OCR5A, &OCR5B, &OCR5C }, &ICR5, 5, q, true, false });
#endif
}
Timer timer = {
/*TCCRnQ*/ { nullptr, nullptr, nullptr },
/*OCRnQ*/ { nullptr, nullptr, nullptr },
/*ICRn*/ nullptr,
0, 0
};
return timer;
return Timer();
}
void set_pwm_frequency(const pin_t pin, int f_desired) {
Timer timer = get_pwm_timer(pin);
if (timer.n == 0) return; // Don't proceed if protected timer or not recognised
uint16_t size;
if (timer.n == 2) size = 255; else size = 65535;
void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) {
const Timer timer = get_pwm_timer(pin);
if (timer.isProtected || !timer.isPWM) return; // Don't proceed if protected timer or not recognized
uint16_t res = 255; // resolution (TOP value)
uint8_t j = 0; // prescaler index
uint8_t wgm = 1; // waveform generation mode
const bool is_timer2 = timer.n == 2;
const uint16_t maxtop = is_timer2 ? 0xFF : 0xFFFF;
uint16_t res = 0xFF; // resolution (TOP value)
uint8_t j = CS_NONE; // prescaler index
uint8_t wgm = WGM_PWM_PC_8; // waveform generation mode
// Calculating the prescaler and resolution to use to achieve closest frequency
if (f_desired != 0) {
int f = (F_CPU) / (2 * 1024 * size) + 1; // Initialize frequency as lowest (non-zero) achievable
uint16_t prescaler[] = { 0, 1, 8, /*TIMER2 ONLY*/32, 64, /*TIMER2 ONLY*/128, 256, 1024 };
constexpr uint16_t prescaler[] = { 1, 8, (32), 64, (128), 256, 1024 }; // (*) are Timer 2 only
uint16_t f = (F_CPU) / (2 * 1024 * maxtop) + 1; // Start with the lowest non-zero frequency achievable (1 or 31)
// loop over prescaler values
LOOP_S_L_N(i, 1, 8) {
uint16_t res_temp_fast = 255, res_temp_phase_correct = 255;
if (timer.n == 2) {
// No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP
#if ENABLED(USE_OCR2A_AS_TOP)
const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired);
res_temp_fast = rtf - 1;
res_temp_phase_correct = rtf / 2;
LOOP_L_N(i, COUNT(prescaler)) { // Loop through all prescaler values
const uint16_t p = prescaler[i];
uint16_t res_fast_temp, res_pc_temp;
if (is_timer2) {
#if ENABLED(USE_OCR2A_AS_TOP) // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP
const uint16_t rft = (F_CPU) / (p * f_desired);
res_fast_temp = rft - 1;
res_pc_temp = rft / 2;
#else
res_fast_temp = res_pc_temp = maxtop;
#endif
}
else {
// Skip TIMER2 specific prescalers when not TIMER2
if (i == 3 || i == 5) continue;
const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired);
res_temp_fast = rtf - 1;
res_temp_phase_correct = rtf / 2;
if (p == 32 || p == 128) continue; // Skip TIMER2 specific prescalers when not TIMER2
const uint16_t rft = (F_CPU) / (p * f_desired);
res_fast_temp = rft - 1;
res_pc_temp = rft / 2;
}
LIMIT(res_temp_fast, 1U, size);
LIMIT(res_temp_phase_correct, 1U, size);
LIMIT(res_fast_temp, 1U, maxtop);
LIMIT(res_pc_temp, 1U, maxtop);
// Calculate frequencies of test prescaler and resolution values
const int f_temp_fast = (F_CPU) / (prescaler[i] * (1 + res_temp_fast)),
f_temp_phase_correct = (F_CPU) / (2 * prescaler[i] * res_temp_phase_correct),
f_diff = ABS(f - f_desired),
f_fast_diff = ABS(f_temp_fast - f_desired),
f_phase_diff = ABS(f_temp_phase_correct - f_desired);
const uint32_t f_diff = _MAX(f, f_desired) - _MIN(f, f_desired),
f_fast_temp = (F_CPU) / (p * (1 + res_fast_temp)),
f_fast_diff = _MAX(f_fast_temp, f_desired) - _MIN(f_fast_temp, f_desired),
f_pc_temp = (F_CPU) / (2 * p * res_pc_temp),
f_pc_diff = _MAX(f_pc_temp, f_desired) - _MIN(f_pc_temp, f_desired);
// If FAST values are closest to desired f
if (f_fast_diff < f_diff && f_fast_diff <= f_phase_diff) {
// Remember this combination
f = f_temp_fast;
res = res_temp_fast;
j = i;
if (f_fast_diff < f_diff && f_fast_diff <= f_pc_diff) { // FAST values are closest to desired f
// Set the Wave Generation Mode to FAST PWM
if (timer.n == 2) {
wgm = (
#if ENABLED(USE_OCR2A_AS_TOP)
WGM2_FAST_PWM_OCR2A
#else
WGM2_FAST_PWM
#endif
);
}
else wgm = WGM_FAST_PWM_ICRn;
wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_FAST_PWM_OCR2A, WGM2_FAST_PWM)) : uint8_t(WGM_FAST_PWM_ICRn);
// Remember this combination
f = f_fast_temp; res = res_fast_temp; j = i + 1;
}
// If PHASE CORRECT values are closes to desired f
else if (f_phase_diff < f_diff) {
f = f_temp_phase_correct;
res = res_temp_phase_correct;
j = i;
else if (f_pc_diff < f_diff) { // PHASE CORRECT values are closes to desired f
// Set the Wave Generation Mode to PWM PHASE CORRECT
if (timer.n == 2) {
wgm = (
#if ENABLED(USE_OCR2A_AS_TOP)
WGM2_PWM_PC_OCR2A
#else
WGM2_PWM_PC
#endif
);
}
else wgm = WGM_PWM_PC_ICRn;
wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_PWM_PC_OCR2A, WGM2_PWM_PC)) : uint8_t(WGM_PWM_PC_ICRn);
f = f_pc_temp; res = res_pc_temp; j = i + 1;
}
}
}
_SET_WGMnQ(timer.TCCRnQ, wgm);
_SET_CSn(timer.TCCRnQ, j);
if (timer.n == 2) {
#if ENABLED(USE_OCR2A_AS_TOP)
_SET_OCRnQ(timer.OCRnQ, 0, res); // Set OCR2A value (TOP) = res
#endif
_SET_WGMnQ(timer, wgm);
_SET_CSn(timer, j);
if (is_timer2) {
TERN_(USE_OCR2A_AS_TOP, _SET_OCRnQ(timer, 0, res)); // Set OCR2A value (TOP) = res
}
else
_SET_ICRn(timer.ICRn, res); // Set ICRn value (TOP) = res
_SET_ICRn(timer, res); // Set ICRn value (TOP) = res
}
void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) {
// If v is 0 or v_size (max), digitalWrite to LOW or HIGH.
// Note that digitalWrite also disables pwm output for us (sets COM bit to 0)
// Note that digitalWrite also disables PWM output for us (sets COM bit to 0)
if (v == 0)
digitalWrite(pin, invert);
else if (v == v_size)
digitalWrite(pin, !invert);
else {
Timer timer = get_pwm_timer(pin);
if (timer.n == 0) return; // Don't proceed if protected timer or not recognised
// Set compare output mode to CLEAR -> SET or SET -> CLEAR (if inverted)
_SET_COMnQ(timer.TCCRnQ, (timer.q
#ifdef TCCR2
+ (timer.q == 2) // COM20 is on bit 4 of TCCR2, thus requires q + 1 in the macro
#endif
), COM_CLEAR_SET + invert
);
uint16_t top;
if (timer.n == 2) { // if TIMER2
top = (
#if ENABLED(USE_OCR2A_AS_TOP)
*timer.OCRnQ[0] // top = OCR2A
#else
255 // top = 0xFF (max)
#endif
);
const Timer timer = get_pwm_timer(pin);
if (timer.isPWM) {
if (timer.n == 0) {
_SET_COMnQ(timer, timer.q, COM_CLEAR_SET); // Only allow a TIMER0B select...
_SET_OCRnQ(timer, timer.q, v); // ...and OCR0B duty update. For output pin D4 no frequency changes are permitted.
}
else if (!timer.isProtected) {
const uint16_t top = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, *timer.OCRnQ[0], 255) : *timer.ICRn;
_SET_COMnQ(timer, SUM_TERN(HAS_TCCR2, timer.q, timer.q == 2), COM_CLEAR_SET + invert); // COM20 is on bit 4 of TCCR2, so +1 for q==2
_SET_OCRnQ(timer, timer.q, uint16_t(uint32_t(v) * top / v_size)); // Scale 8/16-bit v to top value
}
}
else
top = *timer.ICRn; // top = ICRn
_SET_OCRnQ(timer.OCRnQ, timer.q, v * float(top) / float(v_size)); // Scale 8/16-bit v to top value
digitalWrite(pin, v < v_size / 2 ? LOW : HIGH);
}
}
#endif // NEEDS_HARDWARE_PWM
void MarlinHAL::init_pwm_timers() {
// Init some timer frequencies to a default 1KHz
const pin_t pwm_pin[] = {
#ifdef __AVR_ATmega2560__
10, 5, 6, 46
#elif defined(__AVR_ATmega1280__)
12, 31
#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284__)
15, 6
#elif defined(__AVR_AT90USB1286__) || defined(__AVR_mega64) || defined(__AVR_mega128)
16, 24
#endif
};
LOOP_L_N(i, COUNT(pwm_pin))
set_pwm_frequency(pwm_pin[i], 1000);
}
#endif // __AVR__

View File

@@ -245,7 +245,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb
float count = 0;
if (hz > 0 && (dca || dcb || dcc)) {
count = float(F_CPU) / hz; // 1x prescaler, TOP for 16MHz base freq.
uint16_t prescaler; // Range of 30.5Hz (65535) 64.5KHz (>31)
uint16_t prescaler; // Range of 30.5Hz (65535) 64.5kHz (>31)
if (count >= 255. * 256.) { prescaler = 1024; SET_CS(5, PRESCALER_1024); }
else if (count >= 255. * 64.) { prescaler = 256; SET_CS(5, PRESCALER_256); }
@@ -257,7 +257,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb
const float pwm_top = round(count); // Get the rounded count
ICR5 = (uint16_t)pwm_top - 1; // Subtract 1 for TOP
OCR5A = pwm_top * ABS(dca); // Update and scale DCs
OCR5A = pwm_top * ABS(dca); // Update and scale DCs
OCR5B = pwm_top * ABS(dcb);
OCR5C = pwm_top * ABS(dcc);
_SET_COM(5, A, dca ? (dca < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL); // Set compare modes
@@ -277,7 +277,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb
// Restore the default for Timer 5
SET_WGM(5, PWM_PC_8); // PWM 8-bit (Phase Correct)
SET_COMS(5, NORMAL, NORMAL, NORMAL); // Do nothing
SET_CS(5, PRESCALER_64); // 16MHz / 64 = 250KHz
SET_CS(5, PRESCALER_64); // 16MHz / 64 = 250kHz
OCR5A = OCR5B = OCR5C = 0;
}
return round(count);

View File

@@ -118,7 +118,7 @@
*/
// Waveform Generation Modes
enum WaveGenMode : char {
enum WaveGenMode : uint8_t {
WGM_NORMAL, // 0
WGM_PWM_PC_8, // 1
WGM_PWM_PC_9, // 2
@@ -138,19 +138,19 @@ enum WaveGenMode : char {
};
// Wavefore Generation Modes (Timer 2 only)
enum WaveGenMode2 : char {
WGM2_NORMAL, // 0
WGM2_PWM_PC, // 1
WGM2_CTC_OCR2A, // 2
WGM2_FAST_PWM, // 3
WGM2_reserved_1, // 4
WGM2_PWM_PC_OCR2A, // 5
WGM2_reserved_2, // 6
WGM2_FAST_PWM_OCR2A, // 7
enum WaveGenMode2 : uint8_t {
WGM2_NORMAL, // 0
WGM2_PWM_PC, // 1
WGM2_CTC_OCR2A, // 2
WGM2_FAST_PWM, // 3
WGM2_reserved_1, // 4
WGM2_PWM_PC_OCR2A, // 5
WGM2_reserved_2, // 6
WGM2_FAST_PWM_OCR2A, // 7
};
// Compare Modes
enum CompareMode : char {
enum CompareMode : uint8_t {
COM_NORMAL, // 0
COM_TOGGLE, // 1 Non-PWM: OCnx ... Both PWM (WGM 9,11,14,15): OCnA only ... else NORMAL
COM_CLEAR_SET, // 2 Non-PWM: OCnx ... Fast PWM: OCnx/Bottom ... PF-FC: OCnx Up/Down
@@ -158,7 +158,7 @@ enum CompareMode : char {
};
// Clock Sources
enum ClockSource : char {
enum ClockSource : uint8_t {
CS_NONE, // 0
CS_PRESCALER_1, // 1
CS_PRESCALER_8, // 2
@@ -170,7 +170,7 @@ enum ClockSource : char {
};
// Clock Sources (Timer 2 only)
enum ClockSource2 : char {
enum ClockSource2 : uint8_t {
CS2_NONE, // 0
CS2_PRESCALER_1, // 1
CS2_PRESCALER_8, // 2
@@ -203,40 +203,33 @@ enum ClockSource2 : char {
TCCR##T##B = (TCCR##T##B & ~(0x3 << WGM##T##2)) | (((int(V) >> 2) & 0x3) << WGM##T##2); \
}while(0)
#define SET_WGM(T,V) _SET_WGM(T,WGM_##V)
// Runtime (see set_pwm_frequency):
#define _SET_WGMnQ(TCCRnQ, V) do{ \
*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V) & 0x3) << 0); \
*(TCCRnQ)[1] = (*(TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \
}while(0)
// Set Clock Select bits
// Ex: SET_CS3(PRESCALER_64);
#ifdef TCCR2
#define HAS_TCCR2 1
#endif
#define _SET_CS(T,V) (TCCR##T##B = (TCCR##T##B & ~(0x7 << CS##T##0)) | ((int(V) & 0x7) << CS##T##0))
#define _SET_CS0(V) _SET_CS(0,V)
#define _SET_CS1(V) _SET_CS(1,V)
#ifdef TCCR2
#define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
#else
#define _SET_CS2(V) _SET_CS(2,V)
#endif
#define _SET_CS3(V) _SET_CS(3,V)
#define _SET_CS4(V) _SET_CS(4,V)
#define _SET_CS5(V) _SET_CS(5,V)
#define SET_CS0(V) _SET_CS0(CS_##V)
#define SET_CS1(V) _SET_CS1(CS_##V)
#ifdef TCCR2
#if HAS_TCCR2
#define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20))
#define SET_CS2(V) _SET_CS2(CS2_##V)
#else
#define _SET_CS2(V) _SET_CS(2,V)
#define SET_CS2(V) _SET_CS2(CS_##V)
#endif
#define SET_CS3(V) _SET_CS3(CS_##V)
#define SET_CS4(V) _SET_CS4(CS_##V)
#define SET_CS5(V) _SET_CS5(CS_##V)
#define SET_CS(T,V) SET_CS##T(V)
// Runtime (see set_pwm_frequency)
#define _SET_CSn(TCCRnQ, V) do{ \
(*(TCCRnQ)[1] = (*(TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0)); \
}while(0)
// Set Compare Mode bits
// Ex: SET_COMS(4,CLEAR_SET,CLEAR_SET,CLEAR_SET);
@@ -246,22 +239,6 @@ enum ClockSource2 : char {
#define SET_COMB(T,V) SET_COM(T,B,V)
#define SET_COMC(T,V) SET_COM(T,C,V)
#define SET_COMS(T,V1,V2,V3) do{ SET_COMA(T,V1); SET_COMB(T,V2); SET_COMC(T,V3); }while(0)
// Runtime (see set_pwm_duty)
#define _SET_COMnQ(TCCRnQ, Q, V) do{ \
(*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q)))); \
}while(0)
// Set OCRnQ register
// Runtime (see set_pwm_duty):
#define _SET_OCRnQ(OCRnQ, Q, V) do{ \
(*(OCRnQ)[(Q)] = (0x0000) | (int(V) & 0xFFFF)); \
}while(0)
// Set ICRn register (one per timer)
// Runtime (see set_pwm_frequency)
#define _SET_ICRn(ICRn, V) do{ \
(*(ICRn) = (0x0000) | (int(V) & 0xFFFF)); \
}while(0)
// Set Noise Canceler bit
// Ex: SET_ICNC(2,1)

View File

@@ -25,11 +25,51 @@
* Test AVR-specific configuration values for errors at compile-time.
*/
/**
* Check for common serial pin conflicts
*/
#define CHECK_SERIAL_PIN(N) ( \
X_STOP_PIN == N || Y_STOP_PIN == N || Z_STOP_PIN == N \
|| X_MIN_PIN == N || Y_MIN_PIN == N || Z_MIN_PIN == N \
|| X_MAX_PIN == N || Y_MAX_PIN == N || Z_MAX_PIN == N \
|| X_STEP_PIN == N || Y_STEP_PIN == N || Z_STEP_PIN == N \
|| X_DIR_PIN == N || Y_DIR_PIN == N || Z_DIR_PIN == N \
|| X_ENA_PIN == N || Y_ENA_PIN == N || Z_ENA_PIN == N \
|| BTN_EN1 == N || BTN_EN2 == N \
)
#if CONF_SERIAL_IS(0)
// D0-D1. No known conflicts.
#endif
#if NOT_TARGET(__AVR_ATmega644P__, __AVR_ATmega1284P__)
#if CONF_SERIAL_IS(1) && (CHECK_SERIAL_PIN(18) || CHECK_SERIAL_PIN(19))
#error "Serial Port 1 pin D18 and/or D19 conflicts with another pin on the board."
#endif
#else
#if CONF_SERIAL_IS(1) && (CHECK_SERIAL_PIN(10) || CHECK_SERIAL_PIN(11))
#error "Serial Port 1 pin D10 and/or D11 conflicts with another pin on the board."
#endif
#endif
#if CONF_SERIAL_IS(2) && (CHECK_SERIAL_PIN(16) || CHECK_SERIAL_PIN(17))
#error "Serial Port 2 pin D16 and/or D17 conflicts with another pin on the board."
#endif
#if CONF_SERIAL_IS(3) && (CHECK_SERIAL_PIN(14) || CHECK_SERIAL_PIN(15))
#error "Serial Port 3 pin D14 and/or D15 conflicts with another pin on the board."
#endif
#undef CHECK_SERIAL_PIN
/**
* Checks for FAST PWM
*/
#if ENABLED(FAST_PWM_FAN) && (ENABLED(USE_OCR2A_AS_TOP) && defined(TCCR2))
#error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2"
#if ALL(FAST_PWM_FAN, USE_OCR2A_AS_TOP, HAS_TCCR2)
#error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2."
#endif
/**
* Checks for SOFT PWM
*/
#if HAS_FAN0 && FAN_PIN == 9 && DISABLED(FAN_SOFT_PWM) && ENABLED(SPEAKER)
#error "FAN_PIN 9 Hardware PWM uses Timer 2 which conflicts with Arduino AVR Tone Timer (for SPEAKER)."
#error "Disable SPEAKER or enable FAN_SOFT_PWM."
#endif
/**
@@ -42,7 +82,7 @@
#elif NUM_SERVOS > 0 && defined(_useTimer3) && (WITHIN(SPINDLE_LASER_PWM_PIN, 2, 3) || SPINDLE_LASER_PWM_PIN == 5)
#error "Counter/Timer for SPINDLE_LASER_PWM_PIN is used by the servo system."
#endif
#elif defined(SPINDLE_LASER_FREQUENCY)
#elif SPINDLE_LASER_FREQUENCY
#error "SPINDLE_LASER_FREQUENCY requires SPINDLE_LASER_USE_PWM."
#endif
@@ -63,3 +103,7 @@
#if ENABLED(POSTMORTEM_DEBUGGING)
#error "POSTMORTEM_DEBUGGING is not supported on AVR boards."
#endif
#if USING_PULLDOWNS
#error "PULLDOWN pin mode is not available on AVR boards."
#endif

View File

@@ -35,7 +35,7 @@
// C B A is longIn1
// D C B A is longIn2
//
static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) {
FORCE_INLINE static uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) {
uint8_t tmp1;
uint8_t tmp2;
uint16_t intRes;
@@ -89,7 +89,7 @@ static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2
// uses:
// r26 to store 0
// r27 to store the byte 1 of the 24 bit result
static FORCE_INLINE uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) {
FORCE_INLINE static uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) {
uint8_t tmp;
uint16_t intRes;
__asm__ __volatile__ (

View File

@@ -74,7 +74,7 @@
#define MULTI_NAME_PAD 26 // space needed to be pretty if not first name assigned to a pin
void PRINT_ARRAY_NAME(uint8_t x) {
char *name_mem_pointer = (char*)pgm_read_ptr(&pin_array[x].name);
PGM_P const name_mem_pointer = (PGM_P)pgm_read_ptr(&pin_array[x].name);
LOOP_L_N(y, MAX_NAME_LENGTH) {
char temp_char = pgm_read_byte(name_mem_pointer + y);
if (temp_char != 0)
@@ -102,7 +102,7 @@ void PRINT_ARRAY_NAME(uint8_t x) {
return true; \
} else return false
#define ABTEST(N) defined(TCCR##N##A) && defined(COM##N##A1)
/**
* Print a pin's PWM status.
@@ -113,7 +113,7 @@ static bool pwm_status(uint8_t pin) {
switch (digitalPinToTimer_DEBUG(pin)) {
#if defined(TCCR0A) && defined(COM0A1)
#if ABTEST(0)
#ifdef TIMER0A
#if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs
PWM_CASE(0, A);
@@ -122,20 +122,20 @@ static bool pwm_status(uint8_t pin) {
PWM_CASE(0, B);
#endif
#if defined(TCCR1A) && defined(COM1A1)
#if ABTEST(1)
PWM_CASE(1, A);
PWM_CASE(1, B);
#if defined(COM1C1) && defined(TIMER1C)
PWM_CASE(1, C);
#endif
#if defined(COM1C1) && defined(TIMER1C)
PWM_CASE(1, C);
#endif
#endif
#if defined(TCCR2A) && defined(COM2A1)
#if ABTEST(2)
PWM_CASE(2, A);
PWM_CASE(2, B);
#endif
#if defined(TCCR3A) && defined(COM3A1)
#if ABTEST(3)
PWM_CASE(3, A);
PWM_CASE(3, B);
#ifdef COM3C1
@@ -149,7 +149,7 @@ static bool pwm_status(uint8_t pin) {
PWM_CASE(4, C);
#endif
#if defined(TCCR5A) && defined(COM5A1)
#if ABTEST(5)
PWM_CASE(5, A);
PWM_CASE(5, B);
PWM_CASE(5, C);
@@ -166,16 +166,16 @@ static bool pwm_status(uint8_t pin) {
const volatile uint8_t* const PWM_other[][3] PROGMEM = {
{ &TCCR0A, &TCCR0B, &TIMSK0 },
{ &TCCR1A, &TCCR1B, &TIMSK1 },
#if defined(TCCR2A) && defined(COM2A1)
#if ABTEST(2)
{ &TCCR2A, &TCCR2B, &TIMSK2 },
#endif
#if defined(TCCR3A) && defined(COM3A1)
#if ABTEST(3)
{ &TCCR3A, &TCCR3B, &TIMSK3 },
#endif
#ifdef TCCR4A
{ &TCCR4A, &TCCR4B, &TIMSK4 },
#endif
#if defined(TCCR5A) && defined(COM5A1)
#if ABTEST(5)
{ &TCCR5A, &TCCR5B, &TIMSK5 },
#endif
};
@@ -195,11 +195,11 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
{ (const uint8_t*)&OCR1A, (const uint8_t*)&OCR1B, 0 },
#endif
#if defined(TCCR2A) && defined(COM2A1)
#if ABTEST(2)
{ &OCR2A, &OCR2B, 0 },
#endif
#if defined(TCCR3A) && defined(COM3A1)
#if ABTEST(3)
#ifdef COM3C1
{ (const uint8_t*)&OCR3A, (const uint8_t*)&OCR3B, (const uint8_t*)&OCR3C },
#else
@@ -211,7 +211,7 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = {
{ (const uint8_t*)&OCR4A, (const uint8_t*)&OCR4B, (const uint8_t*)&OCR4C },
#endif
#if defined(TCCR5A) && defined(COM5A1)
#if ABTEST(5)
{ (const uint8_t*)&OCR5A, (const uint8_t*)&OCR5B, (const uint8_t*)&OCR5C },
#endif
};
@@ -281,7 +281,7 @@ void timer_prefix(uint8_t T, char L, uint8_t N) { // T - timer L - pwm N -
static void pwm_details(uint8_t pin) {
switch (digitalPinToTimer_DEBUG(pin)) {
#if defined(TCCR0A) && defined(COM0A1)
#if ABTEST(0)
#ifdef TIMER0A
#if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs
case TIMER0A: timer_prefix(0, 'A', 3); break;
@@ -290,7 +290,7 @@ static void pwm_details(uint8_t pin) {
case TIMER0B: timer_prefix(0, 'B', 3); break;
#endif
#if defined(TCCR1A) && defined(COM1A1)
#if ABTEST(1)
case TIMER1A: timer_prefix(1, 'A', 4); break;
case TIMER1B: timer_prefix(1, 'B', 4); break;
#if defined(COM1C1) && defined(TIMER1C)
@@ -298,12 +298,12 @@ static void pwm_details(uint8_t pin) {
#endif
#endif
#if defined(TCCR2A) && defined(COM2A1)
#if ABTEST(2)
case TIMER2A: timer_prefix(2, 'A', 3); break;
case TIMER2B: timer_prefix(2, 'B', 3); break;
#endif
#if defined(TCCR3A) && defined(COM3A1)
#if ABTEST(3)
case TIMER3A: timer_prefix(3, 'A', 4); break;
case TIMER3B: timer_prefix(3, 'B', 4); break;
#ifdef COM3C1
@@ -317,7 +317,7 @@ static void pwm_details(uint8_t pin) {
case TIMER4C: timer_prefix(4, 'C', 4); break;
#endif
#if defined(TCCR5A) && defined(COM5A1)
#if ABTEST(5)
case TIMER5A: timer_prefix(5, 'A', 4); break;
case TIMER5B: timer_prefix(5, 'B', 4); break;
case TIMER5C: timer_prefix(5, 'C', 4); break;
@@ -351,7 +351,6 @@ static void pwm_details(uint8_t pin) {
#endif
} // pwm_details
#ifndef digitalRead_mod // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs
int digitalRead_mod(const int8_t pin) { // same as digitalRead except the PWM stop section has been removed
const uint8_t port = digitalPinToPort_DEBUG(pin);
@@ -397,3 +396,5 @@ static void pwm_details(uint8_t pin) {
#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0)
#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0)
#undef ABTEST

View File

@@ -34,14 +34,14 @@ typedef uint16_t hal_timer_t;
#define HAL_TIMER_RATE ((F_CPU) / 8) // i.e., 2MHz or 2.5MHz
#ifndef STEP_TIMER_NUM
#define STEP_TIMER_NUM 1
#ifndef MF_TIMER_STEP
#define MF_TIMER_STEP 1
#endif
#ifndef PULSE_TIMER_NUM
#define PULSE_TIMER_NUM STEP_TIMER_NUM
#ifndef MF_TIMER_PULSE
#define MF_TIMER_PULSE MF_TIMER_STEP
#endif
#ifndef TEMP_TIMER_NUM
#define TEMP_TIMER_NUM 0
#ifndef MF_TIMER_TEMP
#define MF_TIMER_TEMP 0
#endif
#define TEMP_TIMER_FREQUENCY ((F_CPU) / 64.0 / 256.0)
@@ -58,13 +58,13 @@ typedef uint16_t hal_timer_t;
#define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A)
#define STEPPER_ISR_ENABLED() TEST(TIMSK1, OCIE1A)
#define ENABLE_TEMPERATURE_INTERRUPT() SBI(TIMSK0, OCIE0B)
#define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0B)
#define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0B)
#define ENABLE_TEMPERATURE_INTERRUPT() SBI(TIMSK0, OCIE0A)
#define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0A)
#define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0A)
FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
switch (timer_num) {
case STEP_TIMER_NUM:
case MF_TIMER_STEP:
// waveform generation = 0100 = CTC
SET_WGM(1, CTC_OCRnA);
@@ -84,10 +84,10 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
TCNT1 = 0;
break;
case TEMP_TIMER_NUM:
case MF_TIMER_TEMP:
// Use timer0 for temperature measurement
// Interleave temperature interrupt with millies interrupt
OCR0B = 128;
OCR0A = 128;
break;
}
}
@@ -109,12 +109,12 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) {
* (otherwise, characters will be lost due to UART overflow).
* Then: Stepper, Endstops, Temperature, and -finally- all others.
*/
#define HAL_timer_isr_prologue(TIMER_NUM)
#define HAL_timer_isr_epilogue(TIMER_NUM)
#define HAL_timer_isr_prologue(T) NOOP
#define HAL_timer_isr_epilogue(T) NOOP
/* 18 cycles maximum latency */
#ifndef HAL_STEP_TIMER_ISR
/* 18 cycles maximum latency */
#define HAL_STEP_TIMER_ISR() \
extern "C" void TIMER1_COMPA_vect() __attribute__ ((signal, naked, used, externally_visible)); \
extern "C" void TIMER1_COMPA_vect_bottom() asm ("TIMER1_COMPA_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \
@@ -180,7 +180,7 @@ void TIMER1_COMPA_vect() { \
: \
: [timsk0] "i" ((uint16_t)&TIMSK0), \
[timsk1] "i" ((uint16_t)&TIMSK1), \
[msk0] "M" ((uint8_t)(1<<OCIE0B)),\
[msk0] "M" ((uint8_t)(1<<OCIE0A)),\
[msk1] "M" ((uint8_t)(1<<OCIE1A)) \
: \
); \
@@ -193,9 +193,9 @@ void TIMER1_COMPA_vect_bottom()
/* 14 cycles maximum latency */
#define HAL_TEMP_TIMER_ISR() \
extern "C" void TIMER0_COMPB_vect() __attribute__ ((signal, naked, used, externally_visible)); \
extern "C" void TIMER0_COMPB_vect_bottom() asm ("TIMER0_COMPB_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \
void TIMER0_COMPB_vect() { \
extern "C" void TIMER0_COMPA_vect() __attribute__ ((signal, naked, used, externally_visible)); \
extern "C" void TIMER0_COMPA_vect_bottom() asm ("TIMER0_COMPA_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \
void TIMER0_COMPA_vect() { \
__asm__ __volatile__ ( \
A("push r16") /* 2 Save R16 */ \
A("in r16, __SREG__") /* 1 Get SREG */ \
@@ -223,7 +223,7 @@ void TIMER0_COMPB_vect() { \
A("push r30") \
A("push r31") \
A("clr r1") /* C runtime expects this register to be 0 */ \
A("call TIMER0_COMPB_vect_bottom") /* Call the bottom handler - No inlining allowed, otherwise registers used are not saved */ \
A("call TIMER0_COMPA_vect_bottom") /* Call the bottom handler - No inlining allowed, otherwise registers used are not saved */ \
A("pop r31") \
A("pop r30") \
A("pop r27") \
@@ -251,10 +251,10 @@ void TIMER0_COMPB_vect() { \
A("reti") /* 4 Return from interrupt */ \
: \
: [timsk0] "i"((uint16_t)&TIMSK0), \
[msk0] "M" ((uint8_t)(1<<OCIE0B)) \
[msk0] "M" ((uint8_t)(1<<OCIE0A)) \
: \
); \
} \
void TIMER0_COMPB_vect_bottom()
void TIMER0_COMPA_vect_bottom()
#endif // HAL_TEMP_TIMER_ISR

View File

@@ -1,70 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 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/>.
*
*/
#ifdef __AVR__
#include "../../inc/MarlinConfig.h"
#if ENABLED(USE_WATCHDOG)
#include "watchdog.h"
#include "../../MarlinCore.h"
// Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s.
void watchdog_init() {
#if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S)
#define WDTO_NS WDTO_8S
#else
#define WDTO_NS WDTO_4S
#endif
#if ENABLED(WATCHDOG_RESET_MANUAL)
// Enable the watchdog timer, but only for the interrupt.
// Take care, as this requires the correct order of operation, with interrupts disabled.
// See the datasheet of any AVR chip for details.
wdt_reset();
cli();
_WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE);
_WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5
// So worked for up to WDTO_2S
sei();
wdt_reset();
#else
wdt_enable(WDTO_NS); // The function handles the upper bit correct.
#endif
//delay(10000); // test it!
}
//===========================================================================
//=================================== ISR ===================================
//===========================================================================
// Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled.
#if ENABLED(WATCHDOG_RESET_MANUAL)
ISR(WDT_vect) {
sei(); // With the interrupt driven serial we need to allow interrupts.
SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED);
minkill(); // interrupt-safe final kill and infinite loop
}
#endif
#endif // USE_WATCHDOG
#endif // __AVR__

View File

@@ -1,31 +0,0 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 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
#include <avr/wdt.h>
// Initialize watchdog with a 4 second interrupt time
void watchdog_init();
// Reset watchdog. MUST be called at least every 4 seconds after the
// first watchdog_init or AVR will go into emergency procedures.
inline void HAL_watchdog_refresh() { wdt_reset(); }