Firmware Coding: IDE & Project Setup
Firmware Coding: IDE & Project Setup
By Shimi Cohen
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
}
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
}
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
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
3
FW CODING – CHAPTER 2
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
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
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:
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
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
8
FW CODING – CHAPTER 2
CONFIGURATION PROCESS:
▪ Review pin conflicts and warnings
▪ Set electrical characteristics
▪ Configure pull-up/pull-down resistors
▪ Assign alternate functions
▪ Validate pin assignments
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 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
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
HAL_RCC_OscConfig(&RCC_OscInitStruct);
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
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
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};
HAL_TIM_PWM_Init(&htim3);
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)
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 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
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
while (1)
{
/* USER CODE BEGIN 3 */
// Main application loop
/* USER CODE END 3 */
}
}
16
FW CODING – CHAPTER 2
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
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
19
FW CODING – CHAPTER 2
BUILD CONFIGURATION:
# Debug build flags
DEBUG_FLAGS = -g3 -O0 -Wall -Wextra -DDEBUG
# Common flags
COMMON_FLAGS = -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard
20
FW CODING – CHAPTER 2
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
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
// 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();
// Start conversion
OneWire_WriteByte(0x44);
// Read scratchpad
OneWire_Reset();
OneWire_WriteByte(0xCC);
OneWire_WriteByte(0xBE);
// Calculate temperature
raw_temp = (data[1] << 8) | data[0];
*temperature = (float)raw_temp / 16.0f;
return HAL_OK;
}
23
FW CODING – CHAPTER 2
// Initialize peripherals
MX_GPIO_Init();
MX_I2C1_Init();
MX_TIM2_Init();
MX_USART2_UART_Init();
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();
24
FW CODING – CHAPTER 2
Pick long describing Variables for readable and clear code (Avoid this : a = function(0x24))
25