STM32 System and Timer Clock Configurations

STM32 System and Timer Clock Configurations

I‘ve started writing some software to drive a series of TLC5940 16 Channel LED Driver chips on an ST STM32F407 Microcontroller.

I previously had code working on an Atmel microcontroller, but obviously with the change of hardware comes

the need for re-writing some of the lower peripheral configuration and application code.

The two main requirements for driving the TLC5940 chip from a microcontroller are:

  1. The grayscale clock
  2. A serial data bus

The microcontroller‘s SPI port is well suited to delivering data to the TLC5940 even though this is not officially an ‘SPI‘ device.

For the grayscale clock I have decided to use the STM32‘s timer modules to automatically provide the BLANK and GSCLK signals.

Basically, the blank signal is used to reset the grayscale counter inside the TLC5940,

and depending on the desired LED brightness resolution, we wait a certain number of GSCLK cycles

before generating a BLANK pulse to reset the TLC5940‘s internal counter.

Both these signals have timing requirements placed on them, so we need to understand exactly how the STM32‘s timer modules function.

And before we can do this, we also need to understand the clock source that drives the timers (in my case, the internal system clock).

My board uses an external 8MHz crystal oscillator as its clock source, and although I believed I had it configured correctly,

bit without measuring it, I couldn‘t be sure. Well... I could have flashed an LED at a human-measurable speed, like 1Hz,

but that‘s not as fun as delving right into the core and working out to the timer modules. :p

The maximum frequency specified by ST for this device is 168MHz, so this is the frequency I will configure the device for.

To begin with, the 8MHz clock is the ‘High Speed External‘ clock signal, or HSE as it is known,

enters the microcontroller through its OSC_IN/OSC_OUT pins, before being sent to the ‘Phase Lock Loop‘ (PLL) module

to be divided and multiplied by several pre-scalers to provide the ‘System Clock‘ (SYSCLK),

the ‘Advanced High-performance Bus‘ (AHB) clock, and the two ‘Advanced Peripheral Bus‘ clocks (APB1 and APB2).

There are several other clocks configured by the ‘Reset and Clock Control‘ (RCC) peripheral,

but these aren‘t directly related to the Timer modules I am using.

The C source files in the STM32F4xxStdPeriph Library where the system clock is configured include:

  • system_stm32f4xx.c - the PLL register value #DEFINEs and initialisation code is found here
  • stm32f4xx.h - the HSE frequency is defined at the start

The SystemCoreClock variable in the first of the two files above is calculated using the following two equations found in the

RM0090 Reference Manual:

  • f(VCO clock) = f(PLL clock input) × (PLLN / PLLM)
  • f(PLL general clock output) = f(VCO clock) / PLLP

These equations describe what is happening in the hardware.

First, the HSE clock signal is divided by the PLLM division factor.

The result from this must be between 1 - 2MHz, but a value of 2MHz is recommended to limit PLL jitter.

This pre-scaled signal then enters the Main PLL module where it is multiplied by PLLN, the VCO multiplication factor,

to provide a signal between 64 to 432MHz, and then divided back down by the PLLP factor

to produce the SystemCoreClock with a frequency no greater than 168MHz.

My initial setup used the following configuration:

stm32f4xx.h:

  • #define HSE_VALUE    ((uint32_t)8000000)

system_stm32f4xx.c

  • #define PLL_M      8
  • #define PLL_N      336
  • #define PLL_P      2

However after noticing the mention of increased clock jitter, I decided to change PLL_M to ‘4‘, PLL_N to ‘168‘.

All the PLL parameters above are placed into the ‘RCC PLL Configuration Register‘, RCC_PLLCFGR.

Now, with the settings for the core clock determined and initialised into code, I needed a way to view these signals,

and luckily for me the nice people at ST have provided hardware that can select from various internal clock signals, pre-scale them,

and present them for viewing on two external pins, MCO1 and MCO2 which are the ‘Microcontroller Clock Output‘ pins.

These are configured in the ‘RCC Clock Configuration Register‘ RCC_CFGR using the MCOx bits for selecting the desired signal,

and the MCOxPRE bits to scale the signal by a factor of between 1 and 5.

Using the peripheral library commands below, I‘ve selected the HSE for MCO1, and SYSCLK divided by 5 for MCO2.

This should provide signals of 8MHz and 33.6MHz if my configuration is correct.

  • RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
  • RCC_MCO2Config(RCC_MCO2Source_SYSCLK, RCC_MCO2Div_5);

And viewing these signals in the images below we can see that this is roughly the case.

The 0.8% error in the SYSCLK (blue Ch2) signal is either due to the actual signal being slightly out due to the PLL scaling system,

or possibly the limitations of my cheap 100MHz ‘scope (I will test this tomorrow on some old (but high-end) Le Croy and Tektronix 500MHz ‘scopes at work tomorrow.

Next I‘m moving on to confirming my GSCLK and BLANK timer configuration is as I need it to be.

UPDATE:

I‘ve taken some measurements with the scopes at work and they appear to agree with the 8MHz result,

but definitely show the 33.6MHz SYSCLK (within the margin of error):

Now that I‘ve verified my system‘s core clock is functioning as expected,

I can move onto configuring the clock modules to produce the desired BLANK and GSCLK signals to control the TLC5940 chips.

Using the SYSCLK signal, the ‘Advanced High-performance Bus‘ (AHB) clock frequency is set via a prescaler.

In this case I am using the maximum frequency of the AHB by setting the prescaler to divide by 1.

The two ‘Advanced Peripheral Bus‘ clocks (APB1 and APB2) are generated from the AHB via their associated prescalers.

They have maximum frequencies of 42 and 84MHz respectively,

so my APB1 prescaler is set to ‘4‘ and APB2 prescaler is set to ‘2‘.

The internal signal that can be selected as the timer peripherals‘ clock is the APB1 clock.

HOWEVER...

and this is an important part to take note of:

While the APB1 signal provides the clock for numerous peripherals including the timer modules,

the timers can receive a faster clock if the APB1 prescaler is set to anything other than ‘1‘. The manual states:

  1. If the APB prescaler is 1, the timer clock frequencies are set to the same frequency as that of the APB domain to which the timers are connected.
  2. Otherwise, they are set to twice (×2) the frequency of the APB domain to which the timers are connected.

Since the APB1 prescaler I am using is ‘4‘ and the AHB clock is 168MHz, the internal clock presented to the timer modules is actually (168/4) * 2 = 84MHz.

Inside the TIMx modules there are three main clock signals:

  • CK_INT - The internal clock before prescaling
  • CK_PSC - The clock signal after being divided by the prescaler
  • CK_CNT - The counter clock, which generates pulses every time the prescaler counter overflows

Note that while there are different clock signals (internal clock, external clock, internal triggers, etc)

that can be used as the main input to each timer module, since I don‘t need to synchronise to external signals,

I will use the default internal clock, APB1.

The internal clock signal APB1 is fed into my main timer module TIM3.

This module will be used to provide both the GSCLK signal on an external GPIO pin,

and also to trigger/clock the slave TIM4 module which will provide the BLANK pulses.

Initially I intend to have the TIM3 setup for its fastest possible rate,

and then configure the output for the desired GSCLK frequency once both signals are synchronised correctly.

I am using a prescaler divide factor of 1 (TIM3_PSC = 0), so CK_PSC = CK_INT = APB1 * 2 = 84MHz.

Each TIM module from TIM2 to TIM5 (and some others) can be used as a ‘master‘ or a ‘slave‘ to generate signals or react to them in various ways.

For instance, I can use TIM3 as a master that generates a trigger pulse every time the counter reaches a specific value.

I could then use that trigger pulse as the main clock signal in another timer module.

The way I intend to use TIM3 is to use its ‘Update Event‘ as its external trigger signal (TRGO)

which generates a single pulse every time its counter ‘overflows‘, or reaches the upper limit which is set in the ‘Auto Reload Register‘ (ARR).

The TIM3 counter is configured in ‘Upcounter Mode‘ which means

that every rising edge of the counter clock causes the counter to increment until it reaches the ARR value.

When it reaches this upper limit, it generates an ‘Update Event‘, UEV, which causes the counter to reset to ‘0‘.

Each UEV pulses the trigger signal, so if ARR is set to ‘1‘, the counter moves from 0 to 1, generates a trigger pulse,

resets the counter to 0, and the process repeats forever.

This two-count overflow acts as a divide-by-two on the CK_CNT signal.

Therefore, our TIM3 master trigger signal is 84MHz / 2 = 42MHz.

This trigger is passed into TIM4 on the Internal Trigger network, which, when these two specific timers are used, is the Internal Trigger 2 signal (ITR2).

Returning to the original task of providing grayscale clock and blanking pulse signals for the TLC5940,

the two timer modules now need to be configured to generate these signals on their Output Compare pins.

For the GSCLK signal, the TIM3 Output Compare Channel 1 (OC1) signal is configured to toggle its output

every time the counter matches the desired value; in this case ‘1‘.

This essentially means that it is synchronised with the TRG0 signal at half the frequency.

For the BLANK signal, we need to be able to synchronise the signals with a phase shift so that the desired number of GSCLK pulses are generated

before a blanking pulse is generated.

To do this, the TIM4 counter is configured in ‘Centre Aligned‘ mode,
so that it counts up to the ARR value and then counts down to zero and repeats.

The TIM4 OC1 signal is configured as a PWM signal that is set when the counter is less than OC1‘s  Capture/Compare Register (CCR1) value, where:

  • CCR1 = GSCLK_COUNT + 1

Due to internal timing, the TIM3 OC1 signal is given a slight phase shift relative to the TIM4 OC1,

and the result is that the number of rising edges between consecutive blanking pulses is the desired number (GSCLK_COUNT).

I‘m still not exactly sure why, so I‘ll need to investigate the origin of this phase shift, but for now I am happy that it‘s there.

The timing for each timer module, and the resulting signals seen by the TLC5940 chip are shown below in Fig.1.

Here GSCLK_COUNT is set to ‘2‘.

Note that for TIM4, the CK_INT is TIM3‘s TRG0.

Figure 1: TIM3, TIM4, and the TLC5940 Signal Configuration

I‘ve experimented with changing the TIM3 prescaler to adjust the GSCLK frequency, different numbers of GSCLK_PULSES.

Below Fig. 2 and Fig. 3 show scope captures of two different configurations:

Figure 2: GSCLK = 21MHz, GSCLK_COUNT = 2

Figure 3: GSCLK = 82kHz, GSCLK_COUNT = 4

Now that the timing of the TLC5940 GSCLK and BLANK signals have been configured

and are easily adjustable for the desired frequency and intensity resolution,

it‘s time to move onto the TLC5940‘s serial data interface.

时间: 2024-10-12 11:52:18

STM32 System and Timer Clock Configurations的相关文章

System.Windows.Forms.Timer、System.Timers.Timer、System.Threading.Timer的差别和分别什么时候用

一.System.Windows.Forms.Timer 1.基于Windows消息循环,用事件方式触发,在界面线程执行:是使用得比较多的Timer,Timer Start之后定时(按设定的Interval)调用挂接在Tick事件上的EvnetHandler.在这种Timer的EventHandler中可 以直接获取和修改UI元素而不会出现问题--因为这种Timer实际上就是在UI线程自身上进行调用的. 2.它是一个基于Form的计时器3.创建之后,你可以使用Interval设置Tick之间的跨

System.Threading.Timer

GLog.WLog("_thdTimer before"); _thdTimer = new System.Threading.Timer(new TimerCallback(TimerProc)); //2秒后开始执行计时器:每3秒执行一次 _thdTimer.Change(2000,3000); GLog.WLog("_thdTimer after"); private void TimerProc(object state) { try { GLog.WLog

例说定时器System.Threading.Timer

本文通过实例演示System.Threading.Timer的使用方法. 下面是完整的实例代码. using System; using System.Windows; namespace ThreadingTimerExp { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { System.Threading.Timer timer;

使用System.Timers.Timer类实现程序定时执行

使用System.Timers.Timer类实现程序定时执行 在C#里关于定时器类有3个:System.Windows.Forms.Timer类.System.Threading.Timer类和System.Timers.Timer类. System.Windows.Forms.Timer是应用于WinForm中的,它是通过Windows消息机制实现的,类似于VB或Delphi中的Timer控件,内部使用API  SetTimer实现的.它的主要缺点是计时不精确,而且必须有消息循环,Consol

System.Threading.Timer 用法

System.Threading.Timer用法和例子 (1)首先声明Timer变量://一定要声明成局部变量以保持对Timer的引用,否则会被垃圾收集器回收!private System.Threading.Timer timerClose; (2)在上述自动执行代码后面添加如下Timer实例化代码:// Create a timer thread and start ittimerClose = new System.Threading.Timer(new TimerCallback(tim

例说多线程定时器System.Timers.Timer

System.Timers.Timer是多线程定时器,如果一个Timer没有处理完成,到达下一个时间点,新的Timer同样会被启动,所以在使用Timer时需要注意. 下面的实例显示了Timer的使用方法. using System; using System.Threading; using System.Windows; namespace TimerExp { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> publ

System.Timers.Timer用法

System.Timers.Timer t = new System.Timers.Timer(5000); //设置时间间隔为5秒 private void Form1_Load(object sender, EventArgs e)        {            t.Elapsed += new System.Timers.ElapsedEventHandler(Timer_TimesUp);            t.AutoReset = false; //每到指定时间Elap

C# 通过System.Timers.Timer获取时间

1 public delegate void timeDelegate();// Timer获取时间 2 3 private void Form1_Load(object sender, EventArgs e) 4 { 5 System.Timers.Timer t = new System.Timers.Timer(1000); //设置时间间隔为5秒 6 t.Elapsed += new System.Timers.ElapsedEventHandler(Timer_TimesUp); 7

C# System.Timers.Timer定时器的使用和定时自动清理内存应用

项目比较大有时候会比较卡,虽然有GC自动清理机制,但是还是有不尽人意的地方.所以尝试在项目启动文件中,手动写了一个定时器,定时清理内存,加快项目运行速度. public class Program { [DllImport("psapi.dll")] static extern int EmptyWorkingSet(IntPtr hwProc); //清理内存相关 static void Main() { //启动定时清理内存 SetTimer(); } /// <summar