From ffd46bf3b60e8b363e9ad84bec9eba91184c24cf Mon Sep 17 00:00:00 2001 From: "kevin.larke" Date: Sat, 20 Jul 2019 19:25:06 -0400 Subject: [PATCH] picadae_cmd.py : changed serial output to show all received data in integer format. ctrl/main.c : write commands now send as many data bytes as received rather than just 2. tiny/main.c : Added timer0 and PCM1 options. Added table and EEPROM reading/writing. tiny/Makefile: Enabled 'brown-out-detection'. --- control/app/picadae_cmd.py | 12 +- control/ctrl/main.c | 45 ++-- control/tiny/Makefile | 2 +- control/tiny/i2c_timer_pwm.c | 476 +++++++++++++++++++++++++++++------ 4 files changed, 435 insertions(+), 100 deletions(-) diff --git a/control/app/picadae_cmd.py b/control/app/picadae_cmd.py index 7b28879..60a44ea 100644 --- a/control/app/picadae_cmd.py +++ b/control/app/picadae_cmd.py @@ -152,7 +152,7 @@ class App: cmdD = { 'p':{ 'reg':0, 'n':1, 'min':0, 'max':4 }, # timer pre-scalar: sets timer tick rate 't':{ 'reg':1, 'n':2, 'min':0, 'max':10e7 }, # microseconds - 'd':{ 'reg':3, 'n':1, 'min':0, 'max':100 }, # pwm duty cylce (0-100%) + 'd':{ 'reg':3, 'n':1, 'min':0, 'max':100 }, # pwm duty cylce (0-100%) 'f':{ 'reg':4, 'n':1, 'min':1, 'max':5 }, # pwm frequency divider 1=1,2=8,3=64,4=256,5=1024 } @@ -190,6 +190,7 @@ class App: coarse = int(value/(32*254)) fine = int((value - coarse*32*254)/32) + print(coarse,fine) dataL = [ coarse, fine ] elif opcode == 'd': @@ -272,7 +273,6 @@ class App: op_byteN = len(dataL) - # form the command into a byte array cmd_bV = bytearray( [ ord(op_code), i2c_addr, reg_addr, op_byteN ] + dataL ) @@ -293,7 +293,7 @@ class App: if (i): s = sys.stdin.readline().strip() - if s == 'quit': + if s == 'quit' or s == 'q': break cmd_bV,err_msg = self.parse_cmd(s) @@ -310,7 +310,11 @@ class App: # if a serial msg was received if msg is not None and msg[0] == DATA_MSG: - print("ser:",msg[1],int(msg[1][0])) + str = "" + for i in range(len(msg[1])): + str += "{} ".format(int(msg[1][i])) + + print("ser:",str) self.serialProc.quit() diff --git a/control/ctrl/main.c b/control/ctrl/main.c index 31ea277..5519bf9 100644 --- a/control/ctrl/main.c +++ b/control/ctrl/main.c @@ -20,7 +20,7 @@ volatile int ser_buf_i_idx = 0; // receive buffer input index int ser_buf_o_idx = 0; // receive buffer output index // Receive buffer -char buf[ SER_BUF_N ]; +char ser_buf[ SER_BUF_N ]; void uart_init(void) @@ -105,7 +105,7 @@ void i2c_init() ISR(USART_RX_vect) { // receive the incoming byte - buf[ ser_buf_i_idx ] = uart_getchar(); + ser_buf[ ser_buf_i_idx ] = uart_getchar(); // advance the buffer input index ser_buf_i_idx = (ser_buf_i_idx + 1) % SER_BUF_N; @@ -141,13 +141,16 @@ int main (void) const uint8_t kWaitFl = 1; const uint8_t kSendStopFl = 1; const uint8_t kNoSendStopFl = 0; + const uint8_t data_bufN = 0xff; char c; - uint8_t state = kWait_for_cmd; - char cmd; - uint8_t i2c_addr; - uint8_t dev_reg_addr; - uint8_t op_byte_cnt; + uint8_t state = kWait_for_cmd; // parser state + char cmd; // 'r' or 'w' + uint8_t i2c_addr; // remote i2c address + uint8_t dev_reg_addr; // remote device register address + uint8_t op_byte_cnt; // count of data bytes to send or recv + uint8_t data_buf[ data_bufN ]; // hold data during parsing + uint8_t data_buf_idx = 0; // next avail slot in the data buffer cli(); // mask all interupts @@ -173,14 +176,14 @@ int main (void) if( ser_buf_o_idx != ser_buf_i_idx ) { // get the waiting byte - c = buf[ser_buf_o_idx]; + c = ser_buf[ser_buf_o_idx]; // advance the buffer output index ser_buf_o_idx = (ser_buf_o_idx+1) % SER_BUF_N; // Serial Protocol - // 'r', reg-idx, cnt, -> i2c_read_from() - // 'w', reg-idx, cnt, value0, ... valueN -> i2c_xmit() + // 'r', i2c-addr, reg-idx, cnt, -> i2c_read_from() + // 'w', i2c-addr, reg-idx, cnt, value0, ... valueN -> i2c_xmit() switch(state) { @@ -191,7 +194,7 @@ int main (void) state = kWait_for_i2c; } else - uart_putchar('E'); + uart_putchar('E'); // indicate a protocol error break; case kWait_for_i2c: @@ -215,16 +218,30 @@ int main (void) else { state = kWait_for_value; + data_buf[0] = dev_reg_addr; // make 'dev_reg_addr' the first data value to write + data_buf_idx = 1; // + op_byte_cnt += 1; // incr op_byte_cnt to account for 'dev_reg_addr' as first byte + } break; case kWait_for_value: + if( data_buf_idx >= data_bufN ) { - uint8_t buf[] = { dev_reg_addr, c }; - - i2c_xmit( I2C_REMOTE_ADDR, buf, 2, kSendStopFl); + uart_putchar('F'); // indicate a buffer overrun state = kWait_for_cmd; } + else + { + data_buf[ data_buf_idx++ ] = c; + + if(data_buf_idx == op_byte_cnt ) + { + + i2c_xmit( I2C_REMOTE_ADDR, data_buf, op_byte_cnt, kSendStopFl); + state = kWait_for_cmd; + } + } break; } diff --git a/control/tiny/Makefile b/control/tiny/Makefile index 19bb462..c39630f 100644 --- a/control/tiny/Makefile +++ b/control/tiny/Makefile @@ -22,7 +22,7 @@ all: $(OBJ2HEX) -R .eeprom -O ihex $(TARGET) $(TARGET).hex burn: - $(AVRDUDE) -p $(MCU) -P $(TTY) -C/etc/avrdude/avrdude.conf -v -c avrisp -b 19200 -U flash:w:$(TARGET).hex -U lfuse:w:0xe2:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m + $(AVRDUDE) -p $(MCU) -P $(TTY) -C/etc/avrdude/avrdude.conf -v -c avrisp -b 19200 -U flash:w:$(TARGET).hex -U lfuse:w:0xe2:m -U hfuse:w:0xdd:m -U efuse:w:0xff:m clean: rm -f *.hex *.obj *.o diff --git a/control/tiny/i2c_timer_pwm.c b/control/tiny/i2c_timer_pwm.c index 2551fac..e5dff94 100644 --- a/control/tiny/i2c_timer_pwm.c +++ b/control/tiny/i2c_timer_pwm.c @@ -1,3 +1,15 @@ +/* + 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 @@ -7,123 +19,397 @@ #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 + 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[] = { - 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) + 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 }; -// Tracks the current register pointer position -volatile uint8_t reg_position = 0; -const uint8_t reg_size = sizeof(ctl_regs); +#define tableN 256 +uint8_t table[ tableN ]; + -volatile uint8_t tmr_state = 0; -volatile uint8_t tmr_coarse_cur = 0; +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// EEPROM +// -void tmr_reset() +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 ) { - tmr_state = 1; - OCR1C = 254; + tmr0_state = 1; + OCR0A = 0xff; } - else + else // otherwise go into fine mode { - tmr_state = 2; - OCR1C = ctl_regs[kTmr0_Fine_idx]; + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; } - tmr_coarse_cur = 0; + tmr0_coarse_cur = 0; } -ISR(TIMER1_OVF_vect) +ISR(TIMER0_COMPA_vect) { - switch( tmr_state ) + switch( tmr0_state ) { case 0: + // disabled break; - - case 1: - if( ++tmr_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] ) + + case 1: + // coarse mode + if( ++tmr0_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] ) { - tmr_state = 2; - OCR1C = ctl_regs[kTmr0_Fine_idx]; + tmr0_state = 2; + OCR0A = ctl_regs[kTmr0_Fine_idx]; } break; - - case 2: - PINB = _BV(PINB4) + _BV(PINB1); // writes to PINB toggle the pins - tmr_reset(); + case 2: + // fine mode + PINB = _BV(PINB4); // writes to PINB toggle the pins + + tmr0_reset(); // restart the timer break; } - - } -void timer1_init() +void timer0_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 + TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER1_OVF + TCCR0A |= 0x02; // CTC mode + TCCR0B |= ctl_regs[kTmr0_Prescale_idx]; // set the prescaler - tmr_reset(); + GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value - TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF + tmr0_reset(); // set the timers starting state + TIMSK |= _BV(OCIE0A); // Enable interrupt TIMER1_OVF + } + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// PWM (Timer0) +// + void pwm0_update() { - OCR0B = ctl_regs[kPWM_Duty_idx]; // 50% duty cycle - TCCR0B |= ctl_regs[kPWM_Freq_idx]; // PWM frequency pre-scaler + 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) + // 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); + + + DDRB |= _BV(DDB1); // set direction on } -/** - * 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 - */ + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// +// 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() { - // read and transmit the requestd position - usiTwiTransmitByte(ctl_regs[reg_position]); + 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++; @@ -135,14 +421,14 @@ void on_request() } -/** - * 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, - */ +// +// 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 ) { @@ -174,21 +460,45 @@ void on_receive( uint8_t byteN ) // pointer is now pointing to the first byte to write to while(byteN--) { - ctl_regs[reg_position] = usiTwiReceiveByte(); + // write the value + ctl_regs[reg_position] = usiTwiReceiveByte(); - if( kCS13_10_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) - timer1_init(); + // 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 - if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx ) - pwm0_update(); + + // 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; + } - reg_position++; - if (reg_position >= reg_size) - { - reg_position = 0; - } } + } @@ -197,16 +507,20 @@ void on_receive( uint8_t byteN ) 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(); + + 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; //on_receive; - usi_onRequestPtr = on_request; + usi_onReceiverPtr = on_receive; + usi_onRequestPtr = on_request; usiTwiSlaveInit(I2C_SLAVE_ADDRESS); sei();