Analyzing the task above, we need to vary the LED's brightness over specific time intervals.
As we know, PWM (Pulse Width Modulation) is used to control LED brightness.
Also, we have to vary the LED brightness from 0% to 100% in 2 seconds, then from 100% back to 0% in 1 second.
To vary LED brightness smoothly using PWM with N discrete steps (e.g., 256 steps for 8-bit PWM), calculate the delay per brightness step as:
Examples:
Avoid flickering of LED: Human eyes can detect flicker below ~100 Hz. Always use a PWM frequency above 200 Hz (commonly 500 Hz–1 kHz for LEDs).
Case 1: 5V Supply
Standard resistor values near 320 Ω: 330 Ω or 300 Ω (whichever is available).
Similarly, Case 2: 3.3V Supply
Standard resistor value: 150 Ω.
So, by selecting a proper resistor, LED, and calculating the delay correctly, we can implement the task.
Below are the solutions to the given task using different microcontrollers
We’re using an STM32 NUCLEO-F103RB board, which runs at a 3.3V logic level.
Circuit Diagram
Project Setup in STM32CubeIDE:
SystemClock_Config
).1
→ Timer clock = APB2 / 2.10000
→ sets PWM resolution and base frequency.6400
(used to generate delays by polling the update flag).0
for fast steps, 1
for slower steps).HAL_Init()
→ Initializes the HAL library.SystemClock_Config()
→ Configures system clock using HSI+PLL.MX_GPIO_Init()
→ Sets up GPIO ports.MX_USART2_UART_Init()
→ Configures USART2.MX_TIM1_Init()
→ Initializes TIM1 for PWM.MX_TIM4_Init()
→ Initializes TIM4 for delay generation.PWM control (TIM1 configurations)
static void MX_TIM1_Init(void) {
htim1.Instance = TIM1;
htim1.Init.Prescaler = 1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 10000; // ARR value
// ... other configurations
}
TIM1 is configured as a PWM output:
Timing Control (TIM4 configurations)
static void MX_TIM4_Init(void) {
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0; // Initially fastest speed
htim4.Init.Period = 6400;
// ... other configurations
}
TIM4 is used for timing control:
Main Firmware Logic
int main(void) {
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init(); //optional
MX_TIM4_Init();
MX_TIM1_Init();
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // Start PWM on TIM1 CH1
while (1) {
/* PHASE 1: Configure TIM4 with prescaler=1 and generate upward PWM ramp
* - Stops TIM4 to modify configuration
* - Sets slower clock prescaler for TIM4 (longer delay between steps)
* - Ramp up PWM duty cycle from 0% to 100% (0 to 10000)
* - Uses TIM4 overflow as delay between PWM steps
*/
// Stop TIM4 to reconfigure
HAL_TIM_Base_Stop(&htim4);
// Set slower prescaler (1) for longer delay
htim4.Init.Prescaler = 1;
// Reinitialize TIM4 with new prescaler
HAL_TIM_Base_Init(&htim4);
// Restart TIM4 in polling mode
HAL_TIM_Base_Start(&htim4);
// Ramp up PWM duty cycle (0 → 10000)
for (int i = 0; i < 10000; i++) {
// Update PWM duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, i);
// Wait for TIM4 overflow (delay)
while (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE) == RESET)
;
// Clear overflow flag for next cycle
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
}
/* PHASE 2: Configure TIM4 with prescaler=0 and generate downward PWM ramp
* - Stops TIM4 to modify configuration
* - Sets faster clock prescaler for TIM4 (shorter delay between steps)
* - Ramp down PWM duty cycle from 100% to 0% (10000 to 0)
* - Uses TIM4 overflow as delay between PWM steps
*/
// Stop TIM4 to reconfigure
HAL_TIM_Base_Stop(&htim4);
// Set fastest prescaler (0) for minimal delay
htim4.Init.Prescaler = 0;
// Reinitialize TIM4 with new prescaler
HAL_TIM_Base_Init(&htim4);
// Restart TIM4 in polling mode
HAL_TIM_Base_Start(&htim4);
// Ramp down PWM duty cycle (10000 → 0)
for (int i = 10000; i >= 0; i--) {
// Decrease PWM duty cycle
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, i);
// Wait for TIM4 overflow (shorter delay than before)
while (__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE) == RESET)
;
// Clear overflow flag for next step
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);
}
}
}
Timing Calculations
The system clock is 64 MHz (from PLL configuration in SystemClock_Config()
).
For TIM4 (timing control):
Rise Time (2 seconds):
Fall Time (1 second):
The complete STM32CubeIDE project (including .ioc
configuration, main.c
, and HAL files) is available here:
We are using the ESP32 DevKit v4 development board and programming it using the Arduino IDE.
In this task, we will control the brightness of an LED connected to the ESP32 using PWM.
PWM Options on ESP32
Among these, the LEDC (LED Controller) peripheral is a dedicated hardware PWM controller, optimized for applications like LED brightness control, and we will use it in this task.
Pins to Avoid for PWM on ESP32
Important Note
In Arduino Core v2.x for ESP32, LEDC API functions like ledcSetup()
and ledcAttachPin()
are used for PWM configuration.
In Arduino Core v3.x, these functions are removed to avoid compilation errors. Use the updated LEDC API instead; otherwise, you will encounter a compilation error.
e.g.:
ledcAttach(pin, freq, resolution);
ledcWrite(channel, dutyCycle);
Reference: ESP32 Arduino Core 2.x → 3.0 Migration Guide
#define LED_PIN 14
void setup() {
Serial.begin(115200);
// Attach LED_PIN to PWM with 1 kHz frequency, 12-bit resolution
if (!ledcAttach(LED_PIN, 1000, 12)) {
Serial.println("Failed to attach LEDC to pin");
}
// Start with 0 duty cycle (LED off)
ledcWrite(LED_PIN, 0);
}
void loop() {
/*
Fade in: Gradually increase brightness from 0 to 4095 over 2 seconds
- There are 4096 brightness levels (0 to 4095)
- Total time for fade-in: 2000 milliseconds (2 seconds)
- Delay per step: 2000 ms / 4096 = ~488 microseconds
*/
for (int duty = 0; duty < 4096; duty++) {
ledcWrite(LED_PIN, duty); // Set duty cycle on the pin
delayMicroseconds(488); // Pause for 488 microseconds per step
}
/*
Fade out: Gradually decrease brightness from 4095 to 0 over 1 second
- There are 4096 brightness levels (4095 to 0)
- Total time for fade-out: 1000 milliseconds (1 second)
- Delay per step: 1000 ms / 4096 = ~244 microseconds
*/
for (int duty = 4095; duty >= 0; duty--) {
ledcWrite(LED_PIN, duty); // Set duty cycle on the pin
delayMicroseconds(244); // Pause for 244 microseconds per step
}
}
We are using the Arduino UNO development board and programming it using the Arduino IDE.
The LED is connected to an Arduino UNO, and its brightness is controlled using PWM.
On Arduino UNO, the PWM signal is generated using the analogWrite()
function, which outputs an 8-bit PWM signal (0–255 duty cycle).
Circuit Diagram
Code
// LED is connected to Pin no. 9 (must be a PWM-capable pin for analogWrite)
void setup() {
pinMode(9, OUTPUT); // Set pin 9 as an output to control the LED
}
void loop() {
/*
Fade in: Gradually increase brightness from 0 to 255 over 2 seconds
- There are 256 brightness levels (0 to 255)
- Total time for fade-in: 2000 milliseconds (2 seconds)
- Delay per step: 2000 ms / 256 = ~7.812 ms or 7812 microseconds
*/
for (int brightness = 0; brightness <= 255; brightness++) {
analogWrite(9, brightness); // Set the LED brightness (PWM value)
delayMicroseconds(7812); // Pause for 7812 microseconds per step
}
/*
Fade out: Gradually decrease brightness from 255 to 0 over 1 second
- There are 256 brightness levels (255 to 0)
- Total time for fade-out: 1000 milliseconds (1 second)
- Delay per step: 1000 ms / 256 = ~3.906 ms or 3906 microseconds
*/
for (int brightness = 255; brightness >= 0; brightness--) {
analogWrite(9, brightness); // Set the LED brightness (PWM value)
delayMicroseconds(3906); // Pause for 3906 microseconds per step
}
}