Daar ik zeker geen C-specialist ben kom ik hier even niet uit. Dit compileerde in Keil (8051) prima maar in GCC (AVR) krijg ik een melding :


//*****************************************************************************
// Define segments connected to SAA1064
//*****************************************************************************
#define _A  0x01        //   - A - 
#define _B  0x02        // |       |
#define _C  0x04        // F       B
#define _D  0x08        // |       |
#define _E  0x10        //   - G - 
#define _F  0x20        // |       |
#define _G  0x40        // E       C
#define _DP 0x80        // |       |
                        //   - D -  DP


//*****************************************************************************
// Define Character set
//*****************************************************************************
unsigned char code charset[] = {        // Definition Character Set
    _A+_B+_C+_D+_E+_F   ,               //  0 = CHAR_0           (0x00)
       _B+_C            ,               //  1 = CHAR_1           (0x01)
    _A+_B+   _D+_E+   _G,               //  2 = CHAR_2           (0x02)
    _A+_B+_C+_D+      _G,               //  3 = CHAR_3           (0x03)
       _B+_C+      _F+_G,               //  4 = CHAR_4           (0x04)
    _A+   _C+_D+   _F+_G,               //  5 = CHAR_5           (0x05)
    _A+   _C+_D+_E+_F+_G,               //  6 = CHAR_6           (0x06)
    _A+_B+_C            ,               //  7 = CHAR_7           (0x07)
    _A+_B+_C+_D+_E+_F+_G,               //  8 = CHAR_8           (0x08)
    _A+_B+_C+_D   +_F+_G,               //  9 = CHAR_9           (0x09)
    _A+_B+_C+   _E+_F+_G,               // 10 = CHAR_A           (0x0a)
          _C+_D+_E+_F+_G,               // 11 = CHAR_b           (0x0b)
             _D+_E+   _G,               // 12 = CHAR_c   'c'     (0x0c)
       _B+_C+_D+_E+   _G,               // 13 = CHAR_d           (0x0d)
    _A+      _D+_E+_F+_G,               // 14 = CHAR_E           (0x0e)
    _A+         _E+_F+_G,               // 15 = CHAR_F           (0x0f)
       _B+_C+   _E+_F+_G,               // 16 = CHAR 'H'         (0x10)
             _D+_E+_F   ,               // 17 = CHAR 'L'         (0x11)
                _E      ,               // 18 = CHAR 'i'         (0x12)
    _A+_B+_C+   _E+_F   ,               // 19 = CHAR 'M'         (0x13)
             _D+_E+_F+_G,               // 20 = CHAR 't'         (0x14)
    _A+_B+      _E+_F+_G,               // 21 = CHAR 'P'         (0x15)
          _C+_D+_E+   _G,               // 22 = CHAR_'o'         (0x16)
          _C+   _E+   _G,               // 23 = CHAR 'n'         (0x17)
    _A+   _C+_D+   _F+_G,               // 24 = CHAR 'S'         (0x18)
    _A+         _E+_F   ,               // 25 = CHAR 'R'         (0x19)
    _A+      _D+_E+_F   ,               // 26 = CHAR 'C'         (0x1a)
                _E+   _G,               // 27 = CHAR 'r'         (0x1b)
       _B+_C+_D,                        // 28 = CHAR 'J'         (0x1c)
          _C+_D+_E                      // 29 = CHAR 'u'         (0x1d)
                               };

Ik ben nog veel minder C-specialist, toch maar een gok in het wilde weg: zou het kunnen dat "charset" een gereserveerde term is voor de ene compiler maar niet voor de andere?

[ edit @hieronder: die "code" vond ik ook al zo raadselachtig. Maar dat de andere compiler het wel vreet lijkt er toch op te wijzen dat het iets zou kunnen betekenen... ]

unsigned char code charset[]

Hier benoem je een variabele code als unsinged char.
Maar de variabel charset[] is voor de compiler onbekend.
De compilerverteld je dat ook door te zeggen expected initializer before charset[].

Moet dat niet gewoon aan elkaar met een _ ertussen?
code_charset[]

PS het zou ook kunnen zijn wat Paulina_B zegt dat het voor de ene compiler een bekend key-word is en voor de andere niet. Deze compiler zegt je duidelijk dat hij het key-word niet kent en verwacht van je dat je hem verteld welk type variabele het is.

[Bericht gewijzigd door benleentje op (30%)]

Op zondag 28 januari 2024 18:27:54 schreef benleentje:
unsigned char code charset[]

Hier benoem je een variabele code als unsinged char.
Maar de variabel charset[] is voor de compiler onbekend. Moet dat niet gewoon aan elkaar met een _ ertussen?
code_charset[]

Dit is toch juist de regel om de variabele "charset[]" (array) te declareren ?

Als de compiler m al kende zou ik een multiple definition error krijgen.

//Edit, oh wacht, ja, dit was om de charset in het 8051 code memory te declareren, dat zal bij GCC niet kunnen.

unsigned char charset[] = werkt gewoon wel.

//Edit2
GCC kent het ook :
const unsigned char charset[] PROGMEM = {

Dit is toch juist de regel om de variabele "charset[]" (array) te declareren ?

Alleen zoals Paulina-B al zei wanner de compiler weet wat code charset[] is. Maar deze compiler weet van niets.

Als je naar de volgende regel kijkt:
unsigned char code charset[]

staan daar eigenlijk 2 variabelen de variabele code die netjes gedeclareerd word en de variabele charset[] die niet gedeclareerd word.

Dus even opzoeken hoe je dat voor deze compiler wel moet doen.

Op zondag 28 januari 2024 18:37:56 schreef benleentje:
[...]Alleen zoals Paulina-B al zei wanner de compiler weet wat code charset[] is. Maar deze compiler weet van niets.

Als je naar de volgende regel kijkt:
unsigned char code charset[]

staan daar eigenlijk 2 variabelen de variabele code die netjes gedeclareerd word en de variabele charset[] die niet gedeclareerd word.

Dus even opzoeken hoe je dat voor deze compiler wel moet doen.

Het zat in het woordje "code" , Keil gebruikt dat om de variabele "charset" in het code memory te duwen. GCC niet.

Maar dankzij de discussie / hints hier schoot het me te binnen.

Ok maar komt het dan nu ook in het programma geheugen te staan of in het ram geheugen?

Op zondag 28 januari 2024 18:46:34 schreef benleentje:
Ok maar komt het dan nu ook in het programma geheugen te staan of in het ram geheugen?

Ik had een edit gemaakt :

//Edit2
GCC kent het ook maar dan net anders:
const unsigned char charset[] PROGMEM = {

PROGMEM is typisch iets voor AVR voor zover ik weet.

Ik gebruik voor (ARM) gcc ook vaak dit om er voor te zorgen dat het in 'code' space terecht komt:


static const char bla[] = { ... } ;

Net even in de Arduino IDE getest en dat werkt ook voor AVR (Arduino nano).

Op zondag 28 januari 2024 19:26:29 schreef KGE:
Ik gebruik voor (ARM) gcc ook vaak dit om er voor te zorgen dat het in 'code' space terecht komt:


static const char bla[] = { ... } ;

Net even in de Arduino IDE getest en dat werkt ook voor AVR (Arduino nano).

Dat is de C standaard manier zonder gebruik te hoeven maken van compiler directives.

Op zondag 28 januari 2024 18:48:03 schreef bprosman:
[...]
Ik had een edit gemaakt :

//Edit2
GCC kent het ook maar dan net anders:
const unsigned char charset[] PROGMEM = {

Technisch niet 100% correct: gcc kent het niet, maar de AVR setup-stuff definieert "PROGMEM" zodat het compact te schrijven is, te onthouden is en doet wat je wilt.

In 1987 heb ik in het verslag van een practicum programmeer opdracht geschreven dat

asm ("nop");

in runtime kennelijk die instructie ging assembleren en uitvoeren. Maar de PDP 11 compiler in die tijd zag dat als special case en deed dat dus "compile time". De professor wees me daarop tijdens de bespreking. (Die fout heeft het cijfer kennelijk niet beinvoed.).

Op zondag 28 januari 2024 19:35:38 schreef Bobosje:
[...]

Dat is de C standaard manier zonder gebruik te hoeven maken van compiler directives.

Maar even veranderd dan.

@ Bobosje : Dat werkt wel, maar dan gaat het niet naar code.

De Avr-gcc compiler werkt -net als de gcc compiler voor linux- met attributes om bepaalde stukjes code aan bepaalde geheugens te koppelen. Die attributes worden door de compiler doorgesluisd naar de linker die dan de code in het gevraagde stuk geheugen plaatst.

In dit geval moet dat met :

const unsigned char charset[] __attribute__((__progmem__)) = {

Maar dat kan ook met PROGMEM. Dat is een macro, gedefinieerd in "avr/pgmspace.h", die uiteindelijk hetzelfde effect heeft.

Op dezelfde manier kun je ook een stukje code doorsluizen naar Eeprom mbv EEMEM.

Huhuh, ik leer hier bij aan snel tempo, al heb ik voorlopig meer vragen dan antwoorden. Punten die ik me nog helemaal niet had afgevraagd worden hier beantwoord, ik heb werk aan de winkel. Dank voor het inzicht dat ik (hopelijk) ga verwerven.

Op zondag 28 januari 2024 23:01:55 schreef deKees:
@ Bobosje : Dat werkt wel, maar dan gaat het niet naar code.

De hoeveelheid RAM is heden ten dage geen issue meer en de static const variabelen declaraties kunnen dan tijdens de code init sectie gerust daar naar toe worden gekopieerd.

Als de static const variabelen declaraties op de standaard C wijze gedaan worden dan is het gebruik van dit soort variabelen veel makkelijker en bovendien C portable naar andere platforms.

Op maandag 29 januari 2024 00:04:22 schreef Bobosje:
[...]

De hoeveelheid RAM is heden ten dage geen issue meer en de static const variabelen declaraties kunnen dan tijdens de code init sectie gerust daar naar toe worden gekopieerd.

Voor embeddded systemen is dat absoluut niet waar en daar heb je vrij snel last van dat de boel out of memory loopt. Dan bedoel ik flash CPUs zoals de ST32 series LPC en dat soort CPU's met embedded flash van 64 KiB tot 1 a 2 MiB flash en een paar honderd KiB ram. Met bijvoorbeeld FreeRTOS en een redelijke applicatie met vanalles erin zoals een web interface etc. Nou dat kun je niet slordig zijn in je code want dan krijg je het er echt niet in.
Ik heb het dan dus niet over een linux embedded systeem a la RPi (ook daar is de boel vaak beperkt maar een stuk minder).

Bij een 8051 heb je zo weinig resources dat je alles uit de kast moet halen om en groter programma erin te krijgen.

Maar eh: Waarom overstappen op GCC? Keil op een 8051 is zo ongeveer de beste compiler die je kunt kopen voor een 8051. Daar kun je handmatig in assembly bijna niet van winnen. Maar die kost inderdaad geld.

Als er (nog) voldoende RAM beschikbaar is dan maakt dat het programmeren veel makkelijker. Het moeten opentrekken van truckendozen om in C alle static const variabelen in een ROM sectie te kunnen benaderen heeft ook zijn nadelen zoals extra overhead in de code en de nodige extra delays in het benaderen van die variabelen.

En wie gebruikt er nu voor nieuwe serieuze toepassingen nog µP's uit (ver) vervlogen tijden...

De hoeveelheid RAM is heden ten dage geen issue meer

Er zijn nog altijd genoeg processors waar de hoeveelheid ram nog wel een issue is. :)

Op maandag 29 januari 2024 01:18:51 schreef deKees:
[...]

Er zijn nog altijd genoeg processors waar de hoeveelheid ram nog wel een issue is. :)

En sommige hebben nog niet genoeg met 16GB RAM... :)

Op maandag 29 januari 2024 00:57:20 schreef Bobosje:
Als er (nog) voldoende RAM beschikbaar is dan maakt dat het programmeren veel makkelijker. Het moeten opentrekken van truckendozen om in C alle static const variabelen in een ROM sectie te kunnen benaderen heeft ook zijn nadelen zoals extra overhead in de code en de nodige extra delays in het benaderen van die variabelen.

En wie gebruikt er nu voor nieuwe serieuze toepassingen nog µP's uit (ver) vervlogen tijden...

Ver verleden ? Het gaat om een Atmel AVR. En voor een instelbare droogtrommel timer die na muntinworp 55 minuten (en 5 minuten naloop) moet draaien heb ik niet echt een Raspberry PI 5 voor nodig.

Op zondag 28 januari 2024 19:35:38 schreef Bobosje:


static const int bla = ...

Dat is de C standaard manier zonder gebruik te hoeven maken van compiler directives.

Het is absoluut standaard C, maar het effect dat data op deze manier in ROM terecht komt is een bijeffect van compiler-optimisatie:
Doordat de static locale scope geeft, en de compiler kan zien dat de variablele lokaal niet veranderd kan die de variabele wegoptimaliseren en er een (code-)constante van maken.

Op zich is dat niet erg, maar deze methode heeft een vervelende bijwerking:

Dingen die als 'static const' gedefinieerd zijn zijn alleen zichtbaar binnen de C-file waarin ze staan.

Ik heb een project waarin verschillende plaatjes (icoontjes) mee moeten in de binary. Die plaatjes zijn in de bron gewoon image-files, en daar kan gcc niets mee. Dus ik heb een scriptje, dat door de make-file aangeroepen wordt om er een .C file van te maken.

'static const' werkt daar niet, want dat plaatje wordt natuurlijk in een andere C file gebruikt dan de gegenereerde file waar het gedefinieerd word.

PROGMEM werkt daar wel, want dat verteld de linker(!) dat dit in ROM mag, en niet gekopieerd hoeft te worden.

Het mooie is dat dit in de praktijk ook redelijk portable is, want GCC kan __attribute__ gebruiken voor iedere CPU/MCU, en andere compilers hebben vergelijkbare truken.

Dus porten is alleen een kwestie van de definitie van PROGMEM aanpassen, en eventueel een linker-script aanpassen. En de laatste is per definitie processor- en linker-specifiek.

Extra voordeel is dat deze methode veel meer controle geeft: Je kunt in het linkerscripts nauwkeurig instellen waar de read-only data terecht komt (nuttig als je snel en traag ROM hebt, of het graag op een specifieke plek wil hebben omdat een andere binary (bootloaders!) erbij moet.)

Op maandag 29 januari 2024 00:32:57 schreef henri62:
[...] Voor embeddded systemen is dat absoluut niet waar en daar heb je vrij snel last van dat de boel out of memory loopt. Dan bedoel ik flash CPUs zoals de ST32 series LPC en dat soort CPU's met embedded flash van 64 KiB tot 1 a 2 MiB flash en een paar honderd KiB ram. Met bijvoorbeeld FreeRTOS en een redelijke applicatie met vanalles erin zoals een web interface etc. Nou dat kun je niet slordig zijn in je code want dan krijg je het er echt niet in.
Ik heb het dan dus niet over een linux embedded systeem a la RPi (ook daar is de boel vaak beperkt maar een stuk minder).

Bij een 8051 heb je zo weinig resources dat je alles uit de kast moet halen om en groter programma erin te krijgen.

Maar eh: Waarom overstappen op GCC? Keil op een 8051 is zo ongeveer de beste compiler die je kunt kopen voor een 8051. Daar kun je handmatig in assembly bijna niet van winnen. Maar die kost inderdaad geld.

Als het een 8051 (achtige) geweest zou zijn was ik ook lekker bij de Keil compiler gebleven ( ik heb die, weliswaar misschien niet de laatste maar een prima versie) , maar het is een Atmel AVR , waarbij ik CodeIO gebruik ( eigenlijk de Arduino omgeving). De reden dat ik de 8051 noemde is dat ik voor die processor al eens een SAA1064 , (I2C LED display driver) aansturing gemaakt had en (achteraf) iets te veel copy / paste gedaan had met de code. Het moet in een bestaand kastje waar al een SAA1064 display print in zit ( anders had ik daar ook iets moderners voor genomen).

Op maandag 29 januari 2024 00:04:22 schreef Bobosje:
[...]

De hoeveelheid RAM is heden ten dage geen issue meer en de static const variabelen declaraties kunnen dan tijdens de code init sectie gerust daar naar toe worden gekopieerd.

Dat is niet zo als je de mogelijkheden van die moderne processoren gaat gebruiken.

Ik heb een RP2040 met 4M flash en daar zit een SPI 320x240 display aan. Professioneel statusicoontjes laten maken en die PNGtjes decodeer ik en stuur ik dan naar het display. Maar dat zijn dus wel 5 PNGs van 80k of zoiets. Dit moet je op een atmega328 niet proberen.

Als de static const variabelen declaraties op de standaard C wijze gedaan worden dan is het gebruik van dit soort variabelen veel makkelijker en bovendien C portable naar andere platforms.

Dat Dat ben ik met je eens. Het is jammer dat de AVR / pic / 8051 compilers de tegenwoordig standaard manier niet snappen.

Deels is dat ook omdat bijvoorbeeld op AVR je niet zomaar a = b kan doen als de b variable in flash zou staan. Dat moet dan iets als a = read_from_flash(b); worden.