Forum: Mikrocontroller und Digitale Elektronik SAMD20 timer counter 1 läuft nicht


von chris_ (Gast)


Lesenswert?

Für ein MC-System brauche ich einen Systemcounter mit Microsekunden 
Auflösung.
Ursprünglich wollte ich den ARM0 Systemcounter verwenden und diesen mit 
den ASF-Treibern ansprechen:

Beitrag "ARM Cortex M0 system timer"

Nach einigen Ratschlägen bin ich zum Schluss gekommen, auf die ASF und 
sonstige fertige Treiber zu verzichten, alles von Hand zu machen und 
einen 32 Timer/Counter zu verwenden.

Hier gibt es ein Beispiel:
http://community.atmel.com/forum/timerscounters-0?skey=samd%20timer%20example
1
#define true 1
2
#define false 0
3
4
typedef union {
5
  struct {
6
    uint16_t SWRST:1;          /*!< bit:      0  Software Reset                     */
7
    uint16_t ENABLETC:1;         /*!< bit:      1  Enable                             */
8
    uint16_t MODE:2;           /*!< bit:  2.. 3  TC Mode                            */
9
    uint16_t :1;               /*!< bit:      4  Reserved                           */
10
    uint16_t WAVEGEN:2;        /*!< bit:  5.. 6  Waveform Generation Operation      */
11
    uint16_t :1;               /*!< bit:      7  Reserved                           */
12
    uint16_t PRESCALER:3;      /*!< bit:  8..10  Prescaler                          */
13
    uint16_t RUNSTDBY:1;       /*!< bit:     11  Run in Standby                     */
14
    uint16_t PRESCSYNC:2;      /*!< bit: 12..13  Prescaler and Counter Synchronization */
15
    uint16_t :2;               /*!< bit: 14..15  Reserved                           */
16
    } bit;                       /*!< Structure used for bit  access                  */
17
    uint16_t reg;                /*!< Type      used for register access              */
18
  } TC_CTRLA_TypeX;
19
20
void Init_TC1(void)
21
{
22
  // Turn on the digital interface clock 
23
  PM->APBCMASK.reg |= PM_APBCMASK_TC1;
24
25
  // Turn on the analog interface clock and select GCLK 
26
  GCLK_CLKCTRL_Type clkctrl =
27
  {
28
    .bit.WRTLOCK = false,
29
    .bit.CLKEN = true,
30
    .bit.ID = TC1_GCLK_ID,
31
    .bit.GEN = 0,
32
  };
33
  
34
  GCLK->CLKCTRL.reg = clkctrl.reg;
35
  TC1->COUNT32.CTRLA.bit.SWRST = true;
36
37
  // wait for synchronization to complete 
38
  while (TC1->COUNT32.STATUS.bit.SYNCBUSY == true);
39
40
  // set the access mode for the timer 
41
  TC1->COUNT32.CTRLA.reg = TC_CTRLA_MODE_COUNT32;
42
43
  TC_CTRLA_TypeX tc_ctrla =
44
  {
45
    .bit.PRESCSYNC = 0,
46
    .bit.RUNSTDBY = 0,
47
    .bit.PRESCALER = 4, // TC_PRESCALER_DIV16 
48
    .bit.WAVEGEN = 0,   // Normal Frequency
49
    .bit.MODE = 2,      // TC_MODE_COUNT32 
50
    .bit.ENABLETC = 1,
51
  };
52
53
  TC1->COUNT32.CTRLA.reg = tc_ctrla.reg;
54
  // wait for synchronization to complete 
55
  while (TC1->COUNT32.STATUS.bit.SYNCBUSY == true);
56
 
57
}

In Atmel Studio sieht man, dass die Register initialisiert werden, aber 
der Zähler zählt einfach nicht.

Liegt es vielleicht am Input-Clock? Hat jemand eine Idee?

von Marco H. (damarco)


Lesenswert?

PMC Clock für die Timer aktiviert ? Auch für die PIO ?
1
pmc_enable_periph_clk(ID_PIOB);

Auch die PIO entsprechen Konfiguriert ?
1
pio_configure(PIOB,PIO_PERIPH_B,PIO_PB25,PIO_DEFAULT);    // Port PIO_PB25 auf TIOA vom Timer TC0
2
3
pmc_enable_periph_clk(ID_TC0);
4
5
NVIC_DisableIRQ(TC0_IRQn);
6
7
NVIC_ClearPendingIRQ(TC0_IRQn);
8
9
NVIC_SetPriority(TC0_IRQn,5);
10
11
NVIC_EnableIRQ(TC0_IRQn);
12
13
tc_init(TC0,0,  TC_CMR_TCCLKS_TIMER_CLOCK5|                  // set clock slow clock 32768HZ
14
          TC_CMR_LDRA_FALLING|                    // load RA by falling edge 
15
          TC_CMR_ETRGEDG_RISING|                    // set TC zero by rising edge
16
          TC_CMR_ABETRG|                        // trigger from TIOA
17
          TC_CMR_CPCTRG                        // interrupt by RC compare  
18
          );)
19
20
// set RC on 1.1sec  timeout                                                 
21
    tc_write_rc(TC0,0,36045);
22
23
tc_enable_interrupt(TC0,0,TC_IER_CPCS|
24
              TC_IER_LDRAS);
25
26
tc_start(TC0,0);      // ab hier läuft er los ;)

Wenn Interrupts aktiv
1
void TC0_Handler(void){
2
tc_get_status(TC0,0); // unbedingt auslesen damit das Flag zurückgesetzt wird 
3
4
}


Eigentlich nicht schwer ;) welche Flags man setzen muss kann man aus dem 
Referenz Manual entnehmen. Bitte den Code nicht per Copy übernehmen. Sie 
sind für einen sam3x. Das ASF sollte aber ähnlich sein :)

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

>Auch die PIO entsprechen Konfiguriert ?

Danke für die Antwort. Die Ports brauche ich nicht. Ich brauche nur den 
Zählerstand des Counters. Der Counter wird mit dem Systemclock 
getacktet.

von Marco H. (damarco)


Lesenswert?

Also beim SAM3 kann man das Umstellen. Jeder Timerchannel hat seinen 
eignen Teiler und man kann ihn auf den Slowclock stellen.

In der Referenz ist mit Sicherheit eine Übersicht voraus man die 
Möglichkeiten entnehmen kann.

 tc_start(TC0,0); Startet den Channel sonst läuft dieser nicht los. 
Dieser gibt den Clock frei.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte möglichst keine Hardware-Bitfields selbst definieren.  Diese
sind normalerweise alle über "sam.h" bereits da.  Wenn nicht, musst
du sehen, wie du sie ranbekommst.

Ich habe hier beispielsweise sowas für einen 1-Hz-Takt im TC3:
1
  /*
2
   * TC3: 1 Hz MFRQ mode: prescaler 1024 (= 15625 Hz clock)
3
   */
4
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0) | GCLK_CLKCTRL_ID_20;
5
  TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT16_Val) |
6
    TC_CTRLA_WAVEGEN(TC_CTRLA_WAVEGEN_MFRQ_Val) |
7
    TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV1024_Val);
8
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
9
  TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
10
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
11
  TC3->COUNT16.CC[0].bit.CC = 15625;            /* => 1 Hz */
12
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
13
  TC3->COUNT16.CC[1].bit.CC = 3000;
14
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
15
  TC3->COUNT16.INTENSET.bit.MC0 = 1; /* interrupt on reload */
16
  TC3->COUNT16.INTENSET.bit.MC1 = 1; /* interrupt on match */
17
  NVIC_SetPriority(TC3_IRQn, 1);
18
  NVIC_EnableIRQ(TC3_IRQn);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Marco H. schrieb:
> Also beim SAM3 kann man das Umstellen.

SAMD und Konsorten sind in den Peripherals völlig anders (allerdings
meiner Meinung nach durchaus in vielen Dingen besser).

von chris_ (Gast)


Lesenswert?

>Bitte möglichst keine Hardware-Bitfields selbst definieren.

Hallo Jörg,

ich weiß, ich weiß. Leider gab es in dem Atmel-Framwork einen Fehler in 
"compile.h" weil dort ein

#define ENABLE 1

stand. Was mit der Defintion in "tc.h" kollidiert ist. Deshalb habe ich 
die Struktur zum Test raus genommen und umbenannt.

Ich habe es gerade noch mal mit einer anderen Version der probiert, da 
haben sie den Fehler wohl beseitigt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Das liegt daran, dass die Jungs dort sich nicht an gängige
Konventionen halten: Makros schreibt man GROSS, andere Bezeichner
(wie bspw. in Bitfeldern) schreibt man klein oder camelCase.

Das gleiche passiert einem bei sowas:
1
PM->APBBMASK.bit.PORT = 1;

Ist zum Glück nicht nötig, da der Takt für PORT standardmäßig im
PM aktiv ist.  Wenn man die Zeile compilieren will, stolpert man
drüber, dass das Bitfield namens PORT mit einem gleichnamigen Makro
kollidiert.

von chris_ (Gast)


Lesenswert?

>Ich habe hier beispielsweise sowas für einen 1-Hz-Takt im TC3:

Danke dafür. Jetzt bewegt sich der Counter endlich. Ich musste noch eine 
kleine Änderung für den SAMD20 machen, weil es eine Definition anders 
ist ( siehe Programmtext ).
Jetzt muss ich nur noch auf 32bit und 1us Tick umstellen.
1
void Init_TC3(void)
2
{
3
  // Turn on the digital interface clock 
4
  PM->APBCMASK.reg |= PM_APBCMASK_TC3;
5
  /*
6
   * TC3: 1 Hz MFRQ mode: prescaler 1024 (= 15625 Hz clock)
7
   */
8
 // GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0) | GCLK_CLKCTRL_ID_20;
9
 GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0) |GCLK_CLKCTRL_ID_TC2_TC3;
10
  TC3->COUNT16.CTRLA.reg = TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT16_Val) |
11
    TC_CTRLA_WAVEGEN(TC_CTRLA_WAVEGEN_MFRQ_Val) |
12
    TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV1024_Val);
13
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
14
  TC3->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
15
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
16
  TC3->COUNT16.CC[0].bit.CC = 15625;            /* => 1 Hz */
17
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
18
  TC3->COUNT16.CC[1].bit.CC = 3000;
19
  while (TC3->COUNT16.STATUS.bit.SYNCBUSY) {}
20
}
21
...
22
while(1)
23
{
24
  uint16_t counterValue=TC3->COUNT16.COUNT.reg;
25
  printNumber(counterValue);
26
  delay_ms(100);
27
  print_CR();
28
}

von chris_ (Gast)


Lesenswert?

Leider funktioniert genau die gleiche Konfiguration nur mit 32bit nicht.
Woran könnte das denn liegen? Laut Datenblatt müsste der Counter 32bit 
können.
1
void Init_TC3_32bit(void)
2
{
3
  // Turn on the digital interface clock 
4
  PM->APBCMASK.reg |= PM_APBCMASK_TC3;
5
  
6
  // clock source configuration
7
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0) |GCLK_CLKCTRL_ID_TC2_TC3;
8
  
9
  TC3->COUNT32.CTRLA.reg = TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT32_Val) |
10
  TC_CTRLA_WAVEGEN(TC_CTRLA_WAVEGEN_MFRQ_Val) |
11
  TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV1024_Val);
12
  while (TC3->COUNT32.STATUS.bit.SYNCBUSY) {}
13
  TC3->COUNT32.CTRLA.reg |= TC_CTRLA_ENABLE;
14
  while (TC3->COUNT32.STATUS.bit.SYNCBUSY) {}
15
  TC3->COUNT32.CC[0].bit.CC = 15625;            //=> 1 Hz 
16
  while (TC3->COUNT32.STATUS.bit.SYNCBUSY) {}
17
  TC3->COUNT32.CC[1].bit.CC = 3000;
18
  while (TC3->COUNT32.STATUS.bit.SYNCBUSY) {}  
19
}
20
21
..
22
  while(1)
23
  {
24
    //uint16_t counterValue=TC3->COUNT16.COUNT.reg;
25
    uint32_t counterValue=TC3->COUNT32.COUNT.reg;
26
    printNumber(counterValue);
27
    delay_ms(100);
28
    print_CR();
29
  }

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

chris_ schrieb:
> Leider funktioniert genau die gleiche Konfiguration nur mit 32bit nicht.

32-bit-TC braucht zwei unmittelbar aufeinanderfolgende TCs.  Ist
ein bisschen tricky.  Wenn ich mich recht erinnere, muss man dann
alles auf TC2 konfigurieren, und TC3 wird dabei implizit mit
herangezogen.

Lies diesbezüglich nochmal genau das Datenblatt.  Hab' gerade keine
Zeit dafür.

von chris_ (Gast)


Lesenswert?

Danke für den Hinweis. Ich denke, das ist einer der Punkte, bei denen 
man lange suchen kann.

Ich kopiere mal die entsprechende Stelle aus dem Datenblatt und 
versuche, daraus schlau zu werden:
1
COUNT32: This mode is achieved by pairing two 16-bit TC peripherals. This pairing is explained in Clocks on
2
page 443. The even-numbered TC instance will act as master to the odd-numbered TC peripheral, which will act
3
as a slave. The slave status of the slave is indicated by reading the Slave bit in the Status register
4
(STATUS.SLAVE). The registers of the slave will not reflect the registers of the 32-bit counter. Writing to any of the
5
slave registers will not affect the 32-bit counter. Normal access to the slave COUNT and CCx registers is not
6
allowed.

Was mich wundert: Du hast TC3 als counter verwendet. Das scheinen einige 
so zu tun. Die Frage ist: Warum nicht bei TC0 beginnen? Hat das auch 
etwas mit den "Inhomogenitäten" dieser Peripherie zu tun?

von chris_ (Gast)


Lesenswert?

So, jetzt läuft der Microsekunden Systemcounter endlich:
1
/*
2
  initialize timer 2 and 3 as 32 bit system timer
3
  
4
  This configuration is for 8Mhz system frequency.
5
  
6
*/
7
void Init_TC2_32bit(void)
8
{
9
  
10
  PM->APBCMASK.reg |= PM_APBCMASK_TC2; // Turn on the digital interface clock for timer 2
11
  
12
  // clock source configuration
13
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN      |
14
                      GCLK_CLKCTRL_GEN(0)     | // use clock generator 0 as source
15
            GCLK_CLKCTRL_ID_TC2_TC3 ;
16
  
17
  TC2->COUNT32.CTRLA.reg = TC_CTRLA_MODE      ( TC_CTRLA_MODE_COUNT32_Val    ) | // 32 bit count
18
                           TC_CTRLA_WAVEGEN   ( TC_CTRLA_WAVEGEN_MFRQ_Val    ) | // 32 bit frequency generator
19
                           TC_CTRLA_PRESCALER ( TC_CTRLA_PRESCALER_DIV8_Val  ) ; // 1us clock tick at 8Mhz CLK
20
  while (TC2->COUNT32.STATUS.bit.SYNCBUSY) {} // wait for setup
21
  
22
  // reload value    
23
  TC2->COUNT32.CC[0].bit.CC = 0xFFFFFFFF;
24
  while (TC2->COUNT32.STATUS.bit.SYNCBUSY) {} // wait for setup
25
26
  // start counter
27
  TC2->COUNT32.CTRLA.reg |= TC_CTRLA_ENABLE;
28
  while (TC2->COUNT32.STATUS.bit.SYNCBUSY) {} // wait for setup
29
    
30
}
31
32
/*
33
  get the system time in micro seconds
34
*/
35
uint32_t micros()
36
{
37
  return TC2->COUNT32.COUNT.reg;
38
}

Vielen Dank, Jörg.

von Marco H. (damarco)


Lesenswert?

Ich wollte es gerade hinschreiben ;) Im 32 bit Mode werden zwei TC 
gepaart. Gezählt wird von TC0 -> gerade Master -> ungerade Slave. Der 
Clock wird auch so im PM aktiviert. Du wolltest den TC 3 als Master 
Konfigurieren das geht nicht im 32bit Mode.

Nicht unwichtig

The following registers need synchronization when read:
  Control B Clear register (CTRLBCLR)
  Control B Set register (CTRLBSET)
  Control C register (CTRLC)
  Count Value register (COUNT)
  Period Value register (PERIOD)
  Compare/Capture Value registers (CCx)

Read-synchronization is denoted by the Read-Synchronized property in the 
register description.

: Bearbeitet durch User
von chris_ (Gast)


Lesenswert?

>Nicht unwichtig
>The following registers need synchronization when read:

Das heißt dann für jedes dieser Register eine Zeile dieser Art:
1
  while (TC2->COUNT32.STATUS.bit.SYNCBUSY) {} // wait for setup

oder?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

chris_ schrieb:
> Du hast TC3 als counter verwendet. Das scheinen einige so zu tun. Die
> Frage ist: Warum nicht bei TC0 beginnen?

Hing in diesem Falle mit der IO-Zuordnung für die waveform generation
ab.  Die Alternativfunktionen für die einzelnen IO-Pins sind ja nicht
beliebig frei wählbar.

chris_ schrieb:
>> The following registers need synchronization when read:
>
> Das heißt dann für jedes dieser Register eine Zeile dieser Art:

Ja.  Kann man auch in einem Makro schieben, dann sieht der Code
nicht ganz so „verwürgt“ aus.

von Marco H. (damarco)


Lesenswert?

Ich bin noch was schuldig ;)
1
struct tc_module tc_instance;
2
3
void configure_tc(void){
4
  
5
  struct tc_config config_tc;
6
  tc_get_config_defaults(&config_tc);
7
  config_tc.clock_source=GCLK_GENERATOR_3;
8
  config_tc.clock_prescaler=TC_CLOCK_PRESCALER_DIV8;
9
  config_tc.counter_size=TC_COUNTER_SIZE_32BIT;
10
  config_tc.wave_generation=TC_WAVE_GENERATION_MATCH_FREQ_MODE;
11
  config_tc.pwm_channel[0].enabled=true;
12
  config_tc.pwm_channel[0].pin_out=PIN_PB08E_TC4_WO0;
13
  config_tc.pwm_channel[0].pin_mux=MUX_PB08E_TC4_WO0;
14
  
15
  tc_init(&tc_instance,TC4,&config_tc);
16
  tc_enable(&tc_instance);
17
18
  
19
}

Auslesen
1
uint32_t tc_get_count_value(const struct tc_module *const module_inst);
2
3
value = tc_get_count_value(&tc_instance);

Doch Vorsicht ! Bei mir schwingt der Generator 3 mit 8MHZ und dein 
gewählter Wave Mode hat bei CC 0xffffffff im 
TC_WAVE_GENERATION_NORMAL_FREQ keine Wirkung auf den WO[x]. Da der 
Ausgang beim Match und Top getoggelt wird.

Wichtig man muss zwischen TC und TCC unterscheiden. Sind zwar alles 
Timer aber mit unterschiedlichen Eigenschaften und Registern ! Beim 
SAMD21 gibt es nur 3 Timer dieser Art TC3,TC4,TC5.  Im 32Bit Mode werden 
zwei gepaart. Gerade-> Ungerade. Das geht nur mit TC4 !

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.