4 3-bit LED Counter Using HAL Delay, Timer Interrupts, and Button Interrupt
4.1 Objective:
By the end of this exercise, students will:
- Implement a binary counter using three LEDs (LD1, LD2, LD3) on the STM Nucleo-144 F767ZI board.
- Explore three methods of controlling the LEDs: using HAL delay and for loops, timer interrupts, and push button interrupts.
- Understand the difference between using blocking delays, interrupt-driven operations, and external interrupts in embedded systems.
4.2 Materials:
- STM Nucleo-144 F767ZI development board.
- Onboard user LEDs:
- LD1 (Green): PB0 (or PA5).
- LD2 (Blue): PB7.
- LD3 (Red): PB14.
- Onboard user push button (USER_BUTTON): Connected to PC13.
- CubeIDE installed on the PC.
4.3 CubeMX Configuration:
1. GPIO Configuration for LEDs:
- LD1 (Green): Set PB0 (or PA5 depending on SB settings) as a GPIO Output.
- LD2 (Blue): Set PB7 as a GPIO Output.
- LD3 (Red): Set PB14 as a GPIO Output.
If you are using the default peripheral configuration it should be already set this way.
2. TIM7 Configuration (For Task 2)
- Enable TIM7 (or any other available timer).
- Configure the timer with a prescaler and period to generate an interrupt every 1 second.
- In the Parameter Settings, set the following configuration.
- Prescaler - 48000-1
- Counter Mode - Up
- Counter Period - 2000-1
- \[\begin{align*}\text{Interrupt Interval} &= \frac{(\text{Counter Period}+1)\times(\text{Prescaler}+1)}{\text{Clock Source Frequency}}\\ &=\frac{2000 \times 48000}{96\times10^6}=1 \text{s}\end{align*}\]
- In the NVIC Settings, enable the TIM7 global interrupt.
We are selecting TIM7 for timer as it is one of the basic timers. It uses the APB1 timer clock source as the clock source (Refer this block diagram and this section).
The frequency of the APB1 timer clock can be verified using the Clock Configuration tab.
4. Generate Code:
- Click Project > Generate Code after setting up GPIO, Timer, and Button.
4.4 Explanation of Key Functions:
HAL_Delay
Generates a blocking delay, pausing program execution for the specified number of milliseconds.
Syntax:
(milliseconds); HAL_Delay
HAL_GPIO_WritePin
Sets the state of a GPIO pin to HIGH or LOW, used to control the LEDs.
Syntax:
(GPIOx, GPIO_Pin, PinState); HAL_GPIO_WritePin
Example:
(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // Set PB0 (LD1) to HIGH HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // Set PB0 (LD1) to LOW HAL_GPIO_WritePin
HAL_TIM_Base_Start_IT
Starts a timer in interrupt mode.
Syntax:
(&htim7); // Start TIM7 in interrupt mode HAL_TIM_Base_Start_IT
HAL_TIM_PeriodElapsedCallback
The interrupt callback function triggered when the timer reaches the specified period.
Syntax:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
HAL_GPIO_EXTI_Callback
The interrupt callback function triggered when the external interrupt occurs.
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
4.5 Tasks and Sample IO Behavior:
Task 1: 3-bit LED Counter Using HAL Delay and For Loop
Objective: Create a 3-bit binary counter using the LEDs, with a delay between each count, controlled by
HAL_Delay()
in the main loop.Description:
- Use a for loop in the
main()
function to count from 0 to 7 (binary000
to111
). - Control the LEDs based on the binary value of the counter.
- Use
HAL_Delay()
to wait for 1 second between each count.
- Use a for loop in the
Code Example
int main(void) {
/* USER CODE BEGIN WHILE */
while (1) {
// 3-bit binary counter loop (0 to 7)
for (uint8_t i = 0; i < 8; i++) {
(GPIOB, GPIO_PIN_0, (i & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD1 (LSB)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (i & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD2
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, (i & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD3 (MSB)
HAL_GPIO_WritePin(1000); // 1-second delay
HAL_Delay}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
USER CODE BEGIN WHILE
// 3-bit binary counter loop (0 to 7)
for (uint8_t i = 0; i < 8; i++) {
(GPIOB, GPIO_PIN_0, (i & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD1 (LSB)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (i & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD2
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, (i & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD3 (MSB)
HAL_GPIO_WritePin(1000); // 1-second delay
HAL_Delay}
Sample IO:
- Output: The LEDs will count in binary, with a 1-second delay between each count.
- 000: All LEDs off.
- 001: LD1 on.
- 010: LD2 on.
- 011: LD1 and LD2 on.
- 100: LD3 on.
- 101: LD1 and LD3 on.
- 110: LD2 and LD3 on.
- 111: All LEDs on.
Task 2: 3-bit LED Counter Using Timer Interrupt
Objective: Create a 3-bit binary counter using the LEDs, with the counter updated by a timer interrupt instead of using
HAL_Delay()
.Description:
- Use a timer to generate an interrupt every 1 second.
- Inside the interrupt handler, increment the counter and update the LEDs to reflect the binary count.
Code Example
/* USER CODE BEGIN PV */
volatile uint8_t counter = 0; // 3-bit counter
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
void increment_counter(void) {
= (counter + 1) % 8; // Increment counter and wrap around using modulo
counter // Set LED states based on counter value
(GPIOB, GPIO_PIN_0, (counter & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD1 (LSB)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (counter & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD2
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, (counter & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD3 (MSB)
HAL_GPIO_WritePin}
/* USER CODE END 0 */
int main(void) {
/* USER CODE BEGIN 2 */
// Start timer in interrupt mode
(&htim7);
HAL_TIM_Base_Start_IT/* USER CODE END 2 */
}
/* USER CODE BEGIN 4 */
// Timer interrupt callback
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM7) {
(); // Update LED states based on counter value
increment_counter}
}
/* USER CODE END 4 */
USER CODE BEGIN PV
volatile uint8_t counter = 0; // 3-bit counter
USER CODE BEGIN 0
void increment_counter(void) {
= (counter + 1) % 8; // Increment counter and wrap around using modulo
counter // Set LED states based on counter value
(GPIOB, GPIO_PIN_0, (counter & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD1 (LSB)
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (counter & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD2
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, (counter & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); // LD3 (MSB)
HAL_GPIO_WritePin}
USER CODE BEGIN 2
// Start timer in interrupt mode
(&htim7); HAL_TIM_Base_Start_IT
USER CODE BEGIN 4
// Timer interrupt callback
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM7) {
(); // Update LED states based on counter value
increment_counter}
}
Sample IO:
- Output: The LEDs will count in binary, with a 1-second delay between each count, controlled by the timer interrupt.
- 000: All LEDs off.
- 001: LD1 on.
- 010: LD2 on.
- 011: LD1 and LD2 on.
- 100: LD3 on.
- 101: LD1 and LD3 on.
- 110: LD2 and LD3 on.
- 111: All LEDs on.
4.6 Conclusion:
In this exercise, you implemented a 3-bit binary counter using three different methods of LED control on the STM Nucleo-144 F767ZI board:
HAL Delay and For Loop: Demonstrated a straightforward approach for controlling LEDs with blocking delays, suitable for understanding basic timing but less effective for handling concurrent tasks.
Timer Interrupt: Showcased how to use a timer to handle periodic tasks efficiently, enabling the microcontroller to perform other operations while managing timing in the background. This method is particularly useful for tasks requiring precise timing without blocking the main execution.
Push Button Interrupt: Introduced the concept of external interrupts triggered by user input, allowing real-time interaction with the system. This method illustrated how to handle interrupts for external events, such as button presses, and provided a way to interact with the system dynamically.
These methods provide a comprehensive understanding of managing timing and interrupts in embedded systems, helping you to appreciate the trade-offs and applications of different techniques in real-time system design.