Forum: Mikrocontroller und Digitale Elektronik STK500 PWM und Programmflusslogik


von ope (Gast)


Lesenswert?

Hallo,

um den AVR etwas kennen zu lernen, probiere ich gerade am STK500 die
PWM aus. Wenn der Timer1 (AT90S8515) benutzt wird, wird es wohl auch
Fast PWM genannt. Jedenfalls wollte ich es mit den SW0/1 breiter oder
schmaler werden lassen, allerdings kommt es irgendwo zu einem Überlauf,
denn wenn ich incrementiere begrenzt der Code nicht - auf dem Scope
sieht man dann ein Invertieren des PWM Signals, eigentlich sollte es
nicht weiter verändern.

Wo ist mein Fehler? Im Simulator klappt natürlich alles.

Viele Grüße
Olaf

---8<---
.nolist
.include <8515def.inc>
.list

.equ PWM10_TOPVAL   = 0x03FF
.equ PWM10_50VAL   = 0x01FF
.equ PWM_RLDVAL    = 184    ; 20ms with CK/1024
.equ INCR_VAL    = 2

.def TEMP     = r16
.def PWM10H    = r17
.def PWM10L    = r18
.def ZERO    = r2
.def INCR    = r3
.def PWM_RLD    = r4
.def KEY_SCAN    = r5


.cseg

.org 0x0000
  rjmp init

.org INT0addr            ; External Interrupt Request 0
  reti

.org INT1addr            ; External Interrupt Request 1
  reti

.org ICP1addr            ; Timer/Counter Capture Event
  reti

.org OC1Aaddr            ; Timer/Counter1 Compare Match A
  reti

.org OC1Baddr            ; Timer/Counter1 Compare MatchB
  reti

.org OVF1addr            ; Timer/Counter1 Overflow
  reti

.org OVF0addr            ; Timer/Counter0 Overflow
  rjmp T0_OVF

.org SPIaddr            ; Serial Transfer Complete
  reti

.org URXCaddr            ; UART, Rx Complete
  reti

.org UDREaddr            ; UART Data Register Empty
  reti

.org UTXCaddr            ; UART, Tx Complete
  reti

.org ACIaddr            ; Analog Comparator
  reti


init:
  ; init SP
  ldi TEMP, LOW(RAMEND)
  out SPL, TEMP
  ldi TEMP, HIGH(RAMEND)
  out SPH, TEMP

  ; set vars
  ldi PWM10L, LOW(PWM10_50VAL)  ; PWM10 start val 50%
  ldi PWM10H, HIGH(PWM10_50VAL)

  ldi TEMP, PWM_RLDVAL      ; Timer reload value var
  mov PWM_RLD, TEMP

  ldi TEMP, INCR_VAL
  mov INCR, TEMP
  clr ZERO

  ; enable sleep mode
  ldi TEMP, (1<<SE)
  out MCUCR, TEMP

  ; PD5 as output OC1A
  ldi TEMP, (1<<DDD5)
  out DDRD, TEMP

  ; PD0/PD1 as input with pull-up for switches (PWM up/down 0..100%)
  ldi TEMP, (1<<PORTD0)|(1<<PORTD1)
  out PORTD, TEMP

  ; intall 10-bit pwm mode on T/C1
  ; OC1A non-inverted and OC1B inverted PWM, prescale CK/8
  ldi TEMP, (1<<COM1A1)|(1<<COM1B1)|(1<<COM1B0)|(1<<PWM11)|(1<<PWM10)
  out TCCR1A, TEMP
  ldi TEMP, (1<<CS11)
  out TCCR1B, TEMP

  ; install interrupt for switch PWMing on T/C0
  ldi TEMP, (1<<TOIE0)
  out TIMSK, TEMP
  ldi TEMP, (1<<CS02)|(1<<CS00)  ; CK/1024 => 3.686MHz/1024 = 278us
  ;ldi TEMP, (1<<CS01)        ; CK/8 for SIMULATOR TEST ONLY
  out TCCR0, TEMP
  out TCNT0, PWM_RLD        ; reload für 20ms

  ; pwm 50%, load top values
  ldi TEMP, HIGH(PWM10_50VAL)
  out OCR1AH, TEMP
  ldi TEMP, LOW(PWM10_50VAL)
  out OCR1AL, TEMP

  ldi TEMP, HIGH(PWM10_50VAL)
  out OCR1BH, TEMP
  ldi TEMP, LOW(PWM10_50VAL)
  out OCR1BL, TEMP

  sei

main:
  sleep
  rcall pwm_keys
  rjmp main

; **** sub routines ****

; -- eval switch scan codes --
; Note, the STK500 keys are low active
; SW0 -> down
; SW1 -> up
pwm_keys:

  ; for SIMULATOR TEST ONLY
  ; ldi TEMP, 0x02
  ; mov KEY_SCAN, TEMP

  ; check on sw0/PD0
  sbrs KEY_SCAN, PORTD0
  rcall pwm_dec

  ; check on sw1/PD1
  sbrs KEY_SCAN, PORTD1
  rcall pwm_inc

pwm_keys_exit:

  ldi TEMP, 0x03
  or KEY_SCAN, TEMP        ; clear pwm key scan

  ; set the PWM values
  out OCR1AH, PWM10H
  out OCR1AL, PWM10L

  out OCR1BH, PWM10H
  out OCR1BL, PWM10L

  ret

; -- decrement PWM --
pwm_dec:

  sub PWM10L, INCR
  sbc PWM10H, ZERO

  brmi pwm_dec_limit        ; if PWM10 value less than zero, limit it

  ret

pwm_dec_limit:

  clr PWM10L
  clr PWM10H

  ret

; -- increment PWM --
pwm_inc:

  add PWM10L, INCR
  adc PWM10H, ZERO

  ; test on PWM TOPVAL
  mov TEMP, PWM10L
  subi TEMP, LOW(PWM10_TOPVAL + 1)
  mov TEMP, PWM10H
  sbci TEMP, HIGH(PWM10_TOPVAL + 1)

  breq pwm_inc_limit        ; if PWM10 value greater than TOPVAL, limit

  ret

pwm_inc_limit:

  ldi PWM10L, LOW(PWM10_TOPVAL)
  ldi PWM10H, HIGH(PWM10_TOPVAL)

  ret

; ********* ISR *********

T0_OVF:
  cli

  push TEMP            ; save register
  in TEMP, SREG
  push TEMP

  in KEY_SCAN, PIND        ; scan switches on PD

  out TCNT0, PWM_RLD        ; reload timer

  pop TEMP            ; restore register
  out SREG, TEMP
  pop TEMP

  sei

  reti



--->8---

von ope (Gast)


Lesenswert?

Hi,

keine Hinweise?

von Nico Sachs (Gast)


Lesenswert?

Hi, dies ist natürlich jetzt auf die schnelle ziemlich schwierig einen
überblick von dem assembler code zu machen. Falls du die möglichkeit
hast dies in c zu programmieren, mache dies. Tipp: für kleinere
Prögrämmchen: CodevisionAvr -->
http://www.hpinfotech.ro/html/download.htm
Sorry ansonsten kann ich dir nicht weiterhelfen. Ich assembliere nie
wieder, seit ich mit c arbeite.

von ope (Gast)


Lesenswert?

Also, zwei Tasten am STK500 sollen die Pulswidth der 10-bit PWM
inc/decrementieren. Dabei sollen die "Anschläge" bei TOPVAL=0x3FF
(10bit full) bzw. 0x00 sein.

So würde ich es in C machen:

---8<---
uint8_t keyscan;  // keys are low active, Port read into var
uint16_t pwmval;  // pwm compare value

void pwm_keys() {
  if(~keyscan & 0x01)  // bit 0 active -> decrement
    if(--pwmval < 0) pwmval = 0;

  if(~keyscan & 0x02)  // bit 1 active ->increment
    if(++pwmval > 0x3FF) pwmval = 0x3FF;

  OCR1A = pwmval;
}
--->8---

Ist in diesem Algo schon der Fehler?

Viele Grüße
Olaf

von Schmidle (Gast)


Lesenswert?

Hallo ope,

ich denke, daß der Fehler hier liegt.

if(++pwmval > 0x3FF) pwmval = 0x3FF;
   ^^

Denn angenommen pwmva1 steht auf 0x3FF und soll nochmal um eins erhöht
werden, dann wird zuerst 0x01 zu 0x3FF dazugezählt und man erhält einen
Überlauf, so daß pwmva1 dann 0x00 ist. Der anschließende Vergleich ob
pwmva1 größer wie 0x3FF rennt dann ins Leere.

Probiere mal das hier aus:

if(pwmval < 0x3FF) pwmval++;

Dann sollte pwmva1 bei 0x3FF stehen bleiben.
Beim dekrementieren ist übrigens der selbe Fehler.

Gruß
Schmidle

von ope (Gast)


Lesenswert?

vielen Dank für den Hinweis; aber ++pwmval ist ein pre-increment und
pwmval eine 2-byte/16-Bit Variable, d.h. 0x03FF + 0x0001 = 0x4000
passen ohne Überlauf rein. Mit dem Nachfolgenden Vergleich wird
festgestellt, dass mehr als die 10Bit gesetzt sind (bzw. 0x4000 >
0x03FF) und pwmval wieder auf 0x03FF fest getackert - oder liege ich
hier total falsch?

Viele Grüße
Olaf

von Schmidle (Gast)


Lesenswert?

sorry, du hast natürlich recht.
Ein Überlauf bei 0x3FF íst bei einer 16-Bit Variablen natürlich
Unsinn.

Gruß
Schmidle

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.