Added table writing (not yet tested) and removed 'repeat-mode'.

This commit is contained in:
kevin.larke 2019-08-19 21:27:25 -04:00
parent f24aec16e6
commit 5ffd77da17
3 changed files with 154 additions and 209 deletions

View File

@ -12,7 +12,7 @@ class TinyOp(Enum):
noteOffOp = 3
setReadAddr = 4
writeOp = 5
setModeOp = 6
writeTableOp = 6
invalidOp = 7
@ -30,7 +30,7 @@ class TinyRegAddr(Enum):
kTmrPrescaleAddr = 10
kPwmDutyAddr = 11
kPwmFreqAddr = 12
kModeAddr = 13
kPwmDivAddr = 13
kStateAddr = 14
kErrorCodeAddr = 15
@ -155,6 +155,7 @@ class Picadae:
self.keyMapD = { d['midi']:d for d in key_mapL }
self.i2c_base_addr = i2c_base_addr
self.prescaler_usec = prescaler_usec
self.log_level = 0
self.serialProc.start()
@ -179,7 +180,6 @@ class Picadae:
def call_op( self, midi_pitch, op_code, argL ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ), op_code, argL )
def set_read_addr( self, i2c_addr, mem_id, addr ):
return self. write(i2c_addr, TinyOp.setReadAddr.value,[ mem_id, addr ])
@ -211,7 +211,7 @@ class Picadae:
def block_on_picadae_read( self, midi_pitch, mem_id, reg_addr, byteOutN, time_out_ms ):
def block_on_picadae_read( self, midi_pitch, mem_id, reg_addr, byteOutN, time_out_ms=250 ):
i2c_addr = self._pitch_to_i2c_addr( midi_pitch )
@ -253,26 +253,21 @@ class Picadae:
byteOutN = 2
return self.block_on_picadae_read( midi_pitch, TinyConst.kRdTableSrcId.value, midi_vel*2, byteOutN, time_out_ms )
def set_pwm_duty( self, midi_pitch, duty_cycle_pct ):
def set_pwm( self, midi_pitch, duty_cycle_pct ):
return self.call_op( midi_pitch, TinyOp.setPwmOp.value, [ int( duty_cycle_pct * 255.0 /100.0 )])
def get_pwm_duty( self, midi_pitch, time_out_ms=250 ):
def get_pwm( self, midi_pitch, time_out_ms=250 ):
return self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kPwmDutyAddr.value, time_out_ms=time_out_ms )
def set_pwm_freq( self, midi_pitch, freq_div_id ):
# pwm frequency divider 1=1,2=8,3=64,4=256,5=1024
assert( 1 <= freq_div_id and freq_div_id <= 5 )
pass
def get_pwm_freq( self, midi_pitch, time_out_ms=250 ):
return self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kPwmFreqAddr.value, time_out_ms=time_out_ms )
def set_mode( self, midi_pitch, mode ):
# TODO validate mode value
return self.call_op( midi_pitch, TinyOp.setModeOp.value, [ mode ] )
def get_pwm_div( self, midi_pitch, time_out_ms=250 ):
return self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kPwmDivAddr.value, time_out_ms=time_out_ms )
def get_mode( self, midi_pitch, time_out_ms=250 ):
return self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kModeAddr.value, time_out_ms=time_out_ms )
def write_table( self, midi_pitch, time_out_ms=250 ):
# TODO: sending a dummy byte because we can't handle sending a command with no data bytes.
return self.call_op( midi_pitch, TinyOp.writeTableOp.value,[0])
def make_note( self, midi_pitch, atk_us, dur_ms ):
# TODO: handle error on note_on_us()
@ -280,6 +275,10 @@ class Picadae:
time.sleep( dur_ms / 1000.0 )
return self.note_off(midi_pitch)
def set_log_level( self, log_level ):
self.log_level = log_level
return Result()
def _pitch_to_i2c_addr( self, pitch ):
return self.keyMapD[ pitch ]['index'] + self.i2c_base_addr
@ -306,10 +305,11 @@ class Picadae:
def _print( self, opcode, i2c_addr, reg_addr, byteL ):
s = "{} {} {}".format( opcode, i2c_addr, reg_addr )
if self.log_level:
s = "{} {} {}".format( opcode, i2c_addr, reg_addr )
for x in byteL:
s += " {}".format(x)
for x in byteL:
s += " {}".format(x)
print(s)
print(s)

View File

@ -7,77 +7,44 @@ class PicadaeShell:
def __init__( self, cfg ):
self.p = None
self.parseD = {
'q':{ "func":None, "varN":0, "help":"quit"},
'?':{ "func":"help", "varN":0, "help":"Print usage text."},
'w':{ "func":"write", "varN":-1, "help":"write <i2c_addr> <reg_addr> <data0> ... <dataN>"},
'r':{ "func":"read", "varN":3, "help":"read <i2c_addr> <reg_addr> <byteN>"},
'v':{ "func":"note_on_vel", "varN":2, "help":"note-on <pitch> <vel>"},
'u':{ "func":"note_on_us", "varN":2, "help":"note-on <pitch> <usec>"},
'o':{ "func":"note_off", "varN":1, "help":"note-off <pitch>"},
'T':{ "func":"set_vel_map", "varN":3, "help":"table <pitch> <vel> <usec>"},
't':{ "func":"get_vel_map", "varN":2, "help":"table <pitch> <vel>"},
'D':{ "func":"set_pwm_duty", "varN":2, "help":"duty <pitch> <percent>"},
'd':{ "func":"get_pwm_duty", "varN":1, "help":"duty <pitch>"},
'F':{ "func":"set_pwm_freq", "varN":2, "help":"freq <pitch> <hz>"},
'f':{ "func":"get_pwm_freq", "varN":1, "help":"freq <pitch>"},
'M':{ "func":"set_mode", "varN":2, "help":"set_mode <pitch> <mode-bits> (1=repeat 2=pwm)" },
'm':{ "func":"get_mode", "varN":1, "help":"get_mode <pitch>"},
'N':{ "func":"make_note", "varN":3, "help":"note <pitch> atkUs durMs"},
'q':{ "func":None, "minN":0, "maxN":0, "help":"quit"},
'?':{ "func":"_help", "minN":0, "maxN":0, "help":"Print usage text."},
'w':{ "func":"_write", "minN":-1, "maxN":-1,"help":"write <i2c_addr> <reg_addr> <data0> ... <dataN>"},
'r':{ "func":"_read", "minN":4, "maxN":4, "help":"read <i2c_addr> <src> <reg_addr> <byteN>"},
'v':{ "func":"note_on_vel", "minN":2, "maxN":2, "help":"note-on <pitch> <vel>"},
'u':{ "func":"note_on_us", "minN":2, "maxN":3, "help":"note-on <pitch> <usec> <prescale> (1=1, 2=8, 3=64,(4)=256 16us, 5=1024)"},
'o':{ "func":"note_off", "minN":1, "maxN":1, "help":"note-off <pitch>"},
'T':{ "func":"set_vel_map", "minN":3, "maxN":3, "help":"table <pitch> <vel> <usec>"},
't':{ "func":"get_vel_map", "minN":2, "maxN":2, "help":"table <pitch> <vel>"},
'D':{ "func":"set_pwm", "minN":2, "maxN":4, "help":"duty <pitch> <percent> {<hz> {<div>}} div:2=2,3=4,4=8,5=16,6=32,7=64,8=128,9=256,(10)=512 32us, 11=1024,12=2048,13=4096,14=8192,15=16384" },
'd':{ "func":"get_pwm_duty", "minN":1, "maxN":1, "help":"duty <pitch>"},
'f':{ "func":"get_pwm_freq", "minN":1, "maxN":1, "help":"freq <pitch>"},
'i':{ "func":"get_pwm_div", "minN":1, "maxN":1, "help":"div <pitch>"},
'W':{ "func":"write_table", "minN":1, "maxN":1, "help":"write_table <pitch>"},
'N':{ "func":"make_note", "minN":3, "maxN":3, "help":"note <pitch> atkUs durMs"},
'L':{ "func":"set_log_level","minN":1, "maxN":1, "help":"log <level> (0-1)."}
}
def _do_help( self, _ ):
def _help( self, _=None ):
for k,d in self.parseD.items():
s = "{} = {}".format( k, d['help'] )
print(s)
return Result()
def _do_write( self, argL ):
def _write( self, argL ):
return self.p.write(argL[0], argL[1], argL[2:])
def _do_read( self, argL ):
return self.p.read(*argL)
def _do_note_on_vel( self, argL ):
return self.p.note_on_vel(*argL)
def _do_note_on_us( self, argL ):
return self.p.note_on_us(*argL)
def _do_note_off( self, argL ):
return self.p.note_off(*argL)
def _do_set_vel_map( self, argL ):
return self.p.set_velocity_map(*argL)
def _do_get_vel_map( self, argL ):
return self.p.get_velocity_map(*argL)
def _do_set_pwm_duty( self, argL ):
return self.p.set_pwm_duty(*argL)
def _do_get_pwm_duty( self, argL ):
return self.p.get_pwm_duty(*argL)
def _do_set_pwm_freq( self, argL ):
return self.p.set_pwm_freq(*argL)
def _do_get_pwm_freq( self, argL ):
return self.p.get_pwm_freq(*argL)
def _do_set_mode( self, argL ):
return self.p.set_mode(*argL)
def _do_get_mode( self, argL ):
return self.p.get_mode(*argL)
def _do_make_note( self, argL ):
return self.p.make_note(*argL)
def _read( self, argL ):
return self.p.block_on_picadae_read(argL[0], argL[1], argL[2], argL[3])
def _syntaxError( self, msg ):
print("Syntax Error: " + msg )
return Result()
def _exec_cmd( self, tokL ):
result = Result()
if len(tokL) <= 0:
return None
@ -88,21 +55,29 @@ class PicadaeShell:
d = self.parseD[ opcode ]
func_name = "_do_" + d['func']
func_name = d['func']
func = None
# find the function associated with this command
if hasattr(self, func_name ):
func = getattr(self, func_name )
func = getattr(self, func_name )
elif hasattr(self.p, func_name ):
func = getattr(self.p, func_name )
else:
return self._syntaxError("Exec function not found: '{}'.".format(func_name))
try:
argL = [ int(tokL[i]) for i in range(1,len(tokL)) ]
except:
return self._syntaxError("Unable to create integer arguments.")
try:
# convert the parameter list into integers
argL = [ int(tokL[i]) for i in range(1,len(tokL)) ]
except:
return self._syntaxError("Unable to create integer arguments.")
if d['varN'] != -1 and len(argL) != d['varN']:
return self._syntaxError("Argument mismatch {} != {}.".format(len(argL),d['varN']))
result = func(argL)
# validate the count of command args
if d['minN'] != -1 and (d['minN'] > len(argL) or len(argL) > d['maxN']):
return self._syntaxError("Argument count mismatch. {} is out of range:{} to {}".format(len(argL),d['minN'],d['maxN']))
# call the command function
result = func(*argL)
return result

View File

@ -35,13 +35,13 @@
// Opcodes
enum
{
kSetPwm_Op = 0, // Set PWM registers 0 {<duty> {<freq>}}
kNoteOnVel_Op = 1, // Turn on note 1 {<vel>}
kNoteOnUsec_Op = 2, // Turn on note 2 {<coarse> {<fine> {<prescale>}}}
kNoteOff_Op = 3, // Turn off note 3
kSetReadAddr_Op = 4, // Set a read addr. 4 {<src>} {<addr>} } src: 0=reg 1=table 2=eeprom
kWrite_Op = 5, // Set write 5 {<addrfl|src> {addr} {<value0> ... {<valueN>}} addrFl:0x80 src: 4=reg 5=table 6=eeprom
kSetMode_Op = 6, // Set the mode flags 6 {<mode>} 1=repeat 2=pwm
kSetPwm_Op = 0, // Set PWM duty/hz/div 0 {<duty> {<freq> {<div>}}} 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 {<vel>}
kNoteOnUsec_Op = 2, // Turn on note 4 {<coarse> {<fine> {<prescale>}}}
kNoteOff_Op = 3, // Turn off note 5
kSetReadAddr_Op = 4, // Set a read addr. 6 {<src>} {<addr>} } src: 0=reg 1=table 2=eeprom
kWrite_Op = 5, // Set write 7 {<addrfl|src> {addr} {<value0> ... {<valueN>}} addrFl:0x80 src: 4=reg 5=table 6=eeprom
kWriteTable_Op = 6, // Write table to EEprom 9
kInvalid_Op = 7 //
};
@ -60,33 +60,25 @@ enum
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: 8 (16us)
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 = 13, // 1=repeat 2=pwm
kState_idx = 14, // 1=attk 2=hold
kError_Code_idx = 15, // Error Code
kMax_Coarse_Tmr_idx = 16, // Max. allowable coarse timer value
kMax_idx
};
enum
{
kMode_Repeat_Fl = 1,
kMode_Pwm_Fl = 2,
kAttk_Fl = 1,
kHold_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
@ -99,26 +91,39 @@ volatile uint8_t ctl_regs[] =
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
5, // 8 (0-255) Timer 0 Coarse Value (20400 us)
0, // 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, // 13 mode flags 1=Repeat 2=PWM
0, // 14 state flags 1=attk 2=hold
0, // 14 state flags 1=attk 2=hold (read/only)
0, // 15 (0-255) Error bit field
14, // 16 (0-255) Max allowable coarse timer count
};
// 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
kInvalid_Read_Src_ErrFl = 0x01,
kInvalid_Write_Dst_ErrFl = 0x02,
kInvalid_Coarse_Tmr_ErrFl = 0x04
};
#define set_error( flag ) ctl_regs[ kError_Code_idx ] |= (flag)
@ -143,7 +148,6 @@ void EEPROM_write(uint8_t ucAddress, uint8_t ucData)
EECR |= (1<<EEPE); // Start eeprom write by setting EEPE
}
uint8_t EEPROM_read(uint8_t ucAddress)
{
// Wait for completion of previous write
@ -155,50 +159,33 @@ uint8_t EEPROM_read(uint8_t ucAddress)
return EEDR; // Return data from data register
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//
// Read/Write table
//
// To write table value 42 to 127 (coarse) 64 (fine)
//
// w 8 kTable_Addr_idx 42
// w 8 kTable_Coarse_idx 127
// w 8 kTable_fine_idx 64
//
// TO read table value 42
// w 8 kTable_Addr_idx 42
// r 8 kTable_Coarse_idx -> 127
// r 8 kTable_Fine_idx -> 64
/*
#define eeprom_addr( addr ) (kMax_idx + (addr))
void table_write_cur_value( void )
void write_table()
{
uint8_t tbl_addr = ctl_regs[ kTable_Addr_idx ] * 2;
uint8_t i;
uint8_t regN = sizeof(eeprom_addr);
table[ tbl_addr+0 ] = ctl_regs[ kTable_Coarse_idx ];
table[ tbl_addr+1 ] = ctl_regs[ kTable_Fine_idx ];
// write the persistent registers
for(i=0; i<regN; ++i)
EEPROM_write( i, ctl_regs[ eeprom_addr[i] ] );
EEPROM_write( eeprom_addr( tbl_addr+0 ), ctl_regs[ kTable_Coarse_idx ] );
EEPROM_write( eeprom_addr( tbl_addr+1 ), ctl_regs[ kTable_Fine_idx ]);
// write the table
for(i=0; i<tableN; ++i)
EEPROM_write( regN+i, table[i] );
}
void table_load( void )
void load_table()
{
uint8_t i = 0;
uint8_t i;
uint8_t regN = sizeof(eeprom_addr);
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) );
}
// read the persistent registers
for(i=0; i<regN; ++i)
ctl_regs[ eeprom_addr[i] ] = EEPROM_read(i);
// read the tabke
for(i=0; i<tableN; ++i)
table[i] = EEPROM_read(regN + i);
}
*/
//------------------------------------------------------------------------------
@ -208,16 +195,20 @@ void table_load( void )
// Timer0
//
volatile uint8_t tmr0_state = 0; // 0=disabled 1=coarse mode, 2=fine mode
volatile uint8_t tmr0_state = 0; // current timer mode: 0=disabled 1=coarse mode, 2=fine mode
volatile uint8_t tmr0_coarse_cur = 0;
#define set_attack() do { ctl_regs[kState_idx] |= kAttk_Fl; PORTB |= _BV(ATTK_PIN); } while(0)
#define clear_attack() do { PORTB &= ~_BV(ATTK_PIN); ctl_regs[kState_idx] &= ~kAttk_Fl; } while(0)
#define set_attack() do { ctl_regs[kState_idx] |= kState_Attk_Fl; PORTB |= _BV(ATTK_PIN); } while(0)
#define clear_attack() do { PORTB &= ~_BV(ATTK_PIN); ctl_regs[kState_idx] &= ~kState_Attk_Fl; } while(0)
// Use the current tmr0 ctl_reg[] values to set the timer to the starting state.
void tmr0_reset()
{
tmr0_coarse_cur = 0; // clear the coarse time counter
ctl_regs[kState_idx] |= kState_Attk_Fl; // set the attack state
PORTB |= _BV(ATTK_PIN); // set the attack pin
// if a coarse count exists then go into coarse mode
if( ctl_regs[kTmr_Coarse_idx] > 0 )
{
@ -230,11 +221,7 @@ void tmr0_reset()
OCR0A = ctl_regs[kTmr_Fine_idx];
}
tmr0_coarse_cur = 0;
ctl_regs[kState_idx] |= kAttk_Fl; // set the attack state
PORTB |= _BV(ATTK_PIN); // set the attack pin
TIMSK |= _BV(OCIE0A); // enable the timer interrupt
TIMSK |= _BV(OCIE0A); // enable the timer interrupt
}
ISR(TIMER0_COMPA_vect)
@ -242,7 +229,7 @@ ISR(TIMER0_COMPA_vect)
switch( tmr0_state )
{
case 0:
// disabled
// timer is disabled
break;
case 1:
@ -257,39 +244,12 @@ ISR(TIMER0_COMPA_vect)
case 2:
// fine mode
// If in repeat mode
if(ctl_regs[kMode_idx] & kMode_Repeat_Fl)
{
uint8_t fl = ctl_regs[kState_idx] & kAttk_Fl;
// This marks the end of a timer period
tmr0_reset(); // restart the timer
clear_attack();
// ATTK_PIN is always set after tmr0_reset() but we need to toggle in 'repeat' mode
if( fl )
{
clear_attack();
}
// In repeat mode we run the PWM output continuously
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
}
TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // PWM interupt Enable interrupts
TIMSK &= ~_BV(OCIE0A); // clear timer interrupt
break;
}
@ -343,7 +303,7 @@ void pwm1_init()
// 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 |= 10; // 32us period (512 divider) prescaler
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
@ -487,18 +447,36 @@ void on_receive( uint8_t byteN )
switch( op_id )
{
case kSetPwm_Op:
for(i=0; i<stack_idx && i<2; ++i)
for(i=0; i<stack_idx && i<3; ++i)
ctl_regs[ kPwm_Duty_idx + i ] = stack[i];
// if the PWM prescaler was changed
if( i == 3 )
pwm1_init();
pwm1_update();
break;
case kNoteOnUsec_Op:
for(i=0; i<stack_idx && i<3; ++i)
ctl_regs[ kTmr_Coarse_idx + i ] = stack[i];
// validate the coarse error value
if( ctl_regs[ kTmr_Coarse_idx ] > ctl_regs[ kMax_Coarse_Tmr_idx ])
{
ctl_regs[ kTmr_Coarse_idx ] = ctl_regs[ kMax_Coarse_Tmr_idx ];
set_error( kInvalid_Coarse_Tmr_ErrFl );
}
// if a prescaler was included then the timer needs to be re-initialized
if( i == 3 )
tmr0_init();
tmr0_reset();
break;
case kNoteOff_Op:
TIMSK &= ~_BV(OCIE0A); // clear timer interrupt (shouldn't be necessary)
TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // PWM interupt disable interrupts
PORTB &= ~_BV(HOLD_PIN); // clear the HOLD pin
break;
@ -517,13 +495,8 @@ void on_receive( uint8_t byteN )
_write_op( stack, stack_idx );
break;
case kSetMode_Op:
if( stack_idx > 0)
{
ctl_regs[ kMode_idx ] = stack[0];
tmr0_reset();
}
case kWriteTable_Op:
write_table();
break;
}
}
@ -550,9 +523,6 @@ int main(void)
_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)
{