Updates for initial timer and pwm test on piccadae.
This commit is contained in:
parent
ffd46bf3b6
commit
de586ca2e3
270
control/app/picadae_api.py
Normal file
270
control/app/picadae_api.py
Normal file
@ -0,0 +1,270 @@
|
||||
import os,sys,argparse,yaml,types,select,serial,logging,time,datetime
|
||||
from enum import Enum
|
||||
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 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
|
||||
|
||||
class TinyConst(Enum):
|
||||
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
|
||||
|
||||
class SerialMsgId(Enum):
|
||||
QUIT_MSG = 0xffff
|
||||
DATA_MSG = 0xfffe
|
||||
|
||||
|
||||
def _serial_process_func( serial_dev, baud, pipe ):
|
||||
|
||||
reset_N = 0
|
||||
drop_N = 0
|
||||
noSync_N = 0
|
||||
|
||||
with serial.Serial(serial_dev, baud) as port:
|
||||
|
||||
while True:
|
||||
|
||||
# get the count of available bytes in the serial port buffer
|
||||
bytes_waiting_N = port.in_waiting
|
||||
|
||||
|
||||
# if no serial port bytes are available then sleep ....
|
||||
if bytes_waiting_N == 0:
|
||||
time.sleep(0.01) # ... for 10 ms
|
||||
|
||||
else: # read the serial port ...
|
||||
v = port.read(bytes_waiting_N)
|
||||
|
||||
pipe.send((SerialMsgId.DATA_MSG,v)) # ... and send it to the parent
|
||||
|
||||
|
||||
msg = None
|
||||
if pipe.poll(): # non-blocking check for parent process messages
|
||||
try:
|
||||
msg = pipe.recv()
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
# if an incoming message was received
|
||||
if msg != None:
|
||||
|
||||
# this is a shutdown msg
|
||||
if msg[0] == SerialMsgId.QUIT_MSG:
|
||||
pipe.send(msg) # ... send quit msg back
|
||||
break
|
||||
|
||||
# this is a data xmit msg
|
||||
elif msg[0] == SerialMsgId.DATA_MSG:
|
||||
port.write(msg[1])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class SerialProcess(Process):
|
||||
def __init__(self,serial_dev,serial_baud):
|
||||
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))
|
||||
|
||||
def send(self,msg_id,value):
|
||||
# send a msg to the child process
|
||||
self.parent_end.send((msg_id,value))
|
||||
|
||||
def recv(self):
|
||||
#
|
||||
|
||||
x = None
|
||||
if not self.doneFl and self.parent_end.poll():
|
||||
x = self.parent_end.recv()
|
||||
|
||||
if x[0] == SerialMsgId.QUIT_MSG:
|
||||
self.doneFl = True
|
||||
|
||||
return x
|
||||
|
||||
def is_done(self):
|
||||
return self.doneFl
|
||||
|
||||
|
||||
|
||||
class Picadae:
|
||||
def __init__( self, key_mapL, i2c_base_addr=1, serial_dev='/dev/ttyACM0', serial_baud=38400 ):
|
||||
"""
|
||||
key_mapL = [{ index, board, ch, type, midi, class }]
|
||||
serial_dev = /dev/ttyACM0
|
||||
serial_baud = 38400
|
||||
i2c_base_addr = 1
|
||||
"""
|
||||
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.serialProc.start()
|
||||
|
||||
def close( self ):
|
||||
self.serialProc.quit()
|
||||
|
||||
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_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 ):
|
||||
|
||||
self.read_request( self, i2c_addr, reg_addr, argL, byteOutN )
|
||||
|
||||
ts = datetime.datetime.now() + datetime.timeDelta(milliseconds=time_out_ms)
|
||||
|
||||
retL = []
|
||||
while datetime.datetime.now() < ts and len(retL) < byteOutN:
|
||||
|
||||
x = self.serialProc.recv()
|
||||
if x is not None:
|
||||
retL.append(x)
|
||||
|
||||
time.sleep(0.01)
|
||||
|
||||
return retL
|
||||
|
||||
|
||||
def note_on_vel( self, midi_pitch, midi_vel ):
|
||||
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
||||
TinyOpD['noteOnVelOp'],
|
||||
[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'],
|
||||
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.
|
||||
|
||||
def set_velocity_map( self, midi_pitch, midi_vel, pulse_usec ):
|
||||
pass
|
||||
|
||||
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 ):
|
||||
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
||||
TinyOpD['setPwmOp'],
|
||||
[enableFl, 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,
|
||||
[], 1, 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.read_block( self._pitch_to_i2c_addr( midi_pitch ),
|
||||
TinyRegAddr.kPwmFreqAddr,
|
||||
[], 1, time_out_ms )
|
||||
|
||||
def set_flags( self, midi_pitch, flags ):
|
||||
return self.write( self._pitch_to_i2c_addr( midi_pitch ),
|
||||
TinyOpD['writeOp'],
|
||||
[ 12, 14, flags ])
|
||||
|
||||
def make_note( self, midi_pitch, atk_us, dur_ms ):
|
||||
# TODO: handle error on note_on_us()
|
||||
self.note_on_us(midi_pitch, atk_us);
|
||||
time.sleep( dur_ms / 1000.0 )
|
||||
return self.note_off(midi_pitch)
|
||||
|
||||
def _pitch_to_i2c_addr( self, pitch ):
|
||||
return self.keyMapD[ pitch ]['index'] + self.i2c_base_addr
|
||||
|
||||
def _validate_vel( self, vel ):
|
||||
return vel
|
||||
|
||||
def _usec_to_coarse_and_fine( self, usec ):
|
||||
|
||||
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)
|
||||
|
||||
return coarse,fine
|
||||
|
||||
def _send( self, opcode, i2c_addr, reg_addr, byteL ):
|
||||
|
||||
self._print( opcode, i2c_addr, reg_addr, byteL )
|
||||
|
||||
byteA = bytearray( [ord(opcode), i2c_addr, reg_addr ] + byteL )
|
||||
|
||||
return self.serialProc.send(SerialMsgId.DATA_MSG, byteA )
|
||||
|
||||
def _print( self, opcode, i2c_addr, reg_addr, byteL ):
|
||||
|
||||
s = "{} {} {}".format( opcode, i2c_addr, reg_addr )
|
||||
|
||||
for x in byteL:
|
||||
s += " {}".format(x)
|
||||
|
||||
|
||||
print(s)
|
@ -3,6 +3,108 @@
|
||||
{
|
||||
serial_dev: "/dev/ttyACM0",
|
||||
serial_baud: 38400,
|
||||
i2c_base_addr: 21,
|
||||
prescaler_usec: 32,
|
||||
|
||||
key_mapL: [
|
||||
|
||||
{ index: 0, board: 1, ch: 1, type: 'wB', midi: 21, class: 'A' },
|
||||
{ index: 1, board: 1, ch: 2, type: 'Bl', midi: 22, class: 'A#' },
|
||||
{ index: 2, board: 1, ch: 3, type: 'wF', midi: 23, class: 'B' },
|
||||
{ index: 3, board: 1, ch: 4, type: 'wB', midi: 24, class: 'C' },
|
||||
{ index: 4, board: 1, ch: 5, type: 'Bl', midi: 25, class: 'C#' },
|
||||
{ index: 5, board: 1, ch: 6, type: 'wF', midi: 26, class: 'D' },
|
||||
{ index: 6, board: 1, ch: 7, type: 'Bl', midi: 27, class: 'D#' },
|
||||
{ index: 7, board: 1, ch: 8, type: 'wB', midi: 28, class: 'E' },
|
||||
{ index: 8, board: 1, ch: 9, type: 'wF', midi: 29, class: 'F' },
|
||||
{ index: 9, board: 1, ch: 10, type: 'Bl', midi: 30, class: 'F#' },
|
||||
{ index: 10, board: 1, ch: 11, type: 'wB', midi: 31, class: 'G' },
|
||||
|
||||
{ index: 11, board: 2, ch: 1, type: 'Bl', midi: 32, class: 'G#' },
|
||||
{ index: 12, board: 2, ch: 2, type: 'wF', midi: 33, class: 'A' },
|
||||
{ index: 13, board: 2, ch: 3, type: 'Bl', midi: 34, class: 'A#' },
|
||||
{ index: 14, board: 2, ch: 4, type: 'wB', midi: 35, class: 'B' },
|
||||
{ index: 15, board: 2, ch: 5, type: 'wF', midi: 36, class: 'C' },
|
||||
{ index: 16, board: 2, ch: 6, type: 'Bl', midi: 37, class: 'C#' },
|
||||
{ index: 17, board: 2, ch: 7, type: 'wB', midi: 38, class: 'D' },
|
||||
{ index: 18, board: 2, ch: 8, type: 'Bl', midi: 39, class: 'D#' },
|
||||
{ index: 19, board: 2, ch: 9, type: 'wF', midi: 40, class: 'E' },
|
||||
{ index: 20, board: 2, ch: 10, type: 'wB', midi: 41, class: 'F' },
|
||||
{ index: 21, board: 2, ch: 11, type: 'Bl', midi: 42, class: 'F#' },
|
||||
|
||||
{ index: 22, board: 3, ch: 1, type: 'wF', midi: 43, class: 'G' },
|
||||
{ index: 23, board: 3, ch: 2, type: 'Bl', midi: 44, class: 'G#' },
|
||||
{ index: 24, board: 3, ch: 3, type: 'wB', midi: 45, class: 'A' },
|
||||
{ index: 25, board: 3, ch: 4, type: 'Bl', midi: 46, class: 'A#' },
|
||||
{ index: 26, board: 3, ch: 5, type: 'wF', midi: 47, class: 'B' },
|
||||
{ index: 27, board: 3, ch: 6, type: 'wB', midi: 48, class: 'C' },
|
||||
{ index: 28, board: 3, ch: 7, type: 'Bl', midi: 49, class: 'C#' },
|
||||
{ index: 29, board: 3, ch: 8, type: 'wF', midi: 50, class: 'D' },
|
||||
{ index: 30, board: 3, ch: 9, type: 'Bl', midi: 51, class: 'D#' },
|
||||
{ index: 31, board: 3, ch: 10, type: 'wB', midi: 52, class: 'E' },
|
||||
{ index: 32, board: 3, ch: 11, type: 'wF', midi: 53, class: 'F' },
|
||||
|
||||
{ index: 33, board: 4, ch: 1, type: 'Bl', midi: 54, class: 'F#' },
|
||||
{ index: 34, board: 4, ch: 2, type: 'wB', midi: 55, class: 'G' },
|
||||
{ index: 35, board: 4, ch: 3, type: 'Bl', midi: 56, class: 'G#' },
|
||||
{ index: 36, board: 4, ch: 4, type: 'wF', midi: 57, class: 'A' },
|
||||
{ index: 37, board: 4, ch: 5, type: 'Bl', midi: 58, class: 'A#' },
|
||||
{ index: 38, board: 4, ch: 6, type: 'wB', midi: 59, class: 'B' },
|
||||
{ index: 39, board: 4, ch: 7, type: 'wF', midi: 60, class: 'C' },
|
||||
{ index: 40, board: 4, ch: 8, type: 'Bl', midi: 61, class: 'C#' },
|
||||
{ index: 41, board: 4, ch: 9, type: 'wB', midi: 62, class: 'D' },
|
||||
{ index: 42, board: 4, ch: 10, type: 'Bl', midi: 63, class: 'D#' },
|
||||
{ index: 43, board: 4, ch: 11, type: 'wF', midi: 64, class: 'E' },
|
||||
|
||||
{ index: 44, board: 5, ch: 1, type: 'wB', midi: 65, class: 'F' },
|
||||
{ index: 45, board: 5, ch: 2, type: 'Bl', midi: 66, class: 'F#' },
|
||||
{ index: 46, board: 5, ch: 3, type: 'wF', midi: 67, class: 'G' },
|
||||
{ index: 47, board: 5, ch: 4, type: 'Bl', midi: 68, class: 'G#' },
|
||||
{ index: 48, board: 5, ch: 5, type: 'wB', midi: 69, class: 'A' },
|
||||
{ index: 49, board: 5, ch: 6, type: 'Bl', midi: 70, class: 'A#' },
|
||||
{ index: 50, board: 5, ch: 7, type: 'wF', midi: 71, class: 'B' },
|
||||
{ index: 51, board: 5, ch: 8, type: 'wB', midi: 72, class: 'C' },
|
||||
{ index: 52, board: 5, ch: 9, type: 'Bl', midi: 73, class: 'C#' },
|
||||
{ index: 53, board: 5, ch: 10, type: 'wF', midi: 74, class: 'D' },
|
||||
{ index: 54, board: 5, ch: 11, type: 'Bl', midi: 75, class: 'D#' },
|
||||
|
||||
{ index: 55, board: 6, ch: 1, type: 'wB', midi: 76, class: 'E' },
|
||||
{ index: 56, board: 6, ch: 2, type: 'wF', midi: 77, class: 'F' },
|
||||
{ index: 57, board: 6, ch: 3, type: 'Bl', midi: 78, class: 'F#' },
|
||||
{ index: 58, board: 6, ch: 4, type: 'wB', midi: 79, class: 'G' },
|
||||
{ index: 59, board: 6, ch: 5, type: 'Bl', midi: 80, class: 'G#' },
|
||||
{ index: 60, board: 6, ch: 6, type: 'wF', midi: 81, class: 'A' },
|
||||
{ index: 61, board: 6, ch: 7, type: 'Bl', midi: 82, class: 'A#' },
|
||||
{ index: 62, board: 6, ch: 8, type: 'wB', midi: 83, class: 'B' },
|
||||
{ index: 63, board: 6, ch: 9, type: 'wF', midi: 84, class: 'C' },
|
||||
{ index: 64, board: 6, ch: 10, type: 'Bl', midi: 85, class: 'C#' },
|
||||
{ index: 65, board: 6, ch: 11, type: 'wB', midi: 86, class: 'D' },
|
||||
|
||||
{ index: 66, board: 6, ch: 1, type: 'Bl', midi: 87, class: 'D#' },
|
||||
{ index: 67, board: 6, ch: 2, type: 'wF', midi: 88, class: 'E' },
|
||||
{ index: 68, board: 6, ch: 3, type: 'wB', midi: 89, class: 'F' },
|
||||
{ index: 69, board: 6, ch: 4, type: 'Bl', midi: 90, class: 'F#' },
|
||||
{ index: 70, board: 6, ch: 5, type: 'wF', midi: 91, class: 'G' },
|
||||
{ index: 71, board: 6, ch: 6, type: 'Bl', midi: 92, class: 'G#' },
|
||||
{ index: 72, board: 6, ch: 7, type: 'wB', midi: 93, class: 'A' },
|
||||
{ index: 73, board: 6, ch: 8, type: 'Bl', midi: 94, class: 'A#' },
|
||||
{ index: 74, board: 6, ch: 9, type: 'wF', midi: 95, class: 'B' },
|
||||
{ index: 75, board: 6, ch: 10, type: 'wB', midi: 96, class: 'C' },
|
||||
{ index: 76, board: 6, ch: 11, type: 'Bl', midi: 97, class: 'C#' },
|
||||
|
||||
{ index: 77, board: 7, ch: 1, type: 'wF', midi: 98, class: 'D' },
|
||||
{ index: 78, board: 7, ch: 2, type: 'Bl', midi: 99, class: 'D#' },
|
||||
{ index: 79, board: 7, ch: 3, type: 'wB', midi: 100, class: 'E' },
|
||||
{ index: 80, board: 7, ch: 4, type: 'wF', midi: 101, class: 'F' },
|
||||
{ index: 81, board: 7, ch: 5, type: 'Bl', midi: 102, class: 'F#' },
|
||||
{ index: 82, board: 7, ch: 6, type: 'wB', midi: 103, class: 'G' },
|
||||
{ index: 83, board: 7, ch: 7, type: 'Bl', midi: 104, class: 'G#' },
|
||||
{ index: 84, board: 7, ch: 8, type: 'wF', midi: 105, class: 'A' },
|
||||
{ index: 85, board: 7, ch: 9, type: 'Bl', midi: 106, class: 'A#' },
|
||||
{ index: 86, board: 7, ch: 10, type: 'wB', midi: 107, class: 'B' },
|
||||
{ index: 87, board: 7, ch: 11, type: 'wF', midi: 108, class: 'C' },
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
|
169
control/app/picadae_shell.py
Normal file
169
control/app/picadae_shell.py
Normal file
@ -0,0 +1,169 @@
|
||||
import os,sys,argparse,yaml,types,select,serial,logging,time
|
||||
|
||||
from picadae_api import Picadae
|
||||
|
||||
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>"},
|
||||
'B':{ "func":"set_flags", "varN":2, "help":"flags <pitch> <flags>"},
|
||||
'N':{ "func":"make_note", "varN":3, "help":"note <pitch> atkUs durMs"},
|
||||
}
|
||||
|
||||
def _do_help( self, _ ):
|
||||
for k,d in self.parseD.items():
|
||||
s = "{} = {}".format( k, d['help'] )
|
||||
print(s)
|
||||
|
||||
def _do_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_flags( self, argL ):
|
||||
return self.p.set_flags(*argL)
|
||||
|
||||
def _do_make_note( self, argL ):
|
||||
return self.p.make_note(*argL)
|
||||
|
||||
def _syntaxError( self, msg ):
|
||||
print("Syntax Error: " + msg )
|
||||
return None
|
||||
|
||||
def _exec_cmd( self, tokL ):
|
||||
if len(tokL) <= 0:
|
||||
return None
|
||||
|
||||
opcode = tokL[0]
|
||||
|
||||
if opcode not in self.parseD:
|
||||
return self._syntaxError("Unknown opcode: '{}'.".format(opcode))
|
||||
|
||||
d = self.parseD[ opcode ]
|
||||
|
||||
func_name = "_do_" + d['func']
|
||||
|
||||
if hasattr(self, func_name ):
|
||||
func = getattr(self, func_name )
|
||||
|
||||
try:
|
||||
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)
|
||||
|
||||
return None
|
||||
|
||||
def run( self ):
|
||||
|
||||
# create the API object
|
||||
self.p = Picadae( cfg.key_mapL, cfg.i2c_base_addr, cfg.serial_dev, cfg.serial_baud )
|
||||
|
||||
print("'q'=quit '?'=help")
|
||||
time_out_secs = 1
|
||||
|
||||
while True:
|
||||
|
||||
# wait for keyboard activity
|
||||
i, o, e = select.select( [sys.stdin], [], [], time_out_secs )
|
||||
|
||||
if (i):
|
||||
# read the command
|
||||
s = sys.stdin.readline().strip()
|
||||
|
||||
# tokenize the command
|
||||
tokL = s.split(' ')
|
||||
|
||||
# if this is the 'quit' command
|
||||
if tokL[0] == 'q':
|
||||
break
|
||||
|
||||
# execute the command
|
||||
self._exec_cmd( tokL )
|
||||
|
||||
|
||||
self.p.close()
|
||||
|
||||
def parse_args():
|
||||
"""Parse the command line arguments."""
|
||||
|
||||
descStr = """Picadae auto-calibrate."""
|
||||
logL = ['debug','info','warning','error','critical']
|
||||
|
||||
ap = argparse.ArgumentParser(description=descStr)
|
||||
|
||||
|
||||
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")
|
||||
|
||||
return ap.parse_args()
|
||||
|
||||
|
||||
def parse_yaml_cfg( fn ):
|
||||
"""Parse the YAML configuration file."""
|
||||
|
||||
cfg = None
|
||||
|
||||
with open(fn,"r") as f:
|
||||
cfgD = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
cfg = types.SimpleNamespace(**cfgD['picadae_cmd'])
|
||||
|
||||
return cfg
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = parse_args()
|
||||
|
||||
cfg = parse_yaml_cfg( args.setup )
|
||||
|
||||
app = PicadaeShell(cfg)
|
||||
|
||||
app.run()
|
@ -78,12 +78,12 @@ void i2c_read_from( uint8_t i2c_addr, uint8_t dev_reg_addr, uint8_t read_byte_cn
|
||||
// Request to read from the client. Note that 'sendStop'==0.
|
||||
// Use this call to tell the client what data should be sent
|
||||
// during the subsequent twi_readFrom().
|
||||
twi_writeTo(I2C_REMOTE_ADDR, &dev_reg_addr, 1, kWaitFl, kNoSendStopFl);
|
||||
twi_writeTo(i2c_addr, &dev_reg_addr, 1, kWaitFl, kNoSendStopFl);
|
||||
|
||||
|
||||
// Blocking waiting and wait to read the client's response.
|
||||
for( uint8_t i=0; i<read_byte_cnt; ++i)
|
||||
if( twi_readFrom(I2C_REMOTE_ADDR, &recv_char, 1, i==read_byte_cnt-1) )
|
||||
if( twi_readFrom(i2c_addr, &recv_char, 1, i==read_byte_cnt-1) )
|
||||
uart_putchar(recv_char);
|
||||
|
||||
PORTB ^= _BV(PORTB5); // toggle LED
|
||||
@ -217,6 +217,7 @@ int main (void)
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle case where there are no data bytes (only e.g. note-off)
|
||||
state = kWait_for_value;
|
||||
data_buf[0] = dev_reg_addr; // make 'dev_reg_addr' the first data value to write
|
||||
data_buf_idx = 1; //
|
||||
@ -237,8 +238,13 @@ int main (void)
|
||||
|
||||
if(data_buf_idx == op_byte_cnt )
|
||||
{
|
||||
/*
|
||||
uint8_t ii;
|
||||
for(ii=0; ii<op_byte_cnt; ++ii)
|
||||
uart_putchar( data_buf[ii] );
|
||||
*/
|
||||
|
||||
i2c_xmit( I2C_REMOTE_ADDR, data_buf, op_byte_cnt, kSendStopFl);
|
||||
i2c_xmit( i2c_addr, data_buf, op_byte_cnt, kSendStopFl);
|
||||
state = kWait_for_cmd;
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,7 @@
|
||||
#
|
||||
# Usage: make I2C_ADDR=8
|
||||
#
|
||||
|
||||
ifndef TTY
|
||||
TTY=/dev/ttyACM0
|
||||
endif
|
||||
@ -9,7 +13,7 @@ endif
|
||||
MCU=attiny85
|
||||
AVRDUDEMCU=t85
|
||||
CC=/usr/bin/avr-gcc
|
||||
CFLAGS=-g -Os -Wall -mcall-prologues -mmcu=$(MCU)
|
||||
CFLAGS=-g -Os -Wall -mcall-prologues -mmcu=$(MCU) -DI2C_SLAVE_ADDRESS=$(I2C_ADDR)
|
||||
OBJ2HEX=/usr/bin/avr-objcopy
|
||||
AVRDUDE=avrdude
|
||||
|
||||
|
@ -1,10 +1,14 @@
|
||||
// w 60 0 1 10 : w i2c_addr SetPWM enable duty_val
|
||||
// w 60 5 12 8 32 : w i2c_addr write addrFl|src coarse_val
|
||||
// w 60 4 0 5 : w i2c_addr read src read_addr (set the read address to register 5)
|
||||
// r 60 4 3 : r i2c_addr <dum> cnt (read the first 3 reg's beginning w/ 5)
|
||||
/*
|
||||
AT TINY 85
|
||||
+--\/--+
|
||||
RESET _| 1 8 |_ +5V
|
||||
~OC1B HOLD DDB3 _| 2 7 |_ SCL
|
||||
~OC1B HOLD DDB3 _| 2 7 |_ SCL yellow
|
||||
OC1B ONSET DDB4 _| 3 6 |_ DDB1 LED
|
||||
GND _| 4 5 |_ SDA
|
||||
GND _| 4 5 |_ SDA orange
|
||||
+------+
|
||||
* = Serial and/or programming pins on Arduino as ISP
|
||||
*/
|
||||
@ -12,6 +16,7 @@
|
||||
|
||||
// This program acts as the device (slave) for the control program i2c/a2a/c_ctl
|
||||
#define F_CPU 8000000L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <avr/io.h>
|
||||
#include <util/delay.h>
|
||||
@ -19,50 +24,98 @@
|
||||
|
||||
#include "usiTwiSlave.h"
|
||||
|
||||
#define HOLD_DIR DDB3
|
||||
#define ATTK_DIR DDB4
|
||||
#define LED_DIR DDB1
|
||||
|
||||
#define I2C_SLAVE_ADDRESS 0x8 // the 7-bit address (remember to change this when adapting this example)
|
||||
#define HOLD_PIN PINB3
|
||||
#define ATTK_PIN PINB4
|
||||
#define LED_PIN PINB1
|
||||
|
||||
// Opcodes
|
||||
enum
|
||||
{
|
||||
kSetPwm_Op = 0, // Set PWM registers 0 {<enable> {<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
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
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
|
||||
kReg_Rd_Addr_idx = 0, // Next Reg Address to read
|
||||
kTable_Rd_Addr_idx = 1, // Next Table Address to read
|
||||
kEE_Rd_Addr_idx = 2, // Next EEPROM address to read
|
||||
kRead_Src_idx = 3, // kReg_Rd_Addr_idx=reg, kTable_Rd_Addr_idx=table, kEE_Rd_Addr_idx=eeprom
|
||||
|
||||
kReg_Wr_Addr_idx = 4, // Next Reg Address to write
|
||||
kTable_Wr_Addr_idx = 5, // Next Table Address to write
|
||||
kEE_Wr_Addr_idx = 6, // Next EEPROM address to write
|
||||
kWrite_Dst_idx = 7, // kReg_Wr_Addr_idx=reg, kTable_Wr_Addr_idx=table, kEE_Wr_Addr_idx=eeprom
|
||||
|
||||
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)
|
||||
|
||||
kPwm_Enable_idx = 11, //
|
||||
kPwm_Duty_idx = 12, //
|
||||
kPwm_Freq_idx = 13, //
|
||||
|
||||
kMode_idx = 14, // 1=repeat 2=pwm
|
||||
kState_idx = 15, // 1=attk 2=hold
|
||||
kError_Code_idx = 16, // Error Code
|
||||
kMax_idx
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kTmr_Repeat_Fl= 1,
|
||||
kTmr_Pwm_Fl = 2,
|
||||
kAttk_Fl = 1,
|
||||
kHold_Fl = 2
|
||||
};
|
||||
|
||||
// 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[] =
|
||||
{
|
||||
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
|
||||
0, // 0 (0-(kMax_idx-1)) Reg Read Addr
|
||||
0, // 1 (0-255) Table Read Addr
|
||||
0, // 2 (0-255) EE Read Addr
|
||||
kReg_Rd_Addr_idx, // 3 (0-2) Read source
|
||||
0, // 4 (0-(kMax_idx-1)) Reg Write Addr
|
||||
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
|
||||
};
|
||||
|
||||
#define tableN 256
|
||||
uint8_t table[ tableN ];
|
||||
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
|
||||
};
|
||||
|
||||
#define set_error( flag ) ctl_regs[ kError_Code_idx ] |= (flag)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
@ -113,7 +166,7 @@ uint8_t EEPROM_read(uint8_t ucAddress)
|
||||
// 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 )
|
||||
@ -138,19 +191,7 @@ void table_load( void )
|
||||
table[tbl_addr+1] = EEPROM_read( eeprom_addr(tbl_addr+1) );
|
||||
}
|
||||
}
|
||||
|
||||
void restore_memory_from_eeprom( void )
|
||||
{
|
||||
/*
|
||||
uint8_t i;
|
||||
for(i=0; i<kMax_idx; ++i)
|
||||
{
|
||||
ctl_regs[i] = EEPROM_read( eeprom_addr( i ) );
|
||||
}
|
||||
*/
|
||||
|
||||
table_load();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -163,11 +204,15 @@ void restore_memory_from_eeprom( void )
|
||||
volatile uint8_t tmr0_state = 0; // 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)
|
||||
|
||||
|
||||
// Use the current tmr0 ctl_reg[] values to set the timer to the starting state.
|
||||
void tmr0_reset()
|
||||
{
|
||||
// if a coarse count exists then go into coarse mode
|
||||
if( ctl_regs[kTmr0_Coarse_idx] > 0 )
|
||||
if( ctl_regs[kTmr_Coarse_idx] > 0 )
|
||||
{
|
||||
tmr0_state = 1;
|
||||
OCR0A = 0xff;
|
||||
@ -175,10 +220,14 @@ void tmr0_reset()
|
||||
else // otherwise go into fine mode
|
||||
{
|
||||
tmr0_state = 2;
|
||||
OCR0A = ctl_regs[kTmr0_Fine_idx];
|
||||
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
|
||||
}
|
||||
|
||||
ISR(TIMER0_COMPA_vect)
|
||||
@ -191,18 +240,50 @@ ISR(TIMER0_COMPA_vect)
|
||||
|
||||
case 1:
|
||||
// coarse mode
|
||||
if( ++tmr0_coarse_cur >= ctl_regs[kTmr0_Coarse_idx] )
|
||||
if( ++tmr0_coarse_cur >= ctl_regs[kTmr_Coarse_idx] )
|
||||
{
|
||||
tmr0_state = 2;
|
||||
OCR0A = ctl_regs[kTmr0_Fine_idx];
|
||||
OCR0A = ctl_regs[kTmr_Fine_idx];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// fine mode
|
||||
PINB = _BV(PINB4); // writes to PINB toggle the pins
|
||||
|
||||
tmr0_reset(); // restart the timer
|
||||
// If in repeat mode
|
||||
if(ctl_regs[kMode_idx] & kTmr_Repeat_Fl)
|
||||
{
|
||||
uint8_t fl = ctl_regs[kState_idx] & kAttk_Fl;
|
||||
|
||||
tmr0_reset(); // restart the timer
|
||||
|
||||
// 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] & kTmr_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
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -212,13 +293,12 @@ void timer0_init()
|
||||
{
|
||||
TIMSK &= ~_BV(OCIE0A); // Disable interrupt TIMER1_OVF
|
||||
TCCR0A |= 0x02; // CTC mode
|
||||
TCCR0B |= ctl_regs[kTmr0_Prescale_idx]; // set the prescaler
|
||||
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
|
||||
//tmr0_reset(); // set the timers starting state
|
||||
|
||||
TIMSK |= _BV(OCIE0A); // Enable interrupt TIMER1_OVF
|
||||
|
||||
}
|
||||
|
||||
@ -227,129 +307,28 @@ void timer0_init()
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// PWM (Timer0)
|
||||
// Pwm
|
||||
//
|
||||
|
||||
void pwm0_update()
|
||||
{
|
||||
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)
|
||||
// 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); // set direction on
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// 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
|
||||
// PWM is optimized to use pins OC1A ,~OC1A, OC1B, ~OC1B
|
||||
// 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
|
||||
OCR1B = ctl_regs[kPwm_Duty_idx]; // control duty cycle
|
||||
OCR1C = ctl_regs[kPwm_Freq_idx]; // PWM frequency pre-scaler
|
||||
}
|
||||
|
||||
|
||||
|
||||
ISR(TIMER1_OVF_vect)
|
||||
{
|
||||
PORTB |= _BV(HOLD_PIN); // set PWM pin
|
||||
}
|
||||
|
||||
ISR(TIMER1_COMPB_vect)
|
||||
{
|
||||
PORTB &= ~(_BV(PINB3)); // clear PWM pin
|
||||
PORTB &= ~(_BV(HOLD_PIN)); // clear PWM pin
|
||||
}
|
||||
|
||||
|
||||
@ -357,7 +336,7 @@ void pwm1_init()
|
||||
{
|
||||
TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // Disable interrupts
|
||||
|
||||
DDRB |= _BV(DDB3); // setup PB3 as output
|
||||
DDRB |= _BV(HOLD_DIR); // setup PB3 as output
|
||||
|
||||
// set on TCNT1 == 0 // happens when TCNT1 matches OCR1C
|
||||
// clr on OCR1B == TCNT // happens when TCNT1 matches OCR1B
|
||||
@ -367,11 +346,6 @@ void pwm1_init()
|
||||
GTCCR |= _BV(PSR1); // Set the pre-scaler to the selected value
|
||||
|
||||
pwm1_update();
|
||||
|
||||
TIMSK |= _BV(OCIE1B) + _BV(TOIE1); // Enable interrupts
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -389,38 +363,84 @@ const uint8_t reg_size = sizeof(ctl_regs);
|
||||
// than one byte of data (with TinyWireS.send) to the send-buffer when
|
||||
// using this callback
|
||||
//
|
||||
|
||||
void on_request()
|
||||
{
|
||||
uint8_t val = 0;
|
||||
|
||||
switch( reg_position )
|
||||
switch( ctl_regs[ kRead_Src_idx ] )
|
||||
{
|
||||
case kTable_Coarse_idx:
|
||||
val = table[ ctl_regs[kTable_Addr_idx]*2 + 0 ];
|
||||
case kReg_Rd_Addr_idx:
|
||||
val = ctl_regs[ ctl_regs[kReg_Rd_Addr_idx] ];
|
||||
break;
|
||||
|
||||
case kTable_Fine_idx:
|
||||
val = table[ ctl_regs[kTable_Addr_idx]*2 + 1 ];
|
||||
case kTable_Rd_Addr_idx:
|
||||
val = table[ ctl_regs[kTable_Rd_Addr_idx] ];
|
||||
break;
|
||||
|
||||
case kEE_Rd_Addr_idx:
|
||||
val = EEPROM_read(ctl_regs[kEE_Rd_Addr_idx]);
|
||||
break;
|
||||
|
||||
default:
|
||||
// read and transmit the requestd position
|
||||
val = ctl_regs[reg_position];
|
||||
|
||||
set_error( kInvalid_Read_Src_ErrFl );
|
||||
return;
|
||||
}
|
||||
|
||||
usiTwiTransmitByte(val);
|
||||
|
||||
// Increment the reg position on each read, and loop back to zero
|
||||
reg_position++;
|
||||
if (reg_position >= reg_size)
|
||||
{
|
||||
reg_position = 0;
|
||||
}
|
||||
ctl_regs[ ctl_regs[ kRead_Src_idx ] ] += 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _write_op( uint8_t* stack, uint8_t stackN )
|
||||
{
|
||||
uint8_t stack_idx = 0;
|
||||
|
||||
if( stackN > 0 )
|
||||
{
|
||||
uint8_t src = stack[0] & 0x07;
|
||||
uint8_t addr_fl = stack[0] & 0x08;
|
||||
|
||||
// verify the source value
|
||||
if( src < kReg_Wr_Addr_idx || src > kEE_Wr_Addr_idx )
|
||||
{
|
||||
set_error( kInvalid_Write_Dst_ErrFl );
|
||||
return;
|
||||
}
|
||||
|
||||
// set the write source
|
||||
stack_idx = 1;
|
||||
ctl_regs[ kWrite_Dst_idx ] = src;
|
||||
|
||||
// if an address value was passed also ....
|
||||
if( addr_fl && stackN > 1 )
|
||||
{
|
||||
stack_idx = 2;
|
||||
ctl_regs[ src ] = stack[1];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
for(; stack_idx<stackN; ++stack_idx)
|
||||
{
|
||||
uint8_t addr_idx = ctl_regs[ ctl_regs[kWrite_Dst_idx] ]++;
|
||||
uint8_t val = stack[ stack_idx ];
|
||||
|
||||
switch( ctl_regs[ kWrite_Dst_idx ] )
|
||||
{
|
||||
case kReg_Wr_Addr_idx: ctl_regs[ addr_idx ] = val; break;
|
||||
case kTable_Wr_Addr_idx: table[ addr_idx ] = val; break;
|
||||
case kEE_Wr_Addr_idx: EEPROM_write( table[ addr_idx ], val); break;
|
||||
|
||||
default:
|
||||
set_error( kInvalid_Write_Dst_ErrFl );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The I2C data received -handler
|
||||
//
|
||||
@ -432,87 +452,80 @@ void on_request()
|
||||
|
||||
void on_receive( uint8_t byteN )
|
||||
{
|
||||
if (byteN < 1)
|
||||
PINB = _BV(LED_PIN); // writes to PINB toggle the pins
|
||||
|
||||
const uint8_t stackN = 16;
|
||||
uint8_t stack_idx = 0;
|
||||
uint8_t stack[ stackN ];
|
||||
uint8_t i;
|
||||
|
||||
if (byteN < 1 || byteN > TWI_RX_BUFFER_SIZE)
|
||||
{
|
||||
// Sanity-check
|
||||
return;
|
||||
}
|
||||
|
||||
// get the register index to read/write
|
||||
uint8_t op_id = usiTwiReceiveByte();
|
||||
|
||||
byteN--;
|
||||
|
||||
// If only one byte was received then this was a read request
|
||||
// and the buffer pointer (reg_position) is now set to return the byte
|
||||
// at this location on the subsequent call to on_request() ...
|
||||
if(byteN)
|
||||
{
|
||||
while( byteN-- )
|
||||
{
|
||||
// Sanity-check
|
||||
return;
|
||||
}
|
||||
if (byteN > TWI_RX_BUFFER_SIZE)
|
||||
{
|
||||
// Also insane number
|
||||
return;
|
||||
stack[stack_idx] = usiTwiReceiveByte();
|
||||
++stack_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// get the register index to read/write
|
||||
reg_position = usiTwiReceiveByte();
|
||||
switch( op_id )
|
||||
{
|
||||
case kSetPwm_Op:
|
||||
for(i=0; i<stack_idx; ++i)
|
||||
ctl_regs[ kPwm_Enable_idx + i ] = stack[i];
|
||||
pwm1_update();
|
||||
break;
|
||||
|
||||
byteN--;
|
||||
case kNoteOnUsec_Op:
|
||||
for(i=0; i<stack_idx; ++i)
|
||||
ctl_regs[ kTmr_Coarse_idx + i ] = stack[i];
|
||||
tmr0_reset();
|
||||
break;
|
||||
|
||||
// If only one byte was received then this was a read request
|
||||
// and the buffer pointer (reg_position) is now set to return the byte
|
||||
// at this location on the subsequent call to on_request() ...
|
||||
if (!byteN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ... otherwise this was a write request and the buffer
|
||||
// pointer is now pointing to the first byte to write to
|
||||
while(byteN--)
|
||||
{
|
||||
// write the value
|
||||
ctl_regs[reg_position] = usiTwiReceiveByte();
|
||||
|
||||
// Set timer 1
|
||||
if( kTmr0_Prescale_idx <= reg_position && reg_position <= kTmr0_Fine_idx )
|
||||
{ timer0_init(); }
|
||||
else
|
||||
case kNoteOff_Op:
|
||||
TIMSK &= ~(_BV(OCIE1B) + _BV(TOIE1)); // PWM interupt disable interrupts
|
||||
PORTB &= ~_BV(HOLD_PIN); // clear the HOLD pin
|
||||
break;
|
||||
|
||||
|
||||
// Set PWM 0
|
||||
if( kPWM0_Duty_idx <= reg_position && reg_position <= kPWM0_Freq_idx )
|
||||
{ pwm0_update(); }
|
||||
else
|
||||
|
||||
// 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)
|
||||
case kRead_Op:
|
||||
if( stack_idx > 0 )
|
||||
{
|
||||
reg_position = 0;
|
||||
ctl_regs[ kRead_Src_idx ] = stack[0];
|
||||
|
||||
if( stack_idx > 1 )
|
||||
ctl_regs[ ctl_regs[ kRead_Src_idx ] ] = stack[1];
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
case kWrite_Op:
|
||||
_write_op( stack, stack_idx );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cli(); // mask all interupts
|
||||
|
||||
|
||||
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
|
||||
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();
|
||||
@ -525,9 +538,9 @@ int main(void)
|
||||
|
||||
sei();
|
||||
|
||||
PINB = _BV(PINB4); // writes to PINB toggle the pins
|
||||
PINB = _BV(LED_PIN); // writes to PINB toggle the pins
|
||||
_delay_ms(1000);
|
||||
PINB = _BV(PINB4); // writes to PINB toggle the pins
|
||||
PINB = _BV(LED_PIN); // writes to PINB toggle the pins
|
||||
|
||||
|
||||
while(1)
|
||||
|
Loading…
Reference in New Issue
Block a user