Forum: Mikrocontroller und Digitale Elektronik Variable gelöscht - PCINT bei ATTINY85


von felix (Gast)


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

von Regeln Erklehrbehr (Gast)


Angehängte Dateien:

Lesenswert?

felix schrieb:
> Danke vielmals für die Hilfe schon im Voraus

Wir danken schon mal dafür dass du - wenn du es tust - die
Regeln die für jeden gut einsehbar sind, einhältst.

von Karl M. (Gast)


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..

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


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>.

von achtundsechziger (Gast)


Lesenswert?

Sieht nach fehlendem 'volatile' aus.

von Sebastian R. (sebastian_r569)


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.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

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

Nö.

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


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.

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.