Hi Guys,
I am having real trouble with this issue. Microsoft.SPOT.Hardware.PWM allows one to start PWM with:
PWM MyPWM = new PWM(Cpu.PWMChannel.PWM_2, Frequency, DucyCycle, false);
MyPWM.Start();
I want to be able to calculate the Prescaler and Period the same as does the PWM Class.
Can someone please help with information on how .NETMF calculates the Period and Prescaler.
I can calculate these values and at above 2KHz they seem to work, below 2KHz they are all wrong.
I have found this code in GHI’s Open Source and this does help:
private static unsafe bool PWM_ApplyConfiguration(PWM_CHANNEL channel, GPIO_PIN pin, uint period, uint duration, PWM_SCALE_FACTOR scale, bool invert)
{
int timer = g_STM32F4_PWM_Timer[channel];
int tchnl = g_STM32F4_PWM_Channel[channel];
TIM_TypeDef* treg = g_STM32F4_PWM_Ports[timer - 1];
uint p = period;
uint d = duration;
uint s = scale;
if (d > p) d = p;
// set pre, p, & d such that:
// pre * p = PWM_CLK * period / scale
// pre * d = PWM_CLK * duration / scale
uint clk = PWM1_CLK_HZ;
if ((uint)treg == 0x10000) clk = PWM2_CLK_HZ; // APB2
uint pre = clk / s; // prescaler
if (pre == 0)
{ // s > PWM_CLK
uint sm = s / ONE_MHZ; // scale in MHz
clk = PWM1_CLK_MHZ; // clock in MHz
if ((uint)treg == 0x10000) clk = PWM2_CLK_MHZ; // APB2
if (p > 0xFFFFFFFF / PWM_MAX_CLK_MHZ)
{ // avoid overflow
pre = clk;
p /= sm;
d /= sm;
}
else
{
pre = 1;
p = p * clk / sm;
d = d * clk / sm;
}
}
else
{
while (pre > 0x10000)
{ // prescaler too large
if (p >= 0x80000000) return false;
pre >>= 1;
p <<= 1;
d <<= 1;
}
}
if (timer != 2 && timer != 5)
{ // 16 bit timer
while (p >= 0x10000)
{ // period too large
if (pre > 0x8000) return false;
pre <<= 1;
p >>= 1;
d >>= 1;
}
}
treg->PSC = pre - 1;
treg->ARR = p - 1;
if (timer == 2)
{
if (tchnl == 0)
treg->CCR1 = d;
else if (tchnl == 1)
treg->CCR2 = d;
else if (tchnl == 2)
treg->CCR3 = d;
else if (tchnl == 3)
treg->CCR4 = d;
}
else
*(uint)&((uint)&treg->CCR1)[tchnl] = d;
uint invBit = TIM_CCER_CC1P << (4 * tchnl);
if (invert)
{
treg->CCER |= invBit;
}
else
{
treg->CCER &= ~invBit;
}
return true;
}
Microsoft have some nice code in Microsoft.SPOT.Hardware.PWM but nothing on how they calculate the Prescaler. The Period that is calculated in the methods in here is not the same Period that gets Commited to the ARR Register.
// Decompiled with JetBrains decompiler
// Type: Microsoft.SPOT.Hardware.PWM
// Assembly: Microsoft.SPOT.Hardware.PWM, Version=4.2.0.1, Culture=neutral, PublicKeyToken=null
// MVID: 82F07598-B86F-4A6D-B02A-633AEDCF7C8F
// Assembly location: C:\Program Files (x86)\Microsoft .NET Micro Framework\v4.2\Assemblies\le\Microsoft.SPOT.Hardware.PWM.dll
using System;
using System.Runtime.CompilerServices;
namespace Microsoft.SPOT.Hardware
{
public class PWM : IDisposable
{
private readonly Cpu.Pin m_pin;
private readonly Cpu.PWMChannel m_channel;
private uint m_period;
private uint m_duration;
private bool m_invert;
private PWM.ScaleFactor m_scale;
public Cpu.Pin Pin
{
get
{
return this.m_pin;
}
}
public double Frequency
{
get
{
return PWM.FrequencyFromPeriod((double) this.m_period, this.m_scale);
}
set
{
this.m_period = PWM.PeriodFromFrequency(value, out this.m_scale);
this.Commit();
}
}
public double DutyCycle
{
get
{
return PWM.DutyCycleFromDurationAndPeriod((double) this.m_period, (double) this.m_duration);
}
set
{
this.m_duration = PWM.DurationFromDutyCycleAndPeriod(value, (double) this.m_period);
this.Commit();
}
}
public uint Period
{
get
{
return this.m_period;
}
set
{
this.m_period = value;
this.Commit();
}
}
public uint Duration
{
get
{
return this.m_duration;
}
set
{
this.m_duration = value;
this.Commit();
}
}
public PWM.ScaleFactor Scale
{
get
{
return this.m_scale;
}
set
{
this.m_scale = value;
this.Commit();
}
}
public PWM(Cpu.PWMChannel channel, double frequency_Hz, double dutyCycle, bool invert)
{
HardwareProvider hwProvider = HardwareProvider.HwProvider;
if (hwProvider == null)
throw new InvalidOperationException();
this.m_pin = hwProvider.GetPwmPinForChannel(channel);
this.m_channel = channel;
this.m_period = PWM.PeriodFromFrequency(frequency_Hz, out this.m_scale);
this.m_duration = PWM.DurationFromDutyCycleAndPeriod(dutyCycle, (double) this.m_period);
this.m_invert = invert;
try
{
this.Init();
this.Commit();
Port.ReservePin(this.m_pin, true);
}
catch
{
this.Dispose(false);
}
}
public PWM(Cpu.PWMChannel channel, uint period, uint duration, PWM.ScaleFactor scale, bool invert)
{
HardwareProvider hwProvider = HardwareProvider.HwProvider;
if (hwProvider == null)
throw new InvalidOperationException();
this.m_pin = hwProvider.GetPwmPinForChannel(channel);
this.m_channel = channel;
this.m_period = period;
this.m_duration = duration;
this.m_scale = scale;
this.m_invert = invert;
try
{
this.Init();
this.Commit();
Port.ReservePin(this.m_pin, true);
}
catch
{
this.Dispose(false);
}
}
~PWM()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
}
[MethodImpl(MethodImplOptions.InternalCall)]
public void Start();
[MethodImpl(MethodImplOptions.InternalCall)]
public void Stop();
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void Start(PWM[] ports);
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void Stop(PWM[] ports);
protected void Dispose(bool disposing)
{
try
{
this.Stop();
}
catch
{
}
finally
{
this.Uninit();
Port.ReservePin(this.m_pin, false);
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
protected void Commit();
[MethodImpl(MethodImplOptions.InternalCall)]
protected void Init();
[MethodImpl(MethodImplOptions.InternalCall)]
protected void Uninit();
private static uint PeriodFromFrequency(double f, out PWM.ScaleFactor scale)
{
if (f >= 1000.0)
{
scale = PWM.ScaleFactor.Nanoseconds;
return (uint) (1000000000.0 / f + 0.5);
}
else if (f >= 1.0)
{
scale = PWM.ScaleFactor.Microseconds;
return (uint) (1000000.0 / f + 0.5);
}
else
{
scale = PWM.ScaleFactor.Milliseconds;
return (uint) (1000.0 / f + 0.5);
}
}
private static uint DurationFromDutyCycleAndPeriod(double dutyCycle, double period)
{
if (period <= 0.0)
throw new ArgumentException();
if (dutyCycle < 0.0)
return 0U;
if (dutyCycle > 1.0)
return 1U;
else
return (uint) (dutyCycle * period);
}
private static double FrequencyFromPeriod(double period, PWM.ScaleFactor scale)
{
return (double) scale / period;
}
private static double DutyCycleFromDurationAndPeriod(double period, double duration)
{
if (period <= 0.0)
throw new ArgumentException();
if (duration < 0.0)
return 0.0;
if (duration > period)
return 1.0;
else
return duration / period;
}
public enum ScaleFactor : uint
{
Milliseconds = 1000U,
Microseconds = 1000000U,
Nanoseconds = 1000000000U,
}
}
}
I have added some examples in the attached picture. Any help on this would be much appreciated.
All the Best
Chris