Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug/pwm #550

Merged
merged 3 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
346 changes: 169 additions & 177 deletions source/Meadow.Core/Hardware/SoftPwmPort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,217 +2,209 @@
using System;
using System.Threading;

namespace Meadow.Hardware
namespace Meadow.Hardware;

/// <summary>
/// A Pulse Width Modulation Generator that can
/// generates waveforms in software. The maximum
/// Frequency is about 100 Hz.
///
/// Note: This class is not yet implemented.
/// </summary>
public class SoftPwmPort : IPwmPort

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.Duration'. 'SoftPwmPort.Duration' cannot implement 'IPwmPort.Duration' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.Period'. 'SoftPwmPort.Period' cannot implement 'IPwmPort.Period' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.DutyCycle'. 'SoftPwmPort.DutyCycle' cannot implement 'IPwmPort.DutyCycle' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.TimeScale'

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.Duration'. 'SoftPwmPort.Duration' cannot implement 'IPwmPort.Duration' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.Period'. 'SoftPwmPort.Period' cannot implement 'IPwmPort.Period' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.DutyCycle'. 'SoftPwmPort.DutyCycle' cannot implement 'IPwmPort.DutyCycle' because it does not have the matching return type of 'float'.

Check failure on line 14 in source/Meadow.Core/Hardware/SoftPwmPort.cs

View workflow job for this annotation

GitHub Actions / build

'SoftPwmPort' does not implement interface member 'IPwmPort.TimeScale'
{
/// <summary>
/// A Pulse Width Modulation Generator that can
/// generates waveforms in software. The maximum
/// Frequency is about 100 Hz.
///
/// Note: This class is not yet implemented.
/// Digital output port used for PWM
/// </summary>
public class SoftPwmPort : IPwmPort
protected IDigitalOutputPort Port { get; set; }

/// <summary>
/// PWM duration in ms
/// </summary>
public TimeSpan Duration
{
/// <summary>
/// Digital output port used for PWM
/// </summary>
protected IDigitalOutputPort Port { get; set; }

/// <summary>
/// PWM duration in ms
/// </summary>
public float Duration
{
get => Period * DutyCycle;
set { }
}
get => TimeSpan.FromSeconds(Period.TotalSeconds * DutyCycle);
set { }
}

/// <summary>
/// Period of PWM
/// </summary>
public float Period
{
get => 1 / (float)frequency.Hertz;
set => frequency = new Frequency(1 / value, Units.Frequency.UnitType.Hertz);
}
/// <summary>
/// Period of PWM
/// </summary>
public TimeSpan Period
{
get => TimeSpan.FromSeconds(1 / frequency.Hertz);
set => frequency = new Frequency(1 / value.TotalSeconds, Frequency.UnitType.Hertz);
}

/// <summary>
/// Is the PWM signal inverted
/// </summary>
public bool Inverted { get; set; }
/// <summary>
/// Is the PWM signal inverted
/// </summary>
public bool Inverted { get; set; }


/// <summary>
/// Duty cycle of PWM
/// </summary>
public float DutyCycle
/// <summary>
/// Duty cycle of PWM
/// </summary>
public double DutyCycle
{
get => dutyCycle;
set
{
get => dutyCycle;
set
{
dutyCycle = value;
onTimeMilliseconds = CalculateOnTimeMillis();
offTimeMilliseconds = CalculateOffTimeMillis();
}
dutyCycle = value;
onTimeMilliseconds = CalculateOnTimeMillis();
offTimeMilliseconds = CalculateOffTimeMillis();
}
float dutyCycle;
}

private double dutyCycle;

/// <summary>
/// Frequency of soft PWM
/// </summary>
public Frequency Frequency
/// <summary>
/// Frequency of soft PWM
/// </summary>
public Frequency Frequency
{
get => frequency;
set
{
get => frequency;
set
{
frequency = value;
onTimeMilliseconds = CalculateOnTimeMillis();
offTimeMilliseconds = CalculateOffTimeMillis();
}
frequency = value;
onTimeMilliseconds = CalculateOnTimeMillis();
offTimeMilliseconds = CalculateOffTimeMillis();
}
Frequency frequency = new Frequency(1.0, Units.Frequency.UnitType.Hertz); // in the case it doesn't get set before duty cycle, initialize to 1
}

/// <summary>
/// Channel info for PWM port
/// </summary>
public IPwmChannelInfo Channel { get; protected set; }
private Frequency frequency = new Frequency(1.0, Units.Frequency.UnitType.Hertz); // in the case it doesn't get set before duty cycle, initialize to 1

/// <summary>
/// State of PWM port (running / not running)
/// </summary>
public bool State => running;
/// <summary>
/// Channel info for PWM port
/// </summary>
public IPwmChannelInfo Channel { get; protected set; }

/// <summary>
/// Pin used for soft PWM
/// </summary>
public IPin Pin => Port.Pin;
/// <summary>
/// State of PWM port (running / not running)
/// </summary>
public bool State => running;

IDigitalChannelInfo IPort<IDigitalChannelInfo>.Channel => throw new NotImplementedException();
/// <summary>
/// Pin used for soft PWM
/// </summary>
public IPin Pin => Port.Pin;

/// <summary>
/// Timescale
/// </summary>
public TimeScale TimeScale
{
get => TimeScale.Seconds;
set { }
}
IDigitalChannelInfo IPort<IDigitalChannelInfo>.Channel => throw new NotImplementedException();

Thread? thread = null;
int onTimeMilliseconds = 0;
int offTimeMilliseconds = 0;
bool running = false;

/// <summary>
/// Instantiate a SoftPwm object that can perform PWM using digital pins
/// </summary>
/// <param name="outputPin"></param>
/// <param name="dutyCycle"></param>
/// <param name="frequency"></param>
public SoftPwmPort(IPin outputPin, float dutyCycle = 0.5f, float frequency = 1.0f) :
this(outputPin.CreateDigitalOutputPort(false), dutyCycle, frequency)
{
}
private Thread? thread = null;
private int onTimeMilliseconds = 0;
private int offTimeMilliseconds = 0;
private bool running = false;

/// <summary>
/// Instantiate a SoftPwm object that can perform PWM using digital pins
/// </summary>
/// <param name="outputPort"></param>
/// <param name="dutyCycle"></param>
/// <param name="frequencyInHertz"></param>
public SoftPwmPort(IDigitalOutputPort outputPort, float dutyCycle = 0.0f, float frequencyInHertz = 1000)
{
Port = outputPort;
DutyCycle = dutyCycle;
frequency = new Frequency(frequencyInHertz, Units.Frequency.UnitType.Hertz);
/// <summary>
/// Instantiate a SoftPwm object that can perform PWM using digital pins
/// </summary>
/// <param name="outputPin"></param>
/// <param name="dutyCycle"></param>
/// <param name="frequency"></param>
public SoftPwmPort(IPin outputPin, float dutyCycle = 0.5f, float frequency = 1.0f) :
this(outputPin.CreateDigitalOutputPort(false), dutyCycle, frequency)
{
}

Channel = new PwmChannelInfo("SoftPwmChannel", 0, 0, 1000, 1000, false, false);
}
/// <summary>
/// Instantiate a SoftPwm object that can perform PWM using digital pins
/// </summary>
/// <param name="outputPort"></param>
/// <param name="dutyCycle"></param>
/// <param name="frequencyInHertz"></param>
public SoftPwmPort(IDigitalOutputPort outputPort, float dutyCycle = 0.0f, float frequencyInHertz = 1000)
{
Port = outputPort;
DutyCycle = dutyCycle;
frequency = new Frequency(frequencyInHertz, Units.Frequency.UnitType.Hertz);

/// <summary>
/// Start the pulse width modulation
/// </summary>
public void Start()
{
running = true;
Channel = new PwmChannelInfo("SoftPwmChannel", 0, 0, 1000, 1000, false, false);
}

// create a new thread that actually writes the pwm to the output port
thread = new Thread(() =>
{
while (running)
{
Port.State = !Inverted;
Thread.Sleep(onTimeMilliseconds);
Port.State = Inverted;
Thread.Sleep(offTimeMilliseconds);
}
});
thread.Start();
}
/// <summary>
/// Start the pulse width modulation
/// </summary>
public void Start()
{
running = true;

/// <summary>
/// Stop the pulse width modulation
/// </summary>
public void Stop()
// create a new thread that actually writes the pwm to the output port
thread = new Thread(() =>
{
// setting this will wrap up the thread
running = false;
while (running)
{
Port.State = !Inverted;
Thread.Sleep(onTimeMilliseconds);
Port.State = Inverted;
Thread.Sleep(offTimeMilliseconds);
}
});
thread.Start();
}

// need to make sure the port is off, otherwise it can get
// stuck in an ON state.
Port.State = false;
}
/// <summary>
/// Stop the pulse width modulation
/// </summary>
public void Stop()
{
// setting this will wrap up the thread
running = false;

/// <summary>
/// Calculates the pulse on time in milliseconds
/// </summary>
protected int CalculateOnTimeMillis()
{
var dc = DutyCycle;
// clamp
if (dc < 0) dc = 0;
if (dc > 1) dc = 1;
// on time =
return (int)(dc / frequency.Kilohertz);
}
// need to make sure the port is off, otherwise it can get
// stuck in an ON state.
Port.State = false;
}

/// <summary>
/// Calculates the off time of pulse in milliseconds
/// </summary>
/// <returns></returns>
protected int CalculateOffTimeMillis()
{
var dc = DutyCycle;
// clamp
if (dc < 0) dc = 0;
if (dc > 1) dc = 1;
// off time =
return (int)((1 - dc) / frequency.Kilohertz);
}
/// <summary>
/// Calculates the pulse on time in milliseconds
/// </summary>
protected int CalculateOnTimeMillis()
{
var dc = DutyCycle;
// clamp
if (dc < 0) dc = 0;
if (dc > 1) dc = 1;
// on time =
return (int)(dc / frequency.Kilohertz);
}

/// <summary>
/// Calculates the off time of pulse in milliseconds
/// </summary>
/// <returns></returns>
protected int CalculateOffTimeMillis()
{
var dc = DutyCycle;
// clamp
if (dc < 0) dc = 0;
if (dc > 1) dc = 1;
// off time =
return (int)((1 - dc) / frequency.Kilohertz);
}

private bool disposedValue = false; // To detect redundant calls
private bool disposedValue = false; // To detect redundant calls

/// <summary>
/// Dispose of the object
/// </summary>
/// <param name="disposing">Is disposing</param>
protected virtual void Dispose(bool disposing)
/// <summary>
/// Dispose of the object
/// </summary>
/// <param name="disposing">Is disposing</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (!disposedValue)
if (disposing)
{
if (disposing)
{
}

disposedValue = true;
}
}

///<inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
disposedValue = true;
}
}

///<inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
}
Loading
Loading