{"id":4641,"date":"2026-08-06T12:42:50","date_gmt":"2026-08-06T12:42:50","guid":{"rendered":"https:\/\/blog.embeddedexpert.io\/?p=4641"},"modified":"2026-08-06T12:42:53","modified_gmt":"2026-08-06T12:42:53","slug":"emulating-uart-part-2-sending-data","status":"publish","type":"post","link":"https:\/\/blog.embeddedexpert.io\/?p=4641","title":{"rendered":"Emulating UART Part 2: Sending Data"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-1024x683.png\" alt=\"\" class=\"wp-image-4643\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-1024x683.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-300x200.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-768x512.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-1150x767.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-750x500.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-400x267.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM-250x167.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/ChatGPT-Image-Aug-6-2026-at-02_49_23-PM.png 1535w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>With the timer and GPIO successfully configured, we will now shift our focus to actively transmitting data. By leveraging the TIM3 interrupt, the microcontroller will autonomously shift bits out to the TX pin, ensuring precise baud rate generation without stalling the main loop.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>In this guide, we shall cover the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Firmware Development.<\/li>\n\n\n\n<li>Results.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. Firmware Development:<\/h2>\n\n\n\n<p>We start by creating new header and source file with name of soft_uart.h and soft_uart.c respectively.<\/p>\n\n\n\n<p>To create the header file, right click on inc folder, and select new, header file as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"588\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-1024x588.jpg\" alt=\"\" class=\"wp-image-4644\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-1024x588.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-300x172.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-768x441.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-1536x882.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-2048x1176.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-1150x661.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-750x431.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-400x230.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-25-24-250x144.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Give it a name soft_uart.h and click on Finish.<\/p>\n\n\n\n<p>To create new source file, right click on src folder, new and Source file as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"588\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-1024x588.jpg\" alt=\"\" class=\"wp-image-4645\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-1024x588.jpg 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-300x172.jpg 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-768x441.jpg 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-1536x882.jpg 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-2048x1176.jpg 2048w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-1150x661.jpg 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-750x431.jpg 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-400x230.jpg 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/2026-08-01_09-26-53-250x144.jpg 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Give it soft_uart.c as a name and click on Finish.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, open the soft_uart.h header file.<\/p>\n\n\n\n<p>We start by including the following header files:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#include &quot;stm32f4xx_hal.h&quot;\n#include &lt;stdint.h&gt;\n#include &lt;stdbool.h&gt;\n#include &quot;tim.h&quot;<\/pre><\/div>\n\n\n\n<p>We include the main stm32g4xx_hal header file along side tim.h for the timer.<\/p>\n\n\n\n<p>We also include stdint.h and stdbool to handle uint and bool respectively.<\/p>\n\n\n\n<p>Next, define the baudrate the timer frequency:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define SOFT_UART_BAUDRATE         115200U\n#define SOFT_UART_TIM_CLOCK_HZ     100000000UL<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Calculate the ticks per bit for the UART:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define SOFT_UART_TICKS_PER_BIT   ((SOFT_UART_TIM_CLOCK_HZ + (SOFT_UART_BAUDRATE \/ 2U)) \\\n                                   \/ SOFT_UART_BAUDRATE)<\/pre><\/div>\n\n\n\n<p>This can be changed later in case you need like 9600bps.<\/p>\n\n\n\n<p>Next, define the TX buffer size (depending on your application):<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#define SOFT_UART_TX_BUF_SIZE      128U<\/pre><\/div>\n\n\n\n<p>Next define the following functions:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void  SoftUART_Init(void);<\/pre><\/div>\n\n\n\n<p>This function shall initialize the soft UART.<\/p>\n\n\n\n<p>Next, <\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void  SoftUART_SendByte(uint8_t data);<\/pre><\/div>\n\n\n\n<p>This will allow you to send single byte over soft uart.<\/p>\n\n\n\n<p>Next,<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void  SoftUART_SendBuffer(const uint8_t *data, uint32_t len);<\/pre><\/div>\n\n\n\n<p>This will send a buffer.<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void  SoftUART_SendString(const char *str);<\/pre><\/div>\n\n\n\n<p>This will send a string. <\/p>\n\n\n\n<p>Finally,<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">bool  SoftUART_IsBusy(void);<\/pre><\/div>\n\n\n\n<p>This will be used to inform your firmware that the UART finished transmitting all the bytes.<\/p>\n\n\n\n<p>Hence, the header file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">\/* soft_uart.h \u2014 Software UART via TIM3 Interrupt *\/\n#ifndef SOFT_UART_H\n#define SOFT_UART_H\n\n#include &quot;stm32f4xx_hal.h&quot;\n#include &lt;stdint.h&gt;\n#include &lt;stdbool.h&gt;\n#include &quot;tim.h&quot;\n\/* ===================== User Configuration ===================== *\/\n\n#define SOFT_UART_BAUDRATE         115200U\n#define SOFT_UART_TIM_CLOCK_HZ     100000000UL\n\n\/* Timer ticks per UART bit *\/\n#define SOFT_UART_TICKS_PER_BIT   ((SOFT_UART_TIM_CLOCK_HZ + (SOFT_UART_BAUDRATE \/ 2U)) \\\n                                   \/ SOFT_UART_BAUDRATE)\n\n\/* TX Buffer Size *\/\n#define SOFT_UART_TX_BUF_SIZE      128U\n\n\/* ===================== API ===================== *\/\n\nvoid  SoftUART_Init(void);\nvoid  SoftUART_SendByte(uint8_t data);\nvoid  SoftUART_SendBuffer(const uint8_t *data, uint32_t len);\nvoid  SoftUART_SendString(const char *str);\nbool  SoftUART_IsBusy(void);\n\n#endif \/* SOFT_UART_H *\/<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, open soft_uart.c source file.<\/p>\n\n\n\n<p>We start by including the following header files:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#include &quot;soft_uart.h&quot;\n#include &quot;main.h&quot;<\/pre><\/div>\n\n\n\n<p>Next, declare the following private variables:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">static volatile uint8_t  tx_buffer[SOFT_UART_TX_BUF_SIZE];\nstatic volatile uint16_t tx_head = 0;  \/* Where new data is added *\/\nstatic volatile uint16_t tx_tail = 0;  \/* What the ISR is currently sending *\/\n\nstatic volatile uint8_t  tx_shift_reg = 0;\nstatic volatile uint8_t  tx_bit_count = 0;\nstatic volatile bool     tx_busy = false;<\/pre><\/div>\n\n\n\n<p>Next, for UART initialization, simply, set the TX pin to high as follows:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void SoftUART_Init(void)\n{\n    \/* Ensure PA6 is High (Idle state) *\/\n    HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n}<\/pre><\/div>\n\n\n\n<p>Next, send single byte:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void SoftUART_SendByte(uint8_t data)\n{\n    \/* Calculate next head index *\/\n    uint16_t next_head = (tx_head + 1U) % SOFT_UART_TX_BUF_SIZE;\n\n    \/* Wait if buffer is full *\/\n    while (next_head == tx_tail) { __WFE(); }\n\n    \/* Disable interrupt temporarily to safely update the buffer *\/\n    __disable_irq();\n    tx_buffer[tx_head] = data;\n    tx_head = next_head;\n    __enable_irq();\n\n    \/* Start the timer if it's not already running *\/\n    if (!tx_busy)\n    {\n        tx_busy = true;\n        htim3.Instance-&gt;CNT = 0;\n        HAL_TIM_Base_Start_IT(&amp;htim3);\n    }\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Send buffer and string:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void SoftUART_SendBuffer(const uint8_t *data, uint32_t len)\n{\n    for (uint32_t i = 0; i &lt; len; i++)\n    {\n        SoftUART_SendByte(data[i]);\n    }\n}\n\nvoid SoftUART_SendString(const char *str)\n{\n    while (*str)\n    {\n        SoftUART_SendByte((uint8_t)*str++);\n    }\n}\n<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, soft iart is busy function:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">bool SoftUART_IsBusy(void)\n{\n    return tx_busy;\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, timer 3 interrupt handler. This is where the magic happens:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">void TIM3_IRQHandler(void)\n{\n    \/* Clear the update interrupt flag manually (bypass HAL overhead) *\/\n    TIM3-&gt;SR = ~TIM_SR_UIF;\n\n    if (tx_bit_count == 0)\n    {\n        \/* Load next byte from the ring buffer *\/\n        if (tx_head != tx_tail)\n        {\n            tx_shift_reg = tx_buffer[tx_tail];\n            tx_tail = (tx_tail + 1U) % SOFT_UART_TX_BUF_SIZE;\n\n            \/* Send Start Bit (Low) *\/\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_RESET);\n\n            tx_bit_count = 1; \/* Start processing data bits on next interrupt *\/\n        }\n        else\n        {\n            \/* Buffer empty: Stop the timer *\/\n            HAL_TIM_Base_Stop_IT(&amp;htim3);\n            tx_busy = false;\n        }\n    }\n    else if (tx_bit_count &lt;= 8)\n    {\n        \/* Send Data Bits (LSB first) *\/\n        if (tx_shift_reg &amp; 0x01U)\n        {\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n        }\n        else\n        {\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_RESET);\n        }\n\n        tx_shift_reg &gt;&gt;= 1;\n        tx_bit_count++;\n    }\n    else\n    {\n        \/* Send Stop Bit (High) *\/\n        HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n        tx_bit_count = 0; \/* Reset for next byte *\/\n    }\n}<\/pre><\/div>\n\n\n\n<p>Flow chart to better understanding:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"559\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-1024x559.png\" alt=\"\" class=\"wp-image-4646\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-1024x559.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-300x164.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-768x419.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-1150x627.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-750x409.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-400x218.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe-250x136.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/Gemini_Generated_Image_rdbevsrdbevsrdbe.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>Hence, the source file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">\/* soft_uart.c \u2014 Software UART via TIM3 Interrupt *\/\n\n#include &quot;soft_uart.h&quot;\n#include &quot;main.h&quot;\n\n\/* ===================== Private Variables ===================== *\/\n\nstatic volatile uint8_t  tx_buffer[SOFT_UART_TX_BUF_SIZE];\nstatic volatile uint16_t tx_head = 0;  \/* Where new data is added *\/\nstatic volatile uint16_t tx_tail = 0;  \/* What the ISR is currently sending *\/\n\nstatic volatile uint8_t  tx_shift_reg = 0;\nstatic volatile uint8_t  tx_bit_count = 0;\nstatic volatile bool     tx_busy = false;\n\n\/* ===================== Initialization ===================== *\/\n\nvoid SoftUART_Init(void)\n{\n    \/* Ensure PA6 is High (Idle state) *\/\n    HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n}\n\n\/* ===================== Core TX Logic ===================== *\/\n\nvoid SoftUART_SendByte(uint8_t data)\n{\n    \/* Calculate next head index *\/\n    uint16_t next_head = (tx_head + 1U) % SOFT_UART_TX_BUF_SIZE;\n\n    \/* Wait if buffer is full *\/\n    while (next_head == tx_tail) { __WFE(); }\n\n    \/* Disable interrupt temporarily to safely update the buffer *\/\n    __disable_irq();\n    tx_buffer[tx_head] = data;\n    tx_head = next_head;\n    __enable_irq();\n\n    \/* Start the timer if it's not already running *\/\n    if (!tx_busy)\n    {\n        tx_busy = true;\n        htim3.Instance-&gt;CNT = 0;\n        HAL_TIM_Base_Start_IT(&amp;htim3);\n    }\n}\n\nvoid SoftUART_SendBuffer(const uint8_t *data, uint32_t len)\n{\n    for (uint32_t i = 0; i &lt; len; i++)\n    {\n        SoftUART_SendByte(data[i]);\n    }\n}\n\nvoid SoftUART_SendString(const char *str)\n{\n    while (*str)\n    {\n        SoftUART_SendByte((uint8_t)*str++);\n    }\n}\n\nbool SoftUART_IsBusy(void)\n{\n    return tx_busy;\n}\n\n\/* ===================== Interrupt Handler ===================== *\/\n\n\/* Put this function in your stm32f4xx_it.c file,\n   or keep it here if you are not using CubeMX's IT file.\n   It MUST be named exactly TIM3_IRQHandler. *\/\n\nvoid TIM3_IRQHandler(void)\n{\n    \/* Clear the update interrupt flag manually (bypass HAL overhead) *\/\n    TIM3-&gt;SR = ~TIM_SR_UIF;\n\n    if (tx_bit_count == 0)\n    {\n        \/* Load next byte from the ring buffer *\/\n        if (tx_head != tx_tail)\n        {\n            tx_shift_reg = tx_buffer[tx_tail];\n            tx_tail = (tx_tail + 1U) % SOFT_UART_TX_BUF_SIZE;\n\n            \/* Send Start Bit (Low) *\/\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_RESET);\n\n            tx_bit_count = 1; \/* Start processing data bits on next interrupt *\/\n        }\n        else\n        {\n            \/* Buffer empty: Stop the timer *\/\n            HAL_TIM_Base_Stop_IT(&amp;htim3);\n            tx_busy = false;\n        }\n    }\n    else if (tx_bit_count &lt;= 8)\n    {\n        \/* Send Data Bits (LSB first) *\/\n        if (tx_shift_reg &amp; 0x01U)\n        {\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n        }\n        else\n        {\n            HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_RESET);\n        }\n\n        tx_shift_reg &gt;&gt;= 1;\n        tx_bit_count++;\n    }\n    else\n    {\n        \/* Send Stop Bit (High) *\/\n        HAL_GPIO_WritePin(soft_tx_GPIO_Port, soft_tx_Pin, GPIO_PIN_SET);\n        tx_bit_count = 0; \/* Reset for next byte *\/\n    }\n}<\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, open main.c source file.<\/p>\n\n\n\n<p>We start by including the following header file:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">#include &quot;soft_uart.h&quot;\n\n#include &quot;stdio.h&quot;<\/pre><\/div>\n\n\n\n<p>In user code begin PV (Private variable), declare the following variables:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint8_t buffer[SOFT_UART_TX_BUF_SIZE]={0};\nuint8_t counter=0;<\/pre><\/div>\n\n\n\n<p>In user code begin 2 in main function, initialize the software UART:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">SoftUART_Init();<\/pre><\/div>\n\n\n\n<p>In user code begin 3 in while 1 loop:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;clike&quot;,&quot;mime&quot;:&quot;text\/x-csrc&quot;,&quot;theme&quot;:&quot;dracula&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;C&quot;,&quot;language&quot;:&quot;C&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;c&quot;}\">uint16_t len=snprintf(buffer,SOFT_UART_TX_BUF_SIZE, &quot;counter value = %d \\r\\n&quot;, counter ++);\n\n\nSoftUART_SendBuffer(buffer,len);\nwhile(SoftUART_IsBusy());\n\n\nHAL_Delay(10);<\/pre><\/div>\n\n\n\n<p>We construct a buffer with snprintf and increment the counter.<\/p>\n\n\n\n<p>Send the buffer each 10ms.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Thats all for the guide.<\/p>\n\n\n\n<p>Save, build the project and run it as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"34\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png\" alt=\"\" class=\"wp-image-4349\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1024x34.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-300x10.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-768x26.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1536x51.png 1536w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-1150x38.png 1150w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-750x25.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-400x13.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1-250x8.png 250w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/04\/image-1.png 1986w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<p>You may download the project from <a href=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/Software_UART\" data-type=\"link\" data-id=\"https:\/\/github.com\/hussamaldean\/Embedded-Expert-Post-Projects\/tree\/main\/Projects\/Software_UART\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">2. Results:<\/h2>\n\n\n\n<p>By probing PA6, you should get the following:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"630\" src=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21.png\" alt=\"\" class=\"wp-image-4647\" srcset=\"https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21.png 1024w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21-300x185.png 300w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21-768x473.png 768w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21-750x461.png 750w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21-400x246.png 400w, https:\/\/blog.embeddedexpert.io\/wp-content\/uploads\/2026\/08\/RigolDS21-250x154.png 250w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>We have successfully transmit data using our software UART.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Next part, we shall start receiving data.<\/p>\n\n\n\n<p>Stay tuned.<\/p>\n\n\n\n<p>Happy coding \ud83d\ude09 <\/p>\n","protected":false},"excerpt":{"rendered":"<p>With the timer and GPIO successfully configured, we will now shift our focus to actively transmitting data. By leveraging the TIM3 interrupt, the microcontroller will autonomously shift bits out to the TX pin, ensuring precise baud rate generation without stalling the main loop. In this guide, we shall cover the following: 1. Firmware Development: We [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,11,12],"tags":[],"class_list":["post-4641","post","type-post","status-publish","format-standard","hentry","category-embedded-systems","category-peripheral-drivers","category-stm32"],"_links":{"self":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4641"}],"collection":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=4641"}],"version-history":[{"count":1,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4641\/revisions"}],"predecessor-version":[{"id":4648,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=\/wp\/v2\/posts\/4641\/revisions\/4648"}],"wp:attachment":[{"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4641"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4641"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.embeddedexpert.io\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4641"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}