Forum: Mikrocontroller und Digitale Elektronik Timer/Counter für PWM konfigurieren auf AT91SAM9


von datacom (Gast)


Lesenswert?

Hallo Leute,

ich versuche verzweifelt den Timer/Counter-Channel 1 auf meinem AT91SAM9 
auf ein 25 kHz PWM-Signal mit 50% Duty-Cycle zu konfigurieren. Ich habe 
anhand des Datenblatts alle Register die dafür notwendig sein sollten 
gesetzt und dennoch messe ich am gefragten Ausgang nur konstant 3,3V mit 
dem Oszi, so als wäre der Pin noch im GPIO-Modus.

Leider habe ich keine so richtig passenden Codebeispiele zu dieser MCU 
gefunden, daher musste ich alles selbst schreiben. Es ist also gut 
möglich, dass nur eine Kleinigkeit noch fehlt.

Bei was ich mir wirklich nicht sicher bin, sind die Adressen der 
Register für einen speziellen Channel, da diese Adresse immer vom 
Timer-Modus abhängig ist (Capture oder Waveform) und das im Datenblatt 
etwas unübersichtlich beschrieben wird (siehe z.B. Channel Mode Register 
TC_CMR0).

Auch z.B. das korrekte Setzen der Waveform ist für mich nicht ganz 
eindeutig zu ersehen, da aus der Erklärung für das Channel Mode Register 
nicht eindeutig daraus hervorgeht welches Bit an welche Stelle gehört.

Ich hoffe jemand kennt diesen Controller. Der exakte Typ ist 
AT91SAM9XE512.
1
#include <stdio.h>
2
3
#define PMC_PCER    0xFFFFFC10    // Power Management Controller: Peripheral Clock Enable Register
4
#define TC_CMR0      0xFFFDC004    // Channel mode Register as seen in the datasheet for channel 0
5
#define TC_CMR1      0xFFFDC044    // Channel mode Register as seen in the datasheet for channel 1
6
#define TC_CMR2      0xFFFDC084    // Channel mode Register as seen in the datasheet for channel 2
7
#define TC_RC0      0xFFFDC01C    // Register C as seen in the datasheet for channel 0
8
#define TC_RC1      0xFFFDC05C    // Register C as seen in the datasheet for channel 1
9
#define TC_RC2      0xFFFDC09C    // Register C as seen in the datasheet for channel 2
10
#define TC_RB0      0xFFFDC018    // Register B as seen in the datasheet for channel 0
11
#define TC_RB1      0xFFFDC058    // Register B as seen in the datasheet for channel 1
12
#define TC_RB2      0xFFFDC098    // Register B as seen in the datasheet for channel 2
13
#define TC_RA0      0xFFFDC014    // Register A as seen in the datasheet for channel 0
14
#define TC_RA1      0xFFFDC054    // Register A as seen in the datasheet for channel 1
15
#define TC_RA2      0xFFFDC094    // Register A as seen in the datasheet for channel 2
16
#define TC_CCR0      0xFFFDC000    // Channel Control Register as seen in the datasheet for channel 0
17
#define TC_CCR1      0xFFFDC040    // Channel Control Register as seen in the datasheet for channel 1
18
#define TC_CCR2      0xFFFDC080    // Channel Control Register as seen in the datasheet for channel 2
19
#define PIO_PDR      0xFFFFF404    // PIO Controller PIO Enable Register for Port PIOA
20
#define PIO_ASR      0xFFFFF470    // PIO Peripheral A Select Register for Port PIOA
21
#define TC_IER0      0xFFFDC024    // Interrupt Enable Register for Channel 0
22
23
#define IO_WR32(adress, value) (*((volatile unsigned long *) (adress)) = (value))
24
#define IO_RD32(adress) (*((volatile unsigned long *) adress))
25
#define IO_SET_MASK32(adress, mask) IO_WR32((adress), IO_RD32((adress)) | (mask))
26
27
void pal_pwm_start(){
28
29
  // Channel-Pin-Matching:
30
  // PA26: Peripheral A: TIOA0, Peripheral B: ERX3
31
  // Timer/Counter Channel 0: Output: TIOA0, TIOB0
32
  // Timer1: TC0, TC1, TC2: TCLK0-TCLK2, TIOA0-TIOA2, TIOB0-TIOB2
33
  // PA0 - PA31: Parallel I/O Controller A (PIOA)
34
35
  // Power Management Controller (PMC): Peripherial Clock Enable Register
36
  // - activate the clock signal for T/C 0 (bit 17 to value 1b)
37
  IO_WR32(PMC_PCER, (1<<17)); // Peripheral ID (PID) 17 belongs to Timer/Counter 0 and to Signal TIOA0
38
  IO_WR32(PMC_PCER, (1<<2)); // Parallel I/O Controller A (PIOA)
39
40
  // T/C Channel Control Register for Channel 0:
41
  // - disable the clock signal: CLKDIS-Bit (bit 1 to value 1)
42
  IO_WR32(TC_CCR0, (1<<1));
43
44
  // PIO Disable Register for I/O-Port PIOA
45
  // - disable GPIO-functionality for Pins PA26 and PA27 and enable their Peripheral function: Set Bit 26 to value 1
46
  IO_WR32(PIO_PDR, (1<<26));
47
48
  // Peripheral A Select Register
49
  // - enable Peripheral A instead of Peripheral B for Pins PA26: Set Bit 26 to value 1
50
  IO_WR32(PIO_ASR, (1<<26));
51
52
  // T/C Channel Mode Register for Channel 0:
53
  // - set the Operating Mode to WAVEFORM: WAVE-Bit (bit 15 to value 1b)
54
  // - set the waveform to "up-mode with automatic trigger on RC compare": WAVSEL-Bits (bits 13-14 to value 10b = 2d)
55
  // - set the Clock Signal to MCKL / 2 (TIMER_CLOCK1): TCCLKS-Bits (bits 0-2 to value 000b = 0d)
56
  // - set the RA Compare Effect on TIOA output level to toogle: ACPA-bits (bit 16, 17 to value 11b = 3d)
57
  // - set the RC Compare Effect on TIOA counter to clear: ACPC-Bits (bit 18, 19 to value 10b = 2d)
58
  IO_WR32(TC_CMR0, (1<<15));
59
  IO_WR32(TC_CMR0, (0<<14) | (1<<13));
60
  IO_WR32(TC_CMR0, (0<<2) | (0<<1) | (0<<0));
61
  IO_WR32(TC_CMR0, (1<<17) | (1<<16));
62
  IO_WR32(TC_CMR0, (0<<19) | (1<<18));
63
64
  // T/C Interrupt Enable Register for Channel 0:
65
  // - set the RC Compare interrupt (CPCS)
66
  // - set the RA Compare interrupt (CPAS)
67
  //IO_WR32(TC_IER0, (1<<4));
68
  //IO_WR32(TC_IER0, (1<<2));
69
70
  // T/C Register C for Channel 0:
71
  // - set the "reset value register" to 2^11: Register C to value 2^11d = 2048d (cuts down the 16-bit-resolution to 11-bit)
72
  IO_WR32(TC_RC0, 2048);
73
74
  // T/C Register A for Channel 0:
75
  // - set the LOW-to-HIGH threshold to 2^11 / 2 (50% duty cycle)  --> value 1024d
76
  IO_WR32(TC_RA0, 1024);
77
78
  // T/C Channel Control Register for Channel 0:
79
  // - enable the clock signal: CLKEN-Bit, CLKDIS-Bit (bit 1 to value 0 and bit 0 to value 1)
80
  IO_WR32(TC_CCR0, (0<<1)); // unset disable-bit
81
  IO_WR32(TC_CCR0, (1<<0)); // set enable-bit
82
}

von gerhard (Gast)


Lesenswert?

hallo,
dein beispielcode ist etwas schwer lesbar.
die typ. fehler beim at91 hast du mal nicht gemacht (clock auf 
peripheral schalten und port-multiplexer richtig einstellen).

ich würde dir empfehlen dass du dir mal die sog. software packages 
ansiehst und die darin enthaltene library verwendest.

wenn du mal für dein spezielles derivat kein beispiel-projekt findest 
kannst du bei anderen derivaten nach entspr. beispielen suchen und diese 
verwenden. da die peripherals nahezu bei allen geleich sind ist das kein 
problem.

gruss
gerhard

von Klaus (Gast)


Lesenswert?

Kann es sein, dass du hier:
  IO_WR32(PMC_PCER, (1<<17)); // Peripheral ID (PID) 17 belongs to 
Timer/Counter 0 and to Signal TIOA0
  IO_WR32(PMC_PCER, (1<<2)); // Parallel I/O Controller A (PIOA)

anstatt den Timer/Counter Peripheral zu benutzen, auf den PWM Peripheral 
multiplexen solltest?
Auf bspw. PWMH0 anstatt TIOA0.

Grüße

von gerhard (Gast)


Lesenswert?

hallo nochmals,
der fehler liegt vermutlich in der art und weise wie auf die div. 
register geschrieben wird.
du schreibts auf die register mehrfach hintereinander und damit 
überschreibtst du die immer wieder die schon eingestellten werte.
du musst auf ein register mit einem befehl beschreiben oder den 
bisherigen inhalt auslesen (soweit das überhaupt geht) und mit dem neuen 
wert verknüpfen.


lg
gerhard

von datacom (Gast)


Lesenswert?

Hallo vielen Dank für eure Antworten - das war sehr hilfreich! :)
Außerdem muss man einen Software-Reset des Timers machen bevor es los 
geht. Das Setzen des Enable-Bits reicht nicht aus. Hier mein 
vollständiger Quelltext:
1
#include <stdio.h>
2
3
#define PMC_PCER    0xFFFFFC10    // Power Management Controller: Peripheral Clock Enable Register
4
#define PIO_PDR      0xFFFFF404    // PIO Controller PIO Disable Register for Port PIOA
5
#define PIO_ASR      0xFFFFF470    // PIO Peripheral A Select Register for Port PIOA
6
#define TC_CMR0      0xFFFA0004    // Channel mode Register for channel 0
7
#define TC_CMR1      0xFFFA0044    // Channel mode Register for channel 1
8
#define TC_CMR2      0xFFFA0084    // Channel mode Register for channel 2
9
#define TC_RC0      0xFFFA001C    // Register C  for channel 0
10
#define TC_RC1      0xFFFA005C    // Register C  for channel 1
11
#define TC_RC2      0xFFFA009C    // Register C  for channel 2
12
#define TC_RB0      0xFFFA0018    // Register B  for channel 0
13
#define TC_RB1      0xFFFA0058    // Register B  for channel 1
14
#define TC_RB2      0xFFFA0098    // Register B  for channel 2
15
#define TC_RA0      0xFFFA0014    // Register A  for channel 0
16
#define TC_RA1      0xFFFA0054    // Register A  for channel 1
17
#define TC_RA2      0xFFFA0094    // Register A  for channel 2
18
#define TC_CCR0      0xFFFA0000    // Channel Control Register for channel 0
19
#define TC_CCR1      0xFFFA0040    // Channel Control Register for channel 1
20
#define TC_CCR2      0xFFFA0080    // Channel Control Register for channel 2
21
#define TC_IER0      0xFFFA0024    // Interrupt Enable Register for Channel 0
22
#define TC_IDR0      0xFFFA0028    // TC Interrupt Disable Register for Channel 0
23
#define TC_SR0      0xFFFA0020    // TC Status Register for Channel 0
24
25
#define IO_WR32(adress, value) (*((volatile unsigned long *) (adress)) = (value))
26
#define IO_RD32(adress) (*((volatile unsigned long *) adress))
27
#define IO_SET_MASK32(adress, mask) IO_WR32((adress), IO_RD32((adress)) | (mask))
28
29
void pal_pwm_start(){
30
31
  // Channel-Pin-Matching:
32
  // Timer0: TC0, TC1, TC2: TCLK0-TCLK2, TIOA0-TIOA2, TIOB0-TIOB2 ---> daher gehört TC0 wohl fest zu TIOA0
33
  // PA26: Peripheral A: TIOA0, Peripheral B: ERX3
34
  // Timer/Counter Channel 0: Output: TIOA0, TIOB0
35
  // PA0 - PA31: Parallel I/O Controller A (PIOA)
36
37
  // Power Management Controller (PMC): Peripherial Clock Enable Register
38
  // - activate the clock signal for T/C 0 (bit 17 to value 1b)
39
  IO_WR32(PMC_PCER, (1<<17) | (1<<2)); // Peripheral ID (PID) 17 belongs to Timer/Counter 0 and to Signal TIOA0
40
                     
41
42
  // PIO Disable Register for I/O-Port PIOA
43
  // - disable GPIO-functionality for Pins PA26 and enable their Peripheral function: Set Bit 26 to value 1
44
  IO_WR32(PIO_PDR, (1<<26));
45
46
  // Peripheral A Select Register
47
  // - enable Peripheral A instead of Peripheral B for Pins PA26: Set Bit 26 to value 1
48
  IO_WR32(PIO_ASR, (1<<26));
49
50
  // T/C Channel Control Register for Channel 0:
51
  // - disable the clock signal: CLKDIS-Bit (bit 1 to value 1)
52
  IO_WR32(TC_CCR0, (1<<1));
53
54
  // Disable interrupts
55
  IO_WR32(TC_IDR0, 0xFFFFFFFF);
56
57
  // Clear status register:
58
  IO_RD32(TC_SR0);
59
60
  // T/C Channel Mode Register for Channel 0:
61
  // - set the Operating Mode to WAVEFORM: WAVE-Bit (bit 15 to value 1b)
62
  // - set the waveform to "up-mode with automatic trigger on RC compare": WAVSEL-Bits (bits 13-14 to value 10b = 2d)
63
  // - set the Clock Signal to MCKL / 2 (TIMER_CLOCK1): TCCLKS-Bits (bits 0-2 to value 000b = 0d)
64
  // - set the RA Compare Effect on TIOA output level to toggle: ACPA-bits (bit 16, 17 to value 11b = 3d)
65
  // - set the RC Compare Effect on TIOA counter to clear: ACPC-Bits (bit 18, 19 to value 10b = 2d)
66
  IO_WR32(TC_CMR0, (1<<15) | (1<<14) | (0<<13) | (0<<2) | (0<<1) | (0<<0) | (1<<17) | (1<<16) | (1<<19) | (0<<18));
67
68
  // T/C Register C for Channel 0:
69
  // - set the "reset value register" to 2^11: Register C to value 2^11d = 2048d (cuts down the 16-bit-resolution to 11-bit)
70
  IO_WR32(TC_RC0, 2048);
71
72
  // T/C Register A for Channel 0:
73
  // - set the LOW-to-HIGH threshold to 2^11 / 2 (50% duty cycle)  --> value 1024d
74
  IO_WR32(TC_RA0, 2048); // ab wann der counter resettet wird (ab dann ist er wieder low, den rest von der zeit high)
75
               // hoher RA-Wert bedeutet hoher Low-Prozentwert
76
               // 100% Low (=2048) bedeutet Mindestdrehzahl des Lüfters von z.B. 400 rpm
77
78
  // T/C Channel Control Register for Channel 0:
79
  // - enable the clock signal: CLKEN-Bit (bit 0 to value 1)
80
  IO_WR32(TC_CCR0, (1<<0) | (1<<2)); // set enable-bit and reset counter
81
}

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.