You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVR ATmega16汇编(非C)下PWM占空比修改无效问题排查

Troubleshooting PWM Duty Cycle Issues on ATmega16

Let's break down why adjusting OCR1AL isn't changing your PWM duty cycle, and fix the issues in your code step by step:

1. Your Button Input Isn't Working (So the Duty Cycle Never Gets Updated)

The biggest blocker here is that your input port PORTA doesn’t have internal pull-up resistors enabled. You set DDRA to 0x00 (all inputs), but you didn’t set PORTA to 0xFF to activate the pull-ups. Without pull-ups, the pins are floating—their voltage is unpredictable, so your check cpi r23, 0b11111110 will almost never trigger. That means the code that modifies OCR1AL never runs at all!

Fix: Add this line right after setting DDRA:

ldi r16, 0xff
out PORTA, r16

This enables pull-ups on all PORTA pins, so when your button (connected to PA0) is pressed, PA0 will read low (0), making PINA equal to 0b11111110 as expected.

2. You Misunderstood the Timer1 PWM Mode (OCR1A Isn’t the TOP Value Here)

You thought OCR1A acts as the TOP value, but that’s only true for specific PWM modes. Let’s look at your current timer configuration:

  • TCCR1A = 0xc1 (binary 11000001): Sets COM1A1=1, COM1A0=1, WGM10=1, WGM11=0
  • TCCR1B = 0x01 (binary 00000001): Sets WGM12=0, WGM13=0, CS10=1 (no prescaling)

Combined, WGM13:0 = 0001, which activates 8-bit Phase Correct PWM mode. In this mode:

  • The TOP value is fixed at 0x00FF (255)
  • OCR1A is the compare match value, which controls the duty cycle (not the TOP)

If you want OCR1A to be the TOP value (for custom PWM periods), you’d need to switch to Fast PWM mode with OCR1A as TOP (WGM mode 15). To do that, update your timer setup to:

ldi r20, 0b11001111  ; COM1A1=1, COM1A0=1, WGM11=1, WGM10=1
out TCCR1A, r20
ldi r20, 0b00011001  ; WGM13=1, WGM12=1, CS10=1
out TCCR1B, r20

But for your original goal of adjusting duty cycle from 20% to 70%, sticking with 8-bit Phase Correct PWM is perfectly fine—you just need to adjust how you calculate the duty cycle.

3. Your Compare Output Mode Affects Duty Cycle Calculation

With COM1A1=1 and COM1A0=1 in Phase Correct PWM:

  • The OC1A pin (PD5) is set high when the timer counts up to OCR1A
  • It’s cleared when the timer counts down to OCR1A

This means the duty cycle is calculated as:
Duty Cycle = (256 - OCR1A) / 256 * 100%

So your initial OCR1AL=0x33 (51) gives a duty cycle of (256-51)/256 ≈ 80%, not 20% as you intended. If you want higher duty cycles when OCR1A increases, switch to COM1A1=1 and COM1A0=0:

ldi r20, 0b10000001  ; COM1A1=1, COM1A0=0, WGM10=1
out TCCR1A, r20

Now the duty cycle is:
Duty Cycle = (OCR1A + 1) / 256 * 100%
So OCR1AL=0x33 (51) gives (51+1)/256 ≈ 20%, and OCR1AL=0xB5 (181) gives (181+1)/256 ≈ 71%—which matches your goal of 20% to 70%.

Full Modified Code Snippet

Here’s how your code should look with all fixes applied:

.include "m16def.inc"
.cseg
PWM:
;input - enable pull-ups
ldi r16, 0x00
out DDRA, r16
ldi r16, 0xff
out PORTA, r16  ; Enable internal pull-ups on PORTA
;output
ldi r16, 0xff
out DDRD, r16
out PORTD, r16
; Timer1 setup: 8-bit Phase Correct PWM, COM1A1=1, COM1A0=0
ldi r20, 0b10000001
out TCCR1A, r20
ldi r20, 0x01
out TCCR1B, r20
; Initial duty cycle ~20% (OCR1A=51)
ldi r20, 0x00
out OCR1AH, r20
ldi r20, 0x33
out OCR1AL , r20
in r22, TIFR
andi r22, 0b00000100
out PORTD, r22
SW0:
in r23, PINA
cpi r23, 0b11111110
brne SW0
; Update duty cycle to ~71% (51+130=182)
ldi r16, 0x82
in r17, OCR1AL
ADD r16,r17
out OCR1AL, r16
jmp SW0
ldi r16, 0x00
out PORTD, r16

Final Notes

  • Double-check that you’re measuring the PWM signal on PD5 (OC1A), since that’s the pin tied to Timer1’s compare output.
  • With a 4MHz clock and no prescaling, the 8-bit Phase Correct PWM will have a frequency of ~7.8kHz, which should be stable for most applications.

内容的提问来源于stack exchange,提问作者Giannis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:20:27