From 54c2f70a93d638ce20a4b61a975ec843e45c3e65 Mon Sep 17 00:00:00 2001 From: Greg Ingram Date: Sat, 6 Jul 2019 21:18:47 -0400 Subject: [PATCH 1/7] Initial PwmChannel changes --- .../Pwm/Channels/UnixPwmChannel.Linux.cs | 178 ++++++++++++++++++ .../Channels/Windows10PwmChannel.Windows.cs | 45 +++++ .../Device/Pwm/Drivers/UnixPwmDriver.Linux.cs | 139 -------------- .../Pwm/Drivers/UnixPwmDriver.Windows.cs | 21 --- .../Pwm/Drivers/Windows10PwmDriver.Linux.cs | 21 --- .../Pwm/Drivers/Windows10PwmDriver.Windows.cs | 110 ----------- .../Windows10PwmDriverChannel.Windows.cs | 47 ----- .../Drivers/Windows10PwmDriverChip.Windows.cs | 116 ------------ .../System/Device/Pwm/IPwmController.cs | 47 ----- .../System/Device/Pwm/PwmChannel.Linux.cs | 28 +++ .../System/Device/Pwm/PwmChannel.Windows.cs | 28 +++ .../System/Device/Pwm/PwmChannel.cs | 43 +++++ .../System/Device/Pwm/PwmController.Linux.cs | 16 -- .../Device/Pwm/PwmController.Windows.cs | 16 -- .../System/Device/Pwm/PwmController.cs | 127 ------------- .../System/Device/Pwm/PwmDriver.cs | 58 ------ .../Commands/Pwm/PwmCommand.cs | 30 +-- .../Commands/Pwm/PwmDriverType.cs | 21 --- .../Commands/Pwm/PwmPinOutput.cs | 21 +-- 19 files changed, 338 insertions(+), 774 deletions(-) create mode 100644 src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs create mode 100644 src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Linux.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Linux.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChannel.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChip.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/IPwmController.cs create mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs create mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs create mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmController.Linux.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmController.Windows.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmController.cs delete mode 100644 src/System.Device.Gpio/System/Device/Pwm/PwmDriver.cs delete mode 100644 tools/DevicesApiTester/Commands/Pwm/PwmDriverType.cs diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs new file mode 100644 index 0000000000..7eeb8e2930 --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs @@ -0,0 +1,178 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Threading; + +namespace System.Device.Pwm.Channels +{ + /// + /// Represents a PWM channel running on Unix. + /// + internal class UnixPwmChannel : PwmChannel + { + private readonly int _chip; + private readonly int _channel; + private int _frequency; + private double _dutyCyclePercentage; + private readonly string _chipPath; + private readonly string _channelPath; + + /// + /// Initializes a new instance of the class. + /// + /// The PWM chip number. + /// The PWM channel number. + /// The frequency in hertz. + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + public UnixPwmChannel( + int chip, + int channel, + int frequency, + double dutyCyclePercentage = 0.5) + { + _chipPath = $"/sys/class/pwm/pwmchip{_chip}"; + _channelPath = $"{_chipPath}/pwm{_channel}"; + _chip = chip; + _channel = channel; + Validate(); + Open(); + //Thread.Sleep(100); // TODO: Need a better solution to delay for available file. + Console.WriteLine("Set Frequency"); + SetFrequency(frequency); + DutyCyclePercentage = dutyCyclePercentage; + } + + /// + /// The frequency in hertz. + /// + public override int Frequency =>_frequency; + + /// + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// + public override double DutyCyclePercentage + { + get + { + return _dutyCyclePercentage; + } + set + { + SetDutyCyclePercentage(value); + } + } + + /// + /// Gets the frequency period in nanoseconds. + /// + /// The frequency in hertz. + /// The frequency period in nanoseconds. + private static int GetPeriodInNanoSeconds(int frequency) + { + // In Linux, the period needs to be a whole number and can't have a decimal point. + return (int)((1.0 / frequency) * 1_000_000_000); + } + + /// + /// Sets the frequency for the channel. + /// + /// The frequency in hertz to set. + private void SetFrequency(int frequency) + { + int periodInNanoSeconds = GetPeriodInNanoSeconds(frequency); + File.WriteAllText($"{_channelPath}/period", Convert.ToString(periodInNanoSeconds)); + _frequency = frequency; + } + + /// + /// Sets the duty cycle percentage for the channel. + /// + /// The duty cycle percentage to set represented as a value between 0.0 and 1.0. + private void SetDutyCyclePercentage(double dutyCyclePercentage) + { + if (dutyCyclePercentage < 0 || dutyCyclePercentage > 1) + { + throw new ArgumentOutOfRangeException(nameof(dutyCyclePercentage), dutyCyclePercentage, "Value must be between 0.0 and 1.0."); + } + + // In Linux, the period needs to be a whole number and can't have decimal point. + int dutyCycleInNanoSeconds = (int)(GetPeriodInNanoSeconds(_frequency) * dutyCyclePercentage); + File.WriteAllText($"{_channelPath}/duty_cycle", Convert.ToString(dutyCycleInNanoSeconds)); + _dutyCyclePercentage = dutyCyclePercentage; + } + + /// + /// Verifies the specified chip and channel are available. + /// + private void Validate() + { + if (!Directory.Exists(_chipPath)) + { + throw new ArgumentException($"The chip number {_chip} is invalid or is not enabled."); + } + + string npwmPath = $"{_chipPath}/npwm"; + + if (int.TryParse(File.ReadAllText(npwmPath), out int numberOfSupportedChannels)) + { + if (_channel < 0 || _channel >= numberOfSupportedChannels) + { + throw new ArgumentException($"The PWM chip {_chip} does not support the channel {_channel}."); + } + } + else + { + throw new IOException($"Unable to parse the number of supported channels at {npwmPath}."); + } + } + + /// + /// Stops and closes the channel. + /// + private void Close() + { + if (Directory.Exists(_channelPath)) + { + Stop(); + File.WriteAllText($"{_chipPath}/unexport", Convert.ToString(_channel)); + } + } + + /// + /// Opens the channel. + /// + private void Open() + { + if (!Directory.Exists(_channelPath)) + { + File.WriteAllText($"{_chipPath}/export", Convert.ToString(_channel)); + } + } + + /// + /// Starts writing to the channel. + /// + public override void Start() + { + string enablePath = $"{_channelPath}/enable"; + File.WriteAllText(enablePath, "1"); + } + + /// + /// Stops writing to the channel. + /// + public override void Stop() + { + string enablePath = $"{_channelPath}/enable"; + File.WriteAllText(enablePath, "0"); + } + + protected override void Dispose(bool disposing) + { + Close(); + base.Dispose(disposing); + } + } +} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs new file mode 100644 index 0000000000..caf146810d --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.IO; +using System.Threading; + +namespace System.Device.Pwm.Channels +{ + /// + /// Represents a PWM channel running on Windows 10 IoT. + /// + internal class Windows10PwmChannel : PwmChannel + { + /// + /// Initializes a new instance of the class. + /// + /// The PWM chip number. + /// The PWM channel number. + /// The frequency in hertz. + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + public Windows10PwmChannel( + int chip, + int channel, + int frequency, + double dutyCyclePercentage = 0.5) + { + // TODO: This is just a placeholder to complete later. + } + + public override int Frequency => throw new NotImplementedException(); + + public override double DutyCyclePercentage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public override void Start() + { + throw new NotImplementedException(); + } + + public override void Stop() + { + throw new NotImplementedException(); + } + } +} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Linux.cs deleted file mode 100644 index d7b064743b..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Linux.cs +++ /dev/null @@ -1,139 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace System.Device.Pwm.Drivers -{ - /// - /// A PWM driver for Unix. - /// - public class UnixPwmDriver : PwmDriver - { - private const string PwmPath = "/sys/class/pwm"; - - /// - /// Collection that holds the exported channels and the period in nanoseconds. - /// - private readonly Dictionary<(int, int), int> _exportedChannels = new Dictionary<(int, int), int>(); - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The duty cycle percentage to change. - protected internal override void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage) - { - if (_exportedChannels[(pwmChip, pwmChannel)] == 1) - { - throw new InvalidOperationException("Can not change the duty cycle if channel has not started writing."); - } - int dutyCycleInNanoSeconds = (int)(_exportedChannels[(pwmChip, pwmChannel)] * dutyCyclePercentage / 100.0); - string dutyCyclePath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}", "duty_cycle"); - File.WriteAllText(dutyCyclePath, Convert.ToString(dutyCycleInNanoSeconds)); - } - - /// - /// Closes an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void CloseChannel(int pwmChip, int pwmChannel) - { - ValidatePwmChannel(pwmChip, pwmChannel); - string channelPath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}"); - if (Directory.Exists(channelPath)) - { - File.WriteAllText(Path.Combine(PwmPath, $"pwmchip{pwmChip}", "unexport"), Convert.ToString(pwmChannel)); - _exportedChannels.Remove((pwmChip, pwmChannel)); - } - } - - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void OpenChannel(int pwmChip, int pwmChannel) - { - ValidatePwmChannel(pwmChip, pwmChannel); - string channelPath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}"); - if (!Directory.Exists(channelPath)) - { - File.WriteAllText(Path.Combine(PwmPath, $"pwmchip{pwmChip}", "export"), Convert.ToString(pwmChannel)); - _exportedChannels.Add((pwmChip, pwmChannel), -1); - } - } - - /// - /// Starts writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The frequency in hertz to write. - /// The duty cycle percentage to write. - protected internal override void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) - { - // In Linux, the period needs to be a whole number and can't have decimal point. - int periodInNanoSeconds = (int)((1.0 / frequencyInHertz) * 1_000_000_000); - // In Linux, the duty cycle needs to be a whole number and can't have decimal point. - int dutyCycleInNanoSeconds = (int)(periodInNanoSeconds * dutyCyclePercentage / 100.0); - - string periodPath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}", "period"); - File.WriteAllText(periodPath, Convert.ToString(periodInNanoSeconds)); - _exportedChannels[(pwmChip, pwmChannel)] = periodInNanoSeconds; - - string dutyCyclePath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}", "duty_cycle"); - File.WriteAllText(dutyCyclePath, Convert.ToString(dutyCycleInNanoSeconds)); - - string enablePath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}", "enable"); - File.WriteAllText(enablePath, "1"); // Enable PWM. - } - - /// - /// Stops writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void StopWriting(int pwmChip, int pwmChannel) - { - string enablePath = Path.Combine(PwmPath, $"pwmchip{pwmChip}", $"pwm{pwmChannel}", "enable"); - File.WriteAllText(enablePath, "0"); // Disable PWM - } - - private void ValidatePwmChannel(int pwmChip, int pwmChannel) - { - string chipPath = Path.Combine(PwmPath, $"pwmchip{pwmChip}"); - if (!Directory.Exists(chipPath)) - { - throw new ArgumentException($"The chip number {pwmChip} is invalid or is not enabled."); - } - string supportedChannels = File.ReadAllText(Path.Combine(chipPath, "npwm")); - if (int.TryParse(supportedChannels, out int numSupportedChannels)) - { - if (pwmChip < 0 || pwmChip >= numSupportedChannels) - { - throw new ArgumentException($"The PWM chip {pwmChip} does not support the channel {pwmChannel}."); - } - } - else - { - throw new IOException($"Unable to parse the number of supported channels at {Path.Combine(chipPath, "npwm")}."); - } - } - - protected override void Dispose(bool disposing) - { - while (_exportedChannels.Count > 0) - { - (int, int) channel = _exportedChannels.FirstOrDefault().Key; - CloseChannel(channel.Item1, channel.Item2); - } - base.Dispose(disposing); - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Windows.cs deleted file mode 100644 index c470a5c83f..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/UnixPwmDriver.Windows.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Device.Pwm.Drivers -{ - public class UnixPwmDriver : PwmDriver - { - public UnixPwmDriver() => throw new PlatformNotSupportedException($"The {GetType().Name} class is not available on Windows."); - - protected internal override void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage) => throw new PlatformNotSupportedException(); - - protected internal override void CloseChannel(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - - protected internal override void OpenChannel(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - - protected internal override void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) => throw new PlatformNotSupportedException(); - - protected internal override void StopWriting(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Linux.cs deleted file mode 100644 index b8f8611aec..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Linux.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Device.Pwm.Drivers -{ - public class Windows10PwmDriver : PwmDriver - { - public Windows10PwmDriver() => throw new PlatformNotSupportedException($"The {GetType().Name} class is not available on Linux."); - - protected internal override void OpenChannel(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - - protected internal override void CloseChannel(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - - protected internal override void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage) => throw new PlatformNotSupportedException(); - - protected internal override void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) => throw new PlatformNotSupportedException(); - - protected internal override void StopWriting(int pwmChip, int pwmChannel) => throw new PlatformNotSupportedException(); - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Windows.cs deleted file mode 100644 index 800dd7b323..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriver.Windows.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Windows.Security.ExchangeActiveSyncProvisioning; - -namespace System.Device.Pwm.Drivers -{ - /// - /// A PWM driver for Windows 10 IoT. - /// - public class Windows10PwmDriver : PwmDriver - { - private readonly Dictionary _chipMap = new Dictionary(); - private readonly bool _useDefaultChip; - - public Windows10PwmDriver() - { - // On Hummingboard, fallback to check for generic PWM controller (friendly name is not currently used). - var deviceInfo = new EasClientDeviceInformation(); - if (deviceInfo.SystemProductName.IndexOf("Hummingboard", StringComparison.OrdinalIgnoreCase) >= 0) - { - _useDefaultChip = true; - } - } - - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void OpenChannel(int pwmChip, int pwmChannel) - { - if (!_chipMap.TryGetValue(pwmChip, out Windows10PwmDriverChip chip)) - { - chip = new Windows10PwmDriverChip(pwmChip, _useDefaultChip); - _chipMap[pwmChip] = chip; - } - - chip.OpenChannel(pwmChannel); - } - - /// - /// Closes an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void CloseChannel(int pwmChip, int pwmChannel) - { - // This assumes that PwmController has ensured that the chip is already open. - Windows10PwmDriverChip chip = _chipMap[pwmChip]; - - if (chip.CloseChannel(pwmChannel)) - { - _chipMap.Remove(pwmChannel); - } - } - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The duty cycle percentage to change. - protected internal override void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage) - { - LookupOpenChip(pwmChip).ChangeDutyCycle(pwmChannel, dutyCyclePercentage); - } - - /// - /// Starts writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The frequency in hertz. - /// - protected internal override void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) - { - LookupOpenChip(pwmChip).Start(pwmChannel, frequencyInHertz, dutyCyclePercentage); - } - - /// - /// Stops writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal override void StopWriting(int pwmChip, int pwmChannel) - { - LookupOpenChip(pwmChip).Stop(pwmChannel); - } - - protected override void Dispose(bool disposing) - { - foreach (Windows10PwmDriverChip chip in _chipMap.Values) - { - chip.Dispose(); - } - _chipMap.Clear(); - - base.Dispose(disposing); - } - - private Windows10PwmDriverChip LookupOpenChip(int pwmChip) - { - // This assumes that PwmController has ensured that the chip is already open. - return _chipMap[pwmChip]; - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChannel.Windows.cs deleted file mode 100644 index f2ac9bb2ae..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChannel.Windows.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using WinPwm = Windows.Devices.Pwm; - -namespace System.Device.Pwm.Drivers -{ - internal class Windows10PwmDriverChannel : IDisposable - { - private WinPwm.PwmPin _winPin; - - public Windows10PwmDriverChannel(WinPwm.PwmController winController, int channelIndex) - { - _winPin = winController.OpenPin(channelIndex); - if (_winPin == null) - { - throw new ArgumentOutOfRangeException($"The PWM chip is unable to open a channel at index {channelIndex}.", nameof(channelIndex)); - } - } - - public void ChangeDutyCycle(double dutyCyclePercentage) - { - _winPin?.SetActiveDutyCyclePercentage(dutyCyclePercentage / 100.0); - } - - public void Start(double dutyCyclePercentage) - { - ChangeDutyCycle(dutyCyclePercentage); - _winPin?.Start(); - // This extra call is required to generate PWM output - remove when the underlying issue is fixed. See issue #109 - ChangeDutyCycle(dutyCyclePercentage); - } - - public void Stop() - { - _winPin?.Stop(); - } - - public void Dispose() - { - Stop(); - _winPin?.Dispose(); - _winPin = null; - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChip.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChip.Windows.cs deleted file mode 100644 index 94801277c6..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/Drivers/Windows10PwmDriverChip.Windows.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using Windows.Devices.Enumeration; -using WinPwm = Windows.Devices.Pwm; - -namespace System.Device.Pwm.Drivers -{ - public class Windows10PwmDriverChip : IDisposable - { - private WinPwm.PwmController _winController; - private readonly Dictionary _channelMap = new Dictionary(); - - /// - /// Initializes a new instance of the class. - /// - /// The PWM chip. - /// Use the default chip. - public Windows10PwmDriverChip(int pwmChip, bool useDefaultChip) - { - // Open the Windows PWM controller for the specified PWM chip. - string controllerFriendlyName = $"PWM{pwmChip}"; - string deviceSelector = useDefaultChip ? WinPwm.PwmController.GetDeviceSelector() : WinPwm.PwmController.GetDeviceSelector(controllerFriendlyName); - - DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); - if (deviceInformationCollection.Count == 0) - { - throw new ArgumentException($"No PWM device exists for PWM chip at index {pwmChip}.", $"{nameof(pwmChip)}"); - } - - string deviceId = deviceInformationCollection[0].Id; - _winController = WinPwm.PwmController.FromIdAsync(deviceId).WaitForCompletion(); - } - - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM channel. - public void OpenChannel(int pwmChannel) - { - if (!_channelMap.TryGetValue(pwmChannel, out _)) - { - Windows10PwmDriverChannel channel = new Windows10PwmDriverChannel(_winController, pwmChannel); - _channelMap.Add(pwmChannel, channel); - } - } - - /// - /// Closes an open channel. - /// - /// The PWM channel. - /// if the chip has no more open channels open upon exiting; otherwise. - public bool CloseChannel(int pwmChannel) - { - // This assumes that PwmController has ensured that the channel is already open. - Windows10PwmDriverChannel channel = _channelMap[pwmChannel]; - - channel.Dispose(); - _channelMap.Remove(pwmChannel); - - return _channelMap.Count == 0; - } - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM channel. - /// The duty cycle percentage to change. - public void ChangeDutyCycle(int pwmChannel, double dutyCyclePercentage) - { - // This assumes that PwmController has ensured that the channel is already open. - Windows10PwmDriverChannel channel = _channelMap[pwmChannel]; - - channel.ChangeDutyCycle(dutyCyclePercentage); - } - - /// - /// Starts writing to an open channel. - /// - /// The PWM channel. - /// - /// - public void Start(int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) - { - // This assumes that PwmController has ensured that the channel is already open. - Windows10PwmDriverChannel channel = _channelMap[pwmChannel]; - - _winController.SetDesiredFrequency(frequencyInHertz); - channel.Start(dutyCyclePercentage); - } - - /// - /// Stops writing to an open channel. - /// - /// The PWM channel. - public void Stop(int pwmChannel) - { - // This assumes that PwmController has ensured that the channel is already open. - Windows10PwmDriverChannel channel = _channelMap[pwmChannel]; - channel.Stop(); - } - - public void Dispose() - { - _winController = null; - - foreach (Windows10PwmDriverChannel channel in _channelMap.Values) - { - channel.Dispose(); - } - _channelMap.Clear(); - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/IPwmController.cs b/src/System.Device.Gpio/System/Device/Pwm/IPwmController.cs deleted file mode 100644 index 14d27c3f78..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/IPwmController.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Device.Pwm -{ - public interface IPwmController : IDisposable - { - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM chip. - /// The PWM channel. - void OpenChannel(int pwmChip, int pwmChannel); - - /// - /// Closes an open channel. - /// - /// The PWM chip. - /// The PWM channel. - void CloseChannel(int pwmChip, int pwmChannel); - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The duty cycle percentage to change. - void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage); - - /// - /// Starts writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The frequency in hertz to write. - /// The duty cycle percentage to write. - void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage); - - /// - /// Stops writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - void StopWriting(int pwmChip, int pwmChannel); - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs new file mode 100644 index 0000000000..c1e0ef51f8 --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Device.Pwm +{ + public partial class PwmChannel + { + /// + /// Creates a new instance of the running on Unix. + /// + /// The PWM chip number. + /// The PWM channel number. + /// The frequency in hertz. + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// A PWM channel running on Unix. + public static PwmChannel Create( + int chip, + int channel, + int frequency, + double dutyCyclePercentage = 0.5) => + new Channels.UnixPwmChannel( + chip, + channel, + frequency, + dutyCyclePercentage); + } +} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs new file mode 100644 index 0000000000..fc4f452709 --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Device.Pwm +{ + public partial class PwmChannel + { + /// + /// Creates a new instance of the running on Windows 10 IoT. + /// + /// The PWM chip number. + /// The PWM channel number. + /// The frequency in hertz. + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// A PWM channel running on Windows 10 IoT. + public static PwmChannel Create( + int chip, + int channel, + int frequency, + double dutyCyclePercentage = 0.5) => + new Channels.Windows10PwmChannel( + chip, + channel, + frequency, + dutyCyclePercentage); + } +} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs new file mode 100644 index 0000000000..95ea210b8a --- /dev/null +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Device.Pwm +{ + /// + /// Represents a single PWM channel. + /// + public abstract partial class PwmChannel : IDisposable + { + /// + /// The frequency in hertz. + /// + public abstract int Frequency { get; } + + /// + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// + public abstract double DutyCyclePercentage { get; set; } + + /// + /// Starts the PWM channel. + /// + public abstract void Start(); + + /// + /// Stops the PWM channel. + /// + public abstract void Stop(); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Nothing to do in base class. + } + } +} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmController.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmController.Linux.cs deleted file mode 100644 index 85f508b9de..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmController.Linux.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Device.Pwm.Drivers; - -namespace System.Device.Pwm -{ - public sealed partial class PwmController : IDisposable - { - public PwmController() - : this(new UnixPwmDriver()) - { - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmController.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmController.Windows.cs deleted file mode 100644 index a0c97a9ad4..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmController.Windows.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Device.Pwm.Drivers; - -namespace System.Device.Pwm -{ - public sealed partial class PwmController : IDisposable - { - public PwmController() - : this(new Windows10PwmDriver()) - { - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmController.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmController.cs deleted file mode 100644 index b4cc592d8b..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmController.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; - -namespace System.Device.Pwm -{ - public sealed partial class PwmController : IPwmController - { - private readonly PwmDriver _driver; - /// - /// This collection will hold all of the channels that are currently opened by this controller. - /// - private readonly HashSet<(int, int)> _openChannels; - - /// - /// Initializes a new instance of the class that will use the specified driver. - /// - /// The driver that manages all of the channel operations for the controller. - public PwmController(PwmDriver driver) - { - _driver = driver; - _openChannels = new HashSet<(int, int)>(); - } - - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM chip. - /// The PWM channel. - public void OpenChannel(int pwmChip, int pwmChannel) - { - if (_openChannels.Contains((pwmChip, pwmChannel))) - { - throw new InvalidOperationException("The selected channel is already open."); - } - - _driver.OpenChannel(pwmChip, pwmChannel); - _openChannels.Add((pwmChip, pwmChannel)); - } - - /// - /// Closes an open channel. - /// - /// The PWM chip. - /// The PWM channel. - public void CloseChannel(int pwmChip, int pwmChannel) - { - if (!_openChannels.Contains((pwmChip, pwmChannel))) - { - throw new InvalidOperationException("Can not close a channel that is not open."); - } - - _driver.CloseChannel(pwmChip, pwmChannel); - _openChannels.Remove((pwmChip, pwmChannel)); - } - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The duty cycle percentage to change. - public void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage) - { - if (!_openChannels.Contains((pwmChip, pwmChannel))) - { - throw new InvalidOperationException("Can not change the duty cycle of a channel that is not open."); - } - if (dutyCyclePercentage < 0.0 || dutyCyclePercentage > 100.0) - { - throw new ArgumentException("Duty cycle must be a percentage in the range of 0-100.", nameof(dutyCyclePercentage)); - } - _driver.ChangeDutyCycle(pwmChip, pwmChannel, dutyCyclePercentage); - } - - /// - /// Starts writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The frequency in hertz to write. - /// The duty cycle percentage to write. - public void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage) - { - if (!_openChannels.Contains((pwmChip, pwmChannel))) - { - throw new InvalidOperationException("Can not start writing to a channel that is not open."); - } - if (dutyCyclePercentage < 0.0 || dutyCyclePercentage > 100.0) - { - throw new ArgumentException("Duty cycle must be a percentage in the range of 0-100.", nameof(dutyCyclePercentage)); - } - _driver.StartWriting(pwmChip, pwmChannel, frequencyInHertz, dutyCyclePercentage); - } - - /// - /// Stops writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - public void StopWriting(int pwmChip, int pwmChannel) - { - if (!_openChannels.Contains((pwmChip, pwmChannel))) - { - throw new InvalidOperationException("Can not stop writing to a channel that is not open."); - } - _driver.StopWriting(pwmChip, pwmChannel); - } - - public void Dispose() - { - Dispose(true); - } - - private void Dispose(bool disposing) - { - foreach ((int, int) channel in _openChannels) - { - _driver.CloseChannel(channel.Item1, channel.Item2); - } - _openChannels.Clear(); - _driver.Dispose(); - } - } -} diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmDriver.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmDriver.cs deleted file mode 100644 index 09be1b5897..0000000000 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmDriver.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace System.Device.Pwm -{ - public abstract class PwmDriver : IDisposable - { - /// - /// Opens a channel in order for it to be ready to use. - /// - /// The PWM chip. - /// The PWM channel. - protected internal abstract void OpenChannel(int pwmChip, int pwmChannel); - - /// - /// Closes an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal abstract void CloseChannel(int pwmChip, int pwmChannel); - - /// - /// Changes the duty cycle for an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The duty cycle percentage to change. - protected internal abstract void ChangeDutyCycle(int pwmChip, int pwmChannel, double dutyCyclePercentage); - - /// - /// Starts writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - /// The frequency in hertz to write. - /// The duty cycle percentage to write. - protected internal abstract void StartWriting(int pwmChip, int pwmChannel, double frequencyInHertz, double dutyCyclePercentage); - - /// - /// Stops writing to an open channel. - /// - /// The PWM chip. - /// The PWM channel. - protected internal abstract void StopWriting(int pwmChip, int pwmChannel); - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - // Nothing to do in the base class. - } - } -} diff --git a/tools/DevicesApiTester/Commands/Pwm/PwmCommand.cs b/tools/DevicesApiTester/Commands/Pwm/PwmCommand.cs index 553079d1e0..cd1a8864cb 100644 --- a/tools/DevicesApiTester/Commands/Pwm/PwmCommand.cs +++ b/tools/DevicesApiTester/Commands/Pwm/PwmCommand.cs @@ -10,28 +10,16 @@ namespace DeviceApiTester.Commands.Pwm { public abstract class PwmCommand : DebuggableCommand { - [Option('d', "driver", HelpText = "The PwmDriver to use: { Windows | Unix }", Required = false, Default = PwmDriverType.Windows)] - public PwmDriverType Driver { get; set; } + [Option("chip", HelpText = "The PWM chip number.", Required = false, Default = 0)] + public int Chip { get; set; } - [Option("chip", HelpText = "The PWM chip (controller) to use", Required = false, Default = 0)] - public int PwmChip { get; set; } + [Option("channel", HelpText = "The PWM channel number.", Required = false, Default = 0)] + public int Channel { get; set; } - [Option("channel", HelpText = "The PWM channel (pin) to use", Required = false, Default = 0)] - public int PwmChannel { get; set; } + [Option('f', "frequency", HelpText = "The frequency in hertz.", Required = false, Default = 400)] + public int Frequency { get; set; } - [Option('c', "dutycycle", HelpText = "The duty cycle for PWM output from 1.0-100.0", Required = false, Default = 50.0)] - public double DutyCycle { get; set; } - - [Option('f', "frequency", HelpText = "The frequency in hertz", Required = false, Default = 400.0)] - public double Frequency { get; set; } - - protected PwmController CreatePwmController() - { - PwmDriver pwmDriver = DriverFactory.CreateFromEnum(this.Driver); - - return pwmDriver != null - ? new PwmController(pwmDriver) - : new PwmController(); - } + [Option('d', "dutycycle", HelpText = "The duty cycle percentage for PWM output from 0.0 - 1.0.", Required = false, Default = 0.5)] + public double DutyCyclePercentage { get; set; } } -} \ No newline at end of file +} diff --git a/tools/DevicesApiTester/Commands/Pwm/PwmDriverType.cs b/tools/DevicesApiTester/Commands/Pwm/PwmDriverType.cs deleted file mode 100644 index da5dbcd550..0000000000 --- a/tools/DevicesApiTester/Commands/Pwm/PwmDriverType.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Device.Pwm.Drivers; -using DeviceApiTester.Infrastructure; - -namespace DeviceApiTester.Commands.Pwm -{ - public enum PwmDriverType - { - [ImplementationType(null)] - Default, - - [ImplementationType(typeof(Windows10PwmDriver))] - Windows, - - [ImplementationType(typeof(UnixPwmDriver))] - Unix, - } -} diff --git a/tools/DevicesApiTester/Commands/Pwm/PwmPinOutput.cs b/tools/DevicesApiTester/Commands/Pwm/PwmPinOutput.cs index 6a1acf3793..7ab5c9ed06 100644 --- a/tools/DevicesApiTester/Commands/Pwm/PwmPinOutput.cs +++ b/tools/DevicesApiTester/Commands/Pwm/PwmPinOutput.cs @@ -3,39 +3,32 @@ // See the LICENSE file in the project root for more information. using System; -using System.Device.Pwm; using System.Threading.Tasks; using CommandLine; using DeviceApiTester.Infrastructure; namespace DeviceApiTester.Commands.Pwm { - [Verb("pwm-pin-output", HelpText = "Starts PWM output on the given chip/channel for desired amount of seconds")] + [Verb("pwm-pin-output", HelpText = "Starts PWM output on the specified chip/channel for desired amount of seconds.")] public class PwmPinOutput : PwmCommand, ICommandVerbAsync { /// Executes the command asynchronously. /// The command's exit code. - /// - /// NOTE: This test app uses the base class's method to create a device.
- /// Real-world usage would simply create an instance of : - /// using (var pwm = new PwmController()) - ///
public async Task ExecuteAsync() { - using (var pwm = CreatePwmController()) + using (var pwmChannel = System.Device.Pwm.PwmChannel.Create(Chip, Channel, Frequency, DutyCyclePercentage)) { - Console.WriteLine($"Enabling PWM output with chip {PwmChip} / channel {PwmChannel}, {DutyCycle}% duty cycle @ {Frequency}hz for {Seconds} seconds "); + Console.WriteLine($"Chip={Chip}, Channel={Channel}, Frequency={Frequency}Hz, DC={DutyCyclePercentage}, Duration={Seconds}s"); - pwm.OpenChannel(PwmChip, PwmChannel); - pwm.StartWriting(PwmChip, PwmChannel, Frequency, DutyCycle); + pwmChannel.Start(); await Task.Delay(TimeSpan.FromSeconds(Seconds)); - pwm.StopWriting(PwmChip, PwmChannel); + pwmChannel.Stop(); } return 0; } - [Option('s', "seconds", HelpText = "The number of seconds to output the PWM signal", Required = false, Default = 3)] + [Option('s', "seconds", HelpText = "The number of seconds to output the PWM signal.", Required = false, Default = 3)] public int Seconds { get; set; } } -} \ No newline at end of file +} From 0e60776277eeafa79b299cb77801600a8a901da7 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Fri, 12 Jul 2019 16:14:47 -0700 Subject: [PATCH 2/7] Porting SoftwarePwm on top of PwmChannel so that bindings that use Pwm and SoftwarePwm can have common interface --- .../Pwm/Channels/UnixPwmChannel.Linux.cs | 14 +- .../Channels/Windows10PwmChannel.Windows.cs | 4 +- .../System/Device/Pwm/PwmChannel.Linux.cs | 2 +- .../System/Device/Pwm/PwmChannel.Windows.cs | 2 +- .../System/Device/Pwm/PwmChannel.cs | 2 +- src/devices/Buzzer/Buzzer.cs | 61 ++++--- src/devices/Buzzer/Buzzer.csproj | 6 +- src/devices/DCMotor/DCMotor.csproj | 6 +- src/devices/DCMotor/samples/samples.csproj | 2 +- src/devices/Servo/Servo.csproj | 6 +- src/devices/Servo/ServoMotor.cs | 57 ++++--- src/devices/Servo/samples/Servo.sample.cs | 4 +- src/devices/Servo/samples/Servo.sample.csproj | 6 +- .../{SoftPwm.csproj => SoftwarePwm.csproj} | 8 +- .../{SoftPwm.cs => SoftwarePwmChannel.cs} | 154 +++++++++--------- src/devices/SoftPwm/samples/SoftPwm.sample.cs | 14 +- .../SoftPwm/samples/SoftPwm.sample.csproj | 8 +- 17 files changed, 186 insertions(+), 170 deletions(-) rename src/devices/SoftPwm/{SoftPwm.csproj => SoftwarePwm.csproj} (53%) rename src/devices/SoftPwm/{SoftPwm.cs => SoftwarePwmChannel.cs} (64%) diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs index 7eeb8e2930..df200a4030 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs @@ -29,7 +29,7 @@ internal class UnixPwmChannel : PwmChannel public UnixPwmChannel( int chip, int channel, - int frequency, + int frequency = 400, double dutyCyclePercentage = 0.5) { _chipPath = $"/sys/class/pwm/pwmchip{_chip}"; @@ -47,7 +47,17 @@ public UnixPwmChannel( /// /// The frequency in hertz. /// - public override int Frequency =>_frequency; + public override int Frequency + { + get + { + return _frequency; + } + set + { + SetFrequency(value); + } + } /// /// The duty cycle percentage represented as a value between 0.0 and 1.0. diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs index caf146810d..7b6eaa4a87 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs @@ -22,13 +22,13 @@ internal class Windows10PwmChannel : PwmChannel public Windows10PwmChannel( int chip, int channel, - int frequency, + int frequency = 400, double dutyCyclePercentage = 0.5) { // TODO: This is just a placeholder to complete later. } - public override int Frequency => throw new NotImplementedException(); + public override int Frequency { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public override double DutyCyclePercentage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs index c1e0ef51f8..6a83f46ac8 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Linux.cs @@ -17,7 +17,7 @@ public partial class PwmChannel public static PwmChannel Create( int chip, int channel, - int frequency, + int frequency = 400, double dutyCyclePercentage = 0.5) => new Channels.UnixPwmChannel( chip, diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs index fc4f452709..6a709f58de 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.Windows.cs @@ -17,7 +17,7 @@ public partial class PwmChannel public static PwmChannel Create( int chip, int channel, - int frequency, + int frequency = 400, double dutyCyclePercentage = 0.5) => new Channels.Windows10PwmChannel( chip, diff --git a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs index 95ea210b8a..0c73861c6a 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/PwmChannel.cs @@ -12,7 +12,7 @@ public abstract partial class PwmChannel : IDisposable /// /// The frequency in hertz. /// - public abstract int Frequency { get; } + public abstract int Frequency { get; set; } /// /// The duty cycle percentage represented as a value between 0.0 and 1.0. diff --git a/src/devices/Buzzer/Buzzer.cs b/src/devices/Buzzer/Buzzer.cs index 3156da25c7..1cc3c34018 100644 --- a/src/devices/Buzzer/Buzzer.cs +++ b/src/devices/Buzzer/Buzzer.cs @@ -5,7 +5,6 @@ using System; using System.Device.Pwm; using System.Device.Pwm.Drivers; -using System.IO; using System.Threading; namespace Iot.Device.Buzzer @@ -15,53 +14,48 @@ namespace Iot.Device.Buzzer /// public class Buzzer : IDisposable { - private readonly int _buzzerPin; - private readonly int _pwmChannel; - private readonly PwmController _pwmController; + private readonly PwmChannel _pwmChannel; + + public Buzzer(int pinNumber) + : this(CreatePwmChannel(pinNumber, -1, -1, false)) { } /// /// Create Buzzer class instance with output on specified pin with specified channel. /// - /// The GPIO pin number in case of a software PWM. The chip in case of a hardware PWM. - /// The channel to use in case of a hardware PWM. - public Buzzer(int pinNumber, int pwmChannel) + /// The GPIO pin number in case of a software PWM. The chip in case of a hardware PWM. + /// The channel to use in case of a hardware PWM. + public Buzzer(int chip, int channel) + : this(CreatePwmChannel(-1, chip, channel, true)) { } + + /// + /// Create Buzzer class instance with output on specified pin with specified channel using passed PWM controller. + /// + /// The PWM controller to use during work. + public Buzzer(PwmChannel pwmChannel) { - _buzzerPin = pinNumber; _pwmChannel = pwmChannel; + } - try + private static PwmChannel CreatePwmChannel(int pinNumber, int chip, int channel, bool useHardwarePwm) + { + if (useHardwarePwm) { - _pwmController = new PwmController(); - _pwmController.OpenChannel(_buzzerPin, _pwmChannel); + return PwmChannel.Create(chip, channel); } - catch (Exception ex) when (ex is ArgumentException || ex is IOException) + else { - // If hardware PWM is unable to initialize we will use software PWM. - _pwmController = new PwmController(new SoftPwm(true)); - _pwmController.OpenChannel(_buzzerPin, _pwmChannel); + return new SoftwarePwmChannel(pinNumber); } } - /// - /// Create Buzzer class instance with output on specified pin with specified channel using passed PWM controller. - /// - /// The GPIO pin number in case of a software PWM. The chip in case of a hardware PWM. - /// The channel to use in case of a hardware PWM. - /// The PWM controller to use during work. - public Buzzer(int pinNumber, int pwmChannel, PwmController pwmController) - { - _buzzerPin = pinNumber; - _pwmChannel = pwmChannel; - _pwmController = pwmController; - } - /// /// Set new or overwrite previously set frequency and start playing the sound. /// /// Tone frequency in Hertz. - public void SetFrequency(double frequency) + public void StartPlaying(double frequency) { - _pwmController.StartWriting(_buzzerPin, _pwmChannel, frequency, 0.5); + _pwmChannel.Frequency = (int)frequency; + _pwmChannel.Start(); } /// @@ -69,7 +63,7 @@ public void SetFrequency(double frequency) /// public void StopPlaying() { - _pwmController.StopWriting(_buzzerPin, _pwmChannel); + _pwmChannel.Stop(); } /// @@ -79,7 +73,7 @@ public void StopPlaying() /// Playing duration in millisecons. public void PlayTone(double frequency, int duraton) { - SetFrequency(frequency); + StartPlaying(frequency); Thread.Sleep(duraton); StopPlaying(); } @@ -89,7 +83,8 @@ public void PlayTone(double frequency, int duraton) /// public void Dispose() { - _pwmController.Dispose(); + _pwmChannel?.Dispose(); + _pwmChannel = null; } } } diff --git a/src/devices/Buzzer/Buzzer.csproj b/src/devices/Buzzer/Buzzer.csproj index 4a8462152e..2e9dd8de32 100644 --- a/src/devices/Buzzer/Buzzer.csproj +++ b/src/devices/Buzzer/Buzzer.csproj @@ -9,11 +9,13 @@ - + + $(AdditionalProperties);RuntimeIdentifier=linux + - + diff --git a/src/devices/DCMotor/DCMotor.csproj b/src/devices/DCMotor/DCMotor.csproj index 7c2572ccbc..d8112ae443 100644 --- a/src/devices/DCMotor/DCMotor.csproj +++ b/src/devices/DCMotor/DCMotor.csproj @@ -6,8 +6,10 @@ - - + + $(AdditionalProperties);RuntimeIdentifier=linux + + diff --git a/src/devices/DCMotor/samples/samples.csproj b/src/devices/DCMotor/samples/samples.csproj index fe370755cd..90700fa92a 100644 --- a/src/devices/DCMotor/samples/samples.csproj +++ b/src/devices/DCMotor/samples/samples.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/devices/Servo/Servo.csproj b/src/devices/Servo/Servo.csproj index 47d58e7390..8276397c1e 100644 --- a/src/devices/Servo/Servo.csproj +++ b/src/devices/Servo/Servo.csproj @@ -12,11 +12,13 @@ - + - + + $(AdditionalProperties);RuntimeIdentifier=linux + diff --git a/src/devices/Servo/ServoMotor.cs b/src/devices/Servo/ServoMotor.cs index b6056fd87b..4f1a529ef9 100644 --- a/src/devices/Servo/ServoMotor.cs +++ b/src/devices/Servo/ServoMotor.cs @@ -18,8 +18,6 @@ namespace Iot.Device.Servo { public class ServoMotor : IDisposable { - private int _servoPin; - private int _pwmChannel; private double _pulseFrequency = 20.0; private double _angle; double _currentPulseWidth = 0; @@ -27,7 +25,7 @@ public class ServoMotor : IDisposable /// /// Are we running on hardware or software PWM? True for hardware, false for sofware /// - public bool IsRunningHardwarePwm { get; set; } + public bool IsRunningHardwarePwm { get; private set; } /// /// Servo motor definition. @@ -37,7 +35,7 @@ public class ServoMotor : IDisposable /// The duration per angle's degree. /// private double _rangePerDegree; - private PwmController _pwmController; + private PwmChannel _pwmChannel; /// /// Initialize a ServoMotor class @@ -46,32 +44,41 @@ public class ServoMotor : IDisposable /// The channel to use in case of a hardware PWM. /// The definition of a ServoMotor /// Use -1 for pwmChannel to force using software PWM - public ServoMotor(int pinNumber, int pwmChannel, ServoMotorDefinition definition) + public ServoMotor(PwmChannel pwmChannel, ServoMotorDefinition definition) { this._definition = definition; _pulseFrequency = definition.PeriodMicroseconds / 1000.0; UpdateRange(); - - _servoPin = pinNumber; this._pwmChannel = pwmChannel; - // try hardware PWM first - try - { - _pwmController = new PwmController(); - _pwmController.OpenChannel(pinNumber, pwmChannel); - IsRunningHardwarePwm = true; + _pwmChannel.DutyCyclePercentage = (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100; + _pwmChannel.Start(); + + } + + public ServoMotor(int pinNumber, ServoMotorDefinition definition) + : this(CreatePwmChannel(pinNumber, 0, 0, definition, false), definition) + { + IsRunningHardwarePwm = false; + } + + public ServoMotor(int chip, int channel, ServoMotorDefinition definition) + : this(CreatePwmChannel(-1, chip, channel, definition, true), definition) + { + IsRunningHardwarePwm = true; + } + + private static PwmChannel CreatePwmChannel(int pinNumber, int chip, int channel, ServoMotorDefinition definition, bool useHardwarePwm) + { + if (useHardwarePwm) + { + return PwmChannel.Create(chip, channel, (int)(1000000 / definition.PeriodMicroseconds)); } - catch (Exception ex) when (ex is ArgumentException || ex is IOException) + else { - _pwmController = new PwmController(new SoftPwm(true)); - _pwmController.OpenChannel(pinNumber, pwmChannel); - IsRunningHardwarePwm = false; + return new SoftwarePwmChannel(pinNumber, (int)(1000000 / definition.PeriodMicroseconds)); } - - _pwmController.StartWriting(_servoPin, pwmChannel, 1000 / _pulseFrequency, (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100); - } /// @@ -81,7 +88,7 @@ public ServoMotor(int pinNumber, int pwmChannel, ServoMotorDefinition definition public void SetPulse(uint durationMicroSec) { _currentPulseWidth = durationMicroSec / 1000.0; - _pwmController.ChangeDutyCycle(_servoPin, _pwmChannel, (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100); + _pwmChannel.DutyCyclePercentage = (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100; } /// @@ -95,7 +102,7 @@ public void SetPulse(uint periodMicroSec, uint durationMicroSec) _definition.PeriodMicroseconds = periodMicroSec; _pulseFrequency = periodMicroSec / 1000.0; _currentPulseWidth = durationMicroSec / 1000.0; - _pwmController.ChangeDutyCycle(_servoPin, _pwmChannel, (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100); + _pwmChannel.DutyCyclePercentage = (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100; } /// @@ -135,7 +142,7 @@ private void Rotate() { double duration = (_definition.MinimumDurationMicroseconds + _rangePerDegree * Angle) / 1000.0; _currentPulseWidth = duration; - _pwmController.ChangeDutyCycle(_servoPin, _pwmChannel, (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100); + _pwmChannel.DutyCyclePercentage = (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100; } /// @@ -149,8 +156,8 @@ private void UpdateRange() public void Dispose() { - _pwmController.StopWriting(_servoPin, _pwmChannel); - _pwmController.CloseChannel(_servoPin, _pwmChannel); + _pwmChannel?.Dispose(); + _pwmChannel = null; } /// diff --git a/src/devices/Servo/samples/Servo.sample.cs b/src/devices/Servo/samples/Servo.sample.cs index eacb5abdcc..5f760380cc 100644 --- a/src/devices/Servo/samples/Servo.sample.cs +++ b/src/devices/Servo/samples/Servo.sample.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using Iot.Device.Servo; -using System.Diagnostics; -using System.Device.Gpio; using System.Threading; +using Iot.Device.Servo; class Program { diff --git a/src/devices/Servo/samples/Servo.sample.csproj b/src/devices/Servo/samples/Servo.sample.csproj index 97bbf2c77b..da95034522 100644 --- a/src/devices/Servo/samples/Servo.sample.csproj +++ b/src/devices/Servo/samples/Servo.sample.csproj @@ -1,4 +1,4 @@ - + Exe @@ -7,10 +7,12 @@ + + $(AdditionalProperties);RuntimeIdentifier=linux + - diff --git a/src/devices/SoftPwm/SoftPwm.csproj b/src/devices/SoftPwm/SoftwarePwm.csproj similarity index 53% rename from src/devices/SoftPwm/SoftPwm.csproj rename to src/devices/SoftPwm/SoftwarePwm.csproj index dcd775dc5e..9c878512f8 100644 --- a/src/devices/SoftPwm/SoftPwm.csproj +++ b/src/devices/SoftPwm/SoftwarePwm.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 @@ -7,11 +7,13 @@ - + - + + RuntimeIdentifier=linux + diff --git a/src/devices/SoftPwm/SoftPwm.cs b/src/devices/SoftPwm/SoftwarePwmChannel.cs similarity index 64% rename from src/devices/SoftPwm/SoftPwm.cs rename to src/devices/SoftPwm/SoftwarePwmChannel.cs index d8277fe5dd..e77f007196 100644 --- a/src/devices/SoftPwm/SoftPwm.cs +++ b/src/devices/SoftPwm/SoftwarePwmChannel.cs @@ -2,25 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Device.Pwm; using System.Device.Gpio; +using System.Diagnostics; using System.Threading; +using System.Threading.Tasks; namespace System.Device.Pwm.Drivers { - public class SoftPwm : PwmDriver + public class SoftwarePwmChannel : PwmChannel { // use to determine the freqncy of the PWM // PulseFrequency = total frenquency // curent pulse width = when the signal is hi private double _currentPulseWidth; private double _pulseFrequency; + private int _frequency; // Use to determine the length of the pulse // 100 % = full output. 0%= nothing as output private double _percentage; @@ -28,20 +24,71 @@ public class SoftPwm : PwmDriver private bool _precisionPWM = false; private bool _isRunning; - private bool _istopped = true; + private bool _isStopped = true; private int _servoPin = -1; private Stopwatch _stopwatch = Stopwatch.StartNew(); private Thread _runningThread; private GpioController _controller; - private bool runThread = true; + private bool _runThread = true; + + public override int Frequency + { + get => _frequency; + set + { + _frequency = value; + _pulseFrequency = (_frequency > 0) ? 1 / _frequency * 1000.0 : 0.0; + UpdateRange(); + } + } + + public override double DutyCyclePercentage + { + get => _percentage; + set + { + _percentage = value; + UpdateRange(); + } + } + + public SoftwarePwmChannel(int pinNumber, int frequency = 400, double dutyCyclePercentage = 0.5) + { + _controller = new GpioController(); + if (_controller == null) + { + Debug.WriteLine("GPIO does not exist on the current system."); + return; + } + _servoPin = pinNumber; + _controller.OpenPin(_servoPin, PinMode.Output); + _isRunning = false; + _runningThread = new Thread(RunSoftPWM); + _runningThread.Start(); + + _frequency = frequency; + _pulseFrequency = (frequency > 0) ? 1.0 / frequency * 1000.0 : 0.0; + + DutyCyclePercentage = dutyCyclePercentage; + } + + public SoftwarePwmChannel(int pinNumber, int frequency, double dutyCyclePercentage, bool preceisionTimer) : this(pinNumber, frequency, dutyCyclePercentage) + { + _precisionPWM = preceisionTimer; + } + + private void UpdateRange() + { + _currentPulseWidth = _percentage * _pulseFrequency / 100; + } private void RunSoftPWM() { if (_precisionPWM) Thread.CurrentThread.Priority = ThreadPriority.Highest; - while (runThread) + while (_runThread) { // Write the pin high for the appropriate length of time if (_isRunning) @@ -49,6 +96,7 @@ private void RunSoftPWM() if (_currentPulseWidth != 0) { _controller.Write(_servoPin, PinValue.High); + _isStopped = false; } // Use the wait helper method to wait for the length of the pulse if (_precisionPWM) @@ -64,18 +112,21 @@ private void RunSoftPWM() } else { - if (!_istopped) + if (!_isStopped) { _controller.Write(_servoPin, PinValue.Low); - _istopped = true; + _isStopped = true; } } } } - // A synchronous wait is used to avoid yielding the thread - // This method calculates the number of CPU ticks will elapse in the specified time and spins - // in a loop until that threshold is hit. This allows for very precise timing. + /// + /// A synchronous wait is used to avoid yielding the thread + /// This method calculates the number of CPU ticks will elapse in the specified time and spins + /// in a loop until that threshold is hit. This allows for very precise timing. + /// + /// The milliseconds to wait for private void Wait(double milliseconds) { long initialTick = _stopwatch.ElapsedTicks; @@ -88,76 +139,25 @@ private void Wait(double milliseconds) } } - - public SoftPwm() + public override void Start() { - _controller = new GpioController(); - if (_controller == null) - { - Debug.WriteLine("GPIO does not exist on the current system."); - return; - } - } - - public SoftPwm(bool preceisionTimer) : this() - { - _precisionPWM = preceisionTimer; - } - - private void UpdateRange() - { - _currentPulseWidth = _percentage * _pulseFrequency / 100; - } - - private void ValidatePWMChannel(int pinNumber) - { - if (_servoPin != pinNumber) - { - throw new ArgumentException($"Soft PWM on pin {pinNumber} not initialized"); - } - } - - protected override void OpenChannel(int pinNumber, int pwmChannel) - { - _servoPin = pinNumber; - _controller.OpenPin(_servoPin); - _controller.SetPinMode(_servoPin, PinMode.Output); - _runningThread = new Thread(RunSoftPWM); - _runningThread.Start(); - + _isRunning = true; } - protected override void CloseChannel(int pinNumber, int pwmChannel) + public override void Stop() { - ValidatePWMChannel(pinNumber); - _controller.ClosePin(_servoPin); - _servoPin = -1; _isRunning = false; } - protected override void ChangeDutyCycle(int pinNumber, int pwmChannel, double dutyCycleInPercentage) - { - ValidatePWMChannel(pinNumber); - _percentage = dutyCycleInPercentage; - UpdateRange(); - } - - protected override void StartWriting(int pinNumber, int pwmChannel, double frequencyInHertz, double dutyCycleInPercentage) - { - ValidatePWMChannel(pinNumber); - if (frequencyInHertz > 0) - _pulseFrequency = 1 / frequencyInHertz * 1000.0; - else - _pulseFrequency = 0.0; - _percentage = dutyCycleInPercentage; - UpdateRange(); - _isRunning = true; - } - - protected override void StopWriting(int pinNumber, int pwmChannel) + protected override void Dispose(bool disposing) { - ValidatePWMChannel(pinNumber); _isRunning = false; + _runThread = false; + while (_runningThread.ThreadState == Threading.ThreadState.Running) + Thread.Sleep(100); + _controller?.Dispose(); + _controller = null; + base.Dispose(disposing); } } } diff --git a/src/devices/SoftPwm/samples/SoftPwm.sample.cs b/src/devices/SoftPwm/samples/SoftPwm.sample.cs index 03c589eed7..5b4b41a46d 100644 --- a/src/devices/SoftPwm/samples/SoftPwm.sample.cs +++ b/src/devices/SoftPwm/samples/SoftPwm.sample.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Device.Pwm; using System.Device.Pwm.Drivers; -using System.Diagnostics; -using System.Device.Gpio; using System.Threading; class Program @@ -16,16 +13,13 @@ static void Main(string[] args) { Console.WriteLine("Hello PWM!"); - var PwmController = new PwmController(new SoftPwm()); - PwmController.OpenChannel(17, 0); - PwmController.StartWriting(17, 0, 200, 0); - - while (true) + using (var pwmChannel = new SoftwarePwmChannel(17, 200, 0)) { + pwmChannel.Start(); for (int i = 0; i < 100; i++) { - PwmController.ChangeDutyCycle(17, 0, i); - Thread.Sleep(100); + pwmChannel.DutyCyclePercentage = i; + Thread.Sleep(500); } } diff --git a/src/devices/SoftPwm/samples/SoftPwm.sample.csproj b/src/devices/SoftPwm/samples/SoftPwm.sample.csproj index e0a200b5cc..65446a8e15 100644 --- a/src/devices/SoftPwm/samples/SoftPwm.sample.csproj +++ b/src/devices/SoftPwm/samples/SoftPwm.sample.csproj @@ -1,4 +1,4 @@ - + Exe @@ -6,11 +6,13 @@ - + + + $(AdditionalProperties);RuntimeIdentifier=linux + - From 233084c493a156d626dcd8c965e082922b32cc27 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 16 Jul 2019 14:02:13 -0700 Subject: [PATCH 3/7] Fix DCMotor, tiny fixes in Buzzer and SoftPwm --- src/devices/Buzzer/Buzzer.cs | 18 +----- src/devices/DCMotor/DCMotor.cs | 14 ++--- src/devices/DCMotor/DCMotor2PinNoEnable.cs | 31 ++-------- src/devices/DCMotor/DCMotor3Pin.cs | 35 +++++------ src/devices/DCMotor/DCMotorSettings.cs | 19 +----- .../{samples.csproj => DCMotor.sample.csproj} | 6 +- src/devices/DCMotor/samples/Program.cs | 61 +++++++++++++------ src/devices/DCMotor/samples/README.md | 60 ++++++++++++------ src/devices/SoftPwm/SoftwarePwmChannel.cs | 29 ++++++--- src/devices/SoftPwm/samples/README.md | 45 +++++--------- src/devices/SoftPwm/samples/SoftPwm.sample.cs | 5 +- 11 files changed, 152 insertions(+), 171 deletions(-) rename src/devices/DCMotor/samples/{samples.csproj => DCMotor.sample.csproj} (53%) diff --git a/src/devices/Buzzer/Buzzer.cs b/src/devices/Buzzer/Buzzer.cs index 1cc3c34018..65c0f63f52 100644 --- a/src/devices/Buzzer/Buzzer.cs +++ b/src/devices/Buzzer/Buzzer.cs @@ -14,10 +14,10 @@ namespace Iot.Device.Buzzer /// public class Buzzer : IDisposable { - private readonly PwmChannel _pwmChannel; + private PwmChannel _pwmChannel; public Buzzer(int pinNumber) - : this(CreatePwmChannel(pinNumber, -1, -1, false)) { } + : this(new SoftwarePwmChannel(pinNumber)) { } /// /// Create Buzzer class instance with output on specified pin with specified channel. @@ -25,7 +25,7 @@ public Buzzer(int pinNumber) /// The GPIO pin number in case of a software PWM. The chip in case of a hardware PWM. /// The channel to use in case of a hardware PWM. public Buzzer(int chip, int channel) - : this(CreatePwmChannel(-1, chip, channel, true)) { } + : this(PwmChannel.Create(chip, channel)) { } /// /// Create Buzzer class instance with output on specified pin with specified channel using passed PWM controller. @@ -36,18 +36,6 @@ public Buzzer(PwmChannel pwmChannel) _pwmChannel = pwmChannel; } - private static PwmChannel CreatePwmChannel(int pinNumber, int chip, int channel, bool useHardwarePwm) - { - if (useHardwarePwm) - { - return PwmChannel.Create(chip, channel); - } - else - { - return new SoftwarePwmChannel(pinNumber); - } - } - /// /// Set new or overwrite previously set frequency and start playing the sound. /// diff --git a/src/devices/DCMotor/DCMotor.cs b/src/devices/DCMotor/DCMotor.cs index 8e940f881e..f29d48a56c 100644 --- a/src/devices/DCMotor/DCMotor.cs +++ b/src/devices/DCMotor/DCMotor.cs @@ -48,19 +48,16 @@ public static DCMotor Create(DCMotorSettings settings) { if (settings.UseEnableAsPwm) { - if (settings.Pin0.HasValue && settings.Pin1.HasValue && settings.PwmController != null) + if (settings.Pin0.HasValue && settings.Pin1.HasValue && settings.PwmChannel != null) { return new DCMotor3Pin( - settings.PwmController, - settings.PwmFrequency, - settings.PwmChip, settings.PwmChannel, settings.Pin0.Value, settings.Pin1.Value, settings.Controller); } - throw new ArgumentException("When enable pin is used for PWM all pins and PwmController must be set."); + throw new ArgumentException("When enable pin is used for PWM all pins and PwmChannel must be set."); } else { @@ -73,7 +70,7 @@ public static DCMotor Create(DCMotorSettings settings) pin1 = null; } - if (settings.PwmController != null) + if (settings.PwmChannel != null) { if (pin0.HasValue == pin1.HasValue) { @@ -81,9 +78,6 @@ public static DCMotor Create(DCMotorSettings settings) } return new DCMotor2PinNoEnable( - settings.PwmController, - settings.PwmFrequency, - settings.PwmChip, settings.PwmChannel, pin0, settings.Controller); @@ -96,7 +90,7 @@ public static DCMotor Create(DCMotorSettings settings) throw new ArgumentException("Pin0 or Pin1 must be set when PWM is not specified."); } - return new DCMotor2PinNoEnable(settings.PwmFrequency, pin0.Value, pin1, settings.Controller); + return new DCMotor2PinNoEnable(new SoftwarePwmChannel(pin0.Value, 50, 0.0, controller: settings.Controller), pin1, settings.Controller); } } } diff --git a/src/devices/DCMotor/DCMotor2PinNoEnable.cs b/src/devices/DCMotor/DCMotor2PinNoEnable.cs index f3df4a9dd2..9cfcd061f3 100644 --- a/src/devices/DCMotor/DCMotor2PinNoEnable.cs +++ b/src/devices/DCMotor/DCMotor2PinNoEnable.cs @@ -11,30 +11,22 @@ namespace Iot.Device.DCMotor { internal class DCMotor2PinNoEnable : DCMotor { - private PwmController _pwm; - private int _chip; - private int _channel; + private PwmChannel _pwm; private int? _pin1; private double _speed; public DCMotor2PinNoEnable( - PwmController pwmController, - double pwmFrequency, - int pwmChip, - int pwmChannel, + PwmChannel pwmChannel, int? pin1, GpioController controller) : base(controller) { - _pwm = pwmController; - _chip = pwmChip; - _channel = pwmChannel; + _pwm = pwmChannel; _pin1 = pin1; _speed = 0; - _pwm.OpenChannel(_chip, _channel); - _pwm.StartWriting(_chip, _channel, pwmFrequency, 0); + _pwm.Start(); if (_pin1.HasValue) { @@ -43,11 +35,6 @@ public DCMotor2PinNoEnable( } } - public DCMotor2PinNoEnable(double pwmFrequency, int pin0, int? pin1, GpioController controller) - : this(new PwmController(new SoftPwm()), pwmFrequency, pin0, 0, pin1, controller) - { - } - /// /// Gets or sets the speed of the motor. /// Speed is a value from 0 to 1 or -1 to 1 if direction pin has been provided. @@ -73,7 +60,7 @@ public override double Speed Controller.Write(_pin1.Value, PinValue.Low); } - SetPwmFill(val); + _pwm.DutyCyclePercentage = val; } else { @@ -82,18 +69,12 @@ public override double Speed Controller.Write(_pin1.Value, PinValue.High); } - SetPwmFill(1.0 + val); + _pwm.DutyCyclePercentage = 1.0 + val; } _speed = val; } } - - private void SetPwmFill(double fill) - { - _pwm.ChangeDutyCycle(_chip, _channel, fill * 100.0); - } - public override void Dispose() { base.Dispose(); diff --git a/src/devices/DCMotor/DCMotor3Pin.cs b/src/devices/DCMotor/DCMotor3Pin.cs index cd8dcbd944..c73ccc1891 100644 --- a/src/devices/DCMotor/DCMotor3Pin.cs +++ b/src/devices/DCMotor/DCMotor3Pin.cs @@ -11,37 +11,29 @@ namespace Iot.Device.DCMotor { internal class DCMotor3Pin : DCMotor { - private PwmController _pwm; - private int _chip; - private int _channel; + private PwmChannel _pwm; private int _pin0; private int _pin1; private double _speed; public DCMotor3Pin( - PwmController pwmController, - double pwmFrequency, - int pwmChip, - int pwmChannel, + PwmChannel pwmChannel, int pin0, int pin1, GpioController controller) : base(controller) { - if (pwmController == null) - throw new ArgumentNullException(nameof(pwmController)); + if (pwmChannel == null) + throw new ArgumentNullException(nameof(pwmChannel)); - _pwm = pwmController; - _chip = pwmChip; - _channel = pwmChannel; + _pwm = pwmChannel; _pin0 = pin0; _pin1 = pin1; _speed = 0; - _pwm.OpenChannel(_chip, _channel); - _pwm.StartWriting(_chip, _channel, pwmFrequency, 0); + _pwm.Start(); Controller.OpenPin(_pin0, PinMode.Output); Controller.Write(_pin0, PinValue.Low); @@ -68,7 +60,12 @@ public override double Speed if (_speed == val) return; - if (val >= 0.0) + if (val == 0.0) + { + Controller.Write(_pin0, PinValue.Low); + Controller.Write(_pin1, PinValue.Low); + } + else if (val > 0.0) { Controller.Write(_pin0, PinValue.Low); Controller.Write(_pin1, PinValue.High); @@ -79,19 +76,15 @@ public override double Speed Controller.Write(_pin1, PinValue.Low); } - SetPwmFill(Math.Abs(val)); + _pwm.DutyCyclePercentage = Math.Abs(val); _speed = val; } } - private void SetPwmFill(double fill) - { - _pwm.ChangeDutyCycle(_chip, _channel, fill * 100.0); - } - public override void Dispose() { + _speed = 0.0; _pwm?.Dispose(); _pwm = null; base.Dispose(); diff --git a/src/devices/DCMotor/DCMotorSettings.cs b/src/devices/DCMotor/DCMotorSettings.cs index 37c47b51aa..c163efbfa4 100644 --- a/src/devices/DCMotor/DCMotorSettings.cs +++ b/src/devices/DCMotor/DCMotorSettings.cs @@ -40,25 +40,10 @@ public class DCMotorSettings public GpioController Controller { get; set; } /// - /// PwmController class related to enable pin (3-pin setting). + /// PwmChannel class related to enable pin (3-pin setting). /// For 2- or 1-pin mode this allows changing PWM settings /// instead of automatically picked SoftPwm. /// - public PwmController PwmController { get; set; } - - /// - /// PWM chip number. For SoftPwm this will be a pin number. - /// - public int PwmChip { get; set; } = 0; - - /// - /// PWM channel. For SoftPwm this value is ignored. - /// - public int PwmChannel { get; set; } = 0; - - /// - /// PWM frequency. This value will default to 50Hz if not set. - /// - public double PwmFrequency { get; set; } = 50; + public PwmChannel PwmChannel { get; set; } } } diff --git a/src/devices/DCMotor/samples/samples.csproj b/src/devices/DCMotor/samples/DCMotor.sample.csproj similarity index 53% rename from src/devices/DCMotor/samples/samples.csproj rename to src/devices/DCMotor/samples/DCMotor.sample.csproj index 90700fa92a..59fdf2b59b 100644 --- a/src/devices/DCMotor/samples/samples.csproj +++ b/src/devices/DCMotor/samples/DCMotor.sample.csproj @@ -6,9 +6,9 @@ - - - + + $(AdditionalProperties);RuntimeIdentifier=linux + diff --git a/src/devices/DCMotor/samples/Program.cs b/src/devices/DCMotor/samples/Program.cs index cd331ceff3..8ba9233f9b 100644 --- a/src/devices/DCMotor/samples/Program.cs +++ b/src/devices/DCMotor/samples/Program.cs @@ -7,6 +7,7 @@ using System.Device.Pwm; using System.Device.Pwm.Drivers; using System.Diagnostics; +using System.Threading; using Iot.Device.DCMotor; namespace samples @@ -18,8 +19,8 @@ static DCMotorSettings TwoPinModeAutoPwm() // this will use software PWM on one of the pins return new DCMotorSettings() { - Pin0 = 24, - Pin1 = 23, // for 1 pin mode don't set this and connect your pin to the ground + Pin0 = 5, + Pin1 = 6, // for 1 pin mode don't set this and connect your pin to the ground UseEnableAsPwm = false, }; } @@ -28,38 +29,62 @@ static DCMotorSettings TwoPinModeManualPwm() { return new DCMotorSettings() { - Pin0 = 23, + Pin0 = 5, UseEnableAsPwm = false, - PwmController = new PwmController(new SoftPwm()), - PwmChip = 24, - // PwmChannel = 0, // use for hardware PWM - PwmFrequency = 50, // optional, defaults to 50 + PwmChannel = new SoftwarePwmChannel(6, 50), }; } - static DCMotorSettings ThreePinMode() + static DCMotorSettings ThreePinModeSoftware() { return new DCMotorSettings() { - Pin0 = 27, - Pin1 = 22, - PwmController = new PwmController(new SoftPwm()), - PwmChip = 17, - //PwmChannel = 1, // use for hardware PWM - PwmFrequency = 50, // optional, defaults to 50 + Pin0 = 5, + Pin1 = 6, + PwmChannel = new SoftwarePwmChannel(23, 50), + }; + } + + static DCMotorSettings ThreePinModeHardware() + { + return new DCMotorSettings() + { + Pin0 = 5, + Pin1 = 6, + PwmChannel = PwmChannel.Create(0, 0, 50), }; } static void Main(string[] args) { const double Period = 10.0; - DCMotorSettings settings = ThreePinMode(); + DCMotorSettings settings = ThreePinModeHardware(); Stopwatch sw = Stopwatch.StartNew(); using (DCMotor motor = DCMotor.Create(settings)) { - double time = sw.ElapsedMilliseconds / 1000.0; - // Note: range is from -1 .. 1 (for 1 pin setup 0 .. 1) - motor.Speed = Math.Sin(2.0 * Math.PI * time / Period); + bool done = false; + Console.CancelKeyPress += (o, e) => + { + done = true; + e.Cancel = true; + }; + + string lastSpeedDisp = null; + while (!done) + { + double time = sw.ElapsedMilliseconds / 1000.0; + + // Note: range is from -1 .. 1 (for 1 pin setup 0 .. 1) + motor.Speed = Math.Sin(2.0 * Math.PI * time / Period); + string disp = $"Speed = {motor.Speed:0.00}"; + if (disp != lastSpeedDisp) + { + lastSpeedDisp = disp; + Console.WriteLine(disp); + } + + Thread.Sleep(1); + } } } } diff --git a/src/devices/DCMotor/samples/README.md b/src/devices/DCMotor/samples/README.md index d0c49c7ea6..58a6bc189e 100644 --- a/src/devices/DCMotor/samples/README.md +++ b/src/devices/DCMotor/samples/README.md @@ -11,8 +11,8 @@ // this will use software PWM on one of the pins return new DCMotorSettings() { - Pin0 = 24, - Pin1 = 23, // for 1 pin mode don't set this and connect your pin to the ground + Pin0 = 5, + Pin1 = 6, // for 1 pin mode don't set this and connect your pin to the ground UseEnableAsPwm = false, }; } @@ -21,38 +21,62 @@ { return new DCMotorSettings() { - Pin0 = 23, + Pin0 = 5, UseEnableAsPwm = false, - PwmController = new PwmController(new SoftPwm()), - PwmChip = 24, - // PwmChannel = 0, // use for hardware PWM - PwmFrequency = 50, // optional, defaults to 50 + PwmChannel = new SoftwarePwmChannel(6, 50), }; } - static DCMotorSettings ThreePinMode() + static DCMotorSettings ThreePinModeSoftware() { return new DCMotorSettings() { - Pin0 = 27, - Pin1 = 22, - PwmController = new PwmController(new SoftPwm()), - PwmChip = 17, - //PwmChannel = 1, // use for hardware PWM - PwmFrequency = 50, // optional, defaults to 50 + Pin0 = 5, + Pin1 = 6, + PwmChannel = new SoftwarePwmChannel(23, 50), + }; + } + + static DCMotorSettings ThreePinModeHardware() + { + return new DCMotorSettings() + { + Pin0 = 5, + Pin1 = 6, + PwmChannel = PwmChannel.Create(0, 0, 50), }; } static void Main(string[] args) { const double Period = 10.0; - DCMotorSettings settings = ThreePinMode(); + DCMotorSettings settings = ThreePinModeHardware(); Stopwatch sw = Stopwatch.StartNew(); using (DCMotor motor = DCMotor.Create(settings)) { - double time = sw.ElapsedMilliseconds / 1000.0; - // Note: range is from -1 .. 1 (for 1 pin setup 0 .. 1) - motor.Speed = Math.Sin(2.0 * Math.PI * time / Period); + bool done = false; + Console.CancelKeyPress += (o, e) => + { + done = true; + e.Cancel = true; + }; + + string lastSpeedDisp = null; + while (!done) + { + double time = sw.ElapsedMilliseconds / 1000.0; + + // Note: range is from -1 .. 1 (for 1 pin setup 0 .. 1) + motor.Speed = Math.Sin(2.0 * Math.PI * time / Period); + string disp = $"Speed = {motor.Speed:0.00}"; + if (disp != lastSpeedDisp) + { + lastSpeedDisp = disp; + Console.WriteLine(disp); + } + + Thread.Sleep(1); + } } } ``` diff --git a/src/devices/SoftPwm/SoftwarePwmChannel.cs b/src/devices/SoftPwm/SoftwarePwmChannel.cs index e77f007196..69de12887a 100644 --- a/src/devices/SoftPwm/SoftwarePwmChannel.cs +++ b/src/devices/SoftPwm/SoftwarePwmChannel.cs @@ -54,9 +54,9 @@ public override double DutyCyclePercentage } } - public SoftwarePwmChannel(int pinNumber, int frequency = 400, double dutyCyclePercentage = 0.5) + public SoftwarePwmChannel(int pinNumber, int frequency = 400, double dutyCyclePercentage = 0.5, bool precisionTimer = false, GpioController controller = null) { - _controller = new GpioController(); + _controller = controller ?? new GpioController(); if (_controller == null) { Debug.WriteLine("GPIO does not exist on the current system."); @@ -74,20 +74,18 @@ public SoftwarePwmChannel(int pinNumber, int frequency = 400, double dutyCyclePe DutyCyclePercentage = dutyCyclePercentage; } - public SoftwarePwmChannel(int pinNumber, int frequency, double dutyCyclePercentage, bool preceisionTimer) : this(pinNumber, frequency, dutyCyclePercentage) - { - _precisionPWM = preceisionTimer; - } - private void UpdateRange() { - _currentPulseWidth = _percentage * _pulseFrequency / 100; + _currentPulseWidth = _percentage * _pulseFrequency; } private void RunSoftPWM() { if (_precisionPWM) + { Thread.CurrentThread.Priority = ThreadPriority.Highest; + } + while (_runThread) { // Write the pin high for the appropriate length of time @@ -98,17 +96,28 @@ private void RunSoftPWM() _controller.Write(_servoPin, PinValue.High); _isStopped = false; } + // Use the wait helper method to wait for the length of the pulse if (_precisionPWM) + { Wait(_currentPulseWidth); + } else + { Task.Delay(TimeSpan.FromMilliseconds(_currentPulseWidth)).Wait(); + } + // The pulse if over and so set the pin to low and then wait until it's time for the next pulse _controller.Write(_servoPin, PinValue.Low); + if (_precisionPWM) + { Wait(_pulseFrequency - _currentPulseWidth); + } else + { Task.Delay(TimeSpan.FromMilliseconds(_pulseFrequency - _currentPulseWidth)).Wait(); + } } else { @@ -153,8 +162,8 @@ protected override void Dispose(bool disposing) { _isRunning = false; _runThread = false; - while (_runningThread.ThreadState == Threading.ThreadState.Running) - Thread.Sleep(100); + _runningThread?.Join(); + _runningThread = null; _controller?.Dispose(); _controller = null; base.Dispose(disposing); diff --git a/src/devices/SoftPwm/samples/README.md b/src/devices/SoftPwm/samples/README.md index 497b0b8aa7..288b896c92 100644 --- a/src/devices/SoftPwm/samples/README.md +++ b/src/devices/SoftPwm/samples/README.md @@ -1,4 +1,4 @@ -# Led PWM with softPwm +# Led PWM with SoftPwm ## Schematic @@ -8,39 +8,24 @@ This example shows how to use the software PWM with a Led. Simply connect the Le ## Code -To initialize the software, you need to add ```using System.Device.Pwm.Drivers;``` and ```using System.Device.Pwm```. +To initialize the software with frequency of 200 on pin 17 and duty cycle of 50% (optional, duty cycle is value from 0.0 to 1.0 where 0.0 is 0% and 1.0 is 100%), you need to add ```using System.Device.Pwm``` and use following code: ```csharp -var PwmController = new PwmController(new SoftPwm()); +var channel = new SoftwarePwmChannel(17, 200, 0.5); +channel.Start(); ``` -You then need to open the PWM and start it. Please note that the first parameter is the GPIO you are using, in our case, 17. The second parameter is always ignored. It is used only for hardware PWM. The following code will open the software PWM and start it with a 200Hz frequency and a duty cycle of 0. Duty cycle is value between 0.0 and 100.0. 100.0 represents 100%. +Then you can change the duty cycle during the execution (75% used in the example below): ```csharp -PwmController.OpenChannel(17, 0); -PwmController.StartWriting(17,0, 200, 0); -``` - -Then you can change the duty cycle during the execution, for example to 50% here: - -```csharp -PwmController.ChangeDutyCycle(17, 0, 50); -``` - -Note: to release the GPIO pin, you have to close the PWM: - -```csharp -PwmController.CloseChannel(17, 0); +channel.DutyCyclePercentage = 0.75; ``` Here is a full example: ```csharp using System; -using System.Device.Pwm; using System.Device.Pwm.Drivers; -using System.Diagnostics; -using System.Device.Gpio; using System.Threading; class Program @@ -49,23 +34,21 @@ class Program static void Main(string[] args) { Console.WriteLine("Hello PWM!"); - var PwmController = new PwmController(new SoftPwm()); - PwmController.OpenChannel(17, 0); - PwmController.StartWriting(17,0, 200, 0); - while(true) + + using (var pwmChannel = new SoftwarePwmChannel(17, 200, 0)) { - for(int i = 0; i< 100; i++) + pwmChannel.Start(); + for (double fill = 0.0; fill <= 1.0; fill += 0.01) { - PwmController.ChangeDutyCycle(17, 0, i); - Thread.Sleep(100); + pwmChannel.DutyCyclePercentage = fill; + Thread.Sleep(500); } - } + } } } + ``` ## Other Example You will find another example of SoftPwm in the [Servo Motor class](/src/devices/Servo/samples/README.md). This Servomotor sample uses a precision timer. - - diff --git a/src/devices/SoftPwm/samples/SoftPwm.sample.cs b/src/devices/SoftPwm/samples/SoftPwm.sample.cs index 5b4b41a46d..e0196360d3 100644 --- a/src/devices/SoftPwm/samples/SoftPwm.sample.cs +++ b/src/devices/SoftPwm/samples/SoftPwm.sample.cs @@ -16,12 +16,11 @@ static void Main(string[] args) using (var pwmChannel = new SoftwarePwmChannel(17, 200, 0)) { pwmChannel.Start(); - for (int i = 0; i < 100; i++) + for (double fill = 0.0; fill <= 1.0; fill += 0.01) { - pwmChannel.DutyCyclePercentage = i; + pwmChannel.DutyCyclePercentage = fill; Thread.Sleep(500); } } - } } From 9651ef61c068bd126d5e31bfe54cf9f178325ba9 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Tue, 16 Jul 2019 16:15:55 -0700 Subject: [PATCH 4/7] Adding windows IoT PwmChannel implementation --- .../Channels/Windows10PwmChannel.Windows.cs | 96 +++++++++++++++++-- src/devices/DCMotor/DCMotor.csproj | 2 +- .../DCMotor/samples/DCMotor.sample.csproj | 6 +- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs index 7b6eaa4a87..cee88af5eb 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs @@ -2,16 +2,22 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.IO; -using System.Threading; +using Windows.Devices.Enumeration; +using Windows.Security.ExchangeActiveSyncProvisioning; +using WinPwm = Windows.Devices.Pwm; namespace System.Device.Pwm.Channels { /// /// Represents a PWM channel running on Windows 10 IoT. /// - internal class Windows10PwmChannel : PwmChannel + internal partial class Windows10PwmChannel : PwmChannel { + private WinPwm.PwmController _winController; + private WinPwm.PwmPin _winPin; + private int _frequency; + private double _dutyCyclePercentage; + /// /// Initializes a new instance of the class. /// @@ -25,21 +31,95 @@ public Windows10PwmChannel( int frequency = 400, double dutyCyclePercentage = 0.5) { - // TODO: This is just a placeholder to complete later. + // When running on Hummingboard we require to use the default chip. + var deviceInfo = new EasClientDeviceInformation(); + bool useDefaultChip = false; + if (deviceInfo.SystemProductName.IndexOf("Hummingboard", StringComparison.OrdinalIgnoreCase) >= 0) + { + useDefaultChip = true; + } + + // Open the Windows PWM controller for the specified PWM chip. + string deviceSelector = useDefaultChip ? WinPwm.PwmController.GetDeviceSelector() : WinPwm.PwmController.GetDeviceSelector($"PWM{chip}"); + + DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); + if (deviceInformationCollection.Count == 0) + { + throw new ArgumentException($"No PWM device exists for PWM chip at index {chip}.", $"{nameof(chip)}"); + } + + string deviceId = deviceInformationCollection[0].Id; + _winController = WinPwm.PwmController.FromIdAsync(deviceId).WaitForCompletion(); + + _winPin = _winController.OpenPin(channel); + if (_winPin == null) + { + throw new ArgumentOutOfRangeException($"The PWM chip is unable to open a channel at index {channel}.", nameof(channel)); + } + + Frequency = frequency; + DutyCyclePercentage = dutyCyclePercentage; } - public override int Frequency { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + /// + /// The frequency in hertz. + /// + public override int Frequency + { + get => _frequency; + set + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Value must note be negative."); + } + _winController.SetDesiredFrequency(value); + _frequency = value; + } + } - public override double DutyCyclePercentage { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + /// + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// + public override double DutyCyclePercentage + { + get => _dutyCyclePercentage; + set + { + if (value < 0.0 || value > 1.0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be between 0.0 and 1.0."); + } + _winPin.SetActiveDutyCyclePercentage(value); + _dutyCyclePercentage = value; + } + } + /// + /// Starts the PWM channel. + /// public override void Start() { - throw new NotImplementedException(); + _winPin.Start(); + // This extra call is required to generate PWM output - remove when the underlying issue is fixed. See issue #109 + DutyCyclePercentage = _dutyCyclePercentage; } + /// + /// Stops the PWM channel. + /// public override void Stop() { - throw new NotImplementedException(); + _winPin.Stop(); + } + + protected override void Dispose(bool disposing) + { + Stop(); + _winPin?.Dispose(); + _winPin = null; + _winController = null; + base.Dispose(disposing); } } } diff --git a/src/devices/DCMotor/DCMotor.csproj b/src/devices/DCMotor/DCMotor.csproj index d8112ae443..bb2ef25fb3 100644 --- a/src/devices/DCMotor/DCMotor.csproj +++ b/src/devices/DCMotor/DCMotor.csproj @@ -9,7 +9,7 @@ $(AdditionalProperties);RuntimeIdentifier=linux - + diff --git a/src/devices/DCMotor/samples/DCMotor.sample.csproj b/src/devices/DCMotor/samples/DCMotor.sample.csproj index 59fdf2b59b..3531ebaca4 100644 --- a/src/devices/DCMotor/samples/DCMotor.sample.csproj +++ b/src/devices/DCMotor/samples/DCMotor.sample.csproj @@ -6,11 +6,11 @@ - + $(AdditionalProperties);RuntimeIdentifier=linux - - + + From 4a6205d86c1946323e4089374a653472f5c031fe Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Tue, 16 Jul 2019 18:12:30 -0700 Subject: [PATCH 5/7] fix XML comment --- src/devices/Servo/ServoMotor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/devices/Servo/ServoMotor.cs b/src/devices/Servo/ServoMotor.cs index 4f1a529ef9..d34bb028ca 100644 --- a/src/devices/Servo/ServoMotor.cs +++ b/src/devices/Servo/ServoMotor.cs @@ -40,8 +40,7 @@ public class ServoMotor : IDisposable /// /// Initialize a ServoMotor class /// - /// The GPIO pin number in case of a software PWM. The chip in case of a hardware PWM - /// The channel to use in case of a hardware PWM. + /// The PWM channel to use. /// The definition of a ServoMotor /// Use -1 for pwmChannel to force using software PWM public ServoMotor(PwmChannel pwmChannel, ServoMotorDefinition definition) From 6bb79e8679f096d7a407e0469598be5c1ad0b300 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Wed, 17 Jul 2019 01:16:47 -0700 Subject: [PATCH 6/7] remove Console.WriteLine from the product --- .../System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs index df200a4030..2ffee30409 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs @@ -39,7 +39,6 @@ public UnixPwmChannel( Validate(); Open(); //Thread.Sleep(100); // TODO: Need a better solution to delay for available file. - Console.WriteLine("Set Frequency"); SetFrequency(frequency); DutyCyclePercentage = dutyCyclePercentage; } From b155b5f6122c58f34c8fd89957a31dddf16fd0a1 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Wed, 17 Jul 2019 12:59:13 -0700 Subject: [PATCH 7/7] Fixing README files and add xml comments --- .../Pwm/Channels/UnixPwmChannel.Linux.cs | 6 ++- .../Channels/Windows10PwmChannel.Windows.cs | 4 +- src/devices/Buzzer/README.md | 18 +++---- src/devices/Buzzer/samples/Buzzer.Sample.cs | 4 +- .../Buzzer/samples/Buzzer.Samples.csproj | 5 +- src/devices/Buzzer/samples/README.md | 8 +-- src/devices/Servo/README.md | 14 ++--- src/devices/Servo/ServoMotor.cs | 23 ++++++--- src/devices/Servo/samples/README.md | 5 +- src/devices/Servo/samples/Servo.sample.cs | 2 +- src/devices/SoftPwm/README.md | 51 +++++-------------- src/devices/SoftPwm/SoftwarePwmChannel.cs | 29 ++++++++++- src/devices/SoftPwm/samples/README.md | 2 +- 13 files changed, 92 insertions(+), 79 deletions(-) diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs index 2ffee30409..3951ccb8fe 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/UnixPwmChannel.Linux.cs @@ -38,7 +38,6 @@ public UnixPwmChannel( _channel = channel; Validate(); Open(); - //Thread.Sleep(100); // TODO: Need a better solution to delay for available file. SetFrequency(frequency); DutyCyclePercentage = dutyCyclePercentage; } @@ -90,6 +89,11 @@ private static int GetPeriodInNanoSeconds(int frequency) /// The frequency in hertz to set. private void SetFrequency(int frequency) { + if (frequency < 0) + { + throw new ArgumentOutOfRangeException(nameof(frequency), frequency, "Value must not be negative."); + } + int periodInNanoSeconds = GetPeriodInNanoSeconds(frequency); File.WriteAllText($"{_channelPath}/period", Convert.ToString(periodInNanoSeconds)); _frequency = frequency; diff --git a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs index cee88af5eb..5cbd046089 100644 --- a/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs +++ b/src/System.Device.Gpio/System/Device/Pwm/Channels/Windows10PwmChannel.Windows.cs @@ -45,7 +45,7 @@ public Windows10PwmChannel( DeviceInformationCollection deviceInformationCollection = DeviceInformation.FindAllAsync(deviceSelector).WaitForCompletion(); if (deviceInformationCollection.Count == 0) { - throw new ArgumentException($"No PWM device exists for PWM chip at index {chip}.", $"{nameof(chip)}"); + throw new ArgumentException($"No PWM device exists for PWM chip at index {chip}.", nameof(chip)); } string deviceId = deviceInformationCollection[0].Id; @@ -71,7 +71,7 @@ public override int Frequency { if (value < 0) { - throw new ArgumentOutOfRangeException(nameof(value), value, "Value must note be negative."); + throw new ArgumentOutOfRangeException(nameof(value), value, "Value must not be negative."); } _winController.SetDesiredFrequency(value); _frequency = value; diff --git a/src/devices/Buzzer/README.md b/src/devices/Buzzer/README.md index 477c66ebeb..ef74b3e2c8 100644 --- a/src/devices/Buzzer/README.md +++ b/src/devices/Buzzer/README.md @@ -14,15 +14,15 @@ This binding was tested on two types of piezo buzzers. First type of buzzer has The `Buzzer` class can use either software or hardware PWM. This is done fully transparently by the initialization. -If you want to use the software PWM, you have to specify the GPIO pin you want to use as the first parameter in the constructor. Use the value -1 for the second one. This will force usage of the software PWM as it is not a valid value for hardware PWM. +If you want to use the software PWM, you have to call the constructor that takes in one integer: `public Buzzer(int pinNumber)`. -To use the hardware PWM, make sure you reference correctly the chip and channel you want to use. The `Buzzer` class will always try first to open a hardware PWM then a software PWM. +To use the hardware PWM, make sure you reference correctly the chip and channel you want to use, and call the constructor that takes two integers (chip and channel). -Also you could explicitly pass PWM controller as third parameter of an appropriate constructor. +Also you could explicitly pass a PwmChannel if you want to construct that yourself. Here's an example how you could use `Buzzer`. ```csharp -using (Buzzer buzzer = new Buzzer(21, -1)); // Initialize buzzer with software PWM connected to pin 21. +using (Buzzer buzzer = new Buzzer(21)); // Initialize buzzer with software PWM connected to pin 21. { buzzer.PlayTone(440, 1000); // Play tone with frequency 440 hertz for one second. } @@ -30,9 +30,9 @@ using (Buzzer buzzer = new Buzzer(21, -1)); // Initialize buzzer with software P `Buzzer` allows to play tone for certain duration like in example above. Or you could start tone playing, perform some operation and then stop tone playing like in a following example. ```csharp -using (Buzzer buzzer = new Buzzer(21, -1)); +using (Buzzer buzzer = new Buzzer(21)); { - buzzer.SetFrequency(440); + buzzer.StartPlaying(440); Thread.Sleep(1000); buzzer.StopPlaying(); } @@ -41,11 +41,11 @@ The result will be the same as in previous example. `Buzzer` allows you to play only single tone at a single moment. If you will call `SetFrequency` sequentially with a different frequencies then the last call will override previous calls. Following example explains it. ```csharp -using (Buzzer buzzer = new Buzzer(21, -1)); // Initialize buzzer with software PWM connected to pin 21. +using (Buzzer buzzer = new Buzzer(21)); // Initialize buzzer with software PWM connected to pin 21. { - buzzer.SetFrequency(440); + buzzer.StartPlaying(440); Thread.Sleep(1000); - buzzer.SetFrequency(880); + buzzer.StartPlaying(880); Thread.Sleep(1000); buzzer.StopPlaying(); } diff --git a/src/devices/Buzzer/samples/Buzzer.Sample.cs b/src/devices/Buzzer/samples/Buzzer.Sample.cs index ab94121b07..1eaccc7d48 100644 --- a/src/devices/Buzzer/samples/Buzzer.Sample.cs +++ b/src/devices/Buzzer/samples/Buzzer.Sample.cs @@ -54,8 +54,8 @@ class Program static void Main(string[] args) { - using (var player1 = new MelodyPlayer(new Buzzer(21, -1))) - using (var player2 = new MelodyPlayer(new Buzzer(26, -1))) + using (var player1 = new MelodyPlayer(new Buzzer(21))) + using (var player2 = new MelodyPlayer(new Buzzer(26))) { Task.WaitAll( Task.Run(() => player1.Play(AlphabetSong, 100, -12)), diff --git a/src/devices/Buzzer/samples/Buzzer.Samples.csproj b/src/devices/Buzzer/samples/Buzzer.Samples.csproj index 15831d847b..05f1f86c1d 100644 --- a/src/devices/Buzzer/samples/Buzzer.Samples.csproj +++ b/src/devices/Buzzer/samples/Buzzer.Samples.csproj @@ -6,7 +6,10 @@ - + + + $(AdditionalProperties);RuntimeIdentifier=linux + diff --git a/src/devices/Buzzer/samples/README.md b/src/devices/Buzzer/samples/README.md index bbeb0b4697..b30c9e2055 100644 --- a/src/devices/Buzzer/samples/README.md +++ b/src/devices/Buzzer/samples/README.md @@ -25,7 +25,7 @@ This sample contains a wrapper on a Buzzer called `MelodyPlayer`. To create an instance of a MelodyPlayer use following line: ```csharp -MelodyPlayer player = new MelodyPlayer(new Buzzer(26, -1)); +MelodyPlayer player = new MelodyPlayer(new Buzzer(26)); ``` Constructor takes a single parameter type of `Buzzer`. @@ -49,7 +49,7 @@ IList sequence = new List() new NoteElement(Note.C, Octave.Fourth, Duration.Quarter) }; -using (var player = new MelodyPlayer(new Buzzer(21, -1))) +using (var player = new MelodyPlayer(new Buzzer(21))) { player.Play(sequence, 100); } @@ -71,8 +71,8 @@ player.Play(sequence, 100, -12); As far as `MelodyPlayer.Play` method is not asynchronous, calls of this method are wrapped by task like this: ```csharp -using (var player1 = new MelodyPlayer(new Buzzer(21, -1))) -using (var player2 = new MelodyPlayer(new Buzzer(26, -1))) +using (var player1 = new MelodyPlayer(new Buzzer(21))) +using (var player2 = new MelodyPlayer(new Buzzer(26))) { Task.WaitAll( Task.Run(() => player1.Play(AlphabetSong, 100, -12)), diff --git a/src/devices/Servo/README.md b/src/devices/Servo/README.md index 034ce42e11..0f58eb043c 100644 --- a/src/devices/Servo/README.md +++ b/src/devices/Servo/README.md @@ -14,14 +14,14 @@ All servomotors have specific minimum and maximum pulse frequency for their oper The ```ServoMotor``` class can use either software either hardware PWM. this is done fully transparently by the initialization. -If you want to use the software PWM, you have to specify the GPIO pin you want to use as the first parameter in the constructor. Use the value -1 for the second one. This will force usage of the software PWM as it is not a valid value for hardware PWM. +If you want to use the software PWM, you have to specify the GPIO pin you want to use as the first parameter in the constructor. -To use the hardware PWM, make sure you reference correctly the chip and channel you want to use. The ```ServoMotor``` class will always try first to open a hardware PWM then a software PWM. +To use the hardware PWM, make sure you reference correctly the chip and channel you want to use. ```csharp -// example of software PWM piloted Servo -ServoMotor servoSoft = new ServoMotor(21, -1, new ServoMotorDefinition(540, 2470)); -// example of hardware PWM piloted Servo +// example of software PWM piloted Servo using pin 21 +ServoMotor servoSoft = new ServoMotor(21, new ServoMotorDefinition(540, 2470)); +// example of hardware PWM piloted Servo using chip 0 channel 0 ServoMotor servoHard = new ServoMotor(0, 0, new ServoMotorDefinition(540, 2470)); ``` @@ -61,7 +61,3 @@ servo.SetPulse(2000); This will turn the servo motor using a 2 milliseconds pulse. -## Dependencies - -This class has a dependency on ```SoftPwm```. Please make sure you add this software PWM in your project. - diff --git a/src/devices/Servo/ServoMotor.cs b/src/devices/Servo/ServoMotor.cs index d34bb028ca..0608d46ae6 100644 --- a/src/devices/Servo/ServoMotor.cs +++ b/src/devices/Servo/ServoMotor.cs @@ -31,6 +31,7 @@ public class ServoMotor : IDisposable /// Servo motor definition. /// private ServoMotorDefinition _definition; + /// /// The duration per angle's degree. /// @@ -38,30 +39,40 @@ public class ServoMotor : IDisposable private PwmChannel _pwmChannel; /// - /// Initialize a ServoMotor class + /// Initialize a using a specified PwmChannel /// - /// The PWM channel to use. - /// The definition of a ServoMotor - /// Use -1 for pwmChannel to force using software PWM + /// The to be used to control this servo. + /// The to be used public ServoMotor(PwmChannel pwmChannel, ServoMotorDefinition definition) { - this._definition = definition; + _definition = definition; _pulseFrequency = definition.PeriodMicroseconds / 1000.0; UpdateRange(); - this._pwmChannel = pwmChannel; + _pwmChannel = pwmChannel; _pwmChannel.DutyCyclePercentage = (1 - (_pulseFrequency - _currentPulseWidth) / _pulseFrequency) * 100; _pwmChannel.Start(); } + /// + /// Initialize a using on + /// + /// The pin that will be used for the software pwm + /// The to be used public ServoMotor(int pinNumber, ServoMotorDefinition definition) : this(CreatePwmChannel(pinNumber, 0, 0, definition, false), definition) { IsRunningHardwarePwm = false; } + /// + /// Initialize a using Hardware Pwm on and + /// + /// The chip number for Hardware Pwm + /// The channel number for Hardware Pwm + /// The to be used public ServoMotor(int chip, int channel, ServoMotorDefinition definition) : this(CreatePwmChannel(-1, chip, channel, definition, true), definition) { diff --git a/src/devices/Servo/samples/README.md b/src/devices/Servo/samples/README.md index 652b7948d3..5c3c4d4630 100644 --- a/src/devices/Servo/samples/README.md +++ b/src/devices/Servo/samples/README.md @@ -13,14 +13,13 @@ Note: servomotors are consumming quite a lot. Make sure you have powered enought You can create a servomotor with the following line: ```csharp -ServoMotor servo = new ServoMotor(21, -1, new ServoMotorDefinition(540, 2470)); +ServoMotor servo = new ServoMotor(21, new ServoMotorDefinition(540, 2470)); ``` Make sure you are using the following namespace: ```Iot.Device.Servo``` In the constructor, you will need to pass the following elements by order: - the GPIO pin you want to use for the sofware PWM, here 21 -- to force the usage of software PWM, use -1 as second parameter - a servomotor definition, refer to the main [servomotor documentation](../README.md) for more information To turn your servomotor, just setup an angle: @@ -48,7 +47,7 @@ class Program Console.WriteLine("Hello Servo!"); // example of software PWM piloted Servo on GPIO 21 - ServoMotor servo = new ServoMotor(21, -1, new ServoMotorDefinition(540, 2470, 20000, 100)); + ServoMotor servo = new ServoMotor(21, new ServoMotorDefinition(540, 2470, 20000, 100)); // example of hardware PWM piloted Servo on chip 0 channel 0 // ServoMotor servo = new ServoMotor(0, 0, new ServoMotorDefinition(540, 2470, 20000, 100)); if (servo.IsRunningHardwarePwm) diff --git a/src/devices/Servo/samples/Servo.sample.cs b/src/devices/Servo/samples/Servo.sample.cs index 5f760380cc..e8d3c93a0e 100644 --- a/src/devices/Servo/samples/Servo.sample.cs +++ b/src/devices/Servo/samples/Servo.sample.cs @@ -14,7 +14,7 @@ static void Main(string[] args) Console.WriteLine("Hello Servo!"); // example of software PWM piloted Servo on GPIO 21 - ServoMotor servo = new ServoMotor(21, -1, new ServoMotorDefinition(540, 2470, 20000, 100)); + ServoMotor servo = new ServoMotor(21, new ServoMotorDefinition(540, 2470, 20000, 100)); // example of hardware PWM piloted Servo on chip 0 channel 0 // ServoMotor servo = new ServoMotor(0, 0, new ServoMotorDefinition(540, 2470)); if (servo.IsRunningHardwarePwm) diff --git a/src/devices/SoftPwm/README.md b/src/devices/SoftPwm/README.md index b2e3a27e9d..1924ad6261 100644 --- a/src/devices/SoftPwm/README.md +++ b/src/devices/SoftPwm/README.md @@ -4,78 +4,51 @@ Software PWM is necessary when you cannot use hardware PWM. PWM is used for exam ## How to Use -SoftPwm is part of ```System.Device.Pwm.Drivers``` and is a driver for the ```System.Device.Pwm.PwmController``` class. You can then create a software PWM like this: +You can then create a software PWM like this: ```csharp -var PwmController = new PwmController(new SoftPwm()); +// Creates a Software Pwm channel on GPIO pin 17 with a frequency of 200 hertz an initial duty cycle of 0% +var softwarePwmChannel = new SoftwarePwmChannel(17, 200, 0); ``` -By default SoftPwm is using a low priority clock to emulate the PWM. It is ok if you have leds and other non time sensitive elements attached. +By default SoftwarePwmChannel is using a low priority clock to emulate the PWM. It is ok if you have leds and other non time sensitive elements attached. -**Important:** if you have clock sensitive elements attached to this software PWM, you need to use a high precision timer. In order to make it happen, you need to select it at creation time by passing ```true``` in the constructor: +**Important:** if you have clock sensitive elements attached to this software PWM, you need to use a high precision timer. In order to make it happen, you need to select it at creation time by passing ```true``` to the precisionTimer parameter in the constructor: ```csharp -var PwmController = new PwmController(new SoftPwm(true)); -``` - -## Key elements - -Compare to hardware PWM, software PWM only needs a GPIO Pin and the channel parameter is ignored. So you can pass any ```int``` value, the channel, will always been ignore. - -The first parameter has to be the GPIO and will always be checked and has to be the same. - -### Opening the PWM - -Usage is the same as the PwmController as the SoftPwm fully implement the needed classes. - -This example shows how to open the GPIO 17 as a software PWM. The channel, here 0, is always ignored. It is used for hardware PWM. - -```csharp -PwmController.OpenChannel(17, 0); +var softwarePwmChannelWithPrecisionTimer = new SoftwarePwmChannel(17, frequency: 50, dutyCyclePercentage = 0.5, precisionTimer: true); ``` ### Starting the PWM -Usage is the same as the PwmController. You first need to open the Channel before starting the PWM. +Usage is the same as the PwmChannel. This example shows how to start the PWM, previously open on GPIO 17. It will start a 50Hz software PWM with 50% duty cycle: ```csharp -PwmController.StartWriting(17,0, 50, 50); +softwarePwmChannel.Start(); ``` ### Change Duty Cycle -Usage is the same as the PwmController. You first need to open the Channel before starting the PWM. +Usage is the same as the PwmChannel. This example shows how to change the duty cycle of the PWM, previously open on GPIO 17. Duty cycle is changed for 25% with immediate effect if the PWM is already started. ```csharp -PwmController.ChangeDutyCycle(17, 0, 25); +softwarePwmChannel.DutyCycle = 0.25; ``` ### Stop the PWM -Usage is the same as the PwmController. You first need to open the Channel before starting the PWM. +Usage is the same as the PwmChannel. This example will stop the PWM previously open on GPIO 17. ```csharp -PwmController.StopWriting(17, 0); -``` - -### Close the PWM - -Usage is the same as the PwmController. You first need to open the Channel before starting the PWM. - -This example will release the GPIO 17 previously open. The GPIO is available for other usage. - -```csharp -PwmController.CloseChannel(17, 0); +softwarePwmChannel.Stop(); ``` -Closing the PWM will as well release the thread used for the PWM clock. - ## Performance Considerations The high precision software PWM is resource intensive and is using a high priority thread. You may have resources issues if you are using multiple high precision software PWM. Always prefer hardware PWM to software PWM when you can have access. diff --git a/src/devices/SoftPwm/SoftwarePwmChannel.cs b/src/devices/SoftPwm/SoftwarePwmChannel.cs index 69de12887a..3ad1cb9ba8 100644 --- a/src/devices/SoftPwm/SoftwarePwmChannel.cs +++ b/src/devices/SoftPwm/SoftwarePwmChannel.cs @@ -33,27 +33,49 @@ public class SoftwarePwmChannel : PwmChannel private GpioController _controller; private bool _runThread = true; + /// + /// The frequency in hertz. + /// public override int Frequency { get => _frequency; set { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Value must note be negative."); + } _frequency = value; _pulseFrequency = (_frequency > 0) ? 1 / _frequency * 1000.0 : 0.0; UpdateRange(); } } + /// + /// The duty cycle percentage represented as a value between 0.0 and 1.0. + /// public override double DutyCyclePercentage { get => _percentage; set { + if (value < 0.0 || value > 1.0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Value must be between 0.0 and 1.0."); + } _percentage = value; UpdateRange(); } } + /// + /// Initializes a new instance of the class. + /// + /// The GPIO pin number to be used + /// The frequency in hertz. Defaults to 400 + /// The duty cycle percentage represented as a value between 0.0 and 1.0 + /// to use a precision timer. otherwise + /// The to which belongs to. Null defaults to board GpioController public SoftwarePwmChannel(int pinNumber, int frequency = 400, double dutyCyclePercentage = 0.5, bool precisionTimer = false, GpioController controller = null) { _controller = controller ?? new GpioController(); @@ -147,12 +169,17 @@ private void Wait(double milliseconds) //nothing than waiting } } - + /// + /// Starts the PWM channel. + /// public override void Start() { _isRunning = true; } + /// + /// Stops the PWM channel. + /// public override void Stop() { _isRunning = false; diff --git a/src/devices/SoftPwm/samples/README.md b/src/devices/SoftPwm/samples/README.md index 288b896c92..e725d6bcca 100644 --- a/src/devices/SoftPwm/samples/README.md +++ b/src/devices/SoftPwm/samples/README.md @@ -8,7 +8,7 @@ This example shows how to use the software PWM with a Led. Simply connect the Le ## Code -To initialize the software with frequency of 200 on pin 17 and duty cycle of 50% (optional, duty cycle is value from 0.0 to 1.0 where 0.0 is 0% and 1.0 is 100%), you need to add ```using System.Device.Pwm``` and use following code: +To initialize the software with frequency of 200 on pin 17 and duty cycle of 50% (optional, duty cycle is value from 0.0 to 1.0 where 0.0 is 0% and 1.0 is 100%), you need to use following code: ```csharp var channel = new SoftwarePwmChannel(17, 200, 0.5);