From 61758bb9def5ad3ea6fcbf6fe15d7c2d6e909b49 Mon Sep 17 00:00:00 2001 From: "kevin.larke" Date: Sun, 14 Jul 2019 17:24:57 -0400 Subject: [PATCH] Initial working version of long interval timer and PWM under the control of picadae_cmd.py. --- control/app/picadae_cmd.py | 94 +++++++++++++++++++++++++++++++----- control/tiny/i2c_timer_pwm.c | 64 +++++++++++++++++++----- 2 files changed, 136 insertions(+), 22 deletions(-) diff --git a/control/app/picadae_cmd.py b/control/app/picadae_cmd.py index 3ddc33b..7b28879 100644 --- a/control/app/picadae_cmd.py +++ b/control/app/picadae_cmd.py @@ -120,10 +120,14 @@ class App: print("in:",msg[1]) - def _parse_error( self, msg ): + def _parse_error( self, msg, cmd_str=None ): + + if cmd_str: + msg += " Command:{}".format(cmd_str) + return (None,msg) - def _parse_int( self, token, var_label, max_value ): + def _parse_int( self, token, var_label, min_value, max_value ): # convert the i2c destination address to an integer try: int_value = int(token) @@ -131,11 +135,75 @@ class App: return self._parse_error("Synax error: '{}' is not a legal integer.".format(token)) # validate the i2c address value - if 0 > int_value or int_value > max_value: + if min_value > int_value or int_value > max_value: return self._parse_error("Syntax error: '{}' {} out of range 0 to {}.".format(token,int_value,max_value)) return (int_value,None) + def parse_app_cmd( self, cmd_str ): + """ + Command syntax + """ + + op_tok_idx = 0 + i2c_tok_idx = 1 + val_tok_idx = 2 + + 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%) + 'f':{ 'reg':4, 'n':1, 'min':1, 'max':5 }, # pwm frequency divider 1=1,2=8,3=64,4=256,5=1024 + } + + cmd_str = cmd_str.strip() + + tokenL = cmd_str.split(' ') + + # validate the counf of tokens + if len(tokenL) != 3: + return self._parse_error("Syntax error: Invalid token count.",cmd_str) + + opcode = tokenL[op_tok_idx] + + # validate the opcode + if opcode not in cmdD: + return self._parse_error("Syntax error: Invalid opcode.",cmd_str) + + # convert the i2c destination address to an integer + i2c_addr, msg = self._parse_int( tokenL[i2c_tok_idx], "i2c address", 0,127 ) + + if i2c_addr is None: + return (None,msg) + + d = cmdD[ opcode ] + + # get the value + value, msg = self._parse_int( tokenL[val_tok_idx], "command value", d['min'], d['max'] ) + + if value is None: + return (value,msg) + + dataL = [ value ] + + if opcode == 't': + + coarse = int(value/(32*254)) + fine = int((value - coarse*32*254)/32) + dataL = [ coarse, fine ] + + elif opcode == 'd': + dataL = [ int(value * 255 / 100.0) ] + + cmd_bV = bytearray( [ ord('w'), i2c_addr, d['reg'], len(dataL) ] + dataL ) + + if False: + print('cmd_bV:') + for x in cmd_bV: + print(int(x)) + + return (cmd_bV,None) + def parse_cmd( self, cmd_str ): op_tok_idx = 0 @@ -143,7 +211,11 @@ class App: reg_tok_idx = 2 rdn_tok_idx = 3 - cmd_str.strip() + cmd_str = cmd_str.strip() + + # if this is a high level command + if cmd_str[0] not in ['r','w']: + return self.parse_app_cmd( cmd_str ) # convert the command string to tokens tokenL = cmd_str.split(' ') @@ -163,16 +235,16 @@ class App: if op_code == 'r' and len(tokenL) != 4: return self._parse_error("Syntax error: Illegal read syntax.") - if op_code == 'w' and len(tokenL) == 4: + if op_code == 'w' and len(tokenL) < 4: return self._parse_error("Syntax error: Illegal write command too short.") # convert the i2c destination address to an integer - i2c_addr, msg = self._parse_int( tokenL[i2c_tok_idx], "i2c address", 127 ) + i2c_addr, msg = self._parse_int( tokenL[i2c_tok_idx], "i2c address", 0,127 ) if i2c_addr is None: return (None,msg) - reg_addr, msg = self._parse_int( tokenL[reg_tok_idx], "reg address", 255 ) + reg_addr, msg = self._parse_int( tokenL[reg_tok_idx], "reg address", 0, 255 ) if reg_addr is None: return (None,msg) @@ -181,7 +253,7 @@ class App: # parse and validate the count of bytes to read if op_code == 'r': - op_byteN, msg = self._parse_int( tokenL[ rdn_tok_idx ], "read byte count", 255 ) + op_byteN, msg = self._parse_int( tokenL[ rdn_tok_idx ], "read byte count", 0, 255 ) if op_byteN is None: return (None,msg) @@ -191,7 +263,7 @@ class App: elif op_code == 'w': for j,i in enumerate(range(reg_tok_idx+1,len(tokenL))): - value, msg = self._parse_int( tokenL[i], "write value: %i" % (j), 255 ) + value, msg = self._parse_int( tokenL[i], "write value: %i" % (j), 0, 255 ) if value is None: return (None,msg) @@ -238,7 +310,7 @@ class App: # if a serial msg was received if msg is not None and msg[0] == DATA_MSG: - print("ser:",msg[1]) + print("ser:",msg[1],int(msg[1][0])) self.serialProc.quit() @@ -252,7 +324,7 @@ def parse_args(): ap = argparse.ArgumentParser(description=descStr) - ap.add_argument("-s","--setup", default="cfg/p_ac.yml", help="YAML configuration file.") + ap.add_argument("-s","--setup", default="picadae_cmd.yml", help="YAML configuration file.") ap.add_argument("-c","--cmd", nargs="*", help="Give a command as multiple tokens") ap.add_argument("-r","--run", help="Run a named command list from the setup file.") ap.add_argument("-l","--log_level",choices=logL, default="warning", help="Set logging level: debug,info,warning,error,critical. Default:warning") diff --git a/control/tiny/i2c_timer_pwm.c b/control/tiny/i2c_timer_pwm.c index a051356..2551fac 100644 --- a/control/tiny/i2c_timer_pwm.c +++ b/control/tiny/i2c_timer_pwm.c @@ -12,8 +12,8 @@ enum { - kCS13_10_idx = 0, // Timer 1 Prescalar (CS13,CS12,CS11,CS10) from Table 12-5 pg 89 - kTmr0_Coarse_idx = 1, // count of times timer0 count to 255 before OCR1C is set to Tmr0_Minor + 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 @@ -21,9 +21,9 @@ enum volatile uint8_t ctl_regs[] = { - 0x0f, // 0 (0-15) timer prescalar 0=stop, 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 - 0, // 1 (0-255) Tmr0_Coarse count of times timer count to 255 before loading Tmr0_Minor for final count. - 244, // 2 (0-254) Tmr0_Fine OCR1C value on final phase before triggering timer + 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) }; @@ -32,9 +32,47 @@ volatile uint8_t ctl_regs[] = volatile uint8_t reg_position = 0; const uint8_t reg_size = sizeof(ctl_regs); +volatile uint8_t tmr_state = 0; +volatile uint8_t tmr_coarse_cur = 0; + +void tmr_reset() +{ + if( ctl_regs[kTmr0_Coarse_idx] > 0 ) + { + tmr_state = 1; + OCR1C = 254; + } + else + { + tmr_state = 2; + OCR1C = ctl_regs[kTmr0_Fine_idx]; + } + + tmr_coarse_cur = 0; +} + ISR(TIMER1_OVF_vect) { - PINB = _BV(PINB4) + _BV(PINB1); // writes to PINB toggle the pins + 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; + } + } @@ -46,8 +84,12 @@ void timer1_init() TCCR1 |= _BV(CTC1); // Reset TCNT1 to 0 when TCNT1==OCR1C TCCR1 |= _BV(PWM1A); // Enable PWM A TCCR1 |= ctl_regs[kCS13_10_idx] & 0x0f; // - OCR1C = ctl_regs[kTmr0_Fine_idx]; - TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF + GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value + + tmr_reset(); + + TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF + } void pwm0_update() @@ -136,9 +178,9 @@ void on_receive( uint8_t byteN ) if( kCS13_10_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) timer1_init(); - - if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx ) - pwm0_update(); + else + if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx ) + pwm0_update(); reg_position++;