Variable gelöscht - PCINT bei ATTINY85

Gast #5620738
Lesenswert?

Hallo, ich verwende folgende "Infrastruktur":
IDE: Atmel Studio 7
Programmer: AVR Dragon
Schnittstelle: Debug Wire
µC: ATtiny 85

Vorgang:
Ich verwende auf meinem Attiny Software-Uart und empfange bit für bit in 
einer PCINT-Interrupt routine, der immer dann triggert, sobald bei 
meinem "PIN_UART_RX" (PB0) eine Pegeländerung stattfindet. Dann empfange 
ich einen 9 bit UART-Frame (im Multi Processor Communication Mode).
PS: Ich verwende in meiner ISR delays und bitte darum, sich nicht in 
diese tatsache "hineinzusteigern". Die Verwendung von delays in meiner 
ISR sind für meine Anwendung nicht ausschlaggebend.


Problem:
Jedes mal beim verlassen der ISR, also beim Aufruf des einprogrammierten 
"return" wird meine 16-bit variable "received_char"
empfangen...und sie wird auch richtig empfangen... d.h. der vom 
Transmitter gesendete 9 bit Frame kommt bei meinem Attiny 85 richtig an 
und wird richtig in die variable "received_char" geschrieben.
Das Problem tritt beim Verlassen der ISR auf. Sobald ich das eingebaute 
"return" in der ISR erreiche, wird meine Variable auf "0" gesetzt und 
ich kann das Empfangene nicht mehr interpretieren....Weiß wer warum ?? 
Danke vielmals für die Hilfe schon im Voraus
1
// -------------------------------------------------------------
2
/// \file main.c
3

4
#include <avr/io.h>
5
#include "Communication.h"
6
#include "SlaveConfig.h"
7
#include <util/delay.h>
8
#include <avr/interrupt.h>
9
#include "SlaveFunctions.h"
10
#include <stdio.h>
11

12
uint16_t received_char = 0;
13
uint8_t bit_cnt_receiver = 0;
14
uint8_t address_ok = 0;
15
uint8_t command = 0;
16
uint8_t frame_type = 0; //Flag: 0 = DatenFrame, 1 = Adressframe
17
uint8_t device_addressed = 0;
18
uint16_t ad_value = 0;
19
uint8_t ad_pin_check = 0; //Flag: 0 = falscher PIN eingestellt, 1 = richtiger PIN eingestellt
20

21
#define LED PORTB |= (1<<PINB2);
22

23
//--------------------------------------------------------------
24
/// Worker Routine of the Slave
25
///
26
///
27
int main(void)
28
{
29
DDRB |= (1<<PINB2);
30

31
  initHardware();    //initialize Hardware Configuration
32
  sei();        //Global Interrupts enabled
33
    while (1) 
34
    {    
35
    
36
    
37
     checkIfAddress(); //Es wird kontrolliert, ob der Frame ein AdressFrame ist
38
 
39
     if(device_addressed) //Es wird kontrolliert, ob meine Adresse gemeint ist
40
     {
41
       processCommand(); //Kommandos werden ausgeführt
42
     }
43

44
  }
45

46
  return 0;
47
}
48

49
//--------------------------------------------------------------
50
/// Address recognition routine for 9 bit data UART address frame
51
///
52
///
53
void checkIfAddress(void)
54
{
55

56
   frame_type = ((received_char & 0b0000000100000000) >> 8); //wenn 9tes bit (msb) = 1, dann handelt es sich um einen Address-Frame
57
 
58
   if(frame_type == ADDRESS_FRAME)
59
   {
60
  LED
61
     if((received_char & 0x00FF) == MY_SLAVE_ADDRESS)
62
     {
63
       device_addressed = 1;
64
      
65
     }
66
 
67
     if((received_char & 0x00FF) != MY_SLAVE_ADDRESS)
68
     {
69
       device_addressed = 0;
70
     }
71
     
72
   }
73
  
74
}
75

76
//--------------------------------------------------------------
77
/// Command execution routine
78
///
79
///
80
void processCommand(void)
81
{
82

83
  command = (uint8_t)received_char;
84
  
85
  switch(command)
86
  {
87
    case GET_VOLTAGE:
88
      { 
89
        getVoltage();
90
      }
91
    break;
92

93
    case GET_TEMPERATURE:
94
      {
95
        getTemperature();        
96
      } 
97
    break;
98
  }
99

100
  command = 0;
101
}
102

103
//--------------------------------------------------------------
104
/// Hardware initialization routine for software UART
105
///
106
///
107
void initHardware(void)
108
{
109
  DDRB |= (1<<PIN_UART_TX); //PIN PIN_UART_TX = Output
110
  PORTB |= (1<<PIN_UART_TX); //IDLE-STATE = HIGH
111

112
  DDRB &= ~(1<<PIN_UART_RX); //PIN PIN_UART_RX = Input
113

114
  GIMSK |= (1<<PCIE); //PinChangeInterrupt für PIN_UART_RX ist aktiv
115
  PCMSK |= (1<<PIN_UART_RX); //Pin Change enabled
116

117
  ADCSRA |= (1<<ADEN); //AD Enable
118
  ADCSRA |= (1<<ADIE); //AD Interrupt Enable
119

120
  //Check, ob einer oder beide der AD_PINS auf einen Eingang gelegt wurden, der kein AD ist.
121
  if (!((PIN_AD_VOLT >= PB2 && PIN_AD_VOLT <= PB5) || ((PIN_AD_TEMP >= PB2 && PIN_AD_TEMP <= PB5))))
122
  {
123
    ad_pin_check = 0; //Wrong ad pin
124
  }
125
}
126

127
//--------------------------------------------------------------
128
/// Software UART send routine for 8 bit data UART data frame
129
///
130
///
131
void sendChar(uint8_t chr)  //Character senden
132
{
133
  cli();
134
  uint8_t bit_cnt;  //bit counter -> um die einzelnen bits zu vergleichen
135

136
  PORTB &= ~(1<<PIN_UART_TX); //Start-Bit
137
  _delay_us(ONE_BIT); //Warten von 104 µS
138

139
  for(bit_cnt = 0; bit_cnt < 8; bit_cnt++) //Für die Länge eines 8 bit Frames
140
  {
141
    //Wenn aktuelles bit = 1 ist
142
    if(chr & (1 << bit_cnt)) 
143
    {
144
      PORTB |= (1<<PIN_UART_TX); //PIN_UART_TX = HIGH
145
    }
146

147
    //Wenn aktuelles bit = 0 ist
148
    else
149
    {
150
      PORTB &= ~(1<<PIN_UART_TX); //PIN_UART_TX = LOW
151
    }
152

153
    _delay_us(ONE_BIT); //Warten von 104 µS
154
  }
155

156
  PORTB |= (1<<PIN_UART_TX); //Stop-Bit
157
  _delay_us(ONE_BIT); //Warten von 104 µS
158
  sei();
159
}
160

161
void getTemperature(void)
162
{
163
  setADChannel(PIN_AD_TEMP);
164
  startADConversion();
165
  sendADValueToMaster();
166
}
167

168
void getVoltage(void)
169
{
170
  setADChannel(PIN_AD_VOLT);
171
  startADConversion();
172
  sendADValueToMaster();
173
}
174

175

176
void setADChannel(uint8_t ad_pin)
177
{
178

179
  if(ad_pin_check) //Flag, das nur beim initialisieren gesetzt wird
180
  {
181
    ADMUX &= ~((1<<MUX1)|(1<<MUX0)); //Clear the AD/PIN selection
182

183
    switch(ad_pin)
184
    {
185
      case PINB2:
186
      ADMUX |= (1<<MUX0); //ADC1
187
      break;
188

189
      case PINB3:
190
      ADMUX |= (1<<MUX1) | (1<<MUX0); //ADC3
191
      break;
192

193
      case PINB4:
194
      ADMUX |= (1<<MUX1); //ADC2
195
      break;
196

197
      case PINB5:
198
      //ADC0
199
      break;
200
    }
201
  }
202
}
203

204

205
void startADConversion(void)
206
{
207
  ADCSRA |= (1<<ADSC); //AD/Wandlung im Single Conversion Mode starten
208
  while((ADCSRA & (1 << ADIF)) != 1); //Hänge, solange Wert nicht gelesen wurde...
209
}
210

211

212
void sendADValueToMaster(void)
213
{
214
  sendChar(ad_value >> 8);      //Oberen 2 Bits werden gesendet (Rest wird mit 0 aufgefüllt)
215
  _delay_us(10);            //Sicherheitshalber, um negative timing-fehler wieder auszugleichen
216
  sendChar(ad_value);          //Unteren 8 bits werden gesendet
217
}
218

219
//--------------------------------------------------------------
220
/// Interrupt routine for completed Analog to Digital conversion (10 bit)
221
///
222
///
223
ISR(ADC_vect)
224
{
225
  ad_value = ADC;
226
}
227

228
//--------------------------------------------------------------
229
/// Interrupt routine for PIN-Change for receiving 11 bits (Software-Uart-Receiving) (-> Command + Adress)
230
///
231
///
232
ISR(PCINT0_vect)
233
{
234
received_char = 0;
235
  cli();
236

237
  for(bit_cnt_receiver = 0; bit_cnt_receiver < 11; bit_cnt_receiver++) //von 0 bis 10 = 1 Frame
238
  {
239
    if (bit_cnt_receiver == 0) //Startbit wird empfangen (+5V)
240
    {
241
      _delay_us(HALF_BIT);
242
    }
243
    
244
    if(bit_cnt_receiver > 0 && bit_cnt_receiver < 10)
245
    {
246

247
      if(PINB & (1<<PIN_UART_RX)) //Wenn PinB0 einen High-Pegel empfängt
248
      {
249
        received_char |= (1 << (bit_cnt_receiver - 1)); //LSB wird zuerst empfangen (-1, um vorhergehendes "startbit" zu eliminieren)
250
      }
251

252
      else if (PINB & ~(1<<PIN_UART_RX)) //Wenn PinB0 einen Low-Pegel empfängt
253
      {
254
        received_char &= ~(1 << (bit_cnt_receiver - 1)); //LSB wird zuerst empfangen (-1, um vorhergehendes "startbit" zu eliminieren)
255
      }
256
    }
257

258
    if (bit_cnt_receiver > 9) //Stopbit wird empfangen (0V)
259
    {
260
      bit_cnt_receiver = 0;
261
      _delay_us(HALF_BIT); //Halbes Bit wird abgewartet, damit Frame pünktlich beendet wird
262
      sei();
263
      return;
264
    }
265

266
    _delay_us(ONE_BIT);
267
  }
268

269
}
270
   
271

272

273
/// @} //end group slave
Gast #5620745
Lesenswert?

Hallo,

da machst Du entscheidene Fehler in der ISR(PCINT0_vect);

Nur die fallende Flanke des Startbits triggert einen Interrupt.

Danach, wird über einen Timer für das erste Bit nach 1,5* BIT-Zeit und 
dann
alle 1.0* BIT-Zeit der RX-Eingang eingelesen.

Somit liegt jeder Bit-Sample ~ in der Mitte des seriellen Datenbits.
Nichts mit warten und blockierend!

Der gesamte Empfangsprozess läuft ohne Wartezeiten ab!

Wurde das Stopp-Bit gesampelt, wird auch wieder der Interrupt für 
fallende Flanke des Startbits aktiviert..
Moderator Persönliche Seite #5620747
Lesenswert?

Deine Variable ist OK, aber du änderst sie in der ISR und möchtest sie 
woanders abfragen => typischer Fall für einen notwendigen 
"volatile"-Qualfier.  Ansonsten weiß der Compiler nicht, dass sich die 
Variable außerhalb des regulären Programmflusses (nämlich in der ISR) 
ändern kann, und fragt die entsprechende Speicherstelle nicht neu ab.

"volatile" und "uint16_t" braucht außerdem noch eine "atomic operation"; 
siehe die Dokumentation zum Headerfile <utils/atomic.h>.
#5620749
Lesenswert?

Setz die Variablen, die du im ISR und in der Main benutzt, mal auf 
volatile.

- Main arbeitet mit der Variable.
- Ein Interrupt tritt auf.
- Alle Variablen werden auf dem Stack gesichert
- Das Interrupt verändert die Variable
- Die gesicherten Variablen werden zurückgeholt

Dementsprechend hat das ISR keine Möglichkeit, die Variable fürs main zu 
verändern. Das Volatile verhindert, dass Variablen auf dem Stack 
gesichert werden.
Moderator Persönliche Seite #5621160
Lesenswert?

Markus F. schrieb:
> Sebastian R. schrieb:
>> Das Volatile verhindert, dass Variablen auf dem Stack
>> gesichert werden.
>
> Nö.

Macht er nicht, war aber wohl eher ungeschickt ausgedrückt (oder nicht 
völlig verstanden).

Ohne "volatile" liest der Compiler den Inhalt der Variablen nur einmal, 
danach kann er alles in einem Register zwischenspeichern, denn es kann 
sich aus seiner Sicht nichts ändern. Ein Interrupt muss notwendigerweise 
alle Register auf dem Stack sichern, die er selbst modifiziert. Nach der 
Rückkehr aus dem Interrupt werden die Register zurückgespeichert, sodass 
das unterbrochene Programm nach wie vor mit den im Register gehaltenen 
Wert weiter arbeitet.

Mit "volatile" zwingt man den Compiler, für jeden Zugriff auf die 
entsprechende Variable auf die zugehörige Speicherstelle zuzugreifen.

Aber s.o., "volatile" ist bei einer 16-Bit-Variablen auf einer 
8-Bit-Architektur nur die halbe Miete. Die andere Hälfte besteht darin, 
den 16-Bit-Zugriff atomar zu gestalten, damit zwischen den notwendigen 
8-Bit-Teilzugriffen kein Interrupt "dazwischen funken" kann.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren