16
16
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
17
18
18
use std:: convert:: { TryFrom , TryInto } ;
19
+ use std:: fs:: create_dir;
19
20
use std:: io:: Read ;
21
+ use std:: path:: Path ;
20
22
use std:: sync:: atomic:: { AtomicU16 , AtomicU64 , Ordering } ;
21
23
use std:: sync:: Mutex ;
22
24
use std:: thread;
23
- use std:: thread:: { sleep , JoinHandle } ;
25
+ use std:: thread:: JoinHandle ;
24
26
use std:: time:: { Duration , Instant } ;
25
27
26
28
use anyhow:: { anyhow, Context , Error , Result } ;
@@ -102,6 +104,8 @@ const CHANNELS_PWR: &[ChannelDesc] = &[
102
104
} ,
103
105
] ;
104
106
107
+ const TRIGGER_HR_PWR_DIR : & str = "/sys/kernel/config/iio/triggers/hrtimer/tacd-pwr" ;
108
+
105
109
const TIMESTAMP_ERROR : u64 = u64:: MAX ;
106
110
107
111
#[ derive( Clone , Copy ) ]
@@ -243,43 +247,50 @@ pub struct IioThread {
243
247
}
244
248
245
249
impl IioThread {
246
- fn adc_setup_stm32 ( ) -> Result < ( Vec < Channel > , Buffer ) > {
250
+ fn adc_setup (
251
+ adc_name : & str ,
252
+ trigger_name : & str ,
253
+ sample_rate : i64 ,
254
+ channel_descs : & [ ChannelDesc ] ,
255
+ buffer_len : usize ,
256
+ ) -> Result < ( Vec < Channel > , Buffer ) > {
247
257
let ctx = industrial_io:: Context :: new ( ) ?;
248
258
249
259
debug ! ( "IIO devices:" ) ;
250
260
for dev in ctx. devices ( ) {
251
261
debug ! ( " * {}" , & dev. name( ) . unwrap_or_default( ) ) ;
252
262
}
253
263
254
- let stm32_adc = ctx
255
- . find_device ( "48003000.adc:adc@0" )
256
- . ok_or ( anyhow ! ( "Could not find STM32 ADC" ) ) ?;
264
+ let adc = ctx
265
+ . find_device ( adc_name )
266
+ . ok_or ( anyhow ! ( "Could not find ADC: {}" , adc_name ) ) ?;
257
267
258
- if let Err ( err) = stm32_adc . attr_write_bool ( "buffer/enable" , false ) {
259
- warn ! ( "Failed to disable STM32 ADC buffer: {}" , err) ;
268
+ if let Err ( err) = adc . attr_write_bool ( "buffer/enable" , false ) {
269
+ warn ! ( "Failed to disable {} ADC buffer: {}" , adc_name , err) ;
260
270
}
261
271
262
- let stm32_channels : Vec < Channel > = CHANNELS_STM32
272
+ let channels : Vec < Channel > = channel_descs
263
273
. iter ( )
264
274
. map ( |ChannelDesc { kernel_name, .. } | {
265
- let ch = stm32_adc
275
+ let ch = adc
266
276
. find_channel ( kernel_name, false )
267
- . unwrap_or_else ( || panic ! ( "Failed to open iio channel {}" , kernel_name) ) ;
277
+ . unwrap_or_else ( || panic ! ( "Failed to open kernel channel {}" , kernel_name) ) ;
268
278
269
279
ch. enable ( ) ;
270
280
ch
271
281
} )
272
282
. collect ( ) ;
273
283
274
284
let trig = ctx
275
- . find_device ( "tim4_trgo" )
276
- . ok_or ( anyhow ! ( "Could not find STM32 Timer 4 trigger" ) ) ?;
277
- trig. attr_write_int ( "sampling_frequency" , 1024 ) ?;
285
+ . find_device ( trigger_name)
286
+ . ok_or ( anyhow ! ( "Could not find IIO trigger: {}" , trigger_name) ) ?;
287
+
288
+ trig. attr_write_int ( "sampling_frequency" , sample_rate) ?;
278
289
279
- stm32_adc . set_trigger ( & trig) ?;
290
+ adc . set_trigger ( & trig) ?;
280
291
ctx. set_timeout_ms ( 1000 ) ?;
281
292
282
- let stm32_buf = stm32_adc . create_buffer ( 128 , false ) ?;
293
+ let buf = adc . create_buffer ( buffer_len , false ) ?;
283
294
284
295
set_thread_priority_and_policy (
285
296
thread_native_id ( ) ,
@@ -288,10 +299,17 @@ impl IioThread {
288
299
)
289
300
. map_err ( |e| anyhow ! ( "Failed to set realtime thread priority: {e:?}" ) ) ?;
290
301
291
- Ok ( ( stm32_channels , stm32_buf ) )
302
+ Ok ( ( channels , buf ) )
292
303
}
293
304
294
- pub async fn new_stm32 ( ) -> Result < Arc < Self > > {
305
+ async fn new (
306
+ thread_name : & str ,
307
+ adc_name : & ' static str ,
308
+ trigger_name : & ' static str ,
309
+ sample_rate : i64 ,
310
+ channel_descs : & ' static [ ChannelDesc ] ,
311
+ buffer_len : usize ,
312
+ ) -> Result < Arc < Self > > {
295
313
// Some of the adc thread setup can only happen _in_ the adc thread,
296
314
// like setting the priority or some iio setup, as not all structs
297
315
// are Send.
@@ -303,16 +321,23 @@ impl IioThread {
303
321
304
322
// Spawn a high priority thread that updates the atomic values in `thread`.
305
323
let join = thread:: Builder :: new ( )
306
- . name ( "tacd stm32 iio" . to_string ( ) )
324
+ . name ( format ! ( "tacd {thread_name} iio" ) )
307
325
. spawn ( move || {
308
- let ( thread, channels, mut buf) = match Self :: adc_setup_stm32 ( ) {
326
+ let adc_setup_res = Self :: adc_setup (
327
+ adc_name,
328
+ trigger_name,
329
+ sample_rate,
330
+ channel_descs,
331
+ buffer_len,
332
+ ) ;
333
+ let ( thread, channels, mut buf) = match adc_setup_res {
309
334
Ok ( ( channels, buf) ) => {
310
335
let thread = Arc :: new ( Self {
311
336
ref_instant : Instant :: now ( ) ,
312
337
timestamp : AtomicU64 :: new ( TIMESTAMP_ERROR ) ,
313
338
values : channels. iter ( ) . map ( |_| AtomicU16 :: new ( 0 ) ) . collect ( ) ,
314
339
join : Mutex :: new ( None ) ,
315
- channel_descs : CHANNELS_STM32 ,
340
+ channel_descs,
316
341
} ) ;
317
342
318
343
( thread, channels, buf)
@@ -332,7 +357,7 @@ impl IioThread {
332
357
if let Err ( e) = buf. refill ( ) {
333
358
thread. timestamp . store ( TIMESTAMP_ERROR , Ordering :: Relaxed ) ;
334
359
335
- error ! ( "Failed to refill stm32 ADC buffer: {}" , e) ;
360
+ error ! ( "Failed to refill {} ADC buffer: {}" , adc_name , e) ;
336
361
337
362
// If the ADC has not yet produced any values we still have the
338
363
// queue at hand that signals readiness to the main thread.
@@ -377,127 +402,26 @@ impl IioThread {
377
402
Ok ( thread)
378
403
}
379
404
380
- fn adc_setup_powerboard ( ) -> Result < Vec < Channel > > {
381
- let ctx = industrial_io:: Context :: new ( ) ?;
382
-
383
- debug ! ( "IIO devices:" ) ;
384
- for dev in ctx. devices ( ) {
385
- debug ! ( " * {}" , & dev. name( ) . unwrap_or_default( ) ) ;
386
- }
387
-
388
- let pwr_adc = ctx
389
- . find_device ( "lmp92064" )
390
- . ok_or ( anyhow ! ( "Could not find Powerboard ADC" ) ) ?;
391
-
392
- ctx. set_timeout_ms ( 1000 ) ?;
393
-
394
- let pwr_channels: Vec < Channel > = CHANNELS_PWR
395
- . iter ( )
396
- . map ( |ChannelDesc { kernel_name, .. } | {
397
- pwr_adc
398
- . find_channel ( kernel_name, false )
399
- . unwrap_or_else ( || panic ! ( "Failed to open iio channel {}" , kernel_name) )
400
- } )
401
- . collect ( ) ;
402
-
403
- set_thread_priority_and_policy (
404
- thread_native_id ( ) ,
405
- ThreadPriority :: Crossplatform ( ThreadPriorityValue :: try_from ( 10 ) . unwrap ( ) ) ,
406
- ThreadSchedulePolicy :: Realtime ( RealtimeThreadSchedulePolicy :: Fifo ) ,
405
+ pub async fn new_stm32 ( ) -> Result < Arc < Self > > {
406
+ Self :: new (
407
+ "stm32" ,
408
+ "48003000.adc:adc@0" ,
409
+ "tim4_trgo" ,
410
+ 1024 ,
411
+ CHANNELS_STM32 ,
412
+ 128 ,
407
413
)
408
- . map_err ( |e| anyhow ! ( "Failed to set realtime thread priority: {e:?}" ) ) ?;
409
-
410
- Ok ( pwr_channels)
414
+ . await
411
415
}
412
416
413
417
pub async fn new_powerboard ( ) -> Result < Arc < Self > > {
414
- // Some of the adc thread setup can only happen _in_ the adc thread,
415
- // like setting the priority or some iio setup, as not all structs
416
- // are Send.
417
- // We do however not want to return from new() before we know that the
418
- // setup was sucessful.
419
- // This is why we create Self inside the thread and send it back
420
- // to the calling thread via a queue.
421
- let ( thread_res_tx, mut thread_res_rx) = bounded ( 1 ) ;
422
-
423
- // Spawn a high priority thread that updates the atomic values in `thread`.
424
- let join = thread:: Builder :: new ( )
425
- . name ( "tacd powerboard iio" . to_string ( ) )
426
- . spawn ( move || {
427
- let ( thread, channels) = match Self :: adc_setup_powerboard ( ) {
428
- Ok ( channels) => {
429
- let thread = Arc :: new ( Self {
430
- ref_instant : Instant :: now ( ) ,
431
- timestamp : AtomicU64 :: new ( TIMESTAMP_ERROR ) ,
432
- values : channels. iter ( ) . map ( |_| AtomicU16 :: new ( 0 ) ) . collect ( ) ,
433
- join : Mutex :: new ( None ) ,
434
- channel_descs : CHANNELS_PWR ,
435
- } ) ;
418
+ let hr_trigger_path = Path :: new ( TRIGGER_HR_PWR_DIR ) ;
436
419
437
- ( thread, channels)
438
- }
439
- Err ( e) => {
440
- thread_res_tx. try_send ( Err ( e) ) . unwrap ( ) ;
441
- return ;
442
- }
443
- } ;
444
-
445
- let thread_weak = Arc :: downgrade ( & thread) ;
446
- let mut signal_ready = Some ( ( thread, thread_res_tx) ) ;
447
-
448
- // Stop running as soon as the last reference to this Arc<IioThread>
449
- // is dropped (e.g. the weak reference can no longer be upgraded).
450
- while let Some ( thread) = thread_weak. upgrade ( ) {
451
- // Use the sysfs based interface to get the values from the
452
- // power board ADC at a slow sampling rate.
453
- let voltage = channels[ 0 ] . attr_read_int ( "raw" ) ;
454
- let current = channels[ 1 ] . attr_read_int ( "raw" ) ;
455
-
456
- let ( voltage, current) = match ( voltage, current) {
457
- ( Ok ( v) , Ok ( c) ) => ( v, c) ,
458
- ( Err ( e) , _) | ( _, Err ( e) ) => {
459
- thread. timestamp . store ( TIMESTAMP_ERROR , Ordering :: Relaxed ) ;
460
-
461
- error ! ( "Failed to read Powerboard ADC: {}" , e) ;
462
-
463
- // If the ADC has not yet produced any values we still have the
464
- // queue at hand that signals readiness to the main thread.
465
- // This gives us a chance to return an Err from new().
466
- // If the queue was already used just print an error instead.
467
- if let Some ( ( _, tx) ) = signal_ready. take ( ) {
468
- tx. try_send ( Err ( Error :: new ( e) ) ) . unwrap ( ) ;
469
- }
470
-
471
- break ;
472
- }
473
- } ;
474
-
475
- thread. values [ 0 ] . store ( voltage as u16 , Ordering :: Relaxed ) ;
476
- thread. values [ 1 ] . store ( current as u16 , Ordering :: Relaxed ) ;
477
-
478
- let ts: u64 = Instant :: now ( )
479
- . checked_duration_since ( thread. ref_instant )
480
- . unwrap ( )
481
- . as_nanos ( )
482
- . try_into ( )
483
- . unwrap ( ) ;
484
-
485
- thread. timestamp . store ( ts, Ordering :: Release ) ;
486
-
487
- // Now that we know that the ADC actually works and we have
488
- // initial values: return a handle to it.
489
- if let Some ( ( content, tx) ) = signal_ready. take ( ) {
490
- tx. try_send ( Ok ( content) ) . unwrap ( ) ;
491
- }
492
-
493
- sleep ( Duration :: from_millis ( 50 ) ) ;
494
- }
495
- } ) ?;
496
-
497
- let thread = thread_res_rx. next ( ) . await . unwrap ( ) ?;
498
- * thread. join . lock ( ) . unwrap ( ) = Some ( join) ;
420
+ if !hr_trigger_path. is_dir ( ) {
421
+ create_dir ( hr_trigger_path) . unwrap ( ) ;
422
+ }
499
423
500
- Ok ( thread )
424
+ Self :: new ( "powerboard" , "lmp92064" , "tacd-pwr" , 20 , CHANNELS_PWR , 1 ) . await
501
425
}
502
426
503
427
/// Use the channel names defined at the top of the file to get a reference
0 commit comments