Forum: Mikrocontroller und Digitale Elektronik Arduino Nano - 20 kHz PWM an OC1A/OC1B


von YamA (Gast)


Lesenswert?

Hallo,

ich benötige an einem Arduino Nano an den Pins OC1A und OC1B jeweiles 
ein PWM-Signal mit 20kHz.

Der Arduino läuft mit 16 MHz.
Den Prescaler setzte ich auf 1.
Als Top-Value schreibe ich 800 in das ICR1-Register.

=> 16 MHz  1  800 = 20 kHz

Folgenden Code verwende ich um Timer 1 zu initialisieren:
1
  pinMode(9, OUTPUT);
2
  pinMode(10, OUTPUT);
3
  
4
  TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11) | (1 << WGM10);
5
  TCCR1B = (1 << WGM12)  | (1 << CS10) ;
6
  ICR1L  = (800 >> 0) & 0xFF;
7
  ICR1H  = (800 >> 8) & 0xFF;
8
  
9
  OCR1A  = 1023 / 2; /* 50% DC */
10
  OCR1B  = 1023 / 3; /* 33% DC */

Die Frequenz der Pins messe ich mit einem Oszi. Diese liegt allerdings 
nur bei 15,625 kHz.

Sieht jemand einen Fehler?

Danke

von S. Landolt (Gast)


Lesenswert?

Mode 7: top=0x3FF: 20 MHz / 1024 = 15.625 kHz

von S. Landolt (Gast)


Lesenswert?

Pardon -   16 MHz / 1024 = 15.625 kHz

von YamA (Gast)


Lesenswert?

Das erklärt natürlich die Frequenz.
Allerdings bleibt der Pegel auf den beiden Pins in allen anderen Mode, 
welche als Top-Value das ICR1-Register verwendet wird, immer auf High...

von Carl D. (jcw2)


Lesenswert?

Ein Compare-Wert größer dem Top-Wert ist eigentlich ein "Compare Never".
Entsprechen muß man die 33%/50%-Werte vom Top-Wert ausgehend ermitteln.
Nur mal so vorab, falls sich am Pin 1024/2 nicht als 800/2 herausstellen 
sollten.

von S. Landolt (Gast)


Lesenswert?

Die Reihenfolge von ICR1L/H=... ist falsch (siehe Datenblatt "Accessing 
16-bit Registers") - warum eigentlich nicht direkt ICR1=800?

Beitrag #5011589 wurde vom Autor gelöscht.
von YamA (Gast)


Lesenswert?

Hallo, danke für Eure Hilfe!
Mittlerweile läuft das ganze.

Hier der Code:
1
  pinMode( 9, OUTPUT);
2
  pinMode(10, OUTPUT);
3
  
4
  TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
5
  TCCR1B = (1 << WGM13)  | (1 << WGM12)  | (1 << CS10);
6
  ICR1   = (800- 1);
7
  
8
  OCR1A  = (800 - 1) / 2;
9
  OCR1B  = (800 - 1) / 3;

von S. Landolt (Gast)


Lesenswert?

Nachtrag: der letzte Fehler war exakt der von Carl Drexler beschriebene 
Fall, "compare never": ICR1 wurde nur auf den low-Wert von 800 gesetzt, 
also 32, und damit waren die OCR1_ weit drüber und wurden nie erreicht.

von Don P. (thoughtlessly_b)


Lesenswert?

YamA schrieb:
> Hallo, danke für Eure Hilfe!
> Mittlerweile läuft das ganze.
>
> Hier der Code:
>
1
>   pinMode( 9, OUTPUT);
2
>   pinMode(10, OUTPUT);
3
> 
4
>   TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
5
>   TCCR1B = (1 << WGM13)  | (1 << WGM12)  | (1 << CS10);
6
>   ICR1   = (800- 1);
7
> 
8
>   OCR1A  = (800 - 1) / 2;
9
>   OCR1B  = (800 - 1) / 3;
10
>

Danke für die Informationen. Gibt es dazu eine Art Anleitung? Ist dies 
der vollständige Code? Muss der Pin nicht irgendwo noch auf HIGH gesetzt 
werden?
 Würde gerne ähnliches mit dem Arduin Mega 2560 durchführen und 
unterschiedliche Freqeunzen ausgeben...
Leider kenne ich mich fast gar nicht damit aus. Würde mich über 
deine/eure Hilfe freuen.

von Karl M. (Gast)


Lesenswert?

Hallo,
du musst Dich im Datenblatt mit den Unterschiedlichen Timer Modi 
auseinander setzen und dann die zugehörige Beschreibung lesen und 
verstehen.
Dort findet man auch unterschiedliche Formeln, je nach Modi, für das 
TOP-Register.

Don P. schrieb:
> Würde gerne ähnliches mit dem Arduin Mega 2560 durchführen und
> unterschiedliche Freqeunzen ausgeben...
> Leider kenne ich mich fast gar nicht damit aus. Würde mich über
> deine/eure Hilfe freuen.

von Jens (Gast)


Lesenswert?

YamA schrieb:
...
>   pinMode(10, OUTPUT);
>
>   TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
...
Schöner Mix aus Ardu'ino' und echtem 'C' :-/

Mal blöd gefragt, warum verwendest Du nicht die Arduino-Funktion 'tone'?
https://www.arduino.cc/en/reference/tone

Ich würde mir den Code noch kommentieren,
damit man später auch ohne Blick ins Datenblatt sieht, was gemacht wird:
1
    // Waveform generator mode 14 (Table 20-6)
2
    // Fast PWM, TOP = ICR1 
3
    //
4
    // Clear OC1A/OC1B on compare match, (Table 20-4)
5
    // Set   OC1A/OC1B at bottom
6
    //
7
    // Prescaler = 1 (Table 20-7) 
8
    TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11);
9
    TCCR1B = (1 << WGM13)  | (1 << WGM12)  | (1 << CS10);
10
11
    ICR1   = (800- 1);
12
 
13
    OCR1A  = (800 - 1) / 2;
14
    OCR1B  = (800 - 1) / 3;


Zusätzlich lasse mir gerne die Zählerendwerte vom Präprozessor 
ausrechnen.
Hier ein aktuelles Beispiel, nur für einen anderen Timer:
1
static void
2
timer0_init( const uint16_t frequency) 
3
{
4
#define TIMER0_PRESCALER    256 
5
6
    TCCR0A  |= ( 1 << WGM01);     // CTC mode, Table 19-9
7
    TCCR0A  |= ( 1 << COM0B0);    // Toggle OC0B on Compare Match, Tab. 19-6
8
9
#if TIMER0_PRESCALER == 1
10
    TCCR0B  |= ( 1 << CS00);
11
#elif TIMER0_PRESCALER == 8
12
    TCCR0B  |= ( 1 << CS01);
13
#elif TIMER0_PRESCALER == 64
14
    TCCR0B  |= ( 1 << CS01) | ( 1 << CS00);
15
#elif TIMER0_PRESCALER == 256
16
    TCCR0B  |= ( 1 << CS02);
17
#elif TIMER0_PRESCALER == 1024
18
    TCCR0B  |= ( 1 << CS02) | ( 1 << CS00);
19
#else
20
    #error "TIMER 0 PRESCALER undefined"
21
#endif
22
23
    OCR0A   = ( F_CPU / 2 / TIMER0_PRESCALER / frequency) - 1;
24
    OCR0B   = 0;
25
}

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.