From 08102c2bcd02991576feaf06ecfd130c8123377f Mon Sep 17 00:00:00 2001 From: true Date: Sat, 11 Feb 2023 04:47:30 -0800 Subject: [PATCH] Initial commit of tweaked ATtiny88 code Only some minor cleanup performed. Builds fine with AS7 but is otherwise untested. --- .gitignore | 3 + attiny88_main/bin/wp_dc22_attiny88.elf | Bin 0 -> 22748 bytes attiny88_main/bin/wp_dc22_attiny88.hex | 141 +++++ attiny88_main/src/adc.c | 107 ++++ attiny88_main/src/adc.h | 56 ++ attiny88_main/src/config.h | 34 ++ attiny88_main/src/i2c.h | 60 ++ attiny88_main/src/i2c_interrupt.h | 102 ++++ attiny88_main/src/led.c | 164 +++++ attiny88_main/src/led.h | 44 ++ attiny88_main/src/main.c | 574 ++++++++++++++++++ attiny88_main/src/timer.h | 110 ++++ attiny88_main/wp_dc22_attiny88.atsln | 20 + .../wp_dc22_attiny88.componentinfo.xml | 86 +++ attiny88_main/wp_dc22_attiny88.cproj | 171 ++++++ 15 files changed, 1672 insertions(+) create mode 100644 .gitignore create mode 100644 attiny88_main/bin/wp_dc22_attiny88.elf create mode 100644 attiny88_main/bin/wp_dc22_attiny88.hex create mode 100644 attiny88_main/src/adc.c create mode 100644 attiny88_main/src/adc.h create mode 100644 attiny88_main/src/config.h create mode 100644 attiny88_main/src/i2c.h create mode 100644 attiny88_main/src/i2c_interrupt.h create mode 100644 attiny88_main/src/led.c create mode 100644 attiny88_main/src/led.h create mode 100644 attiny88_main/src/main.c create mode 100644 attiny88_main/src/timer.h create mode 100644 attiny88_main/wp_dc22_attiny88.atsln create mode 100644 attiny88_main/wp_dc22_attiny88.componentinfo.xml create mode 100644 attiny88_main/wp_dc22_attiny88.cproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90fc93e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ + +attiny88_main/Debug +attiny88_main/Release diff --git a/attiny88_main/bin/wp_dc22_attiny88.elf b/attiny88_main/bin/wp_dc22_attiny88.elf new file mode 100644 index 0000000000000000000000000000000000000000..c2f4fb16eceacd5af6198253be32155feeee647f GIT binary patch literal 22748 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 + + +volatile uint8_t adc_busy; +volatile uint8_t adc_reread; + +volatile uint16_t adc_result[ADC_MAX_FEEDBACK + 1]; +static uint8_t adc_averages; + +volatile uint8_t adc_read_mode; + + +/* functions */ +void adc_init() +{ + // make sure ADC power is enabled + PRR &= ~_BV(PRADC); + + // clear all settings; set the ADC prescaler bits to get 500KHz ADC clock (SYSCLK/16), disable auto trigger + ADCSRA = _BV(ADPS2) | _BV(ADPS0) | ~(_BV(ADATE)); + + // set to right-aligned mode + ADMUX &= ~(_BV(ADLAR)); + + // enable the ADC, enable interrupts + ADCSRA |= _BV(ADEN) | _BV(ADIE); +} + +void adc_channel(uint8_t channel, uint8_t vref) +{ + // can we set channel? + if (channel < ADC_CHAN_INVALID) { + // set MUX[3:0] to the specified channel + ADMUX = (ADMUX & 0xf0) | (channel & 0x0f); + // also, if this is an actual pin, disable digital input on this pin + DIDR0 = (channel < 8) ? _BV(channel) : 0; + } + + // set the voltage source + if (vref == ADC_REF_AVCC) { + ADMUX |= (_BV(REFS0)); + } else if (vref == ADC_REF_BANDGAP) { + ADMUX &= ~(_BV(REFS0)); + } +} + +uint8_t adc_start(uint8_t reread, uint8_t enable_averaging) +{ + // is a conversion already running? + if (ADCSRA & _BV(ADSC)) { + return 1; + } + + // set up amount of times to re-read, and mark the amount of values to average + adc_reread = reread; + adc_averages = enable_averaging ? reread : 0; + + // start conversion + adc_busy = 0x80 | (ADMUX & 0x0f); + ADCSRA |= _BV(ADSC); + return 0; +} + + +/* ISR */ +ISR(ADC_vect) +{ + uint8_t channel; + + channel = ADMUX & 0x0f; + + // mark our result in the average table + if (adc_averages) { + // except for the first read + if (adc_averages != adc_reread) { + // we do successive averaging; it's faster, smaller code, less RAM, and good enough for our purpose + // update our feedback + if (channel <= ADC_MAX_FEEDBACK) { + adc_result[channel] = (adc_result[channel] + ADC) >> 1; + } + } + } + + if (adc_reread) { + adc_reread--; + // start another conversion + ADCSRA |= _BV(ADSC); + } else { + // determine if we need to update feedback + if (!adc_averages && channel <= ADC_MAX_FEEDBACK) { + // not averaging, so just store last result + adc_result[channel] = ADC; + } + + adc_busy = 0; + } +} \ No newline at end of file diff --git a/attiny88_main/src/adc.h b/attiny88_main/src/adc.h new file mode 100644 index 0000000..7c0559a --- /dev/null +++ b/attiny88_main/src/adc.h @@ -0,0 +1,56 @@ +/* + * adc.h + * + * Created: 6/9/2014 11:07:51 PM + * Author: true + */ + + +#ifndef ADC_H_ +#define ADC_H_ + + +#include "config.h" + + +#define ADC_CHAN_0 0 +#define ADC_CHAN_1 1 +#define ADC_CHAN_2 2 +#define ADC_CHAN_3 3 +#define ADC_CHAN_4 4 +#define ADC_CHAN_5 5 +#define ADC_CHAN_6 6 +#define ADC_CHAN_7 7 +#define ADC_CHAN_TEMP 8 +#define ADC_CHAN_BANDGAP 14 +#define ADC_CHAN_GND 15 +#define ADC_CHAN_INVALID ADC_CHAN_GND + 1 // one above last valid channel + +#define ADC_REF_AVCC 1 +#define ADC_REF_BANDGAP 2 +#define ADC_REF_NO_SET 255 + +#define ADC_MAX_FEEDBACK ADC_CHAN_TEMP + +// adc read interrupt +extern volatile uint8_t adc_busy; +extern volatile uint16_t adc_result[ADC_MAX_FEEDBACK + 1]; + +// main timer adc handler +extern volatile uint8_t adc_read_mode; +register uint8_t adc_read_step asm("r14"); + + +/* application specific */ +#define ADC_READ_LED_RED ADC_CHAN_0 +#define ADC_READ_LED_GREEN ADC_CHAN_1 +#define ADC_READ_LED_BLUE ADC_CHAN_2 + + +/* prototypes */ +void adc_init(); +void adc_channel(uint8_t channel, uint8_t bandgap_ref); +uint8_t adc_start(uint8_t reread, uint8_t enable_averaging); + + +#endif /* ADC_H_ */ \ No newline at end of file diff --git a/attiny88_main/src/config.h b/attiny88_main/src/config.h new file mode 100644 index 0000000..627ca27 --- /dev/null +++ b/attiny88_main/src/config.h @@ -0,0 +1,34 @@ +/* + * config.h + * + * Created: 5/24/2014 9:22:14 AM + * Author: true + */ + + +#ifndef CONFIG_H_ +#define CONFIG_H_ + + +#include + +#include +#include + + +// system +#define F_CPU 8000000UL + + +// LEDs +#define LED_COMMON_ANODE // defined = common anode, undef = common cathode +//#define LED_RGBX_4LED 1 // defined = use 4 LEDs (RGBX), undef = use 3 LEDs (RGB) + + +// i2c config +#define I2C_SLAVE_ADDRESS 0x73 // 7-bit address (0b1110011) +#define I2C_ENABLE_ADDR_ZERO 0 // 1 = enable response on 0x00, 0 = disable + + + +#endif /* CONFIG_H_ */ \ No newline at end of file diff --git a/attiny88_main/src/i2c.h b/attiny88_main/src/i2c.h new file mode 100644 index 0000000..de45bde --- /dev/null +++ b/attiny88_main/src/i2c.h @@ -0,0 +1,60 @@ +/* + * i2c.h + * + * Created: 5/21/2014 11:41:32 PM + * Author: true + */ + + +#ifndef I2C_H_ +#define I2C_H_ + + +#include "config.h" + + +/* macros */ +#define i2c_clear_int_flag() TWCR |= _BV(TWINT) + +#define i2c_disable_interrupt() TWCR &= ~(_BV(TWIE)) +#define i2c_enable_interrupt() TWCR |= _BV(TWIE) + +#define i2c_disable_slave() TWCR &= ~(_BV(TWEA)) +#define i2c_enable_slave() TWCR |= _BV(TWEA) + + +/* configuration */ +static inline void i2c_slave_init(uint8_t address, uint8_t enable_general_addr) +{ + // set bitrate register, clear prescaler + // should get us 400KHz (fast mode) max rate + TWBR = 2; + TWSR = ~(_BV(TWPS0) | _BV(TWPS1)); + + // set slave address and clear address mask + TWAR = ((address << 1) | (enable_general_addr & 1)); + TWAMR = 0; + + // enable auto-ack, enable TWI, and enable the TWI interrupt + TWCR = _BV(TWEA) | _BV(TWEN) | _BV(TWIE); +} + +/* sending */ +static uint8_t i2c_slave_tx(uint8_t byte, uint8_t is_last_byte) +{ + // load data + TWDR = byte; + + // start sending + TWCR |= _BV(TWINT); + + if (is_last_byte) { + // this is all the data we have to send; send a nack after this byte + i2c_disable_slave(); + } + + return 0; +} + + +#endif /* I2C_H_ */ \ No newline at end of file diff --git a/attiny88_main/src/i2c_interrupt.h b/attiny88_main/src/i2c_interrupt.h new file mode 100644 index 0000000..b14cced --- /dev/null +++ b/attiny88_main/src/i2c_interrupt.h @@ -0,0 +1,102 @@ +/* + * i2c_interrupt.h + * + * Created: 6/13/2014 1:53:36 AM + * Author: true + */ + + +#ifndef I2C_INTERRUPT_H_ +#define I2C_INTERRUPT_H_ + + +#include "config.h" + + +ISR(TWI_vect) +{ + // i2c is low priority - allow it to be interrupted + // sei(); + + // figure out wtf to do + switch (TWSR & 0xf8) { // TWI state + // ** SLAVE RECEIVE ** // + case 0x60: // slave addr+write receive to valid address + case 0x68: { // slave addr+write arbitration lost? + // nothing to do. we're ready to receive. + break; + } + /* + case 0x70: + case 0x78: { + // unused: this is for genaddr mode. + } + */ + case 0x80: { // slave data receive w/ACK returned + if (pegleg_data_rx(TWDR)) { + // I wanted to send a NAK, but TWI already sent an ACK. oh fucking well. + // send NACK if any further packets are received + i2c_disable_slave(); + } + break; + } + /*case 0x90: { // slave genaddr data receive w/ACK returned + // not using this + }*/ + + case 0x88: // slave data receive w/NACK returned, no longer addressed + //case 0x98: // slave genaddr data receive w/NACK returned, no longer addressed + case 0xa0: { // STOP asserted; done receiving? no longer addressed + // we can re-enable the slave... + i2c_enable_slave(); + break; + } + + // ** SLAVE TRANSMIT ** // + case 0xa8: { // slave read request to valid address + // try to send data + pegleg_data_tx(); + break; + } + case 0xb8: { // slave read continues, master ACK'd last packet + // do we have a mode in progress? + // NOTE: by default, slave tx modes won't unset, but slave mode will be disabled + // when the last packet is sent, and re-enabled on the following ACK/NACK. + pegleg_data_tx(); + break; + } + + case 0xb0: // arbitration lost + // I don't even know how the fuck this happened + case 0xc0: // slave read ends, master doesn't want more data + // this usually happens after we've sent data and master doesn't want any more. + // if we had more data, tough shit, master doesn't want it. so we don't send it. + case 0xc8: { // slave read ends, master wants more data though + // this usually happens after we're done sending, but master is requesting more. + // but slave mode is disabled, so don't send anything. + // we need to make sure send mode is disabled, and we need to re-enable slave mode. + i2c_enable_slave(); + break; + } + + // ** ERROR AND UNHANDLED ** // + case 0x00: { // bus error + // set TWSTO, clear TWINT (by setting) as per datasheet + TWCR |= (_BV(TWSTO)) | (_BV(TWINT)); + // and make sure slave is enabled + i2c_enable_slave(); + break; + } + default: { + // something unhandled? fuck it, we want to be a slave. + // reset everything and start over. + i2c_enable_slave(); + } + } + + // done with this TWI bit + i2c_clear_int_flag(); +} + + +#endif /* MAIN_INTERRUPT_H_ */ \ No newline at end of file diff --git a/attiny88_main/src/led.c b/attiny88_main/src/led.c new file mode 100644 index 0000000..1d9b0d4 --- /dev/null +++ b/attiny88_main/src/led.c @@ -0,0 +1,164 @@ +/* + * led.c + * + * Created: 6/13/2014 1:44:28 AM + * Author: true + */ + + +#include "led.h" + + +/* led */ +uint8_t rgbled_pwm_lf[4]; // pwm value for TIMER1 OCA LED outputs +uint8_t rgbled_pwm_rt[4]; // pwm value for TIMER1 OCB LED outputs + +static uint8_t rgbled_read_sel; // LED light sensor select +static uint8_t rgbled_sensitivity[4] = {1, 1, 1, 1}; + +/* adc */ +volatile uint8_t adc_busy; +volatile uint16_t adc_result[ADC_MAX_FEEDBACK + 1]; +volatile uint8_t adc_read_mode; + + +/* functions */ + // sets up the IO pins for LED output function. +void rgbled_io_init() +{ + // PortD[4:7] (left) and PortC[0:3] (right) are LEDs + // Also, Right LEDs are capable of ADC input on ALL pins (ADC[0:3]) + + // set all pins as outputs + DDRD |= 0xf0; + DDRC |= 0x0f; + +#ifdef LED_COMMON_ANODE + // com-anode: pin is high to disable + PORTD |= 0xf0; + PORTC |= 0x0f; +#else + // com-cathode: pin is low to disable + PORTD &= 0x0f; + PORTC &= 0xf0; +#endif +} + + // sets the appropriate pins and PWM values for the LED index specified in rgbled_idx. + // note: this index is updated externally, and not by this function. +void rgbled_update() +{ + // clear all selected LED pins + // we have to clear the select line then set it because we can't fully disable the PWM for some reason +#ifdef LED_COMMON_ANODE + // make sure all pins are high + PORTD |= 0xf0; + PORTC |= 0x0f; +#else + // make sure all pins are low + PORTD &= 0x0f; + PORTC &= 0xf0; +#endif + + // set pwm OCx registers to max + timer1_set_oca(TIMER1_COMPARE); + timer1_set_ocb(TIMER1_COMPARE); + // and set pwm counter value to 1 before expiry + timer1_set(TIMER1_COMPARE - 1); + + // now we set the desired LED + // turn on one LED in each group +#ifdef LED_COMMON_ANODE + // make sure active pin is output low and others are high + PORTD &= ~(_BV(rgbled_idx + 4)); + PORTC &= ~(_BV(rgbled_idx)); +#else + // make sure active pin is output high and others are low + PORTD |= _BV(rgbled_idx) << 4; + PORTC |= _BV(rgbled_idx); +#endif + + // load new pwm OCx values for this LED + timer1_set_oca(rgbled_pwm_lf[rgbled_idx]); + timer1_set_ocb(rgbled_pwm_rt[rgbled_idx]); +} + + // sets up the PortC LED matrix as a diode light sensor. + // configures the specified LED on the ADC. + // this function assumes common anode. +void rgbled_sensor_init(uint8_t led) +{ + // set the left eye high (fixes color flash/tearing) + PORTD |= 0xf0; + + // disable both eye's PWM (required for right eye, fixes color flash/tearing in left eye) + TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(COM1B0)); + + // ground the LED + DDRC &= 0xf0; // set all LEDs cathode as inputs + MCUCR |= (_BV(PUD)); // globally disable pullups + DDRC |= (_BV(led)); // set our LED as an output + PORTC = (PORTC & 0xf0); // set all LEDs cathode low + PORTB |= (_BV(PORTB2)); // set anode high + + // reverse LED bias + PORTC |= (_BV(led)); // set cathode high + PORTB &= ~(_BV(PORTB2)); // set anode low + _delay_us(3); // allow it to charge fully + + // set LED as input + DDRC &= ~(_BV(led)); // set led cathode as input + PORTC &= ~(_BV(led)); // set led cathode pullup off + MCUCR &= ~(_BV(PUD)); // re-enable global pullups +} + + // starts ADC to read the value of charge remaining on the LED +void rgbled_sensor_sensitivity(uint8_t ledidx, uint8_t sensitivity) +{ + if (ledidx <= 0x03) { + if (sensitivity) { + rgbled_sensitivity[ledidx] = sensitivity; + } + } +} + +void rgbled_sensor_read_idx(uint8_t ledidx) +{ + rgbled_read_sel = ledidx & 0x03; +} + +void rgbled_sensor_read() +{ + uint8_t sens; + + sens = rgbled_sensitivity[rgbled_read_sel]; + if (!sens) sens = 1; + + if (adc_read_step == 0) { + // adc_init(); + // clear ADC bias by reading ground + adc_channel(ADC_CHAN_GND, ADC_REF_AVCC); + } else if (adc_read_step == 1) { + adc_start(0, 0); + } else if (adc_read_step == 2) { + // if the ADC is done, charge up LED for reading + if (adc_busy) { + adc_read_step--; + } else { + rgbled_sensor_init(rgbled_read_sel); + adc_channel(rgbled_read_sel, ADC_REF_NO_SET); + } + } else if (adc_read_step == (2 + sens)) { + // if vref has changed, it should be stable by now; start reading LED value + adc_start(3, 1); + } else if (adc_read_step > (2 + sens)) { + adc_read_step--; + if (!adc_busy) { + // we are done! :) finish up... + adc_channel(ADC_CHAN_GND, ADC_REF_NO_SET); // change ADC to not look at a real pin + rgbled_io_init(); // set LED pins up again + timer1_pwm_reinit(); // and set up PWM again + adc_read_mode = 0; // clear ADC read mode + } + } +} diff --git a/attiny88_main/src/led.h b/attiny88_main/src/led.h new file mode 100644 index 0000000..eca7944 --- /dev/null +++ b/attiny88_main/src/led.h @@ -0,0 +1,44 @@ +/* + * led.h + * + * Created: 6/13/2014 1:40:53 AM + * Author: true + */ + + +#ifndef LED_H_ +#define LED_H_ + + +/* incl */ +#include "config.h" + +#include + +#include "adc.h" +#include "timer.h" + + +/* led */ +#define LED_RED 0 +#define LED_GREEN 1 +#define LED_BLUE 2 +#define LED_SPARE 3 + + +/* globals */ +extern uint8_t rgbled_pwm_lf[4]; // pwm value for TIMER1 OCA LED outputs +extern uint8_t rgbled_pwm_rt[4]; // pwm value for TIMER1 OCB LED outputs +register uint8_t rgbled_idx asm("r13"); // the currently operated LED + + +/* prototypes */ +void rgbled_io_init(); +void rgbled_update(); + +void rgbled_sensor_sensitivity(uint8_t ledidx, uint8_t sensitivity); +void rgbled_sensor_read_idx(uint8_t ledidx); +void rgbled_sensor_read(); + + +#endif /* LED_H_ */ \ No newline at end of file diff --git a/attiny88_main/src/main.c b/attiny88_main/src/main.c new file mode 100644 index 0000000..72dbb7f --- /dev/null +++ b/attiny88_main/src/main.c @@ -0,0 +1,574 @@ +/* + * Whiskey Pirates Badge "PEGLEG CPU" + * Sub MCU Firmware for ATtiny88 + * + * Created: 5/21/2014 9:24:48 PM + * Author: true + * ts:4 + * + ******** + * PEGLEG CPU is responsible for: + * - aux gpio (todo: list specifically which GPIOs on the badge) + * - eye RGBLED matrix + * - temperature sensor + * - light sensor (using eye RGBLEDs; in practice uses the left eye red LED) + * - user config eeprom + * + ******** + * Use the following fuses for this project. Needs to run at 8MHz for 400khz I2C operation. + * 8MHz, self-program enabled (bootloader support), preserve EEPROM, BOD disabled, ISP enabled + * -U lfuse:w:0xEE:m -U hfuse:w:0xD7:m -U efuse:w:0x00:m + * (efuse will likely show as 0x06 after setting; this is fine, don't worry about it) + * + * Resources used: Mainline ISR 5% (3/63 timer counts simulated, as of some old rev) + * Flash 26.8%, SRAM 10.4% (as of the above rev) + * + ********** + * Register Variables: + * main.c:comm_cmd:r15 + * adc.h:adc_read_step:r14 + * led.c:rgbled_idx:r13 + */ + + +#include "config.h" + + +/* system */ +#include +#include +#include +#include + +#include + + +#include "i2c.h" + +#include "timer.h" +#include "adc.h" +#include "led.h" + + + +/* modes (commands) */ +#define MODE_NONE 0b0000 + +#define MODE_EXT_CMD 0b0001 // bit 4 unset = extra parameters needed, up to 6 commands +#define MODE_LED_SET_LEVEL 0b0010 // (0x00 reserved, 0x1? extra, leaving 0x2? through 0x7?) +#define MODE_TEMPSENSOR_CAL 0b0011 +#define MODE_EEPROM_READ 0b0100 +#define MODE_EEPROM_WRITE 0b0101 + +#define MODE_TEMPSENSOR_READ 0b1000 // bit 4 set = immediate, no extra parameters, process now +#define MODE_LIGHTSENSOR_READ 0b1001 +#define MODE_SLEEP 0b1111 + +#define MODE_AUX_PIN_GET 0x10 // extended (send 0x10, cmd, data...) +#define MODE_AUX_PIN_SET 0x11 // ext commands MUST be 0x10 or higher! 0x00-0x0f are RESERVED! +#define MODE_LIGHTSENSOR_SENS 0x19 + +/* aux pin mode */ +#define AUX_PIN_COUNT 10 +#define AUX_PIN_OUTPUT 0x01 // sets to output if set, input if cleared +#define AUX_PIN_PORTSET 0x02 // output: sets high/low; input: sets pullup on/off + +/* adc modes */ +#define ADC_MODE_TEMPSENSOR 8 +#define ADC_MODE_LIGHTSENSOR 1 + + +/* prototypes */ +static void system_init(); +static void system_io_init(); +static void tempsensor_cal(); + + +/* globals */ +static uint8_t tim0_milli; +static uint8_t tim0_centi; +static uint8_t tim0_profiler; + +#define COMM_DATA_SIZE 4 // must be power of 2; default value is 4 +register uint8_t comm_cmd asm("r15"); // currently processed SPI command +uint8_t comm_data_idx; // command data write index +uint8_t comm_data[COMM_DATA_SIZE]; // command data register +uint8_t comm_timeout; // command invalidate timeout + + +/* core */ +static uint8_t pirate_sleep_mode = SLEEP_MODE_IDLE; + +/* tempsensor */ +static int8_t temperature; +static int16_t temp_offset; + +/* eeprom */ +#define EEPROM_ADDR_TEMPCAL 62 // 2 bytes + +/* adc */ +volatile uint8_t adc_busy; +volatile uint16_t adc_result[ADC_MAX_FEEDBACK + 1]; + +/* led */ +uint8_t rgbled_pwm_lf[4]; // pwm value for TIMER1 OCA LED outputs +uint8_t rgbled_pwm_rt[4]; // pwm value for TIMER1 OCB LED outputs + +uint8_t rgbled_light_level[4]; + + + +/* it begins */ +int main(void) +{ + // configure device and IO + system_init(); + system_io_init(); + + // configure RGBLEDs + rgbled_io_init(); + + // configure i2c communications + i2c_slave_init(I2C_SLAVE_ADDRESS, I2C_ENABLE_ADDR_ZERO); + + // configure system timer + timer0_init(); + // configure led pwm timer + timer1_init(); + + // initialize adc + adc_init(); + + // try calibrating temperature sensor if needed + tempsensor_cal(); + + // and now we wait. + while (1) { + // set sleep mode, then set for idle sleep (CPU off, all peripherals on) + set_sleep_mode(pirate_sleep_mode); + pirate_sleep_mode = SLEEP_MODE_IDLE; + + // re-enable interrupts and nap. + sei(); + sleep_mode(); + } +} + + +/* func */ +static void system_init() +{ + // make SURE we are running at 8MHz + // do this by first enabling clock divider setup mode, + CLKPR = _BV(CLKPCE); + // and disabling any divider + CLKPR = 0; +} + +static void system_io_init() +{ + // enable break-before-make outputs on all IO + PORTCR |= (_BV(BBMA) | _BV(BBMB) | _BV(BBMC) | _BV(BBMD)); + + // set ALL pins as inputs except PB0 + DDRA = 0x00; + DDRB = 0x01; + DDRC = 0x00; + DDRD = 0x00; + + // and enable pullups (this reduces power consumption) + // in the case of PB0, set it LOW + PORTA = 0xff; + PORTB = 0xfe; + PORTC = 0xff; + PORTD = 0xff; +} + +/* temperature */ +uint8_t tempsensor_process() +{ + int8_t temp_gain; + int8_t temp_adj; + + temp_adj = adc_result[ADC_CHAN_TEMP] - temp_offset; // our offset adjusted temperature + temp_gain = temp_adj >> 3; // our gain adjust amount + while (temp_gain > 0) {temp_gain--; temp_adj--;}; // correct positive gain + while (temp_gain < 0) {temp_gain++; temp_adj++;}; // correct negative gain + + // save temperature + return temp_adj; +} + +void tempsensor_read() +{ + if (adc_read_step == 0) { + // select temperature channel + adc_channel(ADC_CHAN_TEMP, ADC_REF_BANDGAP); + + } else if (adc_read_step == 3) { + // we've waited 1.5ms so the bandgap voltage should have been set. + // start conversion after bandgap ref change timeout per datasheet + adc_start(4, 1); + + } else if (adc_read_step > 3) { + adc_read_step--; + + if (!adc_busy) { + // revert reference to AVCC...per the datasheet only changes to + // bandgap should take ~1ms but changes back to AVCC seem to + // take a while to be accurate as well + adc_channel(ADC_CHAN_GND, ADC_REF_AVCC); + adc_read_mode = 0; + temperature = tempsensor_process(); + } + } +} + +void tempsensor_cal() +{ + // NOTE: CALIBRATION IS SINGLE-POINT TO 0 DEGREES CENTIGRADE! ICE YOUR PEGLEG, MOTHERFUCKER! + // todo: figure out wtf I meant by this note in 2014 (this is me in 2023) + // looking at the code, it looks like a pin needs manually changed to calibrate. + // todo: add a zeroing offset calibration command to the command protocol? + + // is pin PA2 low? (pin 3 on attiny88 - is pulled up - short to pin 5 / ground somewhere) + if ((PINA & _BV(PINA2)) == 0) { + // turn on the ADC, wait for it to warm up + adc_init(); + _delay_ms(100); // todo: is this right? this seems slow + + // is the pin still low? not a glitch? + if ((PINA & _BV(PINA2)) == 0) { + // enable interrupts + sei(); + + // start reading the temp sensor; stall until adc is done + adc_read_mode = ADC_MODE_TEMPSENSOR; + tempsensor_read(); + while (adc_busy); + + // here's our correction factor! + temp_offset = adc_result[ADC_CHAN_TEMP]; + + // write it to eeprom + eeprom_write_word((uint16_t *)EEPROM_ADDR_TEMPCAL, temp_offset); + + // and that's it - we're done. + adc_read_mode = 0; + return; + } + } + + // not saving a value - read the value from eeprom + temp_offset = eeprom_read_word((uint16_t *)EEPROM_ADDR_TEMPCAL); + + // does it seem invalid? + if (temp_offset > 560) { + // yeah, this isn't right. load rough value for 0degC from datasheet. + // but you really should calibrate your chip, you lazy fuck. + temp_offset = 273; + } +} + +/* comms */ +static inline uint8_t pegleg_data_tx() +{ + uint8_t ret; + + switch (comm_cmd) { + case MODE_EEPROM_READ: { + if (comm_data[0] < 64) { + i2c_slave_tx(eeprom_read_byte((uint8_t *)(uint16_t)comm_data[0]), 1); + ret = 1; + } else { + // invalid address... + i2c_disable_slave(); + ret = 255; + } + comm_cmd = 0; + comm_timeout = 0; + return ret; + } + + case MODE_TEMPSENSOR_READ: { + i2c_slave_tx(temperature, 1); + comm_cmd = 0; + comm_timeout = 0; + return 1; + } + case MODE_LIGHTSENSOR_READ: { + i2c_slave_tx(adc_result[comm_data[0] & 0x03] >> 2, 1); + comm_cmd = 0; + comm_timeout = 0; + return 1; + } + + default: { + // nothing active; invalid :( + i2c_disable_slave(); + return 255; + } + } + + return 0; +} + +static uint8_t pegleg_cmd() +{ + switch (comm_cmd) { + // no command: we just ACK this as we shouldn't ever get here + case MODE_NONE: { + comm_cmd = 0; + comm_timeout = 0; + break; + } + + // standard commands + case MODE_EXT_CMD: { // used for more commands + if (comm_data_idx >= 1) { + comm_cmd = comm_data[1]; + // set data index to last (will be reset on next data sent) + // and also reset timeout since this is effectively a new command + comm_data_idx = COMM_DATA_SIZE - 1; + comm_timeout = 0; + } + break; + } + + case MODE_LED_SET_LEVEL: { // sets PWM rate for the 8 PWM LEDs + if (comm_data[0] < 4) { + rgbled_pwm_lf[comm_data[0]] = comm_data[1]; + } else if (comm_data[0] < 8) { + rgbled_pwm_rt[comm_data[0] - 4] = comm_data[1]; + } + comm_cmd = 0; + comm_timeout = 0; + break; + } + + case MODE_TEMPSENSOR_CAL: { + // to use this function, make sure the temperature has been previously read. + // calculates the appropriate offset based on the reported temperature. + if (comm_data_idx >= 1) { + // update the offset + if (adc_result[ADC_CHAN_TEMP]) { + temp_offset = adc_result[ADC_CHAN_TEMP] - (int8_t)comm_data[1]; + // write it to eeprom + eeprom_write_word((uint16_t *)EEPROM_ADDR_TEMPCAL, temp_offset); + // and that's it - we're done. + comm_cmd = 0; + comm_timeout = 0; + } else { + // no valid read performed; I fucking told you to do this! + comm_cmd = 0; + comm_timeout = 0; + return 1; + } + } + break; + } + + case MODE_EEPROM_READ: { + // our data packet is already set; ready for read. + // but if the host keeps writing... + if (comm_data_idx > 1) { + comm_cmd = 0; + comm_timeout = 0; + } + break; + } + case MODE_EEPROM_WRITE: { + // TODO: implement + comm_cmd = 0; + comm_timeout = 0; + break; + } + + // immediate commands + case MODE_TEMPSENSOR_READ: { + // attempt another read + if (!adc_read_mode) { + adc_read_mode = 0; + adc_read_step = 0; + adc_read_mode = ADC_MODE_TEMPSENSOR; + } + + // now we are ready to read. + // data will probably be prior data unless the host waits. (need to verify) + comm_timeout = 0; + break; + } + case MODE_LIGHTSENSOR_READ: { + if (!adc_read_mode) { + // set the led and step to first step + adc_read_mode = 0; + adc_read_step = 0; + rgbled_sensor_read_idx(comm_data[0]); + adc_read_mode = ADC_MODE_LIGHTSENSOR; + } + + // now we are ready to read. + // data will probably be prior data unless the host waits. + comm_timeout = 0; + break; + } + case MODE_SLEEP: { + // put this bitch to bed. + // we'll wake up when we get another I2C command. + pirate_sleep_mode = SLEEP_MODE_PWR_DOWN; + + comm_cmd = 0; + comm_timeout = 0; + break; + } + + // extended commands + case MODE_AUX_PIN_SET: { + // TODO: come up with an implementation that isn't shitty + } + + case MODE_AUX_PIN_GET: { + // TODO: implement when response sending is ...implemented + comm_cmd = 0; + comm_timeout = 0; + break; + } + case MODE_LIGHTSENSOR_SENS: { // 0x10 0x19 + if (comm_data_idx >= 1) { + if ((comm_data[0] <= 0x03) && (comm_data[1])) { + rgbled_sensor_sensitivity(comm_data[0], comm_data[1]); + } + comm_cmd = 0; + comm_timeout = 0; + } + break; + } + + // invalid commands + default: { + // send NAK + comm_cmd = 0; + comm_timeout = 0; + return 1; + } + } + + return 0; +} + +static uint8_t pegleg_data_rx(uint8_t data) +{ + if (comm_cmd == MODE_NONE) { + // we aren't processing any commands, so this must be a new command/request. + // first 4 bits = command + comm_cmd = data >> 4; + // last 4 bits = optional data + comm_data[0] = data & 0x0f; + + // clear our data watchdog and data index + comm_data_idx = 0; + comm_timeout = 0; + + // if this is an immediate command, process it + if (data & 0x80) { + return pegleg_cmd(); + } + } else { + // command in progress - add data and continue + comm_data_idx++; + comm_data_idx &= (COMM_DATA_SIZE - 1); // data will LOOP in case of problems; TODO: NAK on failure + comm_data[comm_data_idx] = data; + return pegleg_cmd(); + } + + return 0; +} + +#include "i2c_interrupt.h" + + +/* ISR handlers */ +ISR(TIMER0_COMPA_vect) +{ + /**** + * this mainline loop runs at 2KHz, or 0.5ms. + * at the faster speed, this is ~3968 cycles/loop @8MHz. + ****/ + + /* TIMEKEEPING */ + // we only count a total of 10 seconds this way before we loop. + tim0_milli++; + if (tim0_milli >= 200) { + tim0_milli = 0; + tim0_centi++; + if (tim0_centi >= 100) { + tim0_centi = 0; + } + } + + // 2khz fix. our timer isn't evenly divisible to get our 2K, so we do this... + OCR0A = (OCR0A == TIMER0_COMPARE) ? TIMER0_COMPARE + 1 : TIMER0_COMPARE; + + + /* ADC / LIGHT SENSOR / TEMP SENSOR */ + // main adc handler + if (adc_read_mode) { + switch (adc_read_mode) { + case ADC_MODE_TEMPSENSOR: { + tempsensor_read(); + break; + } + case ADC_MODE_LIGHTSENSOR: { + rgbled_sensor_read(); + break; + } + } + + if (adc_read_step <= 0xfe) { + adc_read_step++; + } + } /* else { + if (adc_read_step != 0xff) { + adc_read_step++; + if (adc_read_step == 0xff) { + // disable the ADC + ADCSRA &= ~(_BV(ADEN)); + } + } + } */ // NOTE: we need to keep the ADC enabled; it takes too long to warm up. + + + /* LEDs */ + // TODO: test common cathode mode + // figure out next LED to update (order is always RGB(X) repeating) + if (adc_read_mode != ADC_MODE_LIGHTSENSOR) { + rgbled_idx++; +#ifdef LED_RGBX_4LED + rgbled_idx &= 0x03; +#else + if (rgbled_idx > 2) { + rgbled_idx = 0; + } +#endif + + // and now update them + rgbled_update(); + } + + + /* COMMAND IO */ + // stop processing the command if it isn't complete in 100ms. + if (comm_cmd != MODE_NONE) { + if (++comm_timeout >= 50) { + comm_cmd = MODE_NONE; + comm_timeout = 0; + i2c_disable_slave(); + i2c_enable_slave(); + } + } + + /* PROFILING */ + // determine how much CPU usage we are consuming + tim0_profiler = TCNT0; +} + diff --git a/attiny88_main/src/timer.h b/attiny88_main/src/timer.h new file mode 100644 index 0000000..6f11f7b --- /dev/null +++ b/attiny88_main/src/timer.h @@ -0,0 +1,110 @@ +/* + * timer.h + * + * Created: 5/21/2014 10:45:08 PM + * Author: true + */ + + +#ifndef TIMER_H_ +#define TIMER_H_ + + +#include "led.h" + + +#define TIMER0_COMPARE 62 - 1 +#define TIMER1_COMPARE 252 - 1 // divisible by 63 which is set every other round for timer0 + + +uint8_t timer1_mode; + + +/* prototypes */ + + +/* implementations */ +inline static void timer0_init() +{ + // make sure timer is enabled + PRR &= ~PRTIM0; + + // enable timer with prescale divider /64, enable TOP mode + TCCR0A = _BV(CTC0) | (_BV(CS00) | _BV(CS01) | ~(_BV(CS02))); + + // set TOP level to be 62 to 63 counts; this will result in 2KHz cycle @8MHz + OCR0A = TIMER0_COMPARE; + + // clear timer + TCNT0 = 0; + + // and enable OCA interrupt (we don't use overflow interrupt as we are in TOP mode) + TIMSK0 = _BV(OCIE0A); +} + +#define timer0_set_compare(x) OCR0A = x + +inline static void timer1_init() +{ + // make sure timer is enabled + PRR &= ~PRTIM1; + + // clear PWM OCx + OCR1AH = 0xff; // sets temp register to 0xff + OCR1AL = 0xff; + OCR1BL = 0xff; + + // clear timer + TCNT1H = 0; // sets temp register to 0 + TCNT1L = 0; + + // enable timer OC1 and OC2 clear on match / set at TOP, set to fast PWM mode 14 (ICR1 compare), prescaler off + // note: in PWM mode 14, pin PB0 can only be used as an output... =( + TCCR1A = (_BV(COM1A1)) | (_BV(COM1B1)) | (_BV(WGM11) | ~(_BV(WGM10))); + TCCR1B = (_BV(WGM12) | _BV(WGM13)) | (_BV(CS10)); + TCCR1C = 0; + + // store timer1 mode to allow enabling / disabling timer + timer1_mode = TCCR1A; + + // set ICR compare to result in a PWM rate of 8MHz / 252, or 32256Hz + // This is 16 PWM updates per timer0 63-count period (16.25 per 62-count period) + // In practice we don't get the first PWM output, so we get: + // 15/16 PWM / (x) LEDs = 31.25% max duty for 3 LED, 23.43% max duty for 4 LED + ICR1L = TIMER1_COMPARE; + + // set OC pins as outputs + DDRB |= (_BV(DDB2) | _BV(DDB1)); + + // disable interrupts + TIMSK1 = 0; +} + +inline void timer1_pwm_reinit() +{ + // enable pwm output on OCx pins + TCCR1A = (_BV(COM1A1)) | (_BV(COM1B1)) | (_BV(WGM11) | ~(_BV(WGM10))); + + // set OC pins as outputs + DDRB |= (_BV(DDB2) | _BV(DDB1)); +} + +inline void timer1_set(uint16_t set) +{ + TCNT1 = set; +} + + +#define timer1_disable() TCCR1A = 0 +#define timer1_enable() TCCR1A = timer1_mode; + +#ifdef LED_COMMON_ANODE + #define timer1_set_oca(v) OCR1AL = TIMER1_COMPARE - v + #define timer1_set_ocb(v) OCR1BL = TIMER1_COMPARE - v +#else + #define timer1_set_oca(v) OCR1AL = TIMER1_COMPARE + #define timer1_set_ocb(v) OCR1BL = TIMER1_COMPARE +#endif + + +#endif /* TIMER_H_ */ \ No newline at end of file diff --git a/attiny88_main/wp_dc22_attiny88.atsln b/attiny88_main/wp_dc22_attiny88.atsln new file mode 100644 index 0000000..9d796fe --- /dev/null +++ b/attiny88_main/wp_dc22_attiny88.atsln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Atmel Studio Solution File, Format Version 11.00 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "wp_dc22_attiny88", "wp_dc22_attiny88.cproj", "{0AAADF73-70F0-4C23-875D-53CCE8ABBF9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|AVR = Debug|AVR + Release|AVR = Release|AVR + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0AAADF73-70F0-4C23-875D-53CCE8ABBF9B}.Debug|AVR.ActiveCfg = Debug|AVR + {0AAADF73-70F0-4C23-875D-53CCE8ABBF9B}.Debug|AVR.Build.0 = Debug|AVR + {0AAADF73-70F0-4C23-875D-53CCE8ABBF9B}.Release|AVR.ActiveCfg = Release|AVR + {0AAADF73-70F0-4C23-875D-53CCE8ABBF9B}.Release|AVR.Build.0 = Release|AVR + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/attiny88_main/wp_dc22_attiny88.componentinfo.xml b/attiny88_main/wp_dc22_attiny88.componentinfo.xml new file mode 100644 index 0000000..49c3862 --- /dev/null +++ b/attiny88_main/wp_dc22_attiny88.componentinfo.xml @@ -0,0 +1,86 @@ + + + + + + + Device + Startup + + + Atmel + 1.3.0 + C:/Program Files (x86)\Atmel\Studio\7.0\Packs + + + + + C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.172\include + + include + C + + + include + + + + + C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.172\include\avr\iotn88.h + + header + C + S2HUptOYqYmhL8kjFIMzIA== + + include/avr/iotn88.h + + + + + C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.172\templates\main.c + template + source + C Exe + GD1k8YYhulqRs6FD1B2Hog== + + templates/main.c + Main file (.c) + + + + C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.172\templates\main.cpp + template + source + C Exe + YXFphlh0CtZJU+ebktABgQ== + + templates/main.cpp + Main file (.cpp) + + + + C:/Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\ATtiny_DFP\1.3.172\gcc\dev\attiny88 + + libraryPrefix + GCC + + + gcc/dev/attiny88 + + + + + ATtiny_DFP + C:/Program Files (x86)/Atmel/Studio/7.0/Packs/atmel/ATtiny_DFP/1.3.172/Atmel.ATtiny_DFP.pdsc + 1.3.172 + true + ATtiny88 + + + + Resolved + Fixed + true + + + \ No newline at end of file diff --git a/attiny88_main/wp_dc22_attiny88.cproj b/attiny88_main/wp_dc22_attiny88.cproj new file mode 100644 index 0000000..180c2cd --- /dev/null +++ b/attiny88_main/wp_dc22_attiny88.cproj @@ -0,0 +1,171 @@ + + + + 2.0 + 7.0 + com.Atmel.AVRGCC8.C + {0aaadf73-70f0-4c23-875d-53cce8abbf9b} + ATtiny88 + none + Executable + C + $(MSBuildProjectName) + .elf + $(MSBuildProjectDirectory)\$(Configuration) + wp_dc22_attiny88 + wp_dc22_attiny88 + wp_dc22_attiny88 + Native + true + false + true + true + 0x20000000 + + exception_table + 2 + 1 + + + + + + + + + + + + + True + com.atmel.avrdbg.tool.simulator + + + + false + + + + + com.atmel.avrdbg.tool.simulator + + + Simulator + + 0 + + + + + + -mmcu=attiny88 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\gcc\dev\attiny88" + True + True + True + True + False + True + True + + + NDEBUG + + + + + %24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\include + + + Optimize for size (-Os) + True + True + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\include + + + + + + + + + -mmcu=attiny88 -B "%24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\gcc\dev\attiny88" + True + True + True + True + False + True + True + + + DEBUG + + + + + %24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\include + + + Optimize for size (-Os) + -finline-functions + True + True + Default (-g2) + True + True + + + libm + + + + + %24(PackRepoDir)\atmel\ATtiny_DFP\1.3.172\include + + + Default (-Wa,-g) + + + + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + compile + + + + + + + \ No newline at end of file