Forum: Mikrocontroller und Digitale Elektronik I2C Daten im Intervall Abrufen


von Patrick (Gast)


Lesenswert?

Hallo,

ich habe ein Problem mit mein Kleinen Projekt.

Dabei soll jede Sekunde der Aktuelle Strom von einem INA219 abgerufen 
werden, und per Serielle Schnittstelle ausgegeben werden.

Jedoch sobald ich die Lese Routine über die ISR ausführen möchte hängt 
sich der Controller auf.

Lasse ich die Routine im Loop laufen funktioniert sie Fehlerfrei. Auch 
wenn ich zusätzlich in der ISR was anderes mache läuft der LOOP und die 
ISR problemlos.

Kann mir jemand dabei helfen.
1
#include <Wire.h>
2
3
#define Add_Current0 0x40
4
#define Add_Current1 0x41
5
6
#define R_Shunt0 0.1
7
#define R_Shunt1 0.1
8
9
#define Acc0 0.0001   // maximum = max Current/2^15
10
#define Acc1 0.0001
11
12
float data = 0;
13
void setup() 
14
{
15
  Wire.begin();
16
  Serial.begin(250000);
17
 
18
  current_write_Config(Add_Current0,0x399F);
19
  current_write_Config(Add_Current1,0x399F);
20
21
  current_write_Calibration(Add_Current0,R_Shunt0,Acc0);
22
  current_write_Calibration(Add_Current1,R_Shunt1,Acc1);
23
24
  Timer_Setup();
25
 
26
}
27
void loop() 
28
{
29
    
30
    
31
32
}
33
34
ISR(TIMER1_COMPA_vect)
35
{
36
37
  data = current_get_Current (Add_Current1,Acc1);
38
    Serial.println(data,4);
39
  
40
}
41
42
void current_write_Config (byte Addr, short data)
43
{
44
  
45
  Wire.beginTransmission(Addr);
46
  Wire.write(byte(0x00));
47
  byte transmit = data >> 8;
48
  Wire.write(transmit);
49
  
50
  transmit = data;
51
  Wire.write(transmit);
52
  
53
  Wire.endTransmission();
54
  
55
}
56
57
void Timer_Setup()
58
{
59
  TCCR1A = 0;
60
  TCCR1B = 0;
61
  OCR1A = 62500; //1Hz
62
  TCCR1B |= (1<<WGM12)| (1<<CS12);
63
  
64
  TIMSK1 |= (1<<OCIE1A);
65
    
66
}
67
void current_write_Calibration (byte Addr, float shunt, float acc)
68
{
69
  
70
  Wire.beginTransmission(Addr);
71
  Wire.write(byte(0x05));
72
  short data = trunc(0.04096/(acc*shunt));
73
  
74
  byte transmit = data >> 8;
75
  Wire.write(transmit);
76
  
77
  transmit = data;
78
  Wire.write(transmit);
79
  
80
  Wire.endTransmission();
81
  
82
}
83
84
float current_get_Current (byte Addr,float acc)
85
{
86
  
87
  Wire.beginTransmission(Addr); 
88
  Wire.write(byte(0x04));      
89
  Wire.endTransmission(); 
90
  short reading = 0;
91
92
   Wire.requestFrom(Addr, 2);
93
  
94
  if (2 <= Wire.available()) { // if two bytes were received
95
   reading = Wire.read();  // receive high byte (overwrites previous reading)
96
   reading = reading << 8;  // shift high byte to be high 8 bits
97
   reading |= Wire.read(); // receive low byte as lower 8 bits
98
  }
99
   
100
   float current = reading *acc ;
101
   
102
   return(current);
103
}

von leo (Gast)


Lesenswert?

Patrick schrieb:
> ISR(TIMER1_COMPA_vect)
> {
>   data = current_get_Current (Add_Current1,Acc1);
>     Serial.println(data,4);

Mindestens 2 Fehler:
- data muss volatile sein
- Serial.println in einer ISR ist verboten

leo

von Jens W. (jensw)


Lesenswert?

wenn du die einfach in der Loop laufen lässt, wie schnell kommen denn 
dann die Werte?
Viel schneller als 1s oder langsamer?
Und wie ist es mit dem Timer?
Läuft der immer weiter? Oder muss der in der ISR einfach neu gestartet 
werden?
Sprich, ließt er einmal die Werte aus und stoppt dann? (Weil der Timer 
stehen geblieben ist).

Grüße, Jens

von Patrick (Gast)


Lesenswert?

Also er blibt im Timer hängen.

Wenn ich dort nur mir die millis anzeigen lasse kommen die im Sekunden 
Takt.

das eine Print Funktion nix in der ISR zu suchen hat ist mir klar, war 
jetzt nur zum debuggen.

Die print Funktion soll später im Loop und nur ausgeführt werden, wenn 
ich neue Daten über die i2C empfangen habe.

von Patrick (Gast)


Lesenswert?

hab den Datentyp geändert mit "volatile" geändert.

jedoch bleibt das Programm weiter vor der Print Funktion hängen, so das 
diese nicht ausgeführt wird.

von Patrick (Gast)


Lesenswert?

Hab es auch mal so versucht, auch hier wird die Print funktion nicht 
ausgeführt.

Die unterprogramme welche unverändert sind habe ich mal jetzt nicht 
mitgesendet.
1
#include <Wire.h>
2
3
#define Add_Current0 0x40
4
#define Add_Current1 0x41
5
6
#define R_Shunt0 0.1
7
#define R_Shunt1 0.1
8
9
#define Acc0 0.0001   // maximum = max Current/2^15
10
#define Acc1 0.0001
11
12
volatile float data = 0;
13
volatile byte x = 0;
14
void setup() 
15
{
16
  Wire.begin();
17
  Serial.begin(250000);
18
 
19
  current_write_Config(Add_Current0,0x399F);
20
  current_write_Config(Add_Current1,0x399F);
21
22
  current_write_Calibration(Add_Current0,R_Shunt0,Acc0);
23
  current_write_Calibration(Add_Current1,R_Shunt1,Acc1);
24
25
  Timer_Setup();
26
 
27
}
28
void loop() 
29
{
30
    
31
    if (x==1)
32
    {
33
      Serial.println(data,4);
34
      x=0;
35
    }
36
37
}
38
39
ISR(TIMER1_COMPA_vect)
40
{
41
42
  data = current_get_Current (Add_Current1,Acc1);
43
  x=1;  
44
  
45
}

von Patrick (Gast)


Lesenswert?

hab des rätsels Lösung.

Die Wire.h benötigt ebenfalls Interrupt befehle welche in der ISR 
deaktiviert sind.
habe jetzt zu beginn der ISR die Interrupts per sei(); aktiviert und es 
läuft.

keine schöne lösung, funktioniert aber schonmal.

falls jemand ein eleganteren vorschlag hat gerne her damit.

von MinMax (Gast)


Lesenswert?

Weshalb muss die Abfrage der Daten in der ISR stattfinden? Setze in der 
ISR einfach ein Flag und werte es in der Loop aus.

Und überhaupt, weshalb für diesen Zweck einen Timer Interrupt? Bei einer 
Periode von 1 Sekunde kann das auch ein millis() Vergleich in der Loop 
erledigen.

von Patrick (Gast)


Lesenswert?

wichtig ist, das der Daten abruf im sekunden intervall passiert. Da noch 
einiges mehr im Code später passiert, dann es sein das ein kompletter 
durchlauf länger dauert als eine sekunde.

von leo (Gast)


Lesenswert?

Patrick schrieb:
> wichtig ist, das der Daten abruf im sekunden intervall passiert. Da noch
> einiges mehr im Code später passiert,

Verwende einen Taskmanager statt der ISR, e.g.
https://github.com/prampec/arduino-softtimer
https://github.com/arkhipenko/TaskScheduler
usw.

leo

von Michael M. (michael89)


Lesenswert?

Hallo,

Nimm doch die Struktur aus dem Arduino Beispiel "blink without delay".

unsigned long millis_1s =0;

void loop(){

 if( millis() - millis_1s >= 1000){
 millis_1s+=1000;

 // Hier was du jede Sekunde ausgeführt haben willst

 }

 // Restlicher Code
}

Gruß

von Delay me not (Gast)


Lesenswert?

Michael V. schrieb:
> Nimm doch die Struktur aus dem Arduino Beispiel "blink without delay".

Er hat in seiner loop() noch andere Programmteile, die länger als 1 
Sekunde laufen können (vermutlich wegen großzügigem Delay-Einsatz)

---> loop umstrukturieren, dass ein durchlauf wieder nur eine sache von 
wenigen msec ist.

Eine Statemachine kann dabei helfen.

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.