mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik AVR Programmierung C++


Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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?

#include <avr/io.h>
#include <avr/interrupt.h>

class cSerialComm
{
  public:
    // variables
    volatile cRingBuffer TxBuffer;
    // constructor -> setup
    cSerialComm();
    // write string in tx buffer
    // returns -1 if buffer overflow
    // max string length: 255
    int SendString(char* data);
};

cSerialComm::cSerialComm()
{
  //UCSR0A = 
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
  UBRR0 = 47; // baud rate: 115200 kBit/s
}

int cSerialComm::SendString(char* data)
{
  // write in tx buffer
  if( TxBuffer.WriteString(data) ) return -1;
  // initiate serial sending
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) UDR0 = TxBuffer.ReadChar();
  return 0;
}

// Interrupt Service Routines
  // Serial: Received Char
  ISR(USART_RX_vect)
  {
    return;
  }
  // Serial: Finished Sending Char
  ISR(USART_TX_vect)
  {
    if(SerialComm.TxBuffer.RdIndex!=SerialComm.TxBuffer.WrIndex) UDR0 = SerialComm.TxBuffer.ReadChar();
    return;
  }


[Mod: Formatierung korrigiert]

: Bearbeitet durch Moderator
Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
blabla_t wert;
ohne vorher je gesagt zu haben, was blabla_t sein soll.

Autor: Peter (Gast)
Datum:

Bewertung
1 lesenswert
nicht 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.

Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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:
// DataStructs.cpp

#include <avr/io.h>

class cRingBuffer
{
  private:
  // variables
  #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
  char Buffer[32];
  
  public:
  // variables
  uint8_t RdIndex;
  uint8_t WrIndex;
  // constructor
  cRingBuffer()
  {
    RdIndex = 0;
    WrIndex = 0;
  }
  // write string in buffer
  // returns -1 if buffer overflow
  // max string length: 255
  int WriteString(char* data)
  {
    // loop for char access in data string
    uint8_t i=0;
    while(*(data+i)!='\0')
    {
      Buffer[WrIndex] = *(data+i);
      WrIndex = BufferMask&(++WrIndex);
      // 1 unused byte in Buffer for speed optimization
      if(RdIndex==WrIndex) return -1;
    }
    return 0;
  }
  // read char from buffer
  char ReadChar()
  {
  // buffer empty?
  if(RdIndex == WrIndex) return '\0';
  // read next char
  char Temp = Buffer[RdIndex];
  RdIndex = BufferMask&(++RdIndex);
  return Temp;
  }
};


// DataStructs.h

#include <avr/io.h>

class cRingBuffer
{
  private:
  // variables
  #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
  char Buffer[32];
  public:
  // variables
  uint8_t RdIndex;
  uint8_t WrIndex;
  // constructor
  cRingBuffer();
  // write string in buffer
  // returns -1 if buffer overflow
  // max string length: 255
  int WriteString(char* data);
  // read char from buffer
  char ReadChar();
};

#endif /* DATASTRUCTS_H_ */


// SerialComm.cpp

#include <avr/io.h>
#include <avr/interrupt.h>
#include "DataStructs.h"

class cSerialComm
{
  public:
    // variables
    volatile cRingBuffer TxBuffer;
    // constructor -> setup
    cSerialComm();
    // write string in tx buffer
    // returns -1 if buffer overflow
    // max string length: 255
    int SendString(char* data);
};

cSerialComm::cSerialComm()
{
  //UCSR0A = 
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
  UBRR0 = 47; // baud rate: 115200 kBit/s
}

int cSerialComm::SendString(char* data)
{
  // write in tx buffer
  if( TxBuffer.WriteString(data) ) return -1;
  // initiate serial sending
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) UDR0 = TxBuffer.ReadChar();
  return 0;
}

// Interrupt Service Routines
  // Serial: Received Char
  ISR(USART_RX_vect)
  {
    return;
  }
  // Serial: Finished Sending Char
  ISR(USART_TX_vect)
  {
    if(SerialComm.TxBuffer.RdIndex!=SerialComm.TxBuffer.WrIndex) UDR0 = SerialComm.TxBuffer.ReadChar();
    return;
  }

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> ISR ... Kann ich die irgendwie in die Klasse mit einbinden?

Ich glaube das geht leider nicht.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);
}

Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <avr/interrupt.h>
#include "SerialComm.h"

// pin definitions
#if 1
  // Switch
  #define pin_sw        PINB0
  // PLL SPI Communication
  #define pin_SPI_pll_lo_En  PINB1
  #define pin_SPI_pll_sig_En  PINB2
  #define pin_SPI_pll_SDO    PINB3
  #define pin_SPI_pll_SDI    PINB4
  #define pin_SPI_pll_SCK    PINB5
  // Temperature Sensors
  #define pin_temp1      PINC0
  #define pin_temp2      PINC1
  // LED
  #define pin_led        PINC4
  // Fan
  #define pin_fan        PINC5
  // Serial Communication
  #define pin_serial_RXD    PIND0
  #define pin_serial_TXD    PIND1
  // RF Path Control
  #define pin_sw_G2      PIND2
  #define pin_Gain_sig_En    PINC2
  #define pin_Gain_lo_En    PIND6
  // ADC Control
  #define pin_ADC_CS      PINC3
  #define pin_ADC_EOC      PIND3
  #define pin_ADC_RD      PIND4
  #define pin_ADC_CONVST    PIND5
  #define pin_Raspi      PIND7
#endif

// global variables and objects
cSerialComm SerialComm;
struct sTime
{
  int ms;
  int s;
  int min;
  int blink_1s;
};
volatile struct sTime Time;
volatile int TemperatureState[4];

// Functions
void PinSetup()
{
  // Direction: High is Output
  DDRB = (1<<pin_SPI_pll_lo_En)|(1<<pin_SPI_pll_sig_En)|(1<<pin_SPI_pll_SDO)|(1<<pin_SPI_pll_SCK);
  DDRC = (1<<pin_Gain_sig_En)|(1<<pin_ADC_CS)|(1<<pin_led)|(1<<pin_fan);
  DDRD = (1<<pin_serial_TXD)|(1<<pin_sw_G2)|(1<<pin_ADC_RD)|(1<<pin_Gain_lo_En)|(1<<pin_Raspi);
  // Pullup/Output
  PORTB = (1<<pin_sw);
}
void TimerSetup()
{
  // Timer1 (16bit): Clock
  // Overflow Interrupt: 1ms
  // 11.0592MHz/11059
  TCCR1A = (1<<WGM11)|(1<<WGM10);
  TCCR1B = (1<<WGM13)|(1<<WGM12)|(1<<CS10);
  OCR1A = 11059;
  TIMSK1 = (1<<TOIE1);
}
void TempADCSetup()
{
  
}

int SwitchState()
{
  if(PINB&(1<<pin_sw)) return 0;
  return 1;
}
void SetLED(int state)
{
  if(state) PORTC|=(1<<pin_led);
  else PORTC&=~(1<<pin_led);
}
void SetFan(int state)
{
  if(state) PORTC|=(1<<pin_fan);
  else PORTC&=~(1<<pin_fan);
}
void SetGainSig(int state)
{
  if(state) PORTC |= (1<<pin_Gain_sig_En);
  else PORTC &=~(1<<pin_Gain_sig_En);
}
void SetGainLo(int state)
{
  if(state) PORTD |= (1<<pin_Gain_lo_En);
  else PORTD &=~(1<<pin_Gain_lo_En);
}
void PrintTemp()
{
  for(int i=0; i<4; i++)
  {
    int i=0;
  }
}

int main(void)
{
  // Setup
  PinSetup();
  SetFan(1);
  TimerSetup();
  TempADCSetup();
  sei();
    while(1)
    {
    // show device run state
    SetLED(Time.blink_1s);
    }
}

// Interrupt Service Routines
  // Timer1: 1ms
  ISR(TIMER1_OVF_vect)
  {
    ++Time.ms;
    if(Time.ms>=1000)
    {
      Time.ms = 0;
      ++Time.s;
      Time.blink_1s ^= 1;
      // Trigger Temp ADC Conversion
      SerialComm.SendString("test\n");
      if(Time.s>=60)
      {
        Time.s = 0;
        ++Time.min;
      }
    }
    return;
  }
  // Serial: Received Char
  ISR(USART_RX_vect)
  {
    return;
  }
  // Serial: Finished Sending Char
  ISR(USART_TX_vect)
  {
    char NextChar = SerialComm.TxBuffer.ReadChar();
    if(NextChar!='\0') UDR0 = NextChar;
    return;
  }
  
  

SerialComm.h:
#ifndef SERIALCOMM_H_
#define SERIALCOMM_H_

#include <avr/io.h>
#include <avr/interrupt.h>
#include "DataStructs.h"

class cSerialComm
{
  public:
    // variables
    cRingBuffer TxBuffer;
    // constructor -> setup
    cSerialComm();
    // write string in tx buffer
    // returns -1 if buffer overflow
    // max string length: 255
    int SendString(char* data);
};

#endif /* SERIALCOMM_H_ */

SerialComm.cpp:
#include "SerialComm.h"

cSerialComm::cSerialComm()
{
  //UCSR0A = 
  UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0);
  UCSR0C = (1<<UMSEL00)|(1<<UPM01)|(1<<UCSZ00)|(1<<UCSZ01)|(1<<UCPOL0); // mode: synchronous, parity: even, data-bits: 8
  UBRR0 = 47; // baud rate: 115200 kBit/s
}

int cSerialComm::SendString(char* data)
{
  // has all data from buffer been sent?
  int SendActive=1;
  if(TxBuffer.RdIndex == TxBuffer.WrIndex) SendActive=0;
  // write in tx buffer
  if( TxBuffer.WriteString(data) ) return -1;
  // initiate serial sending if required
  if(!SendActive) UDR0 = TxBuffer.ReadChar();
  return 0;
}

DataStructs.h:
#include <stdint.h>

class cRingBuffer
{
  private:
    // variables
    #define BufferMask 0b00011111 // has to correspond with buffer size to avoid overflow
    char Buffer[32];
  public:
    // variables
    uint8_t RdIndex;
    uint8_t WrIndex;
    // constructor
    cRingBuffer();
    // write string in buffer
    // returns -1 if buffer overflow
    // max string length: 255
    int WriteString(char* data);
    // read char from buffer
    char ReadChar();
};

#endif /* DATASTRUCTS_H_ */

DataStructs.cpp:
#include "DataStructs.h"

// constructor
cRingBuffer::cRingBuffer()
{
  RdIndex = 0;
  WrIndex = 0;
}
  
// write string in buffer
// returns -1 if buffer overflow
// max string length: 255
int cRingBuffer::WriteString(char* data)
{
  // loop for char access in data string
  uint8_t i=0;
  while(*(data+i)!='\0')
  {
    Buffer[WrIndex] = *(data+i);
    ++i;
    WrIndex = BufferMask&(++WrIndex);
    // 1 unused byte in Buffer for speed optimization
    if(RdIndex==WrIndex) return -1;
  }
  return 0;
}

// read char from buffer
char cRingBuffer::ReadChar()
{
  // buffer empty?
  if(RdIndex == WrIndex) return '\0';
  // read next char
  char Temp = Buffer[RdIndex];
  RdIndex = BufferMask&(++RdIndex);
  return Temp;
}

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#define STRINGY(x) #x

#define MAKE_ISR(name,vect,...) \
    __attribute__((__externally_visible__,__used__,__signal__)) __VA_ARGS__ \
    static void name () __asm (STRINGY(vect))

class C
{
    static volatile uint16_t n_isrs;
    MAKE_ISR (methodname, INT0_vect, ISR_NOBLOCK);
public:
    static int get_isrs()
    {
        ATOMIC_BLOCK (ATOMIC_RESTORESTATE)
        {
            return n_isrs;
        }
        __builtin_unreachable();
    }
};

uint16_t volatile C::n_isrs;

void C::methodname()
{
    n_isrs++;
}

int main()
{
    return C::get_isrs();
}

I.d.R. Wird man MAKE_ISR mit nur 2 Argumenten verwenden.  avr-g++ 
erzeugt daraus folgenden Code:
...
.global  __vector_1
__vector_1:
  sei
  __gcc_isr 1
  push r21
  lds r20,_ZN1C6n_isrsE
  lds r21,_ZN1C6n_isrsE+1
  subi r20,-1
  sbci r21,-1
  sts _ZN1C6n_isrsE+1,r21
  sts _ZN1C6n_isrsE,r20
  pop r21
  __gcc_isr 2
  reti
  __gcc_isr 0,r20

.global  main
main:
  in r20,__SREG__
  cli
  lds r24,_ZN1C6n_isrsE
  lds r25,_ZN1C6n_isrsE+1
  out __SREG__,r20
  ret
...

Autor: A. (Gast)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
Anonymous U. schrieb:
> Soo. Nun funktioniert mein Kot endlich.

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

Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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.
// 0 < BufferSize_2expN < 8
template <int BufferSize_2expN>
class cRingBuffer
{
  private:
    // variables
    const int BufferMask = ###_berechne_Maske_mit_for{}_###;
    char Buffer[###_berechne_2^(BufferSize_2expN)_###];
    uint8_t RdIndex;
    uint8_t WrIndex;
  public:
    // constructor
    cRingBuffer()
    { ... }
    // write string in buffer
    // returns -1 if buffer overflow
    // max string length: 255
    int WriteString(char* data)
    { ... }
    // read char from buffer
    char ReadChar()
    { ... }
};

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

Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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!
// 0 < BufferSize_2expN < 8
template <int BufferSize_2expN>
class cRingBuffer
{
  private:
    // variables
    int BufferMask;
    char Buffer[(1<<BufferSize_2expN)];
    uint8_t RdIndex;
    uint8_t WrIndex;
  public:
    // constructor
    cRingBuffer()
    {
      // template calculations
      BufferMask = 0;
      for(int i=0; i<BufferSize_2expN; i++)
      {
        BufferMask |= (1<<i);
      }
      // initialization
      RdIndex = 0;
      WrIndex = 0;
    }
 ...

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
template <unsigned BufferSize_2expN>
class cRingBuffer
{
    enum { BufferSize = 1u << BufferSize_2expN };
    enum { BufferMask = BufferSize - 1 };
    char Buffer[BufferSize];
...
};

oder ab C++11 mit constexpr.

Beitrag #5238844 wurde von einem Moderator gelöscht.
Autor: Arduino Fanboy D. (ufuf)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wissen wir!

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johann L. schrieb:
> Folgendes geht:
>
> #define STRINGY(x) #x
> 
> #define MAKE_ISR(name,vect,...) \
>     __attribute__((__externally_visible__,__used__,__signal__)) 
> __VA_ARGS__ \
>     static void name () __asm (STRINGY(vect))
> 
> class C
> {
>     static volatile uint16_t n_isrs;
>     MAKE_ISR (methodname, INT0_vect, ISR_NOBLOCK);
> [...]

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

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

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

Bewertung
-2 lesenswert
nicht 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.
Autor: Anonymous U. (gastt)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Felix F. (wiesel8)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: CC (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du kannst den Modulen einen Pointer auf das Objekt geben...

Autor: Axel S. (a-za-z0-9)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: c-hater (Gast)
Datum:

Bewertung
-1 lesenswert
nicht 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...

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
-1 lesenswert
nicht 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 ;-)

Autor: batman (Gast)
Datum:

Bewertung
-1 lesenswert
nicht 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.

Autor: Felix F. (wiesel8)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: c-hater (Gast)
Datum:

Bewertung
-3 lesenswert
nicht 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...

Autor: Wilhelm M. (wimalopaan)
Datum:

Bewertung
-1 lesenswert
nicht 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.

Autor: Thomas W. (wagneth)
Datum:

Bewertung
0 lesenswert
nicht 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;) :
extern "C"
void __vector_14 (void) __attribute__ ((signal,used, externally_visible));

class TIMER
      {
          public:

        TIMER();

        void prependHandler (TIMERHANDLER &t);

        volatile uint32_t now();

        void connectCOM2A0();
        void disconnectCOM2A0();

          private:

       TIMERHANDLER
            *chainStart = nullptr;

        volatile uint32_t
            time;

        friend
          void __vector_14 (void);

      };


TIMER
  timer;


void __vector_14 (void)
{
      timer.time++;

  if (timer.chainStart != nullptr) timer.chainStart->handler(); 
        // geht nicht mit static, alleine wegen der vorinitialisierung.
        // Auch bei der Liste gibt es Probleme. 
}

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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.