Forum: Mikrocontroller und Digitale Elektronik PIT bei ATMEGA4809 macht nicht was ich gerne hätte


von Andreas N. (orangehead)


Lesenswert?

Hallo Leute,
Ich bau grad ne lorawan Platine mit ATMEGA4809, RFM95 und 2 wasserfesten 
ds18b20 um Bodentemperaturen zu messen. Ich nutze die mcci lmic library 
und deswegen Arduino.
Dabei gehe ich gerade die gesamte Software durch um die Wachzeit des uC 
zu minimieren.
Also, beim ds18b20 muss man ihm 600ms zum auslesen geben und dabei 
wollte ich den uC schlafen legen. Während den 20 minütigen Schlafphasen 
nutze ich den PIT (Periodic interrupt timer) um die Node aus dem 
Tiefschlaf zu wecken. Ich dachte ich kann einfach den PIT prescaler auf 
PIT_DIV1024 setzen was mit dem 1k RTC grob eine Sekunde ist, passt und 
tschüss...
Falsch gedacht, offensichtlich hängt der erste Interrupt davon ab in 
welcher Zählphase der RTC grad ist und erst beim zweiten Interrupt ist 
der Intervall korrekt.
Mit den TCA und TCB kann ich keinen Interrupt auslösen der den uC aus 
dem deep sleep weckt(und hab auch kaum mal was damit gemacht)

Hat da jemand eine Ahnung wie ich die 600ms schlaf hinkriege?

Hier ein kleines blinky Beispielprogramm bei dem ich den Nano Every zum 
rumprobieren nutze
1
#include "settings.h"
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <avr/sleep.h>
5
6
void setup()
7
{  
8
  RTC_init();
9
  SLEEP_init();
10
  PORT_init();
11
}
12
13
void loop()
14
{
15
  blink(1);  
16
  SLEEP_1s();  
17
  sei();
18
  sleep_cpu();
19
  sei();
20
21
  blink(8);
22
  SLEEP_8s();
23
  sei();
24
  sleep_cpu();
25
  sei();
26
}
settings.cpp
1
#include "settings.h"
2
#include <util/delay.h>
3
4
#define F_CPU 16000000UL
5
6
7
void RTC_init(void) {
8
9
  uint8_t temp;
10
  temp = CLKCTRL.OSC32KCTRLA;
11
  temp &= ~CLKCTRL_ENABLE_bm;
12
  CPU_CCP = CCP_IOREG_gc;
13
  CLKCTRL.OSC32KCTRLA = temp;
14
  while (RTC.STATUS > 0)
15
  {
16
    ; 
17
  }
18
19
  RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;
20
  RTC.PITINTCTRL = RTC_PI_bm;
21
22
  RTC.PITCTRLA = RTC_PERIOD_CYC32768_gc | RTC_PITEN_bm;
23
24
  while (RTC.PITSTATUS > 0) {
25
    ;
26
  }
27
}
28
29
30
31
void SLEEP_init(void) {
32
  SLPCTRL.CTRLA |= SLPCTRL_SMODE_PDOWN_gc;
33
  SLPCTRL.CTRLA |= SLPCTRL_SEN_bm;
34
}
35
36
void SLEEP_1s(){
37
  RTC.PITCTRLA = RTC_PERIOD_CYC1024_gc | RTC_PITEN_bm;;
38
  while (RTC.PITSTATUS > 0) {
39
    ;
40
  }
41
}
42
43
void SLEEP_8s(){
44
  RTC.PITCTRLA = RTC_PERIOD_CYC8192_gc | RTC_PITEN_bm;;
45
  while (RTC.PITSTATUS > 0) {
46
    ;
47
  }
48
}
49
50
void PORT_init(void)
51
{
52
  PORTB.DIR |= PIN5_bm;
53
  PORTB.OUTSET |= PIN5_bm;
54
}
55
56
void blink(uint8_t _num){
57
  for(int i = 0; i < _num; i++){
58
    PORTB.OUTTGL = PIN5_bm;
59
    _delay_ms(30);
60
    PORTB.OUTTGL = PIN5_bm;
61
    _delay_ms(30);
62
  }
63
}
64
65
66
ISR(RTC_PIT_vect)
67
{
68
  RTC.PITINTFLAGS = RTC_PI_bm;
69
}
settings.h
1
// settings.h
2
3
#ifndef _SETTINGS_h
4
#define _SETTINGS_h
5
6
void RTC_init(void);
7
void SLEEP_init(void);
8
void SLEEP_8s(void);
9
void SLEEP_1s(void);
10
void PORT_init(void);
11
void blink(uint8_t _num);
12
13
14
15
#endif

von neuer PIC Freund (Gast)


Lesenswert?

> Also, beim ds18b20 muss man ihm 600ms zum auslesen geben

Wo hast du diese Information her?
Für 11 bit reichen auch 375 ms. Evtl weniger.

Kannst du nicht einfach bei 32kHz auf idle schalten, und Vollgas, wenn 
gebraucht? Die 1.5 mA für den Sensor und das HF machen den uc ja zum 
Kleinverbraucher.

von Georg M. (g_m)


Lesenswert?

Andreas N. schrieb:
> Mit den TCA und TCB kann ich keinen Interrupt auslösen der den uC aus
> dem deep sleep weckt

Der TCA und der TCB sind nicht zum Schlafen gedacht. Aber der RTC 
verbraucht im SMODE_STDBY weniger als 2µA, und hat auch die OVF und CMP 
Interrupts.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

hast du das schon durchgearbeitet?
https://github.com/MicrochipTech/TB3213_Getting_Started_with_RTC

von S. Landolt (Gast)


Lesenswert?

Nanu, Andreas N., "nach Diktat verreist"?

So ganz verstehe ich die Frage nicht: der PIT läuft durch, für die lange 
Pause wählt man einen Ausgang mit großem Teiler, für die 
Temperaturmessphase einen mit passend kleinem.

von Andreas N. (orangehead)


Lesenswert?

Sorry für die lange inaktivität,hatte viel zu tun, jemand sollte mal den 
48 Stunden Tag erfinden

neuer PIC Freund schrieb im Beitrag #7129532:
>> Also, beim ds18b20 muss man ihm 600ms zum auslesen geben
>
> Wo hast du diese Information her?
> Für 11 bit reichen auch 375 ms. Evtl weniger.
>
> Kannst du nicht einfach bei 32kHz auf idle schalten, und Vollgas, wenn
> gebraucht? Die 1.5 mA für den Sensor und das HF machen den uc ja zum
> Kleinverbraucher.

Ach Sorry, du hast recht. Es waren 750ms bei 12 bit!!! 11 bit langen 
aber, also 375ms....
Wie meinst du das, die Main clock auf 32k schalten und ein 
_delay_ms(375); und dann wieder die mainclock auf vollgas?

von Andreas N. (orangehead)


Angehängte Dateien:

Lesenswert?

S. Landolt schrieb:
> Nanu, Andreas N., "nach Diktat verreist"?
>
> So ganz verstehe ich die Frage nicht: der PIT läuft durch, für die lange
> Pause wählt man einen Ausgang mit großem Teiler, für die
> Temperaturmessphase einen mit passend kleinem.

So dachte ich mir das ja auch.
Im Datenblatt Example 22-1. PIT Timing Diagram for PERIOD(im Anhang) 
scheint es dass der RTC erst nach einer Periode den interrupt richtig 
erkennt, eben das ist mir nicht ganz klar.
Oben in meinem Beispielprogramm blinkt die led eben nicht nach den 
eingestellten Perioden und irgenwie bin ich ratlos.

von Andreas N. (orangehead)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> hast du das schon durchgearbeitet?
> https://github.com/MicrochipTech/TB3213_Getting_Started_with_RTC

Kapitel PIT, JUP!
Kapitel Periodic Interrup, JUP!
Kapitel Overflow Interrupt, hakt irgendwie...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

kannst du das "hakt irgendwie..." näher beschreiben?

Ich habe mir ein Bsp. genommen und etwas umgebaut. Die RTC triggert mit 
seinem Overflow den ADC mittels Event jede Sekunde, der macht eine 
Messung und in der Hauptschleife wird nur auf die Fertigmeldung des ADC 
gewartet. Das RTC Overflow funktioniert demzufolge. Im ADC Interrupt 
könnte man die RTC jedesmal stoppen und neu konfigurieren o.ä.
1
/*
2
  Programmed and tested with: Arduino IDE 1.8.19
3
  Board: Arduino Nano Every
4
  Registers Emulation: None
5
*/
6
7
#include <avr/interrupt.h>
8
#include <util/atomic.h>
9
10
struct ADC
11
{
12
  volatile unsigned int reading;
13
  volatile bool finish;
14
};
15
ADC adc0;
16
17
void setup()
18
{
19
  Serial.begin(250000);
20
  Serial.println(F("\nReset ### ###"));
21
  
22
  initRTC();
23
  initADC0();   // Pin 21 / PD5
24
  initEventSystem();
25
}
26
27
void loop()
28
{
29
  readingADC0();
30
}
31
32
void readingADC0 (void)
33
{
34
  if (adc0.finish)
35
  {
36
    unsigned int var {0};
37
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
38
    {
39
      var = adc0.reading;
40
    }
41
    Serial.print("ADC0: "); Serial.print(var); Serial.print(" Digits");
42
    // wenn man davon ausgeht das Ub 5.0V sind ...
43
    const unsigned long voltage = 5000UL * var / 1024;
44
    Serial.print(" = "); Serial.print(voltage); Serial.println("mV");
45
    adc0.finish = false;
46
  }
47
}
48
49
void initADC0(void)
50
{
51
  ADC0.MUXPOS  = ADC_MUXPOS_AIN5_gc;    // select ADC0 input channel -->> Pin 21 / PD5
52
  ADC0.EVCTRL  = ADC_STARTEI_bm;        // enable event triggered conversion
53
  ADC0.INTCTRL = ADC_RESRDY_bm;         // enable 'ready' interrupt
54
}
55
56
void initRTC (void)
57
{  
58
  while (RTC.STATUS > 0) { ; }          // wait for all register to be synchronized
59
  RTC.CLKSEL = RTC_CLKSEL_INT32K_gc;    // period duration 30,518µs
60
  RTC.PER = 64;                         // set period -->> 1000ms / 15,625ms
61
  
62
  // RTC.CTRLA Bug, see Errata
63
  byte temp {0};
64
  temp |= RTC_PRESCALER_DIV512_gc;      // prescaler 512 -->> 30,518µs * 512 = 15,625ms
65
  temp |= RTC_RTCEN_bm;                 // RTC enable  
66
  RTC.CTRLA = temp;
67
}
68
69
void initEventSystem(void)
70
{
71
  EVSYS.CHANNEL7 = EVSYS_GENERATOR_RTC_OVF_gc;    // connect 'RTC OVF' to channel 7
72
  EVSYS.USERADC0 = EVSYS_CHANNEL_CHANNEL7_gc;     // connect generator 'RTC OVF' with user 'ADC0' via channel 7
73
}
74
75
ISR(ADC0_RESRDY_vect)
76
{
77
  adc0.reading = ADC0.RES;
78
  adc0.finish = true;
79
  ADC0.INTFLAGS = ADC_RESRDY_bm;          // clear result ready interrupt flag
80
}

von S. Landolt (Gast)


Lesenswert?

Hier ein Muster (Programmausschnitt, in Assembler), wie ich mir den 
Ablauf vorstelle: lange Pause durch LED an C0 dargestellt, blinkt 4 s 
an, 4 s aus; Messphase durch LED an A7 dargestellt, leuchtet zu Beginn 
jeder Pause für 1 s.
1
    sei
2
main_loop:
3
    puti    RTC_PITCTRLA,0b0_1001_00_1      ; /1024, en
4
    sbi     VPORTC_OUT,0                    ; an
5
    sbi     VPORTA_OUT,7                    ; an
6
    sleep
7
    cbi     VPORTA_OUT,7                    ; aus
8
    puti    RTC_PITCTRLA,0b0_1011_00_1      ; /4096, en
9
    sleep
10
    cbi     VPORTC_OUT,0                    ; aus
11
    sleep
12
  rjmp      main_loop

von Georg M. (g_m)


Lesenswert?

Andreas N. schrieb:
> Während den 20 minütigen Schlafphasen
> nutze ich den PIT (Periodic interrupt timer)

Beim PIT beträgt die längste Periode nur 32s. Der RTC kann wesentlich 
längere Perioden generieren.

Hier ist ein Beispielcode (getestet am ATtiny402)
Periodendauer:  ca. 10 min
LED-Leuchtdauer: ca. 0.5s
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
5
ISR(RTC_CNT_vect) 
6
{
7
  if(RTC.INTFLAGS & RTC_OVF_bm)
8
  {
9
    PORTA.OUTSET = PIN3_bm;               // LED on
10
    RTC.INTFLAGS = RTC_OVF_bm;            // clear RTC OVF interrupt flag
11
  }
12
  else if(RTC.INTFLAGS & RTC_CMP_bm)
13
  {
14
    PORTA.OUTCLR = PIN3_bm;               // LED off
15
    RTC.INTFLAGS = RTC_CMP_bm;            // clear RTC CMP interrupt flag
16
  }
17
}
18
19
int main(void)
20
{
21
  PORTA.DIRSET = PIN3_bm;                       // PA3 output (LED)
22
23
  RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;             // 1024 Hz from OSCULP32K
24
  while (RTC.STATUS > 0) {}                     // wait for all register to be synchronized
25
  RTC.INTCTRL = RTC_OVF_bm | RTC_CMP_bm;        // enable RTC OVF and CMP interrupts
26
  RTC.PER = 0x4aff;                             // RTC period (10 min)
27
  RTC.CMP = 0x000f;                             // RTC compare value (500 ms)
28
  RTC.CTRLA = RTC_RUNSTDBY_bm | RTC_PRESCALER_DIV32_gc | RTC_RTCEN_bm;  // enable Run In Standby, RTC clock/32 (32Hz), enable RTC
29
30
  SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc;       // select STANDBY sleep mode
31
  sei();                                        // enable Global Interrupts
32
33
  while(1)
34
  {
35
    sleep_mode();
36
  }
37
}


Ob die Zeile
1
while (RTC.STATUS > 0) {}              // wait for all register to be synchronized
wirklich notwendig ist, kann ich nicht sagen, weiß ich selbst nicht.

von Andreas N. (orangehead)


Lesenswert?

Hallo Georg Danke dir!!
Ich spiel grad mit deinem Programm und hab versucht den unterschied 
zwischen RTC_OVF und RTC_CMP zu verstehen... im prinzip dasselbe nur 
dass OVF den RTC zurücksetzt und CMP nicht.
Das würde heissen:

-OVF
-ds18b20 start reading
-sleep
-CMP
-ds18b20 read sensor und send
-sleep

Morgen versuch ich mal ob das geht

von Andreas N. (orangehead)


Lesenswert?

Das ding läuft!! und ich habs gleich mit PIT kombiniert und einen 
ds18b20 eingebaut. Da ist noch einiges nicht schön aufgeräumt aber es 
tut grad was es soll

Danke Danke an euch alle
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/sleep.h>
4
#include <OneWire.h>
5
6
ISR(RTC_CNT_vect)
7
{
8
  if(RTC.INTFLAGS & RTC_OVF_bm)
9
  {
10
    //PORTB.OUTSET = PIN5_bm;               // LED on
11
    PORTB.OUTTGL = PIN5_bm;
12
    RTC.INTFLAGS = RTC_OVF_bm;            // clear RTC OVF interrupt flag
13
14
  }
15
16
  else if(RTC.INTFLAGS & RTC_CMP_bm)
17
18
  {
19
20
    //PORTB.OUTCLR = PIN5_bm;               // LED off
21
    PORTB.OUTTGL = PIN5_bm;
22
23
    RTC.INTFLAGS = RTC_CMP_bm;            // clear RTC CMP interrupt flag
24
25
  }
26
27
}
28
29
ISR(RTC_PIT_vect)
30
{
31
  /* Clear flag by writing '1': */
32
  RTC.PITINTFLAGS = RTC_PI_bm;
33
}
34
35
void setup()
36
{
37
  Serial.begin(250000);
38
  
39
  
40
PORTB.DIRSET = PIN5_bm;                       // PA3 output (LED)
41
PORTB.OUTSET = PIN5_bm;
42
43
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc;             // 1024 Hz from OSCULP32K
44
45
while (RTC.STATUS > 0) {}                     // wait for all register to be synchronized
46
47
///////////PIT
48
RTC.PITINTCTRL = RTC_PI_bm; /* Periodic Interrupt: enabled */
49
RTC.PITCTRLA = RTC_PERIOD_CYC8192_gc | RTC_PITEN_bm; /* Enable: enabled */
50
51
while (RTC.PITSTATUS > 0) {
52
  ;
53
}
54
///////////PIT end
55
56
RTC.INTCTRL = RTC_OVF_bm;// |RTC_CMP_bm;        // enable RTC OVF and CMP interrupts
57
58
//RTC.PER = 0x4aff;                             // RTC period (10 min)
59
RTC.PER = 0x0020;
60
//RTC.CMP = 0x000f;                             // RTC compare value (500 ms)
61
62
RTC.CTRLA = RTC_RUNSTDBY_bm | RTC_PRESCALER_DIV32_gc | RTC_RTCEN_bm;  // enable Run In Standby, RTC clock/32 (32Hz), enable RTC
63
64
sei();                                        // enable Global Interrupts
65
}
66
67
void loop()
68
{
69
  
70
  startConversion(2);
71
  SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc;
72
  sleep_mode();
73
  Serial.println("OVF wake");
74
  float temp = getTemperature(2);
75
  Serial.println(temp);
76
  SLPCTRL.CTRLA = SLPCTRL_SMODE_PDOWN_gc;
77
  sleep_mode();
78
  Serial.println("PIT wake");
79
}
80
81
82
void startConversion(uint8_t _pin){
83
  OneWire ds(_pin);
84
  //Tx  Master Read ROM address
85
  ds.reset();
86
  
87
  //Tx  "Skip ROM" command vor "temp read" command um ohne adresse messung zu starten
88
  ds.write(0xCC);
89
90
  //Tx  sending Temp reading command
91
  ds.write(0x44, 1);
92
}
93
94
float getTemperature(uint8_t _pin) {
95
  OneWire ds(_pin);
96
  byte data[9];
97
  bool present1 = 0;
98
  bool present2 = 0;
99
100
  //Tx  Master Reset
101
  ds.reset();
102
  
103
  //Tx  "Skip ROM" command vor "read scratchpad" command um ohne adresse das scratchpad zu lesen
104
  ds.write(0xCC);
105
106
  //Rx read Scratchpad command
107
  ds.write(0xBE);
108
109
  //Rx read 9 bytes
110
  for (int i = 0; i < 9; i++) {
111
    data[i] = ds.read();
112
  }
113
    
114
  int16_t convert = (data[1] << 8) | data[0];
115
  float convertToFloat = ((float)convert) / 16;
116
  
117
  return  convertToFloat;  
118
}

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.