Initial working version of long interval timer and PWM under the control of picadae_cmd.py.

This commit is contained in:
kevin.larke 2019-07-14 17:24:57 -04:00
parent 04dc59ee48
commit 61758bb9de
2 changed files with 136 additions and 22 deletions

View File

@ -120,10 +120,14 @@ class App:
print("in:",msg[1]) 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) 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 # convert the i2c destination address to an integer
try: try:
int_value = int(token) int_value = int(token)
@ -131,11 +135,75 @@ class App:
return self._parse_error("Synax error: '{}' is not a legal integer.".format(token)) return self._parse_error("Synax error: '{}' is not a legal integer.".format(token))
# validate the i2c address value # 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 self._parse_error("Syntax error: '{}' {} out of range 0 to {}.".format(token,int_value,max_value))
return (int_value,None) return (int_value,None)
def parse_app_cmd( self, cmd_str ):
"""
Command syntax <opcode> <remote_i2c_addr> <value>
"""
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 ): def parse_cmd( self, cmd_str ):
op_tok_idx = 0 op_tok_idx = 0
@ -143,7 +211,11 @@ class App:
reg_tok_idx = 2 reg_tok_idx = 2
rdn_tok_idx = 3 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 # convert the command string to tokens
tokenL = cmd_str.split(' ') tokenL = cmd_str.split(' ')
@ -163,16 +235,16 @@ class App:
if op_code == 'r' and len(tokenL) != 4: if op_code == 'r' and len(tokenL) != 4:
return self._parse_error("Syntax error: Illegal read syntax.") 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.") return self._parse_error("Syntax error: Illegal write command too short.")
# convert the i2c destination address to an integer # 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: if i2c_addr is None:
return (None,msg) 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: if reg_addr is None:
return (None,msg) return (None,msg)
@ -181,7 +253,7 @@ class App:
# parse and validate the count of bytes to read # parse and validate the count of bytes to read
if op_code == 'r': 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: if op_byteN is None:
return (None,msg) return (None,msg)
@ -191,7 +263,7 @@ class App:
elif op_code == 'w': elif op_code == 'w':
for j,i in enumerate(range(reg_tok_idx+1,len(tokenL))): 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: if value is None:
return (None,msg) return (None,msg)
@ -238,7 +310,7 @@ class App:
# if a serial msg was received # if a serial msg was received
if msg is not None and msg[0] == DATA_MSG: 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() self.serialProc.quit()
@ -252,7 +324,7 @@ def parse_args():
ap = argparse.ArgumentParser(description=descStr) 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("-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("-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") ap.add_argument("-l","--log_level",choices=logL, default="warning", help="Set logging level: debug,info,warning,error,critical. Default:warning")

View File

@ -12,8 +12,8 @@
enum enum
{ {
kCS13_10_idx = 0, // Timer 1 Prescalar (CS13,CS12,CS11,CS10) from Table 12-5 pg 89 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_Minor 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 kTmr0_Fine_idx = 2, // OCR1C timer match value
kPWM_Duty_idx = 3, // kPWM_Duty_idx = 3, //
kPWM_Freq_idx = 4, // 1-4 = clock divider=1=1,2=8,3=64,4=256,5=1024 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[] = 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 9, // 0 9=32 us period w/ 8Mhz clock (timer tick rate)
0, // 1 (0-255) Tmr0_Coarse count of times timer count to 255 before loading Tmr0_Minor for final count. 123, // 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 8, // 2 (0-254) Tmr0_Fine OCR1C value on final phase before triggering timer
127, // 3 (0-255) Duty cycle 127, // 3 (0-255) Duty cycle
4, // 4 (1-4) PWM Frequency (clock pre-scaler) 4, // 4 (1-4) PWM Frequency (clock pre-scaler)
}; };
@ -32,9 +32,47 @@ volatile uint8_t ctl_regs[] =
volatile uint8_t reg_position = 0; volatile uint8_t reg_position = 0;
const uint8_t reg_size = sizeof(ctl_regs); 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) 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(CTC1); // Reset TCNT1 to 0 when TCNT1==OCR1C
TCCR1 |= _BV(PWM1A); // Enable PWM A TCCR1 |= _BV(PWM1A); // Enable PWM A
TCCR1 |= ctl_regs[kCS13_10_idx] & 0x0f; // TCCR1 |= ctl_regs[kCS13_10_idx] & 0x0f; //
OCR1C = ctl_regs[kTmr0_Fine_idx]; GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value
TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF
tmr_reset();
TIMSK |= _BV(TOIE1); // Enable interrupt TIMER1_OVF
} }
void pwm0_update() void pwm0_update()
@ -136,9 +178,9 @@ void on_receive( uint8_t byteN )
if( kCS13_10_idx <= reg_position && reg_position <= kTmr0_Fine_idx ) if( kCS13_10_idx <= reg_position && reg_position <= kTmr0_Fine_idx )
timer1_init(); timer1_init();
else
if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx ) if( kPWM_Duty_idx <= reg_position && reg_position <= kPWM_Freq_idx )
pwm0_update(); pwm0_update();
reg_position++; reg_position++;