未验证 提交 7ded2794 编写于 作者: J Jose Perez Rodriguez 提交者: GitHub

Adding Hardware PWM for Unix Driver (#10)

* Adding Hardware PWM for Unix Driver

* Adding comments to sample pwm project

* PR Feedback
上级 6c1ce8b7
......@@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3E92214F-419
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "System.Device.Gpio", "System.Device.Gpio", "{7AC384A7-051B-4D84-9780-BB1409B74DBA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "pwm-led", "samples\pwm-led\pwm-led.csproj", "{E84D3BE3-4885-4A4C-8368-6C627A164D60}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -39,6 +41,10 @@ Global
{9588B74A-CC0E-4203-8DE4-3ADCB0F7DACA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9588B74A-CC0E-4203-8DE4-3ADCB0F7DACA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9588B74A-CC0E-4203-8DE4-3ADCB0F7DACA}.Release|Any CPU.Build.0 = Release|Any CPU
{E84D3BE3-4885-4A4C-8368-6C627A164D60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E84D3BE3-4885-4A4C-8368-6C627A164D60}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E84D3BE3-4885-4A4C-8368-6C627A164D60}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E84D3BE3-4885-4A4C-8368-6C627A164D60}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -49,6 +55,7 @@ Global
{53B48D1D-B33E-438A-ACC8-4D0883A76F5C} = {216F0CA6-B99D-49AA-814E-2A1B5D7DC4C6}
{9588B74A-CC0E-4203-8DE4-3ADCB0F7DACA} = {216F0CA6-B99D-49AA-814E-2A1B5D7DC4C6}
{7AC384A7-051B-4D84-9780-BB1409B74DBA} = {3E92214F-4190-4154-A0DA-CFDCE396DBEA}
{E84D3BE3-4885-4A4C-8368-6C627A164D60} = {216F0CA6-B99D-49AA-814E-2A1B5D7DC4C6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4843B4E1-7FE9-4733-8250-275DB7641858}
......
using System;
using System.Device.Gpio;
using System.Devices.Gpio;
using System.Threading;
namespace pwm_led
{
class Program
{
static void Main(string[] args)
{
// GPIO pin 18 maps to chip 0 channel 0 on the Raspberry pi
int pwmChannel = 0;
int pwmChip = 0;
int pwmPeriod = (int)((1f/800f)*1_000_000_000); // Setting the period to 800 MHz
int pwm80PercentFrequency = (int)(pwmPeriod*0.8f); // Frequency at 80%
int pwm40PercentFrequency = (int)(pwmPeriod*0.4f); // Frequency at 40%
using (GpioController controller = new GpioController())
{
GpioPWMPin led = controller.OpenPWMPin(pwmChip, pwmChannel);
led.Period = pwmPeriod;
for (int i = 0; i < 10; i++)
{
led.DutyCycle = pwm80PercentFrequency;
led.PWMWrite(); // Setting frequency to 80%. Should make the LED look brighter.
Thread.Sleep(TimeSpan.FromSeconds(2));
led.DutyCycle = pwm40PercentFrequency;
led.PWMWrite(); // Setting frequency to 40%. Should make the LED look less bright.
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<RootNamespace>pwm_led</RootNamespace>
<RuntimeIdentifiers>linux-arm</RuntimeIdentifiers>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/System.Device.Gpio/System.Device.Gpio.csproj" />
</ItemGroup>
</Project>
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Device.Gpio;
using System.Linq;
using System.Runtime.InteropServices;
......@@ -11,6 +12,7 @@ namespace System.Devices.Gpio
public class GpioController : IDisposable
{
private IDictionary<int, GpioPin> _pins;
private IDictionary<Tuple<int, int>, GpioPWMPin> _pwmPins;
public GpioController(PinNumberingScheme numbering = PinNumberingScheme.Gpio)
{
......@@ -43,6 +45,7 @@ namespace System.Devices.Gpio
Driver = driver;
Numbering = numbering;
_pins = new Dictionary<int, GpioPin>();
_pwmPins = new Dictionary<Tuple<int, int>, GpioPWMPin>();
driver.ValueChanged += OnPinValueChanged;
}
......@@ -55,6 +58,12 @@ namespace System.Devices.Gpio
pin.Dispose();
}
while (_pwmPins.Count > 0)
{
GpioPWMPin pin = _pwmPins.Values.First();
pin.Dispose();
}
Driver.Dispose();
}
......@@ -104,6 +113,22 @@ namespace System.Devices.Gpio
return pin;
}
public GpioPWMPin OpenPWMPin(int chip, int channel, PWMMode mode = PWMMode.Balanced, int period = 0, int dutyCycle = 0)
{
GpioPWMPin pin;
bool isOpen = _pwmPins.TryGetValue(new Tuple<int, int>(chip, channel), out pin);
if (isOpen)
{
throw new GpioException("PWM channel is already opened");
}
Driver.OpenPWMPin(chip, channel);
pin = new GpioPWMPin(this, chip, channel, mode, period, dutyCycle);
_pwmPins[new Tuple<int, int>(chip, channel)] = pin;
return pin;
}
public void ClosePin(int pinNumber)
{
int gpioNumber = Driver.ConvertPinNumber(pinNumber, Numbering, PinNumberingScheme.Gpio);
......@@ -130,6 +155,21 @@ namespace System.Devices.Gpio
InternalClosePin(pin);
}
public void ClosePWMPin(GpioPWMPin pin)
{
if (pin == null)
{
throw new ArgumentNullException(nameof(pin));
}
if (pin.Controller != this)
{
throw new ArgumentException("The given PWM pin does not belong to this controller");
}
_pwmPins.Remove(new Tuple<int, int>(pin.Chip, pin.Channel));
Driver.ClosePWMPin(pin.Chip, pin.Channel);
}
private void InternalClosePin(GpioPin pin)
{
int gpioNumber = pin.GpioNumber;
......
......@@ -2,6 +2,8 @@
// 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.Gpio;
namespace System.Devices.Gpio
{
public abstract class GpioDriver : IDisposable
......@@ -16,14 +18,20 @@ namespace System.Devices.Gpio
protected internal abstract void OpenPin(int gpioPinNumber);
protected internal abstract void OpenPWMPin(int chip, int channel);
protected internal abstract void ClosePin(int gpioPinNumber);
protected internal abstract void ClosePWMPin(int chip, int channel);
protected internal abstract void SetPinMode(int gpioPinNumber, PinMode mode);
protected internal abstract PinMode GetPinMode(int gpioPinNumber);
protected internal abstract void Output(int gpioPinNumber, PinValue value);
protected internal abstract void PWMWrite(int chip, int channel, PWMMode mode, int period, int dutyCycle);
protected internal abstract PinValue Input(int gpioPinNumber);
protected internal abstract void SetDebounce(int gpioPinNumber, TimeSpan timeout);
......
using System;
using System.Collections.Generic;
using System.Devices.Gpio;
using System.Text;
namespace System.Device.Gpio
{
public enum PWMMode
{
Balanced,
Serial
}
public class GpioPWMPin : IDisposable
{
internal GpioPWMPin(GpioController controller, int chip, int channel, PWMMode pwmMode, int period, int dutyCycle)
{
Controller = controller;
Chip = chip;
Channel = channel;
PWMMode = pwmMode;
Period = period;
DutyCycle = dutyCycle;
}
public void Dispose()
{
Controller.ClosePWMPin(this);
}
public GpioController Controller { get; }
public int Chip { get; }
public int Channel { get; }
public PWMMode PWMMode { get; set; }
public int Period { get; set; }
public int DutyCycle { get; set; }
public void PWMWrite() => Controller.Driver.PWMWrite(Chip, Channel, PWMMode, Period, DutyCycle);
}
}
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Device.Gpio;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
......@@ -202,6 +203,12 @@ namespace System.Devices.Gpio
Initialize();
}
protected internal override void OpenPWMPin(int chip, int channel)
{
// ToDo: Add validation for chip and channel.
// ToDo: Add initialization setup required for PWM if it is not done already.
}
protected internal override void ClosePin(int gpioPinNumber)
{
ValidatePinNumber(gpioPinNumber);
......@@ -952,6 +959,16 @@ namespace System.Devices.Gpio
}
}
protected internal override void ClosePWMPin(int chip, int channel)
{
throw new NotImplementedException();
}
protected internal override void PWMWrite(int chip, int channel, PWMMode mode, int period, int dutyCycle)
{
throw new NotImplementedException();
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
//private static void SetBit(UInt32* pointer, int bit, uint value = 1)
//{
......
......@@ -4,6 +4,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Device.Gpio;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
......@@ -93,9 +94,11 @@ namespace System.Devices.Gpio
#endregion
private const string GpioPath = "/sys/class/gpio";
private const string PWMPath = "/sys/class/pwm";
private readonly int _pinCount;
private readonly IList<int> _exportedPins;
private readonly IList<Tuple<int,int>> _exportedPWMChannels;
private int _pollFileDescriptor = -1;
private IDictionary<int, int> _pinValueFileDescriptors;
......@@ -116,6 +119,7 @@ namespace System.Devices.Gpio
{
_pinCount = pinCount;
_exportedPins = new List<int>();
_exportedPWMChannels = new List<Tuple<int,int>>();
_pinsToDetectEvents = new List<int>();
_debounceTimeouts = new Dictionary<int, TimeSpan>();
_lastEvents = new Dictionary<int, DateTime>();
......@@ -144,6 +148,11 @@ namespace System.Devices.Gpio
int gpioPinNumber = _exportedPins[0];
UnexportPin(gpioPinNumber);
}
while (_exportedPWMChannels.Count > 0)
{
UnexportPWMPin(_exportedPWMChannels[0].Item1, _exportedPWMChannels[1].Item2);
}
}
protected internal override int PinCount
......@@ -184,6 +193,12 @@ namespace System.Devices.Gpio
ExportPin(gpioPinNumber);
}
protected internal override void OpenPWMPin(int chip, int channel)
{
ValidatePWMChannel(chip, channel);
ExportPWMChannel(chip, channel);
}
protected internal override void ClosePin(int gpioPinNumber)
{
ValidatePinNumber(gpioPinNumber);
......@@ -194,6 +209,12 @@ namespace System.Devices.Gpio
UnexportPin(gpioPinNumber);
}
protected internal override void ClosePWMPin(int chip, int channel)
{
ValidatePWMChannel(chip, channel);
UnexportPWMPin(chip, channel);
}
protected internal override PinMode GetPinMode(int gpioPinNumber)
{
ValidatePinNumber(gpioPinNumber);
......@@ -234,6 +255,28 @@ namespace System.Devices.Gpio
File.WriteAllText(valuePath, stringValue);
}
protected internal override void PWMWrite(int chip, int channel, PWMMode mode, int period, int dutyCycle)
{
if (mode == PWMMode.Serial) // Unix driver only supports balanced pwm mode.
{
throw new NotSupportedException("The selected PWMMode is not supported by this driver. Please select the Balanced mode.");
}
if (dutyCycle > period) // Duty cycle must always be equal to or less than period.
{
throw new ArgumentException("Duty cycle must be less than period.");
}
ValidatePWMChannel(chip, channel); // Ensure that the chip is valid and that it supports the channel
string periodPath = Path.Combine(PWMPath, $"pwmchip{chip}", $"pwm{channel}", "period");
File.WriteAllText(periodPath, Convert.ToString(period)); // Set the period. Must happen first in case duty_cycle is higher than current period.
string dutyCyclePath = Path.Combine(PWMPath, $"pwmchip{chip}", $"pwm{channel}", "duty_cycle");
File.WriteAllText(dutyCyclePath, Convert.ToString(dutyCycle)); // Set the duty cycle.
string enablePath = Path.Combine(PWMPath, $"pwmchip{chip}", $"pwm{channel}", "enable");
File.WriteAllText(enablePath, "1"); // Enable PWM.
}
protected internal override void SetDebounce(int gpioPinNumber, TimeSpan timeout)
{
ValidatePinNumber(gpioPinNumber);
......@@ -523,6 +566,34 @@ namespace System.Devices.Gpio
#region Private Methods
private void ValidatePWMChip(int chipNumber)
{
string chipPath = Path.Combine(PWMPath, $"pwmchip{chipNumber}");
if (!Directory.Exists(chipPath))
{
throw new ArgumentException($"The chip number {chipNumber} is invalid or is not enabled.");
}
}
private void ValidatePWMChannel(int chipNumber, int channel)
{
ValidatePWMChip(chipNumber);
string chipPath = Path.Combine(PWMPath, $"pwmchip{chipNumber}");
string supportedChannels = File.ReadAllText(Path.Combine(chipPath, "npwm"));
int numSupportedChannels;
if (int.TryParse(supportedChannels, out numSupportedChannels))
{
if (chipNumber < 0 || chipNumber >= numSupportedChannels)
{
throw new ArgumentException($"The pwm chip {chipNumber} does not support the channel {channel}");
}
}
else
{
throw new GpioException($"Unable to parse the number of supported channels at {Path.Combine(chipPath, "npwm")}");
}
}
private void ExportPin(int gpioPinNumber)
{
string pinPath = $"{GpioPath}/gpio{gpioPinNumber}";
......@@ -535,6 +606,18 @@ namespace System.Devices.Gpio
_exportedPins.Add(gpioPinNumber);
}
private void ExportPWMChannel(int chip, int channel)
{
string channelPath = Path.Combine(PWMPath, $"pwmchip{chip}", $"pwm{channel}");
if (!Directory.Exists(channelPath))
{
File.WriteAllText(Path.Combine(PWMPath, $"pwmchip{chip}", "export"), Convert.ToString(channel));
}
_exportedPWMChannels.Add(new Tuple<int,int>(chip, channel));
}
private void UnexportPin(int gpioPinNumber)
{
string pinPath = $"{GpioPath}/gpio{gpioPinNumber}";
......@@ -547,6 +630,17 @@ namespace System.Devices.Gpio
_exportedPins.Remove(gpioPinNumber);
}
private void UnexportPWMPin(int chip, int channel)
{
string channelPath = Path.Combine(PWMPath, $"pwmchip{chip}", $"pwm{channel}");
if (Directory.Exists(channelPath))
{
File.WriteAllText(Path.Combine(PWMPath, $"pwmchip{chip}", "unexport"), Convert.ToString(channel));
}
_exportedPWMChannels.Remove(new Tuple<int, int>(chip, channel));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ValidatePinMode(PinMode mode)
{
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Device.Gpio;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
......@@ -137,6 +138,11 @@ namespace System.Devices.Gpio
}
}
protected internal override void OpenPWMPin(int chip, int channel)
{
// Add validation and required code to open the pin
}
protected internal override void Output(int gpioPinNumber, PinValue value)
{
VerifyPinIsInValidRange(gpioPinNumber, nameof(gpioPinNumber));
......@@ -209,6 +215,16 @@ namespace System.Devices.Gpio
return result;
}
protected internal override void ClosePWMPin(int chip, int channel)
{
throw new NotImplementedException();
}
protected internal override void PWMWrite(int chip, int channel, PWMMode mode, int period, int dutyCycle)
{
throw new NotImplementedException();
}
#region Private Implementation
private int VerifyPinIsInValidRange(int gpioPinNumber, string argumentName)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册