š Assignment 7 CMPE1250: Introduction to Timers
š Overview
-
In this Assignment , you will transition from using āMagic Numbersā in your main.c to building a reusable Timer Driver.
-
We will focus on the Basic Timers (TIM6/TIM7) of the STM32G0B1.
-
These timers are stripped-down counters designed specifically for timebase generationāthey do not have input/output channels, making them the perfect starting point.
-
Objectives:
-
Calculate Prescaler (
PSC) and Auto-Reload (ARR) values for specific time intervals. -
Implement a structured peripheral library based on a provided header file.
-
Distinguish between
Blocking(Delay) andNon-Blocking(Polling) timing logic.
-
1ļøā£ Preparatory Work and calculations
- Assuming the System Clock (fSCKā) is being tracked in the
SystemCoreClockvariable, calculate the register values required for the tick time.
Tick Time: \({TickTime} = \frac{PSC + 1}{f_{SCKā}}\)
| Target Tick | Desired Frequency | Suggested PSC |
|---|---|---|
| 1 ms | 1[KHz] | ? |
| 1 µs | 1[MHz] | ? |
- Once the tick time has been defined, the value in the
ARRregister will determine how many ticks occur before the timer generates an update event.
Update Time Event Time: \({TimeEvent} = \frac{(PSC + 1)\cdot(ARR + 1)}{f_{SCKā}}={TickTime}\cdot(ARR + 1)\)
- The event generated could be blocking or unblocking depending on how the logic is required to be implemented in your program.
2ļøā£ Timer Basic Library Implementation
-
Use the header provided
timer.hheader file -
Implement the source file
timer.c
//******************************************************************************
// Timer / PWM Library Implementation
//
// COURSE: CMPE1250
// NAME: ________________
// DATE: ________________
//
// FILE: timer.c
// NOTES: This library assumes the peripheral clocks have been enabled
// in RCC prior to calling these functions (e.g., in HAL_Init()).
//******************************************************************************
#include "timer.h"
/**
* @brief Configures the timer timebase (PSC and ARR).
* @param timer: Pointer to the timer peripheral (e.g., TIM6, TIM7)
* @param psc: Prescaler value
* @param period: Auto-reload value (ARR)
*/
void Timer_Init(TIM_TypeDef * timer, uint16_t psc, uint16_t period)
{
// TODO: Set the Prescaler (PSC) register
// TODO: Set the Auto-reload (ARR) register
// TODO: Trigger an Update Event (UG bit) to load the PSC and ARR
// values into the shadow registers immediately.
// TODO: The UG bit sets the Update Interrupt Flag (UIF) in the SR.
// Clear the UIF flag now so the timer doesn't start with a pending event.
}
/**
* @brief Enables or Disables the timer counter.
* @param en: 1 to start, 0 to stop.
*/
void Timer_SetEnable(TIM_TypeDef * timer, uint16_t en)
{
// TODO: Modify the CEN bit in the Control Register 1 (CR1)
// HINT: Use bitwise OR to set, and bitwise AND with NOT to clear.
}
/**
* @brief Configures the timer for a 1ms tick rate.
* @note This should work for any System Clock Speed
*/
void Timer_SetmsTick(TIM_TypeDef * pTimer)
{
// TODO: Calculate the PSC needed for 1ms.
}
/**
* @brief Configures the timer for a 1us tick rate.
*/
void Timer_SetusTick(TIM_TypeDef * pTimer)
{
// TODO: Calculate the PSC needed for 1us.
}
/**
* @brief Checks the Update Interrupt Flag (UIF).
* @return 1 if a rollover occurred (flag set), 0 otherwise.
* @note This function MUST clear the flag if it is found to be set.
*/
uint8_t Timer_PollUIF(TIM_TypeDef * pTimer)
{
// TODO: Check if the UIF bit in the Status Register (SR) is 1.
// IF SET:
// 1. Clear the UIF bit in the SR.
// 2. Return 1.
// ELSE:
// Return 0.
return 0; // Placeholder
}
/**
* @brief Creates a blocking delay.
* @param ticks: Number of ms/us to wait (depending on timer config).
*/
void Timer_DelayTicks(TIM_TypeDef * pTimer, uint16_t ticks)
{
// 1. TODO: Modify the Auto-Reload Register (ARR) to match the requested ticks.
// HINT: Remember the hardware counts from 0, so account for the off-by-one!
// 2. TODO: Force an Update Event (UG bit in EGR) to reset the counter
// and immediately load the new ARR value into the shadow register.
// 3. TODO: Forcing the update just set the UIF flag in the Status Register (SR).
// Clear it now so we can accurately detect when the actual delay finishes.
// 4. TODO: Wait in a blocking while-loop until the hardware sets the
// UIF flag in the Status Register (SR), indicating the time has elapsed.
// 5. TODO: Clear the UIF flag again before exiting the function if you like to have it clean
// for the next time we use the timer.
}
3ļøā£ Verification: : The Application Loop (Software PWM)
In this final section, you will combine both blocking and non-blocking timer strategies to generate a custom pulse on an LED (PA5). We are going to create a signal that fires every 125 ms, and stays high for 400 µs.
Step 1: System Clock Initialization
-
Use your
Clock_InitPll(PLL_40MHZ)function to boost the system clock to 40 MHz. -
Important: Ensure your calculations inside
Timer_SetmsTickandTimer_SetusTickaccount for this 40 MHz bus speed!
Step 2: Timer Configuration
-
We need two timers with two distinct jobs:
-
TIM6 (The Pacemaker): Call
Timer_SetmsTick(TIM6). By default, this configures a 1 ms tick. To make it poll every 125 ms, manually override the Auto-Reload Register right after initialization. -
TIM7 (The Delay): Call
Timer_SetusTick(TIM7)to establish a 1 µs timebase. We will leave its ARR alone for now, asTimer_DelayTicks()will dynamically modify it. -
Start both timers using your
Timer_SetEnablefunction.
Step 3: The Super Loop
Inside your while(1) loop, implement the following logic:
-
Poll TIM6: Use an
ifstatement to checkTimer_PollUIF(TIM6). This is non-blocking, meaning the CPU is free to do other things while waiting for the 125 ms to pass. -
Generate the Pulse: Inside that
ifblock:-
Turn ON the LED on PA5.
-
Call
Timer_DelayTicks(TIM7, 400)to block the CPU for exactly 400 µs. -
Turn OFF the LED on PA5.
-
Result: You have just created a software-driven pulse!
4ļøā£ The Blocking āHiccupā
-
If you want to visually see the CPU get blocked, add code to toggle
PA6, in addition to the User LED, directly inside your while(1) loop, completely outside the TIM6 if statement. -
Hook up Channel 2 of your oscilloscope to PA6. You will see a high-frequency toggle that completely freezes for exactly 400 µs every time the PA5 pulse fires!. Exaplain why this happens