2 Commits

Author SHA1 Message Date
  kevin 5008c67002 app/picadae_api.py, picadae_shell.py : Added set/get_hold_delay(). Fixed shell._read(). 3 years ago
  kevin 78e533a61f tiny/i2c_timer_pwm.c : Added hold delay and other changes to validate operation after logic analyzer analysis. 3 years ago
3 changed files with 148 additions and 65 deletions
  1. 29
    2
      control/app/picadae_api.py
  2. 23
    21
      control/app/picadae_shell.py
  3. 96
    42
      control/tiny/i2c_timer_pwm.c

+ 29
- 2
control/app/picadae_api.py View File

@@ -16,7 +16,8 @@ class TinyOp(Enum):
16 16
     setReadAddr  = 4
17 17
     writeOp      = 5
18 18
     writeTableOp = 6
19
-    invalidOp    = 7
19
+    holdDelayOp  = 7
20
+    invalidOp    = 8
20 21
     
21 22
 
22 23
 class TinyRegAddr(Enum):
@@ -36,6 +37,9 @@ class TinyRegAddr(Enum):
36 37
     kPwmDivAddr      = 13
37 38
     kStateAddr       = 14
38 39
     kErrorCodeAddr   = 15
40
+    kMaxAllowTmrAddr = 16
41
+    kDelayCoarseAddr = 17
42
+    kDelayFineAddr   = 18
39 43
 
40 44
 class TinyConst(Enum):
41 45
     kRdRegSrcId    = TinyRegAddr.kRdRegAddrAddr.value    # 0
@@ -184,6 +188,7 @@ class Picadae:
184 188
         return self.write_tiny_reg( self._pitch_to_i2c_addr( midi_pitch ), op_code, argL )                          
185 189
 
186 190
     def set_read_addr( self, i2c_addr, mem_id, addr ):
191
+        # mem_id: 0=reg_array 1=vel_table 2=eeprom
187 192
         return self.write_tiny_reg(i2c_addr, TinyOp.setReadAddr.value,[ mem_id, addr ])
188 193
                 
189 194
     def read_request( self, i2c_addr, reg_addr, byteOutN ):
@@ -246,6 +251,19 @@ class Picadae:
246 251
         return self.call_op( midi_pitch, TinyOp.noteOffOp.value,
247 252
                            [0] )  # TODO: sending a dummy byte because we can't handle sending a command with no data bytes.
248 253
 
254
+    def set_hold_delay( self, midi_pitch, pulse_usec ):
255
+        return  self.call_op( midi_pitch, TinyOp.holdDelayOp.value, list(self._usec_to_coarse_and_fine(pulse_usec)) )
256
+
257
+    def get_hold_delay( self, midi_pitch, time_out_ms=250 ):
258
+
259
+        res = self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kDelayCoarseAddr.value, byteOutN=2, time_out_ms=time_out_ms )
260
+
261
+        if len(res.value) == 2:
262
+            res.value = [ self.prescaler_usec*255*res.value[0] + self.prescaler_usec*res.value[1] ]
263
+        
264
+        return res
265
+        
266
+    
249 267
     def set_velocity_map( self, midi_pitch, midi_vel, pulse_usec ):
250 268
         coarse,fine = self._usec_to_coarse_and_fine( pulse_usec )
251 269
         src         = TinyConst.kWrAddrFl.value | TinyConst.kWrTableDstId.value
@@ -258,7 +276,7 @@ class Picadae:
258 276
 
259 277
     def set_pwm_duty( self, midi_pitch, duty_cycle_pct ):
260 278
         if 0 <= duty_cycle_pct and duty_cycle_pct <= 100:
261
-            duty_cycle_pct = 100.0 - duty_cycle_pct
279
+            # duty_cycle_pct = 100.0 - duty_cycle_pct
262 280
             return self.call_op( midi_pitch, TinyOp.setPwmOp.value, [ int( duty_cycle_pct * 255.0 /100.0 )])
263 281
         else:
264 282
             return Result(msg="Duty cycle (%f) out of range 0-100." % (duty_cycle_pct))
@@ -279,6 +297,15 @@ class Picadae:
279 297
     def get_pwm_div( self, midi_pitch, time_out_ms=250 ):
280 298
         return self.block_on_picadae_read_reg( midi_pitch, TinyRegAddr.kPwmDivAddr.value, time_out_ms=time_out_ms )
281 299
 
300
+    def set_pwm_div( self, midi_pitch, div, time_out_ms=250 ):
301
+        res = self.get_pwm_duty( midi_pitch )
302
+        if res:
303
+            duty = res.value[0]
304
+            res = self.get_pwm_freq( midi_pitch )
305
+            if res:
306
+                res = self.call_op( midi_pitch, TinyOp.setPwmOp.value, [ int(duty), int(res.value[0]), int(div) ])
307
+        return res
308
+    
282 309
     def write_table( self, midi_pitch, time_out_ms=250 ):
283 310
         # TODO: sending a dummy byte because we can't handle sending a command with no data bytes.
284 311
         return self.call_op( midi_pitch, TinyOp.writeTableOp.value,[0])

+ 23
- 21
control/app/picadae_shell.py View File

@@ -10,25 +10,27 @@ class PicadaeShell:
10 10
     def __init__( self, cfg ):
11 11
         self.p      = None
12 12
         self.parseD = {
13
-            'q':{ "func":None,           "minN":0,  "maxN":0, "help":"quit"},
14
-            '?':{ "func":"_help",        "minN":0,  "maxN":0, "help":"Print usage text."},
15
-            'w':{ "func":"_write",       "minN":-1, "maxN":-1,"help":"write <i2c_addr> <reg_addr> <data0> ... <dataN>"},
16
-            'r':{ "func":"_read",        "minN":4,  "maxN":4, "help":"read  <i2c_addr> <src> <reg_addr> <byteN>"},
17
-            'v':{ "func":"note_on_vel",  "minN":2,  "maxN":2, "help":"note-on <pitch> <vel>"},
18
-            '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)"},
19
-            'o':{ "func":"note_off",     "minN":1,  "maxN":1, "help":"note-off <pitch>"},
20
-            'T':{ "func":"set_vel_map",  "minN":3,  "maxN":3, "help":"table <pitch> <vel> <usec>"},
21
-            't':{ "func":"get_vel_map",  "minN":2,  "maxN":2, "help":"table <pitch> <vel>"},
22
-            'D':{ "func":"set_pwm_duty", "minN":2,  "maxN":4, "help":"duty <pitch> <percent> {<hz> {<div>}} " },
23
-            'd':{ "func":"get_pwm_duty", "minN":1,  "maxN":1, "help":"duty <pitch>"},
24
-            'F':{ "func":"set_pwm_freq", "minN":2,  "maxN":2, "help":"freq <pitch> <hz> 254=~123Hz"},
25
-            'f':{ "func":"get_pwm_freq", "minN":1,  "maxN":1, "help":"freq <pitch>"},
26
-            'I':{ "func":"set_pwm_div",  "minN":2,  "maxN":2, "help":"div <pitch> <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"},
27
-            'i':{ "func":"get_pwm_div",  "minN":1,  "maxN":1, "help":"div <pitch>"},
28
-            'W':{ "func":"write_table",  "minN":1,  "maxN":1, "help":"write_table <pitch>"},
29
-            'N':{ "func":"make_note",    "minN":3,  "maxN":3, "help":"note <pitch> <atkUs> <durMs>"},
30
-            'S':{ "func":"make_seq",     "minN":5,  "maxN":5, "help":"seq  <pitch> <atkUs> <durMs> <deltaUs> <note_count>"}, 
31
-            'L':{ "func":"set_log_level","minN":1,  "maxN":1, "help":"log <level> (0-1)."}
13
+            'q':{ "func":None,             "minN":0,  "maxN":0, "help":"quit"},
14
+            '?':{ "func":"_help",          "minN":0,  "maxN":0, "help":"Print usage text."},
15
+            'w':{ "func":"_write",         "minN":-1, "maxN":-1,"help":"write <i2c_addr> <reg_addr> <data0> ... <dataN>"},
16
+            'r':{ "func":"_read",          "minN":4,  "maxN":4, "help":"read  <i2c_addr> <src> <reg_addr> <byteN>   src: 0=reg_array 1=vel_table 2=eeprom"},
17
+            'v':{ "func":"note_on_vel",    "minN":2,  "maxN":2, "help":"note-on <pitch> <vel>"},
18
+            'u':{ "func":"note_on_us",     "minN":2,  "maxN":3, "help":"note-on <pitch> <usec> <prescale> (1=1, 2=8 .5us, 3=64 4us,(4)=256 16us, 5=1024 64us)"},
19
+            'o':{ "func":"note_off",       "minN":1,  "maxN":1, "help":"note-off <pitch>"},
20
+            'T':{ "func":"set_vel_map",    "minN":3,  "maxN":3, "help":"table <pitch> <vel> <usec>"},
21
+            't':{ "func":"get_vel_map",    "minN":2,  "maxN":2, "help":"table <pitch> <vel>"},
22
+            'D':{ "func":"set_pwm_duty",   "minN":2,  "maxN":4, "help":"duty <pitch> <percent> {<hz> {<div>}} " },
23
+            'd':{ "func":"get_pwm_duty",   "minN":1,  "maxN":1, "help":"duty <pitch>"},
24
+            'H':{ "func":"set_hold_delay", "minN":2,  "maxN":2, "help":"hold delay <pitch> <usec>"},
25
+            'h':{ "func":"get_hold_delay", "minN":1,  "maxN":1, "help":"hold delay <pitch>"},
26
+            'F':{ "func":"set_pwm_freq",   "minN":2,  "maxN":2, "help":"pwm freq <pitch> <hz> 254=~123Hz"},
27
+            'f':{ "func":"get_pwm_freq",   "minN":1,  "maxN":1, "help":"pwm freq <pitch>"},
28
+            'I':{ "func":"set_pwm_div",    "minN":2,  "maxN":2, "help":"pwm div <pitch> <div> div:2=2,3=4,4=8,(5)=16 1us,6=32,7=64,8=128,9=256,10=512 32us, 11=1024,12=2048,13=4096,14=8192,15=16384"},
29
+            'i':{ "func":"get_pwm_div",    "minN":1,  "maxN":1, "help":"pwm div <pitch>"},
30
+            'W':{ "func":"write_table",    "minN":1,  "maxN":1, "help":"write_table <pitch>"},
31
+            'N':{ "func":"make_note",      "minN":3,  "maxN":3, "help":"note <pitch> <atkUs> <durMs>"},
32
+            'S':{ "func":"make_seq",       "minN":5,  "maxN":5, "help":"seq  <pitch> <atkUs> <durMs> <deltaUs> <note_count>"}, 
33
+            'L':{ "func":"set_log_level",  "minN":1,  "maxN":1, "help":"log <level> (0-1)."}
32 34
             }
33 35
 
34 36
     def _help( self, _=None ):
@@ -40,8 +42,8 @@ class PicadaeShell:
40 42
     def _write( self, argL ):
41 43
         return self.p.write(argL[0], argL[1], argL[2:])
42 44
     
43
-    def _read( self, argL ):
44
-        return self.p.block_on_picadae_read(argL[0], argL[1], argL[2], argL[3])
45
+    def _read( self, i2c_addr, src_id, reg_addr, byteN ):
46
+        return self.p.block_on_picadae_read(i2c_addr, src_id, reg_addr, byteN)
45 47
         
46 48
     def _syntaxError( self, msg ):
47 49
         print("Syntax Error: " + msg )

+ 96
- 42
control/tiny/i2c_timer_pwm.c View File

@@ -44,8 +44,9 @@ enum
44 44
  kNoteOff_Op        =  3,  // Turn off note         5
45 45
  kSetReadAddr_Op    =  4,  // Set a read addr.      6 {<src>} {<addr>} }  src: 0=reg 1=table 2=eeprom
46 46
  kWrite_Op          =  5,  // Set write             7 {<addrfl|src> {addr}  {<value0> ... {<valueN>}}  addrFl:0x80  src: 4=reg 5=table 6=eeprom
47
- kWriteTable_Op     =  6,  // Write table to EEprom 9 
48
- kInvalid_Op        =  7   //                                             
47
+ kWriteTable_Op     =  6,  // Write table to EEprom 9
48
+ kHoldDelay_Op      =  7,  // Set hold delay          {<coarse> {<fine>}}
49
+ kInvalid_Op        =  8   //                                             
49 50
 };
50 51
 
51 52
 
@@ -72,6 +73,10 @@ enum
72 73
  kState_idx          = 14, // 1=attk 2=hold
73 74
  kError_Code_idx     = 15, // Error Code
74 75
  kMax_Coarse_Tmr_idx = 16, // Max. allowable coarse timer value
76
+
77
+ kDelay_Coarse_idx   = 17, // (17,18)=2000 (0,6)=100
78
+ kDelay_Fine_idx     = 18,
79
+ 
75 80
  kMax_idx
76 81
 };
77 82
 
@@ -99,12 +104,16 @@ volatile uint8_t ctl_regs[] =
99 104
    4,                // 10 (1-5)    4=16us per tick
100 105
    
101 106
  127,                // 11 (0-255)  Pwm Duty cycle
102
- 254,                // 12 (0-255)  Pwm Frequency  (123 Hz)
103
-  10,                // 13 (0-15)   Pwm clock div 
107
+ 255,                // 12 (0-255)  Pwm Frequency  (123 Hz)
108
+   5,                // 13 (0-15)   Pwm clock div 
104 109
    
105 110
    0,                // 14 state flags 1=attk   2=hold  (read/only)
106 111
    0,                // 15 (0-255)  Error bit field
107 112
    14,               // 16 (0-255) Max allowable coarse timer count
113
+
114
+   0,                // 17 (0-255) Hold coarse delay  
115
+   6               // 18 (0-255) Hold fine delay    0,6=100us 0,124=2000us w/ 16us Tmr0 tick
116
+   
108 117
 };
109 118
 
110 119
 // These registers are saved to Eeprom
@@ -208,6 +217,34 @@ volatile uint8_t hold_state = 0;  // state=0 hold should not be set, state=1 hol
208 217
 #define clear_hold() PORTB &= ~(_BV(HOLD_PIN))
209 218
 #define set_hold()   PORTB |= _BV(HOLD_PIN)
210 219
 
220
+
221
+void hold_begin()
222
+{
223
+  hold_state = 1;
224
+  
225
+  // Reset the PWM counter to to OCR1C (PWM TOP) so that it immediately triggers
226
+  // set_hold() and latches any new value for OCR1B (See: 12.2.2 Timer/Counter1 in PWM Mode)
227
+  // If this is not done and OCR1B was modified the first pulse will have the incorrect length.
228
+  TCNT1   = ctl_regs[kPwm_Freq_idx];  
229
+  TIMSK  |= _BV(OCIE1B) + _BV(TOIE1);    // PWM interupt Enable interrupts
230
+
231
+  TCCR1  |= ctl_regs[ kPwm_Div_idx];     // 32us period (512 divider) prescaler
232
+  GTCCR  |= _BV(PSR1);                   // Force the pre-scale to be latched by setting PSR1
233
+  
234
+}
235
+
236
+void hold_end()
237
+{
238
+  clear_hold();
239
+  TIMSK  &= ~_BV(OCIE0A);                // Clear timer interrupt (shouldn't be necessary but doesn't hurt on during note-off message)
240
+  TIMSK  &= ~(_BV(OCIE1B) + _BV(TOIE1)); // PWM interupt disable interrupts
241
+
242
+  TCCR1  = 0;              // Stop the PWM timer by setting the pre-scale to 0
243
+  GTCCR  |= _BV(PSR1);     // Force the pre-scale to be latched by setting PSR1
244
+  
245
+  hold_state = 0;
246
+}
247
+
211 248
 // Use the current tmr0 ctl_reg[] values to set the timer to the starting state.
212 249
 void tmr0_reset()
213 250
 {
@@ -216,7 +253,11 @@ void tmr0_reset()
216 253
   PORTB                |= _BV(ATTK_PIN);   // set the attack pin
217 254
   clear_hold();                            // clear the hold pin
218 255
   hold_state = 0;
256
+
257
+  tmr0_state = 1;
258
+  OCR0A      = 0xff;
219 259
   
260
+  /*
220 261
   // if a coarse count exists then go into coarse mode 
221 262
   if( ctl_regs[kTmr_Coarse_idx] > 0 )
222 263
   {
@@ -228,6 +269,7 @@ void tmr0_reset()
228 269
     tmr0_state = 2;
229 270
     OCR0A     = ctl_regs[kTmr_Fine_idx];
230 271
   }
272
+  */
231 273
   
232 274
   TCNT0  = 0;
233 275
   TIMSK |= _BV(OCIE0A);     // enable the timer interrupt
@@ -237,32 +279,50 @@ ISR(TIMER0_COMPA_vect)
237 279
 {
238 280
   switch( tmr0_state )
239 281
   {
240
-    case 0:
241
-      // timer is disabled
282
+    case 0: // timer disabled
242 283
       break;
243 284
 
244
-    case 1: 
245
-      // coarse mode
246
-      if( ++tmr0_coarse_cur >= ctl_regs[kTmr_Coarse_idx] )
285
+    case 1: // attack coarse mode
286
+      // Note: the '+1' here is necessary to absorb an interrupt which is occurring
287
+      // for an unknown reason.  It must have something to do with resetting the
288
+      // OCIE0A interrupt because it doesn't occur on the hold delay coarse timing.
289
+      if( ++tmr0_coarse_cur >= ctl_regs[kTmr_Coarse_idx]+1 )
247 290
       {
248
-        tmr0_state  = 2;
249
-        OCR0A     = ctl_regs[kTmr_Fine_idx];        
291
+        tmr0_state = 2;
292
+        OCR0A      = ctl_regs[kTmr_Fine_idx];        
250 293
       }
251 294
       break;
252 295
 
253
-    case 2:
254
-      // fine mode
255
-
256
-      // This marks the end of a timer period 
257
-
296
+    case 2: // attack fine mode
258 297
       clear_attack();
259 298
 
260
-      TCNT1      = 0;   // reset the PWM counter to 0
261
-      hold_state = 1;   // enable the hold output
262
-      TIMSK  |= _BV(OCIE1B) + _BV(TOIE1);  // PWM interupt Enable interrupts          
263
-      TIMSK &= ~_BV(OCIE0A);               // clear timer interrupt
264
-      
299
+      // if a coarse delay count exists then go into coarse mode 
300
+      if( ctl_regs[kDelay_Coarse_idx] > 0 )
301
+      {
302
+        tmr0_state      = 3;
303
+        tmr0_coarse_cur = 0;
304
+        OCR0A           = 0xff;
305
+      }
306
+      else // otherwise go into fine mode
307
+      {
308
+        tmr0_state = 4;
309
+        OCR0A      = ctl_regs[kDelay_Fine_idx];
310
+      }
311
+      break;
312
+
313
+    case 3: // coarse hold delay
314
+      if( ++tmr0_coarse_cur >= ctl_regs[kDelay_Coarse_idx] )
315
+      {
316
+        tmr0_state = 4;
317
+        OCR0A      = ctl_regs[kDelay_Fine_idx];        
318
+      }      
265 319
       break;
320
+      
321
+    case 4: // hold delay end
322
+      TIMSK      &= ~_BV(OCIE0A);    // clear timer interrupt
323
+      tmr0_state  = 0;
324
+      hold_begin();
325
+      break;      
266 326
   }
267 327
 }
268 328
 
@@ -270,9 +330,11 @@ ISR(TIMER0_COMPA_vect)
270 330
 void tmr0_init()
271 331
 {
272 332
   TIMSK  &= ~_BV(OCIE0A);                 // Disable interrupt TIMER1_OVF
333
+  TCCR0A = 0;                             // Set the timer control registers to their default value
334
+  TCCR0B = 0;
273 335
   TCCR0A  |=  0x02;                       // CTC mode
274 336
   TCCR0B  |= ctl_regs[kTmr_Prescale_idx]; // set the prescaler
275
-  GTCCR   |= _BV(PSR0);                   // Set the pre-scaler to the selected value
337
+  GTCCR   |= _BV(PSR0);                   // Trigger the pre-scaler to be reset to the selected value
276 338
 }
277 339
 
278 340
 
@@ -297,27 +359,24 @@ void pwm1_update()
297 359
 // At this point TCNT1 is reset to 0, new OCR1B values are latched from temp. loctaion to OCR1B
298 360
 ISR(TIMER1_OVF_vect)
299 361
 {
300
-  clear_hold();
362
+  set_hold();
301 363
 }
302 364
 
303 365
 // Called when TCNT1 == OCR1B
304 366
 ISR(TIMER1_COMPB_vect)
305 367
 {
306
-  if(hold_state)
307
-    set_hold();
368
+  clear_hold();
308 369
 }
309 370
 
310 371
 
311 372
 void pwm1_init()
312 373
 {
313
-  TIMSK  &= ~(_BV(OCIE1B) + _BV(TOIE1));    // Disable interrupts
314
-  
315 374
   DDRB   |=  _BV(HOLD_DIR);  // setup PB3 as output
316
-
317
-  TCCR1  |= ctl_regs[ kPwm_Div_idx];    // 32us period (512 divider) prescaler
375
+  
376
+  TCCR1 = 0; // Set the control registers to their default
377
+  GTCCR = 0;
318 378
   GTCCR  |= _BV(PWM1B);    // Enable PWM B and disconnect output pins
319
-  GTCCR  |= _BV(PSR1);     // Set the pre-scaler to the selected value
320
-
379
+  
321 380
   pwm1_update();
322 381
 
323 382
 }
@@ -462,14 +521,6 @@ void on_receive( uint8_t byteN )
462 521
       for(i=0; i<stack_idx && i<3; ++i)
463 522
         ctl_regs[ kPwm_Duty_idx + i ] = stack[i];
464 523
 
465
-      // if the PWM prescaler was changed
466
-      if( i == 3 )
467
-      {
468
-        cli();
469
-        pwm1_init();
470
-        sei();
471
-      }
472
-      
473 524
       pwm1_update();
474 525
       break;
475 526
 
@@ -496,9 +547,7 @@ void on_receive( uint8_t byteN )
496 547
       break;
497 548
 
498 549
     case kNoteOff_Op:
499
-      TIMSK  &= ~_BV(OCIE0A);                // clear timer interrupt (shouldn't be necessary)
500
-      //TIMSK  &= ~(_BV(OCIE1B) + _BV(TOIE1)); // PWM interupt disable interrupts
501
-      hold_state = 0;
550
+      hold_end();
502 551
       break;
503 552
 
504 553
     case kSetReadAddr_Op:
@@ -518,6 +567,11 @@ void on_receive( uint8_t byteN )
518 567
     case kWriteTable_Op:
519 568
       write_table();
520 569
       break;
570
+
571
+    case kHoldDelay_Op:
572
+      for(i=0; i<stack_idx && i<2; ++i)
573
+        ctl_regs[ kDelay_Coarse_idx + i ] = stack[i];
574
+      
521 575
   }
522 576
 }
523 577
 

Loading…
Cancel
Save