Updates to support 16Mhz and reading from register memory from picadae_shell.py.

This commit is contained in:
kevin.larke 2019-08-06 21:36:22 -04:00
parent de586ca2e3
commit 6dc45c0127
6 changed files with 207 additions and 139 deletions

View File

@ -5,49 +5,62 @@ from multiprocessing import Process, Pipe
# Message header id's for messages passed between the application
# process and the microcontroller and video processes
TinyOpD = {
'setPwmOp': 0,
'noteOnVelOp': 1,
'noteOnUsecOp': 2,
'noteOffOp': 3,
'readOp': 4,
'writeOp': 5
}
class TinyOp(Enum):
setPwmOp = 0
noteOnVelOp = 1
noteOnUsecOp = 2
noteOffOp = 3
setReadAddr = 4
writeOp = 5
class TinyRegAddr(Enum):
kRdRegAddrAddr = 0,
kRdTableAddrAddr = 1,
kRdEEAddrAddr = 2,
kRdSrcAddr = 3,
kWrRegAddrAddr = 4,
kWrTableAddrAddr = 5,
kWrEEAddrAddr = 6,
kWrDstAddr = 7,
kTmrCoarseAddr = 8,
kTmrFineAddr = 9,
kTmrPrescaleAddr = 10,
kPwmEnableAddr = 11,
kPwmDutyAddr = 12,
kPwmFreqAddr = 13,
kModeAddr = 14,
kStateAddr = 15,
kErrorCodeAddr = 16
kRdRegAddrAddr = 0
kRdTableAddrAddr = 1
kRdEEAddrAddr = 2
kRdSrcAddr = 3
kWrRegAddrAddr = 4
kWrTableAddrAddr = 5
kWrEEAddrAddr = 6
kWrDstAddr = 7
kTmrCoarseAddr = 8
kTmrFineAddr = 9
kTmrPrescaleAddr = 10
kPwmDutyAddr = 11
kPwmFreqAddr = 12
kModeAddr = 13
kStateAddr = 14
kErrorCodeAddr = 15
class TinyConst(Enum):
kRdRegSrcId = TinyRegAddr.kRdRegAddrAddr, # 0
kRdTableSrcId = TinyRegAddr.kRdTableAddrAddr, # 1
kRdEESrcId = TinyRegAddr.kRdEEAddrAddr # 2
kRdRegSrcId = TinyRegAddr.kRdRegAddrAddr # 0
kRdTableSrcId = TinyRegAddr.kRdTableAddrAddr # 1
kRdEESrcId = TinyRegAddr.kRdEEAddrAddr # 2
kWrRegDstId = TinyRegAddr.kWrRegAddrAddr, # 4
kWrTableDstId = TinyRegAddr.kWrTableAddrAddr, # 5
kWrEEDstId = TinyRegAddr.kWrEEAddrAddr, # 6
kWrAddrFl = 0x08, # first avail bit above kWrEEAddr
kWrRegDstId = TinyRegAddr.kWrRegAddrAddr # 4
kWrTableDstId = TinyRegAddr.kWrTableAddrAddr # 5
kWrEEDstId = TinyRegAddr.kWrEEAddrAddr # 6
kWrAddrFl = 0x08 # first avail bit above kWrEEAddr
class SerialMsgId(Enum):
QUIT_MSG = 0xffff
DATA_MSG = 0xfffe
class Result(object):
def __init__( self, value=None, msg=None ):
self.value = value
self.msg = msg
def set_error( self, msg ):
if self.msg is None:
self.msg = ""
self.msg += " " + msg
def __bool__( self ):
return self.msg is None
def _serial_process_func( serial_dev, baud, pipe ):
reset_N = 0
@ -100,7 +113,8 @@ class SerialProcess(Process):
self.parent_end, child_end = Pipe()
super(SerialProcess, self).__init__(target=_serial_process_func, name="Serial", args=(serial_dev,serial_baud,child_end,))
self.doneFl = False
def quit(self):
# send quit msg to the child process
self.parent_end.send((SerialMsgId.QUIT_MSG,0))
@ -108,6 +122,7 @@ class SerialProcess(Process):
def send(self,msg_id,value):
# send a msg to the child process
self.parent_end.send((msg_id,value))
return Result()
def recv(self):
#
@ -127,7 +142,7 @@ class SerialProcess(Process):
class Picadae:
def __init__( self, key_mapL, i2c_base_addr=1, serial_dev='/dev/ttyACM0', serial_baud=38400 ):
def __init__( self, key_mapL, i2c_base_addr=21, serial_dev='/dev/ttyACM0', serial_baud=38400, prescaler_usec=16 ):
"""
key_mapL = [{ index, board, ch, type, midi, class }]
serial_dev = /dev/ttyACM0
@ -137,65 +152,86 @@ class Picadae:
self.serialProc = SerialProcess( serial_dev, serial_baud )
self.keyMapD = { d['midi']:d for d in key_mapL }
self.i2c_base_addr = i2c_base_addr
self.prescaler_usec = 32
self.prescaler_usec = prescaler_usec
self.serialProc.start()
def close( self ):
self.serialProc.quit()
def wait_for_serial_sync(self, timeoutMs=10000):
# wait for the letter 'a' to come back from the serial port
result = self.block_on_serial_read(1,timeoutMs)
if result and len(result.value)>0 and result.value[0] == ord('a'):
pass
else:
result.set_error("Serial sync failed.")
return result
def write( self, i2c_addr, reg_addr, byteL ):
return self._send( 'w', i2c_addr, reg_addr, [ len(byteL) ] + byteL )
def set_read_addr( self, i2c_addr, src, addr ):
return self. write(i2c_addr, TinyOpD['readOp'], src, addr )
def set_reg_read_addr( self, i2c_addr, addr ):
return self.set_read_addr(i2c_addr, TinyRegAddr.kRdRegAddrAddr, addr )
def set_read_addr( self, i2c_addr, mem_id, addr ):
return self. write(i2c_addr, TinyOp.setReadAddr.value,[ mem_id, addr ])
def read_request( self, i2c_addr, reg_addr, byteOutN ):
return self._send( 'r', i2c_addr, reg_addr,[ byteOutN ] )
def set_table_read_addr( self, i2c_addr, addr ):
return self.set_read_addr(i2c_addr, TinyRegAddr.kRdTableAddrAddr, addr )
def set_eeprom_read_addr( self, i2c_addr, addr ):
return self.set_read_addr(i2c_addr, TinyRegAddr.kRdEEAddrAddr, addr )
def read_request( self, i2c_addr, reg_addr, argL ):
return self._send( 'r', i2c_addr, reg_addr, [ byteOutN, len(argL) ] + argL )
def read_poll( self ):
return self.serialProc.recv()
def read_block( self, i2c_addr, reg_addr, argL, byteOutN, time_out_ms ):
def block_on_serial_read( self, byteOutN, time_out_ms=250 ):
self.read_request( self, i2c_addr, reg_addr, argL, byteOutN )
ts = datetime.datetime.now() + datetime.timeDelta(milliseconds=time_out_ms)
ts = datetime.datetime.now() + datetime.timedelta(milliseconds=time_out_ms)
retL = []
while datetime.datetime.now() < ts and len(retL) < byteOutN:
# If a value is available at the serial port return is otherwise return None.
x = self.serialProc.recv()
if x is not None:
retL.append(x)
if x is not None and x[0] == SerialMsgId.DATA_MSG:
for b in x[1]:
retL.append(int(b))
time.sleep(0.01)
return retL
result = Result(value=retL)
if len(retL) < byteOutN:
result.set_error("Serial port time out on read.")
return result
def block_on_picadae_read( self, i2c_addr, mem_id, reg_addr, argL, byteOutN, time_out_ms ):
result = self.set_read_addr( i2c_addr, mem_id, reg_addr )
if result:
result = self.read_request( i2c_addr, TinyOp.setReadAddr.value, byteOutN )
if result:
result = self.block_on_serial_read( byteOutN, time_out_ms )
return result
def note_on_vel( self, midi_pitch, midi_vel ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
TinyOpD['noteOnVelOp'],
TinyOp.noteOnVelOp.value,
[self._validate_vel(midi_vel)] )
def note_on_us( self, midi_pitch, pulse_usec ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
TinyOpD['noteOnUsecOp'],
TinyOp.noteOnUsecOp.value,
list(self._usec_to_coarse_and_fine(pulse_usec)) )
def note_off( self, midi_pitch ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
TinyOpD['noteOffOp'],[0] ) # TODO: sending a dummy byte because we can handle sending a command with no data bytes.
TinyOp.noteOffOp.value,
[0] ) # TODO: sending a dummy byte because we can't handle sending a command with no data bytes.
def set_velocity_map( self, midi_pitch, midi_vel, pulse_usec ):
pass
@ -203,14 +239,15 @@ class Picadae:
def get_velocity_map( self, midi_pitch, midi_vel, time_out_ms=250 ):
pass
def set_pwm_duty( self, midi_pitch, duty_cycle_pct, enableFl=True ):
def set_pwm_duty( self, midi_pitch, duty_cycle_pct ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
TinyOpD['setPwmOp'],
[enableFl, int( duty_cycle_pct * 255.0 /100.0 )])
TinyOp.setPwmOp.value,
[ int( duty_cycle_pct * 255.0 /100.0 )])
def get_pwm_duty( self, midi_pitch, time_out_ms=250 ):
return self.read_block( self._pitch_to_i2c_addr( midi_pitch ),
TinyRegAddr.kPwmDutyAddr,
return self.block_on_picadae_read( self._pitch_to_i2c_addr( midi_pitch ),
TinyRegAddr.kRdRegAddrAddr.value,
TinyRegAddr.kPwmDutyAddr.value,
[], 1, time_out_ms )
def set_pwm_freq( self, midi_pitch, freq_div_id ):
@ -219,13 +256,14 @@ class Picadae:
pass
def get_pwm_freq( self, midi_pitch, time_out_ms=250 ):
return self.read_block( self._pitch_to_i2c_addr( midi_pitch ),
TinyRegAddr.kPwmFreqAddr,
return self.block_on_picadae_read( self._pitch_to_i2c_addr( midi_pitch ),
TinyRegAddr.kRdRegAddrAddr.value,
TinyRegAddr.kPwmFreqAddr.value,
[], 1, time_out_ms )
def set_flags( self, midi_pitch, flags ):
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
TinyOpD['writeOp'],
TinyOp.writeOp.value,
[ 12, 14, flags ])
def make_note( self, midi_pitch, atk_us, dur_ms ):
@ -245,7 +283,6 @@ class Picadae:
coarse = int( usec / (self.prescaler_usec*255))
fine = int((usec - coarse*self.prescaler_usec*255) / self.prescaler_usec)
print(coarse,fine)
assert( coarse <= 255 )
assert( fine <= 255)

View File

@ -275,6 +275,11 @@ class App:
# form the command into a byte array
cmd_bV = bytearray( [ ord(op_code), i2c_addr, reg_addr, op_byteN ] + dataL )
# s = ""
# for i in range(len(cmd_bV)):
# s += "%i " % (cmd_bV[i])
# print(s)
return (cmd_bV,None)

View File

@ -4,7 +4,8 @@
serial_dev: "/dev/ttyACM0",
serial_baud: 38400,
i2c_base_addr: 21,
prescaler_usec: 32,
prescaler_usec: 16,
serial_sync_timeout_ms: 10000,
key_mapL: [

View File

@ -1,6 +1,7 @@
import os,sys,argparse,yaml,types,select,serial,logging,time
from picadae_api import Picadae
from picadae_api import Result
class PicadaeShell:
def __init__( self, cfg ):
@ -27,6 +28,7 @@ class PicadaeShell:
for k,d in self.parseD.items():
s = "{} = {}".format( k, d['help'] )
print(s)
return Result()
def _do_write( self, argL ):
return self.p.write(argL[0], argL[1], argL[2:])
@ -69,7 +71,7 @@ class PicadaeShell:
def _syntaxError( self, msg ):
print("Syntax Error: " + msg )
return None
return Result()
def _exec_cmd( self, tokL ):
if len(tokL) <= 0:
@ -95,36 +97,48 @@ class PicadaeShell:
if d['varN'] != -1 and len(argL) != d['varN']:
return self._syntaxError("Argument mismatch {} != {}.".format(len(argL),d['varN']))
result = func(argL)
result = func(argL)
return None
return result
def run( self ):
# create the API object
self.p = Picadae( cfg.key_mapL, cfg.i2c_base_addr, cfg.serial_dev, cfg.serial_baud )
self.p = Picadae( cfg.key_mapL, cfg.i2c_base_addr, cfg.serial_dev, cfg.serial_baud, cfg.prescaler_usec )
print("'q'=quit '?'=help")
time_out_secs = 1
while True:
# wait for the letter 'a' to come back from the serial port
result = self.p.wait_for_serial_sync(timeoutMs=cfg.serial_sync_timeout_ms)
# wait for keyboard activity
i, o, e = select.select( [sys.stdin], [], [], time_out_secs )
if not result:
print("Serial port sync failed.")
else:
print(result.value)
print("'q'=quit '?'=help")
time_out_secs = 1
if (i):
# read the command
s = sys.stdin.readline().strip()
while True:
# tokenize the command
tokL = s.split(' ')
# wait for keyboard activity
i, o, e = select.select( [sys.stdin], [], [], time_out_secs )
# if this is the 'quit' command
if tokL[0] == 'q':
break
if (i):
# read the command
s = sys.stdin.readline().strip()
# execute the command
self._exec_cmd( tokL )
# tokenize the command
tokL = s.split(' ')
# if this is the 'quit' command
if tokL[0] == 'q':
break
# execute the command
result = self._exec_cmd( tokL )
if result.value:
print(result.value)
self.p.close()

View File

@ -21,12 +21,15 @@ AVRDUDE=avrdude
# /usr/bin/avrdude -C/etc/avrdude/avrdude.conf -v -pattiny85 -cstk500v1 -P/dev/ttyACM0 -b19200 -Uflash:w:/tmp/arduino_build_108059/i2c.ino.hex:i
#
# lfuse=0xe2 = 8 Mghz
# lfuse=0xe1 = 16 Mghz
all:
$(CC) $(CFLAGS) $(TARGET).c usiTwiSlave.c -o$(TARGET)
$(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:0xdd: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:0xe1:m -U hfuse:w:0xdd:m -U efuse:w:0xff:m
clean:
rm -f *.hex *.obj *.o

View File

@ -15,7 +15,7 @@
// This program acts as the device (slave) for the control program i2c/a2a/c_ctl
#define F_CPU 8000000L
#define F_CPU 16000000L
#include <stdio.h>
#include <avr/io.h>
@ -35,13 +35,15 @@
// Opcodes
enum
{
kSetPwm_Op = 0, // Set PWM registers 0 {<enable> {<duty> {<freq>}}}
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
kRead_Op = 4, // Read a value 4 {<src>} {<addr>} } src: 0=reg 1=table 2=eeprom
kWrite_Op = 5, // Set write 5 {<addrfl|src> {addr} {<value0> ... {<valueN>}}
kInvalid_Op = 6 // addrFl:0x80 src: 4=reg 5=table 6=eeprom
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
kInvalid_Op = 7 //
};
@ -59,26 +61,29 @@ 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 (32us)
kTmr_Prescale_idx = 10, // Timer 0 clock divider: 1=1,2=8,3=64,4=256,5=1024 Default: 8 (16us)
kPwm_Enable_idx = 11, //
kPwm_Duty_idx = 12, //
kPwm_Freq_idx = 13, //
kPwm_Duty_idx = 11, //
kPwm_Freq_idx = 12, //
kMode_idx = 14, // 1=repeat 2=pwm
kState_idx = 15, // 1=attk 2=hold
kError_Code_idx = 16, // Error Code
kMode_idx = 13, // 1=repeat 2=pwm
kState_idx = 14, // 1=attk 2=hold
kError_Code_idx = 15, // Error Code
kMax_idx
};
enum
{
kTmr_Repeat_Fl= 1,
kTmr_Pwm_Fl = 2,
kAttk_Fl = 1,
kHold_Fl = 2
kMode_Repeat_Fl = 1,
kMode_Pwm_Fl = 2,
kAttk_Fl = 1,
kHold_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
@ -93,15 +98,14 @@ volatile uint8_t ctl_regs[] =
0, // 5 (0-255) Table Write Addr
0, // 6 (0-255) EE Write Addr
kReg_Wr_Addr_idx, // 7 (0-2) Write source
123, // 8 (0-255) Timer 0 Coarse Value
8, // 9 (0-255) Timer 0 Fine Value
4, // 10 (1-5) 4=32us per tick
1, // 11 (0-1) Pwm Enable Flag
127, // 12 (0-255) Pwm Duty cycle
254, // 13 (0-255) Pwm Frequency (123 hz)
0, // 14 mode flags 1=Repeat 2=PWM
0, // 15 state flags 1=attk 2=hold
0, // 16 (0-255) Error bit field
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)
kMode_Repeat_Fl, // 13 mode flags 1=Repeat 2=PWM
0, // 14 state flags 1=attk 2=hold
0, // 15 (0-255) Error bit field
};
#define tableN 256
@ -251,7 +255,7 @@ ISR(TIMER0_COMPA_vect)
// fine mode
// If in repeat mode
if(ctl_regs[kMode_idx] & kTmr_Repeat_Fl)
if(ctl_regs[kMode_idx] & kMode_Repeat_Fl)
{
uint8_t fl = ctl_regs[kState_idx] & kAttk_Fl;
@ -271,7 +275,7 @@ ISR(TIMER0_COMPA_vect)
{
clear_attack();
if( ctl_regs[kMode_idx] & kTmr_Pwm_Fl)
if( ctl_regs[kMode_idx] & kMode_Pwm_Fl)
{
TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // PWM interupt Enable interrupts
}
@ -289,17 +293,13 @@ ISR(TIMER0_COMPA_vect)
}
void timer0_init()
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
//tmr0_reset(); // set the timers starting state
GTCCR |= _BV(PSR0); // Set the pre-scaler to the selected value
}
@ -341,7 +341,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 |= 9; // 32us period (256 divider) prescaler
TCCR1 |= 10; // 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
@ -485,13 +485,13 @@ void on_receive( uint8_t byteN )
switch( op_id )
{
case kSetPwm_Op:
for(i=0; i<stack_idx; ++i)
ctl_regs[ kPwm_Enable_idx + i ] = stack[i];
for(i=0; i<stack_idx && i<2; ++i)
ctl_regs[ kPwm_Duty_idx + i ] = stack[i];
pwm1_update();
break;
case kNoteOnUsec_Op:
for(i=0; i<stack_idx; ++i)
for(i=0; i<stack_idx && i<3; ++i)
ctl_regs[ kTmr_Coarse_idx + i ] = stack[i];
tmr0_reset();
break;
@ -501,8 +501,7 @@ void on_receive( uint8_t byteN )
PORTB &= ~_BV(HOLD_PIN); // clear the HOLD pin
break;
case kRead_Op:
case kSetReadAddr_Op:
if( stack_idx > 0 )
{
ctl_regs[ kRead_Src_idx ] = stack[0];
@ -515,6 +514,15 @@ void on_receive( uint8_t byteN )
case kWrite_Op:
_write_op( stack, stack_idx );
break;
case kSetMode_Op:
if( stack_idx > 0)
{
ctl_regs[ kMode_idx ] = stack[0];
tmr0_reset();
}
break;
}
}
@ -523,12 +531,10 @@ 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();
tmr0_init();
pwm1_init();
// setup i2c library
@ -542,10 +548,12 @@ 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)
{
//_delay_ms(1000);
if (!usi_onReceiverPtr)
{