diff options
| author | Selene ToyKeeper | 2023-11-27 07:31:56 -0700 |
|---|---|---|
| committer | Selene ToyKeeper | 2023-11-27 07:31:56 -0700 |
| commit | 1b6f40863b4f08365e39236496d687ab7806ed11 (patch) | |
| tree | af573a54cfdf01d3398ce78799ba0b019f1cb21d /arch | |
| parent | more ADC / DAC / MCU progress... (diff) | |
| download | anduril-1b6f40863b4f08365e39236496d687ab7806ed11.tar.gz anduril-1b6f40863b4f08365e39236496d687ab7806ed11.tar.bz2 anduril-1b6f40863b4f08365e39236496d687ab7806ed11.zip | |
fixed ADC on attiny1634 and related builds
Diffstat (limited to 'arch')
| -rw-r--r-- | arch/attiny1634.c | 129 | ||||
| -rw-r--r-- | arch/attiny1634.h | 40 |
2 files changed, 132 insertions, 37 deletions
diff --git a/arch/attiny1634.c b/arch/attiny1634.c index 0737a81..e29d1c3 100644 --- a/arch/attiny1634.c +++ b/arch/attiny1634.c @@ -7,14 +7,26 @@ ////////// 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 -// 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(); + // _PROTECTED_WRITE(CLKPR, n); CCP = 0xD8; CLKPR = n; sei(); @@ -26,15 +38,33 @@ inline void clock_prescale_set(uint8_t n) { ////////// 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() { @@ -42,31 +72,82 @@ inline void mcu_adc_sleep_mode() { } 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; + // [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; +} -inline uint8_t mcu_adc_lsb() { return (ADCL >> 6) + (ADCH << 2); } + +#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 ////////// @@ -85,6 +166,7 @@ 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(); @@ -113,6 +195,7 @@ inline void mcu_pcint_off() { 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) @@ -126,6 +209,6 @@ void reboot() { inline void prevent_reboot_loop() { // prevent WDT from rebooting MCU again MCUSR &= ~(1<<WDRF); // reset status flag - wdt_disable(); + wdt_disable(); // from avr/wdt.h } diff --git a/arch/attiny1634.h b/arch/attiny1634.h index fb89fa8..559d04e 100644 --- a/arch/attiny1634.h +++ b/arch/attiny1634.h @@ -13,20 +13,25 @@ #define BOGOMIPS (F_CPU/4000) #define DELAY_ZERO_TIME 1020 +inline void mcu_clock_speed(); + ///// clock dividers inline void clock_prescale_set(uint8_t n); +// TODO? allow hwdef to define a base clock speed, +// and adjust these values accordingly 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 + // datasheet 6.5.2, CLKPR - Clock Prescale Register + clock_div_1 = 0, // 8 MHz + clock_div_2 = 1, // 4 MHz + clock_div_4 = 2, // 2 MHz + clock_div_8 = 3, // 1 MHz + clock_div_16 = 4, // 500 kHz + clock_div_32 = 5, // 250 kHz + clock_div_64 = 6, // 125 kHz + clock_div_128 = 7, // 62.5 kHz + clock_div_256 = 8, // 31.75 kHz } clock_div_t; @@ -39,11 +44,11 @@ typedef enum #define ADMUX_VCC 0b00001101 // (1 << V_REF) | (THERM_CHANNEL) // 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 - - - #define hwdef_set_admux_therm mcu_set_admux_therm inline void mcu_set_admux_therm(); @@ -54,8 +59,6 @@ inline void mcu_adc_sleep_mode(); inline void mcu_adc_start_measurement(); -inline void mcu_adc_on(); - inline void mcu_adc_off(); // NOP because interrupt flag clears itself @@ -63,6 +66,15 @@ inline void mcu_adc_off(); inline uint16_t mcu_adc_result(); +// return Volts * 40, range 0 to 6.375V +#define voltage_raw2cooked mcu_vdd_raw2cooked +inline uint8_t mcu_vdd_raw2cooked(uint16_t measurement); +inline uint8_t mcu_vdivider_raw2cooked(uint16_t measurement); + +// return (temp in Kelvin << 6) +#define temp_raw2cooked mcu_temp_raw2cooked +inline uint16_t mcu_temp_raw2cooked(uint16_t measurement); + inline uint8_t mcu_adc_lsb(); |
