2014-10-04 07:02 PM
This is a subject that seems to have a lot of confusion (I do not fully understand). How to calculate the Variables:
if only a few other variables are known. At higher frequencies, for example 1000000Hz simple rules work. At lower Frequencies like 1281Hz things get a little more complicated.
As an example: if we aim for an output frequency of 1281Hz, we need to set the ARR Register to: 32785. We need to set the PSC Register to: 3 and if we want a 50% DutyCycle, we need to set the CCRx Register to: 16393. Where the x in CCRx is the specified Channel one is going to use. 1, 2, 3 or 4. The Registers need to see values of 16 BITs, Integer Values(65535 or less). Equations exist so calculations can be made, for example: TIM_Update_Frequency(Hz) = Clock / (PSC - 1) * (Period - 1) 1281.11273792094 = 84000000 / ((3) * (32785)) now, the problem becomes more complicated, for a desired frequency of 840Hz: 425.946209078739 = 84000000 / ((167) * (1189)) Here in lies the problem, how does one find the value of PSC when one knows only the values for ''Clock'' and ''Period''? The value ''TIM_Update_Frequency(Hz)'' is not always the actual Desired Frequency. Other equations must exist so as to calculate the ''PSC'' Value, or to calculate the ''TIM_Update_Frequency(Hz)'' so as to deduce the other values. Guess work is just not a logical approach here. Can anyone help with more equations for solving for the other variables? Thank you Chris #factoring!2014-10-06 02:21 AM
using
System;
using
System.Linq;
using
System.Text;
using
System.Collections.Generic;
namespace
ConsoleApplication1
{
class
Program
{
#region Fields...
// For Debugging Purposes...
private
static
bool
debug =
true
;
private
static
uint
m_period;
private
static
ScaleFactor m_scale;
private
Clock m_busclock;
private
static
uint
m_prescaler;
private
static
uint
m_duration;
private
static
bool
m_invert;
#endregion
#region Properties...
public
Clock ClockSpeed
{
get
{
return
m_busclock; }
set
{ m_busclock = value; }
}
public
double
Frequency
{
get
{
return
FrequencyFromPeriod((
double
)m_period, m_scale);
}
set
{
m_period = PeriodFromFrequency(value,
out
m_scale);
//this.Commit();
}
}
public
uint
Prescaler
{
get
{
return
m_prescaler; }
set
{ m_prescaler = value; }
}
public
uint
Period
{
get
{
return
m_period;
}
set
{
m_period = value;
//this.Commit();
}
}
public
double
DutyCycle
{
get
{
return
DutyCycleFromDurationAndPeriod((
double
)m_period, (
double
)m_duration);
}
set
{
m_duration = DurationFromDutyCycleAndPeriod(value, (
double
)m_period);
//this.Commit();
}
}
public
uint
Duration
{
get
{
return
m_duration;
}
set
{
m_duration = value;
//this.Commit();
}
}
public
bool
InvertPin
{
get
{
return
m_invert; }
set
{ m_invert = value; }
}
public
ScaleFactor Scale
{
get
{
return
m_scale;
}
set
{
m_scale = value;
//this.Commit();
}
}
#endregion
public
enum
ScaleFactor :
uint
{
Microseconds = 0xf4240,
Milliseconds = 0x3e8,
Nanoseconds = 0x3b9aca00
}
public
enum
Clock :
uint
{
// [DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32%20Series%20-%20Calculations%20of%20Timer%20Variables&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&TopicsView=https%3a//my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/AllItems.aspx¤tviews=22]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex%5fmx%5fstm32%2fSTM32%20Series%20%2d%20Calculations%20of%20Timer%20Variables&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&TopicsView=https%3A%2F%2Fmy%2Est%2Ecom%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex%5Fmx%5Fstm32%2FAllItems%2Easpx¤tviews=22
// See: Post: 10/5/2014 2:43 PM
// On the F4 running at 168 MHz, APB1 is typically 42 MHz (DIV4) and APB2 84 MHz (DIV2),
// and the respective TIMCLKs are 84 MHz, and 168 MHz...
APB1 = 84000000,
APB2 = 168000000
}
static
void
Main(
string
[] args)
{
double
Frequency = 1840;
double
DucyCycle = 0.5;
CalculateValues(Frequency, DucyCycle);
Console.ReadLine();
}
private
static
void
CalculateValues(
double
frequency_Hz,
double
dutyCycle)
{
m_period = PeriodFromFrequency(frequency_Hz,
out
m_scale);
m_duration = DurationFromDutyCycleAndPeriod(dutyCycle, (
double
)m_period);
#region Fields...
uint
period = m_period;
uint
duration = m_duration;
double
mperiod = period;
double
mduration = duration;
uint
scale = ((
uint
)m_scale);
uint
clk;
uint
prescaler;
#endregion
#region System Vars...
int
timer = 8;
uint
treg = 0x20000;
uint
PWM1_CLK_HZ;
uint
PWM2_CLK_HZ;
uint
ONE_MHZ = 1000000;
uint
PWM1_CLK_MHZ = 84;
uint
PWM2_CLK_MHZ = 42;
uint
PWM_MAX_CLK_MHZ;
uint
SYSTEM_CYCLE_CLOCK_HZ = 168000000;
// 168MHz
uint
SYSTEM_APB1_CLOCK_HZ = 42000000;
// 42MHz
uint
SYSTEM_APB2_CLOCK_HZ = 84000000;
// 84MHz
if
(SYSTEM_APB1_CLOCK_HZ == SYSTEM_CYCLE_CLOCK_HZ)
{
PWM1_CLK_HZ = (SYSTEM_APB1_CLOCK_HZ);
}
else
{
PWM1_CLK_HZ = (SYSTEM_APB1_CLOCK_HZ * 2);
PWM1_CLK_MHZ = (PWM1_CLK_HZ / ONE_MHZ);
}
if
(SYSTEM_APB2_CLOCK_HZ == SYSTEM_CYCLE_CLOCK_HZ)
{
PWM2_CLK_HZ = (SYSTEM_APB2_CLOCK_HZ);
}
else
{
PWM2_CLK_HZ = (SYSTEM_APB2_CLOCK_HZ * 2);
PWM2_CLK_MHZ = (PWM2_CLK_HZ / ONE_MHZ);
}
if
(PWM2_CLK_MHZ > PWM1_CLK_MHZ)
{
PWM_MAX_CLK_MHZ = PWM2_CLK_MHZ;
}
else
{
PWM_MAX_CLK_MHZ = PWM1_CLK_MHZ;
}
if
(duration > period) duration = period;
// APB2
if
((
uint
)treg == 0x10000)
{
clk = PWM2_CLK_HZ;
}
else
{
clk = PWM1_CLK_HZ;
}
#endregion
#region Assign Variables...
prescaler = clk / scale;
// scale in MHz
uint
sm = scale / ONE_MHZ;
if
((
uint
)treg == 0x10000)
{
clk = PWM2_CLK_MHZ;
}
else
{
clk = PWM1_CLK_MHZ;
}
#endregion
// set pre, p, & d such that:
// pre * p = PWM_CLK * period / scale
// pre * d = PWM_CLK * duration / scale
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Freq: ''
+ frequency_Hz);
Console.WriteLine(
''Pre: ''
+ prescaler);
Console.WriteLine(
''Period: ''
+ mperiod);
Console.WriteLine(
''Clk: ''
+ clk);
Console.WriteLine(
''Scale: ''
+ scale);
if
(prescaler == 0)
{
if
(period > 0xFFFFFFFF / PWM_MAX_CLK_MHZ)
{
// avoid overflow
prescaler = clk;
period /= sm;
duration /= sm;
}
else
{
Console.WriteLine(
''Setting Values from here...''
);
prescaler = 1;
period = period * clk / sm;
duration = duration * clk / sm;
}
}
else
{
while
(prescaler > 0x10000)
{
// prescaler too large
// if (period >= 0x80000000) return false;
prescaler >>= 1;
period <<= 1;
duration <<= 1;
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Calculated PSC: ''
+ (((168000000 / ((period) / (1 / frequency_Hz))) + 0.5) - 1));
// *** This is what I need to know, this calculation...
}
if
(timer != 2 && timer != 5)
{
// 16 bit timer
while
(period >= 0x10000)
{
// period too large
// if (prescaler > 0x8000) return false;
prescaler <<= 1;
period >>= 1;
duration >>= 1;
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Calculated PSC**: ''
+ (((168000000 / ((period) / (1 / frequency_Hz))) + 0.5) - 1));
// *** This is what I need to know, this calculation...
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''PSC: ''
+ (prescaler) +
'' this value is only sometimes correct and the calculated Prescaler is best used.''
);
Console.WriteLine(
''ARR: ''
+ (period - 1));
Console.WriteLine(
''CCRx: ''
+ duration);
Console.WriteLine(
''*************************************************''
);
}
private
static
uint
PeriodFromFrequency(
double
f,
out
ScaleFactor scale)
{
if
(f >= 1000.0)
{
scale = ScaleFactor.Nanoseconds;
return
(
uint
)((1000000000.0 / f) + 0.5);
}
if
(f >= 1.0)
{
scale = ScaleFactor.Microseconds;
return
(
uint
)((1000000.0 / f) + 0.5);
}
scale = ScaleFactor.Milliseconds;
return
(
uint
)((1000.0 / f) + 0.5);
}
private
uint
PrescalerFromFrequencyAndClock(
double
f)
{
return
(
uint
)(((
uint
)(
this
.ClockSpeed)) / (((
this
.Period - 1) / (1 / f)) + 1));
}
private
static
double
FrequencyFromPeriod(
double
period, ScaleFactor scale)
{
return
(((
double
)scale) / period);
}
private
static
uint
DurationFromDutyCycleAndPeriod(
double
dutyCycle,
double
period)
{
if
(period <= 0.0)
{
throw
new
ArgumentException();
}
if
(dutyCycle < 0.0)
{
return
0;
}
if
(dutyCycle > 1.0)
{
return
1;
}
return
(
uint
)(dutyCycle * 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;
}
return
(duration / period);
}
}
}
Needs a good clean up and a real fine tune but at the moment this rough cut works. This is a Snippett from the Microsoft PWM Class and the GHI Source Code.
All the best
Chris
2014-10-06 12:03 PM
@waclawek.jan - Thank you for the Document number. I have read many documents on the STM32F4 but have missed this particular one. I don't know how.
Thanks All the best Chris URL:http://www.st.com/web/en/resource/technical/document/application_note/DM00042534.pdf
2014-10-06 12:16 PM
Some what of a tidy up:
using
System;
using
System.Text;
using
System.Collections.Generic;
namespace
ConsoleApplication3
{
class
Program
{
#region Fields...
// For Debugging Purposes...
private
static
bool
debug =
true
;
private
static
uint
m_period;
private
static
ScaleFactor m_scale;
private
static
Clock m_busclock;
private
static
uint
m_prescaler;
private
static
uint
m_duration;
private
static
bool
m_invert;
#endregion
#region Properties...
public
static
Clock ClockSpeed
{
get
{
return
m_busclock; }
set
{ m_busclock = value; }
}
public
double
Frequency
{
get
{
return
FrequencyFromPeriod((
double
)m_period, m_scale);
}
set
{
m_period = PeriodFromFrequency(value,
out
m_scale);
}
}
public
uint
Prescaler
{
get
{
return
m_prescaler; }
set
{ m_prescaler = value; }
}
public
uint
Period
{
get
{
return
m_period;
}
set
{
m_period = value;
}
}
public
double
DutyCycle
{
get
{
return
DutyCycleFromDurationAndPeriod((
double
)m_period, (
double
)m_duration);
}
set
{
m_duration = DurationFromDutyCycleAndPeriod(value, (
double
)m_period);
}
}
public
uint
Duration
{
get
{
return
m_duration;
}
set
{
m_duration = value;
}
}
public
bool
InvertPin
{
get
{
return
m_invert; }
set
{ m_invert = value; }
}
public
ScaleFactor Scale
{
get
{
return
m_scale;
}
set
{
m_scale = value;
//this.Commit();
}
}
#endregion
public
enum
ScaleFactor :
uint
{
Microseconds = 0xf4240,
Milliseconds = 0x3e8,
Nanoseconds = 0x3b9aca00
}
public
enum
Clock :
uint
{
APB1 = 84000000,
APB2 = 168000000,
}
static
void
Main(
string
[] args)
{
double
Frequency = 1840;
double
DucyCycle = 0.5;
CalculateValues(Frequency, DucyCycle);
Console.ReadLine();
}
private
static
void
CalculateValues(
double
frequency_Hz,
double
dutyCycle)
{
m_period = PeriodFromFrequency(frequency_Hz,
out
m_scale);
m_duration = DurationFromDutyCycleAndPeriod(dutyCycle, (
double
)m_period);
#region Fields...
ClockSpeed = Clock.APB2DIV0;
uint
clk = (
uint
)ClockSpeed;
uint
prescaler;
uint
period = m_period;
uint
duration = m_duration;
uint
scale = ((
uint
)m_scale);
int
timer = 8;
uint
treg = 0x20000;
uint
ONE_MHZ = 1000000;
uint
PWM1_CLK_HZ = (
uint
)Clock.APB2DIV0;
uint
PWM2_CLK_HZ = (
uint
)Clock.APB1DIV0;
uint
PWM1_CLK_MHZ = PWM1_CLK_HZ / ONE_MHZ;
uint
PWM2_CLK_MHZ = PWM2_CLK_HZ / ONE_MHZ;
uint
PWM_MAX_CLK_MHZ;
if
(PWM2_CLK_MHZ > PWM1_CLK_MHZ)
{
PWM_MAX_CLK_MHZ = PWM2_CLK_MHZ;
}
else
{
PWM_MAX_CLK_MHZ = PWM1_CLK_MHZ;
}
// Scale in MHz...
uint
scaleMHz = scale / ONE_MHZ;
if
(duration > period) duration = period;
if
((
uint
)treg == 0x10000)
{
clk = PWM2_CLK_MHZ;
}
else
{
clk = PWM1_CLK_MHZ;
}
prescaler = clk / scale;
#endregion
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Freq: ''
+ frequency_Hz);
Console.WriteLine(
''PSC: ''
+ prescaler);
Console.WriteLine(
''Period: ''
+ period);
Console.WriteLine(
''Clk: ''
+ clk);
Console.WriteLine(
''Scale: ''
+ scale);
if
(prescaler == 0)
{
if
(period > 0xFFFFFFFF / PWM_MAX_CLK_MHZ)
{
// Avoid Overflow...
prescaler = clk;
period /= scaleMHz;
duration /= scaleMHz;
}
else
{
prescaler = 1;
period = period * clk / scaleMHz;
duration = duration * clk / scaleMHz;
}
}
else
{
while
(prescaler > 0x10000)
{
// Prescaler too Large...
if
(period >= 0x80000000)
break
;
prescaler >>= 1;
period <<= 1;
duration <<= 1;
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Calculated PSC: ''
+ PrescalerFromFrequencyAndClock(period, frequency_Hz));
}
if
(timer != 2 && timer != 5)
{
// 16 bit timer
while
(period >= 0x10000)
{
// period too large
if
(prescaler > 0x8000)
break
;
prescaler <<= 1;
period >>= 1;
duration >>= 1;
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''Calculated PSC: ''
+ PrescalerFromFrequencyAndClock(period, frequency_Hz));
}
Console.WriteLine(
''*************************************************''
);
Console.WriteLine(
''PSC: ''
+ (prescaler) +
'' - sometimes incorrect, calculated Prescaler is best used.''
);
Console.WriteLine(
''ARR: ''
+ (period - 1));
Console.WriteLine(
''CCRx: ''
+ duration);
Console.WriteLine(
''*************************************************''
);
}
private
static
uint
PeriodFromFrequency(
double
f,
out
ScaleFactor scale)
{
if
(f >= 1000.0)
{
scale = ScaleFactor.Nanoseconds;
return
(
uint
)((1000000000.0 / f) + 0.5);
}
if
(f >= 1.0)
{
scale = ScaleFactor.Microseconds;
return
(
uint
)((1000000.0 / f) + 0.5);
}
scale = ScaleFactor.Milliseconds;
return
(
uint
)((1000.0 / f) + 0.5);
}
private
static
uint
PrescalerFromFrequencyAndClock(
double
period,
double
frequency)
{
return
(
uint
)((((
uint
)ClockSpeed / ((period) / (1 / frequency))) + 0.5) - 1);
}
private
static
double
FrequencyFromPeriod(
double
period, ScaleFactor scale)
{
return
(((
double
)scale) / period);
}
private
static
uint
DurationFromDutyCycleAndPeriod(
double
dutyCycle,
double
period)
{
if
(period <= 0.0)
{
throw
new
ArgumentException();
}
if
(dutyCycle < 0.0)
{
return
0;
}
if
(dutyCycle > 1.0)
{
return
1;
}
return
(
uint
)(dutyCycle * 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;
}
return
(duration / period);
}
}
}
This code is generating the correct Values for Period, Duration and most importantly the Prescaler which is the value I was having the most problems with.
This code is modified from the Microsoft PWM and GHI electronics Open Source to suit my needs and hopefully help others out.
All the Best
Chris
2014-10-06 02:36 PM
RCR = 16-bit repetition counter (where present)
8-bit, on TIM1 and TIM8 on STM32F4 per RM0090