Introduction to STM32 system clock

System timer

The SysTick system timer belongs to the core peripheral of CM3, not the on-chip peripheral, that is, as long as the chip using the CM3 core has this function. The definition of registers and some library functions are defined and implemented in core_CM3.h.
The timer is used for timing. Compared with the traditional software simulation (while or for implementation) timing, the timer has a clear advantage in timing accuracy, and does not occupy CPU resources, allowing the CPU to handle other things. . The principle of timing: write the value that needs to be timed into the reload value register, and then configure the control and status register of the system timer to start the countdown. When the value in the current value register decreases to 0, it will decide whether to generate an interrupt according to the configuration of the control and status register, and set the status bit in the control and status register to 1 (the M3 status flag bit is the 16th bit, different core The status flag may be different). If the interrupt is enabled, the CPU executes the corresponding interrupt service function to respond to the interrupt. At the same time, the counter reloads the value in the reload value register into the current value register, and then starts the next round of counting, so as to cycle back and forth.
As you can see from the above, the timer is actually a subtraction count operation. So how often should it be decremented (clock), should an interrupt be generated when the count reaches 0, and should the timer be enabled? We can configure these registers to achieve these problems.

  • STK_CTRL: The 16th bit indicates whether the value in the timer is reduced to 0, if it is 0, it is set to 1 (it will be automatically cleared after reading 1), bit2 is used to select the timer clock, and bit1 is used to configure whether to generate an interrupt. bit0 is the enable switch that turns on the timer
  • Insert picture description here
  • STK_LOAD, 24-bit reload value register. From here we can know that the maximum count value of the system timer is 2 24 .
  • Insert picture description here
  • STK_VAL, current value register, read this register can get the current count value.
  • Insert picture description here
    So we only need to use the above registers to use the system timer. System timers are generally used in operating systems to generate time bases and maintain the heartbeat of the system. Since the system timer uses the processor's clock, the system timer will stop working after the processor enters the low-power mode.

For STM32 programming, it is more inclined to use library functions to configure the system timer. The system timer configuration library function is SysTick_Config (), where the parameter is the value of the reload value register. The system clock is used by default. If necessary, you can modify the configuration in the function yourself. The source code is as follows

/* ##################################    SysTick function  ############################################ */

#if (!defined (__Vendor_SysTickConfig)) || (__Vendor_SysTickConfig == 0)

/**
 * @brief  Initialize and start the SysTick counter and its interrupt.
 *
 * @param   ticks   number of ticks between two interrupts
 * @return  1 = failed, 0 = successful
 *
 * Initialise the system tick timer and its interrupt and start the
 * system tick timer / counter in free running mode to generate 
 * periodical interrupts.
 */
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

#endif

Among them, NVIC_SetPriority () sets the priority of the system timer interrupt. The judgment of IRQn is used to distinguish between system abnormality and external interruption. Among them, the priority of the system exception is controlled by the SHPRx register of the core peripheral SCB, and the priority of the external interrupt is controlled by the IPx register in the core peripheral NVIC.

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}

The kernel peripheral priority mentioned here is very similar to the peripheral interrupt priority described earlier. The priority range is 0 ~ 15. The lower the value, the higher the priority. If the kernel interrupt priority and external interrupt priority are set at the same time, the priority comparison is: convert the external interrupt priority to a 4-bit binary number, and then compare it with the kernel interrupt.

Here's how to use the timer to achieve the time you want. If the clock used by the timer is CLK AHB , then the time for one counting operation is T = 1 / CLK AHB , so the counter time is t = VALUE LOAD * T = VALUE LOAD / CLK AHB . VALUE LOAD = CLK AHB * t, SystemCoreClock is defined as 72M in the library, so you can use this macro to easily calculate the value in the reload value register of the response time.

After configuring the system timer, the next step is to implement the function body of the interrupt service function. It has been recently declared as void SysTick_Handler (void) in the startup file, and realize the function you want in this function.

If you just want to use a timer to achieve simple timing, such as implementing a delay function. Then it can be achieved with a simple method. Always poll the 16-bit value of the control and status register. A value of 1 means that the count is over. After reading 1, it will be cleared to 0 automatically. The code is while (! ((SysTick-> CTRL) & (1 << 16)));

So the programming of the system timer is still very simple:

  • Call the SysTick_Config () function to configure interrupt priority, reload register values, configure control and status register values, and clear current value register values.
  • Next is to implement the interrupt service function SysTick_Handler (), here to achieve the desired function.

Peripheral timer

In the STM32F1 series, in addition to the interconnected products, there are 8 timers, which are divided into basic timers, general-purpose timers, and advanced timers. TIM6 and TIM7 are a 16-bit timer that can only count up and can only time without external IO. The general-purpose timer TIM2 / 3/4/5 is a 16-bit timer that can count up / down, and can be used for timing and output comparison. The advanced timer TIM1 / 8 is a 16-bit timer that can count up / down. It can be timed or used for output comparison and input capture.
No matter what kind of timer, its most basic function is timing. The timing must have a time base, and the time base hasAutomatically reload registers, counters, counter clockscomposition. The frequency of counting the value in the counter is determined by the counter clock (one clock is counted once). The number of counts or the number of counts is determined by the value in the reload register. When the value in the counter reaches the requirement, an interrupt will be generated and start at the same time. The next round of counting .

Basic timer TIM6 / 7

The basic timer is very similar to the system timer, but there are some differences in the counting method and the system timer. The system timer counts down and the general timer counts up. TIM6 / TIM7 can be used as a general-purpose timer to provide a time reference, and in particular can provide a clock for a digital-to-analog converter . In fact, they are directly connected to the DAC inside the chip and directly drive the DAC through the trigger output. Features:
● 16-bit automatic reloading accumulation counter
● 16-bit programmable (can be modified in real time) prescaler, used to divide the input clock according to any value between 1 and 65536
● Trigger the synchronization of DAC Circuit
● An interrupt / DMA request is generated during an update event (counter overflow)
Insert picture description here

  • The clock source TIMxCLK, namely the internal clock CK_INT, is divided by the APB1 prescaler and provided. APB1 prescaler coefficient is 1, then use APB1 clock. Otherwise, double the use. The system of pre-dividing APB1 in the library function is 2, so the clock source for calling the library function is 72M .
  • Counter clock divider, after the timer clock source passes the PSC prescaler, get CK_INT to drive the counter to count. PSC is a 16-bit prescaler that divides the clock TIMxCLK by any number between 1 and 65536.
  • Counter CNT: Counter CNT is a 16-bit counter, which can only count up, the maximum value is 65535. When the count reaches the auto-reload register, an update event is generated and the count is reset from the beginning.
  • The auto-reload register is a 16-bit register. When the counter counts to this value, an interrupt is generated and the counter continues counting from 0.
    The structural block diagram is as simple as the above. Below is a detailed description of the block diagram. If you only want to use the function of the basic timer, you can ignore these details .
    Both the auto-reload register and the prescaler register have shadow registers. Each write is actually by reading and writing the preload register and does not directly operate the shadow register. When the counter generates an event update, the value in the preload register is transferred to the corresponding shadow register. Whether the shadow register is used in the prescaler register is determined by the ARPE field in the TIMx_CR1 register, and the counter clock prescaler has always used the shadow register, that is, the value written in the prescaler should be triggered when the next update event is triggered Will take effect.
    When ARPE = 0 (the shadow register is not used, that is, the TIMx_ARR register is not buffered, and the written value takes effect immediately)
    Insert picture description here
    ARPE = 1 (the TIMx_ARR register is buffered, and the value written to the auto-reload register will not start until the next update event use)
    Insert picture description here

Clock source

Only after the CEN bit (TIMx_CR1 register) is set will the internal clock provide a clock to the prescaler
Insert picture description here

Prescaler

The following figure is the timing diagram of the prescaler coefficient changing from 1 to 2. The prescaler divides the frequency by a 16-bit register (that is, a pulse is generated when the value reaches the value in the prescaler). The prescaler has a buffer, and its value can be changed during operation. The new prescaler value will take effect during the next update event.
Insert picture description here
The update event can be generated by the counter overflow or by software setting the UG bit in the TIMx_EGR register (software setting, hardware clear automatically) . When the URS bit of the TIMx_CR1 register is '0', then whenever the counter reaches the overflow value, the hardware issues an update event, which can also be triggered by software; when set to "1", the update event can only be overflowed by the counter Hardware trigger. If the UDIS bit is set, the generation of update events will be prohibited, so that the update event will not be generated, so the frequency division coefficient will not change, but the counter can work normally, the counter and the prescaler will still restart when the update event should be generated Start counting from 0 (but the prescaler coefficient does not change). If URS (select update request) in the TIMx_CR1 register is set at this time, setting the UG bit can generate an update event UEV, but does not set the UIF flag.
note: When the UG bit of the TIMx_EGR register is set, the timer counter is reinitialized and an update to the register is generated. Note: The prescaler is also cleared (but the prescaler coefficient does not change)

Counting mode

Even when the counter is running, the software can read and write the counter, auto-reload register, and prescaler register. The auto-reload register is preloaded, and each time the auto-reload register is read or written , it is actually realized by reading and writing the preload register. You can configure the auto-reload preload enable bit to implement whether the content written to the preload register is transferred to its shadow register immediately or at each event update. When the counter overflows, the hardware issues an update event.
If you want to modify the timer time, you need to modify the value of the auto-reload register. After modifying the auto-reload value, the auto-reload shadow register is also updated when the update event occurs (provided that ARPE is enabled). As shown below:
Insert picture description here

Basic timer programming

The above is the working principle of the general timer. Define the following structure in the library function:

/** 
  * @brief  TIM Time Base Init structure definition
  * @note   This structure is used with all TIMx except for TIM6 and TIM7.    
  */

typedef struct
{
  uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between 0x0000 and 0xFFFF */

  uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode */

  uint16_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter must be a number between 0x0000 and 0xFFFF.  */ 

  uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                      This parameter can be a value of @ref TIM_Clock_Division_CKD */

  uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                       reaches zero, an update event is generated and counting restarts
                                       from the RCR value (N).
                                       This means in PWM mode that (N+1) corresponds to:
                                          - the number of PWM periods in edge-aligned mode
                                          - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                       @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;    

Among them, TIM_Prescaler and TIM_Period can be used for general registers, that is, the timer prescaler setting coefficient and the value of automatic reload.

The programming flow of the basic timer is:

  • Turn on the clock of the timer, and there is no talk if there is no clock. The basic timer TIM6 / 7 is mounted on the APB1 bus.
  • Initialize the time base structure TIM_TimeBaseInitTypeDef, call the function TIM_TimeBaseInit () to initialize.
  • To enable the interrupt of the basic timer, call TIM_ITConfig (TIM_TypeDef * TIMx, uint16_t TIM_IT, FunctionalState NewState). Basic timers TIM6 and TIM7 can only generate an update interrupt.
  • Start timer void TIM_Cmd (TIM_TypeDef * TIMx, FunctionalState NewState)
  • If you enable interrupts, you need to implement the configuration of the corresponding interrupt service function and NVIC. Such as TIM6_IRQHandler () and TIM7_IRQHandler.
    Because external interrupts often share the same interrupt channel, it is necessary to distinguish what type of interrupt in the interrupt service function. In the basic timer, you can use API: ITStatus TIM_GetITStatus (TIM_TypeDef * TIMx, uint16_t TIM_IT) to obtain what interrupt was triggered. And by calling void TIM_ClearITPendingBit (TIM_TypeDef * TIMx, uint16_t TIM_IT); to clear the interrupt that has been processed.
    The specific implementation of the library function will not be posted here. For those who want to know, you can go to the library file to check the source code.
Published 35 original articles · Like1 · Visits 1870

Guess you like

Origin blog.csdn.net/lzj_linux188/article/details/103762038