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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
//! # `profirust` - A PROFIBUS-DP compatible communication stack
//!
//! _profirust_ is structured according to the layered model of PROFIBUS:
//!
//! - The [`phy`] module abstracts physical interfaces for RS-485 communication.
//! - The [`fdl`] module implements the _Fieldbus Data Link_ layer of basic bus communication and
//! token passing between multiple master stations.
//! - The [`dp`] module implements the PROFIBUS-DP (Decentralized Peripherals) application layer.
//! This is where peripherals are managed and cyclic data exchange is facilitated.
//!
//! # Example
//! To successfully communicate with a peripheral, you need to initialize and parameterize all
//! layers. Here is an example:
//!
//! ```no_run
//! use profirust::{Baudrate, fdl, dp, phy};
//!
//! // Initialize the DP master:
//! // =========================
//! let buffer: [profirust::dp::PeripheralStorage; 4] = Default::default();
//! let mut dp_master = profirust::dp::DpMaster::new(buffer);
//! // or with `std`:
//! // let mut dp_master = dp::DpMaster::new(Vec::new());
//!
//! // Let's add a peripheral:
//! // =======================
//! let remoteio_address = 7;
//! let remoteio_options = dp::PeripheralOptions {
//! // ...
//! // best generated using `gsdtool`
//! // ...
//! ..Default::default()
//! };
//! let mut buffer_inputs = [0u8; 8];
//! let mut buffer_outputs = [0u8; 4];
//!
//! let remoteio_handle = dp_master.add(dp::Peripheral::new(
//! remoteio_address, remoteio_options, &mut buffer_inputs, &mut buffer_outputs
//! ));
//!
//! // Set up the FDL master and parameterize it:
//! // ==========================================
//! let master_address = 2;
//! let mut fdl_master = fdl::FdlMaster::new(
//! fdl::ParametersBuilder::new(master_address, Baudrate::B19200)
//! .slot_bits(300)
//! .build_verified(&dp_master)
//! );
//!
//! // Initialize the PHY layer:
//! // =========================
//! let mut phy = phy::LinuxRs485Phy::new("/dev/ttyS0", fdl_master.parameters().baudrate);
//!
//! // Now let's go live:
//! // ==================
//! fdl_master.set_online();
//! dp_master.enter_operate();
//!
//! // Main Application Cycle
//! // ======================
//! loop {
//! let now = profirust::time::Instant::now();
//! let events = fdl_master.poll(now, &mut phy, &mut dp_master);
//!
//! // Do something whenever new the DP cycle (for all peripherals) completes:
//! if events.cycle_completed {
//! let remoteio = dp_master.get_mut(remoteio_handle);
//! println!("Inputs: {:?}", remoteio.pi_i());
//!
//! // Set some output bits
//! let pi_q = remoteio.pi_q_mut();
//! pi_q[0] = 0x80;
//! }
//! }
//! ```
// TODO: Remove this once the crate has matured.
#![allow(dead_code)]
#![allow(unused_variables)]
#![cfg_attr(not(any(feature = "std", test)), no_std)]
mod consts;
pub mod dp;
pub mod fdl;
pub mod phy;
pub mod time;
#[cfg(all(test, feature = "std"))]
pub mod test_utils;
/// Baudrate for fieldbus communication
///
/// - PROFIBUS DP networks can run at any of the available baudrates given that all stations
/// support the selected speed.
/// - PROFIBUS PA networks must use `B31250` (31.25 kbit/s).
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum Baudrate {
/// 9.6 kbit/s
B9600,
/// 19.2 kbit/s
B19200,
/// 31.25 kbit/s
B31250,
/// 45.45 kbit/s
B45450,
/// 93.75 kbit/s
B93750,
/// 187.5 kbit/s
B187500,
/// 500 kbit/s
B500000,
/// 1.5 Mbit/s
B1500000,
/// 3 Mbit/s
B3000000,
/// 6 Mbit/s
B6000000,
/// 12 Mbit/s
B12000000,
}
impl Baudrate {
/// Convert baudrate into its numeric value in bit/s.
pub fn to_rate(self) -> u64 {
match self {
Baudrate::B9600 => 9600,
Baudrate::B19200 => 19200,
Baudrate::B31250 => 31250,
Baudrate::B45450 => 45450,
Baudrate::B93750 => 93750,
Baudrate::B187500 => 187500,
Baudrate::B500000 => 500000,
Baudrate::B1500000 => 1500000,
Baudrate::B3000000 => 3000000,
Baudrate::B6000000 => 6000000,
Baudrate::B12000000 => 12000000,
}
}
/// At this baudrate, return how long a given number of bits take to transmit.
pub fn bits_to_time(self, bits: u32) -> crate::time::Duration {
crate::time::Duration::from_micros(u64::from(bits) * 1000000 / self.to_rate())
}
/// At this baudrate, return how many bits could be transmitted in the given time.
pub fn time_to_bits(self, time: crate::time::Duration) -> u64 {
time.total_micros() * self.to_rate() / 1000000
}
}
#[cfg(test)]
mod tests {
#[test]
fn baudrate_time_conversions() {
let all_bauds = &[
crate::Baudrate::B9600,
crate::Baudrate::B19200,
crate::Baudrate::B31250,
crate::Baudrate::B45450,
crate::Baudrate::B93750,
crate::Baudrate::B187500,
crate::Baudrate::B500000,
crate::Baudrate::B1500000,
crate::Baudrate::B3000000,
crate::Baudrate::B6000000,
crate::Baudrate::B12000000,
];
let test_values = &[0, 1, 10, 100, 2000, 65536, u32::MAX];
for baud in all_bauds.iter().copied() {
for bits in test_values.iter().copied() {
let time = baud.bits_to_time(bits);
let micros = time.total_micros();
let bits2 = baud.time_to_bits(time);
let max_difference = match baud {
crate::Baudrate::B9600 => 1,
crate::Baudrate::B19200 => 1,
crate::Baudrate::B31250 => 1,
crate::Baudrate::B45450 => 1,
crate::Baudrate::B93750 => 1,
crate::Baudrate::B187500 => 1,
crate::Baudrate::B500000 => 1,
crate::Baudrate::B1500000 => 1,
crate::Baudrate::B3000000 => 2,
crate::Baudrate::B6000000 => 4,
crate::Baudrate::B12000000 => 10,
};
assert!(
u64::from(bits) - bits2 <= max_difference,
"{bits} (={micros}us) was converted to {bits2} (at {baud:?})"
);
}
}
}
}