Difference between revisions of "STM32 LED Blink"

From Stm32World Wiki
Jump to navigation Jump to search
imported>Lth
imported>Lth
Line 95: Line 95:
 
A third approach to blinking a [[led]] is to use one of the built-in timers of the CPU.
 
A third approach to blinking a [[led]] is to use one of the built-in timers of the CPU.
  
== PWM (Pulse Width Modulation ==
+
== PWM (Pulse Width Modulation) ==
  
 
The final approach to blinking a [[LED]] would be to use [[PWM]] ([[Pulse-width modulation]]).  Unfortunately, the PC13 [[GPIO]] port used in the previous examples, is not able to do hardware [[PWM]], so in order to demonstrate this approach an external [[LED]] will have to be hooked up to a pin offering this.
 
The final approach to blinking a [[LED]] would be to use [[PWM]] ([[Pulse-width modulation]]).  Unfortunately, the PC13 [[GPIO]] port used in the previous examples, is not able to do hardware [[PWM]], so in order to demonstrate this approach an external [[LED]] will have to be hooked up to a pin offering this.

Revision as of 07:49, 1 November 2020

When learning a new programming language, programmers often - if not always - begin with a humble "hello world" application, which will print "Hello World!" on the display. As common as that, when it comes to embedded programming (where a display might not be available) a typical "first application" is one which will blink a led. And for this reason, most development boards comes with one or more leds which can be controlled with a GPIO pin.

In this article, I will be using my own Green Pill development board, which for all intents and purposes is comparable to the common Blue Pill. The board is based on an STM32F103 processor, includes a 8 MHz external crystal and has got a led attached to the PC13 GPIO pin.

Common Settings

For these examples, I will be using ST's Stm32CubeIde, which includes Stm32CubeMx. Stm32CubeMx is used to "configure" the processor.

When starting a new project in Stm32CubeIde, I generally go through some common settings. First step I configure the Serial Wire debug (including the trace):

Stm32CubeMX Sys Settings.png

Second step is to configure the CPU to enable the external crystal:

Stm32CubeMx crystal setting.png

Final step is to configure the various clocks:

Stm32CubeMx clock.png

The important values here is the value of the external crystal (in this case 8 MHz), the value of HCLK, which is the frequency the processor will run at. Also important to notice is the value of the APB1 Timer Clocks. This is the frequency at which the timers will operate (in this case 72 MHz - remember this value for later examples).

Main Loop With Delay

This approach - while wildly misguided - is often seen in examples, particularly Arduino based ones. In this approach, the led is simply toggled in the main loop of the program, with an appropriate delay. Using Stm32CubeIde and it's HAL libraries, the main loop will look something like:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

	// Toggle the LED
	HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);

	// Wait for 100 ms
	HAL_Delay(100);

	// Rinse and repeat :)

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

The approach is simple and easily understood. It will toggle the led, not caring what the previous state was, and then wait for 100 ms. The result will be approximately 5 blinks per second:

The keyword in the above description is "approximately". I made the claim earlier that this approach was generally misguided and that is part of the problem. In reality, this approach has at least two problems.

First of all, the call of both HAL_GPIO_TogglePin and HAL_Delay are not single instructions, so the actual time spend in the loop will be "a tiny bit" longer than 100 ms resulting in a frequency which is slighly below the intended 5 Hz. Sure, it will only be "off" by a few micro seconds, but it will be off!

The second problem is the "HAL_Delay". While that is running, the processor is tied up in doing "nothing". While doing nothing but flashing a led that is of course OK, but typically one would want the processor to do "other things" and if that is the case the actual LED frequency will be even more unpredictable.

Main Loop Without Delay

A somewhat better approach would be to check the time in the main loop - something like this:

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  uint32_t then = 0;

  while (1)
  {

	// Check the current tick
	uint32_t now = HAL_GetTick();
	if (now % 100 == 0 && now != then) { // Only if the current tick is 500 ms after the last

		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // Toggle LED

		then = now; // Reset then = now
	}

	// Other stuff can be done here without affecting the blink frequency as long as
	// whatever is being done take less than 100 ms.
	
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Compared to the previous example, this one is still running in the main loop, but without a forced delay. Instead of the delay, this version will check the "HAL_GetTick()".

Using a timer

A third approach to blinking a led is to use one of the built-in timers of the CPU.

PWM (Pulse Width Modulation)

The final approach to blinking a LED would be to use PWM (Pulse-width modulation). Unfortunately, the PC13 GPIO port used in the previous examples, is not able to do hardware PWM, so in order to demonstrate this approach an external LED will have to be hooked up to a pin offering this.