0% found this document useful (0 votes)
11 views25 pages

Firmware Coding: IDE & Project Setup

Firmware development using STM32 boards.

Uploaded by

Saswat Jyoti
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views25 pages

Firmware Coding: IDE & Project Setup

Firmware development using STM32 boards.

Uploaded by

Saswat Jyoti
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Firmware Coding

By Shimi Cohen

Chapter 2: IDE & Project Setup


FW CODING – CHAPTER 2

PROLOGUE A:COMMON ACTIONS


CONCEPT DESCRIPTION CODE EXAMPLE
function A reusable block of code that performs a specific task. int add(int a, int b)
Can accept parameters and return values. {
→ Must be declared before use return a + b;
}
→ Can have zero or more parameters
→ Should have a single, clear purpose //Call add function
→ Return type must match declared type int x = add(5, 7);

if / else Conditional logic that executes different code blocks based on if (temp > 100)
whether a condition is true or false. {
→ Condition must evaluate to true or false cool_down();
→ Else clause is optional
}
else
→ Can chain multiple conditions with else if
{
→ Use braces for code blocks with multiple statements keep_heating();
}
while loop Repeats a block of code continuously as long as a specified while (sensor_value < 500)
condition remains true. {
→ Condition is checked before each iteration increase_power();
→ Must ensure condition eventually becomes false
}

→ Loop may not execute at all if condition is initially false


→ Break to exit early, continue to skip to next iteration

for loop Structured loop with initialization, condition, and for (int i = 0; i < 10; i++)
increment/decrement. Best for counting. {
→ Three parts: initialization, condition, increment blink_led();
→ Initialization runs once at start
}

→ Condition checked before each iteration


→ Increment runs after each iteration

switch/case Multi-branch conditional statement that compares a variable switch (command)


against multiple constant values. {
Rules: case 1:
→ Only works with integer, characters, and enums start_motor();
→ Each case must have a constant value break;
case 2:
→ Use break to prevent fall-through to next case
stop_motor();
→ Default case handles unmatched values break;
→ Cases can be grouped for same action default:
idle();
break;
}

typedef Creates an alias or alternative name for existing data types, making typedef struct
code more readable and maintainable. {
→ Can create aliases for any data type int id;
→ Convention: capitalize typedef names
float temp;
} Sensor;
→ Helps abstract complex type definitions
→ Improves code portability Sensor s1 = {1, 36.5};

2
FW CODING – CHAPTER 2

CONCEPT DESCRIPTION CODE EXAMPLE


pointers Variables that store memory addresses of other int val = 5;
variables. Enable indirect access and modification of int *p = &val;
data.
→ Declare with asterisk (*) before variable // changes val to 10
→ Use ampersand (&) to get var address *p = 10;
→ Initialized before use
→ Used for dynamic memory allocation

arrays Collections of elements of the same data type stored in int nums[5] = {1, 2, 3, 4, 5};
contiguous memory locations, accessed by index.
→ Size must be specified at declaration (in C) nums[2] = 2;
→ Index starts at 0 and goes to size-1
→ Bounds checking not automatic
→ Array name : address of first element

NVIC HW component that manages interrupt priorities and HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);


enables/disables interrupts in ARM Cortex-M
processors.
→ Lower priority : higher precedence
→ Must set priority before enabling interrupts
→ Sub-priority used for tie-breaking
→ Configure before enabling interrupt sources

interrupts Special functions that execute automatically when HAL_NVIC_EnableIRQ(EXTI0_IRQn);


hardware events occur, enabling real-time response to
external stimuli. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
→ Keep ISR functions short and fast {
→ Avoid blocking operations (delays, loops) if (GPIO_Pin == GPIO_PIN_0)
{
→ Enable after configuration is complete
handle_button();
→ Clear interrupt flags (repeated triggering) }
}
GPIO HW pins that can be configured as digital inputs or HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5,
outputs to interface with external devices and sensors.
→ Configure pin mode (in/out) before use GPIO_PIN_SET);
→ Set pull-up/down resistors for inputs
if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) ==
→ Check pin availability GPIO_PIN_SET)
→ Debounce switches and buttons in software {
do_something();
}
Timer HW peripheral that counts clock cycles to generate HAL_TIM_Base_Start_IT(&htim2);
precise timing events, interrupts, and PWM signals.
→ Configure prescaler for desired timing void
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDe
→ Start timer before use and stop when done
f *htim) { your_code(); }
→ Handle timer interrupts promptly
→ Multiple timers for different purposes

ADC HW peripheral that converts analog to digital values. HAL_ADC_Start(&hadc1);


Rules: HAL_ADC_PollForConversion(&hadc1, 100);
→ Configure resolution (8, 10, 12 bits) uint32_t val = HAL_ADC_GetValue(&hadc1);
→ Start conversion before reading value
→ Allow sufficient conversion time

3
FW CODING – CHAPTER 2

PROLOGUE B:BITWISE TRICKS


ACTION DESCRIPTION CODE EXAMPLE
Even/odd Ultra-fast method to determine if number is even or odd. // Check LSB
→ Uses LSB (least significant bit) check if (n & 1)
{
→ Even numbers always have LSB = 0 printf("odd");
→ Much faster than modulo operation }
else
{
printf("even");
}

Swap Exchange two variables without using temporary variable. // swap a and b
→ XOR operation cancels itself out a ^= b;
b ^= a;
→ Saves memory space a ^= b;
→ Works with any integer type

Power of 2 Instantly detect if number is a power of 2. bool PwrOf2 = (n > 0) &&


→ Powers of 2 have only one bit set
((n & (n - 1)) == 0);
→ n-1 flips all bits after the set bit
→ AND operation results in 0 for powers of 2

Toggle bit Flip specific bit without affecting others. // toggles bit at position pos
→ XOR with 1 flips the bit n ^= (1 << pos);
→ XOR with 0 keeps bit unchanged
→ Perfect for toggling flags/state

Count set Brian Kernighan's algorithm to count 1s in binary. int count = 0;


bits → Each iteration removes one set bit
while (n)
→ Loop runs only for number of set bits {
→ Much faster than checking each bit n &= n - 1;
count++;
}
Reverse bits Mirror/reverse all bits in a byte or integer. uint8_t result = 0;
→ Shifts bits from one end to the other
for (int i = 0; i < 8; i++)
→ Useful for endianness conversion {
→ Common in graphics and crypto result = (result << 1) | (n & 1);
n >>= 1;
}
Rightmost LSB Extract the rightmost set bit, zero out all others. // if n=12 (1100), result=4 (0100)
→ Two's complement property int rightmost = n & (-n);
→ Useful for tree/heap operations
→ Finds lowest set bit position

Fast Mul/Div Multiply or divide by powers of 2 using shifts. // multiply by 8, divide by 4


→ Left shift multiplies by 2^n int mul8 = n << 3;
int div4 = n >> 2;
→ Right shift divides by 2^n
→ Much faster than arithmetic operations

Set/clear Precisely control individual bits in registers. // set bit at pos


n |= (1 << pos);
bit → OR with 1 sets the bit
→ AND with 0 clears the bit // clear bit at pos
→ Essential for hardware control n &= ~(1 << pos);

Absolute Get absolute value without branches or conditionals. // works for 32-bit integers
int mask = n >> 31;
value → Uses sign bit propagation
→ Mask extracts sign information int abs = (n + mask) ^ mask;
→ Branchless = faster execution

4
FW CODING – CHAPTER 2

1:ENVIRONMENT SETUP

STM32CubeMX serves as the primary configuration tool for STM32 projects. This
graphical tool generates initialization code and manages project configurations.

Installation Steps:

1. Download STM32CubeMX from STMicroelectronics website


2. Extract installation package
3. Run installer with administrator privileges
4. Configure default workspace directory
5. Accept license agreements
6. Complete installation wizard

EXAMPLE:
STM32F429 Discovery KIT project with CubeMX

Consider buying a well packed Demo Kit with many features such as the STM32F4 DISCOVERY

5
FW CODING – CHAPTER 2

STM32CubeIDE provides comprehensive development environment with integrated


debugging capabilities.
INSTALLATION COMPONENTS:
▪ Eclipse-based IDE framework
▪ GNU ARM Embedded Toolchain
▪ GDB debugger integration
▪ STM32CubeProgrammer utility
▪ OpenOCD debugging interface

IDE CONFIGURATION:
▪ Workspace setup and organization
▪ Compiler optimization settings
▪ Debug configuration templates
▪ Plugin management system

EXAMPLE:
STM32F429 Open Project on CubeIDE ( CubeMX Generated Code )

6
FW CODING – CHAPTER 2

Pick a demo kit that suits your needs and start using HW to master your code expertise.

MODULAR
(3rd Party)
NUCLEO

DISCOVERY

EVAL Kit
7
FW CODING – CHAPTER 2

2:PROJECT CREATION

Project creation begins with device selection and basic configuration in STM32CubeMX.
DEVICE SELECTION PROCESS:
▪ Choose STM32 MCU family
▪ Select specific part number
▪ Verify package and pin count
▪ Check peripheral availability
▪ Confirm memory specifications

PROJECT CONFIGURATION:
▪ Set project name and location
▪ Select toolchain (STM32CubeIDE)
▪ Choose code generation options
▪ Configure project structure
▪ Set up version control integration

INITIAL CONFIGURATION STEPS:


▪ Launch STM32CubeMX application
▪ Create new project from device database
▪ Configure basic system settings
▪ Set up project metadata
▪ Generate initial code structure
▪ Import Code into CubeIDE

8
FW CODING – CHAPTER 2

Pin assignment determines hardware interface capabilities and system connectivity.


PIN CONFIGURATION CATEGORIES:
▪ System pins (reset, power, clock)
▪ GPIO pins (digital input/output)
▪ Peripheral pins (UART, SPI, I2C)
▪ Analog pins (ADC, DAC)
▪ Special function pins (debugging, boot)

CONFIGURATION PROCESS:
▪ Review pin conflicts and warnings
▪ Set electrical characteristics
▪ Configure pull-up/pull-down resistors
▪ Assign alternate functions
▪ Validate pin assignments

PIN ASSIGNMENT BEST PRACTICES:


▪ Group related signals together
▪ Consider PCB routing constraints
▪ Reserve pins for future expansion
▪ Follow manufacturer recommendations
▪ Document pin usage clearly

Pin Type Electrical Config Typical Usage


Digital Input Pull-up enabled Button, switch
Digital Output Push-pull, medium speed LED, relay control
Analog Input No pull resistor Sensor reading
Communication Alternate function UART, SPI, I2C

Name your GPIOs to label them in the generated code (click on pin → Enter User Label)

9
FW CODING – CHAPTER 2

3:CLOCK CONFIGURATION

Clock configuration determines system performance and power consumption


characteristics.
STM32F4 CLOCK SOURCES:
▪ High Speed Internal (HSI) - 16MHz
▪ High Speed External (HSE) - 4-25MHz
▪ Low Speed Internal (LSI) - 32kHz
▪ Low Speed External (LSE) - 32.768kHz
▪ Phase Locked Loop (PLL) - up to 180MHz

CLOCK DISTRIBUTION:
▪ AHB bus clock (system clock)
▪ APB1 peripheral clock (up to 45MHz)
▪ APB2 peripheral clock (up to 90MHz)
▪ USB clock (48MHz required)
▪ RTC clock (low power)

10
FW CODING – CHAPTER 2

Clock configuration directly impacts system performance and power consumption.


PERFORMANCE CONSIDERATIONS:
▪ Maximum CPU frequency selection
▪ Memory access wait states
▪ Bus bandwidth allocation
▪ Peripheral clock requirements
▪ Power consumption trade-offs

OPTIMIZATION STRATEGIES:
▪ Use external crystal for accuracy
▪ Configure PLL for maximum performance
▪ Enable only required peripheral clocks
▪ Optimize flash access timing
▪ Balance performance vs power

CLOCK CONFIGURATION EXAMPLE:


// System clock configuration for 168MHz
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

// Configure HSE oscillator


RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.[Link] = RCC_PLL_ON;
RCC_OscInitStruct.[Link] = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.[Link] = 8; // 8MHz/8 = 1MHz
RCC_OscInitStruct.[Link] = 336; // 1MHz*336 = 336MHz
RCC_OscInitStruct.[Link] = RCC_PLLP_DIV2; // 336MHz/2 = 168MHz
RCC_OscInitStruct.[Link] = 7; // 336MHz/7 = 48MHz (USB)

HAL_RCC_OscConfig(&RCC_OscInitStruct);

// Configure system clocks


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; // 168MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; // 42MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; // 84MHz

HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}

For High-Speed applications (>64MHz) – HSE is a safer choice due to HSI inaccuracy and jitter.

11
FW CODING – CHAPTER 2

4:PERIPHERAL CONFIGURATION

GPIO configuration establishes digital interface capabilities for external components.


GPIO MODES:
▪ Input mode (floating, pull-up, pull-down)
▪ Output mode (push-pull, open-drain)
▪ Alternate function mode
▪ Analog mode

ELECTRICAL CHARACTERISTICS:
▪ Output drive strength (2mA, 8mA, 12mA)
▪ Slew rate control (low, medium, high)
▪ Input threshold levels
▪ ESD protection features

CONFIGURATION STRUCTURE:
// GPIO configuration structure
typedef struct
{
uint32_t Pin; // Pin selection
uint32_t Mode; // Input/Output mode
uint32_t Pull; // Pull-up/Pull-down
uint32_t Speed; // Output speed
uint32_t Alternate; // Alternate function

} GPIO_InitTypeDef;

GPIOs during Boot : input – Consider Pull-Ups for signals that must be 'High' during boot (P.S enable,
etc.)

12
FW CODING – CHAPTER 2

Timer configuration enables precise timing control and PWM generation.


TIMER TYPES:
▪ Basic timers (TIM6, TIM7)
▪ General-purpose timers (TIM2-TIM5)
▪ Advanced timers (TIM1, TIM8)

CONFIGURATION PARAMETERS:
▪ Prescaler value (clock division)
▪ Period/Auto-reload value
▪ Counter mode (up, down, center-aligned)
▪ Clock division factor
▪ Repetition counter

TIMER CALCULATION:
Timer_Frequency = Input_Clock / ((Prescaler + 1) * (Period + 1))
PWM_Frequency = Timer_Frequency
Duty_Cycle = (Compare_Value / Period) * 100%
PWM CONFIGURATION EXAMPLE:
// Timer configuration for PWM generation
void Timer_PWM_Init(void)
{
TIM_HandleTypeDef htim3;
TIM_OC_InitTypeDef sConfigOC = {0};

// Timer base configuration


[Link] = TIM3;
[Link] = 0;
[Link] = TIM_COUNTERMODE_UP;
[Link] = 8399; // 10kHz PWM with 84MHz clock
[Link] = TIM_CLOCKDIVISION_DIV1;

HAL_TIM_PWM_Init(&htim3);

// PWM channel configuration


[Link] = TIM_OCMODE_PWM1;
[Link] = 4200; // 50% duty cycle
[Link] = TIM_OCPOLARITY_HIGH;
[Link] = TIM_OCFAST_DISABLE;

HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);


HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}

Well commented code allows others to review or take-over

13
FW CODING – CHAPTER 2

5:COMMUNICATION SETUP

UART provides asynchronous serial communication for debugging and data transfer.
UART PARAMETERS:
▪ Baud rate (9600, 115200, 921600)
▪ Data bits (7, 8, 9)
▪ Stop bits (1, 2)
▪ Parity (none, even, odd)
▪ Flow control (none, RTS/CTS)

UART USAGE PATTERNS:


▪ Polling mode for simple applications
▪ Interrupt mode for responsive systems
▪ DMA mode for high-throughput applications
▪ Circular buffer for continuous data streams

CONFIGURATION STRUCTURE:
// UART configuration example
UART_HandleTypeDef huart2;

void UART_Init(void)
{
[Link] = USART2;
[Link] = 115200;
[Link] = UART_WORDLENGTH_8B;
[Link] = UART_STOPBITS_1;
[Link] = UART_PARITY_NONE;
[Link] = UART_MODE_TX_RX;
[Link] = UART_HWCONTROL_NONE;
[Link] = UART_OVERSAMPLING_16;

HAL_UART_Init(&huart2);
}

Swapped TX/RX is a very common problem in UART – keep it in mind when debugging your COMM.

14
FW CODING – CHAPTER 2

SPI enables high-speed synchronous communication with peripheral devices.


SPI PARAMETERS:
▪ Clock frequency (up to 42MHz)
▪ Clock polarity (CPOL)
▪ Clock phase (CPHA)
▪ Data size (8-bit, 16-bit)
▪ Bit order (MSB, LSB first)

SPI MODES:
▪ Mode 0: CPOL=0, CPHA=0
▪ Mode 1: CPOL=0, CPHA=1
▪ Mode 2: CPOL=1, CPHA=0
▪ Mode 3: CPOL=1, CPHA=1

CONFIGURATION EXAMPLE:
// SPI configuration for sensor communication
SPI_HandleTypeDef hspi1;

void SPI_Init(void)
{
[Link] = SPI1;
[Link] = SPI_MODE_MASTER;
[Link] = SPI_DIRECTION_2LINES;
[Link] = SPI_DATASIZE_8BIT;
[Link] = SPI_POLARITY_LOW;
[Link] = SPI_PHASE_1EDGE;
[Link] = SPI_NSS_SOFT;
[Link] = SPI_BAUDRATEPRESCALER_16;
[Link] = SPI_FIRSTBIT_MSB;
[Link] = SPI_TIMODE_DISABLE;
[Link] = SPI_CRCCALCULATION_DISABLE;

HAL_SPI_Init(&hspi1);
}

Always check your communication buses using scope to avoid marginal performance

15
FW CODING – CHAPTER 2

6:CODE GENERATION

Code generation is done via CubeMX after MCU configuration


GENERATED CODE STRUCTURE:
▪ main.c (application entry point)
▪ System initialization files
▪ Peripheral initialization functions
▪ HAL library integration
▪ Startup assembly code
▪ Linker script configuration
CODE GENERATION OPTIONS:
▪ Generate peripheral initialization as separate .c/.h files
▪ Keep user code during regeneration
▪ Copy all library files to project
▪ Generate only necessary files
▪ Create backup of user sections

USER CODE SECTIONS:


int main(void)
{
/* USER CODE BEGIN 1 */
// User code before initialization
/* USER CODE END 1 */

HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();

/* USER CODE BEGIN 2 */


// User code after initialization
/* USER CODE END 2 */

while (1)
{
/* USER CODE BEGIN 3 */
// Main application loop
/* USER CODE END 3 */
}
}

Make sure your main loop is short and clean

16
FW CODING – CHAPTER 2

Proper project organization ensures maintainability and scalability of FW projects.


DIRECTORY STRUCTURE:

ProjectName/
├── Core/
│ ├── Inc/ # .h files
│ ├── Src/ # .c files
│ └── Startup/
├── Drivers/
│ ├── STM32F4xx_HAL_Driver/
│ └── CMSIS/
├── Middlewares/ # Third-party
├── Debug/ # Debug build
├── Release/ # Release build
└── [Link] # CubeMX config

FILE ORGANIZATION:
▪ main.c: Application entry point
▪ stm32f4xx_hal_conf.h: HAL configuration
▪ stm32f4xx_it.c: Interrupt handlers
▪ system_stm32f4xx.c: System initialization
▪ User files: Application-specific code

BUILD CONFIGURATION:
▪ Debug configuration (optimization -O0)
▪ Release configuration (optimization -O2)
▪ Custom configurations for specific needs
▪ Compiler flags and definitions
▪ Linker script customization

In most cases each .c file requires .h with the same name (defines, prototype etc.)

17
FW CODING – CHAPTER 2

7:DEBUGGING & PROGRAMMING

ST-Link provides debugging and programming interface for STM32 MCUs.


ST-LINK FEATURES:
▪ JTAG/SWD interface support
▪ Flash programming capability
▪ Real-time debugging
▪ Variable watching
▪ Breakpoint management

DEBUG INTERFACE SELECTION:


▪ Serial Wire Debug (SWD) - 2-wire interface
▪ JTAG - 5-wire interface
▪ Trace capabilities (SWO)
▪ Boot mode selection

CONNECTION SETUP:
▪ Connect ST-Link to target board
▪ Verify power supply connections
▪ Check debug interface pins
▪ Configure debug settings in IDE
▪ Test connection with target

When HW buying Demo Kits – Make sure they support Programming via USB (SWD most likely)

18
FW CODING – CHAPTER 2

Debug configuration enables efficient firmware development and troubleshooting.


DEBUG SESSION SETUP:
▪ Create debug configuration
▪ Select ST-Link as debug probe
▪ Configure memory regions
▪ Set up breakpoint options
▪ Configure variable display

DEBUG SESSION EXAMPLE:


// Debug-friendly code structure
#ifdef DEBUG
#define DBG_PRINT(fmt, args...) printf(fmt, ##args)
#define DBG_ASSERT(condition) if(!(condition)) { __BKPT(0); }
#else
#define DBG_PRINT(fmt, args...)
#define DBG_ASSERT(condition)
#endif

// Function with debug information


void Process_Sensor_Data(uint16_t raw_value)
{
static uint16_t previous_value = 0;
uint16_t filtered_value;

// Simple low-pass filter


filtered_value = (raw_value + previous_value) / 2;
previous_value = raw_value;

DBG_PRINT("Raw: %d, Filtered: %d\n", raw_value, filtered_value);


DBG_ASSERT(filtered_value < 4096);

// Process filtered value


if (filtered_value > THRESHOLD_HIGH) {
// Handle high value condition
}
}

19
FW CODING – CHAPTER 2

8:BUILD & OPTIMIZATION

Compiler settings significantly impact code performance and size.


OPTIMIZATION LEVELS:
▪ -O0: No optimization (debug builds)
▪ -O1: Basic optimization
▪ -O2: Standard optimization (release builds)
▪ -O3: Aggressive optimization
▪ -Os: Optimize for size
COMPILER FLAGS:
▪ -Wall: Enable all warnings
▪ -Wextra: Extra warning checks
▪ -ffunction-sections: Enable function-level linking
▪ -fdata-sections: Enable data-level linking
▪ -flto: Link-time optimization

BUILD CONFIGURATION:
# Debug build flags
DEBUG_FLAGS = -g3 -O0 -Wall -Wextra -DDEBUG

# Release build flags


RELEASE_FLAGS = -O2 -Wall -Wextra -DNDEBUG -flto

# Common flags
COMMON_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard

20
FW CODING – CHAPTER 2

Memory optimization ensures efficient use of limited MCU resources.


CODE SIZE OPTIMIZATION:
▪ Remove unused functions
▪ Use appropriate data types
▪ Enable link-time optimization
▪ Minimize library dependencies
▪ Use const qualifier for constants
RAM USAGE OPTIMIZATION:
▪ Minimize global variables
▪ Use local variables efficiently
▪ Implement memory pools
▪ Avoid dynamic memory allocation
▪ Use appropriate buffer sizes
MEMORY ANALYSIS TOOLS:
▪ MAP file analysis
▪ Memory usage reports
▪ Stack usage analysis
▪ Heap fragmentation monitoring

MEMORY LAYOUT EXAMPLE:


// Memory-efficient structure packing
typedef struct __attribute__((packed)) {
uint8_t status; // 1 byte
uint16_t temperature; // 2 bytes
uint32_t timestamp; // 4 bytes
} sensor_data_t; // Total: 7 bytes instead of 12

// Memory pool for efficient allocation


#define BUFFER_POOL_SIZE 10
static uint8_t buffer_pool[BUFFER_POOL_SIZE][256];
static uint8_t pool_usage[BUFFER_POOL_SIZE];

uint8_t* get_buffer(void)
{
for (int i = 0; i < BUFFER_POOL_SIZE; i++) {
if (!pool_usage[i]) {
pool_usage[i] = 1;
return buffer_pool[i];
}
}
return NULL; // No free buffers
}

21
FW CODING – CHAPTER 2

9:REAL-WORLD PROJECT EXAMPLE

This example demonstrates a complete temperature monitoring system.


SYSTEM REQUIREMENTS:
▪ Read temperature from DS18B20 sensor
▪ Display temperature on LCD
▪ Send data via UART
▪ Control cooling fan based on temperature
▪ Store min/max values in EEPROM
HARDWARE CONFIGURATION:
▪ STM32F401RE Nucleo board
▪ DS18B20 temperature sensor (One-Wire)
▪ 16x2 LCD display (I2C interface)
▪ DC fan with PWM control
▪ AT24C32 EEPROM (I2C interface)

PIN ASSIGNMENT:
// Pin assignments
#define ONEWIRE_PIN GPIO_PIN_2
#define ONEWIRE_PORT GPIOA
#define FAN_PWM_PIN GPIO_PIN_8
#define FAN_PWM_PORT GPIOA
#define LCD_I2C_ADDRESS 0x27
#define EEPROM_I2C_ADDRESS 0x50

22
FW CODING – CHAPTER 2

MODULE ORGANIZATION:
▪ main.c: System initialization and main loop
▪ temperature.c: DS18B20 sensor interface
▪ fan_control.c: PWM-based fan control
▪ eeprom.c: EEPROM data storage
▪ uart_comm.c: UART communication

TEMPERATURE SENSOR MODULE:


// temperature.h
typedef struct {
float current_temp;
float min_temp;
float max_temp;
uint32_t last_reading_time;
uint8_t sensor_status;
} temperature_sensor_t;

// Function prototypes
HAL_StatusTypeDef Temperature_Init(void);
HAL_StatusTypeDef Temperature_Read(float *temperature);
void Temperature_Process(temperature_sensor_t *sensor);

// temperature.c implementation
HAL_StatusTypeDef Temperature_Read(float *temperature)
{
uint8_t data[9];
uint16_t raw_temp;

// Reset pulse
OneWire_Reset();

// Skip ROM command (single device)


OneWire_WriteByte(0xCC);

// Start conversion
OneWire_WriteByte(0x44);

// Wait for conversion (750ms max)


HAL_Delay(750);

// Read scratchpad
OneWire_Reset();
OneWire_WriteByte(0xCC);
OneWire_WriteByte(0xBE);

for (int i = 0; i < 9; i++) {


data[i] = OneWire_ReadByte();
}

// Calculate temperature
raw_temp = (data[1] << 8) | data[0];
*temperature = (float)raw_temp / 16.0f;

return HAL_OK;
}

23
FW CODING – CHAPTER 2

MAIN APPLICATION LOOP:


// main.c - Application main loop
int main(void)
{
HAL_Init();
SystemClock_Config();

// Initialize peripherals
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM2_Init();
MX_USART2_UART_Init();

// Initialize application modules


Temperature_Init();
LCD_Init();
Fan_Control_Init();
EEPROM_Init();

// Load stored min/max values


EEPROM_Read_MinMax(&temp_sensor.min_temp, &temp_sensor.max_temp);

uint32_t last_display_update = 0;
uint32_t last_uart_send = 0;
uint32_t last_eeprom_save = 0;

while (1)
{
uint32_t current_time = HAL_GetTick();

// Read temperature every 2 seconds


if (current_time - temp_sensor.last_reading_time >= 2000) {
Temperature_Process(&temp_sensor);
}

// Update display every 500ms


if (current_time - last_display_update >= 500) {
LCD_Display_Temperature(&temp_sensor);
last_display_update = current_time;
}

// Send UART data every 5 seconds


if (current_time - last_uart_send >= 5000) {
UART_Send_Temperature_Data(&temp_sensor);
last_uart_send = current_time;
}

// Save min/max to EEPROM every 60 seconds


if (current_time - last_eeprom_save >= 60000) {
EEPROM_Save_MinMax(temp_sensor.min_temp, temp_sensor.max_temp);
last_eeprom_save = current_time;
}

// Update fan speed based on temperature


Fan_Control_Update(temp_sensor.current_temp);

// Process any pending tasks


HAL_Delay(10);
}
}

24
FW CODING – CHAPTER 2

FAN CONTROL MODULE:


// fan_control.c
#define FAN_ON_TEMP 25.0f
#define FAN_MAX_TEMP 35.0f
#define FAN_MIN_SPEED 30 // 30% minimum speed
#define FAN_MAX_SPEED 100 // 100% maximum speed

void Fan_Control_Update(float temperature)


{
uint8_t fan_speed = 0;

if (temperature < FAN_ON_TEMP) {


fan_speed = 0; // Fan off
} else if (temperature >= FAN_MAX_TEMP) {
fan_speed = FAN_MAX_SPEED; // Maximum speed
} else {
// Linear interpolation between min and max
float temp_range = FAN_MAX_TEMP - FAN_ON_TEMP;
float speed_range = FAN_MAX_SPEED - FAN_MIN_SPEED;
float temp_ratio = (temperature - FAN_ON_TEMP) / temp_range;
fan_speed = FAN_MIN_SPEED + (uint8_t)(temp_ratio * speed_range);
}

// Update PWM duty cycle


uint32_t pwm_value = (fan_speed * TIM2->ARR) / 100;
TIM2->CCR1 = pwm_value;
}

Pick long describing Variables for readable and clear code (Avoid this : a = function(0x24))

25

You might also like