avr veel variabelen opslaan ?

trix

Golden Member

hallo,
ik heb weer een vraag, en dat gaat eigenlijk, over om iets kort te programeren. ik krijg het wel werkend, maar met een hoop type werk, en dus omslachtig en inefficient. ik zit er al dagen over te denken en naar te kijken maar de korte methode zie ik gewoon niet :?.

ik heb een switch/case constructie met 24 cases. die vullen 3 variabelen (3x een byte). alleen die variabelen wijzigen iedere keer wanneer die swich/case constructie word doorlopen, en dat is 300x. dus op het eind van het liedje heb ik 3x300 = 900 bytes.
als ik dit allemaal met if/then doe dan worden dat enorme lappen text.
ik heb eigenlijk een "reverse array" nodig (denk ik).

om het duidelijk te maken, heb ik een stuk code toegevoegd, waar in de 2e helft de simpele manier staat.
die switch/case constructie moet behouden blijven, i.v.m. synchronisatie met een ander "device".

c code:


ISR(TIMER1_COMPA_vect) 
{
	static uint8_t PulseCounter = 150;
	static uint8_t BitCounter   = 0;
		
	if(PulseCounter == 0)
	{			
		PulseCounter = 150;

		switch(BitCounter++)
		{
																	
		case 0 : { byte_1 |= (PINC & 0x80);  break; }
		case 1 : { byte_1 |= (PINC & 0x40);  break; }
		case 2 : { byte_1 |= (PINC & 0x20);  break; }
		case 3 : { byte_1 |= (PINC & 0x10);  break; }
		case 4 : { byte_1 |= (PINC & 0x08);  break; }
	        case 5 : { byte_1 |= (PINC & 0x04);  break; }
		case 6 : { byte_1 |= (PINC & 0x02);  break; }
   		case 7 : { byte_1 |= (PINC & 0x01);  break; }
					  
		case 8 : { byte_2 |= (PINA & 0x80);  break; }
		case 9 : { byte_2 |= (PINA & 0x40);  break; }
		case 10: { byte_2 |= (PINA & 0x20);  break; }
		case 11: { byte_2 |= (PINA & 0x10);  break; }
		case 12: { byte_2 |= (PINA & 0x08);  break; }
		case 13: { byte_2 |= (PINA & 0x04);  break; }
		case 14: { byte_2 |= (PINA & 0x02);  break; }
		case 15: { byte_2 |= (PINA & 0x01);  break; }
					   
		case 16: { byte_3 |= (PINB & 0x80);  break; }
		case 17: { byte_3 |= (PINB & 0x40);  break; }
		case 18: { byte_3 |= (PINB & 0x20);  break; }
		case 19: { byte_3 |= (PINB & 0x10);  break; }
		case 20: { byte_3 |= (PINB & 0x08);  break; }
		case 21: { byte_3 |= (PINB & 0x04);  break; }
		case 22: { byte_3 |= (PINB & 0x02);  break; }
		case 23: { byte_3 |= (PINB & 0x01); PORTD &= ~(1 << PIND4); break; }				
// below: pulse counter = 0; needed so that TSAL & TSOP number 1 start on the same time.				
		case 24: {  BitCounter = 0; PulseCounter = 0; start_pulse_train = 0; break; }
					
// HIER ONDER ZOU DAN EEN ENORME LAP TEXT KOMEN, WAT IK GRAAG EFFICIENTER WIL DOEN.***********
// column_counter word buiten deze ISR op gehoogd ********************************************
		
		if (column_counter == 1)
		{
			column1_byte1 = byte_1;
			column1_byte2 = byte_2;
			column1_byte3 = byte_3;
		}
				
		if (column_counter == 2)
		{
			column2_byte1 = byte_1;
			column2_byte2 = byte_2;
			column2_byte3 = byte_3;
		}
		.
		.
		.
		.
		.
		.
		.
		.				
		if (column_counter == 300)
		{
			column300_byte1 = byte_1;
                        column300_byte2 = byte_2;
			column300_byte3 = byte_3;
		}
					   
					   			   
	}
}
else
{
	PulseCounter -= 1;
}		
}
eigenwijs = ook wijs
bprosman

Golden Member

De jongere generatie loopt veel te vaak zijn PIC achterna.
trix

Golden Member

heb ik al eens (lang geleden) mee gewerkt, ga ik bekijken.

eigenwijs = ook wijs

Lookup table? Typisch geval van een array.

Je kunt je data als een lijst losse variabelen definieren, maar dat is niet handig. Met een array kun je kiezen welke variabele je wilt gebruiken.

Blijkbaar wil je per column 3 bytes opslaan.
Dan kun je een structure declareren die zo een column definieert.

code:


struct ColumnData
{  uint8_t Byte1;
   uint8_t Byte2;
   uint8_t Byte3;
};

En als je dan 300 van die columns nodig hebt dan maak je een array van 300 van die columns.

code:


ColumnData Columns[300];

Dan kun je in je ISR kiezen welke je wilt updaten:

code:


ISR(TIMER1_COMPA_vect) 
{
   static uint8_t PulseCounter = 150;
   static uint8_t BitCounter   = 0;
      
   if(PulseCounter == 0)
   {        
      PulseCounter = 150;

      ColumnData & MyColumn = Columns[column_counter - 1];

      uint8_t BitMask = (0x80 >> (BitCounter & 0x03));
       
      switch(BitCounter++)
      {
         case 0 :
         case 1 :
         case 2 :
         case 3 :
         case 4 :
         case 5 :
         case 6 :
         case 7 :
         {  MyColumn.Byte1 |= (PINC & BitMask);
            break;
         }
                 
         case 8 : 
         case 9 : 
         case 10: 
         case 11: 
         case 12: 
         case 13: 
         case 14: 
         case 15: 
         {  MyColumn.Byte2 |= (PINA & BitMask);
            break;
         }
                  
         case 16: 
         case 17: 
         case 18: 
         case 19: 
         case 20: 
         case 21: 
         case 22: 
         {  MyColumn.Byte3 |= (PINB & BitMask);
            break;
         }
         
         case 23: 
         {  MyColumn.Byte3 |= (PINB & 0x01); 
            PORTD &= ~(1 << PIND4); 
            break; 
         }          
// below: pulse counter = 0; needed so that TSAL & TSOP number 1 start on the same time.           
         case 24: 
         {  BitCounter   = 0; 
            PulseCounter = 0; 
            start_pulse_train = 0; 
            break; 
         }
      }                        
   }
   else
   {
      PulseCounter -= 1;
   }     
}

Ik gebruik hier een reference

code:


      ColumnData & MyColumn = Columns[column_counter - 1];

Let op het '&' tekentje voor MyColumn. MyColumn is een nieuwe variabele van het type 'ColumnData', maar door het & teken wordt het ook een reference (soort van pointer) naar een positie binnen de 'Columns' array. Die positie wordt aangegeven door column_counter. ( - 1, want de entries in de array zijn genummerd van 0 .. 299). Wijzigingen in MyColumns gaan dan direkt naar je juiste positie in de array.

trix

Golden Member

verdorie kees,... wat moet dat toch heerlijk zijn als je de code zo uit je mouw kunt schudden.

ga ik bekijken.

eigenwijs = ook wijs

Tja, het is mijn vak.
De laatste 40 jaar niks anders gedaan.

Ik schrijf het liever zo:

code:


ColumnData *MyColumn = &Columns[column_counter - 1];

Het is toch normale C-code wat je aan het doen bent en geen C++?
@trix: C kent geen references namelijk.

Dus OOK de spatie weglaten tussen het * en de variable, dat leest veel makkelijker.

Ook de . moet dan -> worden op regels waar dit staat: MyColumn.Byte1
dat wordt dus: MyColumn->Byte1 enzovoort.

Hoe zit het nu met je uart/rs485 spul, werkt dat nu?

P.S. Je hebt die struct niet echt nodig, door een array te declaren van 300x3 bytes en dan kan de switch nog simpeler / compacter.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Klopt, references zijn C++, en werken niet in plain C.
De avr-gcc compiler heeft daar geen problemen mee zolang je sourcefile een .cpp extensie heeft. De compiler is volledig C++ dus waarom zou je dat niet gebruiken?

Maar met pointers kan het ook natuurlijk.

De spatie laat ik normaal ook weg, maar hier wilde ik hem extra zichtbaar maken.

En een tweedimensionale array kan ook inderdaad. Maar of het daar beter van wordt? Ik heb ze nog nooit gebruikt.

De code wordt dan iets als

code:


uint8_t ColumnData[300][3];
...
   ColumnData[column_counter -1][(Bitcounter >> 2) & 0x03)] |= (PINC & BitMask);

Op 16 april 2020 13:20:26 schreef deKees:
Klopt, references zijn C++, en werken niet in plain C.
De avr-gcc compiler heeft daar geen problemen mee zolang je sourcefile een .cpp extensie heeft. De compiler is volledig C++ dus waarom zou je dat niet gebruiken?

Omdat het zeer verwarrend is en totaal onnodig. Als je code 99.9% C is moet je geen stukje C++ ertussen gaan zetten.

Zeker in dit geval heeft de TS er geen ervaring mee dus gewoon niet doen.

Die index (column_counter) kan beter vanaf 0 genummerd worden, maar ik weet waar die vandaan komt.

Verder moeten diverse variable waarschijnlijk nog volatile gedeclareerd worden om ervoor te zorgen dat je compiler de boel niet weg optimaliseerd in je niet-isr code.
Dat zijn in ieder geval je data array en waarschijnlijk ook column_counter ergens.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Op 16 april 2020 13:25:50 schreef henri62:
Die index (column_counter) kan beter vanaf 0 genummerd worden, maar ik weet waar die vandaan komt.

Compiler gaat het toch zo inrichten zoals het hem/haar het beste uitkomt. Het is een illusie om te denken dat een index bij 0 laten beginnen ook maar iets uitmaakt. Je loopt zelfs het risico dat omdat je index eigenlijk niet op 0 begint je zelf moet omrekenen met kans op fouten en de zekerheid van extra code.

Don't Panic!

De compiler maakt geen keuze op zijn array indexen.
Die loopt altijd vanaf 0. In C, en ook in C++ en in veel andere talen.

Als je dan zelf een index variabale vanaf 1 laat lopen dan moet je omrekenen. En daar maak je makkelijke vergissingen.

@henri62
Ieder zijn voorkeur. Ik doe alles in C++ tegenwoordig. Zou niet anders meer willen.

En volatile moet inderdaad wel eigenlijk, ook al zal het hier weinig verschil maken.

Typo in mijn text, het had moeten zijn: ... maar ik weet NIET waar die vandaan komt
Daarmee bedoelde ik dus dat ik niet weet wat de rest van het programma met die 'index' doet.

Zoals deKees zegt, indexen lopen altijd vanaf 0, als je dan zelf op 1 begint is dat onhandig en moet je gaan omrekenen. Zeker in een ISR kost dat weer een instructie extra.
Bij mij gaan de alarmbellen af als ik een -1 in een index zie staan.

@deKees: Wat C++ betreft, wil je dat goed toepassen moet je echt anders denken hoe je een programma goed in elkaar zet. En dat valt met C++ vies tegen.

Kijk simpele objectjes met wat methods er in, prima gaat nog wel, de rest in C. Dat is wat arduino ook doet, maar zit op vele plaatsen heel bedroevend in elkaar. Zelfs de meest simpele dingen kun je maar een keer instantieren.

Op 16 april 2020 14:30:59 schreef deKees:
En volatile moet inderdaad wel eigenlijk, ook al zal het hier weinig verschil maken.

Nou maakt wel degelijk verschil, het werkt dan zonder optimalisatie. Zet je optimalisatie aan stort de boel in. En wat denk je: de compiler krijgt de schuld. Echt niet, gewoon niet goed gedeclareerde variable(n) die zowel in de ISR als in de main code zitten.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

Inderdaad, references en member funkties zijn gewoon handig. Waarom zou je dat willen blokkeren.

De vraag is alleen hoever moet je gaan. En die keuze maakt ieder voor zich.

trix

Golden Member

en ik ben er blij mee, moet mijn eigen er nog in verdiepen, of ik het ook begrijp.

Op 16 april 2020 12:57:14 schreef henri62:
Hoe zit het nu met je uart/rs485 spul, werkt dat nu?

ja...dat werkt :) vandaar dat ik met de volgende stap bezig ben, het versturen van 900 bytes over de RS 485.
eerst schrijven/vullen van die 900 bytes. en dan versturen.

[Bericht gewijzigd door trix op donderdag 16 april 2020 16:58:33 (23%)

eigenwijs = ook wijs
trix

Golden Member

valt niet mee om de code te ontrafelen, het is mij o.a. niet helemaal duidelijk hoe de bitmask tot stand komt:

c code:


uint8_t BitMask = (0x80 >> (BitCounter & 0x03));

die 0x03 begrijp ik niet, waar komt die vandaan ?

eigenwijs = ook wijs
bprosman

Golden Member

Op 16 april 2020 16:47:10 schreef trix:
en ik ben er blij mee, moet mijn eigen er nog in verdiepen, of ik het ook begrijp.

[...]

ja...dat werkt :) vandaar dat ik met de volgende stap bezig ben, het versturen van 900 bytes over de RS 485.
eerst schrijven/vullen van die 900 bytes. en dan versturen.

Moet dat synchroon ? In dat geval zit ik namelijk te denken aan een output buffer die door een interrupt gestuurde seriele routine geleegd wordt.

De jongere generatie loopt veel te vaak zijn PIC achterna.

Effe nagedacht: Je kunt net zo goed alle bytes gewoon achter elkaar in een array van 900 bytes gooien?
Effectief hetzelfde als een array van 300x3 bytes.

Dus alleen de bitjes van een byte bij elkaar harken, dan wegschrijven en counter ophogen, is de counter 900 ben je aan het einde.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.

code:


uint8_t BitMask = (0x80 >> (BitCounter & 0x03));

0x03 is binair 0b00000011
Dus de onderste 2 bits staan op.

Bitcounter telt van 0 naar 24. oftwel 3 keer 8.
Om tot 8 te tellen (0 .. 7) heb je 3 bits nodig.
Dus dan moet je maskeren met 0b0000111, oftwel 0x07.

Die 0x03 is dus fout. Moet 0x07 zijn.

trix

Golden Member

maar bitcounter kan toch hoger worden dan 8 ? als hij b.v. 14 is klopt het toch niet meer ?

eigenwijs = ook wijs

Klopt, maar 'bitcounter & 0x07' gebruikt alleen de onderste 3 bits, en die lopen telkens van 0 naar 7 en dan weer naar 0.

trix

Golden Member

maar waarom zou die dan bij 7 naar 0 gaan en niet gewoon naar 8 ?

c code:


ColumnData *MyColumn = &Columns[column_counter - 1]

(schrijfwijze van hendri62 overgenomen.)

klopt het als ik zeg:
mycolumn is hier een pointer, die naar (stel dat column-counter 200 is) het adres wijst waar de waarde van: column_counter 200 staat.

eigenwijs = ook wijs
PE9SMS

Special Member

bitcounter kan wel 8 worden maar dan is (bitcounter & 0x07) weer 0.

This signature is intentionally left blank.
trix

Golden Member

ja, en ook bij 9-10 en 11. maar dan gebeurt er bij 8-9-10 en 11 toch ook niks ?

eigenwijs = ook wijs

'bitcounter & 0x07' kan niet naar 8 want alleen de laagste 3 bits blijven intact. De hogere bits worden nul.

Binair ziet dat er zo uit:

0b00001000 bitcounter = 8
0b00000111 Masker = 0x07
0b00000000 Resultaat na '&'

0b00001001 bitcounter = 9
0b00000111 Masker = 0x07
0b00000001 Resultaat na '&'

0b00001010 bitcounter = 10
0b00000111 Masker = 0x07
0b00000010 Resultaat na '&'

0b00001011 bitcounter = 11
0b00000111 Masker = 0x07
0b00000011 Resultaat na '&'

trix

Golden Member

aaaaah,...op die manier,..zo had ik er nog niet naar gekeken.

even nog onder de aandacht brengen als het mag, anders zakt het denk ik te ver weg.

c code:


ColumnData *MyColumn = &Columns[column_counter - 1]

(schrijfwijze van hendri62 overgenomen.)

klopt het als ik zeg:
mycolumn is hier een pointer, die naar (stel dat column-counter 200 is) het adres wijst waar de waarde van: column_counter 200 staat.

eigenwijs = ook wijs