www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Software PWM = 1000 % CPU-Last?


Autor: Marcel Holle (multiholle)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe endlich geschafft anhand der vielen Tutorials ein Software PWM 
für mein Atmega168 zu programmieren.

Leider wird die ISR, welche für das PWM-Signal zuständig ist, durch 
einen Timer in jedem Takt aufgerufen. Damit wäre die CPU voll 
ausgelastet.

20.000.000 Hz  1000 PWM-Länge  60 FPS / 256 ? = 1,...

Woher stammt eigentlich die 256 bei der Berechnung der Taktzyklen pro 
Timerdurchlauf? Wie kann ich die CPU-Last senken?
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include "speaker.h"
#include "leds.h"

// led intensity
volatile uint16_t intensity;
// logarithmic intensity table
const uint16_t pwm_table[64] PROGMEM = 
    {0, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 
   15, 17, 19, 21, 23, 26, 29, 32, 36, 40, 44, 49, 55, 61, 68, 76, 85, 94, 105, 
   117, 131, 146, 162, 181, 202, 225, 250, 279, 311, 346, 386, 430, 479, 534, 
     595, 663, 739, 824, 918, 1023};

// interrupt service routine
ISR(TIMER0_OVF_vect) {
    static uint16_t pwm;

    // increase pwm counter
    pwm++;
    // pwm = pwm mod 1024
    if (pwm == 1024) {
        pwm = 0;
        leds_set(0b00001111);
    }
    // turn lights off
    if (pwm > intensity) {
        leds_set(0b00000000);
    }
}

// main
int main (void) {
    // init components
    leds_init();
    speaker_init();

    
    // set timer prescaler to 1/1
    TCCR0B |= (1 << CS00);
    // enable timer overflow interrupt
    TIMSK0 |= (1<<TOIE0);
    // enable global interrupts
    sei();

    while (1) {
        uint8_t i;
        // fade leds up
        for (i = 0; i < 64; i++) {
            intensity = pgm_read_word(pwm_table + i);
            _delay_ms(100);
        }

        // fade leds down
        for (i = 0; i < 64; i++) {
            intensity = pgm_read_word(pwm_table + 63 - i);
            _delay_ms(100);
        }
    }
}

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, zunächst: Die 256 kommt vermutlich daher, dass Du den 
Overflow-Interrupt verwendest, der also aufgerufen wird sobald der 
8-bit-Zähler bis 256 gezählt hat. Es ist mitnichten so, dass der 
Overflow-Interrupt bei jedem Takt stattfindet...

Allerdings würde ich das hier ein bischen anders angehen, es ist nicht 
nötig, für jeden PWM-Schritt den Interrupt auszulösen.

In http://www.mikrocontroller.net/articles/Soft-PWM findest Du 
geschicktere Methoden.

Viele Grüße,
        Simon

Autor: Marcel Holle (multiholle)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich versuche gerade die Funktion der PWM Funktion auf dieser Seite 
http://www.mikrocontroller.net/articles/Soft-PWM nachzuvollziehen. Ich 
habe mir jetzt einen kleinen Teil herausgenommen und wollte diesen auf 
meinem Atmega 168 zum laufen bringen. Leider leuchtet keine der LEDs am 
Ausgang.

Die LEDs sollen nach dem Bitmuser von pwm_mask nacheinander aktiviert 
werden. Zwischen den einzelnen Mustern werden die Pausen aus pwm_timing 
eingehalten.

Hier mein Code. "led_set" aktiviert 8 LEDs mit Hilfe eines übergebenen 
Bitmusters.
/*** INCLUDE ******************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include "leds.h"

/*** DEFINE *******************************************************************/
#define F_CPU         20000000L
#define F_PWM         100L
#define PWM_PRESCALER 8
#define PWM_CHANNELS  8
#define PWM_STEPS     256
#define T_PWM         (F_CPU / (PWM_PRESCALER * F_PWM * PWM_STEPS))

/** VARS/CONSTS ***************************************************************/
volatile uint8_t pwm_count = 3;
volatile uint16_t pwm_timing[] = {2 * T_PWM, 230 * T_PWM, 23 * T_PWM};
volatile uint8_t pwm_mask[] = {0b00001010, 0b00001000, 0b00000000};

/** FUNCTIONS *****************************************************************/
ISR(TIMER0_COMPA_vect) {
  static uint8_t pwm = 0;
  OCR1A +=  pwm_timing[pwm];
  leds_set(pwm_mask[pwm]);
  pwm++;
  if (pwm == 3)
    pwm = 0;
}

int main (void) {
  leds_init();

  TCCR1B |= (1 << CS01);
  TIMSK1 |= (1 << OCIE1A);
  sei();

  while (1);
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel Holle schrieb:
> Ich versuche gerade die Funktion der PWM Funktion auf dieser Seite
> http://www.mikrocontroller.net/articles/Soft-PWM nachzuvollziehen.

Schön.
Aber nächstes mal fang bitte einen neuen Thread an und hijacke nicht 
einen alten.

> Hier mein Code. "led_set" aktiviert 8 LEDs mit Hilfe eines übergebenen
> Bitmusters.

Und die Funktion sieht wie aus?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hier

  TCCR1B |= (1 << CS01);
  TIMSK1 |= (1 << OCIE1A);


schaltet offenbar den Timer 1 ein.

Das hier

ISR(TIMER0_COMPA_vect) {

ist die Service Routine für den Timer 0.

Was passt da nicht zusammen?

Hinweis: Ehe man kompliziertere Dinge in eine ISR packt, ist es oft 
hilfreich in einer ISR einfach nur eine LED einzuschalten um zu sehen ob 
sie überhaupt aufgerufen wird.

Autor: Frosch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 1000 % CPU-Last?

Seit wann hats denn 10-Kern-AVRs?

Autor: Marcel Holle (multiholle)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Schön.
> Aber nächstes mal fang bitte einen neuen Thread an und hijacke nicht
> einen alten.

Sorry, ich dachte es passt. Weil ich oben ja auf das Tutorial verwiesen 
wurden.

Karl heinz Buchegger schrieb:
> Das hier
>
>   TCCR1B |= (1 << CS01);
>   TIMSK1 |= (1 << OCIE1A);
>
>
> schaltet offenbar den Timer 1 ein.
>
> Das hier
>
> ISR(TIMER0_COMPA_vect) {
>
> ist die Service Routine für den Timer 0.
>
> Was passt da nicht zusammen?

Danke :) Manchmal braucht man wirklich nur mal kurz jemanden der einen 
Wachrüttelt, wenn man zu lange programmiert.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Marcel Holle schrieb:
> Karl heinz Buchegger schrieb:
>> Schön.
>> Aber nächstes mal fang bitte einen neuen Thread an und hijacke nicht
>> einen alten.
>
> Sorry, ich dachte es passt. Weil ich oben ja auf das Tutorial verwiesen
> wurden.

Nur dann, wenn du 100% exakt genau dasselbe Problem hast, inklusive 
identischem Code.
Im Zweifel lieber neu anfangen.

Bei dir ist es, dass alles was vor dir steht, für dein Problem völlig 
irrelevant ist. Wenn der Verweis auf einen Artikel schon reichen würde, 
dann hätten wir hier im Forum vielleicht 15 bis 20 Monsterthreads in 
denen sich fast alles abspielt.

Oft merkt man nämlich nicht, dass jetzt ein anderer Fragesteller an der 
Reihe ist und dann studiert man vorhergehenden Beitrag um vorhergehenden 
Beitrag um dann auf Detailfragen zu antworten, die schon monatelang 
keinen mehr interessieren.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.