aboutsummaryrefslogtreecommitdiff
path: root/arch/attiny1634.c
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-11-30 09:19:45 -0700
committerSelene ToyKeeper2023-11-30 09:19:45 -0700
commitf745e12c3bc48d8fe544893871191086cf3cccc9 (patch)
tree0e7f6c2c5f362719ac4efad9d5c2365f3ed3c159 /arch/attiny1634.c
parentadded md5sum to build-all.sh output per target (diff)
parenteliminated direct CCP register access from arch/attiny1616 (diff)
downloadanduril-f745e12c3bc48d8fe544893871191086cf3cccc9.tar.gz
anduril-f745e12c3bc48d8fe544893871191086cf3cccc9.tar.bz2
anduril-f745e12c3bc48d8fe544893871191086cf3cccc9.zip
Merge branch 'avr32dd20-devkit' into trunk
Added support for AVR DD MCUs, particularly avr32dd20. Also did a bunch of refactoring for how MCU support works, cleaned up the ADC code, switched to consistent internal formats for voltage and temperature, fixed the FW3X, and some other little things. * avr32dd20-devkit: (28 commits) eliminated direct CCP register access from arch/attiny1616 made the avr32dd20 flashing script more universal added a build target for FW3X with manually-fixed RGB aux wiring prevent future issues like the FW3X had fixed FW3X thermal regulation fixed incorrect temperature history for a few seconds after waking fsm/adc: removed dead code FW3X: fixed external temperature sensor FW3X: multiple upgrades... fw3x: fixed swapped red+blue, fixed battery measurements, added police color strobe fixed ADC on sp10-pro fixed ADC on attiny85 and related builds fixed ADC on attiny1634 and related builds more ADC / DAC / MCU progress... avr32dd20-devkit: make the defaults a bit more dev friendly (realtime voltage colors, and no simple UI by default) ADC voltage: battcheck 3 digits, fixed t1616, switched back to 8-bit internal volt unit got ADC voltage+temp working on avrdd... but broke all other builds/MCUs 1.55V AA battery should not show as "white" voltage color, only purple started refactoring fsm/adc.*, but need a checkpoint before continuing added dac-scale.py: short script to calculate avrdd DAC+Vref values from level_calc.py ramp data ...
Diffstat (limited to 'arch/attiny1634.c')
-rw-r--r--arch/attiny1634.c214
1 files changed, 214 insertions, 0 deletions
diff --git a/arch/attiny1634.c b/arch/attiny1634.c
new file mode 100644
index 0000000..e29d1c3
--- /dev/null
+++ b/arch/attiny1634.c
@@ -0,0 +1,214 @@
+// 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 //////////
+
+inline void mcu_clock_speed() {
+ // TODO?
+ // (or not; clock speed is set by the fuses)
+ // attiny1634 datasheet 6.5
+ // CLKSR = [OSCRDY, CSTR, CKOUT_IO, SUT, CKSEL3/2/1/0]
+ // default 8MHz calibrated internal clock: SUT=0, CKSEL=0b0010
+ #if 0
+ cli();
+ CCP = 0xD8;
+ CLKSR = 0b01000010;
+ CCP = 0xD8;
+ CLKPR = 0; // CLK / 1 = full speed, 8 MHz
+ sei();
+ #endif
+}
+
+///// clock dividers
+inline void clock_prescale_set(uint8_t n) {
+ cli();
+ // _PROTECTED_WRITE(CLKPR, n);
+ CCP = 0xD8;
+ CLKPR = n;
+ sei();
+}
+
+////////// default hw_setup() //////////
+
+
+////////// ADC voltage / temperature //////////
+
+inline void mcu_set_admux_therm() {
+ // put the ADC in temperature mode
+ // DS table 19-3, 19-4, internal sensor / 1.1V ref
+ // [refs1, refs0, refen, adc0en, mux3, mux2, mux1, mux0]
+ // refs=0b10 : internal 1.1V ref
+ // mux=0b1110 : internal temperature sensor
+ //#define ADMUX_THERM 0b10001110
+ ADMUX = ADMUX_THERM;
+ // other stuff is already set, so no need to re-set it
+}
+
+inline void mcu_set_admux_voltage() {
+ // put the ADC in battery voltage measurement mode
+ // TODO: avr datasheet references
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER;
+ // disable digital input on divider pin to reduce power consumption
+ // TODO: this should be in hwdef init, not here
+ VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC);
+ #else // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC;
+ // 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: [VDEN, VDPD, -, -, ADLAR, ADTS2, ADTS1, ADTS0]
+ ADCSRB = (1 << ADLAR); // left-adjust, free-running
+ //ADCSRB = 0; // right-adjust, free-running
+}
+
+inline void mcu_adc_sleep_mode() {
+ set_sleep_mode(SLEEP_MODE_ADC);
+}
+
+inline void mcu_adc_start_measurement() {
+ // [ADEN, ADSC, ADATE, adif, ADIE, ADPS2, ADPS1, ADPS0]
+ ADCSRA = (1 << ADEN) // enable
+ | (1 << ADSC) // start
+ | (1 << ADATE) // auto-retrigger
+ | (1 << ADIE) // interrupt enable
+ | ADC_PRSCL; // prescale
+}
+
+inline void mcu_adc_off() {
+ ADCSRA &= ~(1<<ADEN); //ADC off
+}
+
+// left-adjusted mode:
+inline uint16_t mcu_adc_result() { return ADC; }
+//inline uint16_t mcu_adc_result() { return (uint16_t)(ADCL | (ADCH << 8)); }
+// right-adjusted mode:
+/*
+inline uint16_t mcu_adc_result() {
+ uint16_t result = (ADCL | (ADCH << 8)) << 6;
+ return result;
+}
+*/
+
+inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement) {
+ // In : 65535 * 1.1 / Vbat
+ // Out: uint8_t: Vbat * 40
+ // 1.1 = ADC Vref
+ #if 0
+ // 1024 = how much ADC resolution we're using (10 bits)
+ // (12 bits available, but it costs an extra 84 bytes of ROM to calculate)
+ uint8_t vbat40 = (uint16_t)(40 * 1.1 * 1024) / (measurement >> 6);
+ #else
+ // ... spend the extra 84 bytes of ROM for better precision
+ // 4096 = how much ADC resolution we're using (12 bits)
+ uint8_t vbat40 = (uint32_t)(40 * 1.1 * 4096) / (measurement >> 4);
+ #endif
+ return vbat40;
+}
+
+
+#ifdef USE_VOLTAGE_DIVIDER
+inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement) {
+ // In : 4095 * Vdiv / 1.1V
+ // Out: uint8_t: Vbat * 40
+ // Vdiv = Vbat / 4.3 (typically)
+ // 1.1 = ADC Vref
+ const uint16_t adc_per_volt =
+ (((uint16_t)ADC_44 << 4) - ((uint16_t)ADC_22 << 4))
+ / (4 * (44-22));
+ uint8_t result = measurement / adc_per_volt;
+ return result;
+}
+#endif
+
+inline uint16_t mcu_temp_raw2cooked(uint16_t measurement) {
+ // convert raw ADC values to calibrated temperature
+ // In: ADC raw temperature (16-bit, or left-aligned)
+ // Out: Kelvin << 6
+ // Precision: 1/64th Kelvin (but noisy)
+ // attiny1634 datasheet section 19.12
+ // nothing to do; input value is already "cooked"
+ return measurement;
+}
+
+inline uint8_t mcu_adc_lsb() {
+ // left-adjusted mode:
+ return (ADCL >> 6) + (ADCH << 2);
+ // right-adjusted mode:
+ // datasheet 19.13.3.2:
+ // "When ADCL is read, the ADC Data Register is not updated
+ // until ADCH is read. ... ADCL must be read first, then ADCH."
+ // So... gotta read it even if not needed?
+ // (the value doesn't matter here, the lower bits are only used
+ // for generating some random seed data)
+ //return ADCL + ADCH;
+}
+
+
+////////// WDT //////////
+
+inline void mcu_wdt_active() {
+ wdt_reset(); // Reset the WDT
+ WDTCSR = (1<<WDIE); // Enable interrupt every 16ms
+}
+
+inline void mcu_wdt_standby() {
+ wdt_reset(); // Reset the WDT
+ WDTCSR = (1<<WDIE) | STANDBY_TICK_SPEED;
+}
+
+inline void mcu_wdt_stop() {
+ cli(); // needed because CCP, below
+ wdt_reset(); // Reset the WDT
+ MCUSR &= ~(1<<WDRF); // clear watchdog reset flag
+ // _PROTECTED_WRITE(WDTCSR, 0);
+ CCP = 0xD8; // enable config changes
+ WDTCSR = 0; // disable and clear all WDT settings
+ sei();
+}
+
+
+////////// PCINT - pin change interrupt (e-switch) //////////
+
+inline void mcu_pcint_on() {
+ // enable pin change interrupt
+ #ifdef SWITCH2_PCIE
+ GIMSK |= ((1 << SWITCH_PCIE) | (1 << SWITCH2_PCIE));
+ #else
+ GIMSK |= (1 << SWITCH_PCIE);
+ #endif
+}
+
+inline void mcu_pcint_off() {
+ // disable all pin-change interrupts
+ GIMSK &= ~(1 << SWITCH_PCIE);
+}
+
+
+////////// misc //////////
+
+void reboot() {
+ // put the WDT in hard reset mode, then trigger it
+ cli();
+ // _PROTECTED_WRITE(WDTCSR, 0b10001000);
+ // allow protected configuration changes for next 4 clock cycles
+ CCP = 0xD8; // magic number
+ // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000)
+ // (DS section 8.5.2 and table 8-4)
+ WDTCSR = 0b10001000;
+ sei();
+ wdt_reset();
+ while (1) {}
+}
+
+inline void prevent_reboot_loop() {
+ // prevent WDT from rebooting MCU again
+ MCUSR &= ~(1<<WDRF); // reset status flag
+ wdt_disable(); // from avr/wdt.h
+}
+