{"id":24780,"date":"2025-09-10T18:11:12","date_gmt":"2025-09-10T18:11:12","guid":{"rendered":"https:\/\/wolles-elektronikkiste.de\/?p=24780"},"modified":"2025-09-10T18:11:25","modified_gmt":"2025-09-10T18:11:25","slug":"stm32-boards-part-2-selected-functions","status":"publish","type":"post","link":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions","title":{"rendered":"STM32 boards &#8211; Part 2: Selected functions"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">About this Post<\/h2>\n\n<p>In the <a href=\"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-1-overview-and-upload\">last post<\/a>, I showed you how to upload sketches to STM32 boards using the Arduino IDE. In this post, I would now like to go one step further and present some selected functions. On the one hand, this includes basic features that are not standard in the Arduino range of functions but are often required, such as timers, PWM or various energy-saving modes. On the other hand, I will go into the peculiarities of the STM32 implementation for well-known functions such as I\u00b2C, SPI or interrupts. And finally, there are certain extras that not every Arduino board offers, such as the digital-to-analog converter (DAC) or the real-time clock (RTC).    <\/p>\n\n<p>Here is an overview of the topics:<\/p>\n<ul>\n<li><a href=\"#selected_boards\">Board selection for this article<\/a><\/li>\n<li><a href=\"#hardware_timer\">Hardware timer interrupts<\/a><\/li>\n<li><a href=\"#pwm\">Pulse Width Modulation (PWM)<\/a><\/li>\n<li><a href=\"#ADC\">Analog-to-digital converter (ADC)<\/a><\/li>\n<li><a href=\"#DAC\">Digital-to-analog converter (DAC)<\/a><\/li>\n<li><a href=\"#EXTI\">External interrupts<\/a><\/li>\n<li><a href=\"#I2C\">I\u00b2C<\/a><\/li>\n<li><a href=\"#SPI\">SPI<\/a><\/li>\n<li><a href=\"#hardware_serial\">HardwareSerial<\/a><\/li>\n<li><a href=\"#software_serial\">SoftwareSerial<\/a><\/li>\n<li><a href=\"#IWDG\">Independent Watchdog (IWDG)<\/a><\/li>\n<li><a href=\"#RTC\">Real Time Clock (RTC)<\/a><\/li>\n<li><a href=\"#low_power\">Low-power modes<\/a><\/li>\n<li><a href=\"#appendix\">Appendix &#8211; Extended RTC sketch<\/a><\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Programming levels of the SM32 boards<\/h3>\n\n<p>Microcontrollers (MCUs) can be programmed at different levels of abstraction. In the case of STM32 MCUs, these are essentially <\/p>\n<ol>\n<li><strong>Register level (&#8220;Bare-Metal&#8221;, &#8220;Low-Level&#8221;)<\/strong>: You access the registers directly via structures and bit masks. This is very efficient, but the least portable. <\/li>\n<li><strong>CMSIS (Cortex Microcontroller Software Interface Standard)<\/strong>: Is specified by ARM and is supplied with ST. IRQ names and core functions, for example, are defined there. &nbsp;<\/li>\n<li><strong>ST-own abstractions<\/strong>:\n<ol style=\"list-style-type: lower-alpha;\">\n<li><strong>LL (Low-Layer)<\/strong>: Still close to the registers.<\/li>\n<li><strong>HAL (Hardware Abstraction Layer)<\/strong>: Higher abstraction, higher portability, but more overhead.<\/li>\n<\/ol>\n<\/li>\n<li><strong>Frameworks \/ cores<\/strong> for high-level development: This includes in particular the Arduino core board package from STM32duino, which I already used in the last article. HAL and LL are the basis for this but are &#8220;hidden&#8221; behind the Arduino-typical functions. &nbsp;<\/li>\n<\/ol>\n<p>This article is only about the high-level programming. However, as you will certainly come across terms such as HAL, LL or bare metal when dealing with the STM32 boards, I didn&#8217;t want to leave them unmentioned. <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"selected_boards\">Board selection for this article<\/h2>\n\n<p>Of course, I cannot discuss all STM32 boards here. Therefore, I have made the same selection as in the first part of the article:<\/p>\n<ul>\n<li>The &#8220;BluePill&#8221; based on the STM32F103C8&nbsp;<\/li>\n<li>The &#8220;BlackPill&#8221; based on the STM32F411<\/li>\n<li>The Nucleo-L432KC board, as a representative of a Nucleo-32 board<\/li>\n<li>The Nucleo-F446RE board, as a representative of a Nucleo-64 board<\/li>\n<li>The Arduino GIGA R1 WIFI, as an example of an STM32-based Arduino board<\/li>\n<\/ul>\n\n<p>The main focus is on the first three boards.&nbsp;<\/p>\n<p>And one more note in advance: When I refer to&#8221;<em>the<\/em> Blackpill&#8221; or&#8221;<em>the<\/em> BluePill&#8221; in this article, I am referring to the above-mentioned types. There are also different versions or fakes of these types. Therefore, your boards may behave differently in certain aspects than discussed here.  <\/p>\n\n<h3 class=\"wp-block-heading\">Pinout of the boards discussed here<\/h3>\n\n<h4 class=\"wp-block-heading\">&#8220;BluePill&#8221; (STM32F103C8)<\/h4>\n\n<p>I took the pinout diagram for the BluePill shown below from <a href=\"https:\/\/mischianti.org\/stm32f1-pinout-specs-and-arduino-ide-configuration-stm32duino-and-stmicroelectronics-1\/\" target=\"_blank\" rel=\"noopener\">this nice article<\/a> by <a href=\"https:\/\/mischianti.org\/\" target=\"_blank\" rel=\"noopener\">Renzo Mischianti<\/a>. If you want to dive deeper into the specific features of the BluePill boards, I can highly recommend Renzo Mischianti&#8217;s articles. <\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution-1024x798.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"798\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution-1024x798.jpg\" alt=\"Pinout BluePill (STM32F103C8) from Renzo Mischianti.\" class=\"wp-image-24500\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution-1024x798.jpg 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution-300x234.jpg 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution-768x598.jpg 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F1-STM32F103-STM32F103C8T6-pinout-low-resolution.jpg 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Pinout Bluepill (STM32F103C8), courtesy of <a href=\"https:\/\/mischianti.org\/\">Renzo Mischianti<\/a> (respect copyright!)<\/figcaption><\/figure>\n\n<p><strong>Peculiarities of this BluePill board<\/strong>:<\/p>\n<ul>\n<li>Some pins are 5-volt tolerant (green squares); others only tolerate 3.3 volts (red-brown squares).<\/li>\n<li>The following applies to PC13, PC14, and PC15:&nbsp;\n<ul>\n<li>These pins must not supply any current in the HIGH state, i.e., you can use the &#8220;HIGH&#8221; state as a logic output, but not operate an LED with it, for example.<\/li>\n<li>In the LOW state, the pins may tolerate a maximum of 3 mA (the LED on PC13 is connected to 3.3 volts and lights up when PC13 is OUTPUT\/LOW).<\/li>\n<li>They shall not work at a frequency higher than 2 MHz and shall not be loaded with more than 30 pF.<\/li>\n<\/ul>\n<\/li>\n<li>The board can be operated via the 3.3-volt or 5-volt pins. VBAT only supplies power to the RTC and backup registers. <\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">&#8220;BlackPill&#8221; STM32F411<\/h4>\n\n<p>As with the BluePill, I also used Renzo Mischianti&#8217;s pinout for the BlackPill. If you want to delve even deeper into the details of the BlackPill boards, I recommend Renzo&#8217;s series of articles. <a href=\"https:\/\/mischianti.org\/black-pill-stm32f4-pinout-specs-and-arduino-ide-configuration-4\/\" target=\"_blank\" rel=\"noopener\">This article<\/a> is a good starting point. <\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution-1024x591.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"591\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution-1024x591.jpg\" alt=\"Pinout BlackPill (STM32F411) from Renzo Mischianti.\" class=\"wp-image-24503\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution-1024x591.jpg 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution-300x173.jpg 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution-768x443.jpg 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/STM32-STM32F4-STM32F411-STM32F411CEU6-pinout-low-resolution.jpg 1280w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Pinout BlackPill (STM32F411), courtesy of <a href=\"https:\/\/mischianti.org\/\">Renzo Mischianti<\/a> (respect copyright!)<\/figcaption><\/figure>\n\n<p><strong>Peculiarities of this BlackPill board:<\/strong> The same restrictions and notes for the pins apply as for the BluePill board, but considerably more pins are 5-volt-tolerant.<\/p>\n\n<h4 class=\"wp-block-heading\">Pinout Nucleo-L432KC<\/h4>\n\n<p>The Nucleo-L432LC board attempts to achieve a certain compatibility with the Arduino Nano boards. The pinout largely matches the pin designations and functionalities. &nbsp;<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-1024x549.webp\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"549\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-1024x549.webp\" alt=\"Pinout Nucleo-L432KC Board\" class=\"wp-image-24581\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-1024x549.webp 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-300x161.webp 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-768x411.webp 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-1536x823.webp 1536w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout-1320x707.webp 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_l432kc_pinout.webp 1934w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Pinout Nucleo-L432KC Board (own creation)<\/figcaption><\/figure>\n\n<p><strong>Peculiarities of the Nucleo-L432KC<\/strong>:<\/p>\n<ul>\n<li>The GPIOs are only compatible with 3.3 volts.<\/li>\n<li>Pins D7 and D8 are not connected to anything. I will come back to this in the solder bridges section below. <\/li>\n<li>You can use the &#8220;3.3V&#8221; and &#8220;5V&#8221; pins to supply other components with power. The maximum permissible current depends on the type of power supply of the board. See the <a href=\"https:\/\/www.st.com\/resource\/en\/user_manual\/um1956-stm32-nucleo32-boards-mb1180-stmicroelectronics.pdf\" target=\"_blank\" rel=\"noopener\">UM1956<\/a> user manual.  <\/li>\n<li>To supply the board via the 3.3V and 5V pins, you must change the configuration of the solder bridges SB<em>xy<\/em> (see below).<\/li>\n<li>You can supply the board with 7 to 12 volts via VIN.<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">Pinout Nucleo-F446RE and Arduino GIGA R1 WIFI<\/h4>\n\n<p>You can find a pinout diagram of the Nucleo-F446RE <a href=\"https:\/\/os.mbed.com\/platforms\/ST-Nucleo-F446RE\/\" target=\"_blank\" rel=\"noopener\">here<\/a>. A total of 50 GPIOs are available with this board. You can find a pinout diagram of the Arduino GIGA R1 WIFI <a href=\"https:\/\/docs.arduino.cc\/resources\/pinouts\/ABX00063-full-pinout.pdf\" target=\"_blank\" rel=\"noopener\">here<\/a> on the Arduino pages. This board even has a whopping 76 GPIOs.   <\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-1024x370.webp\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"370\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-1024x370.webp\" alt=\"STM32 boards Nucleo-F446RE and Arduino GIGA R1 WIFI\" class=\"wp-image-24654\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-1024x370.webp 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-300x108.webp 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-768x278.webp 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga-1320x477.webp 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/arduino_giga.webp 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Nucleo-F446RE and Arduino GIGA R1 WIFI<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Solder bridges of the Nucleo boards<\/h3>\n\n<p>If you look at the Nucleo-L432KC board (or other Nucleo boards), you will see many solder bridges labeled &#8220;SB<em>xy<\/em>&#8220;. Some of them are open, the others are closed via 0 \u03a9 resistors. A detailed description of the solder bridges of the Nucleo-32 boards can be found in <a href=\"https:\/\/www.st.com\/resource\/en\/user_manual\/um1956-stm32-nucleo32-boards-mb1180-stmicroelectronics.pdf\" target=\"_blank\" rel=\"noopener\">UM1956<\/a> mentioned above. Here are some selected settings:   <\/p>\n<ul>\n<li>To supply the board with power via the 3.3-volt pin, SB9 and SB14 must be open. As a restriction, the board-internal ST-LINK is then not available. <\/li>\n<li>To supply the board via the 5-volt pin, SB9 must be open. The board-internal ST-LINK is then not available. The supply via the 5-volt pin and debugging via ST-LINK can be combined, but then a certain procedure must be followed (see UM1956 again).  <\/li>\n<li>If you want to use pins D7 and D8 as GPIOs (PC14\/PC15), then SB4, SB5, SB7 and SB17 must be open; SB6 and SB8 must be closed. However, you will then no longer be able to use the external crystal for the RTC. <\/li>\n<\/ul>\n<p>As you can see, it&#8217;s quite complex. <strong>And before you start soldering your board, be sure to read the UM1956 User Manual carefully!<\/strong><\/p>\n\n<p>The Nucleo-F446RE board also has even more solder bridges. Accordingly, there are also peculiarities here, such as the open solder bridges (SB48 \/ SB49) on PC14 and PC15. Explanations can be found in the <a href=\"https:\/\/www.st.com\/resource\/en\/user_manual\/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf\" target=\"_blank\" rel=\"noopener\">UM2324<\/a> user manual.  <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"hardware_timer\">Hardware timer interrupts<\/h2>\n\n<p>But now we finally start with the practical part. First, let&#8217;s take a look at how to program timer overflow interrupts using the HardwareTimer class. The definition of the class can be found in HardwareTimer.h. This file is located in the directory &#8230;\\Arduino15\\packages\\STMicroelectronics\\hardware\\stm32\\2.11.0\\libraries\\SrcWrapper\\inc. The corresponding file HardwareTimer.cpp is located &#8220;nearby&#8221;, under &#8230;\\SrcWrapper\\src. If you want to delve deeper into the topic and get to know all the functions of the HardwareTimer class, it is worth taking a look at these files.     <\/p>\n<p>The STM32 MCUs have several timers (<em>TIMx<\/em>). You can easily find out what these are in the data sheet of the underlying microcontroller. It also states whether they are 16-bit or 32-bit timers.  <\/p>\n\n<p>I prefer to use the TIM2 timer for the examples, as it is available on all the boards under consideration. On the BluePill TIM2 is a 16-bit timer; on the BlackPill and the Nucleo-L432KC it is a 32-bit timer. <\/p>\n\n<h3 class=\"wp-block-heading\">One interrupt<\/h3>\n\n<h4 class=\"wp-block-heading\">Control via the prescale factor and overflow value<\/h4>\n\n<p>Let&#8217;s take a look at the first example:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"timer_interrupt.ino\" data-enlighter-title=\"timer_interrupt.ino\">HardwareTimer myTim(TIM2);\n\nvoid setup() {  \n    uint32_t prescaleFactor = 10000;    \/\/ fits well to the BlackPill\n    \/\/  uint32_t prescaleFactor = 7200; \/\/ fits well to the BluePill\n    \/\/  uint32_t prescaleFactor = 8000; \/\/ fits well to the L432KC\n    \/\/  uint32_t prescaleFactor = 8400; \/\/ fits well to the F446RE\n    uint32_t timerOverflow = 10000;\n    Serial.begin(115200);\n    myTim.setPrescaleFactor(prescaleFactor);\n    myTim.setOverflow(timerOverflow);\n    myTim.attachInterrupt(onTimer);\n    myTim.resume();\n}\n\nvoid loop() {}\n\nvoid onTimer() {\n    Serial.println(\"1 second\");\n}<\/pre>\n<p>\n\n<p>You will receive the message &#8220;1 second&#8221; on the serial monitor every second.<\/p>\n\n<h5 class=\"wp-block-heading\">Explanations to the sketch<\/h5>\n\n<p>Use <code>HardwareTimer myTim(TIM2)<\/code> to create the myTim object and assign TIM2 to it. Define the prescaler factor with <code>setPrescaleFactor()<\/code>. It can have a value between 1 and2<sup>16<\/sup>. The count frequency of the timer is the system clock divided by the prescaler factor. You should select it so that you can calculate comfortably with it. The examples in the sketch regulate the counting frequency down to 10 kHz. You set the overflow with <code>setOverflow()<\/code>. For 16-bit timers, the maximum value is2<sup>16<\/sup>, for 32-bit timers it is a maximum of2<sup>32<\/sup>-1, as <code>timerOverflow<\/code> is passed as a 32-bit number.       <\/p>\n<p>In the example above, overflow is 10000, and therefore the overflow frequency is 1 Hz according to the following formula:<\/p>\n\n<p><\/p>\n<p><p class=\"ql-center-displayed-equation\" style=\"line-height: 43px;\"><span class=\"ql-right-eqno\"> &nbsp; <\/span><span class=\"ql-left-eqno\"> &nbsp; <\/span><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/ql-cache\/quicklatex.com-5ff83c5a4c1153c3b7803f077508e4d2_l3.png\" height=\"43\" width=\"340\" class=\"ql-img-displayed-equation quicklatex-auto-format\" alt=\"&#92;&#091; &#70;&#95;&#123;&#92;&#116;&#101;&#120;&#116;&#123;&#111;&#118;&#101;&#114;&#102;&#108;&#111;&#119;&#125;&#125;&#32;&#61;&#32;&#92;&#102;&#114;&#97;&#99;&#123;&#92;&#116;&#101;&#120;&#116;&#123;&#115;&#121;&#115;&#116;&#101;&#109;&#67;&#108;&#111;&#99;&#107;&#125;&#125;&#123;&#92;&#116;&#101;&#120;&#116;&#123;&#112;&#114;&#101;&#115;&#99;&#97;&#108;&#101;&#70;&#97;&#99;&#116;&#111;&#114;&#125;&#92;&#99;&#100;&#111;&#116;&#32;&#92;&#116;&#101;&#120;&#116;&#123;&#116;&#105;&#109;&#101;&#114;&#79;&#118;&#101;&#114;&#102;&#108;&#111;&#119;&#125;&#125; &#92;&#093;\" title=\"Rendered by QuickLaTeX.com\"\/><\/p><\/p>\n\n<p>Accordingly, the minimum interrupt frequency when using a 16-timer on a BlackPill board is 100 [MHz] \/ (2<sup>16<\/sup> *2<sup>16<\/sup>) = ~0.023 [Hz]. For a 32-bit timer, it would be ~3.55*10<sup>-7<\/sup> [Hz], which corresponds to a period of approx. 32.5 days. <\/p>\n\n<p>You pass the interrupt service routine (ISR) to be executed to the function <code>attachInterrupt<\/code>. Use <code>resume()<\/code> to start or continue the counter. <\/p>\n\n<h4 class=\"wp-block-heading\">Control via the period<\/h4>\n\n<p>Alternatively, you can simply set the period. Then you don&#8217;t have to calculate and can port the sketch unchanged to other boards: <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"timer_interrupt_microsecs.ino\" data-enlighter-title=\"timer_interrupt_microsecs.ino\">HardwareTimer myTim(TIM2);\n\nvoid setup() {\n    uint32_t timerOverflow = 1000000; \n    Serial.begin(115200);\n    myTim.setOverflow(timerOverflow, MICROSEC_FORMAT);\n    myTim.attachInterrupt(onTimer);\n    myTim.resume();\n}\n\nvoid loop() {}\n\nvoid onTimer() {\n    Serial.println(\"1 second\");\n}<\/pre>\n<p>\n\n<p>I think the sketch is self-explanatory.<\/p>\n\n<h4 class=\"wp-block-heading\">Control via the frequency<\/h4>\n\n<p>You can also set the overflow frequency as a further option. However, 1 Hz is the slowest setting you can make. <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"timer_interrupt_frequency.ino\" data-enlighter-title=\"timer_interrupt_frequency.ino\">HardwareTimer myTim(TIM2);\n\nvoid setup() {\n    uint32_t timerOverflow = 1; \/\/ 1 Hz is the slowest frequency\n    Serial.begin(115200);\n    myTim.setOverflow(timerOverflow, HERTZ_FORMAT);\n    myTim.attachInterrupt(onTimer);\n    myTim.resume();\n}\n\nvoid loop() {}\n\nvoid onTimer() {\n    Serial.println(\"1 second\");\n}<\/pre>\n<p>\n\n<h3 class=\"wp-block-heading\">Multiple interrupts<\/h3>\n\n<p>You can, of course, also set up several interrupts. Here is an example that uses arrays for the timer objects and ISRs. LEDs are &#8220;toggled&#8221; in the ISRs. If you want to try out the sketch, connect LEDs to the corresponding pins.    <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"four_timer_interrupts.ino\" data-enlighter-title=\"four_timer_interrupts.ino\">HardwareTimer myTim0(TIM1);\nHardwareTimer myTim1(TIM2);\nHardwareTimer myTim2(TIM3); \/\/ TIM15 for L432KC; TIM13 for F446RE\nHardwareTimer myTim3(TIM4); \/\/ TIM16 for L432KC; TIM14 for F446RE\nHardwareTimer* myTimers[4] = {&amp;myTim0, &amp;myTim1, &amp;myTim2, &amp;myTim3};\n\nconst int ledPin[4] = {PA0, PA1, PA2, PA3}; \/\/ A0, A1, A2, A3: for L432KC\n\nuint32_t timerOverflow[4] = {500000, 1200000, 1700000, 2200000};\n\nvoid onTimer0() {\n    digitalWrite(ledPin[0], !digitalRead(ledPin[0]));\n}\n\nvoid onTimer1() {\n    digitalWrite(ledPin[1], !digitalRead(ledPin[1]));\n}\n\nvoid onTimer2() {\n    digitalWrite(ledPin[2], !digitalRead(ledPin[2]));\n}\n\nvoid onTimer3() {\n    digitalWrite(ledPin[3], !digitalRead(ledPin[3]));\n}\n\n\nvoid (*onTimer[4])(void) = {onTimer0, onTimer1, onTimer2, onTimer3}; \n\nvoid setup () {\n    for (int i=0; i&lt;4; i++){\n        pinMode(ledPin[i], OUTPUT);\n        myTimers[i]-&gt;setOverflow(timerOverflow[i], MICROSEC_FORMAT);\n        myTimers[i]-&gt;attachInterrupt(onTimer[i]);\n        myTimers[i]-&gt;resume();\n    }\n}\n\nvoid loop() {}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 class=\"wp-block-heading\">Timer interrupts for the Arduino GIGA R1 WIFI Board<\/h3>\n\n<p>Since the HardwareTimer class is firmly anchored in the STM32 Arduino Core package, it does not work with the Arduino GIGA R1 WIFI board if you use the standard Arduino board package.<\/p>\n<p>Alternatively, you can use the Portenta_H7_TimerInterrupt library, which you can install via the Arduino library manager. It was written for the Portenta H7 board, but also works with the Arduino GIGA R1 WIFI, as both have the same microcontroller. <\/p>\n<p>When compiling the example sketches you will get the error message: <em>&#8220;This code is intended to run on the MBED ARDUINO_PORTENTA_H7 platform! Please check your Tools-&gt;Board setting&#8221; <\/em>. The compiler also tells you which file produced this error. It is Portenta_H7_ISR_Timer.hpp on the one hand and Portenta_H7_TimerInterrupt.h on the other. <\/p>\n<p>Go to the files and search for the line (probably 30):<\/p>\n<p><code>#if ( ( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) ) &amp;&amp; defined(ARDUINO_ARCH_MBED) )<\/code><\/p>\n<p>Change it to:<\/p>\n<p><code>#if ( ( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) || defined(ARDUINO_GIGA) ) &amp;&amp; defined(ARDUINO_ARCH_MBED) )<\/code><\/p>\n<p>You can proceed similarly in the example sketches or you can comment out the relevant lines. Then it should work. <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"pwm\">Pulse Width Modulation (PWM)<\/h2>\n\n<p>Pulse width modulation (PWM) can also be easily implemented using the hardware timer class. Here is a simple example:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"pwm_basic.ino\" data-enlighter-title=\"pwm_basic.ino\">HardwareTimer Timer(TIM1); \n\nvoid setup() {\n    uint32_t channel = 3;\n    int pwmPin = PA10; \/\/ D0 for L432KC; D2 for F446RE\n    uint32_t frequency = 1;\n    uint32_t dutyCycle = 20; \/\/ 20%\n    Timer.setPWM(channel, pwmPin, frequency, dutyCycle);\n}\n\nvoid loop() {}<\/pre>\n<p>\n\n<p>The outputs for the various timers and channels are assigned to specific pins. For the boards discussed here, you will find the assignment in the pinout diagrams. Otherwise, take a look at the corresponding data sheet. If you upload the above program and connect an LED to the PWM pin, you will see how it lights up every second for 200 ms.   <\/p>\n<p>You would expect to automatically receive an inverted output at the T1C3N output (e.g., PB1 on the BlackPill). But this is not the case. <\/p>\n<p>You can call callback functions at the end of the period and\/or the DutyCyle to initiate further actions. Here is a small example:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"pwm_with_callback.ino\" data-enlighter-title=\"pwm_with_callback.ino\">HardwareTimer Timer(TIM1);\n\nvoid setup() {\n    uint32_t channel = 3;\n    int pwmPin = PA10; \/\/ PA10 = D0 on the L432KC and D2 on the F446RE\n    uint32_t frequency = 1;\n    uint32_t dutyCycle = 20; \/\/ 20 %\n\n    Serial.begin(115200);\n    Timer.setPWM(channel, pwmPin, frequency, dutyCycle, periodCompleted, compareMatch);\n}\n\nvoid loop() {}\n\nvoid periodCompleted(){\n    Serial.print(\"Hi\");\n}\n\nvoid compareMatch(){\n    Serial.println(\"Ho\");\n}<\/pre>\n<p>\n\n<p>You have a little more options with this variant:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">HardwareTimer Timer(TIM1);\n\nvoid setup() {\n    \/\/  uint32_t compareValue = 2000;\n    Timer.setPrescaleFactor(10000); \/\/ -&gt; 10 KHz f\u00fcr BlackPill (STMF411); adjust to your board\n    Timer.setOverflow(10000); \/\/ -&gt; 1 Hz        \n    Timer.setMode(3, TIMER_OUTPUT_COMPARE_PWM1, PA10); \/\/ channel, mode, pwmPin\n    Timer.setCaptureCompare(3, 20, PERCENT_COMPARE_FORMAT); \/\/ channel, duty cycle (%), format \n    \/\/  Timer.setCaptureCompare(3, compareValue); \/\/ channel, compareValue\n    Timer.resume();\n}\n\nvoid loop() {}<\/pre>\n<p>\n\n<p>TIMER_OUTPUT_COMPARE_PWM1 is one of the possible timer modes. These modes are defined as enum (TimerModes_t) in HardwareTimer.h. Look there if you want to try the other modes.  &nbsp;<\/p>\n<p>PERCENT_COMPARE_FORMAT is one of the possible compare formats. These are defined in the enum <code>TimerCompareFormat_t<\/code> in HardwareTimer.h. If you do not pass a format to <code>setCaptureCompare()<\/code>, then TICK_COMPARE_FORMAT is used as the default. In this case, you pass the compare value as an absolute counter value.   <\/p>\n\n<h3 class=\"wp-block-heading\">PWM with the Arduino GIGA R1 WIFI<\/h3>\n\n<p>For PWM on the Arduino GIGA R1 WIFI, you can use the <a href=\"https:\/\/github.com\/khoih-prog\/Portenta_H7_PWM\" target=\"_blank\" rel=\"noopener\">Portenta_H7_PWM<\/a> and <a href=\"https:\/\/github.com\/khoih-prog\/Portenta_H7_Slow_PWM\" target=\"_blank\" rel=\"noopener\">Portenta_H7_Slow_PWM<\/a> libraries. However, as described above for the Portenta_H7_TimerInterrupt library, you must also add the board definition for the Arduino GIGA R1 WIFI. <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"ADC\">Analog-to-digital converter (ADC)<\/h2>\n\n<p>Next, let&#8217;s take a look at the ADC of the STM32 boards. Please note the following regarding the reference voltage: <\/p>\n<ul>\n<li>The maximum resolution is 12 bits. The BluePill and BlackPill boards do not have a VREF pin. VDD is the standard reference.  <\/li>\n<li>Although there is a VREF pin on the Nucleo-L432KC, you can only tap the reference voltage there, but not apply it.<\/li>\n<li>An external reference can be used with the Nucleo-F446RE if the solder bridges are set accordingly.<\/li>\n<\/ul>\n<p>For programming you can use the usual Arduino functions. For the Arduino GIGA R1 WIFI board there is the <a href=\"https:\/\/github.com\/arduino-libraries\/Arduino_AdvancedAnalog\" target=\"_blank\" rel=\"noopener\">Arduino_AdvanceAnalog<\/a> library, which supports the use of its three ADCs. <\/p>\n\n<h3 class=\"wp-block-heading\">Testing the accuracy of the ADC<\/h3>\n\n<p>I applied voltages between 0 and 3.3 volts, converted them with analogRead() and checked the result with my multimeter. I know from my multimeter that it is very precise. I averaged 50 readings at a time, which reduced the noise to a few millivolts.  <\/p>\n<p>Here is the sketch:  <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"adc_test.ino\" data-enlighter-title=\"adc_test.ino\">const float vRef = 3.3;\n\nvoid setup() {\n    Serial.begin(115200);\n    analogReadResolution(12);\n}\n\nvoid loop() {\n    unsigned long int rawVal = 0;\n    float voltage = 0.0;\n    for (int i=0; i&lt;50; i++) {\n        rawVal += analogRead(PA1);\n        delay(1);\n    }\n    voltage = (rawVal * vRef) \/ (4095 * 50);\n    Serial.print(\"Raw: \");\n    Serial.print(rawVal);\n    Serial.print(\"\\tVoltage [V]: \");\n    Serial.println(voltage, 3);\n    delay(1000);\n}<\/pre>\n<p>\n\n<p>And here is a typical output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_adc_test.png\"><img loading=\"lazy\" decoding=\"async\" width=\"559\" height=\"145\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_adc_test.png\" alt=\"Output adc_test.ino\" class=\"wp-image-24626\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_adc_test.png 559w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_adc_test-300x78.png 300w\" sizes=\"auto, (max-width: 559px) 100vw, 559px\" \/><\/a><figcaption class=\"wp-element-caption\">Output adc_test.ino<\/figcaption><\/figure>\n\n<p>And here are the results over the entire voltage range of the BlackPill board:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/blackpill_adc_test.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1004\" height=\"519\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/blackpill_adc_test.png\" alt=\"STM32 ADC Test: BlackPill\" class=\"wp-image-24495\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/blackpill_adc_test.png 1004w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/blackpill_adc_test-300x155.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/blackpill_adc_test-768x397.png 768w\" sizes=\"auto, (max-width: 1004px) 100vw, 1004px\" \/><\/a><figcaption class=\"wp-element-caption\">ADC test with a blackpill board (STM32F411)<\/figcaption><\/figure>\n\n<p>The result looks pretty good. The linearity is perfect. If you look closely, you can see that the slope of the ADC line is a little bit lower than that of the multimeter line. The reason was the operating voltage of the BlackPill board, which was not 3.3 volts but 3.311 volts. After adjusting <code>vref<\/code> in the sketch above, the lines were even better aligned.    <\/p>\n<p>Similarly good results were achieved with the BluePill board (STM32F103C8) and the Nucleo-L432KC.&nbsp;<\/p>\n\n<h2 class=\"wp-block-heading\" id=\"DAC\">Digital-to-analog converter DAC<\/h2>\n\n<p>Normally, when using <code>analogWrite()<\/code>, you generate a PWM signal. However, if you apply <code>analogWrite()<\/code> to a DAC pin, you will automatically receive a real analog signal. DAC outputs can be found on the Nucleo boards and the Arduino GIGA R1 WIFI, but not on BluePill and BlackPill.  <\/p>\n\n<p>Use the following sketch to enter raw DAC values and output the calculated voltages:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"dac_test.ino\" data-enlighter-title=\"dac_test.ino\">void setup() {\n    Serial.begin(115200);\n    analogWriteResolution(12); \/\/ 12 bit resolution\n}\n\nvoid loop() {\n    float vref = 3.3;\n    if(Serial.available()){\n        int rawVoltage = Serial.parseInt();\n        analogWrite(A3, rawVoltage);\n        Serial.print(rawVoltage);\n        Serial.print(\" -&gt; \");\n        float voltage = (vref * rawVoltage \/ 4095.0);\n        Serial.print(voltage, 3);\n        Serial.println(\" [V] (calc.)\");\n    }\n}<\/pre>\n<p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_dac_check.png\"><img loading=\"lazy\" decoding=\"async\" width=\"581\" height=\"116\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_dac_check.png\" alt=\"Output dac_test.ino\" class=\"wp-image-24587\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_dac_check.png 581w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_dac_check-300x60.png 300w\" sizes=\"auto, (max-width: 581px) 100vw, 581px\" \/><\/a><figcaption class=\"wp-element-caption\">Output dac_test.ino<\/figcaption><\/figure>\n\n<p>I used the sketch to compare the calculated voltage with the actual voltage. Here is the result:&nbsp;<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1-1024x514.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"514\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1-1024x514.png\" alt=\"STM32 DAC test: Nucleo-L432KC\" class=\"wp-image-24633\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1-1024x514.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1-300x151.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1-768x385.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/nucleo_dac_test-1.png 1090w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">DAC output: Measured values vs. calculated values<\/figcaption><\/figure>\n\n<p>Over a wide range, the actual values match the calculated values perfectly. Only at the ends does the curve bend away from the actual values. I was unable to generate voltages lower than 0.045 volts (<code>analogWrite(0)<\/code>) or higher than 3.28 volts (<code>analogWrite(4095)<\/code>). Apart from that, I was very satisfied with the result. If you want to be even more precise, measure the actual board voltage at (V)REF and correct <code>vref<\/code> in the sketch above accordingly.    <\/p>\n<p>I was able to generate voltages between 0.02 volts and 3.226 volts on the Arduino GIGA R1 WIFI. However, the board voltage here was only 3.262 volts. <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"EXTI\">External interrupts<\/h2>\n\n<p>You can set up external interrupts (EXTI) on almost all GPIOs. You should only avoid PB2 (on the BlackPill), PC13, PC14, and PC15. <\/p>\n<p>However, there is one more restriction. You have 16 EXTI lines (0-15) at your disposal. However, the EXTI lines can only be assigned to pins whose number corresponds to the EXTI line. So EXTI<strong>0<\/strong> \u2192 PA<strong>0<\/strong>, PB<strong>0<\/strong>; EXTI<strong>1<\/strong> \u2192 PA<strong>1<\/strong>, PB<strong>1<\/strong>; etc. And you can only assign one interrupt per line. Consequence: You can set up interrupts on PA0 and PB1 in parallel, but not on PA0 and PB0.    <\/p>\n\n<p>I tried setting up four external interrupts with a BlackPill board. Each of the four pins is connected to GND via a pushbutton. <\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit-1024x335.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"335\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit-1024x335.png\" alt=\"STM32 - Example circuit for external interrupts\" class=\"wp-image-24511\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit-1024x335.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit-300x98.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit-768x251.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/interrupt_example_circuit.png 1281w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Circuit for interrupt example<\/figcaption><\/figure>\n\n<p>The pins are set to INPUT_PULLUP mode. The associated interrupts are triggered on the falling edge, i.e. when the buttons are pressed. The triggering pin is displayed on the serial monitor.   Here is the sketch:  <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"interrupt_example.ino\" data-enlighter-title=\"interrupt_example.ino\">\/* tested on a BlackPill (F411CE) *\/\nint intPin[4] = {PA4, PA1, PB3, PB6};\nvolatile bool keyPressed = false;\nvolatile int intKey = 0;\n\nvoid onKey0 () {\n    keyPressed = true;\n    intKey = 0;\n}\n\nvoid onKey1 () {\n    keyPressed = true;\n    intKey = 1;\n}\n\nvoid onKey2 () {\n    keyPressed = true;\n    intKey = 2;\n}\n\nvoid onKey3 () {\n    keyPressed = true;\n    intKey = 3;\n}\n\nvoid (*onKey[4])(void) = {onKey0, onKey1, onKey2, onKey3}; \n\nvoid setup() {\n    Serial.begin(115200);\n    for (int i=0; i&lt;4; i++) {\n        pinMode(intPin[i], INPUT_PULLUP);\n        attachInterrupt (digitalPinToInterrupt(intPin[i]), onKey[i], FALLING);\n        delay(10); \n    }\n}\n\nvoid loop() {\n    if (keyPressed) {\n        Serial.print(\"Key \");\n        Serial.print(intKey);\n        Serial.println(\" has been pressed\");\n        delay(500);\n        intKey = 0;\n        keyPressed = false;\n    }\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"I2C\">I<sup>2<\/sup>C<\/h2>\n\n<p>You can use two I\u00b2C interfaces on the BluePill (STM32F103C8). On the BlackPill (STM32F411), the Nucleo-L432KC, the Nucleo-F446RE and the Arduino GIGA R1 WIFI there are three interfaces. If you only use one interface and do not define anything else, then SDA and SCL are located on the following pins:  <\/p>\n<ul>\n<li>BluePill \/ BlackPill: <span style=\"color: #008000;\">SDA<\/span> &#8211; <span style=\"color: #008000;\">PB7<\/span> \/ <span style=\"color: #0000ff;\">SCL<\/span> &#8211; <span style=\"color: #0000ff;\">PB6<\/span><\/li>\n<li>Nucleo-L432KC: <span style=\"color: #008000;\">SDA<\/span> &#8211; <span style=\"color: #008000;\">PB_7<\/span> (D4) \/ <span style=\"color: #0000ff;\">SCL<\/span> &#8211; <span style=\"color: #0000ff;\">PB_6<\/span> (D5)&nbsp;<\/li>\n<li>Nucleo-F446RE: <span style=\"color: #008000;\">SDA<\/span> &#8211; <span style=\"color: #008000;\">PB_9<\/span> (D14) \/ <span style=\"color: #0000ff;\">SCL<\/span> &#8211; <span style=\"color: #0000ff;\">PB_8<\/span> (D15)&nbsp;<\/li>\n<li>Arduino GIGA R1 WIFI: <span style=\"color: #008000;\">SDA<\/span> &#8211; <span style=\"color: #008000;\">PB11<\/span> (D20) \/ <span style=\"color: #0000ff;\">SCL<\/span> &#8211; <span style=\"color: #0000ff;\">PH4<\/span> (D21)<\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">Illustrative example: MPU6000\/MPU6050\/MPU6500\/MPU9250<\/h3>\n\n<p>I would like to explain the use of I<sup>2<\/sup>C on the STM32 boards using practical examples. I have selected the MPU6000, MPU6050, MPU6500 and MPU9250 acceleration sensors for this purpose. The sketches presented here work for all four sensors. I refer to them collectively as &#8220;MPUXXXX&#8221;.   <\/p>\n\n<p>This is not intended to be an article about these sensors, but a brief explanation is certainly necessary so that we can concentrate on the relevant I<sup>2<\/sup>C functions straight away.<\/p>\n<ul>\n<li>The MPUXXXX are woken up by an entry in their Power Management Register 1 (MPUXXXX_PWR_MGMT_1) and then continuously measure the acceleration of its x, y and z axes.<\/li>\n<li>The acceleration values are 16-bit numbers that are read &#8220;in one go&#8221; from 6 registers, starting with the MPUXXXX_ACCEL_XOUT_H register (MSB of the acceleration in the x direction).<\/li>\n<li>The 6 bytes are then converted into the three acceleration values accX, accY and accZ.<\/li>\n<li>In the examples, the resolution is +\/- 2 g, i.e. 2<sup>14<\/sup> (= 16384) corresponds to one g.<\/li>\n<\/ul>\n\n<h4 class=\"wp-block-heading\">Connection<\/h4>\n\n<p>Here you can see two MPU6500\/MPU920 connected to I2C-1 (default) and I2C-2 of a BlackPillboard:<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-1024x452.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"452\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-1024x452.png\" alt=\"Two MPU6500\/9250 via I2C on the BlackPill\" class=\"wp-image-24662\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-1024x452.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-300x132.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-768x339.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3-1320x582.png 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/ic2_blackpill_mpu9250-3.png 1335w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Two MPU6500\/9250 via I2C on the BlackPill<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">One I\u00b2C device attched to the standard pins<\/h3>\n\n<p>If you use the standard interface with the standard pins, there are no surprises regarding the I2C functions:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"i2c_standard.ino\" data-enlighter-title=\"i2c_standard.ino\">#include \"Wire.h\" \n#define MPUXXXX_ADDR 0x68 \/\/ Alternatively set AD0 to HIGH  --&gt; Address = 0x69\n\/\/ MPUXXXX registers\n#define MPUXXXX_PWR_MGMT_1 0x6B\n#define MPUXXXX_ACCEL_XOUT_H 0x3B\n\nchar result[7]; \/\/ for formatted output\n\nvoid setup() {\n    Serial.begin(115200);\n    Wire.begin();\n    writeRegister(MPUXXXX_PWR_MGMT_1, 0x00); \/\/ wake up MPUXXXX\n}\n\nvoid loop() {\n    uint8_t data[6];\n    int16_t accX, accY, accZ;\n    \n    readRegister(MPUXXXX_ACCEL_XOUT_H, data, 6);\n    \n    accX = data[0] &lt;&lt; 8 | data[1];\n    accY = data[2] &lt;&lt; 8 | data[3]; \n    accZ = data[4] &lt;&lt; 8 | data[5]; \n   \n    Serial.print(\"AcX = \"); Serial.print(toStr(accX));\n    Serial.print(\" | AcY = \"); Serial.print(toStr(accY));\n    Serial.print(\" | AcZ = \"); Serial.println(toStr(accZ));\n\n    delay(1000);\n}\n\nvoid writeRegister(uint8_t reg, uint8_t data) {\n    Wire.beginTransmission(MPUXXXX_ADDR);\n    Wire.write(reg); \n    Wire.write(data); \n    Wire.endTransmission(true);\n}\n\nvoid readRegister(uint8_t reg, uint8_t* buffer, uint8_t length) {\n    Wire.beginTransmission(MPUXXXX_ADDR);\n    Wire.write(reg);\n    Wire.endTransmission(false);                             \n    Wire.requestFrom((uint8_t)MPUXXXX_ADDR, length);\n    for (int i=0; i&lt;length; i++){\n        buffer[i] = Wire.read();\n    }\n}\n\nchar* toStr(int16_t character) { \n  sprintf(result, \"%6d\", character);\n  return result;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>For the sake of completeness, here is a sample output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_standard.png\"><img loading=\"lazy\" decoding=\"async\" width=\"823\" height=\"75\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_standard.png\" alt=\"Output i2c_standard.ino\" class=\"wp-image-24541\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_standard.png 823w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_standard-300x27.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_standard-768x70.png 768w\" sizes=\"auto, (max-width: 823px) 100vw, 823px\" \/><\/a><figcaption class=\"wp-element-caption\">Output i2c_standard.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">One I\u00b2C device attached to alternative pins<\/h3>\n\n<p>If you want to use alternative I\u00b2C pins, you must create a TwoWire object and pass the pins to it. It does not matter whether these are alternative pins of the I2C <sup>standard<\/sup>interface or alternative interfaces.   <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"i2c_alternative_pins.ino\" data-enlighter-title=\"i2c_alternative_pins.ino\">#include \"Wire.h\" \n#define MPUXXXX_ADDR 0x68 \/\/ Alternatively set AD0 to HIGH  --&gt; Address = 0x69\n\/\/ MPUXXXX registers\n#define MPUXXXX_PWR_MGMT_1 0x6B\n#define MPUXXXX_ACCEL_XOUT_H 0x3B\n\nchar result[7]; \/\/ for formatted output\n\n\/* create a TwoWire object *\/\nTwoWire myWire(PB9, PB8); \/\/ SDA, SCL\n\nvoid setup() {\n    Serial.begin(115200);\n    myWire.begin();\n    writeRegister(MPUXXXX_PWR_MGMT_1, 0x00); \/\/ wake up MPUXXXX\n}\n\nvoid loop() {\n    uint8_t data[6];\n    int16_t accX, accY, accZ;\n    \n    readRegister(MPUXXXX_ACCEL_XOUT_H, data, 6);\n    \n    accX = data[0] &lt;&lt; 8 | data[1];\n    accY = data[2] &lt;&lt; 8 | data[3]; \n    accZ = data[4] &lt;&lt; 8 | data[5]; \n   \n    Serial.print(\"AcX = \"); Serial.print(toStr(accX));\n    Serial.print(\" | AcY = \"); Serial.print(toStr(accY));\n    Serial.print(\" | AcZ = \"); Serial.println(toStr(accZ));\n\n    delay(1000);\n}\n\nvoid writeRegister(uint8_t reg, uint8_t data) {\n    myWire.beginTransmission(MPUXXXX_ADDR);\n    myWire.write(reg); \n    myWire.write(data); \n    myWire.endTransmission(true);\n}\n\nvoid readRegister(uint8_t reg, uint8_t* buffer, uint8_t length) {\n    myWire.beginTransmission(MPUXXXX_ADDR);\n    myWire.write(reg);\n    myWire.endTransmission(false);                             \n    myWire.requestFrom((uint8_t)MPUXXXX_ADDR, length);\n    for (int i=0; i&lt;length; i++){\n        buffer[i] = myWire.read();\n    }\n}\n\nchar* toStr(int16_t character) { \n  sprintf(result, \"%6d\", character);\n  return result;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 class=\"wp-block-heading\">Using multiple I\u00b2C interfaces<\/h3>\n\n<p>If you want to use several I<sup>2<\/sup>C interfaces, you must create TwoWire objects for each one. If you define functions that use the TwoWire objects (such as <code>writeRegister()<\/code> and <code>readRegister()<\/code>), then you pass the TwoWire objects as a pointer or as a reference. <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"i2c_two_interfaces.ino\" data-enlighter-title=\"i2c_two_interfaces.ino\">#include \"Wire.h\" \n#define MPUXXXX_ADDR 0x68 \/\/ Alternatively set AD0 to HIGH  --&gt; Address = 0x69\n\/\/ MPUXXXX registers\n#define MPUXXXX_PWR_MGMT_1 0x6B\n#define MPUXXXX_ACCEL_XOUT_H 0x3B\n\nchar result[7]; \/\/ for formatted output\n\n\/* create two TwoWire instances *\/\nTwoWire Wire_1(PB7, PB6); \/\/ you could also use the default Wire object instead\nTwoWire Wire_2(PB3, PB10);\n\nvoid setup() {\n    Serial.begin(115200);\n    Wire_1.begin();\n    Wire_2.begin();\n    writeRegister(&amp;Wire_1, MPUXXXX_PWR_MGMT_1, 0x00); \/\/ wake up MPUXXXX-1\n    writeRegister(&amp;Wire_2, MPUXXXX_PWR_MGMT_1, 0x00); \/\/ wake up MPUXXXX-2\n}\n\nvoid loop() {\n    uint8_t data[6];\n    int16_t accX_1, accY_1, accZ_1, accX_2, accY_2, accZ_2;\n    \n    readRegister(&amp;Wire_1, MPUXXXX_ACCEL_XOUT_H, data, 6);\n    \n    accX_1 = (data[0] &lt;&lt; 8) | data[1];\n    accY_1 = (data[2] &lt;&lt; 8) | data[3];\n    accZ_1 = (data[4] &lt;&lt; 8) | data[5];\n\n    Serial.print(\"AcX_1 = \"); Serial.print(toStr(accX_1));\n    Serial.print(\" | AcY_1 = \"); Serial.print(toStr(accY_1));\n    Serial.print(\" | AcZ_1 = \"); Serial.println(toStr(accZ_1));\n    Serial.flush();\n\n    readRegister(&amp;Wire_2, MPUXXXX_ACCEL_XOUT_H, data, 6);\n    \n    accX_2 = (data[0] &lt;&lt; 8) | data[1];\n    accY_2 = (data[2] &lt;&lt; 8) | data[3];\n    accZ_2 = (data[4] &lt;&lt; 8) | data[5];\n\n    Serial.print(\"AcX_2 = \"); Serial.print(toStr(accX_2));\n    Serial.print(\" | AcY_2 = \"); Serial.print(toStr(accY_2));\n    Serial.print(\" | AcZ_2 = \"); Serial.println(toStr(accZ_2));\n    Serial.println();\n\n    delay(1000);\n}\n\nvoid writeRegister(TwoWire *_wire, uint8_t reg, uint8_t data) {\n    _wire-&gt;beginTransmission(MPUXXXX_ADDR);\n    _wire-&gt;write(reg); \n    _wire-&gt;write(data); \n    _wire-&gt;endTransmission(true);\n}\n\nvoid readRegister(TwoWire *_wire, uint8_t reg, uint8_t* buffer, uint8_t length) {\n    _wire-&gt;beginTransmission(MPUXXXX_ADDR);\n    _wire-&gt;write(reg);\n    _wire-&gt;endTransmission(false);                             \n    _wire-&gt;requestFrom((uint8_t)MPUXXXX_ADDR, length);\n    for (int i=0; i&lt;length; i++){\n        buffer[i] = _wire-&gt;read();\n    }\n}\n\nchar* toStr(int16_t character) { \n  sprintf(result, \"%6d\", character);\n  return result;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>Here is the corresponding output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_two_interfaces.png\"><img loading=\"lazy\" decoding=\"async\" width=\"589\" height=\"157\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_two_interfaces.png\" alt=\"Output i2c_two_interfaces.ino\" class=\"wp-image-24544\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_two_interfaces.png 589w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_i2c_two_interfaces-300x80.png 300w\" sizes=\"auto, (max-width: 589px) 100vw, 589px\" \/><\/a><figcaption class=\"wp-element-caption\">Output i2c_two_interfaces.ino<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">Using libraries<\/h3>\n\n<p>Often, you will use ready-made libraries to control your I\u00b2C devices, such as my <a href=\"https:\/\/github.com\/wollewald\/MPU9250_WE\" target=\"_blank\" rel=\"noopener\">MPU9250_WE<\/a> library. Most libraries for I<sup>2<\/sup>C devices allow you to pass TwoWire objects. This would then look something like this:  <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-linenumbers=\"false\">TwoWire Wire_1(PB7, PB6);\nTwoWire Wire_2(PB3, PB10);\nMPU9250_WE myMPU9250_1 = MPU9250_WE(&amp;Wire_1);\nMPU9250_WE myMPU9250_2 = MPU9250_WE(&amp;Wire_2);<\/pre>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"SPI\">SPI<\/h2>\n\n<p>Everything is similar for SPI. Two SPI interfaces are available on the BluePill board (STM32F103C8), the Nucleo-L432KC and the Arduino GIGA R1 WIFI. There are four on the BlackPill (STM32F411) and the Nucleo-F446RE. The standard pins are:   <\/p>\n<ul>\n<li>BluePill \/ BlackPill: <span style=\"color: #008000;\">SCLK &#8211; PA5<\/span> \/ <span style=\"color: #0000ff;\">MISO &#8211; PA6<\/span> \/ <span style=\"color: #993300;\">MOSI &#8211; PA7<\/span><\/li>\n<li>Nucleo-L432KC: <span style=\"color: #008000;\">SCLK &#8211; PB_3<\/span> (D13) \/ <span style=\"color: #0000ff;\">MISO &#8211; PB_4<\/span> (D12) \/ <span style=\"color: #993300;\">MOSI &#8211; PB_5<\/span> (D11)<\/li>\n<li>Nucleo-F446RE: <span style=\"color: #008000;\">SCLK &#8211; PA_5<\/span> (D13) \/ <span style=\"color: #0000ff;\">MISO &#8211; PA_6<\/span> (D12) \/ <span style=\"color: #993300;\">MOSI &#8211; PA_7<\/span> (D11)<\/li>\n<li>Arduino GIGA R1 WIFI: <span style=\"color: #008000;\">SCLK &#8211; PH6<\/span> (D13) \/ <span style=\"color: #0000ff;\">MISO &#8211; PJ11<\/span> (D12) \/ <span style=\"color: #993300;\">MOSI &#8211; PJ10<\/span> (D11)<\/li>\n<\/ul>\n\n<p>Here are two MPU6500\/MPU9250 on SPI1 (default) and SPI2 of a BlackPill board:<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-1024x502.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"502\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-1024x502.png\" alt=\"STM32 - Two MPU6500\/9250 connected to the BlackPill via SPI\" class=\"wp-image-24536\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-1024x502.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-300x147.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-768x376.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250-1320x647.png 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/spi_blackpill_mpu9250.png 1531w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Two MPU6500\/9250 connected to the BlackPill via SPI<\/figcaption><\/figure>\n\n<h3 class=\"wp-block-heading\">One SPI device on the standard pins<\/h3>\n\n<p>The sketch for using the standard pins contains no surprises:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"spi_standard.ino\" data-enlighter-title=\"spi_standard.ino\">#include &lt;SPI.h&gt;\n\/\/ MPUXXXX registers\n#define MPUXXXX_PWR_MGMT_1 0x6B\n#define MPUXXXX_ACCEL_XOUT_H 0x3B\n\nchar result[7]; \/\/ for formatted output\nconst int CS_PIN = PA4;  \/\/ Chip Select\n\n\nvoid setup() {\n    Serial.begin(115200);\n  \n    pinMode(CS_PIN, OUTPUT);\n    digitalWrite(CS_PIN, HIGH); \n    SPI.begin();\n\n    writeRegister(MPUXXXX_PWR_MGMT_1, 0x00); \/\/ wake up MPUXXXX\n}\n\nvoid loop() {\n    uint8_t data[6];\n    int16_t accX, accY, accZ;\n    readRegisters(MPUXXXX_ACCEL_XOUT_H, data, 6);\n\n    accX = (data[0] &lt;&lt; 8) | data[1];\n    accY = (data[2] &lt;&lt; 8) | data[3];\n    accZ = (data[4] &lt;&lt; 8) | data[5];\n\n    Serial.print(\"AcX = \"); Serial.print(toStr(accX));\n    Serial.print(\" | AcY = \"); Serial.print(toStr(accY));\n    Serial.print(\" | AcZ = \"); Serial.println(toStr(accZ));\n\n    delay(1000);\n}\n\nvoid writeRegister(uint8_t reg, uint8_t data) {\n    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));\n    digitalWrite(CS_PIN, LOW);\n    SPI.transfer(reg &amp; 0x7F); \/\/ write access (MSB = 0)\n    SPI.transfer(data);\n    digitalWrite(CS_PIN, HIGH);\n}\n\nvoid readRegisters(uint8_t reg, uint8_t* buffer, uint8_t length) {\n    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));\n    digitalWrite(CS_PIN, LOW);\n    SPI.transfer(reg | 0x80); \/\/ read access (MSB = 1)\n    for (uint8_t i = 0; i &lt; length; i++) {\n        buffer[i] = SPI.transfer(0x00); \/\/ send dummy byte        \n    }\n    digitalWrite(CS_PIN, HIGH);\n}\n\nchar* toStr(int16_t value) {\n    sprintf(result, \"%6d\", value);\n    return result;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h3 class=\"wp-block-heading\">One SPI device on alternative pins <\/h3>\n\n<p>If you want to use alternative pins, you must set them with <code>SPI.setMOSI()<\/code>, <code>SPI.setMISO()<\/code> and <code>SPI.setSCLK()<\/code>. For example, for the BlackPill board: <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.....\nconst int CS_PIN = PA15;  \/\/ Chip Select\n\nvoid setup() {\n    Serial.begin(115200);\n  \n    pinMode(CS_PIN, OUTPUT);\n    digitalWrite(CS_PIN, HIGH); \n    SPI.setMOSI(PB5); \/\/ MOSI1-1\n    SPI.setMISO(PB4); \/\/ MISO1-3\n    SPI.setSCLK(PB3); \/\/ SCK1-3\n    SPI.begin();\n.....<\/pre>\n<p>\n\n<p>It works the same way if you want to use an alternative SPI interface. Here, for example, on the SPI2 pins of a BlackPill board:   <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">.....\nconst int CS_PIN = PB9;  \/\/ Chip Select\n\nvoid setup() {\n    Serial.begin(115200);\n  \n    pinMode(CS_PIN, OUTPUT);\n    digitalWrite(CS_PIN, HIGH); \n    SPI.setMOSI(PB15); \/\/ MOSI2\n    SPI.setMISO(PB14); \/\/ MISO2\n    SPI.setSCLK(PB13); \/\/ SCK2-4\n    SPI.begin();\n.....<\/pre>\n<p>\n\n<h3 class=\"wp-block-heading\">Using multiple SPI interfaces<\/h3>\n\n<p>If you want to use several SPI interfaces, you must create your own SPI objects to which you pass the SPI pins with the constructor. Strangely enough, you still have to define the SPI pins again using the functions <code>setSCLK()<\/code>, <code>setMISO()<\/code> and <code>setMOSI()<\/code>. <\/p>\n<p>Here is an example that works on both the BlackPill and BluePill boards:<\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"spi_two_interfaces.ino\" data-enlighter-title=\"spi_two_interfaces.ino\">#include &lt;SPI.h&gt;\n\/\/ MPU6500 registers\n#define MPU6500_PWR_MGMT_1 0x6B\n#define MPU6500_ACCEL_XOUT_H 0x3B\n\nchar result[7]; \/\/ for formatted output\nconst int CS_PIN_1 = PA4;  \/\/ chip select MPU6500-1\nconst int CS_PIN_2 = PB9;  \/\/ chip select MPU6500-2\n\n\/* create two SPI instances *\/\nSPIClass SPI_1(PA5, PA6, PA7); \/\/ SCLK, MISO, MOSI\nSPIClass SPI_2(PB13, PB14, PB15); \/\/ SCLK, MISO, MOSI\n  \n\nvoid setup() {\n    Serial.begin(115200);\n    SPI_1.setMOSI(PA7);\n    SPI_1.setMISO(PA6);\n    SPI_1.setSCLK(PA5);\n    SPI_2.setMOSI(PB15);\n    SPI_2.setMISO(PB14);\n    SPI_2.setSCLK(PB13);\n\n    pinMode(CS_PIN_1, OUTPUT);\n    digitalWrite(CS_PIN_1, HIGH); \n    pinMode(CS_PIN_2, OUTPUT);\n    digitalWrite(CS_PIN_2, HIGH);\n    SPI_1.begin(); \n    SPI_2.begin();\n\n    writeRegister(&amp;SPI_1, CS_PIN_1, MPU6500_PWR_MGMT_1, 0x00); \/\/ wake up MPU6500-1\n    writeRegister(&amp;SPI_2, CS_PIN_2, MPU6500_PWR_MGMT_1, 0x00); \/\/ wake up MPU6500-2\n}\n\nvoid loop() {\n    uint8_t data[6];\n    int16_t accX_1, accY_1, accZ_1, accX_2, accY_2, accZ_2;\n    readRegisters(&amp;SPI_1, CS_PIN_1, MPU6500_ACCEL_XOUT_H, data, 6);\n\n    accX_1 = (data[0] &lt;&lt; 8) | data[1];\n    accY_1 = (data[2] &lt;&lt; 8) | data[3];\n    accZ_1 = (data[4] &lt;&lt; 8) | data[5];\n\n    Serial.print(\"AcX_1 = \"); Serial.print(toStr(accX_1));\n    Serial.print(\" | AcY_1 = \"); Serial.print(toStr(accY_1));\n    Serial.print(\" | AcZ_1 = \"); Serial.println(toStr(accZ_1));\n    Serial.flush();\n\n    readRegisters(&amp;SPI_2, CS_PIN_2, MPU6500_ACCEL_XOUT_H, data, 6);\n    \n    accX_2 = (data[0] &lt;&lt; 8) | data[1];\n    accY_2 = (data[2] &lt;&lt; 8) | data[3];\n    accZ_2 = (data[4] &lt;&lt; 8) | data[5];\n\n    Serial.print(\"AcX_2 = \"); Serial.print(toStr(accX_2));\n    Serial.print(\" | AcY_2 = \"); Serial.print(toStr(accY_2));\n    Serial.print(\" | AcZ_2 = \"); Serial.println(toStr(accZ_2));\n    Serial.println();\n\n    delay(1000);\n}\n\nvoid writeRegister(SPIClass *_spi, int _csPin, uint8_t reg, uint8_t data) {\n    _spi-&gt;beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));\n    digitalWrite(_csPin, LOW);\n    _spi-&gt;transfer(reg &amp; 0x7F); \/\/ write access (MSB = 0)\n    _spi-&gt;transfer(data);\n    digitalWrite(_csPin, HIGH);\n}\n\nvoid readRegisters(SPIClass *_spi, int _csPin, uint8_t reg, uint8_t* buffer, uint8_t length) {\n    _spi-&gt;beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));\n    digitalWrite(_csPin, LOW);\n    _spi-&gt;transfer(reg | 0x80); \/\/ read access (MSB = 1)\n    for (uint8_t i = 0; i &lt; length; i++) {\n        buffer[i] = _spi-&gt;transfer(0x00); \/\/ send dummy byte        \n    }\n    digitalWrite(_csPin, HIGH);\n}\n\nchar* toStr(int16_t value) {\n    sprintf(result, \"%6d\", value);\n    return result;\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"hardware_serial\">HardwareSerial<\/h2>\n\n<p>In the previous post, I showed you how to activate the &#8220;standard Serial&#8221; and ensure that you can access the serial monitor via USB. But the STM32 boards have several hardware serial interfaces that you can use. <\/p>\n<ul>\n<li>BluePill (STM32F103C8) \/ BlackPill (STM32F411): <strong>3<\/strong><\/li>\n<li>Nucleo-L432KC: <strong>2 ( <\/strong>plus standard serial via USB)<\/li>\n<li>Nucleo-F446RE: <strong>6 ( <\/strong>plus standard serial via USB)<\/li>\n<li>Arduino GIGA R1 WIFI: <strong>4 ( <\/strong>plus standard serial via USB)<\/li>\n<\/ul>\n<p>Using the additional HardwareSerial interfaces is simple. In a practical example, we let a BluePill and a BlackPill board communicate via HardwareSerial. <\/p>\n<p>Here is the circuit:<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-1024x412.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"412\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-1024x412.png\" alt=\"Hardware Serial Connection: BluePill and BlackPill\" class=\"wp-image-24691\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-1024x412.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-300x121.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-768x309.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1-1320x531.png 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/HardwareSerial-1.png 1416w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Hardware Serial Connection: BluePill and BlackPill<\/figcaption><\/figure>\n\n<p>And here is the sketch that you upload to both boards:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"hardware_serial.ino\" data-enlighter-title=\"hardware_serial.ino\">HardwareSerial mySerial(PA3, PA2); \/\/ RX, TX\n\nvoid setup() {\n    Serial.begin(115200);\n    mySerial.begin(115200);\n}\n\nvoid loop() { \n    if (mySerial.available()) {\n        Serial.write(mySerial.read());\n    }\n    if (Serial.available()) {\n        mySerial.write(Serial.read());\n    }\n}<\/pre>\n<p>\n\n<p>For each sketch you open a serial monitor and can then send messages from one monitor to the other.<\/p>\n\n<p><strong>Additional information<\/strong>: The standard serial interface of the Nucleo boards is provided by the virtual COM port of the on-board ST-LINK programmer.<\/p>\n\n<h4 class=\"wp-block-heading\">Peculiarities of the Arduino GIGA R1 WIFI<\/h4>\n\n<p>If you use the Arduino GIGA R1 WIFI with the Arduino board package, the hardware serial objects and its pins are already predefined. Serial1 is assigned to RX0\/TX0, Serial2 is assigned to RX1\/TX1 and so on. &nbsp;<\/p>\n\n<h2 class=\"wp-block-heading\" id=\"software_serial\">SoftwareSerial<\/h2>\n\n<p>The Arduino core package from stm32duino contains a SoftwareSerial library. But if possible, you should always work with the HardwareSerial interfaces. This is generally safer and saves resources.  <\/p>\n<p>In addition, in my experience, SoftwareSerial on the STM32 boards only runs without errors at low baud rates. On my BluePill and BlackPill boards, it only worked up to 57600, while the limit of the Nucleo-L432KC was 38400.   <\/p>\n<p>Nevertheless, here is an example sketch:<\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">#include &lt;SoftwareSerial.h&gt;\nSoftwareSerial mySerial(A5, A6); \/\/ RX, TX\n\nvoid setup() {\n    Serial.begin(9600);\n    mySerial.begin(9600);\n}\n\nvoid loop() { \n    if (mySerial.available()) {\n        Serial.write(mySerial.read());\n    }\n    if (Serial.available()) {\n        mySerial.write(Serial.read());\n    }\n}<\/pre>\n<p>\n\n<h2 class=\"wp-block-heading\" id=\"IWDG\">Independent Watchdog (IWDG)<\/h2>\n\n<p>The STM32 microcontrollers have an independent watchdog (IWDG) and a window watchdog (WWDG). The IWDG is called independent because it runs completely independently of the rest of the microcontroller. It is so independent that, once started, it can no longer be stopped by software. The IWDG is controlled by a clock generator that is independent of the system clock. All this makes it extremely robust.    <\/p>\n<p>The window watchdog got its name from the fact that it can only be &#8220;fed&#8221; in a specific window. WWDG and IWDG trigger a reset when the timeout is reached. However, only the WWDG triggers a reset if it is fed too early.  <\/p>\n<p>A time period during which the watchdog may not be fed can also be defined for the IWDG. However, premature feeding (the reload) does not lead to a reset but is simply ignored. However, this forbidden window cannot be activated on all STM32 boards.  <\/p>\n<p>The IWDG has its own class in the STM32 board package, which is easy to use. To use the WWDG, you need to go one level deeper into the HAL functions, which is beyond the scope of this article. <\/p>\n\n<h4 class=\"wp-block-heading\">IWDG example sketch <\/h4>\n\n<p>Take an STM32 board of your choice and connect a button to a suitable pin. Connect the other side of the button to GND, e.g. like this: &nbsp;<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test-1024x232.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"232\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test-1024x232.png\" alt=\"STM32 - IWDG Test\" class=\"wp-image-24701\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test-1024x232.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test-300x68.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test-768x174.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/iwdg_test.png 1147w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">IWDG test<\/figcaption><\/figure>\n\n<p>If you use a BlackPill board, you don&#8217;t need a button for the example sketch below. Instead, you can use the KEY button. It is connected between PA0 on one side and GND on the other. <\/p>\n<p>The example sketch triggers a reset after 10 seconds if the IWDG has not been fed by pressing the button. After a reset caused by the IWDG, the board LED flashes briefly five times. <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"iwdg_example.ino\" data-enlighter-title=\"iwdg_example.ino\">#include &lt;IWatchdog.h&gt;\nconst int buttonPin = PA0; \/\/ BlackPill KEY pin\nconst int ledPin = PC13; \/\/ Board LED\n\nvoid setup() {\n    pinMode(ledPin, OUTPUT);\n    digitalWrite(ledPin, HIGH); \/\/ BluePill\/BlackPill board LED off \n    pinMode(buttonPin, INPUT_PULLUP);\n \n    if (IWatchdog.isReset(true)) {\n        \/\/ LED blinks to indicate watchdog reset\n        for (uint8_t i = 0; i &lt; 5; i++) {\n            digitalWrite(ledPin, LOW);\n            delay(100);\n            digitalWrite(ledPin, HIGH);\n            delay(100);\n        }\n    }\n\n    \/\/ Init the watchdog timer with 10 seconds timeout\n    IWatchdog.begin(10000000); \/\/ timeout is uint32_t\n    \/\/ 10 s timeout, but ignore reload with the first 5 s\n    \/\/ IWatchdog.begin(10000000, 5000000); \/\/ does not work with all STM32 MCUs\n\n    if (!IWatchdog.isEnabled()) {\n        \/\/ LED blinks indefinitely\n        while (1) {\n            digitalWrite(ledPin, LOW);\n            delay(500);\n            digitalWrite(ledPin, HIGH);\n            delay(500);\n        }\n  }\n}\n\nvoid loop() {\n    \/\/ Compare current button state of the pushbutton value:\n    if (digitalRead(buttonPin) == LOW) {\n        digitalWrite(ledPin, LOW);\n        delay(1000);\n        digitalWrite(ledPin, HIGH);\n        \/\/ Uncomment to change timeout value to 6 seconds\n        \/\/IWatchdog.set(6000000);\n\n        \/\/ Reload the watchdog only when the button is pressed\n        IWatchdog.reload();\n    } \n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<h5 class=\"wp-block-heading\">Explanations for the example sketch:<\/h5>\n\n<p>The IWDG object is called IWatchdog and is predefined. Use <code>isReset()<\/code> to check whether the IWDG has triggered a reset. If you pass <code>true<\/code> to the function, the reset flag is deleted. If you pass nothing or <code>false<\/code>, the reset flag remains set.   <\/p>\n<p>You start the IWDG with <code>begin()<\/code>. As a parameter, you pass the timeout in microseconds. The optional second parameter defines the length of the &#8220;forbidden&#8221; window in which feeding is ignored. As already mentioned, this does not work with all STM32 boards.   <\/p>\n<p>Other functions used are:<\/p>\n<ul>\n<li><code>isEnabled()<\/code> checks whether the IWDG is active.<\/li>\n<li><code>set()<\/code>&nbsp;allows you to redefine the timeout.<\/li>\n<li>Use <code>reload()<\/code> to feed the watchdog so that the timeout period starts again.&nbsp;<\/li>\n<\/ul>\n\n<h2 class=\"wp-block-heading\" id=\"RTC\">Real Time Clock (RTC)<\/h2>\n\n<p>The STM32 boards and the underlying microcontrollers have a real-time clock. The time is therefore not only measured (in principle, every microcontroller can do this), but also processed as year, month, day, hour, minute, and second. <\/p>\n<p>The stm32duino board package does not have an integrated RTC library. I recommend using <a href=\"https:\/\/github.com\/stm32duino\/STM32RTC\" target=\"_blank\" rel=\"noopener\">STM32duino RTC<\/a>.  You can install it via the library manager of the Arduino IDE.<\/p>\n<p>The board package for the Arduino GIGA R1 WIFI already has the RTC functions already integrated. You can find very helpful example sketches in the <a href=\"https:\/\/docs.arduino.cc\/tutorials\/giga-r1-wifi\/cheat-sheet\/\" target=\"_blank\" rel=\"noopener\">user manual<\/a>. In this article, I will only discuss the STM32duino_RTC library.  <\/p>\n<p>Every clock needs a clock source. You have three options for this: <\/p>\n<ul>\n<li><strong>LSI_CLOCK<\/strong> (Low Speed Internal Clock): internal, rather imprecise oscillator with approx. 32 kHz. This clock is preset in the library. &nbsp;<\/li>\n<li><strong>LSE_CLOCK<\/strong> (Low Speed External Clock): most STM32 boards have an external crystal that oscillates at 32768 Hz. These are usually very accurate. You should select this option.  <\/li>\n<li><strong>HSE_CLOCK<\/strong> (High-Speed External Clock): Uses the fast, external crystal on the board (usually 8 MHz).<\/li>\n<li>(HSI_CLOCK) (High-Speed Internal Clock): Fast, internal oscillator, rather inaccurate. Not supported by the library. <\/li>\n<\/ul>\n\n<h3 class=\"wp-block-heading\">RTC example sketch<\/h3>\n\n<p>The library contains a number of nice example sketches. To get started, I recommend SimpleRTC.ino.  I think the sketch is self-explanatory.  <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"SimpleRTC.ino\" data-enlighter-title=\"SimpleRTC.ino\">\/*\n  SimpleRTC\n\n  This sketch shows how to configure the RTC and to display\n  the date and time periodically\n\n  Creation 12 Dec 2017\n  by Wi6Labs\n  Modified 03 Jul 2020\n  by Frederic Pillon for STMicroelectronics\n\n  This example code is in the public domain.\n\n  https:\/\/github.com\/stm32duino\/STM32RTC\n*\/\n\n#include &lt;STM32RTC.h&gt;\n\n\/* Get the rtc object *\/\nSTM32RTC&amp; rtc = STM32RTC::getInstance();\n\n\/* Change these values to set the current initial time *\/\nconst byte seconds = 0;\nconst byte minutes = 0;\nconst byte hours = 16;\n\n\/* Change these values to set the current initial date *\/\n\/* Monday 15th June 2015 *\/\nconst byte weekDay = 1;\nconst byte day = 15;\nconst byte month = 6;\nconst byte year = 15;\n\nvoid setup()\n{\n  Serial.begin(9600);\n\n  \/\/ Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.\n  \/\/ By default the LSI is selected as source.\n  \/\/rtc.setClockSource(STM32RTC::LSE_CLOCK);\n\n  rtc.begin(); \/\/ initialize RTC 24H format\n\n  \/\/ Set the time\n  rtc.setHours(hours);\n  rtc.setMinutes(minutes);\n  rtc.setSeconds(seconds);\n\n  \/\/ Set the date\n  rtc.setWeekDay(weekDay);\n  rtc.setDay(day);\n  rtc.setMonth(month);\n  rtc.setYear(year);\n\n  \/\/ you can use also\n  \/\/rtc.setTime(hours, minutes, seconds);\n  \/\/rtc.setDate(weekDay, day, month, year);\n}\n\nvoid loop()\n{\n  \/\/ Print date...\n  Serial.printf(\"%02d\/%02d\/%02d \", rtc.getDay(), rtc.getMonth(), rtc.getYear());\n\n  \/\/ ...and time\n  Serial.printf(\"%02d:%02d:%02d.%03d\\n\", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());\n\n  delay(1000);\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>Here is the output:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_SimpleRTC.png\"><img loading=\"lazy\" decoding=\"async\" width=\"672\" height=\"109\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_SimpleRTC.png\" alt=\"Output of the library sketch SimpleRTC.ino\" class=\"wp-image-24571\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_SimpleRTC.png 672w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_SimpleRTC-300x49.png 300w\" sizes=\"auto, (max-width: 672px) 100vw, 672px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of the library sketch SimpleRTC.ino<\/figcaption><\/figure>\n\n<p>It is also worth going through the other example sketches in the library. Among other things, you will learn how to program an alarm interrupt. I also recommend taking a look at the README.md of the library, as all the functions are explained there.  <\/p>\n\n<p>What I missed in the library was a routine for setting the RTC according to the compile time. The RTC is then a few seconds behind, but you are already close to the current time. I have extended SimpleRTC.ino accordingly. You can find the sketch in the appendix.   <\/p>\n\n<h3 class=\"wp-block-heading\">How accurate is the RTC?<\/h3>\n\n<p>The accuracy of the RTC depends on the accuracy of its clock source. With the internal oscillator (LSI), the result was miserable. The deviation in my measurements was up to one second per minute! Things were no better with the HSI clock.   <\/p>\n<p>The results were much better with the external quartz (LSE). The deviations were in the range of a few seconds per day &#8211; not too bad. A DCF77 module or &#8211; especially for the Arduino GIGA R1 WIFI &#8211; an NTP server would be suitable for regular adjustment.  <\/p>\n<p>For the BluePill and BlackPill boards, it is often recommended to remove pins PC14 and PC15 on the pin header so that the external on-board crystal can oscillate undisturbed and therefore more accurately. However, I have not investigated this further. <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"low_power\">Low-power modes<\/h2>\n\n<p>If you want to save valuable power for battery-powered projects, there are various energy-saving modes available to you. In this regard, I recommend the <a href=\"https:\/\/github.com\/stm32duino\/STM32LowPower\" target=\"_blank\" rel=\"noopener\">STM32duino Low Power<\/a> library, which you can install via the library manager of the Arduino IDE. <\/p>\n\n<p>The available modes are:&nbsp;<\/p>\n<ul>\n<li><strong>idle()<\/strong>: Low wake-up latency (\u00b5s range). Memory and power supply are retained. Minimal energy savings mainly in the core itself.  <\/li>\n<li><strong>sleep()<\/strong>: Low wake-up latency (\u00b5s range). Memory and power supply are retained. Minimal energy savings mainly in the core itself, but higher than in idle mode.  <\/li>\n<li><strong>deepSleep()<\/strong>: Average wake-up latency (ms range). Clock frequencies are reduced. Memory and power supply are retained. If supported, peripheral wake-up is possible (UART, I2C &#8230;).   <\/li>\n<li><strong>shutdown()<\/strong>: High wake-up latency (possibly hundreds of ms). The power supply is interrupted (except in the &#8220;always-on&#8221; area), the memory content is lost, and the system is reset. <\/li>\n<\/ul>\n<p>The &#8220;wake-up methods&#8221; are:<\/p>\n<ul>\n<li>Defined time period,<\/li>\n<li>RTC alarm,<\/li>\n<li>external interrupt on the WakeUp pin,<\/li>\n<li>Peripheral event (UART, I2C etc.).<\/li>\n<\/ul>\n\n<p>The low-power library is equipped with some example sketches that illustrate the various wake-up methods. Here is an example of how to wake up the board after a defined period of time: <\/p>\n<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"deep_sleep_example.ino\" data-enlighter-title=\"deep_sleep_example.ino\">#include \"STM32LowPower.h\"\nconst unsigned long sleepTime = 2000; \/\/ sleeping time in ms\n\nvoid setup() {\n    pinMode(LED_BUILTIN, OUTPUT);\n    for(int i=0; i&lt;5; i++){\n        digitalWrite(LED_BUILTIN, LOW);\n        delay(50);\n        digitalWrite(LED_BUILTIN, HIGH);\n        delay(50);\n    }\n    LowPower.begin();\n}\n\nvoid loop() {\n    digitalWrite(LED_BUILTIN, LOW);\n    LowPower.deepSleep(sleepTime);\n    \/\/ LowPower.shutdown(sleepTime); \/\/ try this instead of deepSleep\n    digitalWrite(LED_BUILTIN, HIGH);\n    LowPower.deepSleep(sleepTime);\n}<\/pre>\n<p>\n\n<p>The sketch switches the board LED on and off every two seconds. In between, it goes into deepSleep mode. If you comment out line 17 and uncomment line 18, then it goes into shutdown mode instead, which ends with a reset. You can recognize the reset by the fact that <code>setup()<\/code>is executed and the LED flashes quickly.   &nbsp;<\/p>\n\n<h3 class=\"wp-block-heading\">Power consumption in sleep mode<\/h3>\n\n<p>I carried out a few current measurements in deepSleep and Shutdown mode with BluePill and BlackPill boards.<\/p>\n<p>As the power consumption of the power LED is particularly noticeable in shutdown mode, I first carried out the tests in this mode with the LED, then I removed the LED from the board and tested again.&nbsp;<\/p>\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-1024x188.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"188\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-1024x188.png\" alt=\"Power consumption of BluePill and BlackPill in DeepSleep and Shutdown mode\" class=\"wp-image-24721\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-1024x188.png 1024w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-300x55.png 300w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-768x141.png 768w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2-1320x242.png 1320w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/09\/low_power_test_results-2.png 1330w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Power consumption of BluePill and BlackPill in DeepSleep and Shutdown mode<\/figcaption><\/figure>\n\n<p>On Nucleo boards, the ST-LINK programmer consumes a significant amount of power. You can disconnect it by setting certian solder bridges, but I simply didn&#8217;t feel like trying it out. I also didn&#8217;t take a close look at the Arduino GIGA R1 WIFI in this respect.  <\/p>\n\n<h2 class=\"wp-block-heading\" id=\"appendix\">Appendix &#8211; Extended RTC sketch<\/h2>\n\n<p>I have extended the example sketch SimpleRTC.ino so that it sets the RTC according to the time of compilation. This time is stored in the character strings <code>__DATE__<\/code> and <code>__TIME__<\/code>. Example: &#8220;Aug 11 2025&#8221; and &#8220;22:17:25&#8221;. It is relatively easy to extract the year, day, hours, minutes and seconds from this. For example, __TIME__[1] is the second digit of the hours, but as an ASCII code (e.g. 2 = ASCII code 50). Therefore, the ASCII code of 0 (= 48) is substracted \u2192 __TIME__[1] &#8211; &#8216;0&#8217;.      <\/p>\n<p>The month is determined by a simple string comparison. Calculating the day of the week is a little more difficult. Thank goodness clever people have already thought about this. You find more on this topic in <a href=\"https:\/\/en.wikipedia.org\/wiki\/Determination_of_the_day_of_the_week\" target=\"_blank\" rel=\"noopener\">this Wikipedia article<\/a>. I use Tomohiko Sakamoto&#8217;s algorithm. However, it returns the days of the week as numbers from 0 (= Sun) to 6 (= Sat), whereas the STM32duino RTC-Lib defines numbers from 1 (= Mon) to 7 (= Sun). This is adjusted by line 41.      <\/p>\n<p>I then added a function to set the time more precisely. To do this, set &#8220;No end of line&#8221; in the serial monitor. Then enter the time in the format hh:mm:ss.  <\/p>\n<\/p>\n<div class=\"scroll-paragraph\">\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-group=\"rtc_advanced.ino\" data-enlighter-title=\"rtc_advanced.ino\">#include &lt;STM32RTC.h&gt;\n\n#define MONTH_IS(str) (__DATE__[0] == str[0] &amp;&amp; __DATE__[1] == str[1] &amp;&amp; __DATE__[2] == str[2])\n\n\/* get month from __DATE__ *\/\n#define BUILD_MONTH ( \\\n    MONTH_IS(\"Jan\") ?  1 : \\\n    MONTH_IS(\"Feb\") ?  2 : \\\n    MONTH_IS(\"Mar\") ?  3 : \\\n    MONTH_IS(\"Apr\") ?  4 : \\\n    MONTH_IS(\"May\") ?  5 : \\\n    MONTH_IS(\"Jun\") ?  6 : \\\n    MONTH_IS(\"Jul\") ?  7 : \\\n    MONTH_IS(\"Aug\") ?  8 : \\\n    MONTH_IS(\"Sep\") ?  9 : \\\n    MONTH_IS(\"Oct\") ? 10 : \\\n    MONTH_IS(\"Nov\") ? 11 : \\\n    MONTH_IS(\"Dec\") ? 12 : 0 )\n\n\/* get day and year from __DATE__ *\/\n#define BUILD_DAY  (((__DATE__[4] == ' ') ? 0 : __DATE__[4] - '0') * 10 + (__DATE__[5] - '0'))\n#define BUILD_YEAR  ((__DATE__[7]-'0') * 1000 + (__DATE__[8]-'0') * 100 + (__DATE__[9]-'0') * 10 + (__DATE__[10]-'0'))\n\/* get hour, minute and second from __TIME__ *\/\n#define BUILD_HOUR  ((__TIME__[0]-'0')*10 + (__TIME__[1]-'0'))\n#define BUILD_MIN   ((__TIME__[3]-'0')*10 + (__TIME__[4]-'0'))\n#define BUILD_SEC   ((__TIME__[6]-'0')*10 + (__TIME__[7]-'0'))\n\n#define SAKAMOTO_T(m) \\ \n  ((m)==1?0:(m)==2?3:(m)==3?2:(m)==4?5:(m)==5?0:(m)==6?3:\\\n   (m)==7?5:(m)==8?1:(m)==9?4:(m)==10?6:(m)==11?2:(m)==12?4:0)\n\n\/* Sunday = 0, ..., Saturday = 6 (Original-Sakamoto) *\/\n#define WD_SUN0(y,m,d) \\\n  (((((y) - ((m) &lt; 3))                                         \\\n    + (((y) - ((m) &lt; 3)) \/ 4)                                  \\\n    - (((y) - ((m) &lt; 3)) \/ 100)                                \\\n    + (((y) - ((m) &lt; 3)) \/ 400)                                \\\n    + SAKAMOTO_T(m) + (d)) % 7))\n\n\/* Needed for the STM32 RTC lib: Monday = 1, ..., Sunday = 7 *\/\n#define WD_MON1(y,m,d)  ((((WD_SUN0((y),(m),(d)) + 6) % 7) + 1))\n#define BUILD_WEEKDAY  WD_MON1(BUILD_YEAR, BUILD_MONTH, BUILD_DAY)\n\nchar daysOfTheWeek[7][12] = {\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"};\n\n\/* Get the rtc object *\/\nSTM32RTC&amp; rtc = STM32RTC::getInstance();\n\n\/* time and date variables *\/\nbyte seconds = BUILD_SEC;\nbyte minutes = BUILD_MIN;\nbyte hours = BUILD_HOUR;\nbyte weekDay = BUILD_WEEKDAY;\nbyte day = BUILD_DAY;\nbyte month = BUILD_MONTH;\nbyte year = BUILD_YEAR - 2000;\n\nvoid setup() {\n    Serial.begin(115200);\n\n    \/\/ Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.\n    \/\/ By default the LSI is selected as source.\n    rtc.setClockSource(STM32RTC::LSE_CLOCK);\n\n    rtc.begin(); \/\/ initialize RTC 24H format\n\n    \/\/ Set the time\n    rtc.setHours(hours);\n    rtc.setMinutes(minutes);\n    rtc.setSeconds(seconds);\n\n    \/\/ Set the date\n    rtc.setWeekDay(weekDay);\n    rtc.setDay(day);\n    rtc.setMonth(month);\n    rtc.setYear(year);\n\n    \/\/ you can use also\n    \/\/rtc.setTime(hours, minutes, seconds);\n    \/\/rtc.setDate(weekDay, day, month, year);\n}\n\nvoid loop() {\n    static unsigned long lastPrint = 0;\n    \n    if (millis() - lastPrint &gt;= 1000){\n        \/\/ Print date...\n        Serial.printf(\"%s %02d\/%02d\/%02d \", daysOfTheWeek[weekDay-1], rtc.getDay(), rtc.getMonth(), rtc.getYear());\n        \/\/ ...and time\n        Serial.printf(\"%02d:%02d:%02d.%03d\\n\", rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds());\n        lastPrint = millis();\n    }\n\n    if (Serial.available()){  \/\/ set serial monitor to \"No Line Ending\"\n        hours = Serial.parseInt();\n        minutes = Serial.parseInt();\n        seconds = Serial.parseInt();\n        rtc.setTime(hours, minutes, seconds);\n    }\n}<\/pre>\n<p>\u00a0<\/p>\n<\/div>\n<p>\n\n<p>The output should then look like this:<\/p>\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_rtc_advanced.png\"><img loading=\"lazy\" decoding=\"async\" width=\"538\" height=\"110\" src=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_rtc_advanced.png\" alt=\"Output of rtc_advanced.ino\" class=\"wp-image-24568\" srcset=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_rtc_advanced.png 538w, https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/08\/output_rtc_advanced-300x61.png 300w\" sizes=\"auto, (max-width: 538px) 100vw, 538px\" \/><\/a><figcaption class=\"wp-element-caption\">Output of rtc_advanced.ino<\/figcaption><\/figure>\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I will show you how to implement selected functions on STM32 boards using the Arduino Core package. I will cover timers, PWM, low power, I2C, SPI, ADC, DAC, RTC and more. <\/p>\n","protected":false},"author":1,"featured_media":24088,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[543],"tags":[1498,556,2746,2745,1499,2791,2781,2752,2782,2470,666,2784,691,2783,2748,2710,2789,2790,2787,2788,1970,983,2025,1565,2786,1503,2743,2780,2785],"class_list":["post-24780","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-boards-and-microcontrollers","tag-adc-en","tag-arduino-en-2","tag-blackpill-en","tag-bluepill-en","tag-dac-en","tag-deepsleep-en","tag-f446re-en","tag-giga-r1-wifi-en","tag-hardware-timer-en","tag-hardwareserial-en","tag-i2c-en","tag-independent-watchdog-en","tag-interrupt-en-2","tag-iwdg-en","tag-l432kc-en","tag-low-power-en-2","tag-lse_clock-en","tag-lsi_clock-en","tag-pc14-en","tag-pc15-en","tag-power-consumption-en","tag-pwm-en","tag-real-time-clock-en","tag-rtc-en","tag-solder-bridges-en","tag-spi-en","tag-stm32-en","tag-stm32f103c8-en","tag-wwdg-en"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste<\/title>\n<meta name=\"description\" content=\"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste\" \/>\n<meta property=\"og:description\" content=\"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions\" \/>\n<meta property=\"og:site_name\" content=\"Wolles Elektronikkiste\" \/>\n<meta property=\"article:published_time\" content=\"2025-09-10T18:11:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-09-10T18:11:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png\" \/>\n\t<meta property=\"og:image:width\" content=\"984\" \/>\n\t<meta property=\"og:image:height\" content=\"984\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Wolfgang Ewald\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Wolfgang Ewald\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"40 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions\"},\"author\":{\"name\":\"Wolfgang Ewald\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"headline\":\"STM32 boards &#8211; Part 2: Selected functions\",\"datePublished\":\"2025-09-10T18:11:12+00:00\",\"dateModified\":\"2025-09-10T18:11:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions\"},\"wordCount\":5251,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/post_image_stm32.png\",\"keywords\":[\"ADC\",\"Arduino\",\"BlackPill\",\"BluePill\",\"DAC\",\"DeepSleep\",\"F446RE\",\"Giga R1 WiFi\",\"Hardware timer\",\"HardwareSerial\",\"I2C\",\"Independent Watchdog\",\"Interrupt\",\"IWDG\",\"L432KC\",\"low power\",\"LSE_CLOCK\",\"LSI_CLOCK\",\"PC14\",\"PC15\",\"power consumption\",\"PWM\",\"Real-time clock\",\"RTC\",\"Solder Bridges\",\"SPI\",\"STM32\",\"STM32F103C8\",\"WWDG\"],\"articleSection\":[\"Boards and Microcontrollers\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions\",\"name\":\"STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/post_image_stm32.png\",\"datePublished\":\"2025-09-10T18:11:12+00:00\",\"dateModified\":\"2025-09-10T18:11:25+00:00\",\"description\":\"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#primaryimage\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/post_image_stm32.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2025\\\/06\\\/post_image_stm32.png\",\"width\":984,\"height\":984},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\\\/stm32-boards-part-2-selected-functions#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Startseite\",\"item\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"STM32 boards &#8211; Part 2: Selected functions\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#website\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en\",\"name\":\"Wolles Elektronikkiste\",\"description\":\"Die wunderbare Welt der Elektronik\",\"publisher\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/en#\\\/schema\\\/person\\\/b774e4d64b4766889a2f7c6e5ec85b46\",\"name\":\"Wolfgang Ewald\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"url\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"contentUrl\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\",\"width\":512,\"height\":512,\"caption\":\"Wolfgang Ewald\"},\"logo\":{\"@id\":\"https:\\\/\\\/wolles-elektronikkiste.de\\\/wp-content\\\/uploads\\\/2019\\\/03\\\/cropped-Logo-1.png\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste","description":"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions","og_locale":"en_US","og_type":"article","og_title":"STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste","og_description":"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.","og_url":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions","og_site_name":"Wolles Elektronikkiste","article_published_time":"2025-09-10T18:11:12+00:00","article_modified_time":"2025-09-10T18:11:25+00:00","og_image":[{"width":984,"height":984,"url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png","type":"image\/png"}],"author":"Wolfgang Ewald","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Wolfgang Ewald","Est. reading time":"40 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#article","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions"},"author":{"name":"Wolfgang Ewald","@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"headline":"STM32 boards &#8211; Part 2: Selected functions","datePublished":"2025-09-10T18:11:12+00:00","dateModified":"2025-09-10T18:11:25+00:00","mainEntityOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions"},"wordCount":5251,"commentCount":0,"publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png","keywords":["ADC","Arduino","BlackPill","BluePill","DAC","DeepSleep","F446RE","Giga R1 WiFi","Hardware timer","HardwareSerial","I2C","Independent Watchdog","Interrupt","IWDG","L432KC","low power","LSE_CLOCK","LSI_CLOCK","PC14","PC15","power consumption","PWM","Real-time clock","RTC","Solder Bridges","SPI","STM32","STM32F103C8","WWDG"],"articleSection":["Boards and Microcontrollers"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions","url":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions","name":"STM32 boards - Part 2: Selected functions &#8226; Wolles Elektronikkiste","isPartOf":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#website"},"primaryImageOfPage":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#primaryimage"},"image":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#primaryimage"},"thumbnailUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png","datePublished":"2025-09-10T18:11:12+00:00","dateModified":"2025-09-10T18:11:25+00:00","description":"I will show you how to use selected functions on STM32 boards, such as Timer, PWM, LowPower, I2C, SPI, ADC, DAC, RTC and much more.","breadcrumb":{"@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#primaryimage","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2025\/06\/post_image_stm32.png","width":984,"height":984},{"@type":"BreadcrumbList","@id":"https:\/\/wolles-elektronikkiste.de\/en\/stm32-boards-part-2-selected-functions#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Startseite","item":"https:\/\/wolles-elektronikkiste.de\/en"},{"@type":"ListItem","position":2,"name":"STM32 boards &#8211; Part 2: Selected functions"}]},{"@type":"WebSite","@id":"https:\/\/wolles-elektronikkiste.de\/en#website","url":"https:\/\/wolles-elektronikkiste.de\/en","name":"Wolles Elektronikkiste","description":"Die wunderbare Welt der Elektronik","publisher":{"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wolles-elektronikkiste.de\/en?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/wolles-elektronikkiste.de\/en#\/schema\/person\/b774e4d64b4766889a2f7c6e5ec85b46","name":"Wolfgang Ewald","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","url":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","contentUrl":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png","width":512,"height":512,"caption":"Wolfgang Ewald"},"logo":{"@id":"https:\/\/wolles-elektronikkiste.de\/wp-content\/uploads\/2019\/03\/cropped-Logo-1.png"}}]}},"_links":{"self":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/24780","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/comments?post=24780"}],"version-history":[{"count":25,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/24780\/revisions"}],"predecessor-version":[{"id":24839,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/posts\/24780\/revisions\/24839"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media\/24088"}],"wp:attachment":[{"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/media?parent=24780"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/categories?post=24780"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wolles-elektronikkiste.de\/en\/wp-json\/wp\/v2\/tags?post=24780"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}