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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Copyright 2018 First Rust Competition Developers.
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use sensor_util;
use wpilib_sys::usage::{instances, resource_types};
use wpilib_sys::*;

/// Represents the amount to multiply the minimum servo-pulse pwm period by.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum PeriodMultiplier {
    /// Don't skip pulses. PWM pulses occur every 5.005 ms
    Multiplier1x = 0,
    /// Skip every other pulse. PWM pulses occur every 10.010 ms
    Multiplier2x = 1,
    /// Skip three out of four pulses. PWM pulses occur every 20.020 ms
    Multiplier4x = 3,
}

#[derive(Debug)]
pub struct PWM {
    channel: i32,
    handle: HAL_DigitalHandle,
}

impl PWM {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(channel: i32) -> HalResult<Self> {
        if !sensor_util::check_pwm_channel(channel) {
            return Err(HalError(0));
        }

        let handle = hal_call!(HAL_InitializePWMPort(HAL_GetPort(channel)))?;

        hal_call!(HAL_SetPWMDisabled(handle))?;
        hal_call!(HAL_SetPWMEliminateDeadband(handle, false as i32))?;

        report_usage(resource_types::PWM, channel as instances::Type);

        Ok(PWM { channel, handle })
    }

    /// Set the PWM value directly to the hardware.
    pub fn set_raw(&mut self, value: i32) -> HalResult<()> {
        hal_call!(HAL_SetPWMRaw(self.handle, value))
    }

    /// Get the PWM value directly from the hardware.
    pub fn raw(&self) -> HalResult<i32> {
        hal_call!(HAL_GetPWMRaw(self.handle))
    }

    /// Set the PWM value based on a position. `pos` must be between 0 and 1.
    pub fn set_position(&mut self, pos: f64) -> HalResult<()> {
        hal_call!(HAL_SetPWMPosition(self.handle, pos))
    }

    /// Get the PWM value in terms of a position.
    pub fn position(&self) -> HalResult<f64> {
        hal_call!(HAL_GetPWMPosition(self.handle))
    }

    /// Set the PWM value based on a speed between -1 and 1.
    pub fn set_speed(&mut self, speed: f64) -> HalResult<()> {
        hal_call!(HAL_SetPWMSpeed(self.handle, speed))
    }

    /// Get the PWM value in terms of speed.
    pub fn speed(&self) -> HalResult<f64> {
        hal_call!(HAL_GetPWMSpeed(self.handle))
    }

    /// Temporarily disables the PWM output. The next set call will re-enable.
    pub fn set_disabled(&mut self) -> HalResult<()> {
        hal_call!(HAL_SetPWMDisabled(self.handle))
    }

    /// Slow down the PWM signal for old devices.
    pub fn set_period_multiplier(&mut self, mult: PeriodMultiplier) -> HalResult<()> {
        hal_call!(HAL_SetPWMPeriodScale(self.handle, mult as i32))
    }

    /// Honestly, I have no idea what this does and it isn't documented in wpilib.
    pub fn set_zero_latch(&mut self) -> HalResult<()> {
        hal_call!(HAL_LatchPWMZero(self.handle))
    }

    /// Optionally eliminate the deadband from a speed controller.
    pub fn enable_deadband_elimination(&mut self, eliminate_deadband: bool) -> HalResult<()> {
        hal_call!(HAL_SetPWMEliminateDeadband(
            self.handle,
            eliminate_deadband as i32
        ))
    }

    /// Set the bounds on the PWM pulse widths. This sets the bounds on the PWM values for a
    /// particular type of controller. The values determine the upper and lower speeds as well as
    /// the deadband bracket.
    pub fn set_bounds(
        &mut self,
        max: f64,
        deadband_max: f64,
        center: f64,
        deadband_min: f64,
        min: f64,
    ) -> HalResult<()> {
        hal_call!(HAL_SetPWMConfig(
            self.handle,
            max,
            deadband_max,
            center,
            deadband_min,
            min
        ))
    }

    /// Set the bounds on the PWM values. This sets the bounds on the PWM values for a particular
    /// each type of controller. The values determine the upper and lower speeds as well as the
    /// deadband bracket.
    pub fn set_raw_bounds(
        &mut self,
        max: i32,
        deadband_max: i32,
        center: i32,
        deadband_min: i32,
        min: i32,
    ) -> HalResult<()> {
        hal_call!(HAL_SetPWMConfigRaw(
            self.handle,
            max,
            deadband_max,
            center,
            deadband_min,
            min
        ))
    }

    /// Get the bounds on the PWM values. This Gets the bounds on the PWM values for a particular
    /// each type of controller. The values determine the upper and lower speeds as well as the
    /// deadband bracket.
    pub fn raw_bounds(
        &self,
        max: &mut i32,
        deadband_max: &mut i32,
        center: &mut i32,
        deadband_min: &mut i32,
        min: &mut i32,
    ) -> HalResult<()> {
        hal_call!(HAL_GetPWMConfigRaw(
            self.handle,
            max,
            deadband_max,
            center,
            deadband_min,
            min
        ))
    }

    /// Get the channel of this device.
    pub fn channel(&self) -> i32 {
        self.channel
    }
}

impl Drop for PWM {
    fn drop(&mut self) {
        hal_call!(HAL_SetPWMDisabled(self.handle)).ok();
        hal_call!(HAL_FreePWMPort(self.handle)).ok();
    }
}

#[derive(Debug)]
pub struct PwmSpeedController {
    pwm: PWM,
    inverted: bool,
}

impl PwmSpeedController {
    #[allow(clippy::new_ret_no_self)]
    pub fn new(channel: i32) -> HalResult<Self> {
        Ok(PwmSpeedController {
            pwm: PWM::new(channel)?,
            inverted: false,
        })
    }

    /// Creates a PwmMotorController which is configured as a talonSRX
    pub fn new_talon(channel: i32) -> HalResult<PwmSpeedController> {
        let mut pwm = PWM::new(channel)?;
        pwm.set_bounds(2.004, 1.52, 1.5, 1.48, 0.997)?;
        pwm.set_period_multiplier(PeriodMultiplier::Multiplier1x)?;
        pwm.set_speed(0.0)?;
        pwm.set_zero_latch()?;
        report_usage(resource_types::PWMTalonSRX, channel as instances::Type);
        Ok(PwmSpeedController {
            pwm,
            inverted: false,
        })
    }

    /// Set the PWM value. The PWM value is set using a range of -1.0 to 1.0, appropriately scaling
    /// the value for the FPGA.
    pub fn set(&mut self, speed: f64) -> HalResult<()> {
        self.pwm
            .set_speed(if self.inverted { -speed } else { speed })
    }

    /// Get the recently set value of the PWM.
    pub fn get(&self) -> HalResult<f64> {
        if self.inverted {
            Ok(-self.pwm.speed()?)
        } else {
            self.pwm.speed()
        }
    }

    /// Sets if the provided speed is inverted by default when calling set.
    pub fn set_inverted(&mut self, inverted: bool) {
        self.inverted = inverted;
    }

    /// Gets if the PWM is being inverted.
    pub fn inverted(&self) -> bool {
        self.inverted
    }

    /// Disabled the PWM until the next update.
    pub fn disable(&mut self) -> HalResult<()> {
        self.pwm.set_disabled()
    }
}