diff options
| author | Selene ToyKeeper | 2023-11-10 21:34:40 -0700 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-11-10 21:34:40 -0700 |
| commit | 3d12b7066d27b591e0283e20ed066bc66e29fbe4 (patch) | |
| tree | 08a0ed41a4b0baa7f7f5ea4eed6ee10ac250250c /arch/attiny1616.c | |
| parent | added md5sum to build-all.sh output per target (diff) | |
| download | anduril-3d12b7066d27b591e0283e20ed066bc66e29fbe4.tar.gz anduril-3d12b7066d27b591e0283e20ed066bc66e29fbe4.tar.bz2 anduril-3d12b7066d27b591e0283e20ed066bc66e29fbe4.zip | |
refactor checkpoint: splitting MCU-specific code into arch/$MCU.[ch]
Phew, that's a lot of changes! And there's still a lot more to do...
Diffstat (limited to 'arch/attiny1616.c')
| -rw-r--r-- | arch/attiny1616.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/arch/attiny1616.c b/arch/attiny1616.c new file mode 100644 index 0000000..3b170bb --- /dev/null +++ b/arch/attiny1616.c @@ -0,0 +1,147 @@ +// arch/attiny1616.c: attiny1616 support functions +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/attiny1616.h" + +////////// clock speed / delay stuff ////////// + +///// clock dividers +// this should work, but needs further validation +inline void clock_prescale_set(uint8_t n) { + cli(); + CCP = CCP_IOREG_gc; // temporarily disable clock change protection + CLKCTRL.MCLKCTRLB = n; // Set the prescaler + while (CLKCTRL.MCLKSTATUS & CLKCTRL_SOSC_bm) {} // wait for clock change to finish + sei(); +} + +////////// ADC voltage / temperature ////////// + +inline void mcu_set_admux_therm() { + ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV64_gc + | ADC_REFSEL_INTREF_gc; // Internal ADC reference +} + +inline void mcu_set_admux_voltage() { + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / ADC input pin + // verify that this is correct!!! untested + ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV64_gc + | ADC_REFSEL_INTREF_gc; // Use internal ADC reference + #else // VCC / 1.1V reference + ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference + ADC0.CTRLC = ADC_SAMPCAP_bm + | ADC_PRESC_DIV64_gc + | ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference + #endif +} + +inline void mcu_adc_sleep_mode() { + set_sleep_mode(SLEEP_MODE_STANDBY); +} + +inline void mcu_adc_start_measurement() { + ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt + ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions +} + +inline void mcu_adc_on() { + VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V + // Enabled, free-running (aka, auto-retrigger), run in standby + ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; + // set a INITDLY value because the AVR manual says so (section 30.3.5) + // (delay 1st reading until Vref is stable) + ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; + hwdef_set_admux_voltage(); +} + +inline void mcu_adc_off() { + ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC +} + +inline void mcu_adc_vect_clear() { + ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt +} + +inline uint16_t mcu_adc_result_temp() { + // Use the factory calibrated values in SIGROW.TEMPSENSE0 and + // SIGROW.TEMPSENSE1 to calculate a temperature reading in Kelvin, then + // left-align it. + int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row + uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row + uint32_t temp = ADC0.RES - sigrow_offset; + temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit) + temp += 0x80; // Add 1/2 to get correct rounding on division below + //temp >>= 8; // Divide result to get Kelvin + //return temp << 6; // left align it + return temp >> 2; // left-aligned uint16_t +} + +inline uint16_t mcu_adc_result_volts() { + // FIXME: set up ADC to use left-aligned values natively + return ADC0.RES << 6; // voltage, force left-alignment +} + +inline uint8_t mcu_adc_lsb() { + //return (ADCL >> 6) + (ADCH << 2); + return ADC0.RESL; // right aligned, not left... so should be equivalent? +} + + +////////// WDT ////////// + +inline void mcu_wdt_active() { + RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt + while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm; // Period = 16ms, enable the PI Timer +} + +inline void mcu_wdt_standby() { + RTC.PITINTCTRL = RTC_PI_bm; // enable the Periodic Interrupt + while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm; // Set period, enable the PI Timer +} + +inline void mcu_wdt_stop() { + while (RTC.PITSTATUS > 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = 0; // Disable the PI Timer +} + +inline void mcu_wdt_vect_clear() { + RTC.PITINTFLAGS = RTC_PI_bm; // clear the PIT interrupt flag +} + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +inline void mcu_switch_vect_clear() { + // Write a '1' to clear the interrupt flag + SWITCH_INTFLG |= (1 << SWITCH_PIN); +} + +inline void mcu_pcint_on() { + SWITCH_ISC_REG |= PORT_ISC_BOTHEDGES_gc; +} + +inline void mcu_pcint_off() { + SWITCH_ISC_REG &= ~(PORT_ISC_gm); +} + + +////////// misc ////////// + +void reboot() { + // put the WDT in hard reset mode, then trigger it + cli(); + CCP = CCP_IOREG_gc; // temporarily disable change protection + WDT.CTRLA = WDT_PERIOD_8CLK_gc; // Enable, timeout 8ms + sei(); + wdt_reset(); + while (1) {} +} + |
