@@ -237,23 +237,26 @@ class Analog_input(IO_object):
237237
238238 def __init__ (self , pin , name , sampling_rate , threshold = None , rising_event = None , falling_event = None , data_type = "H" ):
239239 if rising_event or falling_event :
240- self .threshold = Analog_threshold (threshold , rising_event , falling_event )
240+ if threshold is None :
241+ raise ValueError ("A threshold must be specified if rising or falling events are defined." )
242+ self .threshold_watcher = Analog_threshold_watcher (threshold , rising_event , falling_event )
241243 else :
242- self .threshold = False
244+ self .threshold_watcher = False
245+
243246 self .timer = pyb .Timer (available_timers .pop ())
244247 if pin : # pin argument can be None when Analog_input subclassed.
245248 self .ADC = pyb .ADC (pin )
246249 self .read_sample = self .ADC .read
247250 self .name = name
248- self .Analog_channel = Analog_channel (name , sampling_rate , data_type )
251+ self .channel = Analog_channel (name , sampling_rate , data_type )
249252 assign_ID (self )
250253
251254 def _run_start (self ):
252255 # Start sampling timer, initialise threshold, aquire first sample.
253- self .timer .init (freq = self .Analog_channel .sampling_rate )
256+ self .timer .init (freq = self .channel .sampling_rate )
254257 self .timer .callback (self ._timer_ISR )
255- if self .threshold :
256- self .threshold .run_start (self .read_sample ())
258+ if self .threshold_watcher :
259+ self .threshold_watcher .run_start (self .read_sample ())
257260 self ._timer_ISR (0 )
258261
259262 def _run_stop (self ):
@@ -263,9 +266,12 @@ def _run_stop(self):
263266 def _timer_ISR (self , t ):
264267 # Read a sample to the buffer, update write index.
265268 sample = self .read_sample ()
266- self .Analog_channel .put (sample )
267- if self .threshold :
268- self .threshold .check (sample )
269+ self .channel .put (sample )
270+ if self .threshold_watcher :
271+ self .threshold_watcher .check (sample )
272+
273+ def change_threshold (self , new_threshold ):
274+ self .threshold_watcher .set_threshold (new_threshold )
269275
270276 def record (self ): # For backward compatibility.
271277 pass
@@ -286,15 +292,21 @@ class Analog_channel(IO_object):
286292 # data array bytes (variable)
287293
288294 def __init__ (self , name , sampling_rate , data_type , plot = True ):
289- assert data_type in ("b" , "B" , "h" , "H" , "i" , "I" ), "Invalid data_type."
290- assert not any (
291- [name == io .name for io in IO_dict .values () if isinstance (io , Analog_channel )]
292- ), "Analog signals must have unique names."
295+ if data_type not in ("b" , "B" , "h" , "H" , "i" , "I" ):
296+ raise ValueError ("Invalid data_type." )
297+ if any ([name == io .name for io in IO_dict .values () if isinstance (io , Analog_channel )]):
298+ raise ValueError (
299+ "Analog signals must have unique names.{} {}" .format (
300+ name , [io .name for io in IO_dict .values () if isinstance (io , Analog_channel )]
301+ )
302+ )
303+
293304 self .name = name
294305 assign_ID (self )
295306 self .sampling_rate = sampling_rate
296307 self .data_type = data_type
297308 self .plot = plot
309+
298310 self .bytes_per_sample = {"b" : 1 , "B" : 1 , "h" : 2 , "H" : 2 , "i" : 4 , "I" : 4 }[data_type ]
299311 self .buffer_size = max (4 , min (256 // self .bytes_per_sample , sampling_rate // 10 ))
300312 self .buffers = (array (data_type , [0 ] * self .buffer_size ), array (data_type , [0 ] * self .buffer_size ))
@@ -344,45 +356,81 @@ def send_buffer(self, run_stop=False):
344356 fw .usb_serial .send (self .buffers [buffer_n ])
345357
346358
347- class Analog_threshold (IO_object ):
348- # Generates framework events when an analog signal goes above or below specified threshold.
359+ class Crossing :
360+ above = "above"
361+ below = "below"
362+ none = "none"
363+
364+
365+ class Analog_threshold_watcher (IO_object ):
366+ """
367+ Generates framework events when an analog signal goes above or below specified threshold.
368+ If given single threshold value, rising event is triggered when signal > threshold, falling event is triggered when signal <= threshold.
369+ If given tuple of two threshold values, rising event is triggered when signal > upper bound, falling event is triggered when signal < lower bound.
370+ """
349371
350- def __init__ (self , threshold = None , rising_event = None , falling_event = None ):
351- assert isinstance (
352- threshold , int
353- ), "Integer threshold must be specified if rising or falling events are defined."
354- self .threshold = threshold
372+ def __init__ (self , threshold , rising_event = None , falling_event = None ):
373+ self .set_threshold (threshold )
355374 self .rising_event = rising_event
356375 self .falling_event = falling_event
357376 self .timestamp = 0
358- self .crossing_direction = False
377+ self .last_crossing = Crossing . none
359378 assign_ID (self )
360379
380+ def set_threshold (self , threshold ):
381+ if isinstance (threshold , int ): # single threshold value
382+ self .upper_threshold = threshold
383+ self .lower_threshold = threshold + 1 # +1 so falling event is triggered when crossing into <= threshold
384+ elif isinstance (threshold , tuple ):
385+ threshold_requirements_str = "The threshold must be a single integer or a tuple of two integers (lower_bound, upper_bound) where lower_bound <= upper_bound."
386+ if len (threshold ) != 2 :
387+ raise ValueError ("{} is not a valid threshold. {}" .format (threshold , threshold_requirements_str ))
388+ lower , upper = threshold
389+ if not upper >= lower :
390+ raise ValueError (
391+ "{} is not a valid threshold because the lower bound {} is greater than the upper bound {}. {}" .format (
392+ threshold , lower , upper , threshold_requirements_str
393+ )
394+ )
395+ self .upper_threshold = upper
396+ self .lower_threshold = lower
397+ else :
398+ raise ValueError ("{} is not a valid threshold. {}" .format (threshold , threshold_requirements_str ))
399+
361400 def _initialise (self ):
362401 # Set event codes for rising and falling events.
363402 self .rising_event_ID = sm .events [self .rising_event ] if self .rising_event in sm .events else False
364403 self .falling_event_ID = sm .events [self .falling_event ] if self .falling_event in sm .events else False
365404 self .threshold_active = self .rising_event_ID or self .falling_event_ID
366405
367406 def run_start (self , sample ):
368- self .above_threshold = sample > self .threshold
407+ self .was_above = sample > self .upper_threshold
408+ self .was_below = sample < self .lower_threshold
369409
370410 def _process_interrupt (self ):
371411 # Put event generated by threshold crossing in event queue.
372- if self .crossing_direction :
412+ if self .was_above :
373413 fw .event_queue .put (fw .Datatuple (self .timestamp , fw .EVENT_TYP , "i" , self .rising_event_ID ))
374414 else :
375415 fw .event_queue .put (fw .Datatuple (self .timestamp , fw .EVENT_TYP , "i" , self .falling_event_ID ))
376416
377417 @micropython .native
378418 def check (self , sample ):
379- new_above_threshold = sample > self .threshold
380- if new_above_threshold != self .above_threshold : # Threshold crossing.
381- self .above_threshold = new_above_threshold
382- if (self .above_threshold and self .rising_event_ID ) or (not self .above_threshold and self .falling_event_ID ):
383- self .timestamp = fw .current_time
384- self .crossing_direction = self .above_threshold
419+ is_above_threshold = sample > self .upper_threshold
420+ is_below_threshold = sample < self .lower_threshold
421+
422+ if is_above_threshold and not self .was_above and self .last_crossing != Crossing .above :
423+ self .timestamp = fw .current_time
424+ self .last_crossing = Crossing .above
425+ if self .rising_event_ID :
385426 interrupt_queue .put (self .ID )
427+ elif is_below_threshold and not self .was_below and self .last_crossing != Crossing .below :
428+ self .timestamp = fw .current_time
429+ self .last_crossing = Crossing .below
430+ if self .falling_event_ID :
431+ interrupt_queue .put (self .ID )
432+
433+ self .was_above , self .was_below = is_above_threshold , is_below_threshold
386434
387435
388436# Digital Output --------------------------------------------------------------
0 commit comments