Forum: Mikrocontroller und Digitale Elektronik AVR Programmierung C++


von Anonymous U. (gastt)


Lesenswert?

Ich will mit einem Atmega48 eine serielle Kommunikation mit einem 
RingBuffer programmieren. Objektorientierung erschien mir ganz 
praktisch. Leider habe ich nicht besonders viel Ahnung von C++. Bei 
meinem Code bringt der Compiler diese Fehlermeldung: "'TxBuffer' was not 
declared in this scope". Aber warum? TxBuffer wurde doch in der Klasse 
angegeben?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
class cSerialComm
5
{
6
  public:
7
    // variables
8
    volatile cRingBuffer TxBuffer;
9
    // constructor -> setup
10
    cSerialComm();
11
    // write string in tx buffer
12
    // returns -1 if buffer overflow
13
    // max string length: 255
14
    int SendString(char* data);
15
};
16
17
cSerialComm::cSerialComm()
18
{
19
  //UCSR0A = 
20
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
21
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
22
  UBRR0 = 47; // baud rate: 115200 kBit/s
23
}
24
25
int cSerialComm::SendString(char* data)
26
{
27
  // write in tx buffer
28
  if( TxBuffer.WriteString(data) ) return -1;
29
  // initiate serial sending
30
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) UDR0 = TxBuffer.ReadChar();
31
  return 0;
32
}
33
34
// Interrupt Service Routines
35
  // Serial: Received Char
36
  ISR(USART_RX_vect)
37
  {
38
    return;
39
  }
40
  // Serial: Finished Sending Char
41
  ISR(USART_TX_vect)
42
  {
43
    if(SerialComm.TxBuffer.RdIndex!=SerialComm.TxBuffer.WrIndex) UDR0 = SerialComm.TxBuffer.ReadChar();
44
    return;
45
  }

[Mod: Formatierung korrigiert]

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Anonymous U. schrieb:
> TxBuffer wurde doch in der Klasse angegeben?

Nein, nicht wirklich; denn cRingBuffer wurde nie definiert, d.h. 
TxBuffer hat keinen bekannten Typ.  Das ist wie
1
blabla_t wert;
ohne vorher je gesagt zu haben, was blabla_t sein soll.

von Peter (Gast)


Lesenswert?

In dem gezeigten Code wird nirgends ein Objekt SerialComm angelegt, auf 
das Du in der ISR zugreifst.

Ansonsten poste mal den Originalcode als Anhang und die vollständige 
Fehlermeldung inkl. Zeilennummer, in der der Fehler auftritt.

von Anonymous U. (gastt)


Lesenswert?

Peter schrieb:
> In dem gezeigten Code wird nirgends ein Objekt SerialComm angelegt, auf
> das Du in der ISR zugreifst.

Das die ISR noch nicht funktioniert ist mir klar. Kann ich die irgendwie 
in die Klasse mit einbinden? Dazu muss diese aber ein Singleton sein? 
Wie mache ich das, mit static?


Johann L. schrieb:
> Nein, nicht wirklich; denn cRingBuffer wurde nie definiert, d.h.
> TxBuffer hat keinen bekannten Typ.  Das ist wie

Ah ich kommer der lösung näher. Ich habe nun cRingBuffer per header file 
eingebunden. Blame C# for that.

Hier mal mein Code bisher:
1
// DataStructs.cpp
2
3
#include <avr/io.h>
4
5
class cRingBuffer
6
{
7
  private:
8
  // variables
9
  #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
10
  char Buffer[32];
11
  
12
  public:
13
  // variables
14
  uint8_t RdIndex;
15
  uint8_t WrIndex;
16
  // constructor
17
  cRingBuffer()
18
  {
19
    RdIndex = 0;
20
    WrIndex = 0;
21
  }
22
  // write string in buffer
23
  // returns -1 if buffer overflow
24
  // max string length: 255
25
  int WriteString(char* data)
26
  {
27
    // loop for char access in data string
28
    uint8_t i=0;
29
    while(*(data+i)!='\0')
30
    {
31
      Buffer[WrIndex] = *(data+i);
32
      WrIndex = BufferMask&(++WrIndex);
33
      // 1 unused byte in Buffer for speed optimization
34
      if(RdIndex==WrIndex) return -1;
35
    }
36
    return 0;
37
  }
38
  // read char from buffer
39
  char ReadChar()
40
  {
41
  // buffer empty?
42
  if(RdIndex == WrIndex) return '\0';
43
  // read next char
44
  char Temp = Buffer[RdIndex];
45
  RdIndex = BufferMask&(++RdIndex);
46
  return Temp;
47
  }
48
};
49
50
51
// DataStructs.h
52
53
#include <avr/io.h>
54
55
class cRingBuffer
56
{
57
  private:
58
  // variables
59
  #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
60
  char Buffer[32];
61
  public:
62
  // variables
63
  uint8_t RdIndex;
64
  uint8_t WrIndex;
65
  // constructor
66
  cRingBuffer();
67
  // write string in buffer
68
  // returns -1 if buffer overflow
69
  // max string length: 255
70
  int WriteString(char* data);
71
  // read char from buffer
72
  char ReadChar();
73
};
74
75
#endif /* DATASTRUCTS_H_ */
76
77
78
// SerialComm.cpp
79
80
#include <avr/io.h>
81
#include <avr/interrupt.h>
82
#include "DataStructs.h"
83
84
class cSerialComm
85
{
86
  public:
87
    // variables
88
    volatile cRingBuffer TxBuffer;
89
    // constructor -> setup
90
    cSerialComm();
91
    // write string in tx buffer
92
    // returns -1 if buffer overflow
93
    // max string length: 255
94
    int SendString(char* data);
95
};
96
97
cSerialComm::cSerialComm()
98
{
99
  //UCSR0A = 
100
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
101
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
102
  UBRR0 = 47; // baud rate: 115200 kBit/s
103
}
104
105
int cSerialComm::SendString(char* data)
106
{
107
  // write in tx buffer
108
  if( TxBuffer.WriteString(data) ) return -1;
109
  // initiate serial sending
110
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) UDR0 = TxBuffer.ReadChar();
111
  return 0;
112
}
113
114
// Interrupt Service Routines
115
  // Serial: Received Char
116
  ISR(USART_RX_vect)
117
  {
118
    return;
119
  }
120
  // Serial: Finished Sending Char
121
  ISR(USART_TX_vect)
122
  {
123
    if(SerialComm.TxBuffer.RdIndex!=SerialComm.TxBuffer.WrIndex) UDR0 = SerialComm.TxBuffer.ReadChar();
124
    return;
125
  }

von Stefan F. (Gast)


Lesenswert?

> ISR ... Kann ich die irgendwie in die Klasse mit einbinden?

Ich glaube das geht leider nicht.

von Peter (Gast)


Lesenswert?

Schon länger her, aber ich habe das immer so gemacht:

class Sercom
{
  ....
  OnReceiveMessage(char c);
};

//lokal Objekt der Klasse instanziieren
Sercom DevTtyS1;

//globalen Pointer auf Objekt für ISR
Sercom *gp_DevTtyS1 = DevTtyS1

void ISR(void)
{
  if ( gp_DevTtyS1 )
    gp_DevTtyS1->OnReceiveMessage(c);
}

von Anonymous U. (gastt)


Lesenswert?

Soo. Nun funktioniert mein Kot endlich. Ich denke das ich OOP vernünftig 
eingesetzt habe, soweit das kleine µC betrifft. Ich poste den mal hier, 
Meinung, Kritik und Verbesserungsvorschläge erwünscht.

Main File:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include "SerialComm.h"
4
5
// pin definitions
6
#if 1
7
  // Switch
8
  #define pin_sw        PINB0
9
  // PLL SPI Communication
10
  #define pin_SPI_pll_lo_En  PINB1
11
  #define pin_SPI_pll_sig_En  PINB2
12
  #define pin_SPI_pll_SDO    PINB3
13
  #define pin_SPI_pll_SDI    PINB4
14
  #define pin_SPI_pll_SCK    PINB5
15
  // Temperature Sensors
16
  #define pin_temp1      PINC0
17
  #define pin_temp2      PINC1
18
  // LED
19
  #define pin_led        PINC4
20
  // Fan
21
  #define pin_fan        PINC5
22
  // Serial Communication
23
  #define pin_serial_RXD    PIND0
24
  #define pin_serial_TXD    PIND1
25
  // RF Path Control
26
  #define pin_sw_G2      PIND2
27
  #define pin_Gain_sig_En    PINC2
28
  #define pin_Gain_lo_En    PIND6
29
  // ADC Control
30
  #define pin_ADC_CS      PINC3
31
  #define pin_ADC_EOC      PIND3
32
  #define pin_ADC_RD      PIND4
33
  #define pin_ADC_CONVST    PIND5
34
  #define pin_Raspi      PIND7
35
#endif
36
37
// global variables and objects
38
cSerialComm SerialComm;
39
struct sTime
40
{
41
  int ms;
42
  int s;
43
  int min;
44
  int blink_1s;
45
};
46
volatile struct sTime Time;
47
volatile int TemperatureState[4];
48
49
// Functions
50
void PinSetup()
51
{
52
  // Direction: High is Output
53
  DDRB = (1<<pin_SPI_pll_lo_En)|(1<<pin_SPI_pll_sig_En)|(1<<pin_SPI_pll_SDO)|(1<<pin_SPI_pll_SCK);
54
  DDRC = (1<<pin_Gain_sig_En)|(1<<pin_ADC_CS)|(1<<pin_led)|(1<<pin_fan);
55
  DDRD = (1<<pin_serial_TXD)|(1<<pin_sw_G2)|(1<<pin_ADC_RD)|(1<<pin_Gain_lo_En)|(1<<pin_Raspi);
56
  // Pullup/Output
57
  PORTB = (1<<pin_sw);
58
}
59
void TimerSetup()
60
{
61
  // Timer1 (16bit): Clock
62
  // Overflow Interrupt: 1ms
63
  // 11.0592MHz/11059
64
  TCCR1A = (1<<WGM11)|(1<<WGM10);
65
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS10);
66
  OCR1A = 11059;
67
  TIMSK1 = (1<<TOIE1);
68
}
69
void TempADCSetup()
70
{
71
  
72
}
73
74
int SwitchState()
75
{
76
  if(PINB&(1<<pin_sw)) return 0;
77
  return 1;
78
}
79
void SetLED(int state)
80
{
81
  if(state) PORTC|=(1<<pin_led);
82
  else PORTC&=~(1<<pin_led);
83
}
84
void SetFan(int state)
85
{
86
  if(state) PORTC|=(1<<pin_fan);
87
  else PORTC&=~(1<<pin_fan);
88
}
89
void SetGainSig(int state)
90
{
91
  if(state) PORTC |= (1<<pin_Gain_sig_En);
92
  else PORTC &=~(1<<pin_Gain_sig_En);
93
}
94
void SetGainLo(int state)
95
{
96
  if(state) PORTD |= (1<<pin_Gain_lo_En);
97
  else PORTD &=~(1<<pin_Gain_lo_En);
98
}
99
void PrintTemp()
100
{
101
  for(int i=0; i<4; i++)
102
  {
103
    int i=0;
104
  }
105
}
106
107
int main(void)
108
{
109
  // Setup
110
  PinSetup();
111
  SetFan(1);
112
  TimerSetup();
113
  TempADCSetup();
114
  sei();
115
    while(1)
116
    {
117
    // show device run state
118
    SetLED(Time.blink_1s);
119
    }
120
}
121
122
// Interrupt Service Routines
123
  // Timer1: 1ms
124
  ISR(TIMER1_OVF_vect)
125
  {
126
    ++Time.ms;
127
    if(Time.ms>=1000)
128
    {
129
      Time.ms = 0;
130
      ++Time.s;
131
      Time.blink_1s ^= 1;
132
      // Trigger Temp ADC Conversion
133
      SerialComm.SendString("test\n");
134
      if(Time.s>=60)
135
      {
136
        Time.s = 0;
137
        ++Time.min;
138
      }
139
    }
140
    return;
141
  }
142
  // Serial: Received Char
143
  ISR(USART_RX_vect)
144
  {
145
    return;
146
  }
147
  // Serial: Finished Sending Char
148
  ISR(USART_TX_vect)
149
  {
150
    char NextChar = SerialComm.TxBuffer.ReadChar();
151
    if(NextChar!='\0') UDR0 = NextChar;
152
    return;
153
  }

SerialComm.h:
1
#ifndef SERIALCOMM_H_
2
#define SERIALCOMM_H_
3
4
#include <avr/io.h>
5
#include <avr/interrupt.h>
6
#include "DataStructs.h"
7
8
class cSerialComm
9
{
10
  public:
11
    // variables
12
    cRingBuffer TxBuffer;
13
    // constructor -> setup
14
    cSerialComm();
15
    // write string in tx buffer
16
    // returns -1 if buffer overflow
17
    // max string length: 255
18
    int SendString(char* data);
19
};
20
21
#endif /* SERIALCOMM_H_ */

SerialComm.cpp:
1
#include "SerialComm.h"
2
3
cSerialComm::cSerialComm()
4
{
5
  //UCSR0A = 
6
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
7
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
8
  UBRR0 = 47; // baud rate: 115200 kBit/s
9
}
10
11
int cSerialComm::SendString(char* data)
12
{
13
  // has all data from buffer been sent?
14
  int SendActive=1;
15
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) SendActive=0;
16
  // write in tx buffer
17
  if( TxBuffer.WriteString(data) ) return -1;
18
  // initiate serial sending if required
19
  if(!SendActive) UDR0 = TxBuffer.ReadChar();
20
  return 0;
21
}

DataStructs.h:
1
#include <stdint.h>
2
3
class cRingBuffer
4
{
5
  private:
6
    // variables
7
    #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
8
    char Buffer[32];
9
  public:
10
    // variables
11
    uint8_t RdIndex;
12
    uint8_t WrIndex;
13
    // constructor
14
    cRingBuffer();
15
    // write string in buffer
16
    // returns -1 if buffer overflow
17
    // max string length: 255
18
    int WriteString(char* data);
19
    // read char from buffer
20
    char ReadChar();
21
};
22
23
#endif /* DATASTRUCTS_H_ */

DataStructs.cpp:
1
#include "DataStructs.h"
2
3
// constructor
4
cRingBuffer::cRingBuffer()
5
{
6
  RdIndex = 0;
7
  WrIndex = 0;
8
}
9
  
10
// write string in buffer
11
// returns -1 if buffer overflow
12
// max string length: 255
13
int cRingBuffer::WriteString(char* data)
14
{
15
  // loop for char access in data string
16
  uint8_t i=0;
17
  while(*(data+i)!='\0')
18
  {
19
    Buffer[WrIndex] = *(data+i);
20
    ++i;
21
    WrIndex = BufferMask&(++WrIndex);
22
    // 1 unused byte in Buffer for speed optimization
23
    if(RdIndex==WrIndex) return -1;
24
  }
25
  return 0;
26
}
27
28
// read char from buffer
29
char cRingBuffer::ReadChar()
30
{
31
  // buffer empty?
32
  if(RdIndex == WrIndex) return '\0';
33
  // read next char
34
  char Temp = Buffer[RdIndex];
35
  RdIndex = BufferMask&(++RdIndex);
36
  return Temp;
37
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Anonymous U. schrieb:
> Das die ISR noch nicht funktioniert ist mir klar. Kann ich die irgendwie
> in die Klasse mit einbinden? Dazu muss diese aber ein Singleton sein?
> Wie mache ich das, mit static?

Die letzte Frage betrachte ich mal als rhetorisch.

Folgendes geht:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/atomic.h>
4
5
#define STRINGY(x) #x
6
7
#define MAKE_ISR(name,vect,...) \
8
    __attribute__((__externally_visible__,__used__,__signal__)) __VA_ARGS__ \
9
    static void name () __asm (STRINGY(vect))
10
11
class C
12
{
13
    static volatile uint16_t n_isrs;
14
    MAKE_ISR (methodname, INT0_vect, ISR_NOBLOCK);
15
public:
16
    static int get_isrs()
17
    {
18
        ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
19
        {
20
            return n_isrs;
21
        }
22
        __builtin_unreachable();
23
    }
24
};
25
26
uint16_t volatile C::n_isrs;
27
28
void C::methodname()
29
{
30
    n_isrs++;
31
}
32
33
int main()
34
{
35
    return C::get_isrs();
36
}

I.d.R. Wird man MAKE_ISR mit nur 2 Argumenten verwenden.  avr-g++ 
erzeugt daraus folgenden Code:
1
...
2
.global  __vector_1
3
__vector_1:
4
  sei
5
  __gcc_isr 1
6
  push r21
7
  lds r20,_ZN1C6n_isrsE
8
  lds r21,_ZN1C6n_isrsE+1
9
  subi r20,-1
10
  sbci r21,-1
11
  sts _ZN1C6n_isrsE+1,r21
12
  sts _ZN1C6n_isrsE,r20
13
  pop r21
14
  __gcc_isr 2
15
  reti
16
  __gcc_isr 0,r20
17
18
.global  main
19
main:
20
  in r20,__SREG__
21
  cli
22
  lds r24,_ZN1C6n_isrsE
23
  lds r25,_ZN1C6n_isrsE+1
24
  out __SREG__,r20
25
  ret
26
...

von A. (Gast)


Lesenswert?

Anonymous U. schrieb:
> Soo. Nun funktioniert mein Kot endlich.

Was macht denn ein funktionierender Kot? Normalerweise liegt der nur 
rum..

von Anonymous U. (gastt)


Lesenswert?

Noch eine andere Frage: Bei meiner cRingBuffer Klasse verwende ich eine 
Bitmaske. Nun will ich die Buffergröße variabel machen. Dazu habe ich 
die Template-Variable BufferSize_2expN angelegt.
1
// 0 < BufferSize_2expN < 8
2
template <int BufferSize_2expN>
3
class cRingBuffer
4
{
5
  private:
6
    // variables
7
    const int BufferMask = ###_berechne_Maske_mit_for{}_###;
8
    char Buffer[###_berechne_2^(BufferSize_2expN)_###];
9
    uint8_t RdIndex;
10
    uint8_t WrIndex;
11
  public:
12
    // constructor
13
    cRingBuffer()
14
    { ... }
15
    // write string in buffer
16
    // returns -1 if buffer overflow
17
    // max string length: 255
18
    int WriteString(char* data)
19
    { ... }
20
    // read char from buffer
21
    char ReadChar()
22
    { ... }
23
};

Wie bereche ich nun die Bitmaske/Arraygröße generisch?

von Anonymous U. (gastt)


Lesenswert?

Dieser Code funktioniert zwar, aber wenn ich anstatt der automatischen 
Berechnung einfach #defines einfüge, spare ich 12 bytes. Ich verstehe 
aber nicht warum!
1
// 0 < BufferSize_2expN < 8
2
template <int BufferSize_2expN>
3
class cRingBuffer
4
{
5
  private:
6
    // variables
7
    int BufferMask;
8
    char Buffer[(1<<BufferSize_2expN)];
9
    uint8_t RdIndex;
10
    uint8_t WrIndex;
11
  public:
12
    // constructor
13
    cRingBuffer()
14
    {
15
      // template calculations
16
      BufferMask = 0;
17
      for(int i=0; i<BufferSize_2expN; i++)
18
      {
19
        BufferMask |= (1<<i);
20
      }
21
      // initialization
22
      RdIndex = 0;
23
      WrIndex = 0;
24
    }
25
 ...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

1
template <unsigned BufferSize_2expN>
2
class cRingBuffer
3
{
4
    enum { BufferSize = 1u << BufferSize_2expN };
5
    enum { BufferMask = BufferSize - 1 };
6
    char Buffer[BufferSize];
7
...
8
};

oder ab C++11 mit constexpr.

Beitrag #5238844 wurde von einem Moderator gelöscht.
von Einer K. (Gast)


Lesenswert?

Das wissen wir!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Folgendes geht:
>
1
> #define STRINGY(x) #x
2
> 
3
> #define MAKE_ISR(name,vect,...) \
4
>     __attribute__((__externally_visible__,__used__,__signal__)) 
5
> __VA_ARGS__ \
6
>     static void name () __asm (STRINGY(vect))
7
> 
8
> class C
9
> {
10
>     static volatile uint16_t n_isrs;
11
>     MAKE_ISR (methodname, INT0_vect, ISR_NOBLOCK);
12
> [...]

Da einige Leser bessere Vorschläge haben: Her damit!

Je nach Code will man noch ein __no_icf__ zu den Attributen.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

bist mit der uart Lib von Peter Fleury bestimmt besser dran.

Beitrag #5239084 wurde von einem Moderator gelöscht.
Beitrag #5239089 wurde von einem Moderator gelöscht.
Beitrag #5239114 wurde von einem Moderator gelöscht.
Beitrag #5239127 wurde von einem Moderator gelöscht.
von Anonymous U. (gastt)


Lesenswert?

>> Anonymous U. schrieb:
>>> Ich will mit einem Atmega48 eine serielle Kommunikation mit einem
>>> RingBuffer programmieren.
>>
>> Das ist in Asm zu einem Bruchteil des C++ Textwusts möglich. Ohne
>> Studium und abstrakte Verrenkungen.

Mit Assembler kann man das sicher schlanker hinbringen. Aber der Gewinn 
an Übersichtlichkeit vom Sourcecode, den höhere Abstraktionsebenen 
bieten, ist mir der kleine Overhead wert. Natürlich muss man wissen wie 
man sein Werkzeug richtig einsetzt und im Falle C++ versuche ich grade 
herauszufinden wie man den Code optimiert, bzw. wann es Sinn macht OO 
einzusetzen und wo nicht.

By the way: Bei ganz kleinen µC (Stichwort IoT) macht Assembler 
tatsächlich Sinn. Mein aktuelles Projekt wird allerdings etwas größer 
und ich bin mit dem Atmega nicht ganz so beschränkt.

Veit D. schrieb:
> bist mit der uart Lib von Peter Fleury bestimmt besser dran.

Die eigene Lib ist schnell programmiert, was den UART angeht. Das ist 
kein großer Aufwand von der Programmlogik her. Der große Zeitfresser ist 
im Moment zu lernen wie man C++ hier richtig einzusetzt. Ob ich dafür 
jetzt irgendeine Bibliotek reinklatsche oder meine eigene erstelle macht 
in diesem Fall Zeitmäßig keinen Unterschied. Allerdings weiß ich zum 
Schluss halt was mein Code genau macht, bzw. ist der für meine Zwecke 
dann besser optimiert.

: Bearbeitet durch User
von Felix F. (wiesel8)


Lesenswert?

Mal ne kurze Frage bzgl. C++-Klassen und Peripherie.

Wenn ich mir z.B. eine serielle Klasse für den UART implementiere, dann 
muss ich ja ein Objekt von der Klasse erzeugen, damit ich dann auf 
send(), receive() etc... zugreifen kann. Prinzipiell erst mal kein 
Problem. Aber wie funktioniert dass dann Modulübergreifend? Also wie 
kann ich von x verschiedenen Modulen dann auf die serielle Klasse 
zugreifen? Jedesmal ein neues Objekt erzeugen kann ja nicht die Lösung 
sein?

mfg

von CC (Gast)


Lesenswert?

Du kannst den Modulen einen Pointer auf das Objekt geben...

von Axel S. (a-za-z0-9)


Lesenswert?

Felix F. schrieb:
> Wenn ich mir z.B. eine serielle Klasse für den UART implementiere, dann
> muss ich ja ein Objekt von der Klasse erzeugen, damit ich dann auf
> send(), receive() etc... zugreifen kann.

> wie
> kann ich von x verschiedenen Modulen dann auf die serielle Klasse
> zugreifen?

Ich nehme an, du meinst "Übersetzungseinheiten" anstatt "Module" und 
"UART-Objekt" (auch "Instanz") statt "serielle Klasse".

Und die Antwort ist die gleiche wie für C auch: eine globale Variable, 
in diesem Fall ein globales Objekt. Das mußt du in einer 
Übersetzungseinheit definieren und in allen anderen (die es verwenden) 
deklarieren, üblicherweise in einem Headerfile das du überall 
einbindest.

von c-hater (Gast)


Lesenswert?

Anonymous U. schrieb:

> Mit Assembler kann man das sicher schlanker hinbringen. Aber der Gewinn
> an Übersichtlichkeit vom Sourcecode, den höhere Abstraktionsebenen
> bieten, ist mir der kleine Overhead wert.

Also ich sehe da zumindest im konkreten Fall keinerlei Vorteile 
bezüglich der Übersichtlichkeit des Codes, sondern eher das genaue 
Gegenteil...

von Wilhelm M. (wimalopaan)


Lesenswert?

Felix F. schrieb:
> Mal ne kurze Frage bzgl. C++-Klassen und Peripherie.
>
> Wenn ich mir z.B. eine serielle Klasse für den UART implementiere, dann
> muss ich ja ein Objekt von der Klasse erzeugen, damit ich dann auf
> send(), receive() etc... zugreifen kann. Prinzipiell erst mal kein
> Problem. Aber wie funktioniert dass dann Modulübergreifend? Also wie
> kann ich von x verschiedenen Modulen dann auf die serielle Klasse
> zugreifen? Jedesmal ein neues Objekt erzeugen kann ja nicht die Lösung
> sein?

Das könnte man als Monostate - Pattern so machen.

Man kann aber auch gar kein Objekt anlegen, sondern alles static machen. 
Das macht man dann mit Templates: so kann man zu Compilezeit auch gleich 
verhindern, dass zuviele Uarts benutzt werden. Andernfalls müsste man 
es zur Laufzeit prüfen. Compilezeitfehler sind besser ;-)

von batman (Gast)


Lesenswert?

Übersichtlicher wirds im Hauptprogramm, was aber nur bei entsprechend 
komplexen Anwendungen vorteilhaft ist und weniger aufm Controller, wo es 
im Extremfall gar kein Hauptprogramm gibt.

von Felix F. (wiesel8)


Lesenswert?

Wilhelm M. schrieb:
> Felix F. schrieb:
>> Mal ne kurze Frage bzgl. C++-Klassen und Peripherie.
>>
>> Wenn ich mir z.B. eine serielle Klasse für den UART implementiere, dann
>> muss ich ja ein Objekt von der Klasse erzeugen, damit ich dann auf
>> send(), receive() etc... zugreifen kann. Prinzipiell erst mal kein
>> Problem. Aber wie funktioniert dass dann Modulübergreifend? Also wie
>> kann ich von x verschiedenen Modulen dann auf die serielle Klasse
>> zugreifen? Jedesmal ein neues Objekt erzeugen kann ja nicht die Lösung
>> sein?
>
> Das könnte man als Monostate - Pattern so machen.
>
> Man kann aber auch gar kein Objekt anlegen, sondern alles static machen.
> Das macht man dann mit Templates: so kann man zu Compilezeit auch gleich
> verhindern, dass zuviele Uarts benutzt werden. Andernfalls müsste man
> es zur Laufzeit prüfen. Compilezeitfehler sind besser ;-)

Hättest du dazu auch einen Link etc mit einem Beispiel?

mfg

von c-hater (Gast)


Lesenswert?

batman schrieb:

> Übersichtlicher wirds im Hauptprogramm

Nicht mal das ist der Fall. Ob ich da hinschreibe

  rcall getfifobyte
  br(irgenwas) gotfifobyte

gotfifobyte:
  ;got fifo byte, bereits im Register, fertig zur näheren Inspektion

oder

if getfifobyte(dämlicher char-Zeiger)
{
  //got fifobyte, muss aber erst wieder aus dem Speicher geholt werden
  //um es genauer ansehen zu können
}

Dürfte von der Übersicht her gleich sein. Vom Tippaufwand her ist die 
Assemblerversion sogar kürzer. Und von der Effizienz her auch noch 
deutlich überlegen...

von Wilhelm M. (wimalopaan)


Lesenswert?

Felix F. schrieb:

> Hättest du dazu auch einen Link etc mit einem Beispiel?

Zum Monostate:

https://github.com/wllmtrng/wllmtrng.github.io/wiki/Singleton-and-Monostate-Pattern

Im übrigen nutzt Qt das sehr ausgiebig.

Das mit den Templates ist dann auch stark davon abhängig, ob Du schon 
eine geeignete Abstraktion der MCU als solches verwendest bsp. über ein 
class-mapping.

von Thomas W. (wagneth)


Lesenswert?

Das MAKE_ISR von Johann schaut soweit phantastisch aus.

Aber leider sorgt das static dafür das auch alle anderen Daten samt 
Methoden, auf die zugegriffen werden soll, auch static sein müssen.

Oder es muss mit in die ISR Methode rein.

Das macht es schwierig einen Handler einer anderen (fremden) 
Klasseninstanz aufzurufen.

Komplizierter macht es Christian M. und baut quasi eine zweite 
Sprungtabelle :

[[https://www.mikrocontroller.net/articles/AVR_Interrupt_Routinen_mit_C++]]

Hier hatte ich das zum ersten mal gesehen.


Daraus habe ich das folgende "geklaut" (und soweit verstanden;) :
1
extern "C"
2
void __vector_14 (void) __attribute__ ((signal,used, externally_visible));
3
4
class TIMER
5
      {
6
          public:
7
8
        TIMER();
9
10
        void prependHandler (TIMERHANDLER &t);
11
12
        volatile uint32_t now();
13
14
        void connectCOM2A0();
15
        void disconnectCOM2A0();
16
17
          private:
18
19
       TIMERHANDLER
20
            *chainStart = nullptr;
21
22
        volatile uint32_t
23
            time;
24
25
        friend
26
          void __vector_14 (void);
27
28
      };
29
30
31
TIMER
32
  timer;
33
34
35
void __vector_14 (void)
36
{
37
      timer.time++;
38
39
  if (timer.chainStart != nullptr) timer.chainStart->handler(); 
40
        // geht nicht mit static, alleine wegen der vorinitialisierung.
41
        // Auch bei der Liste gibt es Probleme. 
42
}

Es ist garantiert nicht besser, aber aus der ISR kann ich andere 
Methoden aufrufen.

: Bearbeitet durch User
Beitrag #5242476 wurde von einem Moderator gelöscht.
Beitrag #5242612 wurde von einem Moderator gelöscht.
Beitrag #5242752 wurde von einem Moderator gelöscht.
Beitrag #5243591 wurde von einem Moderator gelöscht.
Beitrag #5243734 wurde von einem Moderator gelöscht.
Beitrag #5243738 wurde von einem Moderator gelöscht.
Beitrag #5243775 wurde von einem Moderator gelöscht.
Beitrag #5243784 wurde von einem Moderator gelöscht.
Beitrag #5243806 wurde von einem Moderator gelöscht.
Beitrag #5243809 wurde von einem Moderator gelöscht.
Beitrag #5243811 wurde von einem Moderator gelöscht.
Beitrag #5243830 wurde von einem Moderator gelöscht.
Beitrag #5244020 wurde von einem Moderator gelöscht.
Beitrag #5244790 wurde von einem Moderator gelöscht.
Beitrag #5244828 wurde von einem Moderator gelöscht.
Beitrag #5245306 wurde von einem Moderator gelöscht.
Beitrag #5250588 wurde von einem Moderator gelöscht.
Beitrag #5250609 wurde von einem Moderator gelöscht.
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.