diff options
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/adc-calibration.h | 64 | ||||
| -rw-r--r-- | arch/delay.h | 58 | ||||
| -rw-r--r-- | arch/mcu.h | 157 | ||||
| -rw-r--r-- | arch/random.h | 14 | ||||
| -rw-r--r-- | arch/voltage.h | 178 |
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 + |
