aboutsummaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/adc-calibration.h64
-rw-r--r--arch/delay.h58
-rw-r--r--arch/mcu.h157
-rw-r--r--arch/random.h14
-rw-r--r--arch/voltage.h178
5 files changed, 471 insertions, 0 deletions
diff --git a/arch/adc-calibration.h b/arch/adc-calibration.h
new file mode 100644
index 0000000..f0b05ab
--- /dev/null
+++ b/arch/adc-calibration.h
@@ -0,0 +1,64 @@
+// tk-calibration.h: Attiny calibration header.
+// Copyright (C) 2015-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// This allows using a single set of hardcoded values across multiple projects.
+
+/********************** Voltage ADC calibration **************************/
+// These values were measured using RMM's FET+7135.
+// See battcheck/readings.txt for reference values.
+// the ADC values we expect for specific voltages
+#define ADC_44 194
+#define ADC_43 189
+#define ADC_42 184
+#define ADC_41 178
+#define ADC_40 173
+#define ADC_39 168
+#define ADC_38 163
+#define ADC_37 158
+#define ADC_36 152
+#define ADC_35 147
+#define ADC_34 142
+#define ADC_33 137
+#define ADC_32 131
+#define ADC_31 126
+#define ADC_30 121
+#define ADC_29 116
+#define ADC_28 111
+#define ADC_27 105
+#define ADC_26 100
+#define ADC_25 95
+#define ADC_24 90
+#define ADC_23 84
+#define ADC_22 79
+#define ADC_21 74
+#define ADC_20 69
+
+#define ADC_100p ADC_42 // the ADC value for 100% full (resting)
+#define ADC_75p ADC_40 // the ADC value for 75% full (resting)
+#define ADC_50p ADC_38 // the ADC value for 50% full (resting)
+#define ADC_25p ADC_35 // the ADC value for 25% full (resting)
+#define ADC_0p ADC_30 // the ADC value for 0% full (resting)
+#define ADC_LOW ADC_30 // When do we start ramping down
+#define ADC_CRIT ADC_27 // When do we shut the light off
+
+
+/********************** Offtime capacitor calibration ********************/
+// Values are between 1 and 255, and can be measured with offtime-cap.c
+// See battcheck/otc-readings.txt for reference values.
+// These #defines are the edge boundaries, not the center of the target.
+#ifdef OFFTIM3
+// The OTC value 0.5s after being disconnected from power
+// (anything higher than this is a "short press")
+#define CAP_SHORT 190
+// The OTC value 1.5s after being disconnected from power
+// Between CAP_MED and CAP_SHORT is a "medium press"
+#define CAP_MED 94
+// Below CAP_MED is a long press
+#else
+// The OTC value 1.0s after being disconnected from power
+// Anything higher than this is a short press, lower is a long press
+#define CAP_SHORT 115
+#endif
+
diff --git a/arch/delay.h b/arch/delay.h
new file mode 100644
index 0000000..502e6ab
--- /dev/null
+++ b/arch/delay.h
@@ -0,0 +1,58 @@
+// tk-delay.h: Smaller, more flexible _delay_ms() functions.
+// Copyright (C) 2015-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#ifdef OWN_DELAY
+#include "tk-attiny.h"
+#include <util/delay_basic.h>
+#ifdef USE_DELAY_MS
+// Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
+#define delay_ms _delay_ms
+void _delay_ms(uint16_t n)
+{
+ // TODO: make this take tenths of a ms instead of ms,
+ // for more precise timing?
+ //#ifdef USE_FINE_DELAY
+ //if (n==0) { _delay_loop_2(BOGOMIPS/3); }
+ //else {
+ // while(n-- > 0) _delay_loop_2(BOGOMIPS);
+ //}
+ //#else
+ while(n-- > 0) _delay_loop_2(BOGOMIPS);
+ //#endif
+}
+#endif
+#if defined(USE_FINE_DELAY) || defined(USE_DELAY_ZERO)
+#define delay_zero _delay_zero
+void _delay_zero() {
+ //_delay_loop_2((BOGOMIPS/3) & 0xff00);
+ _delay_loop_2(DELAY_ZERO_TIME);
+}
+#endif
+#ifdef USE_DELAY_4MS
+#ifndef delay_4ms
+#define delay_4ms _delay_4ms
+void _delay_4ms(uint8_t n) // because it saves a bit of ROM space to do it this way
+{
+ while(n-- > 0) _delay_loop_2(BOGOMIPS*4);
+}
+#endif
+#endif
+#ifdef USE_DELAY_S
+#define delay_s _delay_s
+void _delay_s() // because it saves a bit of ROM space to do it this way
+{
+ #ifdef USE_DELAY_4MS
+ _delay_4ms(250);
+ #else
+ #ifdef USE_DELAY_MS
+ _delay_ms(1000);
+ #endif
+ #endif
+}
+#endif
+#else
+#include <util/delay.h>
+#endif
+
diff --git a/arch/mcu.h b/arch/mcu.h
new file mode 100644
index 0000000..9532ed5
--- /dev/null
+++ b/arch/mcu.h
@@ -0,0 +1,157 @@
+// tk-attiny.h: Attiny portability header.
+// Copyright (C) 2014-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// This helps abstract away the differences between various attiny MCUs.
+
+// Choose your MCU here, or in the main .c file, or in the build script
+//#define ATTINY 13
+//#define ATTINY 25
+
+/******************** hardware-specific values **************************/
+#if (ATTINY == 13)
+ #define F_CPU 4800000UL
+ //#define EEPSIZE 64
+ #define V_REF REFS0
+ #define BOGOMIPS 950
+ #define ADMUX_VCC 0b00001100
+ #define DELAY_ZERO_TIME 252
+ #define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
+#elif (ATTINY == 25)
+ // TODO: Use 6.4 MHz instead of 8 MHz?
+ #define F_CPU 8000000UL
+ //#define EEPSIZE 128
+ #define V_REF REFS1
+ #define BOGOMIPS (F_CPU/4000)
+ #define ADMUX_VCC 0b00001100
+ #define ADMUX_THERM 0b10001111
+ #define DELAY_ZERO_TIME 1020
+ #define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
+#elif (ATTINY == 85)
+ // TODO: Use 6.4 MHz instead of 8 MHz?
+ #define F_CPU 8000000UL
+ //#define EEPSIZE 512
+ #define V_REF REFS1
+ #define BOGOMIPS (F_CPU/4000)
+ // (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL)
+ #define ADMUX_VCC 0b00001100
+ // (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL)
+ #define ADMUX_THERM 0b10001111
+ #define DELAY_ZERO_TIME 1020
+ #define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
+#elif (ATTINY == 1634)
+ #define F_CPU 8000000UL
+ #define V_REF REFS1
+ #define BOGOMIPS (F_CPU/4000)
+ // DS table 19-3, 19-4, 1.1V ref / VCC
+ #define ADMUX_VCC 0b00001101
+ // (1 << V_REF) | (THERM_CHANNEL)
+ // DS table 19-3, 19-4, internal sensor / 1.1V ref
+ #define ADMUX_THERM 0b10001110
+ #define DELAY_ZERO_TIME 1020
+ //#define SWITCH_PORT PINA // set this in hwdef
+ //#define VOLTAGE_ADC_DIDR DIDR0 // set this in hwdef
+#elif (ATTINY == 412) || (ATTINY == 416) || (ATTINY == 417) || (ATTINY == 816) || (ATTINY == 817) || (ATTINY == 1616) || (ATTINY == 1617) || (ATTINY == 3216) || (ATTINY == 3217)
+ #define AVRXMEGA3
+ #define F_CPU 10000000UL
+ #define BOGOMIPS (F_CPU/4700)
+ #define EEPSIZE 128
+ #define DELAY_ZERO_TIME 1020
+#else
+ #error Hey, you need to define ATTINY.
+#endif
+
+// auto-detect eeprom size from avr-libc headers
+#ifndef EEPSIZE
+#ifdef E2SIZE
+#define EEPSIZE E2SIZE
+#else
+#define EEPSIZE (E2END+1)
+#endif
+#endif
+
+
+#include <avr/interrupt.h>
+
+/******************** I/O pin and register layout ************************/
+#ifdef HWDEFFILE
+#include "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"
+
+#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<<CLKPCE; CLKPR = n;}
+#elif (ATTINY==1634)
+ // 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();}
+ typedef enum
+ {
+ clock_div_1 = 0,
+ clock_div_2 = 1,
+ clock_div_4 = 2,
+ clock_div_8 = 3,
+ clock_div_16 = 4,
+ clock_div_32 = 5,
+ clock_div_64 = 6,
+ clock_div_128 = 7,
+ clock_div_256 = 8
+ } clock_div_t;
+
+#elif defined(AVRXMEGA3) // ATTINY816, 817, etc
+ // 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();
+ }
+ 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;
+#else
+#error Unable to define MCU macros.
+#endif
+
diff --git a/arch/random.h b/arch/random.h
new file mode 100644
index 0000000..ad65660
--- /dev/null
+++ b/arch/random.h
@@ -0,0 +1,14 @@
+// tk-random.h: Smaller pseudo-random function(s).
+// Copyright (C) 2015-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+uint8_t pgm_rand() {
+ static uint16_t offset = 255;
+ // loop through ROM space, but avoid the first 256 bytes
+ // because the beginning tends to have a big ramp which
+ // doesn't look very random at all
+ offset = ((offset + 1) & 0x3ff) | 0x0100;
+ return pgm_read_byte(offset);
+}
+
diff --git a/arch/voltage.h b/arch/voltage.h
new file mode 100644
index 0000000..36ce9c8
--- /dev/null
+++ b/arch/voltage.h
@@ -0,0 +1,178 @@
+// tk-voltage.h: Voltage / battcheck functions.
+// Copyright (C) 2015-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "tk-attiny.h"
+#include "tk-calibration.h"
+
+#if defined(TEMPERATURE_MON) || defined(THERMAL_REGULATION)
+#ifdef TEMP_10bit
+#define NEED_ADC_10bit
+#define get_temperature read_adc_10bit
+#else
+#define get_temperature read_adc_8bit
+#endif
+
+inline void ADC_on_temperature() {
+ // TODO: (?) enable ADC Noise Reduction Mode, Section 17.7 on page 128
+ // (apparently can only read while the CPU is in idle mode though)
+ // select ADC4 by writing 0b00001111 to ADMUX
+ // 1.1v reference, left-adjust, ADC4
+ #ifdef TEMP_10bit
+ ADMUX = (1 << V_REF) | (0 << ADLAR) | TEMP_CHANNEL;
+ #else
+ ADMUX = (1 << V_REF) | (1 << ADLAR) | TEMP_CHANNEL;
+ #endif
+ // disable digital input on ADC pin to reduce power consumption
+ //DIDR0 |= (1 << TEMP_DIDR);
+ // enable, start, prescale
+ ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
+}
+#endif // TEMPERATURE_MON
+
+#ifdef VOLTAGE_MON
+#define NEED_ADC_8bit
+inline void ADC_on() {
+ // disable digital input on ADC pin to reduce power consumption
+ DIDR0 |= (1 << ADC_DIDR);
+ // 1.1v reference, left-adjust, ADC1/PB2
+ ADMUX = (1 << V_REF) | (1 << ADLAR) | ADC_CHANNEL;
+ // enable, start, prescale
+ ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;
+}
+
+#define get_voltage read_adc_8bit
+#else
+inline void ADC_off() {
+ ADCSRA &= ~(1<<7); //ADC off
+}
+#endif
+
+#ifdef NEED_ADC_8bit
+uint8_t read_adc_8bit() {
+ // Start conversion
+ ADCSRA |= (1 << ADSC);
+ // Wait for completion
+ while (ADCSRA & (1 << ADSC));
+ // Send back the result
+ return ADCH;
+}
+#endif
+
+#ifdef NEED_ADC_10bit
+uint16_t read_adc_10bit() {
+ // Start conversion
+ ADCSRA |= (1 << ADSC);
+ // Wait for completion
+ while (ADCSRA & (1 << ADSC));
+ // Send back the result
+ //return (ADCH<<8) | (ADCL); // ADLAR=0
+ //return (ADCH<<2) | (ADCL>>6); // ADLAR=1
+ ADCSRA |= 0x10; // clear ADIF flag, else only first reading works
+ return ADC; // ADLAR=0
+}
+#endif
+
+#ifdef USE_BATTCHECK
+#ifdef BATTCHECK_4bars
+PROGMEM const uint8_t voltage_blinks[] = {
+ // 0 blinks for less than 1%
+ ADC_0p, // 1 blink for 1%-25%
+ ADC_25p, // 2 blinks for 25%-50%
+ ADC_50p, // 3 blinks for 50%-75%
+ ADC_75p, // 4 blinks for 75%-100%
+ ADC_100p, // 5 blinks for >100%
+ 255, // Ceiling, don't remove (6 blinks means "error")
+};
+#endif // BATTCHECK_4bars
+#ifdef BATTCHECK_8bars
+PROGMEM const uint8_t voltage_blinks[] = {
+ // 0 blinks for less than 1%
+ ADC_30, // 1 blink for 1%-12.5%
+ ADC_33, // 2 blinks for 12.5%-25%
+ ADC_35, // 3 blinks for 25%-37.5%
+ ADC_37, // 4 blinks for 37.5%-50%
+ ADC_38, // 5 blinks for 50%-62.5%
+ ADC_39, // 6 blinks for 62.5%-75%
+ ADC_40, // 7 blinks for 75%-87.5%
+ ADC_41, // 8 blinks for 87.5%-100%
+ ADC_42, // 9 blinks for >100%
+ 255, // Ceiling, don't remove (10 blinks means "error")
+};
+#endif // BATTCHECK_8bars
+#ifdef BATTCHECK_VpT
+/*
+PROGMEM const uint8_t v_whole_blinks[] = {
+ // 0 blinks for (shouldn't happen)
+ 0, // 1 blink for (shouldn't happen)
+ ADC_20, // 2 blinks for 2V
+ ADC_30, // 3 blinks for 3V
+ ADC_40, // 4 blinks for 4V
+ 255, // Ceiling, don't remove
+};
+PROGMEM const uint8_t v_tenth_blinks[] = {
+ // 0 blinks for less than 1%
+ ADC_30,
+ ADC_33,
+ ADC_35,
+ ADC_37,
+ ADC_38,
+ ADC_39,
+ ADC_40,
+ ADC_41,
+ ADC_42,
+ 255, // Ceiling, don't remove
+};
+*/
+PROGMEM const uint8_t voltage_blinks[] = {
+ // 0 blinks for (shouldn't happen)
+ ADC_25,(2<<5)+5,
+ ADC_26,(2<<5)+6,
+ ADC_27,(2<<5)+7,
+ ADC_28,(2<<5)+8,
+ ADC_29,(2<<5)+9,
+ ADC_30,(3<<5)+0,
+ ADC_31,(3<<5)+1,
+ ADC_32,(3<<5)+2,
+ ADC_33,(3<<5)+3,
+ ADC_34,(3<<5)+4,
+ ADC_35,(3<<5)+5,
+ ADC_36,(3<<5)+6,
+ ADC_37,(3<<5)+7,
+ ADC_38,(3<<5)+8,
+ ADC_39,(3<<5)+9,
+ ADC_40,(4<<5)+0,
+ ADC_41,(4<<5)+1,
+ ADC_42,(4<<5)+2,
+ ADC_43,(4<<5)+3,
+ ADC_44,(4<<5)+4,
+ 255, (1<<5)+1, // Ceiling, don't remove
+};
+inline uint8_t battcheck() {
+ // Return an composite int, number of "blinks", for approximate battery charge
+ // Uses the table above for return values
+ // Return value is 3 bits of whole volts and 5 bits of tenths-of-a-volt
+ uint8_t i, voltage;
+ voltage = get_voltage();
+ // figure out how many times to blink
+ for (i=0;
+ voltage > pgm_read_byte(voltage_blinks + i);
+ i += 2) {}
+ return pgm_read_byte(voltage_blinks + i + 1);
+}
+#else // #ifdef BATTCHECK_VpT
+inline uint8_t battcheck() {
+ // Return an int, number of "blinks", for approximate battery charge
+ // Uses the table above for return values
+ uint8_t i, voltage;
+ voltage = get_voltage();
+ // figure out how many times to blink
+ for (i=0;
+ voltage > pgm_read_byte(voltage_blinks + i);
+ i ++) {}
+ return i;
+}
+#endif // BATTCHECK_VpT
+#endif
+
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.