<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>STM32 Tutorial Archives | Microcontroller Tutorials</title>
	<atom:link href="https://www.teachmemicro.com/category/tutorials/stm32-tutorial/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.teachmemicro.com/category/tutorials/stm32-tutorial/</link>
	<description>Microcontroller Tutorials and Resources</description>
	<lastBuildDate>Sun, 28 Jun 2026 02:22:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>

<image>
	<url>https://www.teachmemicro.com/wp-content/uploads/2019/04/blue-icon-65x65.png</url>
	<title>STM32 Tutorial Archives | Microcontroller Tutorials</title>
	<link>https://www.teachmemicro.com/category/tutorials/stm32-tutorial/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Understanding -O1, -O2, -O3, -Os, and -Og in Embedded Firmware</title>
		<link>https://www.teachmemicro.com/understanding-o1-o2-o3-os-and-og-in-embedded-firmware/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=understanding-o1-o2-o3-os-and-og-in-embedded-firmware</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 23:00:21 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11810</guid>

					<description><![CDATA[<p>flashWhen compiling embedded C or C++ firmware, you will often see compiler flags such as -O0, -O1, -O2, -O3, -Os, and -Og. These are compiler optimization levels. They tell the compiler how much effort it should spend improving the generated machine code. For desktop applications, optimization usually means “make the program run faster.” In embedded &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/understanding-o1-o2-o3-os-and-og-in-embedded-firmware/">Understanding -O1, -O2, -O3, -Os, and -Og in Embedded Firmware</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>flashWhen compiling embedded C or C++ firmware, you will often see compiler flags such as <em><strong>-O0, -O1, -O2, -O3, -Os</strong></em>, and <strong><em>-Og</em></strong>. These are compiler optimization levels. They tell the compiler how much effort it should spend improving the generated machine code.</p>
<p>For desktop applications, optimization usually means “make the program run faster.” In embedded systems, optimization has a wider meaning. You may care about speed, flash usage, RAM usage, interrupt latency, power consumption, boot time, or debugging reliability. Choosing the wrong optimization level can result in larger firmware, increased difficulty in debugging, or even expose bugs that were previously hidden at lower optimization levels.</p>
<p>This tutorial explains what each optimization level does, when to use it, and what embedded developers should be aware of.</p>
<p><span id="more-11810"></span></p>
<hr />
<h2>What Is Compiler Optimization?</h2>
<p>Compiler optimization is the process of transforming your source code into more efficient machine code without changing the intended behavior of the program.</p>
<p>For example, this code:</p>
<pre><code class="language-c">int x = 5 * 10;
</code></pre>
<p>may be compiled as if it were written like this:</p>
<pre><code class="language-c">int x = 50;
</code></pre>
<p>The compiler can also remove unused code, simplify loops, inline small functions, reduce memory accesses, and store variables in CPU registers instead of RAM.</p>
<p>In embedded systems, these optimizations can make a big difference. A small 8-bit <a href="https://www.teachmemicro.com/arduino-tutorials/what-is-arduino/">AVR</a>, an ARM Cortex-M0, or an <a href="https://www.teachmemicro.com/esp32-board-guide-for-beginners/">ESP32</a> has limited flash, limited RAM, and real-time timing requirements. The compiler’s optimization level affects all of these.</p>
<hr />
<h2>Common Optimization Levels</h2>
<h3>-O0 : No Optimization</h3>
<p>-O0 means optimization is disabled. This is usually the default when no optimization flag is provided.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -O0 -g -o firmware.elf
</code></pre>
<p>This level is commonly used during early debugging because the generated code closely follows the source code.</p>
<p>Advantages:</p>
<div class="checklist tie-list-shortcode">
<ul>
<li>Fast compile time</li>
<li>Easiest debugging</li>
<li>Variables are easier to inspect</li>
<li>Breakpoints behave more predictably</li>
</ul>
</div>
<p>Disadvantages:</p>
<div class="cons tie-list-shortcode">
<ul>
<li>Larger code</li>
<li>Slower execution</li>
<li>Higher power consumption in some cases</li>
<li>Timing may be very different from release builds</li>
</ul>
</div>
<p>&nbsp;</p>
<p>For embedded development, -O0 is useful while bringing up hardware, checking peripheral initialization, or stepping through code line by line. However, you should not assume that firmware tested only at -O0 will behave the same way at release optimization levels.</p>
<hr />
<h2>-O1: Basic Optimization</h2>
<p>-O1 enables a basic set of optimizations. It improves code quality without being too aggressive.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -O1 -g -o firmware.elf
</code></pre>
<p>At this level, the compiler may remove unused code, simplify expressions, reduce redundant memory loads, and make better use of CPU registers.</p>
<p>For embedded use, -O1 is a good first step when you want better performance than -O0 but still want debugging to remain somewhat manageable.</p>
<p>Use -O1 when:</p>
<pre><code class="language-text">- You want light optimization
- You are debugging a timing-sensitive issue
- `-O0` is too slow or too large
- You are not yet ready to use full release optimization
</code></pre>
<p>However, many embedded projects skip -O1 and use either -Og for debugging or -O2 / -Os for release builds.</p>
<hr />
<h2>-O2: Common Release Optimization</h2>
<p>-O2 is one of the most common optimization levels for production firmware.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -O2 -o firmware.elf
</code></pre>
<p>This level enables many optimizations that improve speed without usually causing a huge increase in code size.</p>
<p>In embedded systems, -O2 is often a good default for release builds when performance matters.</p>
<p>Typical effects of -O2 include:</p>
<pre><code class="language-text">- Faster loops
- Better register allocation
- Dead code elimination
- Common subexpression elimination
- Function inlining where reasonable
- Improved instruction scheduling
</code></pre>
<p>For microcontrollers like STM32, SAMD, ESP32, RP2040, and many ARM Cortex-M devices, -O2 often gives a good balance between speed and size.</p>
<p>Use -O2 when:</p>
<pre><code class="language-text">- You are building release firmware
- Execution speed matters
- Your flash size is not extremely tight
- You want a stable general-purpose optimization level
</code></pre>
<p>One important warning: debugging optimized code can be confusing. Variables may appear as “optimized out,” breakpoints may not behave exactly as expected, and the compiler may rearrange instructions.</p>
<hr />
<h2>-O3: Aggressive Speed Optimization</h2>
<p>-O3 enables more aggressive optimizations than -O2.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -O3 -o firmware.elf
</code></pre>
<p>It may perform more aggressive inlining, loop transformations, and other speed-focused optimizations.</p>
<p>However, -O3 is not always better for embedded firmware.</p>
<p>Why?</p>
<p>Because embedded systems often have limited flash, limited cache, and strict timing requirements. Aggressive inlining can make the firmware larger. Larger code may reduce instruction cache efficiency or exceed flash limits. In some cases, -O3 can make firmware slower than -O2.</p>
<p>Use -O3 only when:</p>
<pre><code class="language-text">- You have measured a real performance problem
- You have benchmarked `-O3` against `-O2`
- Your flash size is still acceptable
- You have tested timing-sensitive code carefully
</code></pre>
<p>Do not assume that -O3 is automatically the best release setting. In embedded work, measurement matters more than the optimization number.</p>
<hr />
<h2>-Os: Optimize for Size</h2>
<p>-Os tells the compiler to optimize for smaller code size.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -Os -o firmware.elf
</code></pre>
<p>This is very useful in embedded systems because flash memory is often limited.</p>
<p>For example, if your firmware is close to the flash limit of an ATmega328P, STM32F030, PIC32, or other small microcontroller, -Os may help the firmware fit.</p>
<p>Use -Os when:</p>
<pre><code class="language-text">- Flash memory is limited
- You are building for small MCUs
- Code size matters more than maximum speed
- You are near the firmware size limit
</code></pre>
<p>For many embedded projects, -Os is a better release choice than -O2, especially for small devices.</p>
<p>Common examples:</p>
<pre><code class="language-text">AVR/Arduino Uno: often use -Os
Small ARM Cortex-M0/M0+: often use -Os
Bootloaders: often use -Os
Tiny sensor nodes: often use -Os
</code></pre>
<p>However, smaller code is not always slower. In some MCUs, smaller code can perform well because it fits better in flash or cache.</p>
<hr />
<h2>-Og: Optimize for Debugging</h2>
<p>-Og is designed to improve debugging while still enabling some optimizations.</p>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c -Og -g -o firmware.elf
</code></pre>
<p>For embedded development, -Og is often better than -O0 once your project becomes more complex.</p>
<p>It keeps debugging usable while allowing the compiler to perform optimizations that do not heavily interfere with the debugging experience.</p>
<p>Use -Og when:</p>
<pre><code class="language-text">- You are actively debugging firmware
- You want better code than -O0
- You still need meaningful breakpoints and variable inspection
- You want your debug build to behave closer to release builds
</code></pre>
<p>A good embedded workflow is:</p>
<pre><code class="language-text">Debug build:   -Og -g
Release build: -O2 or -Os
</code></pre>
<hr />
<h2>Recommended Optimization Levels for Embedded Projects</h2>
<p>A practical setup looks like this:</p>
<pre><code class="language-text">Development / debugging:
-Og -g

Early hardware bring-up:
-O0 -g

Normal release build:
-O2

Flash-limited release build:
-Os

Performance-critical release build:
-O3 only after benchmarking
</code></pre>
<p>For example, an STM32 project might use:</p>
<pre><code class="language-bash">CFLAGS_DEBUG = -Og -g3
CFLAGS_RELEASE = -O2
</code></pre>
<p>An AVR project might use:</p>
<pre><code class="language-bash">CFLAGS_DEBUG = -Og -g3
CFLAGS_RELEASE = -Os
</code></pre>
<p>A bootloader might use:</p>
<pre><code class="language-bash">CFLAGS_RELEASE = -Os
</code></pre>
<hr />
<h2>Why Code Works at -O0 but Fails at -O2</h2>
<p>This is one of the most common embedded firmware problems.</p>
<p>A developer writes code, tests it at -O0, and everything works. Then they enable -O2 or -Os, and the firmware stops working.</p>
<p>It is tempting to blame the compiler, but the real cause is usually a bug in the code. Optimization often exposes bugs that were already present.</p>
<p>Common causes include:</p>
<div class="plus tie-list-shortcode">
<ul>
<li>Missing volatile</li>
<li>Undefined behavior</li>
<li>Bad pointer usage</li>
<li>Stack overflow</li>
<li>Race conditions</li>
<li>Timing assumptions</li>
<li>Uninitialized variables</li>
<li>Incorrect delay loops</li>
<li>Memory-mapped registers accessed incorrectly</li>
</ul>
</div>
<hr />
<h2><strong>The Importance of volatile</strong></h2>
<p>In embedded C, <em>volatile</em> tells the compiler that a variable can change outside the normal program flow.</p>
<p>This is important for:</p>
<div class="starlist tie-list-shortcode">
<ul>
<li>Hardware registers</li>
<li>Interrupt service routines</li>
<li>Flags shared between ISR and main code</li>
<li>Memory-mapped peripherals</li>
</ul>
</div>
<p>Consider this example:</p>
<pre><code class="language-c">int button_pressed = 0;

void EXTI0_IRQHandler(void)
{
    button_pressed = 1;
}

int main(void)
{
    while (button_pressed == 0)
    {
        // wait
    }

    // continue when button is pressed
}
</code></pre>
<p>At higher optimization levels, the compiler may assume that <pre><code class="language-cpp">button_pressed</code></pre> does not change inside the <pre><code class="language-cpp">while</code></pre> loop because it cannot see any code inside the loop modifying it. The compiler may optimize the loop into an infinite loop.</p>
<p>The correct version is:</p>
<pre><code class="language-c">volatile int button_pressed = 0;

void EXTI0_IRQHandler(void)
{
    button_pressed = 1;
}

int main(void)
{
    while (button_pressed == 0)
    {
        // wait
    }

    // continue when button is pressed
}
</code></pre>
<p>Now the compiler knows it must reload <em>button_pressed</em> from memory each time.</p>
<p>Use <em>volatile</em> for variables that can change due to interrupts or hardware.</p>
<p>Do not use <em>volatile</em> as a general fix for all optimization problems. It is not a replacement for proper locking, atomic access, or good program design.</p>
<hr />
<h2>Hardware Registers and Optimization</h2>
<p>Peripheral registers must usually be accessed through volatile-qualified pointers or structs.</p>
<p>Example:</p>
<pre><code class="language-c">#define GPIOA_ODR (*(volatile uint32_t *)0x48000014)

void led_on(void)
{
    GPIOA_ODR |= (1 &lt;&lt; 5);
}
</code></pre>
<p>Without <em>volatile</em>, the compiler might remove or combine register accesses in ways that are valid for normal memory but incorrect for hardware registers.</p>
<p>Most vendor libraries already handle this correctly. For example, STM32 HAL, CMSIS, AVR headers, and ESP-IDF register definitions normally mark hardware registers as volatile.</p>
<hr />
<h2>Delay Loops Can Break Under Optimization</h2>
<p>This is bad embedded code:</p>
<pre><code class="language-c">void delay(void)
{
    for (int i = 0; i &lt; 100000; i++)
    {
    }
}
</code></pre>
<p>At higher optimization levels, the compiler may remove the loop because it does nothing.</p>
<p>A slightly better version is:</p>
<pre><code class="language-c">void delay(void)
{
    for (volatile int i = 0; i &lt; 100000; i++)
    {
    }
}
</code></pre>
<p>But the best solution is to use a hardware timer, SysTick, RTOS delay, or vendor-provided delay function.</p>
<p>Better examples:</p>
<pre><code class="language-c">HAL_Delay(100);
</code></pre>
<p>or:</p>
<pre><code class="language-c">vTaskDelay(pdMS_TO_TICKS(100));
</code></pre>
<p>or a timer-based delay function.</p>
<p>In embedded firmware, timing should not rely on empty loops unless you fully understand the compiler, CPU clock, and generated assembly.</p>
<hr />
<h2>Code Size: -O2 vs -Os</h2>
<p>In embedded work, smaller code is often better.</p>
<p>A typical comparison might look like this:</p>
<pre><code class="language-text">-O0:  42 KB
-O1:  31 KB
-O2:  28 KB
-Os:  24 KB
-O3:  36 KB
</code></pre>
<p>This is not universal, but it shows a common pattern.</p>
<p>-O0 is often large because the compiler does not simplify much.</p>
<p>-O2 often reduces size while improving speed.</p>
<p>-Os usually gives the smallest output.</p>
<p>-O3 may increase size because of aggressive inlining and loop optimizations.</p>
<p>Always check your firmware size after changing optimization levels.</p>
<p>For GCC-based embedded builds, you may see output like:</p>
<pre><code class="language-text">text    data     bss     dec     hex
24576   128      2048    26752   6880
</code></pre>
<p>Where:</p>
<pre><code class="language-text">text = code and constants in flash
data = initialized variables copied to RAM
bss  = zero-initialized variables in RAM
</code></pre>
<hr />
<h2>Useful Size Optimization Flags</h2>
<p>For embedded firmware, -Os is commonly combined with section garbage collection.</p>
<p>Compiler flags:</p>
<pre><code class="language-bash">-ffunction-sections -fdata-sections
</code></pre>
<p>Linker flag:</p>
<pre><code class="language-bash">-Wl,--gc-sections
</code></pre>
<p>Example:</p>
<pre><code class="language-bash">arm-none-eabi-gcc main.c \
  -Os \
  -ffunction-sections \
  -fdata-sections \
  -Wl,--gc-sections \
  -o firmware.elf
</code></pre>
<p>These options allow the linker to remove unused functions and data from the final firmware image.</p>
<p>This is especially useful when using large libraries where only a small part of the library is actually needed.</p>
<hr />
<h2>Optimization and Interrupts</h2>
<p>Optimization can affect interrupt-related code if shared variables are not handled correctly.</p>
<p>Example:</p>
<pre><code class="language-c">uint8_t rx_ready = 0;

void USART_IRQHandler(void)
{
    rx_ready = 1;
}

int main(void)
{
    while (!rx_ready)
    {
    }

    process_data();
}
</code></pre>
<p>This should be:</p>
<pre><code class="language-c">volatile uint8_t rx_ready = 0;

void USART_IRQHandler(void)
{
    rx_ready = 1;
}

int main(void)
{
    while (!rx_ready)
    {
    }

    process_data();
}
</code></pre>
<p>For multi-byte variables shared with interrupts, <pre><code class="language-cpp">volatile</code></pre> alone may not be enough.</p>
<p>Example:</p>
<pre><code class="language-c">volatile uint32_t tick_count;
</code></pre>
<p>On an 8-bit MCU, reading a 32-bit value may require multiple instructions. An interrupt could update the value halfway through the read.</p>
<p>In that case, you may need to temporarily disable interrupts or use an atomic access method.</p>
<p>Example:</p>
<pre><code class="language-c">uint32_t get_tick_count(void)
{
    uint32_t value;

    __disable_irq();
    value = tick_count;
    __enable_irq();

    return value;
}
</code></pre>
<p>The exact method depends on your platform.</p>
<hr />
<h2>Optimization and Debugging</h2>
<p>When optimization is enabled, debugging can become confusing.</p>
<p>You may see:</p>
<pre><code class="language-text">- Variables shown as &lt;optimized out&gt;
- Breakpoints skipped
- Source lines executed out of order
- Functions inlined and not visible in the call stack
- Loops transformed into different assembly
</code></pre>
<p>This does not necessarily mean the debugger is broken. It means the compiler changed the code structure while preserving the intended behavior.</p>
<p>For debugging, prefer:</p>
<pre><code class="language-bash">-Og -g3
</code></pre>
<p>Instead of:</p>
<pre><code class="language-bash">-O2 -g
</code></pre>
<p>You can still debug -O2 builds, but the experience is harder.</p>
<hr />
<h2>Per-Function Optimization</h2>
<p>Sometimes you may want most of the firmware optimized normally, but one function optimized differently.</p>
<p>With GCC, you can use function attributes.</p>
<p>Example:</p>
<pre><code class="language-c">__attribute__((optimize("O0")))
void debug_sensitive_function(void)
{
    // Easier to debug
}
</code></pre>
<p>Or:</p>
<pre><code class="language-c">__attribute__((optimize("O3")))
void performance_critical_function(void)
{
    // Speed-critical code
}
</code></pre>
<p>Use this carefully. Per-function optimization can be useful, but it can also make the build harder to understand and maintain.</p>
<hr />
<h2>Practical Embedded Build Recommendations</h2>
<p>For most embedded firmware projects, use separate debug and release configurations.</p>
<h3>Debug Build</h3>
<pre><code class="language-bash">-Og -g3
</code></pre>
<p>Good for:</p>
<pre><code class="language-text">- Stepping through code
- Inspecting variables
- Debugging peripheral setup
- Testing logic
</code></pre>
<h3>Release Build for Speed</h3>
<pre><code class="language-bash">-O2
</code></pre>
<p>Good for:</p>
<pre><code class="language-text">- General production firmware
- Motor control
- Communication stacks
- Real-time applications
- DSP-like code, if size is acceptable
</code></pre>
<h3>Release Build for Size</h3>
<pre><code class="language-bash">-Os
</code></pre>
<p>Good for:</p>
<pre><code class="language-text">- Small microcontrollers
- Bootloaders
- Arduino/AVR projects
- Battery-powered sensor nodes
- Firmware near the flash limit
</code></pre>
<h3>Aggressive Performance Build</h3>
<pre><code class="language-bash">-O3
</code></pre>
<p>Use only after testing and benchmarking.</p>
<hr />
<h2>Example Makefile Setup</h2>
<pre><code class="language-makefile">MCU = cortex-m4

CC = arm-none-eabi-gcc

COMMON_FLAGS = \
    -mcpu=$(MCU) \
    -mthumb \
    -Wall \
    -Wextra \
    -ffunction-sections \
    -fdata-sections

DEBUG_FLAGS = -Og -g3
RELEASE_FLAGS = -O2

LDFLAGS = -Wl,--gc-sections

debug:
	$(CC) $(COMMON_FLAGS) $(DEBUG_FLAGS) main.c $(LDFLAGS) -o firmware_debug.elf

release:
	$(CC) $(COMMON_FLAGS) $(RELEASE_FLAGS) main.c $(LDFLAGS) -o firmware_release.elf

size:
	arm-none-eabi-size firmware_release.elf
</code></pre>
<p>For a size-focused release build, change:</p>
<pre><code class="language-makefile">RELEASE_FLAGS = -O2
</code></pre>
<p>to:</p>
<pre><code class="language-makefile">RELEASE_FLAGS = -Os
</code></pre>
<hr />
<h2>How to Choose the Right Optimization Level</h2>
<p>A good decision flow is:</p>
<pre><code class="language-text">Are you debugging?
Use -Og -g3.

Are you doing early board bring-up?
Use -O0 -g or -Og -g3.

Are you building production firmware?
Use -O2.

Are you running out of flash?
Use -Os.

Are you chasing maximum speed?
Try -O3, but compare it against -O2.

Did the code break when optimization was enabled?
Look for missing volatile, undefined behavior, race conditions, timing assumptions, or stack problems.
</code></pre>
<hr />
<h2>Final Thoughts</h2>
<p>Compiler optimization is not just a performance setting. In embedded systems, it affects code size, timing, debugging, interrupt behavior, and hardware access.</p>
<p>For most projects, a good default is:</p>
<pre><code class="language-text">Debug:   -Og -g3
Release: -O2 or -Os
</code></pre>
<p>Use -O2 when performance matters. When flash size matters, use -Os. Finally, use -O3 only after measuring. Avoid relying on -O0 behavior for final firmware.</p>
<p>Most importantly, when optimized firmware behaves differently from unoptimized firmware, do not immediately blame the compiler. In embedded C and C++, optimization often reveals hidden bugs such as missing <em>volatile</em>, unsafe interrupt sharing, undefined behavior, or timing assumptions.</p>
<p>A reliable embedded project should be tested using the same optimization level that will be used in production.</p>
<p>The post <a href="https://www.teachmemicro.com/understanding-o1-o2-o3-os-and-og-in-embedded-firmware/">Understanding -O1, -O2, -O3, -Os, and -Og in Embedded Firmware</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Structures and Padding in Embedded Systems: Why sizeof() Is Bigger Than You Expect</title>
		<link>https://www.teachmemicro.com/structures-and-padding-in-embedded-systems-why-sizeof-is-bigger-than-you-expect/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=structures-and-padding-in-embedded-systems-why-sizeof-is-bigger-than-you-expect</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 16 Jun 2026 01:00:01 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=11803</guid>

					<description><![CDATA[<p>In embedded systems, every byte matters. Whether you are working with a small 8-bit microcontroller, an ARM Cortex-M device, or a memory-constrained IoT module, understanding how data is stored in memory can help you write more efficient and reliable firmware. One common source of confusion is the size of struct variables in C. Beginners often &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/structures-and-padding-in-embedded-systems-why-sizeof-is-bigger-than-you-expect/">Structures and Padding in Embedded Systems: Why sizeof() Is Bigger Than You Expect</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In embedded systems, every byte matters. Whether you are working with a small 8-bit microcontroller, an ARM Cortex-M device, or a memory-constrained IoT module, understanding how data is stored in memory can help you write more efficient and reliable firmware.</p>
<p>One common source of confusion is the size of <em>struct</em> variables in C. Beginners often expect the size of a structure to be equal to the sum of the sizes of its members. But in many cases, <em>sizeof(struct)</em> returns a larger value than expected.</p>
<p>The reason is <strong>padding</strong>.</p>
<p><a href="https://en.wikipedia.org/wiki/Data_structure_alignment">Structure padding</a> is added by the compiler to make memory access faster and properly aligned for the target processor. In embedded systems, this can affect RAM usage, communication protocols, EEPROM layouts, flash storage, and hardware register mapping.</p>
<p><span id="more-11803"></span></p>
<hr />
<h2>What Is a Structure in C?</h2>
<p>A structure, or <a href="https://www.teachmemicro.com/arduino-programming-structs/"><em>struct</em></a>, is a user-defined data type that groups related variables together.</p>
<p>For example:</p>
<pre><code class="language-c">typedef struct {
    uint8_t id;
    uint16_t value;
    uint8_t status;
} SensorData;
</code></pre>
<p>At first glance, you might expect this structure to use:</p>
<pre><code class="language-c">uint8_t id;       // 1 byte
uint16_t value;  // 2 bytes
uint8_t status;  // 1 byte
</code></pre>
<p>Total:</p>
<pre><code class="language-text">1 + 2 + 1 = 4 bytes
</code></pre>
<p>But depending on the compiler and target architecture, this structure may actually occupy <strong>6 bytes</strong> instead of 4.</p>
<p>Why? Because the compiler may insert unused bytes between members. These unused bytes are called <strong>padding bytes</strong>.</p>
<hr />
<h2>What Is Padding?</h2>
<p>Padding is extra space inserted by the compiler between structure members or at the end of a structure.</p>
<p>The purpose of padding is to align data members to memory addresses that are efficient, or sometimes required, for the CPU to access.</p>
<p>For example, many 32-bit microcontrollers prefer 16-bit values to be placed at addresses divisible by 2, and 32-bit values to be placed at addresses divisible by 4.</p>
<p>Consider this structure:</p>
<pre><code class="language-c">typedef struct {
    uint8_t a;
    uint32_t b;
} Example;
</code></pre>
<p>You might expect:</p>
<pre><code class="language-text">a = 1 byte
b = 4 bytes
Total = 5 bytes
</code></pre>
<p>But the compiler may arrange it like this:</p>
<pre><code class="language-text">Offset 0: a
Offset 1: padding
Offset 2: padding
Offset 3: padding
Offset 4: b byte 0
Offset 5: b byte 1
Offset 6: b byte 2
Offset 7: b byte 3
</code></pre>
<p>So the actual size becomes:</p>
<pre><code class="language-text">8 bytes
</code></pre>
<p>The compiler inserted 3 padding bytes so that <em>b</em> starts at offset 4, which is properly aligned for a 32-bit value.</p>
<hr />
<h2>Why Alignment Matters in Embedded Systems</h2>
<p>Memory alignment affects how efficiently a processor can read and write data.</p>
<p>On some processors, unaligned access is allowed but slower. On others, unaligned access can cause a fault or exception.</p>
<p>For example, reading a <em>uint32_t</em> from an address divisible by 4 is usually efficient. But reading the same value from an odd address may require multiple memory accesses, or may not be allowed at all.</p>
<p>This is especially important in embedded systems because firmware often interacts directly with:</p>
<ul>
<li>Peripheral registers</li>
<li>Communication buffers</li>
<li>EEPROM or flash memory</li>
<li>DMA buffers</li>
<li>Packed protocol frames</li>
<li>Bootloaders</li>
<li>Memory-mapped hardware</li>
</ul>
<p>A structure that looks correct in C may not have the exact memory layout you expect unless you account for padding.</p>
<hr />
<h2>Example: Padding Changes Structure Size</h2>
<p>Consider this structure:</p>
<pre><code class="language-c">#include &lt;stdint.h&gt;
#include &lt;stdio.h&gt;

typedef struct {
    uint8_t  id;
    uint32_t count;
    uint16_t voltage;
} DeviceData;
</code></pre>
<p>Expected size:</p>
<pre><code class="language-text">id      = 1 byte
count   = 4 bytes
voltage = 2 bytes
Total   = 7 bytes
</code></pre>
<p>But the actual layout may look like this:</p>
<pre><code class="language-text">Offset 0: id
Offset 1: padding
Offset 2: padding
Offset 3: padding
Offset 4: count
Offset 8: voltage
Offset 10: padding
Offset 11: padding
</code></pre>
<p>Actual size:</p>
<pre><code class="language-text">12 bytes
</code></pre>
<p>That is 5 extra bytes of padding.</p>
<p>On a desktop computer, this may not matter much. But on a microcontroller with only 2 KB of SRAM, wasting several bytes per structure can become a real problem, especially if you create large arrays.</p>
<p>For example:</p>
<pre><code class="language-c">DeviceData devices[100];
</code></pre>
<p>If each structure is 12 bytes instead of 7 bytes, the array uses:</p>
<pre><code class="language-text">12 × 100 = 1200 bytes
</code></pre>
<p>instead of:</p>
<pre><code class="language-text">7 × 100 = 700 bytes
</code></pre>
<p>That is 500 extra bytes of RAM.</p>
<hr />
<h2>Member Order Affects Padding</h2>
<p>One simple way to reduce padding is to arrange structure members from largest to smallest.</p>
<p>Instead of this:</p>
<pre><code class="language-c">typedef struct {
    uint8_t  id;
    uint32_t count;
    uint16_t voltage;
} DeviceData;
</code></pre>
<p>Use this:</p>
<pre><code class="language-c">typedef struct {
    uint32_t count;
    uint16_t voltage;
    uint8_t  id;
} DeviceDataOptimized;
</code></pre>
<p>The new layout may be:</p>
<pre><code class="language-text">Offset 0: count   // 4 bytes
Offset 4: voltage // 2 bytes
Offset 6: id      // 1 byte
Offset 7: padding // 1 byte
</code></pre>
<p>Actual size:</p>
<pre><code class="language-text">8 bytes
</code></pre>
<p>By simply reordering the members, the structure size is reduced from 12 bytes to 8 bytes.</p>
<p>That may not seem like much for one variable, but it matters when the structure is used in arrays, queues, buffers, logs, and communication packets.</p>
<hr />
<h2>Checking Structure Size with <pre><code class="language-cpp">sizeof()</code></pre></h2>
<p>The <pre><code class="language-cpp">sizeof()</code></pre> operator tells you the actual memory size used by a structure.</p>
<p>Example:</p>
<pre><code class="language-c">printf("Size of DeviceData: %u\n", sizeof(DeviceData));
printf("Size of DeviceDataOptimized: %u\n", sizeof(DeviceDataOptimized));
</code></pre>
<p>On embedded systems, you may not always have <em>printf()</em> available. In that case, you can inspect the size in the debugger, use a compile-time assertion, or view the map file generated by the compiler.</p>
<p>Example using a compile-time check:</p>
<pre><code class="language-c">_Static_assert(sizeof(DeviceDataOptimized) == 8, "Unexpected struct size");
</code></pre>
<p>This is useful when the structure size must not change, such as when it is used for a communication packet or saved data format.</p>
<hr />
<h2>Padding at the End of a Structure</h2>
<p>Padding can also be added at the end of a structure.</p>
<p>For example:</p>
<pre><code class="language-c">typedef struct {
    uint32_t timestamp;
    uint8_t status;
} LogEntry;
</code></pre>
<p>The members use:</p>
<pre><code class="language-text">timestamp = 4 bytes
status    = 1 byte
Total     = 5 bytes
</code></pre>
<p>But the structure may still be 8 bytes because the compiler pads the end of the structure to maintain alignment when used in arrays.</p>
<p>Why?</p>
<p>Because in this array:</p>
<pre><code class="language-c">LogEntry logs[10];
</code></pre>
<p>each <em>timestamp</em> in each array element should still be aligned properly.</p>
<p>Without end padding, the second structure might start at an address that causes its <em>timestamp</em> member to become misaligned.</p>
<hr />
<h2>Packed Structures</h2>
<p>Sometimes, you need the structure layout to match an exact byte format. This is common in communication protocols, file formats, EEPROM layouts, and binary packet parsing.</p>
<p>In GCC, you can use the <em>packed</em> attribute:</p>
<pre><code class="language-c">typedef struct __attribute__((packed)) {
    uint8_t  id;
    uint32_t count;
    uint16_t voltage;
} PackedDeviceData;
</code></pre>
<p>This tells the compiler not to insert padding bytes.</p>
<p>The structure size becomes:</p>
<pre><code class="language-text">1 + 4 + 2 = 7 bytes
</code></pre>
<p>For ARM compilers or other toolchains, the syntax may be different. Some compilers use pragmas such as:</p>
<pre><code class="language-c">#pragma pack(push, 1)

typedef struct {
    uint8_t  id;
    uint32_t count;
    uint16_t voltage;
} PackedDeviceData;

#pragma pack(pop)
</code></pre>
<p>However, packed structures should be used carefully.</p>
<hr />
<h2>The Danger of Packed Structures</h2>
<p>Packed structures save memory, but they can also create problems.</p>
<p>When a structure is packed, members may be placed at unaligned addresses. Accessing those members directly can be slower or unsafe on some microcontrollers.</p>
<p>For example:</p>
<pre><code class="language-c">typedef struct __attribute__((packed)) {
    uint8_t header;
    uint32_t value;
} Packet;
</code></pre>
<p>In this structure, <em>value</em> may start at offset 1. That means it is not aligned to a 4-byte boundary.</p>
<p>On some processors, this access may be inefficient. On others, it may cause a hard fault.</p>
<p>A safer approach is to copy the unaligned data into an aligned variable before using it:</p>
<pre><code class="language-c">uint32_t value;
memcpy(&amp;value, &amp;packet.value, sizeof(value));
</code></pre>
<p>This may look unnecessary, but it avoids unaligned memory access problems and is often safer for portable embedded code.</p>
<hr />
<h2>Structures and Communication Protocols</h2>
<p>Padding becomes very important when sending structures over UART, SPI, I2C, CAN, BLE, or Ethernet.</p>
<p>For example:</p>
<pre><code class="language-c">typedef struct {
    uint8_t command;
    uint16_t length;
    uint32_t checksum;
} Message;
</code></pre>
<p>If you send this directly:</p>
<pre><code class="language-c">uart_write((uint8_t *)&amp;msg, sizeof(msg));
</code></pre>
<p>you may accidentally send padding bytes too.</p>
<p>The receiver may expect a compact packet, but the transmitted data may contain extra bytes inserted by the compiler. This can break communication between devices, especially if the other side is written in a different language, uses a different compiler, or runs on a different architecture.</p>
<p>For protocol data, it is often better to serialize the packet manually:</p>
<pre><code class="language-c">buffer[0] = command;
buffer[1] = length &amp; 0xFF;
buffer[2] = (length &gt;&gt; 8) &amp; 0xFF;
buffer[3] = checksum &amp; 0xFF;
buffer[4] = (checksum &gt;&gt; 8) &amp; 0xFF;
buffer[5] = (checksum &gt;&gt; 16) &amp; 0xFF;
buffer[6] = (checksum &gt;&gt; 24) &amp; 0xFF;
</code></pre>
<p>Manual serialization gives you full control over byte order, padding, and packet format.</p>
<hr />
<h2>Structures and EEPROM or Flash Storage</h2>
<p>Another common mistake is saving a structure directly to EEPROM or flash:</p>
<pre><code class="language-c">eeprom_write((uint8_t *)&amp;settings, sizeof(settings));
</code></pre>
<p>This works only if you are certain the structure layout will never change.</p>
<p>Problems can happen when:</p>
<ul>
<li>The compiler changes</li>
<li>Optimization settings change</li>
<li>The target architecture changes</li>
<li>Members are reordered</li>
<li>New fields are added</li>
<li>Packing settings change</li>
<li>Padding bytes contain random values</li>
</ul>
<p>If the structure is used for persistent storage, consider adding:</p>
<pre><code class="language-c">typedef struct {
    uint32_t magic;
    uint16_t version;
    uint16_t size;
    uint8_t data[32];
    uint32_t crc;
} SettingsBlock;
</code></pre>
<p>A version number, size field, and CRC make the stored data easier to validate and migrate when the firmware changes.</p>
<hr />
<h2>Structures and Hardware Registers</h2>
<p>In embedded systems, structures are often used to represent hardware registers.</p>
<p>Example:</p>
<pre><code class="language-c">typedef struct {
    volatile uint32_t CR;
    volatile uint32_t SR;
    volatile uint32_t DR;
} UART_TypeDef;
</code></pre>
<p>This works because the structure is designed to match the exact register layout described in the microcontroller datasheet.</p>
<p>However, hardware register structures must be written very carefully. If a register is reserved, the structure must include a reserved field to preserve the correct offset.</p>
<p>Example:</p>
<pre><code class="language-c">typedef struct {
    volatile uint32_t CR;
    volatile uint32_t SR;
    uint32_t RESERVED0;
    volatile uint32_t DR;
} UART_TypeDef;
</code></pre>
<p>Without the reserved field, <em>DR</em> would appear at the wrong offset, and the firmware would access the wrong register.</p>
<p>This is why vendor header files usually contain many <em>RESERVED</em> fields in peripheral structure definitions.</p>
<hr />
<h2>How to Inspect Member Offsets</h2>
<p>The <em>offsetof()</em> macro from <em>&lt;stddef.h&gt;</em> can be used to check where each member is placed inside a structure.</p>
<p>Example:</p>
<pre><code class="language-c">#include &lt;stddef.h&gt;

printf("id offset: %u\n", offsetof(DeviceData, id));
printf("count offset: %u\n", offsetof(DeviceData, count));
printf("voltage offset: %u\n", offsetof(DeviceData, voltage));
</code></pre>
<p>This helps you confirm whether the compiler inserted padding between members.</p>
<p>For embedded debugging, this is useful when checking protocol structures, register maps, or memory layouts.</p>
<hr />
<h2>Best Practices for Embedded Systems</h2>
<p>When using structures in embedded firmware, keep these guidelines in mind.</p>
<ul>
<li>First, do not assume that the size of a structure is equal to the sum of its members. Always check with <em>sizeof()</em>.</li>
<li>Second, arrange members from largest to smallest when RAM usage matters. This can reduce padding without needing compiler-specific packing attributes.</li>
<li>Third, avoid sending raw structures directly over communication interfaces unless the layout is explicitly controlled.</li>
<li>Fourth, be careful with packed structures. They are useful for matching exact byte layouts, but they can cause unaligned memory access.</li>
<li>Fifth, use fixed-width integer types such as <em>uint8_t</em>, <em>uint16_t</em>, and <em>uint32_t</em> instead of plain <em>int</em>, <em>short</em>, or <em>long</em> when the binary layout matters.</li>
<li>Sixth, use <em>offsetof()</em> and  <em>_Static_assert()</em> to verify structure sizes and member positions.</li>
<li>Seventh, for EEPROM, flash, or protocol data, consider manual serialization instead of writing the raw structure directly.</li>
<li>Finally, always check the compiler documentation for packing, alignment, and ABI behavior, especially when moving code between compilers or microcontroller families.</li>
</ul>
<p>On classic AVR-based Arduino boards, structure padding is usually less noticeable because the CPU is 8-bit and has fewer strict alignment requirements. However, the habit of checking <em>sizeof()</em>, using fixed-width integer types, and avoiding raw struct transfers is still important—especially if the code may later run on <a href="https://www.teachmemicro.com/esp32-board-guide-for-beginners/">ESP32</a>, STM32, SAMD, RP2040, or other 32-bit boards.</p>
<hr />
<h2>Conclusion</h2>
<p>Structure padding is one of those C language details that becomes very important in embedded systems. It affects RAM usage, binary compatibility, communication packets, EEPROM layouts, flash storage, DMA buffers, and hardware register definitions.</p>
<p>Padding is not a compiler bug. It is a normal part of how C structures are arranged in memory to satisfy alignment requirements.</p>
<p>For ordinary application-level data, padding is usually harmless. But for embedded systems, where memory layout often matters, you need to be aware of it.</p>
<p>The key rule is simple:</p>
<p>Do not guess the size or layout of a structure. Check it.</p>
<p>Use <em>sizeof(), offsetof(), _Static_assert()</em>, and careful member ordering. Use packed structures only when necessary, and be careful when accessing unaligned members.</p>
<p>Understanding structure padding will help you write embedded firmware that is smaller, safer, more portable, and easier to debug.</p>
<p>The post <a href="https://www.teachmemicro.com/structures-and-padding-in-embedded-systems-why-sizeof-is-bigger-than-you-expect/">Structures and Padding in Embedded Systems: Why sizeof() Is Bigger Than You Expect</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>What are Linker Files in STM32?</title>
		<link>https://www.teachmemicro.com/what-are-linker-files-in-stm32/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=what-are-linker-files-in-stm32</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 03 Mar 2025 22:46:25 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=8258</guid>

					<description><![CDATA[<p>Linker scripts play a crucial role in STM32 firmware development by defining how the compiler organizes the program in memory. Understanding linker files is essential for embedded engineers working with STM32 microcontrollers, as it allows them to control memory layout, define sections, and optimize RAM/Flash usage. This tutorial will cover: What is a Linker File? &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/what-are-linker-files-in-stm32/">What are Linker Files in STM32?</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p data-start="55" data-end="372">Linker scripts play a crucial role in STM32 firmware development by defining how the compiler organizes the program in memory. Understanding linker files is essential for embedded engineers working with STM32 microcontrollers, as it allows them to control memory layout, define sections, and optimize RAM/Flash usage.</p>
<p data-start="374" data-end="399">This tutorial will cover:</p>
<ol data-start="401" data-end="709">
<li data-start="401" data-end="430"><strong data-start="404" data-end="430">What is a Linker File?</strong></li>
<li data-start="431" data-end="475"><strong data-start="434" data-end="475">Understanding the STM32 Memory Layout</strong></li>
<li data-start="476" data-end="524"><strong data-start="479" data-end="524">Structure of a Linker Script (<em>.ld</em> file)</strong></li>
<li data-start="525" data-end="573"><strong data-start="528" data-end="573">Common Sections in an STM32 Linker Script</strong></li>
<li data-start="574" data-end="606"><strong data-start="577" data-end="606">Modifying a Linker Script</strong></li>
<li data-start="607" data-end="658"><strong data-start="610" data-end="658">Practical Example: Customizing RAM and Flash</strong></li>
<li data-start="659" data-end="709"><strong data-start="662" data-end="709">Debugging and Troubleshooting Linker Errors</strong></li>
</ol>
<hr data-start="711" data-end="714" />
<h2 data-start="716" data-end="748"><strong data-start="719" data-end="748">1. What is a Linker File?</strong></h2>
<p data-start="749" data-end="944">A linker file (<em>.ld</em> file) is a script used by the GNU linker (<em>ld</em>) to define the memory regions of a microcontroller and specify where different parts of the program should be placed in memory.</p>
<p data-start="946" data-end="1015">In STM32 projects, the linker file tells the compiler where to place:</p>
<ul data-start="1016" data-end="1128">
<li data-start="1016" data-end="1038">The <strong data-start="1022" data-end="1038">startup code</strong></li>
<li data-start="1039" data-end="1070">The <strong data-start="1045" data-end="1070">main application code</strong></li>
<li data-start="1071" data-end="1100"><strong data-start="1073" data-end="1100">Global/static variables</strong></li>
<li data-start="1101" data-end="1128"><strong data-start="1103" data-end="1128">Heap and stack memory</strong></li>
</ul>
<hr data-start="1130" data-end="1133" />
<h2 data-start="1135" data-end="1182"><strong data-start="1138" data-end="1182">2. Understanding the STM32 Memory Layout</strong></h2>
<p data-start="1183" data-end="1246">STM32 microcontrollers typically have two main types of memory:</p>
<ul data-start="1248" data-end="1477">
<li data-start="1248" data-end="1370">
<p data-start="1250" data-end="1370"><strong data-start="1250" data-end="1266">Flash Memory</strong> (Non-volatile)<br data-start="1281" data-end="1284" />Stores the program (code and constants). Flash is usually located at <strong data-start="1355" data-end="1369">0x08000000</strong>.</p>
</li>
<li data-start="1374" data-end="1477">
<p data-start="1376" data-end="1477"><strong data-start="1376" data-end="1383">RAM</strong> (Volatile)<br data-start="1394" data-end="1397" />Stores variables, stack, and heap. RAM is typically located at <strong data-start="1462" data-end="1476">0x20000000</strong>.</p>
</li>
</ul>
<p data-start="1479" data-end="1547">Example memory layout for an STM32F103C8T6 (64 KB Flash, 20 KB RAM):</p>
<table data-start="1549" data-end="1693">
<thead data-start="1549" data-end="1585">
<tr data-start="1549" data-end="1585">
<th data-start="1549" data-end="1559">Region</th>
<th data-start="1559" data-end="1576">Start Address</th>
<th data-start="1576" data-end="1585">Size</th>
</tr>
</thead>
<tbody data-start="1622" data-end="1693">
<tr data-start="1622" data-end="1657">
<td>Flash</td>
<td>0x08000000</td>
<td>64 KB</td>
</tr>
<tr data-start="1658" data-end="1693">
<td>RAM</td>
<td>0x20000000</td>
<td>20 KB</td>
</tr>
</tbody>
</table>
<hr data-start="1695" data-end="1698" />
<h2 data-start="1700" data-end="1751"><strong data-start="1703" data-end="1751">3. Structure of a Linker Script (.ld file)</strong></h2>
<p data-start="1752" data-end="1807">A typical STM32 linker script is divided into sections:</p>
<h3 data-start="1809" data-end="1848"><strong data-start="1813" data-end="1848">1. Header and Memory Definition</strong></h3>
<p data-start="1849" data-end="1922">Defines the memory regions (Flash, RAM) available in the microcontroller.</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">MEMORY 
{ 
    FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K 
    RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 
}</code></pre></pre>
</div>
</div>
</div>
<ul data-start="2048" data-end="2181">
<li data-start="2048" data-end="2109"><em>rx</em> : Flash is <strong data-start="2065" data-end="2077">readable</strong> (<em>r</em>) and <strong data-start="2088" data-end="2102">executable</strong> (<em>x</em>).</li>
<li data-start="2110" data-end="2181"><em>rwx</em>: RAM is <strong data-start="2126" data-end="2138">readable</strong>, <strong data-start="2140" data-end="2152">writable</strong>, and <strong data-start="2158" data-end="2172">executable</strong> (<em>rwx</em>).</li>
</ul>
<h3 data-start="2183" data-end="2216"><strong data-start="2187" data-end="2216">2. Entry Point Definition</strong></h3>
<p data-start="2217" data-end="2248">Defines where execution starts.</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">ENTRY(Reset_Handler)</code></pre></pre>
</div>
</div>
</div>
<p data-start="2280" data-end="2383">The <strong data-start="2284" data-end="2301">Reset_Handler</strong> is defined in <em>startup_stm32.s</em> and is the first function executed after a reset.</p>
<h3 data-start="2385" data-end="2415"><strong data-start="2389" data-end="2415">3. Sections Definition</strong></h3>
<p data-start="2416" data-end="2455">Organizes program sections into memory.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">SECTIONS
{
   /* Code and Read-Only Data in Flash */
   .text :
   {
      KEEP(*(.isr_vector)) /* Interrupt Vector Table */
      *(.text*) /* Code */
      *(.rodata*) /* Read-only data */
      . = ALIGN(4);
   } &gt; FLASH

   /* Initialized Data in RAM */
   .data :
   {
      *(.data*) /* Initialized global/static variables */
      . = ALIGN(4);
   } &gt; RAM AT&gt; FLASH

   /* Uninitialized Data in RAM */
   .bss :
   {
      *(.bss*) /* Uninitialized global/static variables */
      . = ALIGN(4);
   } &gt; RAM

   /* Stack and Heap */
   _estack = ORIGIN(RAM) + LENGTH(RAM);
   _Min_Heap_Size = 0x100; /* Minimum heap size */
   _Min_Stack_Size = 0x400; /* Minimum stack size */
}</code></pre></pre>
</div>
<hr data-start="3238" data-end="3241" />
<h2 data-start="3243" data-end="3294"><strong data-start="3246" data-end="3294">4. Common Sections in an STM32 Linker Script</strong></h2>
<p data-start="3295" data-end="3336">Here’s a breakdown of important sections:</p>
<table data-start="3338" data-end="3720">
<thead data-start="3338" data-end="3374">
<tr data-start="3338" data-end="3374">
<th data-start="3338" data-end="3348">Section</th>
<th data-start="3348" data-end="3362">Description</th>
<th data-start="3362" data-end="3374">Location</th>
</tr>
</thead>
<tbody data-start="3411" data-end="3720">
<tr data-start="3411" data-end="3455">
<td><em>.text</em></td>
<td>Code (functions, ISRs)</td>
<td>Flash</td>
</tr>
<tr data-start="3456" data-end="3515">
<td><em>.rodata</em><code data-start="3458" data-end="3467"></code></td>
<td>Read-only data (constants, strings)</td>
<td>Flash</td>
</tr>
<tr data-start="3516" data-end="3577">
<td><em>.data</em></td>
<td>Initialized variables</td>
<td>RAM (loaded from Flash)</td>
</tr>
<tr data-start="3578" data-end="3620">
<td><em>.bss</em></td>
<td>Uninitialized variables</td>
<td>RAM</td>
</tr>
<tr data-start="3621" data-end="3669">
<td><em>.heap</em></td>
<td>Dynamic memory (malloc/free)</td>
<td>RAM</td>
</tr>
<tr data-start="3670" data-end="3720">
<td><em>.stack</em></td>
<td>Call stack for function calls</td>
<td>RAM</td>
</tr>
</tbody>
</table>
<hr data-start="3722" data-end="3725" />
<h2 data-start="3727" data-end="3762"><strong data-start="3730" data-end="3762">5. Modifying a Linker Script</strong></h2>
<h3 data-start="3763" data-end="3799"><strong data-start="3767" data-end="3799">1. Increasing the Stack Size</strong></h3>
<p data-start="3800" data-end="3851">To change the stack size, modify <em>_Min_Stack_Size</em>:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">_Min_Stack_Size = 0x800; /* Increase stack size to 2 KB */</code></pre></pre>
</div>
</div>
</div>
<h3 data-start="3923" data-end="3974"><strong data-start="3927" data-end="3974">2. Placing a Variable at a Specific Address</strong></h3>
<p data-start="3975" data-end="4022">If you need a variable at a fixed RAM location:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">__attribute__((section(&quot;.my_section&quot;))) int my_var = 42;</code></pre></pre>
</div>
</div>
</div>
<p data-start="4089" data-end="4132">Then add this section to the linker script:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">.my_section : { *(.my_section) } &gt; RAM</code></pre></pre>
</div>
</div>
</div>
<h3 data-start="4187" data-end="4228"><strong data-start="4191" data-end="4228">3. Allocating a Bootloader Region</strong></h3>
<p data-start="4229" data-end="4312">If using a bootloader (e.g., 16 KB at the beginning of Flash), adjust Flash origin:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">MEMORY
{
   FLASH (rx) : ORIGIN = 0x08004000, LENGTH = 48K /* Bootloader takes first 16 KB */
   RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}</code></pre></pre>
</div>
</div>
</div>
<hr data-start="4475" data-end="4478" />
<h2 data-start="4480" data-end="4534"><strong data-start="4483" data-end="4534">6. Practical Example: Customizing RAM and Flash</strong></h2>
<p data-start="4535" data-end="4554">Suppose we want to:</p>
<ul data-start="4555" data-end="4642">
<li data-start="4555" data-end="4604"><strong data-start="4557" data-end="4604">Allocate a special section for a DMA buffer</strong></li>
<li data-start="4605" data-end="4642"><strong data-start="4607" data-end="4642">Reserve memory for a bootloader</strong></li>
</ul>
<h3 data-start="4644" data-end="4694"><strong data-start="4648" data-end="4694">1. Define a Special Section for DMA Buffer</strong></h3>
<p data-start="4695" data-end="4707">In <em>main.c</em>:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950"></div>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">__attribute__((section(&quot;.dma_buffer&quot;))) uint8_t dmaBuffer[1024];</code></pre></pre>
</div>
</div>
</div>
<p data-start="4782" data-end="4800">In <em>STM32F103_FLASH.ld</em>:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">.dma_buffer (NOLOAD) : 
{ 
   *(.dma_buffer) 
} &gt; RAM</code></pre></pre>
</div>
</div>
</div>
<p data-start="4863" data-end="4919">(<em>NOLOAD</em> prevents initialization in <em>.data</em> or <em>.bss</em>.)</p>
<h3 data-start="4921" data-end="4959"><strong data-start="4925" data-end="4959">2. Modify Flash for Bootloader</strong></h3>
<p data-start="4960" data-end="4981">Modify memory layout:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">MEMORY 
{ 
   FLASH (rx) : ORIGIN = 0x08004000, LENGTH = 48K 
   RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K 
}</code></pre></pre>
</div>
</div>
</div>
<hr data-start="5108" data-end="5111" />
<h2 data-start="5113" data-end="5166"><strong data-start="5116" data-end="5166">7. Debugging and Troubleshooting Linker Errors</strong></h2>
<h3 data-start="5167" data-end="5191"><strong data-start="5171" data-end="5191">1. Common Errors</strong></h3>
<table data-start="5192" data-end="5536">
<thead data-start="5192" data-end="5220">
<tr data-start="5192" data-end="5220">
<th data-start="5192" data-end="5200">Error</th>
<th data-start="5200" data-end="5208">Cause</th>
<th data-start="5208" data-end="5220">Solution</th>
</tr>
</thead>
<tbody data-start="5250" data-end="5536">
<tr data-start="5250" data-end="5347">
<td><em>undefined reference to symbol</em></td>
<td>Missing function definition</td>
<td>Ensure correct file is linked</td>
</tr>
<tr data-start="5348" data-end="5454">
<td><em>section .data will not fit in region RAM</em></td>
<td>Not enough RAM</td>
<td>Reduce <em>.data</em> size or increase RAM size</td>
</tr>
<tr data-start="5455" data-end="5536">
<td><em>heap region overflow</em></td>
<td>Heap too large</td>
<td>Reduce heap size (<em>_Min_Heap_Size</em>)</td>
</tr>
</tbody>
</table>
<h3 data-start="5538" data-end="5567"><strong data-start="5542" data-end="5567">2. Check Memory Usage</strong></h3>
<p data-start="5568" data-end="5593">Use <em>arm-none-eabi-size</em>:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-bash" data-lang="Bash"><pre><code class="language-cpp">arm-none-eabi-size -B firmware.elf</code></pre></pre>
</div>
</div>
</div>
<p data-start="5639" data-end="5654">Example output:</p>
<div class="contain-inline-size rounded-md border-[0.5px] border-token-border-medium relative bg-token-sidebar-surface-primary dark:bg-gray-950">
<div class="overflow-y-auto p-4" dir="ltr">
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-bash" data-lang="Bash"><pre><code class="language-cpp">text data bss dec hex filename 13500 800 3000 17300 439c firmware.elf</code></pre></pre>
</div>
</div>
</div>
<ul data-start="5765" data-end="5854">
<li data-start="5765" data-end="5785"><em>text</em> → Code size</li>
<li data-start="5786" data-end="5820"><em>data</em> → Initialized data in RAM</li>
<li data-start="5821" data-end="5854"><em>bss</em> → Uninitialized variables</li>
</ul>
<hr data-start="5856" data-end="5859" />
<h2 data-start="5861" data-end="5878"><strong data-start="5864" data-end="5878">Conclusion</strong></h2>
<p data-start="5879" data-end="6047">Linker scripts are a critical part of STM32 firmware development, defining how memory is allocated and structured. By understanding and modifying linker files, you can:</p>
<ul data-start="6048" data-end="6143">
<li data-start="6048" data-end="6071">Optimize memory usage</li>
<li data-start="6072" data-end="6091">Relocate sections</li>
<li data-start="6092" data-end="6143">Implement bootloaders and special memory mappings</li>
</ul>
<p data-start="6145" data-end="6263" data-is-last-node="" data-is-only-node="">Mastering linker scripts helps ensure efficient and reliable embedded software development for STM32 microcontrollers.</p>
<p>The post <a href="https://www.teachmemicro.com/what-are-linker-files-in-stm32/">What are Linker Files in STM32?</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Using ADC Scan Mode with DMA in STM32F407</title>
		<link>https://www.teachmemicro.com/using-adc-scan-mode-with-dma-in-stm32f407/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-adc-scan-mode-with-dma-in-stm32f407</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Tue, 24 Dec 2024 02:47:25 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=7543</guid>

					<description><![CDATA[<p>In this tutorial, we will explore how to configure and use the ADC (Analog-to-Digital Converter) in the STM32F407 microcontroller in scan mode with DMA (Direct Memory Access). This setup is particularly useful when working with multiple analog inputs that need to be converted into digital values efficiently. We will also discuss the concept of ADC &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/using-adc-scan-mode-with-dma-in-stm32f407/">Using ADC Scan Mode with DMA in STM32F407</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400;">In this tutorial, we will explore how to configure and use the ADC (Analog-to-Digital Converter) in the STM32F407 microcontroller in scan mode with DMA (Direct Memory Access). This setup is particularly useful when working with multiple analog inputs that need to be converted into digital values efficiently. We will also discuss the concept of ADC channel ranking, different operational modes such as continuous and non-continuous conversion, and the differences between normal and circular DMA modes.</span></p>
<p><span id="more-7543"></span></p>
<h3><b>Overview of ADC in STM32F407</b></h3>
<p><span style="font-weight: 400;">The STM32F407 microcontroller comes with a 12-bit resolution ADC that can handle up to 16 multiplexed input channels. This makes it ideal for applications requiring the processing of multiple analog signals, such as reading from sensor arrays. The ADC supports single-shot and continuous conversion modes, with the ability to automatically scan through multiple channels in sequence. Conversion can be triggered either through software commands or external hardware signals, providing flexibility in its integration into various systems. The DMA module can be used in conjunction with the ADC to transfer conversion data directly to memory, which minimizes CPU load and allows real-time data collection for high-performance applications.</span></p>
<h3><b>ADC Scan Mode with DMA</b></h3>
<p><span style="font-weight: 400;">In scan mode, the ADC sequentially converts multiple channels, each identified by a specific rank. This ensures that channels are processed in the required order, which is especially important when dealing with structured or time-sensitive data. When used with DMA, the ADC can transfer the results of these conversions directly into a memory buffer without involving the CPU. This combination of ADC and DMA significantly enhances efficiency and reduces processing overhead, as the CPU is free to handle other tasks during data transfer.</span></p>
<h3><b>Configuring ADC with DMA</b></h3>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2024/12/ADC-DMA.jpg" alt="ADC DMA illustration" width="554" height="334" class="wp-image-7548 size-full aligncenter" srcset="https://www.teachmemicro.com/wp-content/uploads/2024/12/ADC-DMA.jpg 554w, https://www.teachmemicro.com/wp-content/uploads/2024/12/ADC-DMA-300x181.jpg 300w" sizes="auto, (max-width: 554px) 100vw, 554px" /></p>
<p><span style="font-weight: 400;">To set up ADC in scan mode with DMA, you need to configure various peripherals and settings. First, enable the necessary clocks for the ADC and GPIO peripherals. For instance, enabling the clock for ADC1 and GPIOA can be achieved using the appropriate RCC commands. Next, configure the GPIO pins that will serve as analog inputs. For example, setting PA0 as an analog input involves configuring its mode to </span><span style="font-weight: 400;">GPIO_Mode_AN</span><span style="font-weight: 400;"> and disabling any pull-up or pull-down resistors.</span></p>
<p><span style="font-weight: 400;">The ADC itself must be initialized with the desired resolution, scan mode, and conversion mode settings. You can enable scan mode by setting </span><span style="font-weight: 400;">ADC_ScanConvMode</span><span style="font-weight: 400;"> to </span><span style="font-weight: 400;">ENABLE</span><span style="font-weight: 400;">, which allows the ADC to automatically move through the sequence of channels. The number of conversions in the sequence is set using </span><span style="font-weight: 400;">ADC_NbrOfConversion</span><span style="font-weight: 400;">, while each channel's rank in the sequence is configured using the </span><span style="font-weight: 400;">ADC_RegularChannelConfig</span><span style="font-weight: 400;"> function. This ensures that the channels are converted in the required order, with each channel assigned a unique rank.</span></p>
<p>Of course, all of these can be easily done using STM32CubeMX. An example configuration is shown in the following section.</p>
<p><span style="font-weight: 400;">DMA configuration involves defining the memory buffer where the ADC data will be stored, specifying the size of the buffer, and setting the transfer mode. The base address of the ADC data register and the memory buffer address is linked in the DMA configuration, and the DMA mode is set to either normal or circular, depending on the application's needs. Finally, both the ADC and DMA modules must be enabled, and the ADC conversion process must be started using a software command or an external trigger.</span></p>
<h3>Operational Modes of the ADC</h3>
<p><span style="font-weight: 400;">The STM32F407 ADC can operate in continuous or non-continuous conversion modes. In continuous conversion mode, the ADC repeatedly converts data from the selected channels without any additional input or trigger, making it suitable for real-time data acquisition tasks. This mode is enabled by setting the </span><span style="font-weight: 400;">ADC_ContinuousConvMode</span><span style="font-weight: 400;"> flag to </span><span style="font-weight: 400;">ENABLE</span><span style="font-weight: 400;">. On the other hand, the non-continuous conversion mode completes a single cycle of conversions and then stops, requiring a new trigger to restart. This mode is useful for applications that need periodic sampling or where power efficiency is critical.</span></p>
<h3><strong>DMA Modes</strong></h3>
<p><span style="font-weight: 400;">The DMA module operates in either normal or circular mode. In normal mode, data transfer stops once the specified number of conversions has been completed. This mode is best suited for applications where data is required only once or at periodic intervals. In contrast, circular mode continuously transfers data, looping back to the start of the memory buffer once it reaches the end. This mode is ideal for applications that require real-time monitoring or continuous data streams, as it eliminates the need for manual intervention to restart the transfer.</span></p>
<h3>ADC Channel Ranking</h3>
<p><span style="font-weight: 400;">Channel ranking is a crucial aspect of ADC configuration in scan mode. Each channel is assigned a rank that determines its position in the conversion sequence. For example, a channel with rank 1 will be converted first, followed by channels with higher ranks. This ensures that data is processed in a predictable order, which is important in multi-sensor systems where timing or channel order matters. The ranking is configured using the </span><span style="font-weight: 400;">ADC_RegularChannelConfig</span><span style="font-weight: 400;"> function, which specifies the channel number, rank, and sample time for each channel.</span></p>
<h3><strong>STM32CubeMX Configuration</strong></h3>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-adc-scan-config.jpg" alt="STM32CubeMX ADC multi-channel Config" width="694" height="849" class="aligncenter wp-image-7545 size-full" srcset="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-adc-scan-config.jpg 694w, https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-adc-scan-config-245x300.jpg 245w" sizes="auto, (max-width: 694px) 100vw, 694px" /></p>
<p>Here I am using ADC1 of the STM32F407 and its inputs Channel 0 to Channel 3. The ADC resolution is set to 12 bits, scan conversion mode enabled (to use multiple channels) and the End of Conversion Flag will be set at the end of a single channel conversion. For the multiscan to work, the number of conversions must be set to the number of channels used, in my case, 4 channels. Then I set the ranking of each channel according to the order I want the data to be stored. This means the first data will be coming from channel 0, the second data from channel 1, and so on.</p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-dma-adc.jpg" alt="STM32407 ADC DMA" width="696" height="687" class="aligncenter wp-image-7546 size-full" srcset="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-dma-adc.jpg 696w, https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-dma-adc-300x296.jpg 300w" sizes="auto, (max-width: 696px) 100vw, 696px" /></p>
<p>Next, I add DMA to ADC1 where the mode is Normal and the <em>increment address</em> is set to memory. The data width is set to half-word only since our ADC resolution is just 12 bits. Remember that a whole word in STM32 is 32 bits.</p>
<p>My STM32F407 development board only has an 8 MHz external crystal but I want to maximize its performance. Here is my clock configuration:</p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-clock-settings-1024x738.jpg" alt="STM32407 ADC DMA clock config" width="618" height="445" class="aligncenter wp-image-7547 size-large" srcset="https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-clock-settings-1024x738.jpg 1024w, https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-clock-settings-300x216.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-clock-settings-768x554.jpg 768w, https://www.teachmemicro.com/wp-content/uploads/2024/12/STM32CubeMX-clock-settings.jpg 1111w" sizes="auto, (max-width: 618px) 100vw, 618px" /></p>
<h2><b>Full Example Code</b></h2>
<p>The following code is generated once you click "GENERATE CODE" in STM32CubeMX. I have removed the comments for brevity.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp"> #include &quot;main.h&quot;

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);

int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  while (1)
  {
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 160;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&amp;RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&amp;RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig = {0};

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 4;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&amp;hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = 4;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

static void MX_DMA_Init(void)
{

  __HAL_RCC_DMA2_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

void assert_failed(uint8_t *file, uint32_t line)
{
}</code></pre></pre>
</div>
<p>We will need to add to this! First, we initiate ADC reading with DMA support using:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">HAL_ADC_Start_DMA(&amp;hadc1, (uint32_t*)adcBuffer, BUFFER_SIZE)</code></pre></pre>
</div>
<p>Where adcBuffer is where the ADC results are stored and the <em>BUFFER_SIZE</em> is the number of ADC channels used.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">#define BUFFER_SIZE 4
uint16_t adcBuffer[BUFFER_SIZE];</code></pre></pre>
</div>
<p>In our example, we disabled continuous mode so the conversion stops when <em>HAL_ADC_Start_DMA</em> is called and done. If you need always to do the conversion, there are two options: to put <em>HAL_ADC_Start_DMA</em> inside the loop or to enable continuous conversion mode.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">hadc1.Init.ContinuousConvMode = ENABLE;</code></pre></pre>
</div>
<p>Next, we add a callback function that triggers every time the conversion is done.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1)
{
// This function is automatically called when DMA completes a transfer

}</code></pre></pre>
</div>
<p>You can add to this function whatever you want done when the conversion completes.</p>
<p>So our full example code is now:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">#include &quot;main.h&quot;

#define BUFFER_SIZE 4 
uint16_t adcBuffer[BUFFER_SIZE];

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);

int main(void)
{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  while (1)
  {
     HAL_ADC_Start_DMA(&amp;hadc1, (uint32_t*)adcBuffer, BUFFER_SIZE)
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 160;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&amp;RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&amp;RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig = {0};

  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ENABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 4;
  hadc1.Init.DMAContinuousRequests = DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  if (HAL_ADC_Init(&amp;hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = 2;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = 3;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = 4;
  if (HAL_ADC_ConfigChannel(&amp;hadc1, &amp;sConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

static void MX_DMA_Init(void)
{

  __HAL_RCC_DMA2_CLK_ENABLE();

  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc1) 
{ 
   // This function is automatically called when DMA completes a transfer 
}

void Error_Handler(void)
{
  __disable_irq();
  while (1)
  {
  }
}

void assert_failed(uint8_t *file, uint32_t line)
{
}</code></pre></pre>
</div>
<h3><b>Summary</b></h3>
<p><span style="font-weight: 400;">This tutorial outlined how to configure the STM32F407 ADC in scan mode with DMA for efficient data acquisition from multiple channels. We explored the roles of continuous and non-continuous conversion modes, discussed the benefits of normal versus circular DMA, and demonstrated how to rank ADC channels in the conversion sequence. This setup is highly effective for applications requiring real-time data processing and minimizes CPU overhead, making it ideal for embedded systems.</span><span style="font-weight: 400;"></span></p>
<p>The post <a href="https://www.teachmemicro.com/using-adc-scan-mode-with-dma-in-stm32f407/">Using ADC Scan Mode with DMA in STM32F407</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Fading an LED using STM32&#039;s PWM</title>
		<link>https://www.teachmemicro.com/fading-an-led-using-stm32s-pwm/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=fading-an-led-using-stm32s-pwm</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Wed, 23 Oct 2024 01:00:42 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=7383</guid>

					<description><![CDATA[<p>In this tutorial, we'll walk through the steps to create a project for fading an LED using Pulse Width Modulation (PWM) on an STM32 microcontroller. We'll use the STM32CubeMX tool to generate the initialization code and set up the PWM, and then we will write the code to control the brightness of the LED. Requirements &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/fading-an-led-using-stm32s-pwm/">Fading an LED using STM32&#039;s PWM</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400;">In this tutorial, we'll walk through the steps to create a project for fading an LED using Pulse Width Modulation (PWM) on an STM32 microcontroller. We'll use the STM32CubeMX tool to generate the initialization code and set up the PWM, and then we will write the code to control the brightness of the LED.</span></p>
<h2><b>Requirements</b></h2>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>STM32 microcontroller</b><span style="font-weight: 400;"> (e.g., STM32F4 Discovery, STM32F0 Nucleo board, etc.)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>STM32CubeMX</b><span style="font-weight: 400;"> (for code generation)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>STM32CubeIDE</b><span style="font-weight: 400;"> (or any compatible IDE like Keil, IAR, etc.)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>LED</b><span style="font-weight: 400;"> (or the onboard LED, if your board has one)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>Resistor</b><span style="font-weight: 400;"> (if using an external LED)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>USB cable</b><span style="font-weight: 400;"> (for powering and programming the board)</span></li>
</ol>
<h2><b>Steps</b></h2>
<h3><b>1. Create a New STM32CubeMX Project</b></h3>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>Open STM32CubeMX</b><span style="font-weight: 400;"> and click on </span><b>New Project</b><span style="font-weight: 400;">.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Select your microcontroller or board from the available list (e.g., STM32F407VG for STM32F4 Discovery).</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Click </span><b>Start Project</b><span style="font-weight: 400;">.</span></li>
</ol>
<h3><b>2. Configure the PWM Output Pin</b></h3>
<p><span style="font-weight: 400;">To use PWM, we need to configure one of the general-purpose timers (TIM) and assign a pin for the PWM signal output.</span></p>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>Enable the timer:</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">In the </span><b>Pinout &amp; Configuration</b><span style="font-weight: 400;"> tab, locate the </span><b>Timers</b><span style="font-weight: 400;"> section.</span></li>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Enable one of the timers capable of PWM (e.g., </span><b>TIM3</b><span style="font-weight: 400;"> or </span><b>TIM2</b><span style="font-weight: 400;">). Click the dropdown arrow and select </span><b>PWM Generation CH1</b><span style="font-weight: 400;"> (or any available channel).</span></li>
</ul>
</li>
<li style="font-weight: 400;" aria-level="1"><b>Assign a pin for PWM:</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Once you select a PWM channel, CubeMX will automatically assign a GPIO pin as the PWM output. For instance, if you select </span><b>TIM3 CH1</b><span style="font-weight: 400;">, it might assign </span><b>PA6</b><span style="font-weight: 400;"> as the output pin. You can confirm the pin in the </span><b>Pinout</b><span style="font-weight: 400;"> view.</span></li>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Ensure the pin is configured as </span><b>PWM output</b><span style="font-weight: 400;">.</span></li>
</ul>
</li>
</ol>
<h3><b>3. Configure the Timer for PWM</b></h3>
<p><span style="font-weight: 400;">Now we need to configure the timer's frequency and duty cycle.</span></p>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>Go to the Configuration tab:</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">In the </span><b>Peripherals</b><span style="font-weight: 400;"> tree (on the left), expand </span><b>Timers</b><span style="font-weight: 400;"> and select </span><b>TIM3</b><span style="font-weight: 400;"> (or your selected timer).</span></li>
</ul>
</li>
<li style="font-weight: 400;" aria-level="1"><b>Set PWM frequency:</b></li>
</ol>
<p><span style="font-weight: 400;">Set the </span><b>Prescaler</b><span style="font-weight: 400;"> and </span><b>Counter Period</b><span style="font-weight: 400;"> to achieve your desired PWM frequency. The PWM frequency is calculated using the formula:</span><span style="font-weight: 400;"><br />
</span></p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-cpp" data-lang="C++"><pre><code class="language-cpp">PWM frequency = Timer clock / [(Prescaler + 1) * (Period + 1)]</code></pre></pre>
</div>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">For example, if the timer clock is 84 MHz, and you want a PWM frequency of 1 kHz, you can set the prescaler to 83 and the period to 999.</span></li>
</ul>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>Set the Duty Cycle:</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">The duty cycle determines the LED brightness. A duty cycle of 0% (0 in the Compare value register) will turn the LED off, and a duty cycle of 100% (equal to the period value) will turn the LED fully on. You can dynamically adjust this in your code later.</span></li>
</ul>
</li>
<li style="font-weight: 400;" aria-level="1"><b>Enable the Timer:</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">In the same configuration window, ensure the timer is set to </span><b>PWM mode</b><span style="font-weight: 400;"> and the </span><b>Output Compare</b><span style="font-weight: 400;"> is enabled for the selected channel (e.g., CH1).</span></li>
</ul>
</li>
</ol>
<h3><b>4. Configure the Clock</b></h3>
<p><span style="font-weight: 400;">Ensure the clock configuration is correctly set. Typically, STM32CubeMX will auto-configure the clock based on the default settings, but verify that the </span><b>APB1/2 timers</b><span style="font-weight: 400;"> are enabled and running at the correct frequency for your chosen timer.</span></p>
<h3><b>5. Generate Code</b></h3>
<ol>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Click on </span><b>Project Manager</b><span style="font-weight: 400;"> and give your project a name.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Choose your preferred toolchain (e.g., </span><b>STM32CubeIDE</b><span style="font-weight: 400;">).</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Click </span><b>Generate Code</b><span style="font-weight: 400;">.</span></li>
</ol>
<h3><b>6. Writing the Code in STM32CubeIDE</b></h3>
<p><span style="font-weight: 400;">Once you generate the project, open it in STM32CubeIDE (or your chosen IDE). STM32CubeMX has already generated the initialization code, so we'll focus on adding the logic to adjust the PWM duty cycle.</span></p>
<ol>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Open the </span><span style="font-weight: 400;">main.c</span><span style="font-weight: 400;"> file.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">The timer and GPIO initialization should already be done in the </span><span style="font-weight: 400;">MX_TIM3_Init()</span><span style="font-weight: 400;"> function.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Inside the </span><span style="font-weight: 400;">main()</span><span style="font-weight: 400;"> function, start the PWM signal generation by adding:</span></li>
</ol>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-cpp" data-lang="C++"><pre><code class="language-cpp">HAL_TIM_PWM_Start(&amp;htim3, TIM_CHANNEL_1);</code></pre></pre>
</div>
<p><span style="font-weight: 400;">This function starts the PWM generation on </span><b>TIM3</b><span style="font-weight: 400;"> channel 1 (which is linked to </span><b>PA6</b><span style="font-weight: 400;"> or the pin you configured).</span></p>
<p><span style="font-weight: 400;">Now, let's create a simple loop to gradually increase and decrease the LED brightness using the PWM duty cycle:</span></p>
<p>&nbsp;</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-cpp" data-lang="C++"><pre><code class="language-cpp">while (1)
{

    // Gradually increase brightness
    for (int duty = 0; duty &lt; __HAL_TIM_GET_AUTORELOAD(&amp;htim3); duty++)
    {
        __HAL_TIM_SET_COMPARE(&amp;htim3, TIM_CHANNEL_1, duty); // Set the duty cycle
        HAL_Delay(10); // Small delay to see the fade effect
    }

    // Gradually decrease brightness
    for (int duty = __HAL_TIM_GET_AUTORELOAD(&amp;htim3); duty &gt; 0; duty--)
    {
        __HAL_TIM_SET_COMPARE(&amp;htim3, TIM_CHANNEL_1, duty); // Set the duty cycle
        HAL_Delay(10); // Small delay to see the fade effect
    }
}</code></pre></pre>
</div>
<h3><b>7. Build and Flash the Code</b></h3>
<ol>
<li style="font-weight: 400;" aria-level="1"><b>Build</b><span style="font-weight: 400;"> the project by clicking the build button (hammer icon).</span></li>
<li style="font-weight: 400;" aria-level="1"><b>Flash</b><span style="font-weight: 400;"> the code to your STM32 board by clicking the debug/run button (bug or play icon).</span></li>
</ol>
<h3><b>8. Testing</b></h3>
<p><span style="font-weight: 400;">If everything is set up correctly, your LED should start fading in and out as the PWM duty cycle changes from 0% to 100% and back. The LED's brightness will gradually increase and then decrease, creating a fading effect.</span></p>
<h2><b>Summary</b></h2>
<p><span style="font-weight: 400;">In this tutorial, we created a project using STM32CubeMX to generate the code for PWM control. We configured a timer to generate PWM signals, wrote code to adjust the duty cycle dynamically, and successfully created a fading effect for an LED.</span></p>
<h3><b>Key Concepts:</b></h3>
<ul>
<li style="font-weight: 400;" aria-level="1"><b>PWM:</b><span style="font-weight: 400;"> Used to control the brightness of an LED by adjusting the duty cycle.</span></li>
<li style="font-weight: 400;" aria-level="1"><b>STM32CubeMX:</b><span style="font-weight: 400;"> Simplifies peripheral configuration and code generation.</span></li>
<li style="font-weight: 400;" aria-level="1"><b>Timers:</b><span style="font-weight: 400;"> Essential for PWM generation in STM32.</span></li>
</ul>
<p><span style="font-weight: 400;">You can modify the frequency, adjust the fade speed, or control multiple LEDs using the same process.</span></p>
<p>The post <a href="https://www.teachmemicro.com/fading-an-led-using-stm32s-pwm/">Fading an LED using STM32&#039;s PWM</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Set Up LwIP Raw on STM32F407</title>
		<link>https://www.teachmemicro.com/how-to-set-up-lwip-raw-on-stm32f407/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-set-up-lwip-raw-on-stm32f407</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Sun, 29 Sep 2024 01:37:16 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=7311</guid>

					<description><![CDATA[<p>LwIP (Lightweight IP) is a small TCP/IP stack used in embedded systems like STM32. It helps devices communicate over networks using internet protocols. In this post, I will show how to set up LwIP in "raw" mode for an STM32F407 microcontroller. What is LwIP Raw Mode? In raw mode, LwIP uses low-level functions without an &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-set-up-lwip-raw-on-stm32f407/">How to Set Up LwIP Raw on STM32F407</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><span style="font-weight: 400;">LwIP (Lightweight IP) is a small TCP/IP stack used in embedded systems like STM32. It helps devices communicate over networks using internet protocols. In this post, I will show how to set up LwIP in "raw" mode for an STM32F407 microcontroller.</span></p>
<h4><b>What is LwIP Raw Mode?</b></h4>
<p><span style="font-weight: 400;">In raw mode, LwIP uses low-level functions without an RTOS. This is perfect for small devices where you need full control of network data and don't want the overhead of an RTOS.</span></p>
<h4><b>What You Need:</b></h4>
<ul>
<li style="font-weight: 400;" aria-level="1"><b>STM32F407 microcontroller</b><span style="font-weight: 400;"> (e.g., STM32F407 Discovery board)</span></li>
<li style="font-weight: 400;" aria-level="1"><b>STM32CubeIDE</b><span style="font-weight: 400;"> or another IDE that supports STM32</span></li>
<li style="font-weight: 400;" aria-level="1"><b>LwIP library</b><span style="font-weight: 400;"> (this comes with the STM32Cube HAL)</span></li>
</ul>
<h4><b>Step-by-Step Setup</b></h4>
<h5><b>Step 1: Install STM32CubeIDE</b></h5>
<p><span style="font-weight: 400;">Download and install </span><b>STM32CubeIDE</b><span style="font-weight: 400;">. This is an integrated development environment for STM32 microcontrollers. You will use this tool to set up your project and code for LwIP.</span></p>
<h5><b>Step 2: Create a New Project</b></h5>
<ol>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Open </span><b>STM32CubeIDE</b><span style="font-weight: 400;">.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Click </span><b>File &gt; New &gt; STM32 Project</b><span style="font-weight: 400;">.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Select your STM32F407 microcontroller (e.g., STM32F407VGTx) and click </span><b>Next</b><span style="font-weight: 400;">.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Name your project and click </span><b>Finish</b><span style="font-weight: 400;">.</span></li>
</ol>
<h5><b>Step 3: Enable Ethernet and LwIP</b></h5>
<ol>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Open </span><b>STM32CubeMX</b><span style="font-weight: 400;"> (inside CubeIDE) to configure your STM32.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Go to the </span><b>Pinout &amp; Configuration</b><span style="font-weight: 400;"> tab and enable </span><b>Ethernet</b><span style="font-weight: 400;"> by clicking on the pin that supports Ethernet (PA1, PA2, PA7, etc.).</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Under the </span><b>Middleware</b><span style="font-weight: 400;"> section, enable </span><b>LwIP</b><span style="font-weight: 400;">.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Choose </span><b>RAW API</b><span style="font-weight: 400;"> in the LwIP settings. This sets LwIP to raw mode.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Configure the IP settings (you can use a static IP like 192.168.0.10).</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Click </span><b>Generate Code</b><span style="font-weight: 400;">.</span></li>
</ol>
<h5><b>Step 4: Add Ethernet Driver</b></h5>
<p><span style="font-weight: 400;">The STM32F407 has a built-in Ethernet MAC (Media Access Controller). You will need an external </span><b>PHY</b><span style="font-weight: 400;"> (physical layer) chip to connect to an Ethernet network, like the </span><b>LAN8720</b><span style="font-weight: 400;">.</span></p>
<p><span style="font-weight: 400;">Make sure the </span><b>ETH HAL driver</b><span style="font-weight: 400;"> is enabled in STM32CubeMX. It will automatically handle Ethernet communication for you.</span></p>
<h5><b>Step 5: Write LwIP Code</b></h5>
<p><span style="font-weight: 400;">Next, we write the code that will use LwIP to send and receive data.</span></p>
<p><b>Initialize LwIP</b><span style="font-weight: 400;">: </span></p>
<p><span style="font-weight: 400;">In your main </span><span style="font-weight: 400;">main.c</span><span style="font-weight: 400;"> file, include the LwIP header:</span></p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">#include &quot;lwip.h&quot;</code></pre></pre>
</div>
<p><span style="font-weight: 400;">In the </span><span style="font-weight: 400;">main()</span><span style="font-weight: 400;"> function, call the LwIP initialization function:</span></p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">MX_LWIP_Init();</code></pre></pre>
</div>
<p><b>Create a New TCP Server</b><span style="font-weight: 400;">: </span></p>
<p><span style="font-weight: 400;">Use the raw API to create a TCP server that listens for connections. Example:</span><span style="font-weight: 400;"><span style="background-color: inherit; color: inherit; font-family: inherit; font-size: var(--hcb--fz,14px); white-space: pre;">struct tcp_pcb *pcb = tcp_new();</span><br />
</span></p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">tcp_bind(pcb, IP_ADDR_ANY, 80); // Bind to port 80
pcb = tcp_listen(pcb);
tcp_accept(pcb, my_accept);</code></pre></pre>
</div>
<p><span style="font-weight: 400;">Define the </span><span style="font-weight: 400;">my_accept</span><span style="font-weight: 400;"> function, which handles new connections.</span></p>
<p><b>Handle Incoming Data</b><span style="font-weight: 400;">: </span></p>
<p><span style="font-weight: 400;">Inside the accept function, you will handle incoming data from clients. Example:</span><span style="font-weight: 400;"><br />
</span><span style="font-weight: 400;"><br />
</span></p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">err_t my_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
    if (p != NULL) {
        // Process data
        tcp_recved(pcb, p-&gt;tot_len);
        pbuf_free(p);
    } else {
        tcp_close(pcb);
    }
    return ERR_OK;
}</code></pre></pre>
</div>
<h5><b>Step 6: Compile and Flash</b></h5>
<ol>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Build your project by clicking the </span><b>Build</b><span style="font-weight: 400;"> button.</span></li>
<li style="font-weight: 400;" aria-level="1"><span style="font-weight: 400;">Flash the program to your STM32F407 board using the </span><b>Debug</b><span style="font-weight: 400;"> button.</span></li>
</ol>
<h5><b>Step 7: Test the Setup</b></h5>
<p><span style="font-weight: 400;">Connect your STM32F407 to your network. Open a web browser and enter the IP address you assigned earlier (e.g., </span><span style="font-weight: 400;">192.168.0.10</span><span style="font-weight: 400;">). If everything is set up correctly, your STM32 should now be responding to network requests.</span></p>
<p>Here’s an example of a simple main.c file that shows how to initialize LwIP and set up a basic TCP server using the raw API on the STM32F407. This code creates a server that listens for incoming connections on port 80 and responds to any data it receives.</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">#include &quot;main.h&quot;
#include &quot;lwip.h&quot;
#include &quot;tcp.h&quot;

/* Function prototypes */
err_t my_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
err_t my_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);

int main(void) {
    /* Initialize the hardware (clocks, peripherals, etc.) */
    HAL_Init();
    SystemClock_Config();  // Configure system clock
    MX_GPIO_Init();        // Initialize GPIO
    MX_LWIP_Init();        // Initialize LwIP stack

    /* Create a new TCP control block */
    struct tcp_pcb *pcb = tcp_new();
    if (pcb == NULL) {
        /* Handle error if TCP PCB creation failed */
        while (1);
    }

    /* Bind the PCB to port 80 (HTTP) */
    err_t bind_err = tcp_bind(pcb, IP_ADDR_ANY, 80);  // Bind to any IP address, port 80
    if (bind_err != ERR_OK) {
        /* Handle error if bind failed */
        tcp_close(pcb);
        while (1);
    }

    /* Put the PCB into the listening state */
    pcb = tcp_listen(pcb);
    if (pcb == NULL) {
        /* Handle error if listen failed */
        while (1);
    }

    /* Set the accept callback function */
    tcp_accept(pcb, my_accept);

    /* Infinite loop */
    while (1) {
        /* Handle LwIP tasks */
        MX_LWIP_Process();
    }
}

/* Accept callback function - called when a new connection is established */
err_t my_accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
    /* Set the receive callback function for the new connection */
    tcp_recv(newpcb, my_recv);
    return ERR_OK;
}

/* Receive callback function - called when data is received */
err_t my_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
    if (p != NULL) {
        /* Process the received data */
        char *data = (char *)p-&gt;payload;
        data[p-&gt;len] = &#039;\0&#039;;  // Null-terminate the data
        printf(&quot;Received data: %s\n&quot;, data);  // Print received data

        /* Send a response back to the client */
        const char *response = &quot;HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!&quot;;
        tcp_write(tpcb, response, strlen(response), TCP_WRITE_FLAG_COPY);

        /* Acknowledge that we&#039;ve received the data */
        tcp_recved(tpcb, p-&gt;tot_len);

        /* Free the pbuf */
        pbuf_free(p);
    } else {
        /* If p is NULL, the connection was closed */
        tcp_close(tpcb);
    }

    return ERR_OK;
}
 </code></pre></pre>
</div>
<h4><b>Troubleshooting</b></h4>
<ul>
<li style="font-weight: 400;" aria-level="1"><b>No response from STM32?</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Check the Ethernet cable and PHY chip connection.</span></li>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Make sure the IP address is set correctly.</span></li>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">Ensure LwIP is initialized properly in the code.</span></li>
</ul>
</li>
<li style="font-weight: 400;" aria-level="1"><b>Data loss?</b>
<ul>
<li style="font-weight: 400;" aria-level="2"><span style="font-weight: 400;">If data is not being received or sent properly, adjust the buffer sizes in LwIP settings. You can increase the size of the receive and send buffers in STM32CubeMX.</span></li>
</ul>
</li>
</ul>
<h4><b>Conclusion</b></h4>
<p><span style="font-weight: 400;">Setting up LwIP raw on an STM32F407 is straightforward when following these steps. By configuring Ethernet in STM32CubeMX and writing raw TCP code, you can create efficient, low-level network applications.</span></p>
<p>&nbsp;</p>
<p>The post <a href="https://www.teachmemicro.com/how-to-set-up-lwip-raw-on-stm32f407/">How to Set Up LwIP Raw on STM32F407</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Generating Sine, Triangular, and Sawtooth Waveforms using STM32F4 DAC</title>
		<link>https://www.teachmemicro.com/generating-sine-triangular-and-sawtooth-waveforms-using-stm32f4-dac/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=generating-sine-triangular-and-sawtooth-waveforms-using-stm32f4-dac</link>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 05 Jun 2023 04:00:04 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=6069</guid>

					<description><![CDATA[<p>The STM32F4 microcontroller series is popular in embedded systems applications due to its powerful features. One of its key peripherals is the Digital-to-Analog Converter (DAC), which enables the generation of analog waveforms. In this article, I will show you how to generate sine, triangular, and sawtooth waveforms using the STM32F4 DAC and MATLAB. The STM32F4 &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/generating-sine-triangular-and-sawtooth-waveforms-using-stm32f4-dac/">Generating Sine, Triangular, and Sawtooth Waveforms using STM32F4 DAC</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The <a href="https://www.ampheo.com/product/stmicroelectronics-stm32f411vct6-25392">STM32F4 microcontroller</a> series is popular in <a href="https://www.ampheo.com/">embedded systems</a> applications due to its powerful features. One of its key peripherals is the Digital-to-Analog Converter (DAC), which enables the generation of analog waveforms. In this article, I will show you how to generate sine, triangular, and sawtooth waveforms using the STM32F4 DAC and MATLAB.</p>
<p><span id="more-6069"></span></p>
<p>The STM32F4 DAC provides one or more channels that can be used to convert digital values into analog voltages of 12-bit resolution. With proper configuration, the DAC can generate precise and smooth analog waveforms.</p>
<h3><strong>Setting up the DAC</strong></h3>
<p>Enabling the DAC of the STM32F4 is easy by using STM32CubeMX.</p>
<ol>
<li>Select <a href="https://www.win-source.net/products/detail/stmicroelectronics/stm32f407vet6.html">STM32F407VET6</a> as the <a href="https://www.ampheo.com/c/microcontrollers">microcontroller</a></li>
<li>On <em>Pinout &amp; Configuration</em>, select Analog &gt; DAC then check the <em>OUT1 Configuration</em> checkbox.</li>
<li>In the <em>Parameter Settings </em>box, set the ff:
<ol>
<li>Output Buffer: Enable</li>
<li>Trigger: Timer 2 Trigger Out event</li>
<li>Wave generation mode: Triangle wave generation</li>
<li>Maximum Triangle Amplitude: 15</li>
</ol>
</li>
<li>In <em>Pinout View</em>, click the PA4 pin and select DAC_OUT1</li>
</ol>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting.jpg"><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting-1024x468.jpg" alt="" width="618" height="282" class="aligncenter wp-image-6071 size-large" srcset="https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting-1024x468.jpg 1024w, https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting-300x137.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting-768x351.jpg 768w, https://www.teachmemicro.com/wp-content/uploads/2023/06/STM32CubeMX-setting.jpg 1404w" sizes="auto, (max-width: 618px) 100vw, 618px" /></a></p>
<p>&nbsp;</p>
<p style="padding-left: 40px;">5. We also add two inputs for adjusting the frequency speed. These pins are mapped to the on-board buttons on the STM32F407 development board.</p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2023/06/frequency-control-buttons.jpg" alt="" width="340" height="226" class="size-full wp-image-6072 aligncenter" srcset="https://www.teachmemicro.com/wp-content/uploads/2023/06/frequency-control-buttons.jpg 340w, https://www.teachmemicro.com/wp-content/uploads/2023/06/frequency-control-buttons-300x199.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2023/06/frequency-control-buttons-310x205.jpg 310w" sizes="auto, (max-width: 340px) 100vw, 340px" /></p>
<p style="padding-left: 40px;">5. We also need to use the DMA controller to transfer the waveform data from memory to the DAC register for continuous output.</p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2023/06/DMA-settings.jpg" alt="" width="723" height="129" class="size-full wp-image-6074 aligncenter" srcset="https://www.teachmemicro.com/wp-content/uploads/2023/06/DMA-settings.jpg 723w, https://www.teachmemicro.com/wp-content/uploads/2023/06/DMA-settings-300x54.jpg 300w" sizes="auto, (max-width: 723px) 100vw, 723px" /></p>
<p style="padding-left: 40px;">6. Finally, we setup the TIMER2 timer as this acts as trigger for our waveform generation.</p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2023/06/timer2-settings.jpg" alt="" width="721" height="612" class="size-full wp-image-6073 aligncenter" srcset="https://www.teachmemicro.com/wp-content/uploads/2023/06/timer2-settings.jpg 721w, https://www.teachmemicro.com/wp-content/uploads/2023/06/timer2-settings-300x255.jpg 300w" sizes="auto, (max-width: 721px) 100vw, 721px" /></p>
<p>After this, name your project and then click the "Generate Code" button on the top right. Now let's do some coding!</p>
<h3><strong>Generating Waveforms with MATLAB</strong></h3>
<p><a href="https://www.mathworks.com/products/matlab.html">MATLAB</a> is a powerful tool for numerical computation and signal processing. We can leverage MATLAB's capabilities to generate waveform data and then transfer it to the STM32F4 microcontroller for DAC output. Here's a step-by-step guide:</p>
<h4><strong>Step 1: Generate Waveform Data in MATLAB:</strong></h4>
<p>In MATLAB, use built-in functions or mathematical equations to generate the waveform data for the desired waveform types (sine, triangular, or sawtooth). For instance, you can use the <em>sin(T)</em>, <em>sawtooth(T)</em>, or <em>sawtooth(T, 0.5)</em> functions to create the respective waveforms. Specify the desired duration, sample points, and DAC resolution for the waveforms.</p>
<p>Here's the MATLAB script for generating a sine wave:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">clear; clc; % Clear The Previous Points
Ns = 128; % Set The Number of Sample Points 
RES = 12; % Set The DAC Resolution 
OFFSET = 0; % Set An Offset Value For The DAC Output 
%------------[ Calculate The Sample Points ]------------- 

T = 0:((2*pi/(Ns-1))):(2*pi); 
Y = sin(T);
Y = Y + 1;
Y = Y*((2^RES-1)-2*OFFSET)/(2+OFFSET);
Y = round(Y);
plot(T, Y);
grid
%--------------[ Print The Sample Points ]---------------

fprintf(&#039;%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, \n&#039;, Y);</code></pre></pre>
<p>&nbsp;</p>
</div>
<p>For the other waveforms, we only need to change the function. For Sawtooth:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">clear; clc; % Clear The Previous Points
Ns = 128; % Set The Number of Sample Points
RES = 12; % Set The DAC Resolution
OFFSET = 0; % Set An Offset Value For The DAC Output
%------------[ Calculate The Sample Points ]-------------

T = 0:((2*pi/(Ns-1))):(2*pi);
Y = sawtooth(T);
Y = Y + 1; 
Y = Y*((2^RES-1)-2*OFFSET)/(2+OFFSET);
Y = round(Y); 
plot(T, Y);
grid
%--------------[ Print The Sample Points ]---------------

fprintf(&#039;%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, \n&#039;, Y);</code></pre></pre>
<p>&nbsp;</p>
</div>
<p>For Triangular:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">clear; clc; % Clear The Previous Points
Ns = 128; % Set The Number of Sample Points
RES = 12; % Set The DAC Resolution
OFFSET = 0; % Set An Offset Value For The DAC Output
%------------[ Calculate The Sample Points ]-------------

T = 0:((2*pi/(Ns-1))):(2*pi);
Y = sawtooth(T);
Y = Y + 1; 
Y = Y*((2^RES-1)-2*OFFSET)/(2+OFFSET);
Y = round(Y); 
plot(T, Y);
grid
%--------------[ Print The Sample Points ]---------------

fprintf(&#039;%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, \n&#039;, Y);</code></pre></pre>
<p>&nbsp;</p>
</div>
<h4><strong>Step 2: Export Waveform Data from MATLAB</strong></h4>
<p>The MATLAB scripts above will generate the data needed to create a lookup table for the STM32 code.</p>
<p>For Sine:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">2048, 2149, 2250, 2350, 2450, 2549, 2646, 2742, 2837, 2929, 3020, 3108, 3193, 3275, 3355, 
3431, 3504, 3574, 3639, 3701, 3759, 3812, 3861, 3906, 3946, 3982, 4013, 4039, 4060, 4076, 
4087, 4094, 4095, 4091, 4082, 4069, 4050, 4026, 3998, 3965, 3927, 3884, 3837, 3786, 3730, 
3671, 3607, 3539, 3468, 3394, 3316, 3235, 3151, 3064, 2975, 2883, 2790, 2695, 2598, 2500, 
2400, 2300, 2199, 2098, 1997, 1896, 1795, 1695, 1595, 1497, 1400, 1305, 1212, 1120, 1031, 
944, 860, 779, 701, 627, 556, 488, 424, 365, 309, 258, 211, 168, 130, 97, 
69, 45, 26, 13, 4, 0, 1, 8, 19, 35, 56, 82, 113, 149, 189, 
234, 283, 336, 394, 456, 521, 591, 664, 740, 820, 902, 987, 1075, 1166, 1258, 
1353, 1449, 1546, 1645, 1745, 1845, 1946, 2047</code></pre></pre>
</div>
<p>For Sawtooth:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">0, 32, 64, 97, 129, 161, 193, 226, 258, 290, 322, 355, 387, 419, 451, 
484, 516, 548, 580, 613, 645, 677, 709, 742, 774, 806, 838, 871, 903, 935, 
967, 1000, 1032, 1064, 1096, 1129, 1161, 1193, 1225, 1258, 1290, 1322, 1354, 1386, 1419, 
1451, 1483, 1515, 1548, 1580, 1612, 1644, 1677, 1709, 1741, 1773, 1806, 1838, 1870, 1902, 
1935, 1967, 1999, 2031, 2064, 2096, 2128, 2160, 2193, 2225, 2257, 2289, 2322, 2354, 2386, 
2418, 2451, 2483, 2515, 2547, 2580, 2612, 2644, 2676, 2709, 2741, 2773, 2805, 2837, 2870, 
2902, 2934, 2966, 2999, 3031, 3063, 3095, 3128, 3160, 3192, 3224, 3257, 3289, 3321, 3353, 
3386, 3418, 3450, 3482, 3515, 3547, 3579, 3611, 3644, 3676, 3708, 3740, 3773, 3805, 3837, 
3869, 3902, 3934, 3966, 3998, 4031, 4063, 0</code></pre></pre>
</div>
<p>For Triangular:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-plain" data-lang="Plain Text"><pre><code class="language-cpp">0, 64, 129, 193, 258, 322, 387, 451, 516, 580, 645, 709, 774, 838, 903, 
967, 1032, 1096, 1161, 1225, 1290, 1354, 1419, 1483, 1548, 1612, 1677, 1741, 1806, 1870, 
1935, 1999, 2064, 2128, 2193, 2257, 2322, 2386, 2451, 2515, 2580, 2644, 2709, 2773, 2837, 
2902, 2966, 3031, 3095, 3160, 3224, 3289, 3353, 3418, 3482, 3547, 3611, 3676, 3740, 3805, 
3869, 3934, 3998, 4063, 4063, 3998, 3934, 3869, 3805, 3740, 3676, 3611, 3547, 3482, 3418, 
3353, 3289, 3224, 3160, 3095, 3031, 2966, 2902, 2837, 2773, 2709, 2644, 2580, 2515, 2451, 
2386, 2322, 2257, 2193, 2128, 2064, 1999, 1935, 1870, 1806, 1741, 1677, 1612, 1548, 1483, 
1419, 1354, 1290, 1225, 1161, 1096, 1032, 967, 903, 838, 774, 709, 645, 580, 516, 
451, 387, 322, 258, 193, 129, 64, 0</code></pre></pre>
</div>
<p>This will be the DAC levels, generated in between TIMER2 intervals, which will result in the specific waveform. The above data can be easily converted to an array in C like this:</p>
<div class="hcb_wrap">
<pre class="prism undefined-numbers lang-c" data-lang="C"><pre><code class="language-cpp">#define NS 128

uint32_t Sine_Wave_LUT[NS] = {
2048, 2149, 2250, 2350, 2450, 2549, 2646, 2742, 2837, 2929, 3020, 3108, 3193, 3275, 3355,
3431, 3504, 3574, 3639, 3701, 3759, 3812, 3861, 3906, 3946, 3982, 4013, 4039, 4060, 4076,
4087, 4094, 4095, 4091, 4082, 4069, 4050, 4026, 3998, 3965, 3927, 3884, 3837, 3786, 3730,
3671, 3607, 3539, 3468, 3394, 3316, 3235, 3151, 3064, 2975, 2883, 2790, 2695, 2598, 2500,
2400, 2300, 2199, 2098, 1997, 1896, 1795, 1695, 1595, 1497, 1400, 1305, 1212, 1120, 1031,
944, 860, 779, 701, 627, 556, 488, 424, 365, 309, 258, 211, 168, 130, 97,
69, 45, 26, 13, 4, 0, 1, 8, 19, 35, 56, 82, 113, 149, 189,
234, 283, 336, 394, 456, 521, 591, 664, 740, 820, 902, 987, 1075, 1166, 1258,
1353, 1449, 1546, 1645, 1745, 1845, 1946, 2047
};

uint32_t Saw_Wave_LUT[NS] = {
0, 32, 64, 97, 129, 161, 193, 226, 258, 290, 322, 355, 387, 419, 451,
484, 516, 548, 580, 613, 645, 677, 709, 742, 774, 806, 838, 871, 903, 935,
967, 1000, 1032, 1064, 1096, 1129, 1161, 1193, 1225, 1258, 1290, 1322, 1354, 1386, 1419,
1451, 1483, 1515, 1548, 1580, 1612, 1644, 1677, 1709, 1741, 1773, 1806, 1838, 1870, 1902,
1935, 1967, 1999, 2031, 2064, 2096, 2128, 2160, 2193, 2225, 2257, 2289, 2322, 2354, 2386,
2418, 2451, 2483, 2515, 2547, 2580, 2612, 2644, 2676, 2709, 2741, 2773, 2805, 2837, 2870,
2902, 2934, 2966, 2999, 3031, 3063, 3095, 3128, 3160, 3192, 3224, 3257, 3289, 3321, 3353,
3386, 3418, 3450, 3482, 3515, 3547, 3579, 3611, 3644, 3676, 3708, 3740, 3773, 3805, 3837,
3869, 3902, 3934, 3966, 3998, 4031, 4063, 0
};

uint32_t Tri_Wave_LUT[NS] = {
0, 64, 129, 193, 258, 322, 387, 451, 516, 580, 645, 709, 774, 838, 903,
967, 1032, 1096, 1161, 1225, 1290, 1354, 1419, 1483, 1548, 1612, 1677, 1741, 1806, 1870,
1935, 1999, 2064, 2128, 2193, 2257, 2322, 2386, 2451, 2515, 2580, 2644, 2709, 2773, 2837,
2902, 2966, 3031, 3095, 3160, 3224, 3289, 3353, 3418, 3482, 3547, 3611, 3676, 3740, 3805,
3869, 3934, 3998, 4063, 4063, 3998, 3934, 3869, 3805, 3740, 3676, 3611, 3547, 3482, 3418,
3353, 3289, 3224, 3160, 3095, 3031, 2966, 2902, 2837, 2773, 2709, 2644, 2580, 2515, 2451,
2386, 2322, 2257, 2193, 2128, 2064, 1999, 1935, 1870, 1806, 1741, 1677, 1612, 1548, 1483,
1419, 1354, 1290, 1225, 1161, 1096, 1032, 967, 903, 838, 774, 709, 645, 580, 516,
451, 387, 322, 258, 193, 129, 64, 0
};</code></pre></pre>
</div>
<p>The full code can be seen in my repo: <a href="https://github.com/kurimawxx00/STM32F4WaveFormGen/">https://github.com/kurimawxx00/STM32F4WaveFormGen/</a></p>
<h4><strong>Step 4: Run the Application</strong></h4>
<p>Finally, upload and run the firmware on the STM32F407 development board. The microcontroller will receive the waveform data from MATLAB, configure the DAC and DMA, and generate the desired waveform on the DAC output pin. Press the K0 button the decrease or the K1 button to increase the frequency of the waveform. Press both K0 and K1 buttons to change waveforms.</p>
<h3><strong>Conclusion</strong></h3>
<p>By leveraging the STM32F4 DAC and MATLAB's powerful signal processing capabilities, we can generate precise and smooth waveforms like sine, triangular, and sawtooth waveforms in embedded applications. The combination of MATLAB's waveform generation capabilities and the STM32F4 DAC's accurate voltage output allows for versatile waveform generation in various applications such as audio synthesis, waveform analysis, and testing. Experiment with different waveform parameters and explore additional features of the STM32F4 DAC and MATLAB to further enhance waveform generation capabilities in your projects. Happy waveform generation!</p>
<p>The post <a href="https://www.teachmemicro.com/generating-sine-triangular-and-sawtooth-waveforms-using-stm32f4-dac/">Generating Sine, Triangular, and Sawtooth Waveforms using STM32F4 DAC</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Sending and Receiving Data over STM32 USB</title>
		<link>https://www.teachmemicro.com/stm32-cdc-usb-send-receive-data/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=stm32-cdc-usb-send-receive-data</link>
					<comments>https://www.teachmemicro.com/stm32-cdc-usb-send-receive-data/#respond</comments>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 26 Sep 2022 03:39:20 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=5622</guid>

					<description><![CDATA[<p>The serial port is the most usual comm channel between a microcontroller and a computer. However, the different voltage levels and loss of the RS-232 port in modern computers led to the use of USB-TLL converter chips like CH340, CP2102, etc. This is what happens in the NodeMCU ESP8266 and ESP32. Another example, the Arduino &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/stm32-cdc-usb-send-receive-data/">Sending and Receiving Data over STM32 USB</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The serial port is the most usual comm channel between a microcontroller and a computer. However, the different voltage levels and loss of the RS-232 port in modern computers led to the use of USB-TLL converter chips like CH340, CP2102, etc.</p>
<p>This is what happens in the <a href="https://www.teachmemicro.com/intro-nodemcu-arduino/">NodeMCU ESP8266</a> and <a href="https://www.teachmemicro.com/using-esp32-for-the-first-time/">ESP32</a>. Another example, the Arduino UNO, uses an extra microcontroller just for its USB interface.</p>
<p><img loading="lazy" decoding="async" class="" src="https://lh4.googleusercontent.com/EObwX3gDYQLOa1i9o0c6Xpv96E6WJoxyh5axvszKtxoJ7b_mZ10YagNXnv7Mw0MN0Ifh2pEcgF865yfeAsmXowteq6OM7M_fhu3DF_Xk4xjdId93Pxpzl1dPCyaPvuhejbMGrOMf2KEak1Tzq4LRkl0JK2ZabH9oPG59yDN1n75mRgjVJxTPpkT92Q" width="300" height="300" /><img loading="lazy" decoding="async" class="alignnone" src="https://lh6.googleusercontent.com/HlPG_8etMYkXFv6RlJoDpi2BRkx9_8ry1A5gNMRmw9OCMSzqAslDYZqARZpC2QuGIb8G0XKbNCCoMRLhoeYE6kqssL4lY2V_lQBVvDg7dq7A3LV4SR2sPutnL9PDAUAI5ONXybhVumeNRreSr2RrTolRGXNl_I06amvW6PzDyjPuxUBPynoSw2M3ag" width="307" height="225" /></p>
<p>Adding another chip would entail costs in both part counts in PCB space. Thankfully, the <a href="https://www.teachmemicro.com/programming-stm32f407ve-black-board/">STM32F4 microcontroller</a> provides a neat solution.</p>
<h3><strong>What is CDC?</strong></h3>
<p><strong>Communications Device Class (CDC)</strong> is one of many <a href="https://en.wikipedia.org/wiki/USB#Device_classes">device classes</a> specified by the USB protocol. Its primary use is for “computer networking devices akin to a network card, providing an interface for transmitting Ethernet or ATM frames onto some physical media. It is also used for modems, ISDN, fax machines, and telephony applications for performing regular voice calls.”</p>
<p>With CDC, a USB device acts like a normal serial port device. You can then send and receive messages serially from/to the computer. Since it’s not a “real” COM port, CDC devices are also called Virtual COM Ports.</p>
<h3><strong>Setting Up STM32 USB CDC</strong></h3>
<p>For this tutorial, I will be using the STM32F4 Black Board. This board doesn’t come with any USB-TLL converter or ST-Link, unlike the STM32 Discovery line. It does come with a miniUSB port, whose D-, and D+ pins are wired to pins PA11 and PA12.</p>
<p><img decoding="async" class="aligncenter" src="https://lh6.googleusercontent.com/7EAoo-c0BFHBbdGBEupRvBQVKXzfl3QmWlTWzvXFRiSnCTI8tJ0OM50ZjWp88BxDi2eXK1m23k4ijyX4b_VpcKmWDq2tkYvKj3jVqaCBRzCr_LUdxeg7h9WtSA7VEJG1WWmb2p2MkRDr5B_7JDQYPtwK4Xx9D7o3E_ydBVRFfzFWI75GijVLIDLpSg" /></p>
<p>The application will control the onboard LEDs via sending characters thru CDC. For reference, the onboard LEDs are at PA6 and PA7.</p>
<p><img decoding="async" class="aligncenter" src="https://lh3.googleusercontent.com/GwnrNb2aUZvTry6LlZJy1zvAtRlJ7XlWYG6Jzpa3x5uqkKGM42gl6mQuiCaTjaQq4qRvJk5PaYbg29NVpx4Q22tPYqetP5n9YBaTYh2ynm2-jlJl6cknB3nVQ_v0BLVwRXqxrjqx_-Q_XL9s63nlREvrLcuPz7dzyEc2TnLp3Ho48e4CAoBTDmiw-g" /></p>
<p>When an “x” is received, D2 lights up. When a “y” is received, D2 turns off. Alternatively, when an “a” is received, D3 lights up and when a “b” is received, D3 turns off.</p>
<p>It’s time to run STM32CubeMX! First up, we set the GPIO pins for the LEDs.</p>
<p>Make both PA6 and PA7 output pins:</p>
<p><img decoding="async" class="aligncenter" src="https://lh5.googleusercontent.com/iVprAdHoQ-0GHP-z2Z_GnnhHEO4FbfWCH3VnsIGBAiBH8_5Vw_IHwHgxPIGNMcIwbepy6bLX5kXeNnTdMEYPgvaiztkrqOgoMs72xNqbx4ftKKswqy4Zz_sD-Ju2nQnzcw3vVKHdUiz8Hve9XtsBUWA2Lz8PaCSprQnFS7JdJjULwK44TdJMdk2vWg" /></p>
<p>To rename them, just go to <strong><em>System Core &gt; GPIO</em></strong> and assign user labels:</p>
<p><img decoding="async" class="aligncenter" src="https://lh5.googleusercontent.com/2S5SJjPg534t4_fWu-dWYduPYID_cBZJPJ8COt90X2p8OYyJtoklbPeK0uNUk3m-s-i5XaI0CDJZiUF7JBLBnHw7PnS8IayT5wQpMkjmqvL8TkO8AXqbS_AvYDr4J60LI19mKcq89kg_-UHriPgSXOxF6nVZOtYZj0d8qowwJ5nhnVTz_k1XT0BeUQ" /></p>
<p>Next, we make use of the 8 MHz crystal on the board. Set the <em>PHO</em> and <em>PH1</em> pins as <em>RCC</em> pins.</p>
<p><img decoding="async" class="aligncenter" src="https://lh6.googleusercontent.com/LcRF43RaLxKbuI_u96d9cMPq1Ky_kJMklxN5lgNHBZ8PAUe5HlNAUc1hu8T3lK_Fbb4p72EmnNgsFxAfO2yay7dFl9aoo4tU7paxoOoZuvGO-GlV_-EvYvTL6XSsATv1ueY5meXJqF9uWeJacb5_sh6GF9QCJ_UuuBcePjS2iVk36AcTIjH7ZHEQUQ" /></p>
<p>Go to <strong><em>System Core &gt; RCC</em></strong> and select <em>Crystal/Ceramic Resonator</em> as <em>High Speed Clock</em> source.</p>
<p><img decoding="async" class="aligncenter" src="https://lh4.googleusercontent.com/J7LXSxK0OO1kiEbvpp81xK6WKBHoP2M4Nh-DpCV8BGrIulKM_8_HIYc0H4SL2jryGBuKYJ7vKYJrbRJsERioHSfM-pbssurIKkzazm4xkkrVHHHP3b9K_HPl4ixc7hWGnNqZeA-x93O6Lc4dR14xUrUvee9Ny9BFMmyp7d4wpPzCzJRbDRSXkwuI1A" /></p>
<p>At this point, you can go to the <strong>Clock Configuration</strong> tab and change the input frequency to 8 MHz, HSE.</p>
<p><img decoding="async" class="aligncenter" src="https://lh3.googleusercontent.com/U3hbKu7dyNzOed2GBkAM3wcJmWpiRJR4OJ2XW1slHKTD0SWMwICzp5u-oeGoSF9oosfUmaib4poNzSUQ4LM98NSoCwDTGN4k1V1xuvrB5s0jxpP9yDKPClYalrYzYUqeOETO4kAfIglG85ahbhxWl4t9p5wZK2k3hUew5mABSPwHUoJuRT2-4LDM7A" /></p>
<p>Of course, we still need to enable USB. Change pins <em>PA11</em> and <em>PA12</em> to their <a href="https://en.wikipedia.org/wiki/USB_On-The-Go"><em>USB_OTG</em></a> alternate functions.</p>
<p><img decoding="async" class="aligncenter" src="https://lh6.googleusercontent.com/Wc36WHFN6McsixdtLnahRABrYPGYJOMwHJSjRCZiBjUiPKOlpXuTN0TljrlJE9q3zm5I3r68kwYRKZ5ic138aoByrMtW1aawQiOhiPe6IB1sTyYv58_d6lS-CnhRDL1q4G4blqLBA_21qk4EsV8Fv8CaKawgkTbT0OQlq59FiQMCClI3QL62nVnrYQ" /></p>
<p>Then go to <strong><em>Connectivity &gt; USB_OTG_FS</em></strong> and select <em>Device_Only</em> mode.</p>
<p><img decoding="async" class="aligncenter" src="https://lh4.googleusercontent.com/8QrbpMv53xZN5I-3OC4s8WPLi9G73TKwlGyBz7IzE-GwV930gYSzUW8CIV9lTx1km5m8Hq_C9rTLvtVbMy4S5vhoC0-FvcKJvwAqyVyjOsozkvGSGL-SzrUMwMWLPGJ_qPqnXW7Jdw2JllJMo6gBrKAVfh8q1S0gdWa6rb9NbPBo46DxfEujIhtn4w" /></p>
<p>Next, go to <strong><em>Middleware</em> &gt; </strong><em><strong>USB_DEVICE</strong>:</em></p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2022/09/usb-cdc-middleware.jpg" alt="" width="400" height="383" class="alignnone size-full wp-image-5880" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/09/usb-cdc-middleware.jpg 400w, https://www.teachmemicro.com/wp-content/uploads/2022/09/usb-cdc-middleware-300x287.jpg 300w" sizes="auto, (max-width: 400px) 100vw, 400px" /></p>
<p>On the dropdown on the right window, select <em>Communication Device Class:</em></p>
<p><img loading="lazy" decoding="async" src="https://www.teachmemicro.com/wp-content/uploads/2022/09/usb_device_mode.jpg" alt="" width="442" height="610" class="alignnone size-full wp-image-5881" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/09/usb_device_mode.jpg 442w, https://www.teachmemicro.com/wp-content/uploads/2022/09/usb_device_mode-217x300.jpg 217w" sizes="auto, (max-width: 442px) 100vw, 442px" /></p>
<p>Finally, go over to <strong>Project Configuration</strong>, name your project, select a location to save it, and specify the toolchain (MDK-ARM if you’re using Keil). You may also need to change the heap size to 0x800 if you’re encountering problems whenever you’re plugging in the STM32F4 board on your computer.</p>
<p><img decoding="async" class="aligncenter" src="https://lh4.googleusercontent.com/NnTn4f47wS1cAQCqq0NN2uTYe3yKi9Okw6RDj_BUp9zLP8qSrFWYItWDdeVmy7X8jJnDqWzKsBCoWQesOOFOVfRgiq3e5Cwy01FFlQjS0Yu3XFURwW1uhIC1HeLxIsn0zacnyGeIbHdCD4H_cV1NYvnI9xfFI2E9U5EpbbScYRFKX4RpiA3KbouT_Q" /></p>
<p>Click “Generate Code” and open Keil.</p>
<h3><strong>Receiving Data from PC to STM32</strong></h3>
<p>In <em>main.c</em>, declare a variable that will serve as our buffer for user input. I placed mine between the <em>Private Variables</em> section:</p>
<p><img decoding="async" src="https://lh5.googleusercontent.com/esQ3qQyM1WmjuZSFUeT541widBNK2btWqfFlieNNacHLac0HDcJndMRSH_cf6rdk4lKSGlhZcf_y8qjdV-BGCOl2zW0uvMfHfNBbEYA4sk1fdx-kx56qYh9tcnJcFHvRp4E-fDlBF09XGZ-a2M55FAHYggYnOajjskW16BcLP5FM0jn5y0205i9Qyg" /></p>
<p>Just before the <em>while(1)</em> loop in int main, turn off the LEDs:</p>
<p><img decoding="async" src="https://lh5.googleusercontent.com/koPptxALSBARMcVt_FEeMSo-E-Vx0tEaaLifaZyxZo1bg8ge5GjhptvYsFZUfhpjcnSWdYLOZr916MsZbeUvQt1E8R06jiq2n17uNtq0g8UEumOeBkEPf0sv8zJs7NZbsLRMD_vFPWfCDZDknrq0fWOoQQZBPQbPgHU0VQ2u_JfvGyEPUaDJT77gmw" /></p>
<p>Inside the <em>while(1)</em> loop, we insert the part where we check the serial input and control the LEDs accordingly:</p>
<p><img decoding="async" src="https://lh5.googleusercontent.com/zKkp_95dgy_WTwiHGfjkSnuui26nrchMGBHvI8RrvDt8iXdasqBnmEz5qPj3GELtnHS0JrfHKZIy2SM3Xul8AnSyra6nUajFDmwD22LyAbGfFnRsFRsu8NuLQmX6gnAc84C_Rda9Z80U83-byzf7-c5pGb_VbYHTb27R4U44ifHr9NfWJFC7bzMLsQ" /></p>
<p>We’re not done yet! Open the file <em>usbd_cdc_if.c</em>. This is where all the implementing functions for CDC are found.</p>
<p>Declare the same variable we used as buffer in main.c:</p>
<p><img decoding="async" src="https://lh6.googleusercontent.com/5J3rlS0eGVXdVorHBp7xSmE2aWftuolocrNUcUI5MmqGwYmzSaQWa1u220hO94DE7flmaGsl8Ga5BstoWDwUcj4ajvQ7bHxg9e-5qZ-v-bhPiSEc4iUhMIfhG1-82FdKHHd5Ih-J_CYbNX7m6y-3lnv4vYtVuUJP5rH6jm32WUbt2fIMl6v71r1q4Q" /></p>
<p>The keyword extern means it’s the same variable on main.c. This gives <em>user_inp[]</em> a global scope between the two files.</p>
<p>Locate the function <em>CDC_Receive_FS()</em> and add the following lines.</p>
<p><img decoding="async" src="https://lh6.googleusercontent.com/iGjutghEi1nTQwVoWrOA4_GGT9-bjj7gnnRLukGPR3-A3ZI_h9NX-D8YHCVdQYKYa4doukAvd2NXfHXlh02rcW7XSGZ83S95Al6SYo4ReMgTL8vwf5UMqJGpI2UkWBMpiqdATNMJz4aLTuvXeevip6kugUL7J7s9FFyzlt_Zod7Le8iw2FufIoD7zg" /></p>
<p>This function is a callback function that auto triggers whenever there is a received message thru CDC. That message is saved to the memory pointed by <em>Buf</em>, and its length is given by <em>Len</em>. All we have to do is copy that message to <em>user_inp</em> using <a href="https://cplusplus.com/reference/cstring/memcpy/">memcpy</a>. Before that, we must make sure that <em>user_inp</em> does not retain its previous value. This is what the first <a href="https://cplusplus.com/reference/cstring/memset/">memset()</a> does. The second memset is for clearing the <em>Buf</em> variable once we have transferred its contents to <em>user_inp</em>.</p>
<p>That’s about it for the code. Build and flash it to the board. As soon as you connect the board to the computer via USB, it will appear as a COM port in Device Manager:</p>
<p><img decoding="async" class="aligncenter" src="https://lh6.googleusercontent.com/9D8Phtg3b0UuBNL2N1MEd6kz2XCuJPYA666c974ygvLaRpxtVszVrpKpuSzVlzvcn6TRz-9JiHWnw0Tr72u9rdd37sSDNpDPZyH0AoF7AcSG0JjEph-7AOkEpO-0f1Y6i-e60zW7hsyGgo4NupkoiA2el8hf_kOo1hIhTD7XdWCeMcTX7f3v5LXU0Q" /></p>
<p>Using a terminal application like <a href="https://www.putty.org/">Putty</a>, we can now send messages to the STM32F4 Black Board from the computer.</p>
<p><img decoding="async" class="aligncenter" src="https://lh4.googleusercontent.com/cE24KGpxWzCzIWjYKinF5VeJDjj42xIBw3_xJIwMepbTcRU7qlNPgxdZX79ZIoLxtCgFSYdTqRDLbBTUL36g6j1jvpKEqybYJRP8MKn9FrF-QWU1Dk-cqscIkIGuN1N4eZxYImeQVbuwwarp2mRs6Ik0pvKed5DhLpSOBZrF4lWS9jt6oLjTFb52yw" /></p>
<p>Here's the output of this application:<br />
<iframe loading="lazy" width="560" height="315" src="https://www.youtube.com/embed/geEWA4QRHFc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="allowfullscreen"></iframe></p>
<p>&nbsp;</p>
<h3><strong>Sending Data From STM32 to PC</strong></h3>
<p>If there’s a function for receiving data, there’s also one for transmitting:</p>
<p>This function accepts two parameters: the data to be sent, and its length.</p>
<p>You can set those parameters like this:</p>
<p><img decoding="async" src="https://lh4.googleusercontent.com/chIxdSI1Dee1sr-Jy16sZKkSFDXikyyQyw9-luwlKvRN7rECup3CKtEaGcW3hjKH9i8FqNuvb3LjRf7s0xrpLEfHWL5o33cR2t4kIYi9XCSh0dzuiXuGS0uwj7FOWeIzV4Fa-UO8dn6u4rWx8v6Ty2ctJrV1FTGU7celoqjUnl1N1fgEQvAto-fDJg" /></p>
<p>Also, you need to define the prototype function with <em>extern</em> command:</p>
<p><img decoding="async" src="https://lh4.googleusercontent.com/MORyNvAnoGbl7NP9ZcplXGNakfCoLXzF2aBl3bvHD8u2yBTZkYqdbfx01pi40SK5_W4-vSZAJwiSShcAiTx0BycEv49rUobJp0K7LNjoDeryBWP9G6dV5g6ChdVl1qp2rcY2-bo7lvzzugkuQW-pwbujeyZc8qgEFIQV3H0X1dmuv9hwB9PfxrIdog" /></p>
<p>Finally, you can send messages. Here’s an example that sends a message every second:</p>
<p><img decoding="async" src="https://lh3.googleusercontent.com/G05bjKu8nyMJAfwOwL1opwpDLhgSm8SGf41TrKBeitPCuQYrWFTOHzAobyNpuBVfRyFWzXTZD7rGlvtuGTYp73BfZ7FuLaHk3kXHndtCn0NKQqWViAnnvYBkTx66LP-_wLGzJHkC24O-Ousj_Uk485u_e6lF0Xktkvu3ZWaB-V_yPWHwPj05qyWNFg" /></p>
<p>I hope you found this tutorial on STM32 CDC USB useful. Happy coding!</p>
<p>The post <a href="https://www.teachmemicro.com/stm32-cdc-usb-send-receive-data/">Sending and Receiving Data over STM32 USB</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.teachmemicro.com/stm32-cdc-usb-send-receive-data/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>STM32F1 Serial Port and printf()</title>
		<link>https://www.teachmemicro.com/stm32f1-serial-printf/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=stm32f1-serial-printf</link>
					<comments>https://www.teachmemicro.com/stm32f1-serial-printf/#respond</comments>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Sun, 05 Jun 2022 13:00:50 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=5568</guid>

					<description><![CDATA[<p>Needless to say, a serial output is a necessary tool in debugging embedded system applications. Placing the right messages in the right place will help you save hours in figuring out what went wrong in your code. Arduino programmers are very familiar with Serial. print() and its derivatives. But how can we implement it with STM32F1? &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/stm32f1-serial-printf/">STM32F1 Serial Port and printf()</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Needless to say, a serial output is a necessary tool in debugging embedded system applications. Placing the right messages in the right place will help you save hours in figuring out what went wrong in your code. Arduino programmers are very familiar with <em>Serial. print()</em> and its derivatives. But how can we implement it with STM32F1?</p>
<p><span id="more-5568"></span></p>
<p><span style="color: #ff0000;">Here's a <a href="https://www.teachmemicro.com/microcontroller-serial-communication/" style="color: #ff0000;">refresher on serial communication</a> just in case you need it.</span></p>
<p>Like most microcontrollers, the <a href="https://www.win-source.net/products/detail/stmicroelectronics/stm32f103rbt6.html"><strong>STM32F103RB</strong></a> has dedicated serial pins. This tutorial will use the PA2 and PA3 pins which house USART2 TX and RX pins respectively. The reason behind this choice is that for the <a href="https://www.st.com/en/evaluation-tools/nucleo-f103rb.html">Nucleo-64 F103B</a>, the USB port is wired directly to these pins.</p>
<p>In contrast, the <a href="https://www.teachmemicro.com/getting-started-blue-pill-stm32cube/">STM32 Blue Pill</a>, which uses an STM32F103C8, has pins PA11 and PA12 (USART1) wired to its USB port.</p>
<p>Assign the pins PA2 and PA3 to their USART equivalents as shown, using <em>STM32CubeMX</em>:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-pin-setup-CUBEMX.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5569" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-pin-setup-CUBEMX.jpg" alt="STM32F1 pin setup for serial output on USART2" width="558" height="492" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-pin-setup-CUBEMX.jpg 558w, https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-pin-setup-CUBEMX-300x265.jpg 300w" sizes="auto, (max-width: 558px) 100vw, 558px" /></a></p>
<p>Generate the code to an <em>MDK ARM</em> output. If you choose to open the project, Keil V5 should start up and you have your ready-to-use code.</p>
<p><span style="color: #ff0000;">Here's a <a href="https://www.teachmemicro.com/getting-started-blue-pill-stm32cube/" style="color: #ff0000;">separate tutorial</a> on how to use STM32CubeMX to generate code.</span></p>
<p>In <em>main.c</em>, you’ll notice a typedef for UART2.</p>
<pre class="lang:arduino decode:true "><pre><code class="language-cpp">UART_HandleTypeDef huart2;</code></pre></pre>
<p>This is used to initiate everything that needs to be initiated in order to use the serial port. Of course, this is also generated by STM32CubeMX:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-CubeMX-generated-uart-setup.jpg"><img loading="lazy" decoding="async" class="aligncenter wp-image-5571 size-full" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-CubeMX-generated-uart-setup.jpg" alt="Generated STM32F1 UART setup function by STM32CubeMX" width="413" height="442" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-CubeMX-generated-uart-setup.jpg 413w, https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-CubeMX-generated-uart-setup-280x300.jpg 280w" sizes="auto, (max-width: 413px) 100vw, 413px" /></a></p>
<p>All that is left is to call the transmit function to send messages via serial.</p>
<pre class="lang:arduino decode:true "><pre><code class="language-cpp">HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)</code></pre></pre>
<p>The function requires the UART typedef, a char array for the message to be sent, the size of that char array, and a timeout period.</p>
<p>Locate the line with the infinite loop (while (1)) and insert the following:</p>
<pre class="lang:arduino decode:true "><pre><code class="language-cpp">/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

  unsigned char stringMsg[] = &quot;Celtics 2022 Champs!\r\n&quot;;    //Data to send
  HAL_UART_Transmit(&amp;huart2,stringMsg,sizeof(stringMsg),10);// Sending in normal mode
  HAL_Delay(1000);

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */</code></pre></pre>
<p>This prints out the given string as long as the STM32F1 is on.</p>
<p>With the STM32F1 connected to the PC using the USB port, we use terminal apps like <a href="https://www.putty.org/">Putty</a> to view the message:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-serial-output-via-putty.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5572" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-serial-output-via-putty.jpg" alt="Serial output via Putty" width="383" height="274" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-serial-output-via-putty.jpg 383w, https://www.teachmemicro.com/wp-content/uploads/2022/06/STM32F1-serial-output-via-putty-300x215.jpg 300w" sizes="auto, (max-width: 383px) 100vw, 383px" /></a></p>
<h3><strong>Using <em>printf()</em> Function</strong></h3>
<p>We can further simplify printing out messages via serial by using the <em>printf()</em> function. For this, we need the <em>stdio</em> library.</p>
<p>Add a reference to the stdio library just below “main.h”:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/include-stdio.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5573" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/include-stdio.jpg" alt="include stdio library to use printf" width="157" height="36" /></a></p>
<p>Next, define a struct below the MX_GPIO_INIT function:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/struct-_file.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5574" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/struct-_file.jpg" alt="struct definition" width="568" height="95" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/struct-_file.jpg 568w, https://www.teachmemicro.com/wp-content/uploads/2022/06/struct-_file-300x50.jpg 300w" sizes="auto, (max-width: 568px) 100vw, 568px" /></a></p>
<p>Below this line, create an instance of the struct and name it <em>_stdout</em>:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/file-stdout.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5575" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/file-stdout.jpg" alt="" width="121" height="25" /></a></p>
<p>Then, define a function <em>fputc()</em>. This is what is required so that the UART channel is diverted to printf():</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/fputc-function.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5576" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/fputc-function.jpg" alt="fputc-function" width="454" height="147" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/fputc-function.jpg 454w, https://www.teachmemicro.com/wp-content/uploads/2022/06/fputc-function-300x97.jpg 300w" sizes="auto, (max-width: 454px) 100vw, 454px" /></a></p>
<p>We also add a <em>ferror</em> function that would handle (although here it does nothing) errors in UART.</p>
<p>All in all, this should be what it looks like:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2022/06/complete-fputc-function.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5577" src="https://www.teachmemicro.com/wp-content/uploads/2022/06/complete-fputc-function.jpg" alt="complete fputc function" width="594" height="377" srcset="https://www.teachmemicro.com/wp-content/uploads/2022/06/complete-fputc-function.jpg 594w, https://www.teachmemicro.com/wp-content/uploads/2022/06/complete-fputc-function-300x190.jpg 300w" sizes="auto, (max-width: 594px) 100vw, 594px" /></a></p>
<p>Now, instead of using this to transmit messages via serial port:</p>
<pre class="lang:arduino decode:true "><pre><code class="language-cpp">unsigned char stringMsg[] = &quot;Celtics 2022 Champs!\r\n&quot;; 
HAL_UART_Transmit(&amp;huart2,stringMsg,sizeof(stringMsg),10);</code></pre></pre>
<p>We can now use:</p>
<pre class="lang:arduino decode:true"><pre><code class="language-cpp">printf(“Celtics 2022 Champs!\r\n”);</code></pre></pre>
<p>The whole code for this tutorial is on <a href="https://github.com/kurimawxx00/stm32f1-serial-output">my repository</a>.</p>
<p>The post <a href="https://www.teachmemicro.com/stm32f1-serial-printf/">STM32F1 Serial Port and printf()</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.teachmemicro.com/stm32f1-serial-printf/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Programming the STM32F407VE Black Board</title>
		<link>https://www.teachmemicro.com/programming-stm32f407ve-black-board/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=programming-stm32f407ve-black-board</link>
					<comments>https://www.teachmemicro.com/programming-stm32f407ve-black-board/#comments</comments>
		
		<dc:creator><![CDATA[Roland Pelayo]]></dc:creator>
		<pubDate>Mon, 15 Mar 2021 01:00:19 +0000</pubDate>
				<category><![CDATA[STM32 Tutorial]]></category>
		<guid isPermaLink="false">https://www.teachmemicro.com/?p=4994</guid>

					<description><![CDATA[<p>I just got my hands on a STM32F407VET black development board from China and was excited about the possible projects I can build with it. To my demise, there’s not much information about programming the board so I had to find that out by myself. This post is for those who also purchased this board &#8230;</p>
<p>The post <a href="https://www.teachmemicro.com/programming-stm32f407ve-black-board/">Programming the STM32F407VE Black Board</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>I just got my hands on a <a href="https://ph.banggood.com/custlink/K3DySf5TZ3">STM32F407VET black development board</a> from China and was excited about the possible projects I can build with it. To my demise, there’s not much information about programming the board so I had to find that out by myself. This post is for those who also purchased this board and are having a hard time how to start.</p>
<p><span id="more-4994"></span></p>
<p>The board comes one of the more popular <a href="https://www.win-source.net/products/detail/stmicroelectronics/stm32f407vet6.html">STM32F407VET</a> <a href="https://www.ampheo.com">Embedded IC</a> and comes with a variety of features:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-board-features.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-4996" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-board-features.jpg" alt="STM32F407VE black board features" width="579" height="561" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-board-features.jpg 579w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-board-features-300x291.jpg 300w" sizes="auto, (max-width: 579px) 100vw, 579px" /></a></p>
<p>There’s a <a href="https://www.teachmemicro.com/using-microsd-breakout-board-arduino/">microSD</a> slot, external flash chip, 3 user buttons, 2 user LEDs, <a href="https://www.teachmemicro.com/arduino-nrf24l01/">NR24L01</a> socket, and easily accessible <a href="https://www.teachmemicro.com/microcontroller-serial-communication/">serial</a> pins. I believe these are enough to have a good grasp of using this <a href="https://www.ampheo.com/c/microcontrollers">microcontroller</a>.</p>
<p>The <a href="https://www.ampheo.com/product/stmicroelectronics-stm32f407vet6-6034"><em>STM32F407VE microcontroller</em></a> itself packs a punch. It has DSP and Ethernet support not to mention I2S capabilities, a 168-MHz max CPU frequency, 82 (!) GPIOs and USB (with OTG). More information about this microcontroller is in its <a href="https://www.st.com/resource/en/datasheet/stm32f407ve.pdf">datasheet</a>.</p>
<p>Not surprisingly, much of the steps in programming this STM32F407VET board is the same as that of the <a href="https://www.teachmemicro.com/getting-started-blue-pill-stm32cube/">STM32 Blue Pill</a>. We will be using the <a href="https://www.st.com/en/development-tools/stm32cubeide.html#get-software">STM32CubeIDE</a> for writing code and the <a href="https://www.st.com/en/development-tools/stsw-link004.html">ST-Link Utility</a> for uploading the program to the board.</p>
<h3><strong>Setting Up the Dev Environment</strong></h3>
<p>Install and open STM32CubeIDE. If it’s your first time opening it, you’ll need to specify the workspace directory. Tick on the checkbox below to make that workspace default and prevent this window from appearing again.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-workspace.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-4997" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-workspace.png" alt="Select workspace directory" width="617" height="277" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-workspace.png 617w, https://www.teachmemicro.com/wp-content/uploads/2021/03/select-workspace-300x135.png 300w" sizes="auto, (max-width: 617px) 100vw, 617px" /></a></p>
<p>Next, select your project type. We’ll want to start a new project for now so click on <em>Start new STM32 Project.</em></p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-project-type.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-4998" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-project-type.jpg" alt="Select STM32 Project Type" width="848" height="525" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-project-type.jpg 848w, https://www.teachmemicro.com/wp-content/uploads/2021/03/select-project-type-300x186.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/select-project-type-768x475.jpg 768w" sizes="auto, (max-width: 848px) 100vw, 848px" /></a></p>
<p>The next step is to select our target device. On the left panel, type in <em>STM32F407VE</em> in the <em>Part Number</em> field.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-stm32-device.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-4999" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-stm32-device.jpg" alt="Select STM32 device" width="860" height="535" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/select-stm32-device.jpg 860w, https://www.teachmemicro.com/wp-content/uploads/2021/03/select-stm32-device-300x187.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/select-stm32-device-768x478.jpg 768w" sizes="auto, (max-width: 860px) 100vw, 860px" /></a></p>
<p>A table will appear on the right panel (see above image). Click on that and then click the <em>Next &gt;</em> button.</p>
<p>Next, give your project a name. For our example, we’ll name it <em>F4_Blinky</em>.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-project-name.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5000" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-project-name.jpg" alt="Name your project" width="478" height="533" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-project-name.jpg 478w, https://www.teachmemicro.com/wp-content/uploads/2021/03/give-project-name-269x300.jpg 269w" sizes="auto, (max-width: 478px) 100vw, 478px" /></a></p>
<p>A pop-up window will appear. Click yes to add <em>Associated Perspective</em> to the workspace.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/associated-perspective-popup.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5001" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/associated-perspective-popup.jpg" alt="Just click Yes!" width="515" height="161" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/associated-perspective-popup.jpg 515w, https://www.teachmemicro.com/wp-content/uploads/2021/03/associated-perspective-popup-300x94.jpg 300w" sizes="auto, (max-width: 515px) 100vw, 515px" /></a></p>
<p>At this point, STM32CubeIDE will start downloading the software packages for the STM32F407VE microcontroller.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/downloading-required-files.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5002" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/downloading-required-files.jpg" alt="Downloading required files" width="391" height="215" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/downloading-required-files.jpg 391w, https://www.teachmemicro.com/wp-content/uploads/2021/03/downloading-required-files-300x165.jpg 300w" sizes="auto, (max-width: 391px) 100vw, 391px" /></a></p>
<p>Once downloads are finished, you will now be in the <em>Device Configuration Tool</em>. This is a handy tool for configuring the functions of each of the microcontroller’s pins. The tool also allows you to configure the clock for the device, among other features. For more information about this tool, read this <a href="https://www.st.com/resource/en/data_brief/stm32cubemx.pdf">specification sheet</a>.</p>
<p>We want to write a code that flashes the two user LEDs.</p>
<p id="nUzSkKF"><img loading="lazy" decoding="async" class="size-full wp-image-5757 aligncenter" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/stm32f407vet6_left02.png" alt="" width="944" height="756" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/stm32f407vet6_left02.png 944w, https://www.teachmemicro.com/wp-content/uploads/2021/03/stm32f407vet6_left02-300x240.png 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/stm32f407vet6_left02-768x615.png 768w" sizes="auto, (max-width: 944px) 100vw, 944px" /></p>
<p>The pinout diagram above shows that the user LEDs <em>LED1</em> and <em>LED2</em> (D2 and D3 in the board) connect to GPIOs <em>PA6</em> and <em>PA7</em>. Hence in the Device Configuration Tool, we click on both pins and change their function to <em>GPIO_OUTPUT</em>.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/configure-gpio-pins.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5003" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/configure-gpio-pins.jpg" alt="Device configuration tool" width="997" height="634" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/configure-gpio-pins.jpg 997w, https://www.teachmemicro.com/wp-content/uploads/2021/03/configure-gpio-pins-300x191.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/configure-gpio-pins-768x488.jpg 768w" sizes="auto, (max-width: 997px) 100vw, 997px" /></a></p>
<p>Next, we click on <em>System Core</em>, then <em>GPIO</em> to reveal the <em>GPIO Mode and Configuration</em> panel. Here we can give a label to pins PA6 and PA7.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5005" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins.jpg" alt="Give labels to pins PA6 and PA7" width="997" height="637" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins.jpg 997w, https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-300x192.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-768x491.jpg 768w" sizes="auto, (max-width: 997px) 100vw, 997px" /></a></p>
<p>Here I gave the pins the appropriate labels:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-2.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5006" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-2.jpg" alt="PA6 and PA7 are now LED1 and LED2" width="642" height="310" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-2.jpg 642w, https://www.teachmemicro.com/wp-content/uploads/2021/03/give-label-pins-2-300x145.jpg 300w" sizes="auto, (max-width: 642px) 100vw, 642px" /></a></p>
<p>Clicking <em>File &gt; Save</em> or pressing <em>CTRL+S</em> will trigger another popup window:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/generate-code.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5007" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/generate-code.jpg" alt="Generate code" width="514" height="158" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/generate-code.jpg 514w, https://www.teachmemicro.com/wp-content/uploads/2021/03/generate-code-300x92.jpg 300w" sizes="auto, (max-width: 514px) 100vw, 514px" /></a></p>
<p>Click Yes and a code with all the configurations will be created automatically!</p>
<p>After that, another window will popup. Clicking Yes to this will add an outline view of your project in the left panel:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/just-click-yes.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5008" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/just-click-yes.jpg" alt="Just click Yes again!" width="514" height="160" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/just-click-yes.jpg 514w, https://www.teachmemicro.com/wp-content/uploads/2021/03/just-click-yes-300x93.jpg 300w" sizes="auto, (max-width: 514px) 100vw, 514px" /></a></p>
<p>Our IDE is now open and we're ready to code!</p>
<h3><strong>Writing Code for the STM32F407VE</strong></h3>
<p>At this point, we will edit <em>main.c</em> to be able to flash both LED1 and LED2.</p>
<p>Inside the <em>main()</em> function, look for an empty <em>while(1)</em> loop. Whatever is inside this loop is run indefinitely by the microcontroller. Thus, we insert a few lines of code for setting or clearing the user LEDs pin. Here’s how it looks:</p>
<pre class="lang:arduino decode:true"><pre><code class="language-cpp">while (1)
{
/* USER CODE END WHILE */
  HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_RESET);
  HAL_GPIO_WritePin(GPIOA, LED2_Pin, GPIO_PIN_SET);
  delay(500000);
  HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_SET);
  HAL_GPIO_WritePin(GPIOA, LED2_Pin, GPIO_PIN_RESET);
  delay(500000);
/* USER CODE BEGIN 3 */
}</code></pre></pre>
<p><em>HAL_GPIO_WritePin()</em> is a built-in function for making a pin HIGH or LOW. It accepts three parameters, the first being the pin's PORT, the second is the pin's name and the third parameter is the pin's state. To make a pin LOW, the state is <em>GPIO_PIN_RESET</em>. Otherwise, the state is <em>GPIO_PIN_SET</em>.</p>
<p>Just like in Arduino or PIC, we'll need to configure the LED pins to be output pins. Fortunately, this is already done through the Device Configuration Tool. You'll see the configurations inside the <em>MX_GPIO_Init()</em> function.</p>
<p>Notice that we are using a <em>delay()</em> function. This is in fact not a built-in function and thus must be also added to the main.c file. Find the <em>MX_GPIO_Init()</em> function and after the closing bracket of that function, insert the following:</p>
<pre class="lang:arduino decode:true"><pre><code class="language-cpp">/* USER CODE BEGIN 4 */

void delay (int a)
{
  volatile int i,j;
  for (i=0 ; i &lt; a ; i++) 
  { 
    j++; 
  } 
  return; 
} 

/* USER CODE END 4 */</code></pre></pre>
<p>This is a function that keeps the microcontroller busy. It uses a <em>for-loop</em> that counts up to the passed parameter (in our code, that value is 500000).</p>
<p>We also need to add a prototype of this function right with the other prototypes. The following lines should be right before the main function:</p>
<pre class="lang:arduino decode:true"><pre><code class="language-cpp">void SystemClock_Config(void);
static void MX_GPIO_Init(void);
void delay (int a);</code></pre></pre>
<p>Our code is ready for compilation! Click <em>Project &gt; Build All</em>.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/build-all.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5010" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/build-all.jpg" alt="Build the code" width="754" height="448" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/build-all.jpg 754w, https://www.teachmemicro.com/wp-content/uploads/2021/03/build-all-300x178.jpg 300w" sizes="auto, (max-width: 754px) 100vw, 754px" /></a></p>
<p>&nbsp;</p>
<p>If there are no errors, this should be the output on the console at the bottom:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/no-errors.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5009" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/no-errors.jpg" alt="Look Ma, no errors" width="584" height="126" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/no-errors.jpg 584w, https://www.teachmemicro.com/wp-content/uploads/2021/03/no-errors-300x65.jpg 300w" sizes="auto, (max-width: 584px) 100vw, 584px" /></a></p>
<h3><strong>Uploading Code</strong></h3>
<p>A binary file is now inside the folder <em>&lt;Project-Name&gt;/Debug/</em>. Next, it’s time to load this binary file to the microcontroller.</p>
<p>I am using an <a href="https://ph.banggood.com/custlink/3mvhZuHw8c"><em>ST-Link V2 USB</em> dongle</a> with its SWD pins wired to the SWD pins of the STM32F407VE board.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-scaled.jpg"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-5011" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-1024x605.jpg" alt="STM32F407VE JTAG to ST-Link SWD" width="618" height="365" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-1024x605.jpg 1024w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-300x177.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-768x454.jpg 768w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-1536x908.jpg 1536w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32407VE-jtag-to-st-link-swd-2048x1211.jpg 2048w" sizes="auto, (max-width: 618px) 100vw, 618px" /></a></p>
<p>Also, before programming, make sure the BT0 pin is connected to ground and BT1 is wired to 3.3V. Here I am using jumpers to do just that.<a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-scaled.jpg"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-5012" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-768x1024.jpg" alt="Boot select" width="618" height="824" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-768x1024.jpg 768w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-225x300.jpg 225w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-1152x1536.jpg 1152w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-1536x2048.jpg 1536w, https://www.teachmemicro.com/wp-content/uploads/2021/03/STM32F407VE-boot-select-scaled.jpg 1920w" sizes="auto, (max-width: 618px) 100vw, 618px" /></a></p>
<p>In ST-Link Utility, click <em>Target &gt; Connect</em>.</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-utility.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5013" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-utility.jpg" alt="Connect using ST-Link Utility" width="810" height="592" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-utility.jpg 810w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-utility-300x219.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-utility-768x561.jpg 768w" sizes="auto, (max-width: 810px) 100vw, 810px" /></a><br />
If the data in the memory of the microcontroller is now visible, then the connection is successful:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-device-detected.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5014" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-device-detected.jpg" alt="" width="812" height="593" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-device-detected.jpg 812w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-device-detected-300x219.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-device-detected-768x561.jpg 768w" sizes="auto, (max-width: 812px) 100vw, 812px" /></a></p>
<p>Next, click <em>File &gt; Open</em> to load the binary file. After that, click <em>Target &gt; Program.</em></p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-program-device.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5015" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-program-device.jpg" alt="" width="815" height="598" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-program-device.jpg 815w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-program-device-300x220.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/st-link-program-device-768x564.jpg 768w" sizes="auto, (max-width: 815px) 100vw, 815px" /></a></p>
<p>Finally, click <em>Start</em> to initiate the binary transfer:</p>
<p><a href="https://www.teachmemicro.com/wp-content/uploads/2021/03/start-programming.jpg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-5016" src="https://www.teachmemicro.com/wp-content/uploads/2021/03/start-programming.jpg" alt="" width="457" height="316" srcset="https://www.teachmemicro.com/wp-content/uploads/2021/03/start-programming.jpg 457w, https://www.teachmemicro.com/wp-content/uploads/2021/03/start-programming-300x207.jpg 300w, https://www.teachmemicro.com/wp-content/uploads/2021/03/start-programming-110x75.jpg 110w" sizes="auto, (max-width: 457px) 100vw, 457px" /></a></p>
<p>If nothing went wrong, the STM32F407VE board now has a new program!</p>
<p>The LEDs LED1 (D2) and LED2 (D3) should now be flashing alternately.</p>
<p>Have any questions about this tutorial? Feel free to drop a comment below.</p>
<p>The post <a href="https://www.teachmemicro.com/programming-stm32f407ve-black-board/">Programming the STM32F407VE Black Board</a> appeared first on <a href="https://www.teachmemicro.com">Microcontroller Tutorials</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.teachmemicro.com/programming-stm32f407ve-black-board/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: www.teachmemicro.com @ 2026-08-08 07:23:59 by W3 Total Cache
-->