LCD-Adresse im C-Programm

#7873692
Lesenswert?

Hallo

ich möchte ein LC-Display mit einem Mikrocontroller AT89C51RC2 ansteuern. Die Hardware-Adresse von dem LC-Display ist: „0xF800“. Mit dem Assembler-Code: LCD_ADR EQU 0F800H ; Basisadresse LCD

MOV DPTR,#LCD_ADR

kann ich die entsprechende Adresse ansteuern. Wie kann ich das aber im C-Programm realisieren ?

Mein Compiler: SDCC Version 4.5.0

MfG Juergen

#7874062
Lesenswert?

Hallo, vielen Dank für Eure Hinweise. Damit habe ich aber folgende Probleme:

1
__xdata __at (0xF800) uint8_t LCD_ADR;

Compiler-Meldung:

1
main.c:42: error 1: Syntax error, declaration ignored at 'uint8_t'
2
main.c:42: syntax error: token -> 'LCD_ADR' ; column 37
1
*((uint8_t*)0xF800) = data;

Compiler-Meldung:

1
main.c:44: syntax error: token -> '*' ; column 11

MfG Juergen

#7874533
Lesenswert?

Hi Juergen!

Wenn das LCD mehrere Register hat (was ich jetzt mal annehme), dann könntest du auch eine Struktur erstellen und mit Zeiger auf die einzelnen Register zugreifen.

Ein Beispiel zwar nicht für den AT89C51RC2, aber für einen STM32:

1
struct stm32_pwr {
2
    volatile unsigned int CR1;              /* PWR control register 1 */
3
    volatile unsigned int CSR1;             /* PWR control status register 1 */
4
    volatile unsigned int CR2;              /* PWR control register 2 */
5
    volatile unsigned int CR3;              /* PWR control register 3 */
6
    volatile unsigned int CPU1CR;           /* PWR CPU1 control register */
7
    volatile unsigned int CPU2CR;           /* PWR CPU2 control register */
8
    volatile unsigned int D3CR;             /* PWR D3 domain control register */
9
    volatile unsigned int WKUPCR;           /* PWR wakeup clear register */
10
    volatile unsigned int WKUPFR;           /* PWR wakeup flag register */
11
    volatile unsigned int WKUPEPR;          /* PWR wakeup enable and polarity register */
12
};
13

14
#define PWR_BASE                        0x58024800
15
#define PWR                             ((struct stm32_pwr *) PWR_BASE)

Zugreifen auf die einzelnen Register kannst du dann so:

1
PWR->CR1 = 0x1234ABCD;

Diesen Tipp hat mir ein Entwickler auf der Mailingliste von GCC gegeben.

Ich muss zugeben, dass ich dieser Lösung vorher nicht vertraut habe, da ich die Vermutung hatte, dass der erzeugte Maschinencode relativ groß sein wird bzw. der Compiler die ganze Struktur in die Objektdatei hinterlegt, wenn zum Beispiel nur ein Register tatsächlich verwendet (man darauf zugreift) wird. Weiß nicht wie SDCC das handhabt aber GCC optimiert hier doch automatisch den Zugriff bzw. hinterlegt nicht die ganze Struktur.

#7874572
Lesenswert?

Oliver S. schrieb:

Johannes schrieb:

Wenn das LCD mehrere Register hat (was ich jetzt mal annehme),

und diese alle hintereinander im Adressraum liegen,

Ja, betreffend meines Beispieles.

Man kann sich aber bei reservierten bzw. unbenutzten Speicherstellen behelfen. So zum Beispiel:

1
struct some_register {
2
    volatile unsigned int CR;
3
    volatile unsigned int reserved0;
4
    volatile unsigned int CSR;
5
    volatile unsigned int reserved1[10];
6
    volatile unsigned int IER;
7
};

So macht es zum Beispiel STMicroelectronics in seinen Headern. Sollte selbsterklärend sein.

#7874588
Lesenswert?

Oliver S. schrieb:

Johannes schrieb:

und diese alle hintereinander im Adressraum liegen,

Ja, betreffend meines Beispieles.

Nur gehst hier um das Display von Jürgen.

Ja das ist mir durchaus bewusst.

Ich weiß nicht wie Juergen die Hardware aufgebaut hat. Wenn aber die LCD Register in den Adressraum des Mikrocontrollers gemappt sind, dann ist diese Lösung IMHO komfortabel. Ist aber IMHO egal um welche Peripherie (LCD, etc.) es sich jetzt genau handelt.

Ich jedenfalls finde es eine schöne Lösung. Obwohl ich persönlich nicht so weit gehe und andere Lösungen abwerte, wie dieser GCC Entwickler es geschrieben hat (hat andere Lösungen als schlechter Programmierstil deklariert).

Sollte nur ein Tipp sein. Wie es Juergen löst, bleibt ihm selbst überlassen.

#7874643
Lesenswert?

Oliver S. schrieb:

Johannes schrieb:

Ich jedenfalls finde es eine schöne Lösung.

Ich sag mal so: die Lösung birgt einige Fallstricke, gerade auf deinem Prozessor. Wenn sich denen bewusst ist, ist alles gut, als copy-Paste-Programmierer kann man aber reinfallen.

Bitte um Erklärung der Fallstricke. Mir persönlich fällt im Moment nichts ein, habe aber immer ein offenes Ohr bezüglich Verbesserungen.

Oliver P.S. Welches LCD-Display hat eigentlich 32-Bit-Register?

Wer oder was garantiert, dass der C Datentyp "int" immer 32Bit breit sein muss? Der C Standard mal nicht, der gibt nur eine minimale Grenze an. Für zum Beispiel "int" nur 16-Bit. Bei nicht standardkonformen Anwendungen auch mal 8-Bit. Da gibt es den Compiler switch "-mint8" bei AVR zum Beispiel mit GCC.

Auszug aus dem C11 Standard:

1
The contents of the header <limits.h> are given below, in alphabetical order. The minimum magnitudes shown shall be replaced by implementation-defined
2
magnitudes with the same sign. The values shall all be constant expressions suitable for use in #if preprocessing directives. The components are 
3
described further in 5.2.4.2.1.
4

5
#define CHAR_BIT 8
6
#define CHAR_MAX UCHAR_MAX or SCHAR_MAX
7
#define CHAR_MIN 0 or SCHAR_MIN
8
#define INT_MAX +32767
9
#define INT_MIN -32767
10
#define LONG_MAX +2147483647
11
#define LONG_MIN -2147483647
12
#define LLONG_MAX +9223372036854775807
13
#define LLONG_MIN -9223372036854775807
14
#define MB_LEN_MAX 1
15
#define SCHAR_MAX +127
16
#define SCHAR_MIN -127
17
#define SHRT_MAX +32767
18
#define SHRT_MIN -32767
19
#define UCHAR_MAX 255
20
#define USHRT_MAX 65535
21
#define UINT_MAX 65535
22
#define ULONG_MAX 4294967295
23
#define ULLONG_MAX 18446744073709551615

Quelle: http://port70.net/%7Ensz/c/c11/n1570.pdf [Annex E - Implementation limits]

Zu einem LCD-Controller mit 32-Bit Registern hab ich mal ChatGPT befragt und der hat ein paar ausgegeben. War zu Faul um selber zu suchen:

Meine Eingabe bei ChatGPT:

1
Hi! Exists there LCD controller with 32 bit registers?

Ausgabe von ChatGPT:

1
Hi! Yes, there **are LCD controllers with 32-bit registers**—especially in modern microcontrollers and display controllers that are designed for higher-resolution displays or more complex graphics.
2

3
### Examples of LCD controllers with 32-bit registers:
4

5
1. **STM32 Microcontrollers (STMicroelectronics)**
6

7
   * Many STM32 MCUs (like STM32F4, STM32F7, STM32H7 series) have **LCD-TFT controllers** (LTDC) with 32-bit registers.
8
   * Example: The **LTDC** controller in STM32F429 has 32-bit wide registers used for pixel formatting, layer configuration, and frame buffer control.
9

10
2. **SSD1963 LCD Controller**
11

12
   * While its data bus can be 8/16/24-bit depending on configuration, **its internal control registers are accessed through 16-bit commands**, but some registers can act like 32-bit when combined.
13

14
3. **RA8875 / RA8889 (RAiO)**
15

16
   * These are advanced TFT LCD controllers that sometimes deal with 32-bit-wide data when interfacing with a 32-bit system, although their register interface is typically 8/16-bit.
17

18
4. **FT81x series (FTDI / Bridgetek)**
19

20
   * The FT810/FT811/FT812/FT813 (EVE - Embedded Video Engine) family uses **32-bit aligned registers and memory access**, especially for graphics command FIFO and display list processing.
21

22
5. **FPGAs with Soft or Hard LCD Controllers**
23

24
   * In custom or SoC FPGA designs (e.g., Xilinx Zynq or Intel/Altera SoC FPGAs), the LCD controller (AXI-based or custom) often uses **32-bit or even 64-bit registers** for configuration and data.
25

26
---
27

28
### Why 32-bit Registers?
29

30
* **More efficient access** on 32-bit or 32-bit+ architectures.
31
* Allows configuration of multiple fields (e.g., x/y position, color depth, layer blending) in one register.
32
* Easier to align with 32-bit graphics memory addressing.
33

34
Would you like a specific recommendation based on resolution, interface type (RGB, MIPI, SPI, etc.), or target platform (MCU, FPGA, etc.)?

Wobei zu mindestens die Ausgabe vom RA8875 nicht stimmt. Die sind laut Datenblatt nur 8-Bit breit. ChatGPT behauptet 8/16Bit.

#7874675
Lesenswert?

Johannes schrieb:

P.S. Welches LCD-Display hat eigentlich 32-Bit-Register?

Wer oder was garantiert, dass der C Datentyp "int" immer 32Bit breit sein muss? Der C Standard mal nicht,

<blafusel>

Johannes schrieb:

STM32

Da IST ein int 32 Bit breit, und genau um den im Zusammenhang mit deinem struct ging die Frage.

Ebenso war die Frage nach einem LCD mit 32 Bit-Registern genau auf dein Beispiel bezogen.

Der Spaß fängt genau dann an, wenn die Register 8Bit breit sind (was sie üblicherweise auch sind).

Oliver

#7874720
Lesenswert?

OT:

Eigentlich sollte ich nicht mehr in diesem Thread schreiben, aber...

Oliver S. schrieb:

Johannes schrieb:

P.S. Welches LCD-Display hat eigentlich 32-Bit-Register?

Wer oder was garantiert, dass der C Datentyp "int" immer 32Bit breit sein muss? Der C Standard mal nicht,

<blafusel>

Hhm, also ist das was im C Standard steht laut dir ein Blödsinn.

Okay, deine Meinung. Aber nicht böse werden, ich vertraue dem C Standard und dem Compiler was tatsächlich die breite für einen Datentyp ist etwas mehr.

GCC-AVR (gekürzt):

1
$ touch blah.c
2
$ avr-gcc -mmcu=atmega1284p -dM -E blah.c | grep __SIZEOF | sort
3
#define __SIZEOF_DOUBLE__ 4
4
#define __SIZEOF_FLOAT__ 4
5
#define __SIZEOF_INT__ 2
6
#define __SIZEOF_INT24__ 3
7
#define __SIZEOF_LONG__ 4
8
#define __SIZEOF_LONG_DOUBLE__ 8
9
#define __SIZEOF_LONG_LONG__ 8
10
#define __SIZEOF_POINTER__ 2
11
#define __SIZEOF_PTRDIFF_T__ 2
12
#define __SIZEOF_SHORT__ 2
13
#define __SIZEOF_SIZE_T__ 2
14
#define __SIZEOF_WCHAR_T__ 2
15
#define __SIZEOF_WINT_T__ 2

Ist jetzt also sinnlos laut dir vorher zu überprüfen mit den GNU autoconf Tools zum Beispiel (./configure) was jetzt der Compiler für welche Breite jetzt den jeweiligen Datentyp verwendet, wenn man zum Beispiel einen Code für mehrere Architekturen schreibt.

Johannes schrieb:

STM32

Da IST ein int 32 Bit breit, und genau um den im Zusammenhang mit deinem struct ging die Frage.

Ja, auf der ARMv7-M Architektur ist das der Datentyp tatsächlich 32 Bit breit. Aber ich weiß jetzt nicht auf was du mit "<blafusel>" hinaus willst. Passt ja laut dir nicht...

Ebenso war die Frage nach einem LCD mit 32 Bit-Registern genau auf dein Beispiel bezogen.

Der Spaß fängt genau dann an, wenn die Register 8Bit breit sind (was sie üblicherweise auch sind).

Nein, eben nicht. Zum Beispiel beim integrierten LCD Controller von der STM32 Serie nicht. Da sind es nativ 32 Bit.

Insbesondere hast du noch nicht erklärt, warum meine gepostete Lösung Fallstricken hat.

Ich weiß nicht wie alt du bist, aber angefangen mit 16 Jahren verwende ich C seit 25 Jahren auf unterschiedlichen Architekturen. Aber es gibt noch immer Dinge die ich nicht weiß. Also bitte die Fallstricken, danke! Das einzige auf was achten muss ist ein jeweiliges Alignment, was mir einfällt.

PS:

SDCC gibt leider keine ausführliche Angabe wie GCC von dem vom TE verwendeten Controller:

1
$ sdcc -mmcs51 -pat89c51 -dM -E blah.c
2
#define __SDCC_mcs51 1
3
#define __STDC_VERSION__ 201112L
4
#define __STDC_HOSTED__ 0
5
#define __SDCCCALL 0
6
#define __STDC_UTF_16__ 1
7
#define __SDCC_VERSION_MINOR 2
8
#define __SDCC_MODEL_SMALL 1
9
#define __STDC_ISO_10646__ 201409L
10
#define __SDCC_VERSION_PATCH 0
11
#define SDCC 420
12
#define __SDCC_FLOAT_REENT 1
13
#define __SDCC_VERSION_MAJOR 4
14
#define __STDC_NO_VLA__ 1
15
#define __SDCC 4_2_0
16
#define __STDC_UTF_32__ 1
17
#define __STDC_NO_THREADS__ 1
18
#define __SDCC_CHAR_UNSIGNED 1
19
#define __STDC_NO_ATOMICS__ 1
20
#define __STDC__ 1
21
#define __SDCC_REVISION 13081
22
#define __STDC_NO_COMPLEX__ 1

Außerdem schrieb ich dass das Beispiel FÜR einen STM32 ist. Ein bisschen selber nachdenken, setze ich hier eigentlich voraus. Ist ja nicht so, dass sich hier Menschen tummeln, für die das komplett Neuland ist.

Aber vermutlich ist eh wieder alles was ich geschrieben habe "<blafusel>".

#7874745
Lesenswert?

Johannes schrieb:

Okay, deine Meinung. Aber nicht böse werden, ich vertraue dem C Standard und dem Compiler was tatsächlich die breite für einen Datentyp ist etwas mehr.

Johannes, Du hast geschrieben und gezeigt, dass Du bisher wenig Kenntnisse vom C-Compiler hast, Dich aber sehr gewissenhaft einarbeitest. Das ist wirklich positiv. Ob Oliver ein Troll ist oder einfach ein paar Jahre mehr Erfahrung hat und gewisse Dinge voraussetzt, ist unerheblich.

Olivers Frage war tatsächlich, welches Display genau Dein Beispiel benutzt, also welches Display (bzw. welcher Controller) 32-bittig organisiert ist. Es war keine Bewertung der vielen anderen Dinge, die Du geschrieben hast.

#7874925
Lesenswert?

Bruno V. schrieb:

Johannes schrieb:

Okay, deine Meinung. Aber nicht böse werden, ich vertraue dem C Standard und dem Compiler was tatsächlich die breite für einen Datentyp ist etwas mehr.

Johannes, Du hast geschrieben und gezeigt, dass Du bisher wenig Kenntnisse vom C-Compiler hast, Dich aber sehr gewissenhaft einarbeitest. Das ist wirklich positiv. Ob Oliver ein Troll ist oder einfach ein paar Jahre mehr Erfahrung hat und gewisse Dinge voraussetzt, ist unerheblich.

Mag sein, dass ich wenige Kenntnisse vom C Compiler habe. Deshalb hab ich als ein Projekt einen eigenen C Compiler angefangen. Das Projekt ist aber noch in einer frühen Phase. Hab derzeit nur den C Lexer (aber ohne GNU bison nur reines C) und den Präprozessor geschrieben. Aber nur ein Spaßprojekt derzeit.

Olivers Frage war tatsächlich, welches Display genau Dein Beispiel benutzt, also welches Display (bzw. welcher Controller) 32-bittig organisiert ist. Es war keine Bewertung der vielen anderen Dinge, die Du geschrieben hast.

Hab ich erwähnt. Ist aber in einem SoC implementiert. LTDC bei zum Beispiel STM32H747. Diesen Controller verwendet STMicroelectronics auch in anderen Controllern, nicht nur den STM32H747.

Anbei ein Screenshot von der Register Übersicht in der TRM vom LTDC im SoC. Sehen für mich persönlich wie 32-Bit Register aus. Gibt auch eine Exception vom Cortex-M7 oder M4 wenn man hier auf Teile (unaligned) zugreift.

Und es gibt sicher auch andere.

PS: Auch wenn ich mich nicht gut mit C Compilern auskenne, dann kann ich auch sagen, dass mir bestimmte Eigenschaften von den geläufigen C Compilern nicht gefallen. Zum Beispiel - auch durch Unwissenheit - wenn ein Compiler eine Codestelle einfach weg optimiert ohne zu warnen, was aber fatale Folgen für den erzeugten Maschinencode hat.

Angehängte Dateien:
#7874988
Lesenswert?

Johannes schrieb:

Zum Beispiel - auch durch Unwissenheit - wenn ein Compiler eine Codestelle einfach weg optimiert ohne zu warnen, was aber fatale Folgen für den erzeugten Maschinencode hat.

Das ist ein gutes Beispiel für "wenig Erfahrung": Aufgabe des Compilers ist es, die Wirkung Deines Code umzusetzen, nicht die Schritte.

Also "was" Du programmierst, nicht "wie" Du es programmierst, mit sehr genauer Spezifikation, wie das "was" interpretiert wird. Sonst würden Dinge passieren, wie Du sie selbst befürchtet hast.

Johannes schrieb:

da ich die Vermutung hatte, dass der erzeugte Maschinencode relativ groß sein wird bzw. der Compiler die ganze Struktur in die Objektdatei hinterlegt, wenn zum Beispiel nur ein Register tatsächlich verwendet (man darauf zugreift) wird.

Dann wäre C-Code tatsächlich nur eine andere Form von Assembler.

#7875048
Lesenswert?

Johannes schrieb:

Ich muss zugeben, dass ich dieser Lösung vorher nicht vertraut habe, da ich die Vermutung hatte, dass der erzeugte Maschinencode relativ groß sein wird bzw. der Compiler die ganze Struktur in die Objektdatei hinterlegt, wenn zum Beispiel nur ein Register tatsächlich verwendet (man darauf zugreift) wird.

Eine Struct belegt erstmal keinen Speicher und keinen Code. Die Compiler sind nicht dumm. Wenn sie zur Compilezeit erkennen, daß du nur ein Byte zugreifst, wird auch nur dafür Code erzeugt. Hier mal ein Beispiel für ein VFD:

1
#define vfd_data  ((u8 volatile xdata*) VFD_ADDR)
2

3
bit DisplayOut( u8 c ){
4
  u8 i = VFD_MAX_BUSY;      // 250 * 8 cycle
5
  do{
6
    if( 0 == (*vfd_data & VFD_BUSY)){
7
      *vfd_data = c;      // byte to diplay
8
      return 0;        // successful
9
    }
10
  }while( --i );
11
  return 1;           // timeout
12
}

Und der daraus erzeugte Assembler:

1
             ; FUNCTION _DisplayOut (BEGIN)
2
;---- Variable 'c' assigned to Register 'R7' ----
3
;---- Variable 'i' assigned to Register 'R6' ----
4
0000 7EFA          MOV     R6,#0FAH
5
0002         ?C0003:
6
0002 90FE00        MOV     DPTR,#0FE00H
7
0005 E0            MOVX    A,@DPTR
8
0006 20E004        JB      ACC.0,?C0001
9
0009 EF            MOV     A,R7
10
000A F0            MOVX    @DPTR,A
11
000B C3            CLR     C
12
000C 22            RET     
13
000D         ?C0001:
14
000D DEF3          DJNZ    R6,?C0003
15
000F D3            SETB    C
16
0010         ?C0005:
17
0010 22            RET     
18
             ; FUNCTION _DisplayOut (END)
#7875203
Lesenswert?

Hallo Peter!

Peter D. schrieb:

Eine Struct belegt erstmal keinen Speicher und keinen Code. Die Compiler sind nicht dumm. Wenn sie zur Compilezeit erkennen, daß du nur ein Byte zugreifst, wird auch nur dafür Code erzeugt.

Muss ich zugeben, hab das vorher nicht gewusst. Bin zu mindestens davon ausgegangen, dass ein bestimmte Optimierungsstufe benötigt wird, damit zum Beispiel GCC so reagiert wie du es beschrieben hast.

Hab das vorher schon ausprobiert und Danke für dein Beispiel. Ich habe beide Varianten (mit struct und ohne) kurz verglichen. Bis auf das, dass im Assembler Code ein anderes Displacement verwendet wird, sind beide Varianten identisch von der Größe vom erzeugten Code.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren