213 lines
7.2 KiB
C++
213 lines
7.2 KiB
C++
/**
|
|
* Marlin 3D Printer Firmware
|
|
* Copyright (c) 2022 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/>.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* SAMD21 HAL developed by Bart Meijer (brupje)
|
|
* Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician)
|
|
*/
|
|
#ifdef __SAMD21__
|
|
|
|
#include "../../inc/MarlinConfig.h"
|
|
|
|
#include <wiring_private.h>
|
|
|
|
#if USING_HW_SERIALUSB
|
|
DefaultSerial1 MSerialUSB(false, SerialUSB);
|
|
#endif
|
|
#if USING_HW_SERIAL0
|
|
DefaultSerial2 MSerial1(false, Serial1);
|
|
#endif
|
|
#if USING_HW_SERIAL1
|
|
DefaultSerial3 MSerial2(false, Serial2);
|
|
#endif
|
|
|
|
|
|
|
|
#define WDT_CONFIG_PER_7_Val 0x9u
|
|
#define WDT_CONFIG_PER_Pos 0
|
|
#define WDT_CONFIG_PER_7 (WDT_CONFIG_PER_7_Val << WDT_CONFIG_PER_Pos)
|
|
|
|
#if ENABLED(USE_WATCHDOG)
|
|
|
|
#define WDT_TIMEOUT_REG TERN(WATCHDOG_DURATION_8S, WDT_CONFIG_PER_CYC8192, WDT_CONFIG_PER_CYC4096) // 4 or 8 second timeout
|
|
|
|
void MarlinHAL::watchdog_init() {
|
|
// Set up the generic clock (GCLK2) used to clock the watchdog timer at 1.024kHz
|
|
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(4) | // Divide the 32.768kHz clock source by divisor 32, where 2^(4 + 1): 32.768kHz/32=1.024kHz
|
|
GCLK_GENDIV_ID(2); // Select Generic Clock (GCLK) 2
|
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
|
|
|
REG_GCLK_GENCTRL = GCLK_GENCTRL_DIVSEL | // Set to divide by 2^(GCLK_GENDIV_DIV(4) + 1)
|
|
GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
|
|
GCLK_GENCTRL_GENEN | // Enable GCLK2
|
|
GCLK_GENCTRL_SRC_OSCULP32K | // Set the clock source to the ultra low power oscillator (OSCULP32K)
|
|
GCLK_GENCTRL_ID(2); // Select GCLK2
|
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
|
|
|
// Feed GCLK2 to WDT (Watchdog Timer)
|
|
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable GCLK2 to the WDT
|
|
GCLK_CLKCTRL_GEN_GCLK2 | // Select GCLK2
|
|
GCLK_CLKCTRL_ID_WDT; // Feed the GCLK2 to the WDT
|
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
|
|
|
WDT->CONFIG.bit.PER = WDT_CONFIG_PER_7; // Set the WDT reset timeout to 4 seconds
|
|
while (WDT->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
|
REG_WDT_CTRL = WDT_CTRL_ENABLE; // Enable the WDT in normal mode
|
|
while (WDT->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
|
}
|
|
|
|
// Reset watchdog. MUST be called at least every 4 seconds after the
|
|
// first watchdog_init or SAMD will go into emergency procedures.
|
|
void MarlinHAL::watchdog_refresh() {
|
|
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
|
|
while (WDT->STATUS.bit.SYNCBUSY);
|
|
}
|
|
|
|
#endif
|
|
|
|
// ------------------------
|
|
// Types
|
|
// ------------------------
|
|
|
|
// ------------------------
|
|
// Private Variables
|
|
// ------------------------
|
|
|
|
// ------------------------
|
|
// Private functions
|
|
// ------------------------
|
|
|
|
void MarlinHAL::dma_init() {}
|
|
|
|
// ------------------------
|
|
// Public functions
|
|
// ------------------------
|
|
|
|
// HAL initialization task
|
|
void MarlinHAL::init() {
|
|
TERN_(DMA_IS_REQUIRED, dma_init());
|
|
#if ENABLED(SDSUPPORT)
|
|
#if HAS_SD_DETECT && SD_CONNECTION_IS(ONBOARD)
|
|
SET_INPUT_PULLUP(SD_DETECT_PIN);
|
|
#endif
|
|
OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up
|
|
#endif
|
|
}
|
|
|
|
#pragma push_macro("WDT")
|
|
#undef WDT // Required to be able to use '.bit.WDT'. Compiler wrongly replace struct field with WDT define
|
|
uint8_t MarlinHAL::get_reset_source() {
|
|
|
|
return 0;
|
|
}
|
|
#pragma pop_macro("WDT")
|
|
|
|
void MarlinHAL::reboot() { NVIC_SystemReset(); }
|
|
|
|
extern "C" {
|
|
void * _sbrk(int incr);
|
|
extern unsigned int __bss_end__; // end of bss section
|
|
}
|
|
|
|
// Return free memory between end of heap (or end bss) and whatever is current
|
|
int freeMemory() {
|
|
int free_memory, heap_end = (int)_sbrk(0);
|
|
return (int)&free_memory - (heap_end ?: (int)&__bss_end__);
|
|
}
|
|
|
|
// ------------------------
|
|
// ADC
|
|
// ------------------------
|
|
|
|
uint16_t MarlinHAL::adc_result;
|
|
|
|
void MarlinHAL::adc_init() {
|
|
/* thanks to https://www.eevblog.com/forum/microcontrollers/samd21g18-adc-with-resrdy-interrupts-only-reads-once-or-twice/ */
|
|
|
|
ADC->CTRLA.bit.ENABLE = false;
|
|
while(ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
// load chip corrections
|
|
uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
|
|
uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
|
|
linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
|
|
|
|
/* Wait for bus synchronization. */
|
|
while (ADC->STATUS.bit.SYNCBUSY) {};
|
|
|
|
ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
|
|
|
|
/* Wait for bus synchronization. */
|
|
while (ADC->STATUS.bit.SYNCBUSY) {};
|
|
|
|
ADC->CTRLA.bit.SWRST = true;
|
|
while(ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
|
|
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_32| ADC_AVGCTRL_ADJRES(4);;
|
|
|
|
|
|
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV128 |
|
|
ADC_CTRLB_RESSEL_16BIT |
|
|
ADC_CTRLB_FREERUN;
|
|
while(ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
ADC->SAMPCTRL.bit.SAMPLEN = 0x00;
|
|
while(ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
ADC->INPUTCTRL.reg = ADC_INPUTCTRL_INPUTSCAN(HAL_ADC_AIN_LEN) // scan (INPUTSCAN + NUM_EXTUDERS - 1) pins
|
|
| ADC_INPUTCTRL_GAIN_DIV2 |ADC_INPUTCTRL_MUXNEG_GND| HAL_ADC_AIN_START ; /* set to first AIN */
|
|
|
|
while(ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
ADC->INTENSET.reg |= ADC_INTENSET_RESRDY; // enable Result Ready ADC interrupts
|
|
while (ADC->STATUS.bit.SYNCBUSY);
|
|
|
|
NVIC_EnableIRQ(ADC_IRQn); // enable ADC interrupts
|
|
|
|
NVIC_SetPriority(ADC_IRQn, 3);
|
|
|
|
ADC->CTRLA.bit.ENABLE = true;
|
|
}
|
|
|
|
volatile uint32_t adc_results[HAL_ADC_AIN_NUM_SENSORS];
|
|
|
|
void ADC_Handler() {
|
|
while(ADC->STATUS.bit.SYNCBUSY == 1);
|
|
int pos = ADC->INPUTCTRL.bit.INPUTOFFSET;
|
|
|
|
adc_results[pos] = ADC->RESULT.reg; /* Read the value. */
|
|
ADC->INTFLAG.reg = ADC_INTENSET_RESRDY; /* Clear the data ready flag. */
|
|
}
|
|
|
|
void MarlinHAL::adc_start(const pin_t pin) {
|
|
/* due to the way INPUTOFFSET works, the last sensor is the first position in the array
|
|
and we want the ADC_handler interrupt to be as simple possible, so we do the calculation here.
|
|
*/
|
|
unsigned int pos = PIN_TO_INPUTCTRL(pin) - HAL_ADC_AIN_START + 1;
|
|
if (pos == HAL_ADC_AIN_NUM_SENSORS) pos = 0;
|
|
adc_result = adc_results[pos]; // 16-bit resolution
|
|
//adc_result = 0xFFFF;
|
|
}
|
|
|
|
#endif // __SAMD21__
|