pwm simulation

This commit is contained in:
ImplFerris 2024-12-24 23:35:59 +05:30
parent 985b498feb
commit efedc23c04
3 changed files with 469 additions and 2 deletions

View File

@ -1,6 +1,75 @@
# Pulse Width Modulation (PWM)
<style>
.slider-container {
margin: 20px 0;
}
label {
margin-right: 10px;
}
.led-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
position: relative;
}
.led-body {
width: 30px;
height: 40px;
background: radial-gradient(circle at center, #ff5555, #cc0000);
border-radius: 50% 50% 0 0;
border: 2px solid #990000;
position: relative;
box-shadow: 0 0 10px rgba(255, 85, 85, 0.8);
}
.led-body::after {
content: '';
position: absolute;
top: 5px;
left: 7px;
width: 16px;
height: 16px;
background: rgba(255, 255, 255, 0.4);
border-radius: 50%;
}
.led-pin {
width: 2px;
height: 40px;
background-color: #333;
position: relative;
}
.anode {
height: 50px; /* Longer pin for the anode */
margin-right: 15px;
background-color: #666;
}
.cathode {
height: 40px; /* Shorter pin for the cathode */
margin-left: 15px;
background-color: #333;
position: absolute;
margin-top: 45px;
}
canvas {
border: 1px solid #ccc;
display: block;
margin: 10px auto;
}
#pwmCanvas {
background-color: #fefefe;
</style>
In this section, we will explore what is PWM and why we need it.
## Digital vs Analog
@ -29,10 +98,34 @@ The percentage of time the signal is on during one cycle.
<img style="display: block; margin: auto;" alt="Duty Cycle" src="../images/pwm-duty-cycle.png" />
<span style="text-align: center;display: block; margin: auto; font-size: 12px;">Image Credit: Wikipedia</span>
## PWM Simulation
Here is the interactive simulation. Use the sliders to adjust the duty cycle and frequency, and watch how the pulse width and LED brightness change.
<canvas id="pwmCanvas" width="800" height="200"></canvas>
<div class="led-container">
<div class="led-body" id="ledBody"></div>
<div class="led-pin anode"></div>
<div class="led-pin cathode"></div>
</div>
<div class="slider-container">
<label for="dutyCycle">Duty Cycle (%): </label>
<input type="range" id="dutyCycle" min="0" max="100" value="50">
<span id="dutyCycleValue">50</span>%
</div>
<div class="slider-container">
<label for="frequency">Frequency (Hz): </label>
<input type="range" id="frequency" min="1" max="50" value="10">
<!-- <span id="frequencyValue">x</span> Hz -->
</div>
## Period and Frequency
Period is the total time for one on-off cycle to complete.
The frequency of a PWM signal is the number of cycles it completes in one second, measured in Hertz (Hz). Frequency is the inverse of the period:
The frequency of a PWM signal is the number of cycles it completes in one second, measured in Hertz (Hz). Frequency is the inverse of the period. So, a higher frequency means a shorter period, resulting in faster switching between HIGH and LOW states.
\\[
\text{Frequency (Hz)} = \\frac{1}{\text{Period (s)}}
@ -51,7 +144,6 @@ For example, if the period is 20ms(0.02s), the frequency will be 50Hz.
\\]
**Calculating Cycle count from Frequency per second**
The Formula to calculate cycle count:
@ -60,3 +152,81 @@ The Formula to calculate cycle count:
\\]
If a PWM signal has a frequency of 50Hz, it means it completes 50 cycles in one second.
<script>
const pwmCanvas = document.getElementById('pwmCanvas');
const pwmCtx = pwmCanvas.getContext('2d');
const dutyCycleSlider = document.getElementById('dutyCycle');
const dutyCycleValue = document.getElementById('dutyCycleValue');
const frequencySlider = document.getElementById('frequency');
const frequencyValue = document.getElementById('frequencyValue');
const ledBody = document.getElementById('ledBody');
let dutyCycle = 50; // Initial duty cycle in percentage
let frequency = 10; // Initial frequency in Hz
function drawPWM() {
pwmCtx.clearRect(0, 0, pwmCanvas.width, pwmCanvas.height);
const period = 1000 / frequency; // Period in ms
const onTime = period * (dutyCycle / 100); // On time in ms
const offTime = period - onTime; // Off time in ms
const totalWidth = pwmCanvas.width;
const cycles = frequency; // Number of cycles to display
const cycleWidth = totalWidth / cycles;
pwmCtx.strokeStyle = 'black';
pwmCtx.lineWidth = 2;
pwmCtx.beginPath();
let x = 0;
if (dutyCycle === 100) {
pwmCtx.moveTo(0, 50);
pwmCtx.lineTo(pwmCanvas.width, 50);
} else if (dutyCycle === 0) {
pwmCtx.moveTo(0, 150);
pwmCtx.lineTo(pwmCanvas.width, 150);
} else {
for (let i = 0; i < cycles; i++) {
const highWidth = (onTime / period) * cycleWidth;
const lowWidth = (offTime / period) * cycleWidth;
pwmCtx.moveTo(x, 50);
pwmCtx.lineTo(x + highWidth, 50);
pwmCtx.lineTo(x + highWidth, 150);
pwmCtx.lineTo(x + highWidth + lowWidth, 150);
pwmCtx.lineTo(x + highWidth + lowWidth, 50);
x += cycleWidth;
}
}
pwmCtx.stroke();
}
function updateLED() {
const brightness = dutyCycle / 100;
ledBody.style.background = `radial-gradient(circle at center, rgba(255, 85, 85, ${brightness}), #cc0000)`;
}
function update() {
dutyCycle = parseInt(dutyCycleSlider.value, 10);
frequency = parseInt(frequencySlider.value, 10);
dutyCycleValue.textContent = dutyCycle;
// frequencyValue.textContent = frequency;
drawPWM();
updateLED();
}
dutyCycleSlider.addEventListener('input', update);
frequencySlider.addEventListener('input', update);
// Initial draw
drawPWM();
updateLED();
</script>

View File

@ -4,5 +4,13 @@ The ESP32 has LED PWM Controller(LEDC) that generates PWM signals for controllin
The LEDC includes 16 independent PWM generators and supports a maximum PWM duty cycle resolution of 20 bits. The 16 PWM channels further classified into two types: 8 high speed channel and 8 low speed channels.
<div class="alert-box alert-box-info">
<span class="icon"><i class="fa fa-info"></i></span>
<div class="alert-content">
<b class="alert-title">High vs Low Speed channels</b>
<p>High-speed channels use hardware to automatically adjust the PWM duty cycle in a glitch-free manner, ensuring smooth operation. In contrast, low-speed channels rely on software to manually adjust the duty cycle.</p>
</div>
</div>
The PWM controller can automatically increase or decrease the duty cycle gradually, allowing for smooth fades without using the processor. For more details, refer to page 390 of the [ESP32 Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm).

View File

@ -240,3 +240,292 @@ sup {
.result-no-output {
font-style: italic;
}
/* Wire body */
.wire {
position: relative;
width: 200px;
height: 4px;
border-radius: 3px;
margin-bottom: 20px; /* Space between wires */
}
/* Wire colors */
.wire.red { background: #ff0000; } /* Red wire */
.wire.black {
background: #000000;
border: 2px solid #FFFFFF;
} /* Black wire */
.wire.green { background: #13d613; } /* Green wire */
.wire.yellow { background: #e6e61c; } /* Yellow wire */
.wire.blue { background: #057af7; } /* Default blue wire */
.wire.orange { background: #fc5310; } /* Orange wire */
.wire.brown { background: #824101; } /* Orange wire */
.wire.purple {background: #800080;}
/* White wire with border to make it visible */
.white {
background: #ffffff; /* White color */
/* border: 1px dotted #AAAAAA; */
box-shadow: 0 0 6px rgba(0, 0, 0, 0.3);
}
/* Male connector (left) */
.male-left {
position: absolute;
left: -19px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 8px;
background: #000;
border: 2px solid #CCC;
}
/* Pin extending from left connector */
.male-left::after {
content: '';
position: absolute;
left: -12px; /* Reduced gap between connector and pin */
top: 50%;
transform: translateY(-50%);
width: 12px;
height: 2px;
background: #555; /* Pin color */
}
/* Male connector (right) */
.male-right {
position: absolute;
right: -8px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 8px;
background: #000;
border: 2px solid #CCC;
}
/* Pin extending from right connector */
.male-right::after {
content: '';
position: absolute;
right: -12px; /* Reduced gap between connector and pin */
top: 50%;
transform: translateY(-50%);
width: 12px;
height: 2px;
background: #555; /* Pin color */
}
/* Female connector (left) */
.female-left {
position: absolute;
left: -19px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 8px;
background: #000;
border: 2px solid #CCC;
}
.female-left::after {
content: '';
position: absolute;
left: 1px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 6px;
background: #999;
border-radius: 50%;
}
/* Female connector (right) */
.female-right {
position: absolute;
right: -19px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 8px;
background: #000;
border: 2px solid #CCC;
}
.female-right::after {
content: '';
position: absolute;
right: 1px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 6px;
background: #999;
border-radius: 50%;
}
.boxed-text {
display: inline-block;
padding: 3px 10px;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background-clip: padding-box;
transition: all 0.3s ease-in-out;
}
.alert-box {
display: flex;
align-items: flex-start;
/* padding: 16px; */
border-radius: 8px;
margin: 16px 0;
font-family: Arial, sans-serif;
position: relative;
padding: 12px 0px 0px 10px;
}
.alert-box .icon {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
width: 32px;
height: 32px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.5); /* Semi-transparent background */
border: 2px solid currentColor;
position: absolute;
top: 10px;
left: 8px;
color: inherit; /* Matches the alert's text color */
}
.alert-box .alert-content {
margin-left: 48px; /* Leaves space for the round icon */
}
.alert-box .alert-title {
margin: 0px 8px 0px 0px;
font-size: 18px;
margin-bottom: 20px;
}
/* .alert-box p { */
/* margin: 0; */
/* font-size: 14px; */
/* } */
.alert-box.alert-box-warning {
background-color: #fff4cc;
border: 1px solid #ffcc00;
color: #856404;
}
.alert-box.alert-box-info {
background-color: #E8F0Fe;
border: 1px solid #17a2b8;
color: #0c5460;
}
.alert-box.alert-box-danger {
background-color: #f8d7da;
border: 1px solid #dc3545;
color: #721c24;
}
.alert-box.alert-box-success {
background-color: #d4edda;
border: 1px solid #28a745;
color: #155724;
}
.icon i {
background: none !important; /* Remove any background set by Font Awesome */
box-shadow: none !important; /* Remove shadow if applied */
color: inherit; /* Inherit alert text color */
}
/* General styles for slanted text */
.slanted-text {
display: inline-block;
padding: 3px 6px;
margin: 0 5px;
font-weight: bold;
transform: skew(-8deg);
border-radius: 3px;
}
/* Individual color themes */
.slanted-text.red {
/* background: linear-gradient(135deg, #e74c3c, #c0392b); */
background: #e74c3c;
color: black;
}
.slanted-text.yellow {
/* background: linear-gradient(135deg, #f39c12, #e67e22); */
background: #efe012;
color: white;
}
.slanted-text.teal {
/* background: linear-gradient(135deg, #1abc9c, #16a085); */
background: #1abc9c;
color: black;
}
.slanted-text.blue {
/* background: linear-gradient(135deg, #00bcd4, #008c8c); */
background: #3457D5;
color: black;
}
.slanted-text.pink {
/* background: linear-gradient(135deg, #e91e63, #c2185b); */
background: #e91e63;
color: black;
}
.slanted-text.purple {
/* background: linear-gradient(135deg, #8e44ad, #6f4f37); */
background: #8e44ad;
color: black;
}
.slanted-text.brown {
/* background: linear-gradient(135deg, #6f4f37, #a0522d); */
background: #a0522d;
color: black;
}
.slanted-text.green {
/* background: linear-gradient(135deg, #2ecc71, #27ae60); */
background: #00A550;
color: black;
}
.slanted-text.gray {
background: linear-gradient(135deg, #bdc3c7, #95a5a6);
color: black;
}
.slanted-text.indigo {
background: linear-gradient(135deg, #3f51b5, #303f9f);
color: black;
}
.slanted-text.black {
background: #333333;
color: #999;
}
.slanted-text span {
display: inline-block;
transform: skew(10deg);
}