From 766dc5d6a6ed5a5e1fc08a5e30216b9479e9f304 Mon Sep 17 00:00:00 2001 From: "kevin.larke" Date: Sun, 1 Sep 2019 13:11:21 -0400 Subject: [PATCH] Added earlier and extended versions of picadae/tiny/i2c_timer_pwn*.c in the folder picadae/tiny/versions --- control/tiny/versions/i2c_timer_pwm_0.c | 394 +++++++++++++++ control/tiny/versions/i2c_timer_pwm_1.c | 566 +++++++++++++++++++++ control/tiny/versions/i2c_timer_pwm_1a.c | 444 +++++++++++++++++ control/tiny/versions/i2c_timer_pwm_2.c | 502 +++++++++++++++++++ control/tiny/versions/i2c_timer_pwm_3.c | 609 +++++++++++++++++++++++ 5 files changed, 2515 insertions(+) create mode 100644 control/tiny/versions/i2c_timer_pwm_0.c create mode 100644 control/tiny/versions/i2c_timer_pwm_1.c create mode 100644 control/tiny/versions/i2c_timer_pwm_1a.c create mode 100644 control/tiny/versions/i2c_timer_pwm_2.c create mode 100644 control/tiny/versions/i2c_timer_pwm_3.c diff --git a/control/tiny/versions/i2c_timer_pwm_0.c b/control/tiny/versions/i2c_timer_pwm_0.c new file mode 100644 index 0000000..7adefe6 --- /dev/null +++ b/control/tiny/versions/i2c_timer_pwm_0.c @@ -0,0 +1,394 @@ +/* + AT TINY 85 + +--\/--+ + RESET _| 1 8 |_ +5V + HOLD DDB3 _| 2 7 |_ SCL + ONSET DDB4 _| 3 6 |_ DDB1 LED + GND _| 4 5 |_ SDA + +------+ + * = Serial and/or programming pins on Arduino as ISP +*/ + + +// This program acts as the device (slave) for the control program i2c/a2a/c_ctl +#define F_CPU 8000000L +#include +#include +#include +#include + +#include "usiTwiSlave.h" + +#define I2C_SLAVE_ADDRESS 0x8 // the 7-bit address (remember to change this when adapting this example) + + +enum +{ + kCS13_10_idx = 0, // Timer 1 Prescalar (CS13,CS12,CS11,CS10) from Table 12-5 pg 89 (0-15) prescaler = pow(2,val-1), 0=stop,1=1,2=2,3=4,4=8,....14=8192,15=16384 pre_scaled_hz = clock_hz/value + kTmr0_Coarse_idx = 1, // count of times timer0 count to 255 before OCR1C is set to Tmr0_Fine + kTmr0_Fine_idx = 2, // OCR1C timer match value + kPWM_Duty_idx = 3, // + kPWM_Freq_idx = 4, // 1-4 = clock divider=1=1,2=8,3=64,4=256,5=1024 + kTable_Addr_idx = 5, // Next table address to write + kTable_Coarse_idx= 6, // Next table coarse value to write + kTable_Fine_idx = 7, // Next table fine value to write + kMax_idx +}; + +volatile uint8_t ctl_regs[] = +{ + 9, // 0 9=32 us period w/ 8Mhz clock (timer tick rate) + 123, // 1 (0-255) Tmr0_Coarse count of times timer count to 255 before loading Tmr0_Minor for final count. + 8, // 2 (0-254) Tmr0_Fine OCR1C value on final phase before triggering timer + 127, // 3 (0-255) Duty cycle + 4, // 4 (1-4) PWM Frequency (clock pre-scaler) + 0, // 7 (0-127) Next table addr to read/write + 0, // 5 (0-255) Next table coarse value to write + 0, // 6 (0-255) Next table fine value to write +}; + +#define tableN 256 +uint8_t table[ tableN ]; + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// + +void EEPROM_write(uint8_t ucAddress, uint8_t ucData) +{ + // Wait for completion of previous write + while(EECR & (1< 0 ) + { + tmr_state = 1; + OCR1C = 254; + } + else + { + tmr_state = 2; + OCR1C = ctl_regs[kTmr0_Fine_idx]; + } + + tmr_coarse_cur = 0; +} + +ISR(TIMER1_OVF_vect) +{ + switch( tmr_state ) + { + case 0: + break; + + case 1: + if( ++tmr_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] ) + { + tmr_state = 2; + OCR1C = ctl_regs[kTmr0_Fine_idx]; + } + break; + + case 2: + PINB = _BV(PINB4) + _BV(PINB1); // writes to PINB toggle the pins + + tmr_reset(); + break; + } +} + +void timer1_init() +{ + TIMSK &= ~_BV(TOIE1); // Disable interrupt TIMER1_OVF + OCR1A = 255; // Set to anything greater than OCR1C (the counter never gets here.) + TCCR1 |= _BV(CTC1); // Reset TCNT1 to 0 when TCNT1==OCR1C + TCCR1 |= _BV(PWM1A); // Enable PWM A + TCCR1 |= ctl_regs[kCS13_10_idx] & 0x0f; // + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + tmr_reset(); + + TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// Tracks the current register pointer position +volatile uint8_t reg_position = 0; +const uint8_t reg_size = sizeof(ctl_regs); + +// +// Read Request Handler +// +// This is called for each read request we receive, never put more +// than one byte of data (with TinyWireS.send) to the send-buffer when +// using this callback +// +void on_request() +{ + uint8_t val = 0; + + switch( reg_position ) + { + case kTable_Coarse_idx: + val = table[ reg_position*2 + 0 ]; + break; + + case kTable_Fine_idx: + val = table[ reg_position*2 + 1 ]; + break; + + default: + // read and transmit the requestd position + val = ctl_regs[reg_position]; + + } + + usiTwiTransmitByte(val); + + // Increment the reg position on each read, and loop back to zero + reg_position++; + if (reg_position >= reg_size) + { + reg_position = 0; + } + +} + + +// +// The I2C data received -handler +// +// This needs to complete before the next incoming transaction (start, +// data, restart/stop) on the bus does so be quick, set flags for long +// running tasks to be called from the mainloop instead of running +// them directly, +// + +void on_receive( uint8_t byteN ) +{ + if (byteN < 1) + { + // Sanity-check + return; + } + if (byteN > TWI_RX_BUFFER_SIZE) + { + // Also insane number + return; + } + + // get the register index to read/write + reg_position = usiTwiReceiveByte(); + + byteN--; + + // If only one byte was received then this was a read request + // and the buffer pointer (reg_position) is now set to return the byte + // at this location on the subsequent call to on_request() ... + if (!byteN) + { + return; + } + + // ... otherwise this was a write request and the buffer + // pointer is now pointing to the first byte to write to + while(byteN--) + { + ctl_regs[reg_position] = usiTwiReceiveByte(); + + // Set timer + if( kCS13_10_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) + { timer1_init(); } + else + + // Set PWM + if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx ) + { pwm0_update(); } + else + + // Write table + if( reg_position == kTable_Fine_idx ) + { table_write(); } + + reg_position++; + + if (reg_position >= reg_size) + { + reg_position = 0; + } + + + } + + +} + + + +int main(void) +{ + cli(); // mask all interupts + + DDRB |= _BV(DDB4) + _BV(DDB1); // setup PB4 as output + PORTB &= ~(_BV(PINB4) + _BV(PINB1)); + + timer1_init(); + pwm0_init(); + + // setup i2c library + usi_onReceiverPtr = on_receive; //on_receive; + usi_onRequestPtr = on_request; + usiTwiSlaveInit(I2C_SLAVE_ADDRESS); + + sei(); + + PINB = _BV(PINB4); // writes to PINB toggle the pins + _delay_ms(1000); + PINB = _BV(PINB4); // writes to PINB toggle the pins + + + while(1) + { + //_delay_ms(1000); + + if (!usi_onReceiverPtr) + { + // no onReceive callback, nothing to do... + continue; + } + + if (!(USISR & ( 1 << USIPF ))) + { + // Stop not detected + continue; + } + + + uint8_t amount = usiTwiAmountDataInReceiveBuffer(); + if (amount == 0) + { + // no data in buffer + continue; + } + + + usi_onReceiverPtr(amount); + + + } + return 0; +} + + + diff --git a/control/tiny/versions/i2c_timer_pwm_1.c b/control/tiny/versions/i2c_timer_pwm_1.c new file mode 100644 index 0000000..e5dff94 --- /dev/null +++ b/control/tiny/versions/i2c_timer_pwm_1.c @@ -0,0 +1,566 @@ +/* + AT TINY 85 + +--\/--+ + RESET _| 1 8 |_ +5V + ~OC1B HOLD DDB3 _| 2 7 |_ SCL + OC1B ONSET DDB4 _| 3 6 |_ DDB1 LED + GND _| 4 5 |_ SDA + +------+ + * = Serial and/or programming pins on Arduino as ISP +*/ + + +// This program acts as the device (slave) for the control program i2c/a2a/c_ctl +#define F_CPU 8000000L +#include +#include +#include +#include + +#include "usiTwiSlave.h" + + +#define I2C_SLAVE_ADDRESS 0x8 // the 7-bit address (remember to change this when adapting this example) + + +enum +{ + kTmr0_Prescale_idx = 0, // Timer 0 clock divider: 1=1,2=8,3=64,4=256,5=1024 + kTmr0_Coarse_idx = 1, // + kTmr0_Fine_idx = 2, // + kPWM0_Duty_idx = 3, // + kPWM0_Freq_idx = 4, // 1-4 = clock divider=1=1,2=8,3=64,4=256,5=1024 + kCS13_10_idx = 5, // Timer 1 Prescalar (CS13,CS12,CS11,CS10) from Table 12-5 pg 89 (0-15) prescaler = pow(2,val-1), 0=stop,1=1,2=2,3=4,4=8,....14=8192,15=16384 pre_scaled_hz = clock_hz/value + kTmr1_Coarse_idx = 6, // count of times timer0 count to 255 before OCR1C is set to Tmr0_Fine + kTmr1_Fine_idx = 7, // OCR1C timer match value + kPWM1_Duty_idx = 8, // + kPWM1_Freq_idx = 9, // + kTable_Addr_idx = 10, // Next table address to read/write + kTable_Coarse_idx = 11, // Next table coarse value to read/write + kTable_Fine_idx = 12, // Next table fine value to read/write + kMax_idx +}; + + +volatile uint8_t ctl_regs[] = +{ + 4, // 0 (1-5) 4=32us per tick + 123, // 1 (0-255) Timer 0 Coarse Value + 8, // 2 (0-255) Timer 0 Fine Value + 127, // 3 (0-255) Duty cycle + 4, // 4 (1-4) PWM Frequency (clock pre-scaler) + 9, // 5 9=32 us period w/ 8Mhz clock (timer tick rate) + 123, // 6 (0-255) Tmr1_Coarse count of times timer count to 255 before loading Tmr0_Minor for final count. + 8, // 7 (0-254) Tmr1_Fine OCR1C value on final phase before triggering timer + 127, // 8 (0-255) PWM1 Duty cycle + 254, // 9 (0-255) PWM1 Frequency (123 hz) + 0, // 10 (0-127) Next table addr to read/write + 0, // 11 (0-255) Next table coarse value to write + 0, // 12 (0-255) Next table fine value to write +}; + +#define tableN 256 +uint8_t table[ tableN ]; + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// + +void EEPROM_write(uint8_t ucAddress, uint8_t ucData) +{ + // Wait for completion of previous write + while(EECR & (1< 127 +// r 8 kTable_Fine_idx -> 64 + + +#define eeprom_addr( addr ) (kMax_idx + (addr)) + +void table_write_cur_value( void ) +{ + uint8_t tbl_addr = ctl_regs[ kTable_Addr_idx ] * 2; + + table[ tbl_addr+0 ] = ctl_regs[ kTable_Coarse_idx ]; + table[ tbl_addr+1 ] = ctl_regs[ kTable_Fine_idx ]; + + EEPROM_write( eeprom_addr( tbl_addr+0 ), ctl_regs[ kTable_Coarse_idx ] ); + EEPROM_write( eeprom_addr( tbl_addr+1 ), ctl_regs[ kTable_Fine_idx ]); +} + +void table_load( void ) +{ + uint8_t i = 0; + + for(; i<128; ++i) + { + uint8_t tbl_addr = i*2; + table[tbl_addr+0] = EEPROM_read( eeprom_addr(tbl_addr+0) ); + table[tbl_addr+1] = EEPROM_read( eeprom_addr(tbl_addr+1) ); + } +} + +void restore_memory_from_eeprom( void ) +{ + /* + uint8_t i; + for(i=0; i 0 ) + { + tmr0_state = 1; + OCR0A = 0xff; + } + else // otherwise go into fine mode + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; + } + + tmr0_coarse_cur = 0; +} + +ISR(TIMER0_COMPA_vect) +{ + switch( tmr0_state ) + { + case 0: + // disabled + break; + + case 1: + // coarse mode + if( ++tmr0_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] ) + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; + } + break; + + case 2: + // fine mode + PINB = _BV(PINB4); // writes to PINB toggle the pins + + tmr0_reset(); // restart the timer + break; + } +} + + +void timer0_init() +{ + TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER1_OVF + TCCR0A |= 0x02; // CTC mode + TCCR0B |= ctl_regs[kTmr0_Prescale_idx]; // set the prescaler + + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value + + tmr0_reset(); // set the timers starting state + + TIMSK |= _BV(OCIE0A); // Enable interrupt TIMER1_OVF + +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// PWM (Timer0) +// + +void pwm0_update() +{ + OCR0B = ctl_regs[kPWM0_Duty_idx]; // 50% duty cycle + TCCR0B |= ctl_regs[kPWM0_Freq_idx]; // PWM frequency pre-scaler +} + +void pwm0_init() +{ + // WGM[1:0] = 3 (TOP=255) + // OCR0B = duty cycle (0-100%) + // COM0A[1:0] = 2 non-inverted + // + + TCCR0A |= 0x20 + 3; // 0x20=non-inverting 3=WGM bits Fast-PWM mode (0=Bot 255=Top) + TCCR0B |= 0x00 + 4; // 3=256 pre-scaler 122Hz=1Mghz/(v*256) where v=64 + + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value + + pwm0_update(); + + + DDRB |= _BV(DDB1); // set direction on +} + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// Timer1 +// + +volatile uint8_t tmr1_state = 0; +volatile uint8_t tmr1_coarse_cur = 0; +static uint8_t tmr1_init_fl = 0; + +void tmr1_reset() +{ + if( ctl_regs[kTmr1_Coarse_idx] > 0 ) + { + tmr1_state = 1; + OCR1C = 254; + } + else + { + tmr1_state = 2; + OCR1C = ctl_regs[kTmr1_Fine_idx]; + } + + tmr1_coarse_cur = 0; +} + +ISR(TIMER1_OVF_vect) +{ + if( !tmr1_init_fl ) + { + PORTB |= _BV(PINB3); // set PWM pin + } + else + { + switch( tmr1_state ) + { + + case 0: + // disabled + break; + + case 1: + // coarse mode + if( ++tmr1_coarse_cur >= ctl_regs[kTmr1_Coarse_idx] ) + { + tmr1_state = 2; + OCR1C = ctl_regs[kTmr1_Fine_idx]; + } + break; + + case 2: + // fine mode + PINB = _BV(PINB4); // writes to PINB toggle the pins + + tmr1_reset(); + break; + } + } +} + +void timer1_init() +{ + TIMSK &= ~_BV(TOIE1); // Disable interrupt TIMER1_OVF + OCR1A = 255; // Set to anything greater than OCR1C (the counter never gets here.) + TCCR1 |= _BV(CTC1); // Reset TCNT1 to 0 when TCNT1==OCR1C + TCCR1 |= _BV(PWM1A); // Enable PWM A (to generate overflow interrupts) + TCCR1 |= ctl_regs[kCS13_10_idx] & 0x0f; // + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + tmr1_reset(); + tmr1_init_fl = 1; + TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// PWM1 +// +// PWM is optimized to use pins OC1A ,~OC1A, OC1B, ~OC1B but this code +// but since these pins are not available this code uses +// ISR's to redirect the output to PIN3 + +void pwm1_update() +{ + OCR1B = ctl_regs[kPWM1_Duty_idx]; // control duty cycle + OCR1C = ctl_regs[kPWM1_Freq_idx]; // PWM frequency pre-scaler +} + +ISR(TIMER1_COMPB_vect) +{ + PORTB &= ~(_BV(PINB3)); // clear PWM pin +} + + +void pwm1_init() +{ + TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // Disable interrupts + + DDRB |= _BV(DDB3); // setup PB3 as output + + // set on TCNT1 == 0 // happens when TCNT1 matches OCR1C + // clr on OCR1B == TCNT // happens when TCNT1 matches OCR1B + // // COM1B1=1 COM1B0=0 (enable output on ~OC1B) + TCCR1 |= 9; // 32us period (256 divider) prescaler + GTCCR |= _BV(PWM1B); // Enable PWM B and disconnect output pins + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + pwm1_update(); + + TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // Enable interrupts + + + +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// Tracks the current register pointer position +volatile uint8_t reg_position = 0; +const uint8_t reg_size = sizeof(ctl_regs); + +// +// Read Request Handler +// +// This is called for each read request we receive, never put more +// than one byte of data (with TinyWireS.send) to the send-buffer when +// using this callback +// +void on_request() +{ + uint8_t val = 0; + + switch( reg_position ) + { + case kTable_Coarse_idx: + val = table[ ctl_regs[kTable_Addr_idx]*2 + 0 ]; + break; + + case kTable_Fine_idx: + val = table[ ctl_regs[kTable_Addr_idx]*2 + 1 ]; + break; + + default: + // read and transmit the requestd position + val = ctl_regs[reg_position]; + + } + + usiTwiTransmitByte(val); + + // Increment the reg position on each read, and loop back to zero + reg_position++; + if (reg_position >= reg_size) + { + reg_position = 0; + } + +} + + +// +// The I2C data received -handler +// +// This needs to complete before the next incoming transaction (start, +// data, restart/stop) on the bus does so be quick, set flags for long +// running tasks to be called from the mainloop instead of running +// them directly, +// + +void on_receive( uint8_t byteN ) +{ + if (byteN < 1) + { + // Sanity-check + return; + } + if (byteN > TWI_RX_BUFFER_SIZE) + { + // Also insane number + return; + } + + // get the register index to read/write + reg_position = usiTwiReceiveByte(); + + byteN--; + + // If only one byte was received then this was a read request + // and the buffer pointer (reg_position) is now set to return the byte + // at this location on the subsequent call to on_request() ... + if (!byteN) + { + return; + } + + // ... otherwise this was a write request and the buffer + // pointer is now pointing to the first byte to write to + while(byteN--) + { + // write the value + ctl_regs[reg_position] = usiTwiReceiveByte(); + + // Set timer 1 + if( kTmr0_Prescale_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) + { timer0_init(); } + else + + + // Set PWM 0 + if( kPWM0_Duty_idx <= reg_position && reg_position <= kPWM0_Freq_idx ) + { pwm0_update(); } + else + + // Set timer 1 + if( kCS13_10_idx <= reg_position && reg_position <= kTmr1_Fine_idx ) + { timer1_init(); } + else + + // Set PWM 1 + if( kPWM1_Duty_idx <= reg_position && reg_position <= kPWM1_Freq_idx ) + { pwm1_update(); } + else + + + // Write table + if( reg_position == kTable_Fine_idx ) + { table_write_cur_value(); } + + reg_position++; + + if (reg_position >= reg_size) + { + reg_position = 0; + } + + + } + + +} + + + +int main(void) +{ + cli(); // mask all interupts + + + restore_memory_from_eeprom(); + + DDRB |= _BV(DDB4) + _BV(DDB3) + _BV(DDB1); // setup PB4,PB3,PB1 as output + PORTB &= ~(_BV(PINB4) + _BV(PINB3) + _BV(PINB1)); // clear output pins + + + timer0_init(); + pwm1_init(); + + // setup i2c library + usi_onReceiverPtr = on_receive; + usi_onRequestPtr = on_request; + usiTwiSlaveInit(I2C_SLAVE_ADDRESS); + + sei(); + + PINB = _BV(PINB4); // writes to PINB toggle the pins + _delay_ms(1000); + PINB = _BV(PINB4); // writes to PINB toggle the pins + + + while(1) + { + //_delay_ms(1000); + + if (!usi_onReceiverPtr) + { + // no onReceive callback, nothing to do... + continue; + } + + if (!(USISR & ( 1 << USIPF ))) + { + // Stop not detected + continue; + } + + + uint8_t amount = usiTwiAmountDataInReceiveBuffer(); + if (amount == 0) + { + // no data in buffer + continue; + } + + + usi_onReceiverPtr(amount); + + + } + return 0; +} + + + diff --git a/control/tiny/versions/i2c_timer_pwm_1a.c b/control/tiny/versions/i2c_timer_pwm_1a.c new file mode 100644 index 0000000..e3dacd8 --- /dev/null +++ b/control/tiny/versions/i2c_timer_pwm_1a.c @@ -0,0 +1,444 @@ +/* + AT TINY 85 + +--\/--+ + RESET _| 1 8 |_ +5V + ~OC1B HOLD DDB3 _| 2 7 |_ SCL + OC1B ONSET DDB4 _| 3 6 |_ DDB1 LED + GND _| 4 5 |_ SDA + +------+ + * = Serial and/or programming pins on Arduino as ISP +*/ + + +// This program acts as the device (slave) for the control program i2c/a2a/c_ctl +#define F_CPU 8000000L +#include +#include +#include +#include + +#include "usiTwiSlave.h" + + +enum +{ + kTmr0_Prescale_idx = 0, // Timer 0 clock divider: 1=1,2=8,3=64,4=256,5=1024 + kTmr0_Coarse_idx = 1, // + kTmr0_Fine_idx = 2, // + kPWM1_Duty_idx = 3, // + kPWM1_Freq_idx = 4, // + kTable_Addr_idx = 5, // Next table address to read/write + kTable_Coarse_idx = 6, // Next table coarse value to read/write + kTable_Fine_idx = 7, // Next table fine value to read/write + kMax_idx +}; + + +volatile uint8_t ctl_regs[] = +{ + 4, // 0 (1-5) 4=32us per tick + 123, // 1 (0-255) Timer 0 Coarse Value + 8, // 2 (0-255) Timer 0 Fine Value + 127, // 3 (0-255) PWM1 Duty cycle + 254, // 4 (0-255) PWM1 Frequency (123 hz) + 0, // 5 (0-127) Next table addr to read/write + 0, // 6 (0-255) Next table coarse value to write + 0, // 7 (0-255) Next table fine value to write +}; + +#define tableN 256 +uint8_t table[ tableN ]; + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// + +void EEPROM_write(uint8_t ucAddress, uint8_t ucData) +{ + // Wait for completion of previous write + while(EECR & (1< 127 +// r 8 kTable_Fine_idx -> 64 + + +#define eeprom_addr( addr ) (kMax_idx + (addr)) + +void table_write_cur_value( void ) +{ + uint8_t tbl_addr = ctl_regs[ kTable_Addr_idx ] * 2; + + table[ tbl_addr+0 ] = ctl_regs[ kTable_Coarse_idx ]; + table[ tbl_addr+1 ] = ctl_regs[ kTable_Fine_idx ]; + + EEPROM_write( eeprom_addr( tbl_addr+0 ), ctl_regs[ kTable_Coarse_idx ] ); + EEPROM_write( eeprom_addr( tbl_addr+1 ), ctl_regs[ kTable_Fine_idx ]); +} + +void table_load( void ) +{ + uint8_t i = 0; + + for(; i<128; ++i) + { + uint8_t tbl_addr = i*2; + table[tbl_addr+0] = EEPROM_read( eeprom_addr(tbl_addr+0) ); + table[tbl_addr+1] = EEPROM_read( eeprom_addr(tbl_addr+1) ); + } +} + +void restore_memory_from_eeprom( void ) +{ + /* + uint8_t i; + for(i=0; i 0 ) + { + tmr0_state = 1; + OCR0A = 0xff; + } + else // otherwise go into fine mode + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; + } + + tmr0_coarse_cur = 0; +} + +ISR(TIMER0_COMPA_vect) +{ + switch( tmr0_state ) + { + case 0: + // disabled + break; + + case 1: + // coarse mode + if( ++tmr0_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] ) + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; + } + break; + + case 2: + // fine mode + PINB = _BV(PINB4); // writes to PINB toggle the pins + + tmr0_reset(); // restart the timer + break; + } +} + + +void timer0_init() +{ + TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER1_OVF + TCCR0A |= 0x02; // CTC mode + TCCR0B |= ctl_regs[kTmr0_Prescale_idx]; // set the prescaler + + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value + + tmr0_reset(); // set the timers starting state + + TIMSK |= _BV(OCIE0A); // Enable interrupt TIMER1_OVF + +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// PWM1 +// +// PWM is optimized to use pins OC1A ,~OC1A, OC1B, ~OC1B but this code +// but since these pins are not available this code uses +// ISR's to redirect the output to PIN3 + +void pwm1_update() +{ + OCR1B = ctl_regs[kPWM1_Duty_idx]; // control duty cycle + OCR1C = ctl_regs[kPWM1_Freq_idx]; // PWM frequency pre-scaler +} + + + +ISR(TIMER1_OVF_vect) +{ + PORTB |= _BV(PINB3); // set PWM pin +} + +ISR(TIMER1_COMPB_vect) +{ + PORTB &= ~(_BV(PINB3)); // clear PWM pin +} + + +void pwm1_init() +{ + TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // Disable interrupts + + DDRB |= _BV(DDB3); // setup PB3 as output + + // set on TCNT1 == 0 // happens when TCNT1 matches OCR1C + // clr on OCR1B == TCNT // happens when TCNT1 matches OCR1B + // // COM1B1=1 COM1B0=0 (enable output on ~OC1B) + TCCR1 |= 9; // 32us period (256 divider) prescaler + GTCCR |= _BV(PWM1B); // Enable PWM B and disconnect output pins + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + pwm1_update(); + + TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // Enable interrupts + + + +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// Tracks the current register pointer position +volatile uint8_t reg_position = 0; +const uint8_t reg_size = sizeof(ctl_regs); + +// +// Read Request Handler +// +// This is called for each read request we receive, never put more +// than one byte of data (with TinyWireS.send) to the send-buffer when +// using this callback +// +void on_request() +{ + uint8_t val = 0; + + switch( reg_position ) + { + case kTable_Coarse_idx: + val = table[ ctl_regs[kTable_Addr_idx]*2 + 0 ]; + break; + + case kTable_Fine_idx: + val = table[ ctl_regs[kTable_Addr_idx]*2 + 1 ]; + break; + + default: + // read and transmit the requestd position + val = ctl_regs[reg_position]; + + } + + usiTwiTransmitByte(val); + + // Increment the reg position on each read, and loop back to zero + reg_position++; + if (reg_position >= reg_size) + { + reg_position = 0; + } + +} + + +// +// The I2C data received -handler +// +// This needs to complete before the next incoming transaction (start, +// data, restart/stop) on the bus does so be quick, set flags for long +// running tasks to be called from the mainloop instead of running +// them directly, +// + +void on_receive( uint8_t byteN ) +{ + PINB = _BV(PINB1); // writes to PINB toggle the pins + + if (byteN < 1) + { + // Sanity-check + return; + } + if (byteN > TWI_RX_BUFFER_SIZE) + { + // Also insane number + return; + } + + // get the register index to read/write + reg_position = usiTwiReceiveByte(); + + byteN--; + + // If only one byte was received then this was a read request + // and the buffer pointer (reg_position) is now set to return the byte + // at this location on the subsequent call to on_request() ... + if (!byteN) + { + return; + } + + // ... otherwise this was a write request and the buffer + // pointer is now pointing to the first byte to write to + while(byteN--) + { + // write the value + ctl_regs[reg_position] = usiTwiReceiveByte(); + + // Set timer 1 + if( kTmr0_Prescale_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) + { timer0_init(); } + else + + + // Set PWM 1 + if( kPWM1_Duty_idx <= reg_position && reg_position <= kPWM1_Freq_idx ) + { pwm1_update(); } + else + + + // Write table + if( reg_position == kTable_Fine_idx ) + { table_write_cur_value(); } + + reg_position++; + + if (reg_position >= reg_size) + { + reg_position = 0; + } + + + } + + +} + + + +int main(void) +{ + cli(); // mask all interupts + + + restore_memory_from_eeprom(); + + DDRB |= _BV(DDB4) + _BV(DDB3) + _BV(DDB1); // setup PB4,PB3,PB1 as output + PORTB &= ~(_BV(PINB4) + _BV(PINB3) + _BV(PINB1)); // clear output pins + + + timer0_init(); + pwm1_init(); + + // setup i2c library + usi_onReceiverPtr = on_receive; + usi_onRequestPtr = on_request; + usiTwiSlaveInit(I2C_SLAVE_ADDRESS); + + sei(); + + PINB = _BV(PINB1); // writes to PINB toggle the pins + _delay_ms(1000); + PINB = _BV(PINB1); // writes to PINB toggle the pins + + + while(1) + { + //_delay_ms(1000); + + if (!usi_onReceiverPtr) + { + // no onReceive callback, nothing to do... + continue; + } + + if (!(USISR & ( 1 << USIPF ))) + { + // Stop not detected + continue; + } + + + uint8_t amount = usiTwiAmountDataInReceiveBuffer(); + if (amount == 0) + { + // no data in buffer + continue; + } + + + usi_onReceiverPtr(amount); + + + } + return 0; +} + + + diff --git a/control/tiny/versions/i2c_timer_pwm_2.c b/control/tiny/versions/i2c_timer_pwm_2.c new file mode 100644 index 0000000..0ba9683 --- /dev/null +++ b/control/tiny/versions/i2c_timer_pwm_2.c @@ -0,0 +1,502 @@ +/* + AT TINY 85 + +--\/--+ + RESET _| 1 8 |_ +5V + ~OC1B HOLD DDB3 _| 2 7 |_ SCL + OC1B ONSET DDB4 _| 3 6 |_ DDB1 LED + GND _| 4 5 |_ SDA + +------+ + * = Serial and/or programming pins on Arduino as ISP +*/ + + +// This program acts as the device (slave) for the control program i2c/a2a/c_ctl + +#define F_CPU 8000000L +#include +#include +#include +#include + +#include "usiTwiSlave.h" + +#define HOLD_DIR DDB3 +#define ATTK_DIR DDB4 +#define LED_DIR DDB1 + +#define HOLD_PIN PINB3 +#define ATTK_PIN PINB4 +#define LED_PIN PINB1 + +// Opcodes +enum +{ + kSetReg_Op = 0, // Set register ... + kSetPwm_Op = 1, // Set PWM registers + kNoteOnVel_Op = 2, // Turn on note + kNoteOnUsec_Op = 3, // Turn on note + kNoteOff_Op = 4, // Turn off note + kRead_Op = 5, // Read a value {{ } } + kInvalid_Op = 6 +}; + + +// Register addresses +enum +{ + kTmr_Coarse_idx = 0, // Current Timer 0 coarse count + kTmr_Fine_idx = 1, // Current Timer 0 fine count + kTmr_Prescale_idx = 2, // Current Timer 0 clock divider: 1=1,2=8,3=64,4=256,5=1024 + kPwm_Enable_idx = 3, // Current PWM 1 enable flag + kPwm_Duty_idx = 4, // Current PWM 1 duty cycle + kPwm_Freq_idx = 5, // Current PWM 1 frequency + kRead_Src_idx = 6, // 0=reg, 1=table, 2=eeprom + kReg_Addr_idx = 7, // Next Reg Address to read + kTable_Addr_idx = 8, // Next Table Address to read + kEE_Addr_idx = 9, // Next EEPROM address to read + kError_Code_idx = 10, // Error Code + kMax_idx +}; + +// Regster memory +volatile uint8_t ctl_regs[] = +{ + + 123, // 1 (0-255) Timer 0 Coarse Value + 8, // 2 (0-255) Timer 0 Fine Value + 4, // 0 (1-5) 4=32us per tick + + 1, // 5 (0-1) PWM1 Enable + 127, // 3 (0-255) PWM1 Duty cycle (0-100%) + 254, // 4 (0-255) PWM1 Frequency (123 hz) + + 0, // 6 (0-255) Read Source + 0, // 7 (0-255) Reg addr + 0, // 8 (0-255) Table addr + 0, // 9 (0-255) EEPROM addr + + 0, // 10 (0-255) Error code + +}; + +volatile uint8_t table[128]; + + + +#define stackN 16 +volatile uint8_t stack[ stackN ]; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// + +void EEPROM_write(uint8_t ucAddress, uint8_t ucData) +{ + // Wait for completion of previous write + while(EECR & (1< 0 ) + { + tmr0_state = 1; + OCR0A = 0xff; + } + else // otherwise go into fine mode + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr_Fine_idx]; + } + + tmr0_coarse_cur = 0; +} + +ISR(TIMER0_COMPA_vect) +{ + switch( tmr0_state ) + { + case 0: + // disabled + break; + + case 1: + // coarse mode + if( ++tmr0_coarse_cur >= ctl_regs[kTmr_Coarse_idx] ) + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr_Fine_idx]; + } + break; + + case 2: + // fine mode + PINB = _BV(ATTK_PIN); // writes to PINB toggle the pins + + tmr0_reset(); // restart the timer + break; + } +} + + +void timer0_init() +{ + TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER0_COMPA_vect + TCCR0A |= 0x02; // CTC mode + TCCR0B |= ctl_regs[kTmr_Prescale_idx]; // set the prescaler + + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value + + tmr0_reset(); // set the timers starting state + + TIMSK |= _BV(OCIE0A); // Enable interrupt TIMER0_COMPA_vect + +} + + + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// PWM1 +// +// PWM is optimized to use pins OC1A ,~OC1A, OC1B, ~OC1B but this code +// but since these pins are not available this code uses +// ISR's to redirect the output to PIN3 + +void pwm1_update() +{ + OCR1B = ctl_regs[kPwm_Duty_idx]; // control duty cycle + OCR1C = ctl_regs[kPwm_Freq_idx]; // PWM frequency pre-scaler +} + +ISR(TIMER1_OVF_vect) +{ + PORTB |= _BV(HOLD_PIN); // set PWM pin +} + +ISR(TIMER1_COMPB_vect) +{ + PORTB &= ~(_BV(HOLD_PIN)); // clear PWM pin +} + + +void pwm1_init() +{ + TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // Disable interrupts + + DDRB |= _BV(HOLD_DIR); // setup PB3 as output + + // set on TCNT1 == 0 // happens when TCNT1 matches OCR1C + // clr on OCR1B == TCNT // happens when TCNT1 matches OCR1B + // // COM1B1=1 COM1B0=0 (enable output on ~OC1B) + TCCR1 |= 9; // 32us period (256 divider) prescaler + GTCCR |= _BV(PWM1B); // Enable PWM B and disconnect output pins + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + pwm1_update(); + + TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // Enable interrupts + + + +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +// +// Read Request Handler +// +// This is called for each read request we receive, never put more +// than one byte of data (with TinyWireS.send) to the send-buffer when +// using this callback +// + +void on_request() +{ + uint8_t val = 0; + + switch( ctl_regs[ kRead_Src_idx ] ) + { + case 0: + val = table[ ctl_regs[ kReg_Addr_idx ] ]; + ctl_regs[ kReg_Addr_idx ] += 1; + break; + + case 1: + val = table[ ctl_regs[ kTable_Addr_idx ] ]; + ctl_regs[ kTable_Addr_idx ] += 1; + break; + + case 2: + val = EEPROM_read(ctl_regs[ kEE_Addr_idx]); + ctl_regs[ kEE_Addr_idx ] += 1; + } + + usiTwiTransmitByte(val); + +} + + +// +// The I2C data received -handler +// +// This needs to complete before the next incoming transaction (start, +// data, restart/stop) on the bus does so be quick, set flags for long +// running tasks to be called from the mainloop instead of running +// them directly, +// + +void on_receive( uint8_t byteN ) +{ + uint8_t stack_idx = 0; + + PINB = _BV(LED_PIN); // writes to PINB toggle the pins + + // Sanity-check + if( byteN < 1 || byteN > TWI_RX_BUFFER_SIZE) + { + // TODO: signal an error + return; + } + + // get the command byte + uint8_t cur_op_id = usiTwiReceiveByte(); + + --byteN; + + // verify that cur_op_id is valid + if( cur_op_id < kInvalid_Op ) + { + // TODO: signal an error + return; + } + + // get the command arguments + while(byteN--) + { + // write the value + stack[stack_idx] = usiTwiReceiveByte(); + + ++stack_idx; + + if(stack_idx >= stackN) + { + // TODO: signal an error + break; + } + } + + // execute the operation + switch( cur_op_id ) + { + case kSetReg_Op: // Set register ... + if( stack_idx > 1 ) + { + uint8_t addr = stack[0]; + uint8_t i = 2; + for(; i,, + { + uint8_t addr = kPwm_Enable_idx; + uint8_t i = 0; + for(; i + if( stack_idx == 1 ) + { + uint8_t addr = stack[0] >> 2; // divide by 2 (we have only 64 entries in the table) + + ctl_regs[ kTmr_Coarse_idx ] = table[ addr ]; + ctl_regs[ kTmr_Fine_idx ] = table[ addr+1 ]; + } + tmr0_reset(); + break; + + case kNoteOnUsec_Op: // Turn on note + if( stack_idx == 2 ) + { + ctl_regs[ kTmr_Coarse_idx ] = stack[0]; + ctl_regs[ kTmr_Fine_idx ] = stack[1]; + } + tmr0_reset(); + break; + + case kNoteOff_Op: // Turn off note + PORTB &= ~(_BV(ATTK_PIN) + _BV(HOLD_PIN)); + break; + + case kRead_Op: // Read a value {{ } } + if( stack_idx > 0) + { + ctl_regs[ kRead_Src_idx ] = stack[0]; + } + + if( stack_idx > 1 ) + { + uint8_t reg_addr = 4; + + switch( ctl_regs[ kRead_Src_idx ] ) + { + case 0: reg_addr = kReg_Addr_idx; break; + case 1: reg_addr = kTable_Addr_idx; break; + case 2: reg_addr = kEE_Addr_idx; break; + default: + // TODO: signal error + break; + } + + if( reg_addr <= 2 ) + ctl_regs[ reg_addr ] = stack[1]; + + + } + } +} + + + +int main(void) +{ + cli(); // mask all interupts + + + DDRB |= _BV(ATTK_DIR) + _BV(HOLD_DIR) + _BV(LED_DIR); // setup PB4,PB3,PB1 as output + PORTB &= ~(_BV(ATTK_PIN) + _BV(HOLD_PIN) + _BV(LED_PIN)); // clear output pins + + + timer0_init(); + pwm1_init(); + + // setup i2c library + usi_onReceiverPtr = on_receive; + usi_onRequestPtr = on_request; + usiTwiSlaveInit(I2C_SLAVE_ADDRESS); + + sei(); + + PINB = _BV(LED_PIN); // writes to PINB toggle the pins + _delay_ms(1000); + PINB = _BV(LED_PIN); // writes to PINB toggle the pins + + + while(1) + { + //_delay_ms(1000); + + if (!usi_onReceiverPtr) + { + // no onReceive callback, nothing to do... + continue; + } + + if (!(USISR & ( 1 << USIPF ))) + { + // Stop not detected + continue; + } + + + uint8_t amount = usiTwiAmountDataInReceiveBuffer(); + if (amount == 0) + { + // no data in buffer + continue; + } + + + usi_onReceiverPtr(amount); + + + } + return 0; +} + + + diff --git a/control/tiny/versions/i2c_timer_pwm_3.c b/control/tiny/versions/i2c_timer_pwm_3.c new file mode 100644 index 0000000..436764e --- /dev/null +++ b/control/tiny/versions/i2c_timer_pwm_3.c @@ -0,0 +1,609 @@ +// w 60 0 1 10 : w i2c_addr SetPWM enable duty_val +// w 60 5 12 8 32 : w i2c_addr write addrFl|src coarse_val +// w 60 4 0 5 : w i2c_addr read src read_addr (set the read address to register 5) +// r 60 4 3 : r i2c_addr cnt (read the first 3 reg's beginning w/ 5) +/* + AT TINY 85 + +--\/--+ + RESET _| 1 8 |_ +5V + ~OC1B HOLD DDB3 _| 2 7 |_ SCL yellow + OC1B ONSET DDB4 _| 3 6 |_ DDB1 LED + GND _| 4 5 |_ SDA orange + +------+ + * = Serial and/or programming pins on Arduino as ISP +*/ + + +// This program acts as the device (slave) for the control program i2c/a2a/c_ctl +#define F_CPU 16000000L + +#include +#include +#include +#include + +#include "usiTwiSlave.h" + +#define HOLD_DIR DDB3 +#define ATTK_DIR DDB4 +#define LED_DIR DDB1 + +#define HOLD_PIN PINB3 +#define ATTK_PIN PINB4 +#define LED_PIN PINB1 + +// Opcodes +enum +{ + kSetPwm_Op = 0, // Set PWM duty/hz/div 0 { { {
}}} div:2=2,3=4,4=8,5=16,6=32,7=64,8=128,9=256,10=512,11=1024,12=2048,13=4096,14=8192,15=16384 + kNoteOnVel_Op = 1, // Turn on note 3 {} + kNoteOnUsec_Op = 2, // Turn on note 4 { { {}}} + kNoteOff_Op = 3, // Turn off note 5 + kSetReadAddr_Op = 4, // Set a read addr. 6 {} {} } src: 0=reg 1=table 2=eeprom + kWrite_Op = 5, // Set write 7 { {addr} { ... {}} addrFl:0x80 src: 4=reg 5=table 6=eeprom + kSetMode_Op = 6, // Set the mode flags 8 {} 1=repeat 2=pwm + kWriteTable_Op = 7, // Write table to EEprom 9 + kInvalid_Op = 8 // +}; + + +enum +{ + kReg_Rd_Addr_idx = 0, // Next Reg Address to read + kTable_Rd_Addr_idx = 1, // Next Table Address to read + kEE_Rd_Addr_idx = 2, // Next EEPROM address to read + kRead_Src_idx = 3, // kReg_Rd_Addr_idx=reg, kTable_Rd_Addr_idx=table, kEE_Rd_Addr_idx=eeprom + + kReg_Wr_Addr_idx = 4, // Next Reg Address to write + kTable_Wr_Addr_idx = 5, // Next Table Address to write + kEE_Wr_Addr_idx = 6, // Next EEPROM address to write + kWrite_Dst_idx = 7, // kReg_Wr_Addr_idx=reg, kTable_Wr_Addr_idx=table, kEE_Wr_Addr_idx=eeprom + + kTmr_Coarse_idx = 8, // + kTmr_Fine_idx = 9, // + kTmr_Prescale_idx = 10, // Timer 0 clock divider: 1=1,2=8,3=64,4=256,5=1024 Default: 4 (16us) + + kPwm_Duty_idx = 11, // + kPwm_Freq_idx = 12, // + kPwm_Div_idx = 13, // + + kMode_idx = 14, // 1=repeat 2=pwm + kState_idx = 15, // 1=attk 2=hold + kError_Code_idx = 16, // Error Code + kMax_idx +}; + +enum +{ + kMode_Repeat_Fl = 1, + kMode_Pwm_Fl = 2, + kState_Attk_Fl = 1, + kState_Hold_Fl = 2 +}; + + +#define isInRepeatMode() ctl_regs[ kMode_idx ] & kMode_Repeat_Fl +#define isInPwmMode() ctl_regs[ kMode_idx ] & kMode_Pwm_Fl + +// Flags: +// 1=Repeat: 1=Timer and PWM are free running. This allows testing with LED's. 0=Timer triggers does not reset on time out. +// 2=PWM: On timer timeout 1=PWM HOLD 0=Set HOLD + +volatile uint8_t ctl_regs[] = +{ + 0, // 0 (0-(kMax_idx-1)) Reg Read Addr + 0, // 1 (0-255) Table Read Addr + 0, // 2 (0-255) EE Read Addr + kReg_Rd_Addr_idx, // 3 (0-2) Read source + + 0, // 4 (0-(kMax_idx-1)) Reg Write Addr + 0, // 5 (0-255) Table Write Addr + 0, // 6 (0-255) EE Write Addr + kReg_Wr_Addr_idx, // 7 (0-2) Write source + + 245, // 8 (0-255) Timer 0 Coarse Value + 25, // 9 (0-255) Timer 0 Fine Value + 4, // 10 (1-5) 4=16us per tick + + 127, // 11 (0-255) Pwm Duty cycle + 254, // 12 (0-255) Pwm Frequency (123 Hz) + 10, // 13 (0-15) Pwm clock div + + kMode_Repeat_Fl, // 14 mode flags 1=Repeat 2=PWM + 0, // 15 state flags 1=attk 2=hold (read/only) + 0, // 16 (0-255) Error bit field +}; + +// These registers are saved to Eeprom +uint8_t eeprom_addr[] = +{ + kTmr_Prescale_idx, + kPwm_Duty_idx, + kPwm_Freq_idx, + kPwm_Div_idx +}; + + + +#define tableN 256 +uint8_t table[ tableN ]; // [ coarse_0,fine_0, coarse_1, fine_1, .... coarse_127,fine_127] + + +enum +{ + kInvalid_Read_Src_ErrFl = 0x01, + kInvalid_Write_Dst_ErrFl = 0x02 +}; + +#define set_error( flag ) ctl_regs[ kError_Code_idx ] |= (flag) + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// + +void EEPROM_write(uint8_t ucAddress, uint8_t ucData) +{ + // Wait for completion of previous write + while(EECR & (1< 0 ) + { + tmr0_state = 1; + OCR0A = 0xff; + } + else // otherwise go into fine mode + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr_Fine_idx]; + } + + TIMSK |= _BV(OCIE0A); // enable the timer interrupt +} + +ISR(TIMER0_COMPA_vect) +{ + switch( tmr0_state ) + { + case 0: + // timer is disabled + break; + + case 1: + // coarse mode + if( ++tmr0_coarse_cur >= ctl_regs[kTmr_Coarse_idx] ) + { + tmr0_state = 2; + OCR0A = ctl_regs[kTmr_Fine_idx]; + } + break; + + case 2: + // fine mode + + // This marks the end of a timer period + + // If in repeat mode ... + if(ctl_regs[kMode_idx] & kMode_Repeat_Fl) + { + // store the current state of the attack flag + uint8_t fl = ctl_regs[kState_idx] & kState_Attk_Fl; + + tmr0_reset(); // ... restart the timer + + // ATTK_PIN is always set after tmr0_reset() but we need to toggle in 'repeat' mode + if( fl ) + { + clear_attack(); + } + + // In repeat mode we always run the PWM output continuously. + // This guarantees that no matter how the modes may be changing that PWM will be enabled. + TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // Enable PWM interrupts + + } + else // ... not in repeat mode + { + clear_attack(); + + if( ctl_regs[kMode_idx] & kMode_Pwm_Fl) + { + TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // PWM interupt Enable interrupts + } + else + { + PORTB |= _BV(HOLD_PIN); // set the HOLD pin + } + + TIMSK &= ~_BV(OCIE0A); // clear timer interrupt + + } + + break; + } +} + + +void tmr0_init() +{ + TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER1_OVF + TCCR0A |= 0x02; // CTC mode + TCCR0B |= ctl_regs[kTmr_Prescale_idx]; // set the prescaler + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// Pwm +// +// PWM is optimized to use pins OC1A ,~OC1A, OC1B, ~OC1B +// but since these pins are not available this code uses +// ISR's to redirect the output to PIN3 + +void pwm1_update() +{ + OCR1B = ctl_regs[kPwm_Duty_idx]; // control duty cycle + OCR1C = ctl_regs[kPwm_Freq_idx]; // PWM frequency pre-scaler +} + + + +ISR(TIMER1_OVF_vect) +{ + PORTB |= _BV(HOLD_PIN); // set PWM pin +} + +ISR(TIMER1_COMPB_vect) +{ + PORTB &= ~(_BV(HOLD_PIN)); // clear PWM pin +} + + +void pwm1_init() +{ + TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // Disable interrupts + + DDRB |= _BV(HOLD_DIR); // setup PB3 as output + + // set on TCNT1 == 0 // happens when TCNT1 matches OCR1C + // clr on OCR1B == TCNT // happens when TCNT1 matches OCR1B + // // COM1B1=1 COM1B0=0 (enable output on ~OC1B) + TCCR1 |= ctl_regs[ kPwm_Div_idx]; // 32us period (512 divider) prescaler + GTCCR |= _BV(PWM1B); // Enable PWM B and disconnect output pins + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + pwm1_update(); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// Tracks the current register pointer position +volatile uint8_t reg_position = 0; +const uint8_t reg_size = sizeof(ctl_regs); + +// +// Read Request Handler +// +// This is called for each read request we receive, never put more +// than one byte of data (with TinyWireS.send) to the send-buffer when +// using this callback +// + +void on_request() +{ + uint8_t val = 0; + + switch( ctl_regs[ kRead_Src_idx ] ) + { + case kReg_Rd_Addr_idx: + val = ctl_regs[ ctl_regs[kReg_Rd_Addr_idx] ]; + break; + + case kTable_Rd_Addr_idx: + val = table[ ctl_regs[kTable_Rd_Addr_idx] ]; + break; + + case kEE_Rd_Addr_idx: + val = EEPROM_read(ctl_regs[kEE_Rd_Addr_idx]); + break; + + default: + set_error( kInvalid_Read_Src_ErrFl ); + return; + } + + usiTwiTransmitByte(val); + + ctl_regs[ ctl_regs[ kRead_Src_idx ] ] += 1; + +} + + +void _write_op( uint8_t* stack, uint8_t stackN ) +{ + uint8_t stack_idx = 0; + + if( stackN > 0 ) + { + uint8_t src = stack[0] & 0x07; + uint8_t addr_fl = stack[0] & 0x08; + + // verify the source value + if( src < kReg_Wr_Addr_idx || src > kEE_Wr_Addr_idx ) + { + set_error( kInvalid_Write_Dst_ErrFl ); + return; + } + + // set the write source + stack_idx = 1; + ctl_regs[ kWrite_Dst_idx ] = src; + + // if an address value was passed also .... + if( addr_fl && stackN > 1 ) + { + stack_idx = 2; + ctl_regs[ src ] = stack[1]; + } + } + + // + for(; stack_idx TWI_RX_BUFFER_SIZE) + { + // Sanity-check + return; + } + + // get the register index to read/write + uint8_t op_id = usiTwiReceiveByte(); + + byteN--; + + // If only one byte was received then this was a read request + // and the buffer pointer (reg_position) is now set to return the byte + // at this location on the subsequent call to on_request() ... + if(byteN) + { + while( byteN-- ) + { + stack[stack_idx] = usiTwiReceiveByte(); + ++stack_idx; + } + } + + switch( op_id ) + { + case kSetPwm_Op: + for(i=0; i 0 ) + { + ctl_regs[ kRead_Src_idx ] = stack[0]; + + if( stack_idx > 1 ) + ctl_regs[ ctl_regs[ kRead_Src_idx ] ] = stack[1]; + } + break; + + case kWrite_Op: + _write_op( stack, stack_idx ); + break; + + case kSetMode_Op: + if( stack_idx > 0) + { + ctl_regs[ kMode_idx ] = stack[0]; + + // if repeat mode was enabled + if( ctl_regs[ kMode_idx ] & kMode_Repeat_Fl ) + tmr0_reset(); + + pwm1_init(); // the state of PWM may have been changed + } + break; + + case kWriteTable_Op: + write_table(); + break; + } +} + + +int main(void) +{ + cli(); // mask all interupts + + DDRB |= _BV(ATTK_DIR) + _BV(HOLD_DIR) + _BV(LED_DIR); // setup PB4,PB3,PB1 as output + PORTB &= ~(_BV(ATTK_PIN) + _BV(HOLD_PIN) + _BV(LED_PIN)); // clear output pins + + tmr0_init(); + pwm1_init(); + + // setup i2c library + usi_onReceiverPtr = on_receive; + usi_onRequestPtr = on_request; + usiTwiSlaveInit(I2C_SLAVE_ADDRESS); + + sei(); + + PINB = _BV(LED_PIN); // writes to PINB toggle the pins + _delay_ms(1000); + PINB = _BV(LED_PIN); // writes to PINB toggle the pins + + // if in repeat mode + if( ctl_regs[ kMode_idx ] & kMode_Repeat_Fl) + tmr0_reset(); + + while(1) + { + + if (!usi_onReceiverPtr) + { + // no onReceive callback, nothing to do... + continue; + } + + if (!(USISR & ( 1 << USIPF ))) + { + // Stop not detected + continue; + } + + + uint8_t amount = usiTwiAmountDataInReceiveBuffer(); + if (amount == 0) + { + // no data in buffer + continue; + } + + + usi_onReceiverPtr(amount); + + + } + return 0; +} + + +