<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://stm32world.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Niclas</id>
	<title>Stm32World Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://stm32world.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Niclas"/>
	<link rel="alternate" type="text/html" href="https://stm32world.com/wiki/Special:Contributions/Niclas"/>
	<updated>2026-10-22T02:23:34Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.0</generator>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion-LEDs.svg&amp;diff=8735</id>
		<title>File:Colibri 8-IO Expansion-LEDs.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion-LEDs.svg&amp;diff=8735"/>
		<updated>2026-10-21T09:58:07Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Colibri 8-IO Expansion-LEDs.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Schematics for Colibri_8 IO Expansion, LEDs&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_8-Power_Supply.svg&amp;diff=8734</id>
		<title>File:Colibri 8-Power Supply.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_8-Power_Supply.svg&amp;diff=8734"/>
		<updated>2026-10-21T09:57:38Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Colibri 8-Power Supply.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Colibri 8 powers supply schematics&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion-X1.svg&amp;diff=8733</id>
		<title>File:Colibri 8-IO Expansion-X1.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion-X1.svg&amp;diff=8733"/>
		<updated>2026-10-21T09:57:03Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Colibri 8-IO Expansion-X1.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Schematics of the I/O slot on Colibri 8. This is repeated 7 times.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion.svg&amp;diff=8732</id>
		<title>File:Colibri 8-IO Expansion.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_8-IO_Expansion.svg&amp;diff=8732"/>
		<updated>2026-10-21T09:56:33Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Colibri 8-IO Expansion.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Schematics of the I/O subsystem on Colibri 8&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_8.svg&amp;diff=8731</id>
		<title>File:Colibri 8.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_8.svg&amp;diff=8731"/>
		<updated>2026-10-21T09:55:58Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Schematics of Colibri 8&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Schematics of Colibri 8&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_8&amp;diff=8730</id>
		<title>Colibri 8</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_8&amp;diff=8730"/>
		<updated>2026-10-21T09:55:41Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Schematics */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Colibri]][[Category:Work in progress]]{{metadesc|Colibri 8 carrier board}}&lt;br /&gt;
&lt;br /&gt;
DIN-rail mounted Colibri carrier board with 1 MCU slot and 7 I/O clots. MCU slot is to be populated with any of the MCU variants available. &lt;br /&gt;
[[File:Colibri_8_photo1.jpg|thumb|250px]]&lt;br /&gt;
[[File:Colibri_8_photo2.jpg|thumb|250px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Features = &lt;br /&gt;
&lt;br /&gt;
== MCU slot ==&lt;br /&gt;
The MCU slot is the standard [[Colibri MCU slot]] for accepting any of the available MCU modules. The MCU module can &lt;br /&gt;
be programmed in many different ways, and see the [[Colibri Programming Languages]] page for details on that. &lt;br /&gt;
&lt;br /&gt;
The MCU in the MCU slot will be the manager of all the hardware resources;&lt;br /&gt;
* SPI bus and SPI slot select device&lt;br /&gt;
* I2C bus and I2C Mux&lt;br /&gt;
* Galvanically Isolated RS-485 port, for SCADA and other supervisory systems.&lt;br /&gt;
* 16MB flash memory&lt;br /&gt;
* 128kB EEPROM memory&lt;br /&gt;
* Temperature and Humidity sensor&lt;br /&gt;
&lt;br /&gt;
== I/O module slots ==&lt;br /&gt;
There are seven I/O module slots on this carrier board. Most I/O modules can be used, but there are I/O modules that&lt;br /&gt;
will depend on the power supply, for instance [[Colibri Triac1]] will only work if the ''Colibri 8'' are powered by an AC source.&lt;br /&gt;
Also [[Colibri DIO1]] must '''NOT''' be used on systems with AC power supply.&lt;br /&gt;
&lt;br /&gt;
== I2C communications ==&lt;br /&gt;
The I2C bus from the MCU module must be multiplexed to allow for many I/O modules of the same type to be used in different I/O slots.&lt;br /&gt;
&lt;br /&gt;
The multiplexer is a TCA9548APWR on address 0x70 (0b1110000x), and each bit in the &amp;quot;data byte&amp;quot; written to the multiplexer will enable&lt;br /&gt;
that I/O slot's I2C bus. I2C bus 0 is the ''Colibri 8'' carrier board itself, i.e. to the EEPROM. It is important that only one I2C&lt;br /&gt;
bus is enable at a time.&lt;br /&gt;
&lt;br /&gt;
== SPI communications ==&lt;br /&gt;
Each I/O slot has a SPI Chip Select (CS, active low) individually addressable, and two shared chip selects &lt;br /&gt;
(no defined state, I/O module dependent, often unused) for addressing more than one SPI device on a single I/O module.&lt;br /&gt;
&lt;br /&gt;
SPI communications happens at 1Mbps by default.&lt;br /&gt;
&lt;br /&gt;
== I/O slot power ==&lt;br /&gt;
Each I/O slot has +3.3V, +5V and +12V available on the connector. On the ''Colibri 8'' the power for +3.3V and +5V can be turned on/off &lt;br /&gt;
individually by the MCU, meaning the MCU can for instance turn off the +5V on X2, for instance to re-initialize some circuit.&lt;br /&gt;
&lt;br /&gt;
Additionally, the external power that is used to power the ''Colibri 8'' device is also available on the I/O slot header. This is how some &lt;br /&gt;
I/O modules become dependent on the type of power that drives the ''Colibri 8'', for instance the [[Colibri DIO1]] that will malfunction and&lt;br /&gt;
possibly break if AC voltage is used.&lt;br /&gt;
&lt;br /&gt;
== Power input  ==&lt;br /&gt;
The built-in power supply is versatile and accepts either AC or DC voltages, over a vast range;&lt;br /&gt;
&lt;br /&gt;
* 7-36 Volt AC&lt;br /&gt;
* 6-50 Volt DC&lt;br /&gt;
&lt;br /&gt;
== USB-C port ==&lt;br /&gt;
There is a USB-C port for upstream communication with PC or other devices, typically used for Service and Maintenance.&lt;br /&gt;
&lt;br /&gt;
== Jolt Connect programming header ==&lt;br /&gt;
A standard Jolt Connect compatible programming header is available. The device will NOT be powered through that header.&lt;br /&gt;
&lt;br /&gt;
= I2C Addresses =&lt;br /&gt;
  TCA9548 = 0x70    ; Selects which I/O slot the I2C should be connected to.&lt;br /&gt;
&lt;br /&gt;
  ; Slot 0 is selected in MUX (TCA9548)&lt;br /&gt;
  TCA6424 = 0x22       ; IO Expander for RESET, POWER and SPI select&lt;br /&gt;
  M24M01  = 0x50-0x51  ; 1 Mbit of EEPROM&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
[[File:Architecture-colibri-8.svg]]&lt;br /&gt;
&lt;br /&gt;
= Schematics =&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_8.svg|2400px]]&lt;br /&gt;
[[File:Colibri_8-IO_Expansion.svg|2400px]]&lt;br /&gt;
[[File:Colibri 8-IO Expansion-X1.svg|2400px]]&lt;br /&gt;
[[File:Colibri_8-Power Supply.svg|2400px]]&lt;br /&gt;
[[File:Colibri 8-IO Expansion-LEDs.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Errata =&lt;br /&gt;
* In Revision A, the secondary USART on the MCU slot was not yet defined, so the RS-485 transceiver is connected to the programming USART (USART2). This has been corrected for later revisions.&lt;br /&gt;
&lt;br /&gt;
= Documentation =&lt;br /&gt;
[[File:Colibri_8-rev_A-schematics.pdf|Colibri 8 Schematics]]&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A-schematics.svg&amp;diff=8729</id>
		<title>File:Colibri mcu6-rev A-schematics.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A-schematics.svg&amp;diff=8729"/>
		<updated>2026-10-20T12:35:28Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Schematics of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Schematics of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_left_render.png&amp;diff=8728</id>
		<title>File:Colibri mcu6-rev A top left render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_left_render.png&amp;diff=8728"/>
		<updated>2026-10-20T12:34:48Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Top left Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Top left Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_right_render.png&amp;diff=8727</id>
		<title>File:Colibri mcu6-rev A top right render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_right_render.png&amp;diff=8727"/>
		<updated>2026-10-20T12:34:31Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Top tight Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Top tight Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_right_render.png&amp;diff=8726</id>
		<title>File:Colibri mcu6-rev A bot right render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_right_render.png&amp;diff=8726"/>
		<updated>2026-10-20T12:34:05Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Colibri mcu6-rev A bot right render.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Top right Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_left_render.png&amp;diff=8725</id>
		<title>File:Colibri mcu6-rev A bot left render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_left_render.png&amp;diff=8725"/>
		<updated>2026-10-20T12:33:11Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Bottom left Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Bottom left Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_render.png&amp;diff=8724</id>
		<title>File:Colibri mcu6-rev A bot render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_render.png&amp;diff=8724"/>
		<updated>2026-10-20T12:32:21Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Bottom Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Bottom Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_right_render.png&amp;diff=8723</id>
		<title>File:Colibri mcu6-rev A bot right render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_bot_right_render.png&amp;diff=8723"/>
		<updated>2026-10-20T12:31:44Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Top right Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Top right Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_render.png&amp;diff=8722</id>
		<title>File:Colibri mcu6-rev A top render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Colibri_mcu6-rev_A_top_render.png&amp;diff=8722"/>
		<updated>2026-10-20T12:31:14Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Top Render of Colibri MCU6.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Top Render of Colibri MCU6.&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8721</id>
		<title>Colibri MCU6</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8721"/>
		<updated>2026-10-20T12:30:10Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Components and Modules]][[Category:Modules]][[Category:Colibri]]{{metadesc|Colibri MCU6 module}}&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_top_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_right_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_render.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Colibri]] MCU6 is a processor module for the [[Colibri]] carrier boards, using STM32H735RG and a 16MB external flash for the [[Colibri Runtime]]&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
The MCU6 is a relatively simple MCU module, on an M.2 board with the size 22x42mm and the Key E configuration.&lt;br /&gt;
&lt;br /&gt;
Besides the [[STM32H735]] MCU there are also &lt;br /&gt;
* 128 Mbits (16MB) flash memory for application use.&lt;br /&gt;
* a dual-colored (Green/Red) LED&lt;br /&gt;
* 16MHz HSE crystal&lt;br /&gt;
&lt;br /&gt;
'''NOTE: All modules in Colibri are NOT electrically compatible with the M.2 connector standard. DO NOT plug Colibri modules into any M.2 socket other than Colibri (or compatible) ones. And DO NOT plug any M.2 modules (Key E is typically WiFi/BLE) into a Colibri carrier board.&lt;br /&gt;
&lt;br /&gt;
= Programming =&lt;br /&gt;
This MCU module is intended to be running the [[Colibri Runtime]] firmware, which will handle the peripherals, the carrier board subsystem, the Comm module and the loading/running the software for the I/O modules mounted on the carrier board. [[Colibri Runtime]] in turn is intended to be programmed with Lua and/or a new exciting [[Colibri-hakkas| visual programming language]] that is in the making.&lt;br /&gt;
&lt;br /&gt;
== Low Level ==&lt;br /&gt;
If you intend to program this (i.e. not using [[Colibri Runtime]]), then confer with the schematics below and schematics for the carrier boards, comm modules and I/O modules intended to be used. The standard SWDIO/SWCLK pins are available in the pin header together with UART0 and UART1 for effective low level development environment.&lt;br /&gt;
&lt;br /&gt;
== C/C++ ==&lt;br /&gt;
Typically use STM32CubeIDE, CLion or VScode. &lt;br /&gt;
&lt;br /&gt;
= Zephyr =&lt;br /&gt;
Zephyr is an MCU-independent embedded OS, with a very modular approach to software, which fits quite well with the modular support of Streamline and Colibri products.&lt;br /&gt;
&lt;br /&gt;
Read more about how [[Colibri Zephyr]] is intended (work in progress) to operate on the [[Colibri]] family.&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_mcu6-rev_A-schematics.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_left_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8720</id>
		<title>Colibri MCU6</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8720"/>
		<updated>2026-10-20T12:28:38Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Components and Modules]][[Category:Modules]][[Category:Colibri]]{{metadesc|Colibri MCU6 module}}&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_top_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_right_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_render.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Colibri]] MCU6 is a processor module for the [[Colibri]] carrier boards, using STM32H735RG and a 16MB external flash for the [[Colibri Runtime]]&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
The MCU6 is a relatively simple MCU module, on an M.2 board with the size 22x42mm and the Key E configuration.&lt;br /&gt;
&lt;br /&gt;
Besides the [[STM32H735]] MCU there are also &lt;br /&gt;
* 128 Mbits (16MB) flash memory for application use.&lt;br /&gt;
* a dual-colored (Green/Red) LED&lt;br /&gt;
* 16MHz HSE crystal&lt;br /&gt;
&lt;br /&gt;
'''NOTE: All modules in Colibri are NOT electrically compatible with the M.2 connector standard. DO NOT plug Colibri modules into any M.2 socket other than Colibri (or compatible) ones. And DO NOT plug any M.2 modules (Key E is typically WiFi/BLE) into a Colibri carrier board.&lt;br /&gt;
&lt;br /&gt;
= Programming =&lt;br /&gt;
This MCU module is intended to be running the [Colibri Runtime] firmware, which will handle the peripherals, the carrier board subsystem, the Comm module and the loading/running the software for the I/O modules mounted on the carrier board. [Colibri Runtime] in turn is intended to be programmed with Lua and/or a new exciting [Colibri-hakkas] visual programming language] that is in the making.&lt;br /&gt;
&lt;br /&gt;
== Low Level ==&lt;br /&gt;
If you intend to program this (i.e. not using [[Colibri Runtime]]), then confer with the schematics below and schematics for the carrier boards, comm modules and I/O modules intended to be used. The standard SWDIO/SWCLK pins are available in the pin header together with UART0 and UART1 for effective low level development environment.&lt;br /&gt;
&lt;br /&gt;
== C/C++ ==&lt;br /&gt;
Typically use STM32CubeIDE, CLion or VScode. &lt;br /&gt;
&lt;br /&gt;
= Zephyr =&lt;br /&gt;
Zephyr is an MCU-independent embedded OS, with a very modular approach to software, which fits quite well with the modular support of Streamline and Colibri products.&lt;br /&gt;
&lt;br /&gt;
Read more about how [[Colibri Zephyr]] is intended (work in progress) to operate on the [[Colibri]] family.&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_mcu6-rev_A-schematics.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_left_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8719</id>
		<title>Colibri MCU6</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_MCU6&amp;diff=8719"/>
		<updated>2026-10-20T12:28:10Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Created page with &amp;quot;Category:Components and ModulesCategory:ModulesCategory:Colibri{{metadesc|Colibri MCU6 module}} thumb File:Colibri_mcu...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Components and Modules]][[Category:Modules]][[Category:Colibri]]{{metadesc|Colibri MCU6 module}}&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_top_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_right_render.png|thumb]]&lt;br /&gt;
[[File:Colibri_mcu6-rev_A_bot_render.png|thumb]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Colibri]] MCU6 is a processor module for the [[Colibri]] carrier boards, using STM32H735RG and a 16MB external flash for the [[Colibri Runtime]]&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
The MCU6 is a relatively simple MCU module, on an M.2 board with the size 22x42mm and the Key E configuration.&lt;br /&gt;
&lt;br /&gt;
Besides the [[STM32H735]] MCU there are also &lt;br /&gt;
* 128 Mbits (16MB) flash memory for application use.&lt;br /&gt;
* a dual-colored (Green/Red) LED&lt;br /&gt;
* 16MHz HSE crystal&lt;br /&gt;
&lt;br /&gt;
'''NOTE: All modules in Colibri are NOT electrically compatible with the M.2 connector standard. DO NOT plug Colibri modules into any M.2 socket other than Colibri (or compatible) ones. And DO NOT plug any M.2 modules (Key E is typically WiFi/BLE) into a Colibri carrier board.&lt;br /&gt;
&lt;br /&gt;
= Programming =&lt;br /&gt;
This MCU module is intended to be running the [Colibri Runtime] firmware, which will handle the peripherals, the carrier board subsystem, the Comm module and the loading/running the software for the I/O modules mounted on the carrier board. [Colibri Runtime] in turn is intended to be programmed with Lua and/or a new exciting [[Colibri-hakkas] visual programming language] that is in the making.&lt;br /&gt;
&lt;br /&gt;
== Low Level ==&lt;br /&gt;
If you intend to program this (i.e. not using [[Colibri Runtime]]), then confer with the schematics below and schematics for the carrier boards, comm modules and I/O modules intended to be used. The standard SWDIO/SWCLK pins are available in the pin header together with UART0 and UART1 for effective low level development environment.&lt;br /&gt;
&lt;br /&gt;
== C/C++ ==&lt;br /&gt;
Typically use STM32CubeIDE, CLion or VScode. &lt;br /&gt;
&lt;br /&gt;
= Zephyr =&lt;br /&gt;
Zephyr is an MCU-independent embedded OS, with a very modular approach to software, which fits quite well with the modular support of Streamline and Colibri products.&lt;br /&gt;
&lt;br /&gt;
Read more about how [[Colibri Zephyr]] is intended (work in progress) to operate on the [[Colibri]] family.&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_mcu6-rev_A-schematics.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_bot_left_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_right_render.png&lt;br /&gt;
File:Colibri_mcu6-rev_A_top_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri&amp;diff=8714</id>
		<title>Colibri</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri&amp;diff=8714"/>
		<updated>2026-10-14T05:39:32Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Modules &amp;amp; Carriers */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Colibri]][[Category:Work in progress]]{{metadesc|Colibri Automation System}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb tright&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width: 250px;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div style=&amp;quot;width: 250px; height: 150px; overflow: hidden; position: relative; background: #000;&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;div style=&amp;quot;position: absolute; top: -20px; left: -15px;&amp;quot;&amp;gt;&lt;br /&gt;
        [[File:Colibri_8_photo1.jpg|250px|link=File:Colibri_8_photo1.jpg|alt=Colibri 8 PCB]]&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
[[File:Colibri-3-6.jpg|thumb|250px]]&lt;br /&gt;
[[File:Colibri_3a_photo1.jpg|thumb|250px]]&lt;br /&gt;
[[File:Colibri rs485u-2.jpg|180px|thumb]]&lt;br /&gt;
[[File:Colibri aic-RevA-2.jpg|180px|thumb]]&lt;br /&gt;
&lt;br /&gt;
Check out the [[Colibri Videos]] on Youtube.&lt;br /&gt;
&lt;br /&gt;
Colibri is a low-cost automation system, primarily intended for building automation, home automation, energy supervision, agriculture supervision and similar systems. Main features beside costs are;&lt;br /&gt;
* Flexible I/O system, via MCU modules, Comm modules and I/O modules in M.2 Key E sockets. Many types already developed.&lt;br /&gt;
* Power supply of 9-36 Volt, either AC or DC (some I/O cards won't work with DC supply, and some not with AC supply)&lt;br /&gt;
* Each I/O slot can be powered-up/down individually from the MCU.&lt;br /&gt;
* Each I/O slot is addressable individually.&lt;br /&gt;
* MCU communicates with each I/O slot via I2C and/or SPI. The I2C bus is multiplexed to avoid address collision. SPI uses one Chip Select per I/O slot and 2 additional Chip Select if multi SPI devices are on a single I/O module.&lt;br /&gt;
* Each I/O module has an 1Mbit EEPROM with calibration parameters and a program for the MCU module to execute. This is accomplished with &amp;quot;PIC&amp;quot; (Position Independent Code) and executing from RAM.&lt;br /&gt;
* Some I/O modules have on-board secondary processors for time-critical or other reasons, for instance RS-485 communication, digital inputs (pulse counting) and PID regulator.&lt;br /&gt;
&lt;br /&gt;
== Architecture Overview ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_Architecture-1.svg|240px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Colors in this view represents who creates the software for each part.&lt;br /&gt;
&lt;br /&gt;
== Programming ==&lt;br /&gt;
The Colibri system is designed around two main external projects, [[Colibri Zephyr|Zephyr]] and [http://www.lua.org Lua].&lt;br /&gt;
&lt;br /&gt;
Zephyr provides a lot of ''operating system'' type of features, like task scheduling, concurrency data types, timers, peripheral drivers and cross-platform compatibility to be able to support STM32, RP2354 and possibly other MCU families. &lt;br /&gt;
&lt;br /&gt;
We experimented a bit with WebAssembly, but found the practical barrier a bit too high as well as memory footprint is much more difficult to manage.&lt;br /&gt;
&lt;br /&gt;
Without WebAssembly we are still able to allow for;&lt;br /&gt;
* I/O modules that are unknown to the MCU module to be installed without re-flashing the firmware (using PIC)&lt;br /&gt;
* Uploading of user programs, without flashing the firmware, upload Lua text files&lt;br /&gt;
* Sandboxed execution to limit issues that user programs can create,&lt;br /&gt;
* Simplified tool chain,&lt;br /&gt;
&lt;br /&gt;
The WebAssembly approach might be revisited in the future.&lt;br /&gt;
&lt;br /&gt;
== Carrier Boards ==&lt;br /&gt;
* ''Colibri 3a'' has 2 I/O slots and one MCU slot, one USB-C port to the MCU slot, Jolt Connect pin header for programming. (Postponed to be re-evaluated)&lt;br /&gt;
* ''Colibri 8'' has 7 I/O slots, a Communcation slot, MCU slot, one USB-C port to the MCU slot, Jolt Connect pin header for programming.&lt;br /&gt;
* Planned; ''Colibri 8pi'' is like the ''Colibri 8'' but with a full-fledged Raspberry Pi Compute Module 5. 1 Colibri MCU slot, Comm slot and 7 I/O slots,&lt;br /&gt;
* Planned; ''Colibri 16pi'' is like the ''Colibri 8pi'', but with 15 I/O slots rather than 7.&lt;br /&gt;
&lt;br /&gt;
== MCU Modules ==&lt;br /&gt;
There is a choice, and more will follow;&lt;br /&gt;
&lt;br /&gt;
* ''[[Colibri MCU1]]'' - (deprecated) MCU module with a STM32WLE5, 256kB Flash, 64kB RAM, on-chip LoRa and LoraWAN protocol available in STM32CubeMX.&lt;br /&gt;
* ''[[Colibri MCU2]]'' - MCU module with a STM32H562RG, 1024kB Flash, 640kB RAM and a 16MB external flash memory.&lt;br /&gt;
* ''[[Colibri MCU3]]'' - (deprecated) MCU module with a STM32F405RG, 1MB Flash, 192kB+4kB RAM and a 16MB external flash memory.&lt;br /&gt;
* ''[[Colibri MCU4]]'' - (deprecated) MCU module with a STM32L475RG, 1MB Flash, 128kB RAM and a 16MB external flash memory.&lt;br /&gt;
* ''[[Colibri MCU5]]'' - (deprecated) MCU module with a STM32F103RE, 512kB Flash and 64kB RAM. No external flash memory.&lt;br /&gt;
* ''[[Colibri MCU7]]'' - MCU module with a RP2350A, 16MB external Flash and 520kB RAM&lt;br /&gt;
&lt;br /&gt;
== Comm Modules ==&lt;br /&gt;
Comm modules exist to establish a communication link with the device in one of the currently 4 planned types;&lt;br /&gt;
&lt;br /&gt;
* ''[[Colibri Comm Ethernet]]'' - TCP/IP communications, incl Modbus/TCP, MQTT and HTTPS.&lt;br /&gt;
* ''[[Colibri Comm LoraWAN]]'' - LoraWAN, primarily to report sensor data and status.&lt;br /&gt;
* ''[[Colibri Comm RS485]]'' - For RS485-based protocols, primarily Modbus, but potentially BACnet and others.&lt;br /&gt;
* ''[[Colibri Comm WiFi]]'' - Wireless TCP/IP communications, incl Modbus/TCP, MQTT and HTTPS.&lt;br /&gt;
&lt;br /&gt;
== I/O Modules ==&lt;br /&gt;
The Colibri system is based around the M.2 connector and a non-standard bus for I2C and SPI communications. Each I/O module has an [[Colibri_EEPROM_Layout|EEPROM memory]] available to identify which module is present in each I/O slot, links to documentation, calibration parameters, testing results and (up to) 8kB of C code for the MCU module to execute.&lt;br /&gt;
&lt;br /&gt;
== [[Colibri Form Factor|Form Factor]] ==&lt;br /&gt;
&lt;br /&gt;
Both Colibri I/O modules, Comm modules and MCU modules are 22x42mm and has an edge connector called M.2 Key E, '''but it doesn't follow the M.2 specification for Key E'''. The distance between the I/O slots are specified to 23.5mm, theoretically making it possible for multi-slot I/O modules (none are planned).&lt;br /&gt;
&lt;br /&gt;
The Comm module MUST be have a free zone of at least 5mm between its edge and the edge of the next modules on either side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;color:#FF4444&amp;quot;&amp;gt;'''WARNING!!! Do NOT plug Colibri I/O modules nor any MCU module into other equipment. Do NOT plug any M.2 card into a Colibri system, unless that card/board is specifically made for Colibri.'''&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modules &amp;amp; Carriers ==&lt;br /&gt;
&lt;br /&gt;
{| class=wikitable&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Type&lt;br /&gt;
! Description &lt;br /&gt;
! Status&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri 3a]]''&lt;br /&gt;
| Carrier&lt;br /&gt;
| Minimal Colibri carrier board with 2 I/O slots and one MCU slot. MCU slot is to be populated with any of the MCU variants available.&lt;br /&gt;
| Deprecated, on hold.&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri 8]]''&lt;br /&gt;
| Carrier&lt;br /&gt;
| Colibri carrier board with Colibri MCU slot and 7 Colibri I/O slots. A galvanically isolated RS-485 port connected to the secondary USART of the MCU slot.&lt;br /&gt;
| Early Access&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri-8pi]]''&lt;br /&gt;
| Carrier&lt;br /&gt;
| Colibri carrier board with Raspberry Pi CM5, Colibri MCU slot, 7 Colibri I/O slots, two Ethernet ports, 3 USB ports and SDcard connector.&lt;br /&gt;
| Planned&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri-16pi]]''&lt;br /&gt;
| Carrier&lt;br /&gt;
| Colibri carrier board with Raspberry Pi CM5, Colibri MCU slot, 15 Colibri I/O slots, two Ethernet ports, 3 USB ports and SDcard connector.&lt;br /&gt;
| Planned&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri 1dp]]''&lt;br /&gt;
| Carrier&lt;br /&gt;
| Minimal Colibri Host with a single MCU slot and 0-500Pa air pressure sensor.&lt;br /&gt;
| Prototype - Re-evaluated after introduction of Comm modules.&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri MCU2]]''&lt;br /&gt;
| MCU module&lt;br /&gt;
| MCU module with a STM32H562RG and a 16MB flash memory.&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri MCU7]]''&lt;br /&gt;
| MCU module&lt;br /&gt;
| MCU module with a Raspberry Pi RP2354B and a 16MB flash memory.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Comm Ethernet]]''&lt;br /&gt;
| Comm module&lt;br /&gt;
| TCP/IP communications, incl Modbus/TCP, MQTT and HTTPS.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Comm LoraWAN]]''&lt;br /&gt;
| Comm module&lt;br /&gt;
| LoraWAN, primarily to report sensor data and status.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Comm RS485]]''&lt;br /&gt;
| Comm module&lt;br /&gt;
| For RS485-based protocols, primarily Modbus, but potentially BACnet and others.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Comm WiFi]]''&lt;br /&gt;
| Comm module&lt;br /&gt;
| Wireless TCP/IP communications, incl Modbus/TCP, MQTT and HTTPS.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[M.2 E Breakout]]''&lt;br /&gt;
| M.2 breakout board&lt;br /&gt;
| Generic breakout board for M.2 E key boards.&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri AIC]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two analog 0-20mA inputs&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri AIV]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two analog 0-10V inputs&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri AQV]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two analog 0-10V outputs&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DII]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two ISOLATED digital inputs, for state, counter, pulsewidth measurement. On-board STM32C071 for ability to count very fast pulses and high accuracy PWM measurements.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DIO1]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| IEC 61131-2 compliant, 4-channels digital inputs and outputs.&lt;br /&gt;
| Active&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DIU]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Four digital inputs, for state, counter, pulsewidth measurement. On-board STM32C071 for ability to count very fast pulses and high accuracy PWM measurements.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DP1]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| A 0-500Pa air pressure sensor mounted on I/O module.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DP2]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| A 0-10kPa air pressure sensor mounted on I/O module.&lt;br /&gt;
| Planned&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri DP3]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| A 0-100kPa air pressure sensor mounted on I/O module.&lt;br /&gt;
| Planned&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Fallback]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Single SPDT relay&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri FET]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Four digital outputs, FET outputs sinking to GND. Max 1 Amp per channel.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri PID1]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| PID regulator, with a PT1000 input and a 0-10V analog output. On-board STM32C071 for host-independent operation.&lt;br /&gt;
| Prototype&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Pt100]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| One port for 3-wire or 4-wire Pt100 temperature sensor.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri Pt1000]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two PT1000 temperature sensor inputs.&lt;br /&gt;
| Prototype&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri RS485I]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Isolated RS-485 port. On-board STM32C071 to implement protocols, such as Modbus RTU.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri RS485U]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two non-isolated RS-485 ports. On-board STM32C071 implementing Modbus protocol.&lt;br /&gt;
| Designed&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri SSR]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Two Solid State Relays, ON/OFF.&lt;br /&gt;
| Prototype&lt;br /&gt;
|-&lt;br /&gt;
| ''[[Colibri TRIAC1]]''&lt;br /&gt;
| I/O module&lt;br /&gt;
| Four Triac outputs, driving 24V AC.&lt;br /&gt;
| Prototype&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= License =&lt;br /&gt;
All Schematics are licensed with the GPLv3, meaning that you are free to make your own designs or manufacture devices using these. We expect that if you derive new design, that you publish these, preferably on this website.&lt;br /&gt;
&lt;br /&gt;
The software will also be published with GPLv3, when something is workable.&lt;br /&gt;
&lt;br /&gt;
= Resources =&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_Relay&amp;diff=8685</id>
		<title>Colibri Relay</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_Relay&amp;diff=8685"/>
		<updated>2026-10-06T10:15:15Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Colibri]]{{metadesc|Colibri Relay module}}&lt;br /&gt;
[[File:Colibri_relay-rev_A_top_render.png|thumb]]&lt;br /&gt;
  &lt;br /&gt;
Colibri Relay is an digital output I/O module with one SPDT (single-pole, dual-throw) relay.&lt;br /&gt;
&lt;br /&gt;
=== What about that name? ===&lt;br /&gt;
We have extensively used this in HVAC renovation projects, where the customer wants to keep the previous system in place, as an insurance against we going out of business, something breaks or in case the software goes haywire. So, we connect the valve to pin &amp;quot;b&amp;quot;, the Colibri output signal on pin &amp;quot;a&amp;quot; and the old system's output signal on pin &amp;quot;c&amp;quot;. We can remotely control if we want to swap over to previous system, or if the owners wants to do it, they just turn the Colibri system OFF.&lt;br /&gt;
&lt;br /&gt;
=== I2C Addresses === &lt;br /&gt;
  M24M01  = 0x50-0x51&lt;br /&gt;
  PCA9536 = 0x41&lt;br /&gt;
&lt;br /&gt;
= EEPROM factory layout =&lt;br /&gt;
&lt;br /&gt;
See the [[Colibri EEPROM Layout]] page for the full picture.&lt;br /&gt;
&lt;br /&gt;
== Calibration data in EEPROM ==&lt;br /&gt;
Work in progress. Not available in product if ordered now.&lt;br /&gt;
&lt;br /&gt;
{| class=wikitable&lt;br /&gt;
|-&lt;br /&gt;
! Address&lt;br /&gt;
! Length&lt;br /&gt;
! Description &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Test Report ==&lt;br /&gt;
Work in progress. Not available in product if ordered now.&lt;br /&gt;
&lt;br /&gt;
{| class=wikitable&lt;br /&gt;
|-&lt;br /&gt;
! Address&lt;br /&gt;
! Length&lt;br /&gt;
! Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0100&lt;br /&gt;
| 4&lt;br /&gt;
| Year * 12 + month&lt;br /&gt;
|-&lt;br /&gt;
| 0x0104&lt;br /&gt;
| 4&lt;br /&gt;
| Date * 24 + hour&lt;br /&gt;
|-&lt;br /&gt;
| 0x0108&lt;br /&gt;
| 8&lt;br /&gt;
| Reserved for future use.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0110&lt;br /&gt;
| 4&lt;br /&gt;
| EEPROM write/read/erase test. 32 cycles. Each cycle is represented in one bit. 0=Success, 1=Fail.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_relay-rev_A-schematics.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Colibri_relay-rev_A_top_render.png&lt;br /&gt;
File:Colibri_relay-rev_A_bot_render.png&lt;br /&gt;
File:Colibri_relay-rev_A_top_right_render.png&lt;br /&gt;
File:Colibri_relay-rev_A_top_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_Fallback&amp;diff=8684</id>
		<title>Colibri Fallback</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_Fallback&amp;diff=8684"/>
		<updated>2026-10-06T10:13:58Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas moved page Colibri Fallback to Colibri Relay: Fallback is a legacy name and too narrow.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[Colibri Relay]]&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_Relay&amp;diff=8683</id>
		<title>Colibri Relay</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_Relay&amp;diff=8683"/>
		<updated>2026-10-06T10:13:58Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas moved page Colibri Fallback to Colibri Relay: Fallback is a legacy name and too narrow.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Colibri]]{{metadesc|Colibri Fallback module}}&lt;br /&gt;
[[File:Colibri_fallback-rev_A_top_render.png|thumb]]&lt;br /&gt;
  &lt;br /&gt;
Colibri Fallback is an digital output I/O module with one SPDT (single-pole, dual-throw) relay.&lt;br /&gt;
&lt;br /&gt;
=== What about that name? ===&lt;br /&gt;
We have extensively used this in HVAC renovation projects, where the customer wants to keep the previous system in place, as an insurance against we going out of business, something breaks or in case the software goes haywire. So, we connect the valve to pin &amp;quot;b&amp;quot;, the Colibri output signal on pin &amp;quot;a&amp;quot; and the old system's output signal on pin &amp;quot;c&amp;quot;. We can remotely control if we want to swap over to previous system, or if the owners wants to do it, they just turn the Colibri system OFF.&lt;br /&gt;
&lt;br /&gt;
=== I2C Addresses === &lt;br /&gt;
  M24M01  = 0x50-0x51&lt;br /&gt;
  PCA9536 = 0x41&lt;br /&gt;
&lt;br /&gt;
= EEPROM factory layout =&lt;br /&gt;
&lt;br /&gt;
See the [[Colibri EEPROM Layout]] page for the full picture.&lt;br /&gt;
&lt;br /&gt;
== Calibration data in EEPROM ==&lt;br /&gt;
Work in progress. Not available in product if ordered now.&lt;br /&gt;
&lt;br /&gt;
{| class=wikitable&lt;br /&gt;
|-&lt;br /&gt;
! Address&lt;br /&gt;
! Length&lt;br /&gt;
! Description &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Test Report ==&lt;br /&gt;
Work in progress. Not available in product if ordered now.&lt;br /&gt;
&lt;br /&gt;
{| class=wikitable&lt;br /&gt;
|-&lt;br /&gt;
! Address&lt;br /&gt;
! Length&lt;br /&gt;
! Description &lt;br /&gt;
|-&lt;br /&gt;
| 0x0100&lt;br /&gt;
| 4&lt;br /&gt;
| Year * 12 + month&lt;br /&gt;
|-&lt;br /&gt;
| 0x0104&lt;br /&gt;
| 4&lt;br /&gt;
| Date * 24 + hour&lt;br /&gt;
|-&lt;br /&gt;
| 0x0108&lt;br /&gt;
| 8&lt;br /&gt;
| Reserved for future use.&lt;br /&gt;
|-&lt;br /&gt;
| 0x0110&lt;br /&gt;
| 4&lt;br /&gt;
| EEPROM write/read/erase test. 32 cycles. Each cycle is represented in one bit. 0=Success, 1=Fail.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Colibri_fallback-rev_A-schematics.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Colibri_fallback-rev_A_top_render.png&lt;br /&gt;
File:Colibri_fallback-rev_A_bot_render.png&lt;br /&gt;
File:Colibri_fallback-rev_A_top_right_render.png&lt;br /&gt;
File:Colibri_fallback-rev_A_top_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Colibri_Tutorial&amp;diff=8682</id>
		<title>Colibri Tutorial</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Colibri_Tutorial&amp;diff=8682"/>
		<updated>2026-10-06T09:59:24Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Created page with &amp;quot;Category:Work in progress{{metadesc|Colibri Automation System}}  &amp;lt;div class=&amp;quot;thumb tright&amp;quot;&amp;gt;   &amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width: 250px;&amp;quot;&amp;gt;     &amp;lt;div style=&amp;quot;width: 250px;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Work in progress]]{{metadesc|Colibri Automation System}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;thumb tright&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;thumbinner&amp;quot; style=&amp;quot;width: 250px;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div style=&amp;quot;width: 250px; height: 150px; overflow: hidden; position: relative; background: #000;&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;div style=&amp;quot;position: absolute; top: -20px; left: -15px;&amp;quot;&amp;gt;&lt;br /&gt;
        [[File:Colibri_8_photo1.jpg|250px|link=File:Colibri_8_photo1.jpg|alt=Colibri 8 PCB]]&lt;br /&gt;
      &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
[[File:Colibri-3-6.jpg|thumb|250px]]&lt;br /&gt;
[[File:Colibri_3a_photo1.jpg|thumb|250px]]&lt;br /&gt;
[[File:Colibri rs485u-2.jpg|180px|thumb]]&lt;br /&gt;
[[File:Colibri aic-RevA-2.jpg|180px|thumb]]&lt;br /&gt;
&lt;br /&gt;
= Tutorial Overview = &lt;br /&gt;
&lt;br /&gt;
* [[Colibri Tutorial - Requirements|Requirements]] - Requirements for Hardware and Software&lt;br /&gt;
* [[Colibri Tutorial - Design|Design]] - Solutions for the Requirements in Hardware and Software&lt;br /&gt;
* [[Colibri Tutorial - Architecture|Architecture]] - Details of Software Architecture&lt;br /&gt;
* [[Colibri Tutorial - Colibri 3b|Colibri 3b carrier board]] - Features and functions of the Colibri 3b carrier board.&lt;br /&gt;
* [[Colibri Tutorial - Colibri MCU2|Colibri MCU2, MCU module with STM32H562 ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Relay|Colibri Relay, One SPDT relay]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri AQV|Colibri AQV, Two 0-10V analog outputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri AIV|Colibri AIV, Two 0-10V analog inputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Comm Ethernet|Colibri Comm Ethernet, Comm module with Ethernet ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri AIC|Colibri AIC, Two 0-20mA analog outputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Pt1000|Colibri Pt1000, Two Pt1000 inputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Comm RS485|Colibri Comm RS485, Comm module with isolated RS485 ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri SSR|Colibri SSR, Two Solid State Relays ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri FET|Colibri FET, Four digital sink outputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri 8|Colibri 8 carrier board]] - Features and functions of the Colibri 8 carrier board.&lt;br /&gt;
* [[Colibri Tutorial - Colibri DIO1|Colibri DIO1, Four IEC61131-2 compliant inputs/outputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Comm Lora|Colibri Comm Lora, Comm module with LoraWan ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri DIU|Colibri DIU, Four non-isolated digital inputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri DII|Colibri DII, Two isolated digital inputs]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri MCU6|Colibri MCU6, MCU module with STM32H735 ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri PID1|Colibri PID1, PID regulator, Pt1000 to 0-10V output ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Pt100|Colibri Pt100, One 3-wire or 4-wire Pt100 input]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Comm Wifi|Colibri Comm Wifi, Comm module with Wifi ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri RS485u|Colibri RS485u, Two non-isolated RS485 transceivers ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri RS485i|Colibri RS485i, One isolated RS485 transceiver ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri DP1|Colibri DP1, 0-500Pa pressure sensor]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri MCU7|Colibri MCU7, MCU module with RP2350 ]] - &lt;br /&gt;
* [[Colibri Tutorial - Colibri Triac1|Colibri Triac1, Four Triac outputs ]] -&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Streamline&amp;diff=8650</id>
		<title>Streamline</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Streamline&amp;diff=8650"/>
		<updated>2026-10-02T06:08:43Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Miscellaneous */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:STM32]][[Category:Streamline]][[Category:CurrentMakers]][[Category:Work in progress]]{{metadesc|Streamline Development Boards Home}}&lt;br /&gt;
[[File:Streamline Backplane with 2 blades - top right 3.jpg|300px|thumb|Streamline Backplane with 2 blades - 1 Connect and 1 MCU]]&lt;br /&gt;
This page documents the new [[CurrentMakers]] [[Streamline]] series of [[Green]] development boards.  If you are looking for the previous series, that is document here: [[STM32World Legacy]].&lt;br /&gt;
&lt;br /&gt;
[[Streamline]] boards are available here: [https://currentmakers.com/products/streamline/ Streamline on CurrentMakers]&lt;br /&gt;
&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Rationale ==&lt;br /&gt;
[[File:3 STM32World Modules stacked.jpg|300px|thumb]]&lt;br /&gt;
A number of [[STM32World Legacy]] boards have been developed, and produced, and in general the &amp;quot;system&amp;quot; worked very well.  It did however have one fatal flaw which was the sole cause of it never being considered for more widespread production.  This flaw was the board to board connector.&lt;br /&gt;
&lt;br /&gt;
The original boards used a 2x20 standard pin header on each side giving a result of 80 pins.  In terms of connectivity this was a pretty good solution.  It did however present some issues:&lt;br /&gt;
&lt;br /&gt;
# Cost - the long pin 2x20 connectors are fairly expensive - around $0.80 each - so $1.60 per board.  This may not sound that high but when producing a lot it becomes a serious cost.&lt;br /&gt;
# Manufacturing -  the headers can not be manufactured with traditional SMD technologies but require a manual step or hand soldering.&lt;br /&gt;
# Fragile - when shipping or disconnecting boards it is hard not to bend one or more pins and once bend it becomes extremely difficult to reconnect them.&lt;br /&gt;
&lt;br /&gt;
To get around this flaw a new &amp;quot;connector&amp;quot; was considered and after a long search the best solution was the standard PCI-E X8 connector.&lt;br /&gt;
&lt;br /&gt;
== Videos ==&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube|5xQqV7BhYxM}}&lt;br /&gt;
&lt;br /&gt;
Receiving the first prototypes ever:&lt;br /&gt;
&lt;br /&gt;
{{#ev:youtube|YS1w_8ghCf4}}&lt;br /&gt;
&lt;br /&gt;
== Backplane ==&lt;br /&gt;
&lt;br /&gt;
The overall idea is to base [[StreamLine]] around a passive backplane.  The backplane contains a number of PCI connectors with 2 x 49 pins (98) that is simply connected in a parallel bus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Sl bp 5 top left.png|1000px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In each end of the bus all the A and B signals are broken out into regular pin headers.&lt;br /&gt;
&lt;br /&gt;
=== Backplane rendering ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Backplane top render.png&lt;br /&gt;
File:Backplane front render.png&lt;br /&gt;
File:Backplane front left render.png&lt;br /&gt;
File:Sl bp 5 top.png&lt;br /&gt;
File:Sl bp 5 top left.png&lt;br /&gt;
File:Sl bp 5 top right.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
&lt;br /&gt;
=== [[Backplane]]s === &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Image&lt;br /&gt;
! Description&lt;br /&gt;
! Status&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Backplane 5]]&lt;br /&gt;
| [[File:sl_bp_5_top_left.png|80px]]&lt;br /&gt;
| Basic 5 position backplane&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_backplane_5/ Available]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== [[Blade]]s ===&lt;br /&gt;
&lt;br /&gt;
==== STM32 ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Image&lt;br /&gt;
! Description&lt;br /&gt;
! Status&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Connect]] (ST-Link firmware)&lt;br /&gt;
| [[File:Sl prog top left render.png|80px]]&lt;br /&gt;
| Streamline Programmer &amp;amp; Debugger.  This module is essentially identical to [[Jolt Connect]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_connect/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU STM32F407]]&lt;br /&gt;
| [[File:Sl mcu stm32f407 top left render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[STM32F407]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_mcu_stm32f407/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU STM32H562]]&lt;br /&gt;
| [[File:Sl mcu stm32h562 top left render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[STM32H562]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_mcu_stm32h562/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU STM32L475]]&lt;br /&gt;
| [[File:Sl mcu stm32l475 top left render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[STM32L475]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_mcu_stm32l475/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU STM32C551]]&lt;br /&gt;
| [[File:Sl_mcu_stm32c551_top_left_render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[STM32C551]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_mcu_stm32c551/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU STM32H723]]&lt;br /&gt;
| [[File:Sl_mcu_stm32h723_top_left_render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a STM32H723&lt;br /&gt;
| Prototype&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Other MCU's ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Image&lt;br /&gt;
! Description&lt;br /&gt;
! Status&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Connect]] (DAPlink FIrmware)&lt;br /&gt;
| [[File:Sl prog top left render.png|80px]]&lt;br /&gt;
| Streamline Programmer &amp;amp; Debugger.  This module is essentially identical to [[Jolt Connect]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_connect/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU RP2354]]&lt;br /&gt;
| [[File:Sl mcu rp2354 top left render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[RP2354]]&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_mcu_rp2354/ Available]&lt;br /&gt;
|- &lt;br /&gt;
| [[Streamline MCU ESP32S6]]&lt;br /&gt;
| [[File:Sl mcu esp32s6 top left render.png|80px]]&lt;br /&gt;
| Streamline MCU board including a [[ESP32S6]]&lt;br /&gt;
| Planned&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Miscellaneous ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Name&lt;br /&gt;
! Image&lt;br /&gt;
! Description&lt;br /&gt;
! Status&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Neopixel 8x8]]&lt;br /&gt;
| [[File:Sl neopixel 8x8 top left render.png|80px]]&lt;br /&gt;
| Module with an 8x8 matrix of Neopixel WS2812 LEDs&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_neopixel_8x8/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Storage SD]]&lt;br /&gt;
| [[File:Sl storage sd top left render rev A.png|80px]]&lt;br /&gt;
| Module with Micro-SD slot&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_sd/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Storage Flash]]&lt;br /&gt;
| [[File:Sl storage flash top left render.png|80px]]&lt;br /&gt;
| Module with 16 MB Flash storage ([[W25q128]])&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_flash/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Storage EEPROM]]&lt;br /&gt;
| [[File:Sl storage eeprom top left render.png|80px]]&lt;br /&gt;
| Module with 512kB EEPROM storage ([[M24M01]])&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_eeprom/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline IO Audio DAC]]&lt;br /&gt;
| [[File:Sl_io_pcm5102_top_left_render.png|80px]]&lt;br /&gt;
| PCM5102 I2S Audio DAC&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_io_audio_dac/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline IO CAN]]&lt;br /&gt;
| [[File:Sl_io_can_top_left_render.png|80px]]&lt;br /&gt;
| CAN bus interface&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_io_can/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline Ethernet]]&lt;br /&gt;
| [[File:Streamline_ethernet_top_left_render.png|80px]]&lt;br /&gt;
| Ethernet interface for STM32F407&lt;br /&gt;
| [https://currentmakers.com/products/streamline/streamline_ethernet/ Available]&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline SPI-Ethernet]]&lt;br /&gt;
| [[File:Streamline_seth_top_left_render.png|80px]]&lt;br /&gt;
| Ethernet interface via the SPI bus&lt;br /&gt;
| Prototype&lt;br /&gt;
|-&lt;br /&gt;
| [[Streamline RS485]]&lt;br /&gt;
| [[File:Streamline_rs485_top_left_render.png|80px]]&lt;br /&gt;
| Non-isolated RS485 interface&lt;br /&gt;
| Prototype&lt;br /&gt;
|-&lt;br /&gt;
| Streamline Real Time Clock&lt;br /&gt;
| &lt;br /&gt;
| Real time clock with on board coin cell battery (or supercap) backup&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline ESC&lt;br /&gt;
| &lt;br /&gt;
| Electronic speed control for BLDC motors&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline HS USB&lt;br /&gt;
| &lt;br /&gt;
| HS USB blade using [[USB3300]]&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline WiFi&lt;br /&gt;
| &lt;br /&gt;
| WiFi interface&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline Temperature/Humidity&lt;br /&gt;
| &lt;br /&gt;
| Sensor of temperature and humidity&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline Compass&lt;br /&gt;
| &lt;br /&gt;
| Compass&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline Gyro&lt;br /&gt;
| &lt;br /&gt;
| Gyro&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline Accelerometer&lt;br /&gt;
| &lt;br /&gt;
| Accelerometer&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline GPS&lt;br /&gt;
| &lt;br /&gt;
| GPS&lt;br /&gt;
| Idea&lt;br /&gt;
|-&lt;br /&gt;
| Streamline FM Radio&lt;br /&gt;
| &lt;br /&gt;
| FM and DAB radio receiver&lt;br /&gt;
| Idea&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Standard Pinout ==&lt;br /&gt;
&lt;br /&gt;
The pinout table is based on a [[MCU]] board with a 100 pin STM32F407.  Boards using [[STM32]] [[MCU]]s with less pins will leave some of the pins unused.&lt;br /&gt;
&lt;br /&gt;
* [[Streamline Peripheral Pin Mapping]] - peripheral-by-peripheral pin mapping.&lt;br /&gt;
* [[Streamline Header Mapping]] - Backplane-to-GPIO and GPIO-to-Backplane mapping.&lt;br /&gt;
&lt;br /&gt;
== Prototype Photos == &lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:Streamline Backplane with 2 blades - top right 3.jpg&lt;br /&gt;
File:Streamline Backplane with 2 blades - top right 2.jpg&lt;br /&gt;
File:Streamline Backplane with 2 blades - top right.jpg&lt;br /&gt;
File:Streamline Backplane with 2 blades - top left.jpg&lt;br /&gt;
File:Streamline Backplane with 2 blades - front.jpg&lt;br /&gt;
File:Streamline Multiple Blades.jpg&lt;br /&gt;
File:Streamline MCU STM32L475 - top view.jpg&lt;br /&gt;
File:Streamline MCU STM32L475 - bottom view.jpg&lt;br /&gt;
File:Streamline MCU STM32H562 - top view.jpg&lt;br /&gt;
File:Streamline MCU STM32H562 - bottom view.jpg&lt;br /&gt;
File:Streamline MCU STM32F407 - top view.jpg&lt;br /&gt;
File:Streamline MCU STM32F407 - bottom view.jpg&lt;br /&gt;
File:Streamline Connect - top view.jpg&lt;br /&gt;
File:Streamline Connect - bottom view.jpg&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links ==&lt;br /&gt;
&lt;br /&gt;
* [https://currentmakers.com CurrentMakers main site]&lt;br /&gt;
* [https://github.com/currentmakers/streamline_kicad_libraries Streamline KiCAD Libraries]&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Streamline_Peripheral_Pin_Mapping&amp;diff=8602</id>
		<title>Streamline Peripheral Pin Mapping</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Streamline_Peripheral_Pin_Mapping&amp;diff=8602"/>
		<updated>2026-09-26T07:42:33Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* SWD */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Streamline]][[Category:Work in progress]]&lt;br /&gt;
&lt;br /&gt;
== General notes ==&lt;br /&gt;
&amp;lt;span style=&amp;quot;background:#0C0&amp;quot;&amp;gt;field&amp;lt;/span&amp;gt; means that all MCUs can be mapped identically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;background:#CC0&amp;quot;&amp;gt;field&amp;lt;/span&amp;gt; means it is missing the peripheral.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;background:#F44&amp;quot;&amp;gt;field&amp;lt;/span&amp;gt; means that pins are not same across all MCUs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span style=&amp;quot;background:#0FF&amp;quot;&amp;gt;field&amp;lt;/span&amp;gt; is I2Sx_MCK only exist on one pin.&lt;br /&gt;
&lt;br /&gt;
=== RP2350 ===&lt;br /&gt;
&lt;br /&gt;
Trying to avoid 12 - 19 as these can be used for HSTX but they should be wired to backplane.&lt;br /&gt;
&lt;br /&gt;
== LED &amp;amp; Button ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | All&lt;br /&gt;
| bgcolor=#0C0|LED&lt;br /&gt;
| PC13&lt;br /&gt;
| PC13&lt;br /&gt;
| PC13&lt;br /&gt;
| PC13&lt;br /&gt;
| PC13&lt;br /&gt;
| PC13&lt;br /&gt;
| GPIO37&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|Button&lt;br /&gt;
| PC15&lt;br /&gt;
| PC15&lt;br /&gt;
| PC15&lt;br /&gt;
| PC15&lt;br /&gt;
| PC15&lt;br /&gt;
| PC15&lt;br /&gt;
| GPIO47&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SWD ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | All&lt;br /&gt;
| bgcolor=#0C0|SWDIO&lt;br /&gt;
| PA13&lt;br /&gt;
| PA13&lt;br /&gt;
| PA13&lt;br /&gt;
| PA13&lt;br /&gt;
| PA13&lt;br /&gt;
| PA13&lt;br /&gt;
| SWDIO&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP Pico Dedicated pin&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|SWCLK&lt;br /&gt;
| PA14&lt;br /&gt;
| PA14&lt;br /&gt;
| PA14&lt;br /&gt;
| PA14&lt;br /&gt;
| PA14&lt;br /&gt;
| PA14&lt;br /&gt;
| SWCLK&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SWO&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| TBD&lt;br /&gt;
| N/A&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== UARTs ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART1&lt;br /&gt;
| bgcolor=#0C0|TX&lt;br /&gt;
| PA9&lt;br /&gt;
| PA9&lt;br /&gt;
| PA9&lt;br /&gt;
| PA9&lt;br /&gt;
| PA9&lt;br /&gt;
| PA9&lt;br /&gt;
| GPIO0&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | Connect default&amp;lt;br/&amp;gt;RP2350 UART0&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|RX&lt;br /&gt;
| PA10&lt;br /&gt;
| PA10&lt;br /&gt;
| PA10&lt;br /&gt;
| PA10&lt;br /&gt;
| PA10&lt;br /&gt;
| PA10&lt;br /&gt;
| GPIO1&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART1A&lt;br /&gt;
| bgcolor=#0C0|TX&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|RX&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART2&lt;br /&gt;
| bgcolor=#0C0|TX&lt;br /&gt;
| PA2&lt;br /&gt;
| PA2&lt;br /&gt;
| PA2&lt;br /&gt;
| PA2&lt;br /&gt;
| PA2&lt;br /&gt;
| PA2&lt;br /&gt;
| GPIO44&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP2350 UART1&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|RX&lt;br /&gt;
| PA3&lt;br /&gt;
| PA3&lt;br /&gt;
| PA3&lt;br /&gt;
| PA3&lt;br /&gt;
| PA3&lt;br /&gt;
| PA3&lt;br /&gt;
| GPIO45&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART2A&lt;br /&gt;
| bgcolor=#0C0|TX&lt;br /&gt;
| PD5&lt;br /&gt;
| PD5&lt;br /&gt;
| PD5&lt;br /&gt;
| PD5&lt;br /&gt;
| PD5&lt;br /&gt;
| PD5&lt;br /&gt;
| GPIO4&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP2350 UART1&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|RX&lt;br /&gt;
| PD6&lt;br /&gt;
| PD6&lt;br /&gt;
| PD6&lt;br /&gt;
| PD6&lt;br /&gt;
| PD6&lt;br /&gt;
| PD6&lt;br /&gt;
| GPIO5&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART3&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART3A1&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PD8&lt;br /&gt;
| PD8&lt;br /&gt;
| PD8&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD8&lt;br /&gt;
| PD8&lt;br /&gt;
| GPIO6&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PD9&lt;br /&gt;
| PD9&lt;br /&gt;
| PD9&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD9&lt;br /&gt;
| PD9&lt;br /&gt;
| GPIO7&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART3A2&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | UART4&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PA0&lt;br /&gt;
| PA0&lt;br /&gt;
| PA0&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA0&lt;br /&gt;
| PA0&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PA1&lt;br /&gt;
| PA1&lt;br /&gt;
| PA1&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA1&lt;br /&gt;
| PA1&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | UART4A&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | UART5&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PD2&lt;br /&gt;
| PD2&lt;br /&gt;
| PD2&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD2&lt;br /&gt;
| PD2&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | USART6&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PC6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC6&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PC7&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC7&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC7&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CAN ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | CAN1&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| TBD&lt;br /&gt;
| rowspan=2 | Conflict with USB&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | CAN1A&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PD1&lt;br /&gt;
| PD1&lt;br /&gt;
| PD1&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD1&lt;br /&gt;
| PD1&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PD0&lt;br /&gt;
| PD0&lt;br /&gt;
| PD0&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD0&lt;br /&gt;
| PD0&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | CAN2&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13 &lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | CAN2A&lt;br /&gt;
| bgcolor=#CC0|TX&lt;br /&gt;
| PB6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB6&lt;br /&gt;
| TBD&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|RX&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB6&lt;br /&gt;
| TBD&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== USB ==&lt;br /&gt;
STM32F407=HS/FS host/device/otg&lt;br /&gt;
&lt;br /&gt;
STM32L475=FS host/device/otg no crystal&lt;br /&gt;
&lt;br /&gt;
STM32H562=FS PD&lt;br /&gt;
&lt;br /&gt;
STM32C071=FS no crystal&lt;br /&gt;
&lt;br /&gt;
STM32C551=FS host/device&lt;br /&gt;
&lt;br /&gt;
STM32H723=HS/FS host/device/otg&lt;br /&gt;
&lt;br /&gt;
RP2354=FS host/device&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | All&lt;br /&gt;
| bgcolor=#0C0|DM&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| PA11&lt;br /&gt;
| DM&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP2350 dedicated pins&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|DP&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| PA12&lt;br /&gt;
| DP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2C ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | I2C1&lt;br /&gt;
| bgcolor=#0C0|SCL&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| PB6&lt;br /&gt;
| GPIO9&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP2350 ICL0&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|SDA&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| PB7&lt;br /&gt;
| GPIO8&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | I2C1A&lt;br /&gt;
| bgcolor=#0C0|SCL&lt;br /&gt;
| PB8&lt;br /&gt;
| PB8&lt;br /&gt;
| PB8&lt;br /&gt;
| PB8&lt;br /&gt;
| PB8&lt;br /&gt;
| PB8&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|SDA&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | I2C2&lt;br /&gt;
| bgcolor=#0C0|SCL&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| GPIO3&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | RP2350 I2C1&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|SDA&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| PB11&lt;br /&gt;
| GPIO2&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | I2C3&lt;br /&gt;
| bgcolor=#F44|SCL&lt;br /&gt;
| PA8&lt;br /&gt;
| bgcolor=#F44|PC0&lt;br /&gt;
| PA8&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA8&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|SDA&lt;br /&gt;
| PC9&lt;br /&gt;
| bgcolor=#F44|PC1&lt;br /&gt;
| PC9&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC9&lt;br /&gt;
| TBD&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SPI ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
! Remarks&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI1&lt;br /&gt;
| bgcolor=#0C0|SCK&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| GPIO42&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | RP2350 SPI1&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MISO&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| GPIO40&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MOSI&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| GPIO43&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI1A&lt;br /&gt;
| bgcolor=#0C0|SCK&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | PB3 conflicts with SWO&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MISO&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MOSI&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI2&lt;br /&gt;
| bgcolor=#0C0|SCK&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| &lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MISO&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MOSI&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI2A&lt;br /&gt;
| bgcolor=#0C0|SCK&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| GPIO22&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | RP2350 SPI0&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MISO&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| GPIO20&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#0C0|MOSI&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| GPIO23 &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI3&lt;br /&gt;
| bgcolor=#CC0|SCK&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|MISO&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|MOSI&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; | SPI3A&lt;br /&gt;
| bgcolor=#CC0|SCK&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| &lt;br /&gt;
| Conflicts with SWO&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|MISO&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|MOSI&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SDIO ==&lt;br /&gt;
STM32F407: SDIO&lt;br /&gt;
&lt;br /&gt;
STM32L475: SDMMC&lt;br /&gt;
&lt;br /&gt;
STM32H562: SDMMC&lt;br /&gt;
&lt;br /&gt;
STM32C071: N/A&lt;br /&gt;
&lt;br /&gt;
STM32C551: N/A&lt;br /&gt;
&lt;br /&gt;
STM32H723: SDMMC&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; | Only&lt;br /&gt;
| bgcolor=#CC0|CMD&lt;br /&gt;
| PD2&lt;br /&gt;
| PD2&lt;br /&gt;
| PD2&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PD2&lt;br /&gt;
| GPIO30&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|CK&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC12&lt;br /&gt;
| GPIO31&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D0&lt;br /&gt;
| PC8&lt;br /&gt;
| PC8&lt;br /&gt;
| PC8&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC8&lt;br /&gt;
| GPIO35&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D1&lt;br /&gt;
| PC9&lt;br /&gt;
| PC9&lt;br /&gt;
| PC9&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC9&lt;br /&gt;
| GPIO34&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D2&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| GPIO33&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D3&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| GPIO32&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Ethernet (RMII) ==&lt;br /&gt;
STM32F407: &lt;br /&gt;
&lt;br /&gt;
STM32L475: &lt;br /&gt;
&lt;br /&gt;
STM32H562: &lt;br /&gt;
&lt;br /&gt;
STM32C071: &lt;br /&gt;
&lt;br /&gt;
STM32C551: &lt;br /&gt;
&lt;br /&gt;
STM32H723:&lt;br /&gt;
&lt;br /&gt;
RP2354: With PIO, https://github.com/rscott2049/pico-rmii-ethernet_nce &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;10&amp;quot; | Only&lt;br /&gt;
| bgcolor=#CC0| MDIO&lt;br /&gt;
| PA2&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA2&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA2&lt;br /&gt;
| GPIO44&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| REF_CLK&lt;br /&gt;
| PA1&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA1&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA1&lt;br /&gt;
| GPIO41&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| MDC&lt;br /&gt;
| PC1&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC1&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC1&lt;br /&gt;
| GPIO47&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| TX_EN&lt;br /&gt;
| PB11&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PB11&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PB11&lt;br /&gt;
| GPIO2&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| TXD0&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PB12&lt;br /&gt;
| GPIO24&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44| TXD1&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#F44 | PB15&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| GPIO22&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| RXD0&lt;br /&gt;
| PC4&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC4&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC4&lt;br /&gt;
| GPIO10&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| RXD1&lt;br /&gt;
| PC5&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC5&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PC5&lt;br /&gt;
| GPIO11&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| CRS_DV&lt;br /&gt;
| PA7&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA7&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| bgcolor=#CC0 | N/A&lt;br /&gt;
| PA7&lt;br /&gt;
| GPIO43&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ULPI ==&lt;br /&gt;
STM32F407: ULPI&lt;br /&gt;
&lt;br /&gt;
STM32L475: N/A&lt;br /&gt;
&lt;br /&gt;
STM32H562: N/A&lt;br /&gt;
&lt;br /&gt;
STM32C071: N/A&lt;br /&gt;
&lt;br /&gt;
STM32C551: N/A&lt;br /&gt;
&lt;br /&gt;
STM32H723: ULPI&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;12&amp;quot; | Only&lt;br /&gt;
| bgcolor=#CC0| STP&lt;br /&gt;
| PC0&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| PC0&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| DIR&lt;br /&gt;
| PC2&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| PC2&lt;br /&gt;
| bgcolor=#CC0| N/A &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| NXT&lt;br /&gt;
| PC3&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| PC3&lt;br /&gt;
| bgcolor=#CC0| N/A &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0| CK&lt;br /&gt;
| PA5&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| bgcolor=#CC0| N/A&lt;br /&gt;
| PA5&lt;br /&gt;
| bgcolor=#CC0| N/A &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D0&lt;br /&gt;
| PA3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D1&lt;br /&gt;
| PB0&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB0&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D2&lt;br /&gt;
| PB1&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB1&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D3&lt;br /&gt;
| PB10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D4&lt;br /&gt;
| PB11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D5&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D6&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|D7&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== I2S ==&lt;br /&gt;
STM32F407: I2S2, I2S3&lt;br /&gt;
&lt;br /&gt;
STM32L475: SAI1, SAI2  (serial audio interface)&lt;br /&gt;
&lt;br /&gt;
STM32H562: I2S1, I2S2, I2S3&lt;br /&gt;
&lt;br /&gt;
STM32C071: I2S1 (half duplex only)&lt;br /&gt;
&lt;br /&gt;
STM32C551: I2S1, I2S2, I2S3&lt;br /&gt;
&lt;br /&gt;
STM32H723: I2S1, I2S2, I2S3&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable sortable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | I2S1&lt;br /&gt;
| bgcolor=#F44|MCK&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#F44|PB8&lt;br /&gt;
| PC4&lt;br /&gt;
| bgcolor=#F44|PA6, PA11, PD5, PB4, PB6&lt;br /&gt;
| PC4&lt;br /&gt;
| PC4&lt;br /&gt;
| GPIO10&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|SDI&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#F44|PB5&lt;br /&gt;
| PA6&lt;br /&gt;
| bgcolor=#F44|PA2,PA7,PA12,PB5,PD6,PB6††&lt;br /&gt;
| PA6&lt;br /&gt;
| PA6&lt;br /&gt;
| GPIO40&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|SDO&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#F44|PC3&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| PA7&lt;br /&gt;
| GPIO43&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|CK&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#F44|PB10&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| GPIO42&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|WS&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#F44|PB9&lt;br /&gt;
| PA4&lt;br /&gt;
| bgcolor=#F44|PA1,PA5,PD8,PB3,PB6&lt;br /&gt;
| PA4&lt;br /&gt;
| PA4&lt;br /&gt;
| GPIO27&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | I2S2&lt;br /&gt;
| bgcolor=#F44|MCK&lt;br /&gt;
| PC6&lt;br /&gt;
| bgcolor=#F44|PB14&lt;br /&gt;
| PC6&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC6&lt;br /&gt;
| PC6&lt;br /&gt;
| GPIO26&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|SDI&lt;br /&gt;
| PC2&lt;br /&gt;
| bgcolor=#F44|PC12&lt;br /&gt;
| PC2&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC2&lt;br /&gt;
| PC2&lt;br /&gt;
| GPIO25&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|SDO&lt;br /&gt;
| PC3&lt;br /&gt;
| bgcolor=#F44|PD11&lt;br /&gt;
| PC3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC3&lt;br /&gt;
| PC3&lt;br /&gt;
| GPIO21&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#F44|CK&lt;br /&gt;
| PB10&lt;br /&gt;
| bgcolor=#F44|PD10&lt;br /&gt;
| PB10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB10&lt;br /&gt;
| PB10&lt;br /&gt;
| GPIO3&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|WS&lt;br /&gt;
| PB12&lt;br /&gt;
| PB12&lt;br /&gt;
| PB12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB12&lt;br /&gt;
| PB12&lt;br /&gt;
| GPIO24&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | I2S2a&lt;br /&gt;
| bgcolor=#0FF|MCK&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| PD9&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDI&lt;br /&gt;
| PB14&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB14&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB14&lt;br /&gt;
| PB14&lt;br /&gt;
| GPIO20&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDO&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB15&lt;br /&gt;
| PB15&lt;br /&gt;
| GPIO23&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|CK&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB13&lt;br /&gt;
| PB13&lt;br /&gt;
| GPIO22&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|WS&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB9&lt;br /&gt;
| PB9&lt;br /&gt;
| GPIO28&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | I2S3&lt;br /&gt;
| bgcolor=#CC0|MCK&lt;br /&gt;
| PC7&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC7&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC7&lt;br /&gt;
| PC7&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDI&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC11&lt;br /&gt;
| PC11&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDO&lt;br /&gt;
| PC12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC12&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC12&lt;br /&gt;
| PC12&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|CK&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PC10&lt;br /&gt;
| PC10&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|WS&lt;br /&gt;
| PA4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA4&lt;br /&gt;
| PA4&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;5&amp;quot; | I2S3a&lt;br /&gt;
| bgcolor=#0FF|MCK&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| bgcolor=#0FF|N/A&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDI&lt;br /&gt;
| PB4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB4&lt;br /&gt;
| PB4&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|SDO&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB5&lt;br /&gt;
| PB5&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|CK&lt;br /&gt;
| PB3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB3&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PB3&lt;br /&gt;
| PB3&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|WS&lt;br /&gt;
| PA15&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA15&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA15,PB8,PE4 †††&lt;br /&gt;
| PA15&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Notes; &lt;br /&gt;
&lt;br /&gt;
†† The STM32C071 has only one half-duplex I2S, and the _SD pin can either be in INPUT or OUTPUT mode. Many pins, how do we map?&lt;br /&gt;
&lt;br /&gt;
††† I2S3_WS is ONLY mapped on PA15 (used for SWDIO), except on STM32C551 which ALSO has it on PB8.&lt;br /&gt;
&lt;br /&gt;
== DACs ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; |DAC1&lt;br /&gt;
| bgcolor=#CC0|OUT1&lt;br /&gt;
| PA4&lt;br /&gt;
| PA4&lt;br /&gt;
| PA4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA4&lt;br /&gt;
| PA4&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|-&lt;br /&gt;
| bgcolor=#CC0|OUT2&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| PA5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
| PA5&lt;br /&gt;
| bgcolor=#CC0|N/A&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ADCs ==&lt;br /&gt;
ADCs are so numerous that comparative tables are difficult to establish.&lt;br /&gt;
&lt;br /&gt;
STM32F407; 12 channels common for ADC1, ADC2 and ADC3. 4 channels for ADC1 and ADC2&lt;br /&gt;
&lt;br /&gt;
STM32F407; 4 channels common for ADC1, ADC2 and ADC3. 12 channels for ADC1 and ADC2&lt;br /&gt;
&lt;br /&gt;
STM32H562; All 16 channels common for ADC1 and ADC2, can also be configured as 8 differential pair inputs.&lt;br /&gt;
&lt;br /&gt;
STM32C071; Only ADC1, 19 external channels.&lt;br /&gt;
&lt;br /&gt;
STM32C551; 7 channels common for ADC1 and ADC2.&lt;br /&gt;
&lt;br /&gt;
STM32H723; ADC1 and ADC2 are 16 bits, ADC3 is 12 bits. All 3 can also be configured as 8 differential pair inputs.&lt;br /&gt;
&lt;br /&gt;
RP2354; One ADC, 12 bits, 0.5 Msps&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Variant&lt;br /&gt;
! PIN &lt;br /&gt;
! STM32F407&lt;br /&gt;
! STM32L475&lt;br /&gt;
! STM32H562&lt;br /&gt;
! STM32C071&lt;br /&gt;
! STM32C551&lt;br /&gt;
! STM32H723&lt;br /&gt;
! RP2354&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |ADC1&lt;br /&gt;
| Pins&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| PA0-PA8,PA13-PA14,PC0-PC5,PB0-PB2,PB10-PB12&lt;br /&gt;
| PA0-PA7,PC0-PC3&lt;br /&gt;
| PA0-PA7,PC0-PC2,PC4-PC5,PB0-PB1&lt;br /&gt;
| GPIO40-GPIO47&lt;br /&gt;
|-&lt;br /&gt;
| Channels&lt;br /&gt;
| 16&lt;br /&gt;
| 16&lt;br /&gt;
| 16&lt;br /&gt;
| 19&lt;br /&gt;
| 12&lt;br /&gt;
| 15&lt;br /&gt;
| 8&lt;br /&gt;
|-&lt;br /&gt;
| Msps&lt;br /&gt;
| 2.4&lt;br /&gt;
| 5&lt;br /&gt;
| 5&lt;br /&gt;
| 2.5&lt;br /&gt;
| 2.25&lt;br /&gt;
| 3.6&lt;br /&gt;
| 0.5&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |ADC2&lt;br /&gt;
| Pins&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| PA0-PA7,PC0-PC5,PB0-PB1&lt;br /&gt;
| N/A&lt;br /&gt;
| PA0-PA3,PC1-PC5,PB0-PB2&lt;br /&gt;
| PA2-PA7,PC0-PC2,PC4-PC5,PB0-PB1&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| Channels&lt;br /&gt;
| 16&lt;br /&gt;
| 16&lt;br /&gt;
| 16&lt;br /&gt;
| N/A&lt;br /&gt;
| 12&lt;br /&gt;
| 13&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| Msps&lt;br /&gt;
| 2.4&lt;br /&gt;
| 5&lt;br /&gt;
| 5&lt;br /&gt;
| N/A&lt;br /&gt;
| 2.25&lt;br /&gt;
| 3.6&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;3&amp;quot; |ADC3&lt;br /&gt;
| Pins&lt;br /&gt;
| PA0-PA7,PC0-PC3&lt;br /&gt;
| PC0-PC3&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| PC0-PC3&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| Channels&lt;br /&gt;
| 6&lt;br /&gt;
| 4&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| 4 (12 bits)&lt;br /&gt;
| N/A&lt;br /&gt;
|-&lt;br /&gt;
| Msps&lt;br /&gt;
| 2.4&lt;br /&gt;
| 5&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| N/A&lt;br /&gt;
| 5&lt;br /&gt;
| N/A&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8582</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8582"/>
		<updated>2026-09-23T08:34:04Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Macros, Pragmas &amp;amp; Preprocessor */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;, and for &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; types it can also be &amp;lt;code&amp;gt;unsigned&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The compiler will also allow various emissions and stacking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
short i = 10;&lt;br /&gt;
long i = 10;&lt;br /&gt;
unsigned short i = 10;&lt;br /&gt;
unsigned long long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Type Casting ===&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax. One reason it is quite easy to end up with the worst spaghetti code possible, as the compiler will not guard against structural mismatching.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
=== Pointer Arithmetic ===&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The &amp;lt;code&amp;gt;volatile&amp;lt;/code&amp;gt; Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Common Compile Errors ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8581</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8581"/>
		<updated>2026-09-23T08:33:09Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Expressions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;, and for &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; types it can also be &amp;lt;code&amp;gt;unsigned&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The compiler will also allow various emissions and stacking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
short i = 10;&lt;br /&gt;
long i = 10;&lt;br /&gt;
unsigned short i = 10;&lt;br /&gt;
unsigned long long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Type Casting ===&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax. One reason it is quite easy to end up with the worst spaghetti code possible, as the compiler will not guard against structural mismatching.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
=== Pointer Arithmetic ===&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The &amp;lt;code&amp;gt;volatile&amp;lt;/code&amp;gt; Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8580</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8580"/>
		<updated>2026-09-23T08:28:46Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;, and for &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; types it can also be &amp;lt;code&amp;gt;unsigned&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The compiler will also allow various emissions and stacking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
short i = 10;&lt;br /&gt;
long i = 10;&lt;br /&gt;
unsigned short i = 10;&lt;br /&gt;
unsigned long long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Type Casting ===&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax. One reason it is quite easy to end up with the worst spaghetti code possible, as the compiler will not guard against structural mismatching.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
=== Pointer Arithmetic ===&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The &amp;lt;code&amp;gt;volatile&amp;lt;/code&amp;gt; Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8579</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8579"/>
		<updated>2026-09-23T08:23:26Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Variable types */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;, and for &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; types it can also be &amp;lt;code&amp;gt;unsigned&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The compiler will also allow various emissions and stacking.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
short i = 10;&lt;br /&gt;
long i = 10;&lt;br /&gt;
unsigned short i = 10;&lt;br /&gt;
unsigned long long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8578</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8578"/>
		<updated>2026-09-23T08:21:11Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Variable types */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;, and for &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; types it can also be &amp;lt;code&amp;gt;unsigned&amp;lt;/code&amp;gt;. The compiler will also allow various emissions, such as &amp;lt;code&amp;gt;long my_variable;&amp;lt;/code&amp;gt;, and stacking such as &amp;lt;code&amp;gt;unsigned long long my_variable;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8577</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8577"/>
		<updated>2026-09-23T08:16:59Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Loops &amp;amp; Iteration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8576</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8576"/>
		<updated>2026-09-23T08:16:46Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Loops &amp;amp; Iteration */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
while (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8575</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8575"/>
		<updated>2026-09-23T08:16:17Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Embedded */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exit since there is nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8574</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8574"/>
		<updated>2026-09-23T08:14:03Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Embedded */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop (called the &amp;quot;super loop&amp;quot; or &amp;quot;main loop&amp;quot;), or hand over control to an operating system that doesn't return.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8573</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8573"/>
		<updated>2026-09-23T08:12:12Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Embedded */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exit, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8572</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8572"/>
		<updated>2026-09-23T08:06:07Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Expressions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
=== Operators ===&lt;br /&gt;
&lt;br /&gt;
=== Order of Evaluation ===&lt;br /&gt;
&lt;br /&gt;
=== Precedence ===&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8571</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8571"/>
		<updated>2026-09-23T08:05:16Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Control Flow &amp;amp; Decision Making */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Expressions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8570</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8570"/>
		<updated>2026-09-23T08:04:31Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Standard UNIX */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;. The convention is that returning a non-zero value indicates an error.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8569</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8569"/>
		<updated>2026-09-23T08:03:46Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Standard UNIX */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(int argc, char** argv) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;int argc&amp;lt;/code&amp;gt; is a variable containing how many command-line arguments was received, and &amp;lt;code&amp;gt;char** argv&amp;lt;/code&amp;gt; is an array (think of a list) with those arguments). The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8568</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8568"/>
		<updated>2026-09-23T07:59:19Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Standard UNIX */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions interacting with the I/O system in the operating system, such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;void&amp;lt;/code&amp;gt; explain that the function does not need any input arguments. The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8567</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8567"/>
		<updated>2026-09-23T07:58:41Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Standard UNIX */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint (called &amp;quot;function prototype&amp;quot;) for functions such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;void&amp;lt;/code&amp;gt; explain that the function does not need any input arguments. The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8566</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8566"/>
		<updated>2026-09-23T07:56:39Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint for functions such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;void&amp;lt;/code&amp;gt; explain that the function does not need any input arguments. The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Running the same on a Debian 64-bit OS,&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 8&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 16&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 8&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice the difference of sizes of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;long int&amp;lt;/code&amp;gt; and others, but the same sizes of &amp;lt;code&amp;gt;uint8_t&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;uint16_t&amp;lt;/code&amp;gt; and so forth.&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8565</id>
		<title>Learning C</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Learning_C&amp;diff=8565"/>
		<updated>2026-09-23T07:50:01Z</updated>

		<summary type="html">&lt;p&gt;Niclas: /* Fixed width types */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:C]][[Category:Work in progress]]{{metadesc|Learning C tutorial}}&lt;br /&gt;
[[File:c-programming-language-1.jpg|thumb|300px|Cover of the 2nd edition of &amp;quot;The C Programming Language&amp;quot;]]&lt;br /&gt;
[[C]] is a general purpose programming language which was created in 1972 by Dennis Ritchie.&lt;br /&gt;
&lt;br /&gt;
While it was originally designed to implement operating systems, the features which made the language ideal for that purpose, also make it ideal for developing software for small resource constrained [[MCU]]s.&lt;br /&gt;
&lt;br /&gt;
On this page we will go through how to use C for embedded development.&lt;br /&gt;
{{clear}}&lt;br /&gt;
== Hello World - Desktop vs. MCU ==&lt;br /&gt;
&lt;br /&gt;
When learning any programming language, the first &amp;quot;project&amp;quot; will typically be a small program which prints out &amp;quot;Hello World!&amp;quot;.  C was designed to give programmers relatively direct access to the target hardware.  This was ideal for creating operating systems and indeed most, if not all, serious operating systems are largely implemented in C.  This design also make [[C]] an ideal language for developing embedded software targeting small [[MCU]]s with very limited resources.&lt;br /&gt;
&lt;br /&gt;
=== Standard UNIX ===&lt;br /&gt;
&lt;br /&gt;
On a standard UNIX system, that program, implemented in standard C, would typically look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
    printf(&amp;quot;Hello World!\n&amp;quot;);&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let us break down the classic C program line by line.&lt;br /&gt;
&lt;br /&gt;
Line 1 contains a '''Preprocessor Directive'''.  This essentially instruct the compiler to include a header file called &amp;lt;code&amp;gt;stdio.h&amp;lt;/code&amp;gt; (Standard Input/Output).  This file in turn contains the blueprint for functions such as &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
In line 3 follows a function declaration, creating a function named &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt;.  Every C program includes a function called &amp;lt;code&amp;gt;main&amp;lt;/code&amp;gt; and this function serves as the entry point.  The &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; tell the compiler that this function will return an integer value and the &amp;lt;code&amp;gt;void&amp;lt;/code&amp;gt; explain that the function does not need any input arguments. The actual function code is enclosed in a code block using curly brackets.&lt;br /&gt;
&lt;br /&gt;
In line 4 we call the function &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; which will print a string on the terminal executing the program.  The &amp;lt;code&amp;gt;printf&amp;lt;/code&amp;gt; is provided with an argument, which is a string enclosed in double-quotes &amp;lt;code&amp;gt;&amp;quot;Hello World!&amp;quot;&amp;lt;/code&amp;gt;.  The compiler will include this string as a constant.&lt;br /&gt;
&lt;br /&gt;
Finally in line 5, we return the value &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Most UNIX like systems have (or bloody should have) a C compiler, typically named &amp;lt;code&amp;gt;cc&amp;lt;/code&amp;gt;, so above &amp;lt;code&amp;gt;hello.c&amp;lt;/code&amp;gt; can be built into a binary executable thus:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ cc hello.c -o hello&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce a binary ''hello'', which can now be executed:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;Bash&amp;quot;&amp;gt;&lt;br /&gt;
$ ./hello&lt;br /&gt;
Hello World!&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Embedded ===&lt;br /&gt;
&lt;br /&gt;
On an embedded system using any [[MCU]] whether it be [[STM32]], [[RP2350]] or any other, things get a little bit more complicated.  On a [[UNIX]] system, the kernel knows how to execute a binary, and the shell (for example Bash) know how to load it, pass parameters and interpret the return value.  Typically, a [[MCU]] core will simply jump to a specific address - often &amp;lt;code&amp;gt;0x00000000&amp;lt;/code&amp;gt;.  The actual format of the binary depends on the [[MCU]] core in use and the architecture.  There are seldom a general way to print information, so typically the developer will need to specify how to print.  Finally, an embedded application will never actually exist, so the &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; will typically run an infinite loop called the &amp;quot;super loop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
During boot, typically, part of the [[MCU]] will need to be initialized.  It ''is'' possible to do this manually, but it will often be handled by some sdk provided by the vendor of the [[MCU]].  This startup code will typically lead up to calling &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Skipping the actual [[MCU]] initialization and only blinking a [[LED]] rather than printing a statement, a very minimal &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; could look something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
int main(void) {&lt;br /&gt;
    uint16_t led = PIN('C', 13);           // Blue LED&lt;br /&gt;
    RCC-&amp;gt;AHB1ENR |= BIT(PINBANK(led));     // Enable GPIO clock for LED&lt;br /&gt;
    gpio_set_mode(led, GPIO_MODE_OUTPUT);  // Set blue LED to output mode&lt;br /&gt;
    for (;;) {&lt;br /&gt;
        gpio_write(led, true);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
        gpio_write(led, false);&lt;br /&gt;
        spin(999999);&lt;br /&gt;
    }&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will all appear quite incomprehensible for a C beginner, but for now let's just realize that a C program always have a &amp;lt;code&amp;gt;main()&amp;lt;/code&amp;gt; function and on embedded systems this function will normally never exist since it's got nowhere else to go.&lt;br /&gt;
&lt;br /&gt;
== Variables &amp;amp; Fixed-Width Types ==&lt;br /&gt;
&lt;br /&gt;
Variables is one of the key concepts in (almost) any programming language.  A variable is a named container which can contain any value of the type of the variable.&lt;br /&gt;
&lt;br /&gt;
=== Variable declaration ===&lt;br /&gt;
&lt;br /&gt;
The most basic variable declaration in [[C]] looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code snippet will declare (create) a variable by the name &amp;lt;code&amp;gt;i&amp;lt;/code&amp;gt; with a '''type''' of &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and assigning an initial value of &amp;lt;code&amp;gt;10&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The type &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; is an integer.  The actual number of bits in an int depends on the platform but on a 32 bit [[MCU]] it is most often 32 bits.  The value is signed, so it can hold values in the following range:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;-2,147,483,648 to 2,147,483,647 (2^31 − 1)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Variable types ===&lt;br /&gt;
&lt;br /&gt;
[[C]] only defines a few basic types:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Type&lt;br /&gt;
! Range&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| char &lt;br /&gt;
| 0 - 255&lt;br /&gt;
| A single byte capable of holding one character &lt;br /&gt;
|- &lt;br /&gt;
| int&lt;br /&gt;
| -2,147,483,648 - 2,147,483,647&lt;br /&gt;
| An integer - on modern 32-bit [[MCU]]s typically a 32-bit value&lt;br /&gt;
|-&lt;br /&gt;
| float&lt;br /&gt;
| ±1.175 × 10⁻³⁸ to ±3.403 × 10³⁸&lt;br /&gt;
| 32-bit floating point value&lt;br /&gt;
|-&lt;br /&gt;
| double&lt;br /&gt;
| ±1.7 × 10^308&lt;br /&gt;
| 64-bit (8 byte) floating point&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The types can be modified by applying a so-called ''modifier'', which can be either &amp;lt;code&amp;gt;long&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;short&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot;&amp;gt;&lt;br /&gt;
long int i = 10;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Fixed width types ====&lt;br /&gt;
&lt;br /&gt;
In K&amp;amp;R, the following is mentioned:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Each compiler is free to choose appropriate sizes for its own hardware, subject only &lt;br /&gt;
to the the restriction that shorts and ints are at least 16 bits, longs are&lt;br /&gt;
at least 32 bits, and short is no longer than int, which is no longer than long&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This unpredictable nature can be troublesome when creating libraries that need to work on multiple platforms, or used for protocol and data format implementations.  For that reason, a library called &amp;lt;code&amp;gt;[https://en.cppreference.com/c/header/stdint stdint.h]&amp;lt;/code&amp;gt; was added in C99 containing: &amp;lt;code&amp;gt;uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t&amp;lt;/code&amp;gt; for unsigned and signed integer types of specific number of bits.&lt;br /&gt;
&lt;br /&gt;
==== Example ====&lt;br /&gt;
&lt;br /&gt;
Consider the following code running on a [[STM32]] [[MCU]]:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
    printf(&amp;quot;\n\n\n--------\nStarting\n&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;sizeof(char) = %d\n&amp;quot;, sizeof(char));&lt;br /&gt;
    printf(&amp;quot;sizeof(short int) = %d\n&amp;quot;, sizeof(short int));&lt;br /&gt;
    printf(&amp;quot;sizeof(int) = %d\n&amp;quot;, sizeof(int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long int) = %d\n&amp;quot;, sizeof(long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(long long int) = %d\n&amp;quot;, sizeof(long long int));&lt;br /&gt;
    printf(&amp;quot;sizeof(float) = %d\n&amp;quot;, sizeof(float));&lt;br /&gt;
    printf(&amp;quot;sizeof(double) = %d\n&amp;quot;, sizeof(double));&lt;br /&gt;
    printf(&amp;quot;sizeof(long double) = %d\n&amp;quot;, sizeof(long double));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint8_t) = %d\n&amp;quot;, sizeof(uint8_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint16_t) = %d\n&amp;quot;, sizeof(uint16_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint32_t) = %d\n&amp;quot;, sizeof(uint32_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(uint64_t) = %d\n&amp;quot;, sizeof(uint64_t));&lt;br /&gt;
    printf(&amp;quot;sizeof(void *) = %d\n&amp;quot;, sizeof(void*));&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will produce the following output:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--------&lt;br /&gt;
Starting&lt;br /&gt;
sizeof(char) = 1&lt;br /&gt;
sizeof(short int) = 2&lt;br /&gt;
sizeof(int) = 4&lt;br /&gt;
sizeof(long int) = 4&lt;br /&gt;
sizeof(long long int) = 8&lt;br /&gt;
sizeof(float) = 4&lt;br /&gt;
sizeof(double) = 8&lt;br /&gt;
sizeof(long double) = 8&lt;br /&gt;
sizeof(uint8_t) = 1&lt;br /&gt;
sizeof(uint16_t) = 2&lt;br /&gt;
sizeof(uint32_t) = 4&lt;br /&gt;
sizeof(uint64_t) = 8&lt;br /&gt;
sizeof(void *) = 4&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
Variables in [[C]] can be declared in different locations (scopes). If a variable is declared before any function declarations they are said to be ''global'', but a variable can also be declared inside a function which will limit the scope to that particular functions.  For this reason, local variables can have the same name and each will refer to the local value.&lt;br /&gt;
&lt;br /&gt;
== Control Flow &amp;amp; Decision Making ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Loops &amp;amp; Iteration ==&lt;br /&gt;
&lt;br /&gt;
=== The Super Loop ===&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, embedded programs are never supposed to exit, since they got nowhere to go really.  They often achieve this by running an endless loop, known as the super loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (1) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endless loop ''can'' be written in multiple ways.  Another often seen approach is:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
for (;;) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Which one is the most efficient is a topic of an almost religious discussion.  There might have been a time where one was better than the other (better as in wasting less cpu cycles) but with modern optimizing compilers I doubt very much that there will be any difference.&lt;br /&gt;
&lt;br /&gt;
Fans of &amp;quot;The Hitchhikers Guide to the Galaxy&amp;quot; will appreciate:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
While (42) { // Loop forever&lt;br /&gt;
    // Do something here&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will execute exactly like the first one, taking advantage of the fact that anything non-zero is &amp;quot;true&amp;quot; per definition.&lt;br /&gt;
&lt;br /&gt;
For the final approach we'll look at a feature seldom used in [[C]] programs.  K&amp;amp;R describes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
C provides the infinitely-abusable goto statement, and labels to branch to. Formally, the goto&lt;br /&gt;
statement is never necessary, and in practice it is almost always easy to write code without it.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Apart from being hilarious it does open the possibility of doing:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;C&amp;quot; line&amp;gt;&lt;br /&gt;
loop_start:&lt;br /&gt;
//Do something&lt;br /&gt;
goto loop_start;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While I have ''never'' seen this in the wild, it is technically valid [[C]] syntax.&lt;br /&gt;
&lt;br /&gt;
== Functions &amp;amp; Modular Code == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Bitwise Operations Masterclass == &lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Pointers Demystified ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Arrays &amp;amp; Pointer Arithmetic ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Structs &amp;amp; Memory Alignment ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== The volatile Keyword ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Type Casting &amp;amp; Unions ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Function Pointers &amp;amp; Callbacks ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Enums &amp;amp; State Machines ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Dynamic Memory vs. Static Allocation ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Macros, Pragmas &amp;amp; Preprocessor ==&lt;br /&gt;
&lt;br /&gt;
To be added&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links == &lt;br /&gt;
&lt;br /&gt;
* &amp;lt;span class=&amp;quot;newwin&amp;quot;&amp;gt;[[Media:The C Programming Language (Kernighan Ritchie).pdf|Kernighan &amp;amp; Ritchie: The C Programming Language (2nd Edition)]]&amp;lt;/span&amp;gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Streamline_MCU_STM32H723&amp;diff=8512</id>
		<title>Streamline MCU STM32H723</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Streamline_MCU_STM32H723&amp;diff=8512"/>
		<updated>2026-09-17T13:05:41Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:STM32]][[Category:Streamline]][[Category:Streamline Blade]]{{metadesc|STM32H723 Streamline MCU board documentation}}&lt;br /&gt;
[[File:sl_mcu_stm32h723-rev_A_top_right_render.png|350px|thumb|Streamline MCU STM32H723]]&lt;br /&gt;
The [[Streamline MCU STM32H723]] is a higher performant [[Streamline]] [[STM32]] [[MCU]] board build around a [[STM32H723]].&lt;br /&gt;
{{clear}}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
|'''MCU'''&lt;br /&gt;
|STM32H723VE&lt;br /&gt;
|-&lt;br /&gt;
|'''Flash'''&lt;br /&gt;
|512kB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM'''&lt;br /&gt;
|564kB&lt;br /&gt;
|-&lt;br /&gt;
|'''Crystal'''&lt;br /&gt;
|16MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''LED'''&lt;br /&gt;
|PC13 (open drain)&lt;br /&gt;
|-&lt;br /&gt;
|'''Button'''&lt;br /&gt;
|Boot0/PA14 (pulled down)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Sl mcu stm32h723.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_right_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_left_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_right_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links ==&lt;br /&gt;
&lt;br /&gt;
* [https://www.st.com/resource/en/datasheet/stm32h723ve.pdf Datasheet]&lt;br /&gt;
* [https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf STM32H723/725/730/733/735 Reference Manual]&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=Streamline_MCU_STM32H723&amp;diff=8511</id>
		<title>Streamline MCU STM32H723</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=Streamline_MCU_STM32H723&amp;diff=8511"/>
		<updated>2026-09-17T13:05:21Z</updated>

		<summary type="html">&lt;p&gt;Niclas: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:STM32]][[Category:Streamline]][[Category:Streamline Blade]]{{metadesc|STM32H723 Streamline MCU board documentation}}&lt;br /&gt;
[[File:sl_mcu_stm32h723-rev_A_top_right_render.png|350px|thumb|Streamline MCU STM32H723]]&lt;br /&gt;
The [[Streamline MCU STM32H723]] is a higher performant [[Streamline]] [[STM32]] [[MCU]] board build around a [[STM32H723]].&lt;br /&gt;
{{clear}}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| &lt;br /&gt;
|-&lt;br /&gt;
|'''MCU'''&lt;br /&gt;
|STM32H723VE&lt;br /&gt;
|-&lt;br /&gt;
|'''Flash'''&lt;br /&gt;
|512kB&lt;br /&gt;
|-&lt;br /&gt;
|'''RAM'''&lt;br /&gt;
|564kB&lt;br /&gt;
|-&lt;br /&gt;
|'''Crystal'''&lt;br /&gt;
|16MHz&lt;br /&gt;
|-&lt;br /&gt;
|'''LED'''&lt;br /&gt;
|PC13 (open drain)&lt;br /&gt;
|-&lt;br /&gt;
|'''Button'''&lt;br /&gt;
|Boot0/PC15 (pulled down)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Schematics ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;res-img&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Sl mcu stm32h723.svg|2400px]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gallery ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery&amp;gt;&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_right_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_top_left_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_right_render.png&lt;br /&gt;
File:sl_mcu_stm32h723-rev_A_bot_left_render.png&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Miscellaneous Links ==&lt;br /&gt;
&lt;br /&gt;
* [https://www.st.com/resource/en/datasheet/stm32h723ve.pdf Datasheet]&lt;br /&gt;
* [https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf STM32H723/725/730/733/735 Reference Manual]&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_bot_render.png&amp;diff=8510</id>
		<title>File:Sl mcu rp2354 bot render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_bot_render.png&amp;diff=8510"/>
		<updated>2026-09-17T13:01:07Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Sl mcu rp2354 bot render.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_left_render.png&amp;diff=8509</id>
		<title>File:Sl mcu rp2354 top left render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_left_render.png&amp;diff=8509"/>
		<updated>2026-09-17T13:00:33Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Sl mcu rp2354 top left render.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_render.png&amp;diff=8508</id>
		<title>File:Sl mcu rp2354 top render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_render.png&amp;diff=8508"/>
		<updated>2026-09-17T13:00:06Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Sl mcu rp2354 top render.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_right_render.png&amp;diff=8507</id>
		<title>File:Sl mcu rp2354 top right render.png</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354_top_right_render.png&amp;diff=8507"/>
		<updated>2026-09-17T12:59:34Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Sl mcu rp2354 top right render.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354.svg&amp;diff=8506</id>
		<title>File:Sl mcu rp2354.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Sl_mcu_rp2354.svg&amp;diff=8506"/>
		<updated>2026-09-17T12:58:16Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Sl mcu rp2354.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;File uploaded with MsUpload&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
	<entry>
		<id>https://stm32world.com/w/index.php?title=File:Streamline_seth.svg&amp;diff=8505</id>
		<title>File:Streamline seth.svg</title>
		<link rel="alternate" type="text/html" href="https://stm32world.com/w/index.php?title=File:Streamline_seth.svg&amp;diff=8505"/>
		<updated>2026-09-17T12:51:12Z</updated>

		<summary type="html">&lt;p&gt;Niclas: Niclas uploaded a new version of File:Streamline seth.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Niclas</name></author>
	</entry>
</feed>