无PWM模块单片机单计数器+中断生成指定占空比输出方案问询
Alright, let's break down these two counter-based PWM problems step by step—no fancy PWM modules required here!
First off, let's recall what a 20% duty cycle means: the output is HIGH for 20% of the total period, and LOW for the remaining 80%. Here's how to pull this off with a single counter:
Core Idea
We'll use a counter in auto-reload mode (it resets to 0 once it hits a pre-defined "reload" value). By setting a comparison threshold, we can set/clear the output pin when the counter crosses that threshold—no interrupts needed if your counter supports hardware compare match.
Step-by-Step Implementation
- Pick a counter period: Let's use easy math for 20%—set the counter's auto-reload register to
9(most counters count from 0 to the reload value, making the total periodreload_value + 1= 10 timer ticks). - Set the comparison value: For 20% duty cycle, the HIGH time should be 2 ticks. So set the counter's compare register to
1(counts 0 and 1 as HIGH, that's exactly 2 ticks). - Configure output behavior: Set up the counter to drive the output pin HIGH when the counter starts at 0, and LOW when the counter matches the compare value.
Example Pseudocode (for a typical 8-bit MCU)
// Configure counter in auto-reload mode TIMER->CR1 |= TIMER_CR1_ARPE; // Enable auto-reload preload TIMER->ARR = 9; // Total period: 10 timer ticks TIMER->CCR1 = 1; // Compare threshold for 20% HIGH time // Configure output compare to stay HIGH until match, then LOW TIMER->CCMR1 |= TIMER_CCMR1_OC1M_2 | TIMER_CCMR1_OC1M_0; // PWM mode 1 TIMER->CCER |= TIMER_CCER_CC1E; // Enable output on channel 1 TIMER->CR1 |= TIMER_CR1_CEN; // Start the counter
This setup makes the output pin stay HIGH from counter value 0 to 1, then LOW from 2 to 9—perfect 20% duty cycle.
Since we don't have hardware PWM, we'll use counter interrupts to manually toggle the pins. The key insight here: 80% duty cycle is just the inverse of 20%, so we can sync both pins to the same counter's events.
Core Idea
We'll use two interrupt events from the same counter:
- A compare match interrupt when the counter hits the 20% threshold (to switch pin1 LOW and pin2 HIGH)
- An overflow interrupt when the counter resets (to switch pin1 HIGH and pin2 LOW)
Step-by-Step Implementation
- Configure the counter: Same as before—auto-reload mode, reload value
9(10-tick total period). - Set comparison value:
1for the 20% threshold. - Enable interrupts: Turn on the compare match interrupt (for the
1value) and the counter overflow interrupt. - Write interrupt service routines (ISRs): Handle pin state changes in each ISR, and don't forget to clear interrupt flags!
Example Pseudocode
// Initialize GPIO pins as outputs GPIO->DDR |= (1 << PIN1) | (1 << PIN2); GPIO->ODR |= (1 << PIN1); // Start pin1 HIGH, pin2 LOW // Configure counter TIMER->ARR = 9; TIMER->CCR1 = 1; TIMER->DIER |= TIMER_DIER_CC1IE | TIMER_DIER_UIE; // Enable compare and overflow interrupts TIMER->CR1 |= TIMER_CR1_CEN; // Enable global interrupts (depends on your MCU) __enable_irq(); // Compare match ISR (triggers when counter hits 1) void TIMER_CC1_IRQHandler(void) { TIMER->SR &= ~TIMER_SR_CC1IF; // Clear interrupt flag GPIO->ODR &= ~(1 << PIN1); // Set pin1 LOW GPIO->ODR |= (1 << PIN2); // Set pin2 HIGH } // Overflow ISR (triggers when counter resets to 0) void TIMER_IRQHandler(void) { TIMER->SR &= ~TIMER_SR_UIF; // Clear interrupt flag GPIO->ODR |= (1 << PIN1); // Set pin1 HIGH GPIO->ODR &= ~(1 << PIN2); // Set pin2 LOW }
This way, pin1 stays HIGH for 2 ticks (20%) and LOW for 8, while pin2 is the exact opposite—HIGH for 8 ticks (80%) and LOW for 2. All handled via interrupts, so your MCU can run other code without polling pins nonstop.
内容的提问来源于stack exchange,提问作者W.Wafi

