-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpwm_generator.rs
137 lines (115 loc) · 5.79 KB
/
pwm_generator.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! 8-bit PWM sine wave generation on P9_29 (pru0_pru_r30_1).
//!
//! This example demonstrates:
//!
//! * PRU memory allocation,
//! * communication of data from host to PRU
//! * communication of data from PRU to host (and use of VolatileCell),
//! * two-way event signaling between host and PRU.
//!
//! The PRU code is designed to take exactly 10 PRU cycles per PWM subsamples independently of the
//! program flow. Since each PWM cycle is divided into 255 subsamples and the PRU is clocked at
//! 200MHz, the PWM frequency is approximately 78431Hz.
//!
//! The duty cycle of each subsample of the sine wave is specified as a fraction N/255 with
//! 0<=N<=255. The values N are stored in a 256-elements array allocated at 0x00000100 of the
//! local RAM of PRU0.
//! Other data exchanged between the host and the PRU is stored in a control struct allocated
//! at 0x00000000 of the local RAM of PRU0. The wave frequency is controlled by variable
//! `ctrl.sample_length` which sets the number of PWM sub-samples to be generated for any of the
//! 256 samples of the sine wave. Upon completion, the PRU writes the total number of subsamples
//! actually generated in variable `ctrl.sample_count`.
extern crate prusst;
use prusst::{Pruss, IntcConfig, Evtout, Sysevt};
use prusst::util::VolatileCell;
use std::fs::File;
use std::f32::consts;
use std::io::{self, Write};
use std::time::Duration;
use std::thread;
const ARRAY_BASE: usize = 0x100;
const NB_SAMPLES: usize = 256;
const PRU_FREQUENCY: f32 = 200e6;
const TICKS_PER_SUBSAMPLE: u32 = 10; // nb of PRU clock ticks per sub-sample
const MIN_SAMPLE_LENGTH: u32 = 255; // min nb of sub-samples per wave sample
#[repr(C)]
#[derive(Copy, Clone)]
struct Ctrl {
sample_count: VolatileCell<u32>, // number of full wavelengths already generated
sample_length: u32, // number of sub-samples per wave sample
}
fn main() {
// Get a view of the PRU subsystem.
let mut pruss = match Pruss::new(&IntcConfig::new_populated()) {
Ok(p) => p,
Err(e) => match e {
prusst::Error::AlreadyInstantiated
=> panic!("You can't instantiate more than one `Pruss` object at a time."),
prusst::Error::PermissionDenied
=> panic!("You do not have permission to access the PRU subsystem: \
maybe you should run this program as root?"),
prusst::Error::DeviceNotFound
=> panic!("The PRU subsystem could not be found: are you sure the `uio_pruss` \
module is loaded and supported by your kernel?"),
prusst::Error::OtherDeviceError
=> panic!("An unidentified problem occured with the PRU subsystem: \
do you have a valid overlay loaded?")
}
};
// Split the PRU data RAM into two segments, then allocate the control struct and allocate the
// waveform array; note that the array could have been a field of the Ctrl struct to avoid
// separate allocation, but this way we have learned how to that and as a bonus we get a
// nice round address for the array too...
let (mut bank1, mut bank2) = pruss.dram0.split_at(ARRAY_BASE);
let ctrl = bank1.alloc(Ctrl { sample_count: VolatileCell::new(0), sample_length: 0 });
let wave = unsafe { bank2.alloc_uninitialized::<[u8; NB_SAMPLES]>() };
// Ask for the amplitude and frequency of the wave.
let max_frequency: f32 = PRU_FREQUENCY /
(TICKS_PER_SUBSAMPLE as f32 * MIN_SAMPLE_LENGTH as f32 * NB_SAMPLES as f32);
let amplitude: f32 = get_input("Amplitude [%]", 0.0, 100.0)/100.0;
let frequency: f32 = get_input("Frequency [Hz]", 0.0, max_frequency);
let duration: f32 = get_input("Duration [s]", 0.0, 60.0);
// Compute the number of sub-sampling cycles per sample required for the requested frequency.
let sampling_frequency: f32 = frequency * (NB_SAMPLES as f32);
let sampling_period: f32 = 1.0/sampling_frequency;
let ticks_per_sample: f32 = sampling_period * PRU_FREQUENCY; // PRU clock ticks per sample
let sample_length: u32 = (ticks_per_sample / (TICKS_PER_SUBSAMPLE as f32)).round() as u32;
// Write the cycle length and generate the sine wave data.
println!("\nGenerating wave with frequency {} Hz", PRU_FREQUENCY /
(TICKS_PER_SUBSAMPLE as f32 * sample_length as f32 * NB_SAMPLES as f32) );
ctrl.sample_length = sample_length;
for (i, val) in wave.iter_mut().enumerate() {
let phi = (i as f32)/(NB_SAMPLES as f32)*(2.0 * consts::PI);
*val = (0.5*(1.0 - phi.cos()) * amplitude * 255.0).round() as u8;
}
// Get a handle to an event out.
let irq = pruss.intc.register_irq(Evtout::E0);
// Open and load a PRU binary.
let mut pru_binary = File::open("examples/pwm_generator.bin").unwrap();
unsafe { pruss.pru0.load_code(&mut pru_binary).unwrap().run(); }
// Request a PRU halt when the duration has elapsed.
thread::sleep(Duration::new(duration.floor() as u64,
(duration.fract()*1e9).floor() as u32));
pruss.intc.send_sysevt(Sysevt::S21);
// Await acknowledgement from PRU.
irq.wait();
pruss.intc.clear_sysevt(Sysevt::S19);
println!("{} PWM samples have been generated", ctrl.sample_count.get());
}
fn get_input(prompt: &str, min: f32, max: f32) -> f32 {
loop {
print!("{} ({}-{}): ", prompt, min, max);
io::stdout().flush().unwrap();
let mut val = String::new();
io::stdin().read_line(&mut val).expect("failed to read input");
if let Ok(val) = val.trim().parse::<f32>() {
if val>=min && val<=max {
return val;
} else {
println!("the input should be between {} and {}", min, max);
}
} else {
println!("not a number");
}
}
}