From 3d12b7066d27b591e0283e20ed066bc66e29fbe4 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Fri, 10 Nov 2023 21:34:40 -0700 Subject: 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... --- arch/attiny1616.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++ arch/attiny1616.h | 96 +++++++++++++++++++++++++++++++ arch/attiny1634.c | 125 ++++++++++++++++++++++++++++++++++++++++ arch/attiny1634.h | 98 ++++++++++++++++++++++++++++++++ arch/attiny85.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ arch/attiny85.h | 91 +++++++++++++++++++++++++++++ arch/mcu.c | 10 ++++ arch/mcu.h | 147 ++++------------------------------------------- 8 files changed, 745 insertions(+), 136 deletions(-) create mode 100644 arch/attiny1616.c create mode 100644 arch/attiny1616.h create mode 100644 arch/attiny1634.c create mode 100644 arch/attiny1634.h create mode 100644 arch/attiny85.c create mode 100644 arch/attiny85.h create mode 100644 arch/mcu.c (limited to 'arch') 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) {} +} + diff --git a/arch/attiny1616.h b/arch/attiny1616.h new file mode 100644 index 0000000..5989785 --- /dev/null +++ b/arch/attiny1616.h @@ -0,0 +1,96 @@ +// arch/attiny1616.h: attiny1616 support header +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// FIXME: remove this +#define AVRXMEGA3 + +////////// clock speed / delay stuff ////////// + +#define F_CPU 10000000UL +#define BOGOMIPS (F_CPU/4700) +#define DELAY_ZERO_TIME 1020 + +///// clock dividers +// this should work, but needs further validation +inline void clock_prescale_set(uint8_t n); + +typedef enum +{ + // Actual clock is 20 MHz, but assume that 10 MHz is the top speed and work from there + // TODO: measure PWM speed and power use at 1.25/2.5/5/10 MHz, to determine which speeds are optimal + clock_div_1 = (CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm), // 10 MHz + clock_div_2 = (CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm), // 5 MHz + clock_div_4 = (CLKCTRL_PDIV_8X_gc | CLKCTRL_PEN_bm), // 2.5 MHz + clock_div_8 = (CLKCTRL_PDIV_16X_gc | CLKCTRL_PEN_bm), // 1.25 MHz + clock_div_16 = (CLKCTRL_PDIV_32X_gc | CLKCTRL_PEN_bm), // 625 kHz + clock_div_32 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz, max without changing to the 32 kHz ULP + clock_div_64 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz + clock_div_128 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm), // 312 kHz + clock_div_256 = (CLKCTRL_PDIV_64X_gc | CLKCTRL_PEN_bm) // 312 kHz +} clock_div_t; + + +////////// ADC voltage / temperature ////////// + +#define hwdef_set_admux_therm mcu_set_admux_therm +inline void mcu_set_admux_therm(); + +#define hwdef_set_admux_voltage mcu_set_admux_voltage +inline void mcu_set_admux_voltage(); + +inline void mcu_adc_sleep_mode(); + +inline void mcu_adc_start_measurement(); + +inline void mcu_adc_on(); + +inline void mcu_adc_off(); + +#define ADC_vect ADC0_RESRDY_vect +inline void mcu_adc_vect_clear(); + +// read ADC differently for temperature and voltage +#define MCU_ADC_RESULT_PER_TYPE + +inline uint16_t mcu_adc_result_temp(); + +inline uint16_t mcu_adc_result_volts(); + +inline uint8_t mcu_adc_lsb(); + + +////////// WDT ////////// + +inline void mcu_wdt_active(); + +inline void mcu_wdt_standby(); + +inline void mcu_wdt_stop(); + +// *** Note for the AVRXMEGA3 (1-Series, eg 816 and 817), the WDT +// is not used for time-based interrupts. A new peripheral, the +// Periodic Interrupt Timer ("PIT") is used for this purpose. + +#define WDT_vect RTC_PIT_vect +inline void mcu_wdt_vect_clear(); + + +////////// PCINT - pin change interrupt (e-switch) ////////// + +// set these in hwdef +//#define SWITCH_PORT PINA +//#define SWITCH_VECT PCINT0_vect + +inline void mcu_switch_vect_clear(); + +inline void mcu_pcint_on(); + +inline void mcu_pcint_off(); + + +////////// misc ////////// + +void reboot(); + diff --git a/arch/attiny1634.c b/arch/attiny1634.c new file mode 100644 index 0000000..d4b3767 --- /dev/null +++ b/arch/attiny1634.c @@ -0,0 +1,125 @@ +// arch/attiny1634.c: attiny85 support functions +// Copyright (C) 2014-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "arch/attiny1634.h" + +////////// clock speed / delay stuff ////////// + +///// clock dividers +// make it a NOP for now +// FIXME +//#define clock_prescale_set(x) ((void)0) +//#define clock_prescale_set(n) {cli(); CCP = 0xD8; CLKPR = n; sei();} +//#define clock_prescale_set(n) {cli(); CCP = 0xD8; CLKPR = n; sei();} +inline void clock_prescale_set(uint8_t n) { + cli(); + CCP = 0xD8; + CLKPR = n; + sei(); +} + +////////// default hw_setup() ////////// + + +////////// ADC voltage / temperature ////////// + +inline void mcu_set_admux_therm() { + ADMUX = ADMUX_THERM; +} + +inline void mcu_set_admux_voltage() { + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC; + #endif +} + +inline void mcu_adc_sleep_mode() { + set_sleep_mode(SLEEP_MODE_ADC); +} + +inline void mcu_adc_start_measurement() { + ADCSRA |= (1 << ADSC) | (1 << ADIE); +} + +inline void mcu_adc_on() { + hwdef_set_admux_voltage(); + #ifdef USE_VOLTAGE_DIVIDER + // disable digital input on divider pin to reduce power consumption + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); + #else + // disable digital input on VCC pin to reduce power consumption + //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin + #endif + //ACSRA |= (1 << ACD); // turn off analog comparator to save power + ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; +} + +inline void mcu_adc_off() { + ADCSRA &= ~(1<> 6) + (ADCH << 2); } + + +////////// WDT ////////// + +inline void mcu_wdt_active() { + wdt_reset(); // Reset the WDT + WDTCSR = (1<= 1 + DDRB |= (1 << PWM1_PIN); + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + #if (PWM1_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + // tint ramping needs second channel enabled, + // despite PWM_CHANNELS being only 1 + #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING) + DDRB |= (1 << PWM2_PIN); + #if (PWM2_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 3 + DDRB |= (1 << PWM3_PIN); + #if (PWM3_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 4 + // 4th PWM channel is ... not actually supported in hardware :( + DDRB |= (1 << PWM4_PIN); + //OCR1C = 255; // Set ceiling value to maximum + TCCR1 = 1<> 6) + (ADCH << 2); } + + +////////// WDT ////////// + +inline void mcu_wdt_active() { + // interrupt every 16ms + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1< #include +#include +#include +#include +#include -/******************** I/O pin and register layout ************************/ -#ifdef HWDEFFILE -#include "fsm/tk.h" -#include incfile(HWDEFFILE) -#endif - -#if 0 // placeholder - -#elif defined(NANJG_LAYOUT) -#include "hwdef-nanjg.h" -#elif defined(FET_7135_LAYOUT) -#include "hwdef-FET_7135.h" +// for consistency, ROM_SIZE + EEPROM_SIZE +#define ROM_SIZE PROGMEM_SIZE -#elif defined(TRIPLEDOWN_LAYOUT) -#include "hwdef-Tripledown.h" - -#elif defined(FERRERO_ROCHER_LAYOUT) -#include "hwdef-Ferrero_Rocher.h" - -#endif // no more recognized driver types - -#ifndef LAYOUT_DEFINED -#error Hey, you need to define an I/O pin layout. -#endif - -#if (ATTINY==13) - // no changes needed -#elif (ATTINY==25) || (ATTINY==45) || (ATTINY==85) - // use clock_prescale_set(n) instead; it's safer - //#define CLOCK_DIVIDER_SET(n) {CLKPR = 1<