Forum: Mikrocontroller und Digitale Elektronik u8g2 lib - SSD1309 mit Hardware-SPI aber ohne Arduino?


von Manni (Gast)


Lesenswert?

Hallo U8G2 Anwender ;)

ich komme leider irgendwie mit der Doku dieser ansonsten mega 
hilfreichen Bibliothek U8G2 nicht weiter.

Ich habe hier ein Electronic Assembly OLEDL128-6 Display mit SSD1309 
Controller. Dazu habe ich mir ausßerdem die aktuellste Version der U8G2 
Bibliothek heruntergeladen und versuche nun, "die tollsten Sachen" damit 
zu machen ;)

An und für sich komme ich damit gut zurecht und ich kann das Display im 
Software-SPI Modus mit meinem Atmega2560 prima ansteuern.
1
#define F_CPU 16000000UL
2
3
#include <avr/io.h>
4
#include <util/delay.h>
5
#include <stdio.h>
6
#include <u8g2.h>
7
8
#define DISPLAY_CLK_DIR DDRK
9
#define DISPLAY_CLK_PORT PORTK
10
#define DISPLAY_CLK_PIN PK0
11
12
#define DISPLAY_DATA_DIR DDRK
13
#define DISPLAY_DATA_PORT PORTK
14
#define DISPLAY_DATA_PIN PK1
15
16
#define DISPLAY_CS_DIR DDRK
17
#define DISPLAY_CS_PORT PORTK
18
#define DISPLAY_CS_PIN PK3
19
20
#define DISPLAY_DC_DIR DDRK
21
#define DISPLAY_DC_PORT PORTK
22
#define DISPLAY_DC_PIN PK4
23
24
#define DISPLAY_RESET_DIR DDRK
25
#define DISPLAY_RESET_PORT PORTK
26
#define DISPLAY_RESET_PIN PK2
27
28
u8g2_t u8g2;
29
30
uint8_t u8x8_avr_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
31
{
32
  uint8_t cycles;
33
34
  switch(msg)
35
  {
36
    case U8X8_MSG_DELAY_NANO:     // delay arg_int * 1 nano second
37
      // At 20Mhz, each cycle is 50ns, the call itself is slower.
38
      break;
39
    case U8X8_MSG_DELAY_100NANO:    // delay arg_int * 100 nano seconds
40
      // Approximate best case values...
41
#define CALL_CYCLES 26UL
42
#define CALC_CYCLES 4UL
43
#define RETURN_CYCLES 4UL
44
#define CYCLES_PER_LOOP 4UL
45
46
      cycles = (100UL * arg_int) / (P_CPU_NS * CYCLES_PER_LOOP);
47
48
      if(cycles > CALL_CYCLES + RETURN_CYCLES + CALC_CYCLES) 
49
        break;
50
51
      __asm__ __volatile__ (
52
      "1: sbiw %0,1" "\n\t" // 2 cycles
53
      "brne 1b" : "=w" (cycles) : "0" (cycles) // 2 cycles
54
      );
55
      break;
56
    case U8X8_MSG_DELAY_10MICRO:    // delay arg_int * 10 micro seconds
57
      for(int i=0 ; i < arg_int ; i++)
58
        _delay_us(10);
59
      break;
60
    case U8X8_MSG_DELAY_MILLI:      // delay arg_int * 1 milli second
61
      for(int i=0 ; i < arg_int ; i++)
62
        _delay_ms(1);
63
      break;
64
    default:
65
      return 0;
66
  }
67
  return 1;
68
}
69
70
uint8_t u8x8_avr_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
71
{
72
  // Re-use library for delays
73
74
  switch(msg)
75
  {
76
    case U8X8_MSG_GPIO_AND_DELAY_INIT:  // called once during init phase of u8g2/u8x8
77
      DISPLAY_CLK_DIR |= 1<<DISPLAY_CLK_PIN;
78
      DISPLAY_DATA_DIR |= 1<<DISPLAY_DATA_PIN;
79
      DISPLAY_CS_DIR |= 1<<DISPLAY_CS_PIN;
80
      DISPLAY_DC_DIR |= 1<<DISPLAY_DC_PIN;
81
      DISPLAY_RESET_DIR |= 1<<DISPLAY_RESET_PIN;
82
      break;              // can be used to setup pins
83
    case U8X8_MSG_GPIO_SPI_CLOCK:        // Clock pin: Output level in arg_int
84
      if(arg_int)
85
        DISPLAY_CLK_PORT |= (1<<DISPLAY_CLK_PIN);
86
      else
87
        DISPLAY_CLK_PORT &= ~(1<<DISPLAY_CLK_PIN);
88
      break;
89
    case U8X8_MSG_GPIO_SPI_DATA:        // MOSI pin: Output level in arg_int
90
      if(arg_int)
91
        DISPLAY_DATA_PORT |= (1<<DISPLAY_DATA_PIN);
92
      else
93
        DISPLAY_DATA_PORT &= ~(1<<DISPLAY_DATA_PIN);
94
      break;
95
    case U8X8_MSG_GPIO_CS:        // CS (chip select) pin: Output level in arg_int
96
      if(arg_int)
97
        DISPLAY_CS_PORT |= (1<<DISPLAY_CS_PIN);
98
      else
99
        DISPLAY_CS_PORT &= ~(1<<DISPLAY_CS_PIN);
100
      break;
101
    case U8X8_MSG_GPIO_DC:        // DC (data/cmd, A0, register select) pin: Output level in arg_int
102
      if(arg_int)
103
        DISPLAY_DC_PORT |= (1<<DISPLAY_DC_PIN);
104
      else
105
        DISPLAY_DC_PORT &= ~(1<<DISPLAY_DC_PIN);
106
      break;
107
    
108
    case U8X8_MSG_GPIO_RESET:     // Reset pin: Output level in arg_int
109
      if(arg_int)
110
        DISPLAY_RESET_PORT |= (1<<DISPLAY_RESET_PIN);
111
      else
112
        DISPLAY_RESET_PORT &= ~(1<<DISPLAY_RESET_PIN);
113
      break;
114
    default:
115
      if (u8x8_avr_delay(u8x8, msg, arg_int, arg_ptr))  // check for any delay msgs
116
        return 1;
117
      u8x8_SetGPIOResult(u8x8, 1);      // default return value
118
      break;
119
  }
120
  return 1;
121
}
122
123
124
125
void main(void) {
126
127
  u8g2_Setup_ssd1309_128x64_noname0_f(&u8g2, U8G2_R2, u8x8_byte_4wire_sw_spi, u8x8_avr_gpio_and_delay);      
128
  u8g2_InitDisplay(&u8g2);
129
  u8g2_SetPowerSave(&u8g2, 0);
130
  u8g2_SetContrast(&u8g2, 128);
131
132
  u8g2_ClearBuffer(&u8g2);
133
  u8g2_SetFont(&u8g2, u8g2_font_5x7_tf);
134
  u8g2_DrawStr(&u8g2, 121, 59, "Hallo Welt!");
135
  u8g2_SendBuffer(&u8g2);
136
137
  while(1) {
138
  }
139
140
}

Soweit, so minimal.

Das Display wird somit im 4-wire Software-SPI Modus betrieben.

Nun würde ich das aber gerne mit dem Hardware-SPI des Atmega2560 
betreiben.
In der U8G2 Doku findet man C++/Arduino Beispiele, in denen das gemacht 
wird.

Aber im C-Teil der Doku finde dahingehend nix. Heisst das jetzt, das man 
Hardware-SPI nur in der Arduino Umgebung konfigurieren und nutzen kann?

In der oben gezeigten Konfiguration ist das Display wirklich 
fürchterlich langsam. Meine Hoffnung war, dass es mit Hardware-SPI 
schneller läuft.

Kann mir hier jemand auf die Sprünge helfen?
Hat jemand schonmal die U8G2 lib mit einem SSD1309 Controller verwendet?

Vielen Dank vorab ;)

LG, Manni

von Frank J. (glorfindel)


Lesenswert?

Hallo,

da ich nun vor dem gleichen "Problem" stehe ... haste was rausbekommen ?

von Vincent H. (vinci)


Lesenswert?

Ich verwend zwar einen ST7920 aber das Prinzip sollt das gleiche sein. 
Man übergibt bei der Initialisierung einen Callback für die 
SPI-Kommunikation.

Die Init sieht in meinem Fall so aus:
1
u8g2_Setup_st7920_s_128x64_f(&u8g2, U8G2_R0, u8x8_byte_hw_spi, u8x8_gpio_and_delay);

Und der SPI-Callback hat folgende Signatur:
1
uint8_t u8x8_byte_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);


Das hat bei mir eigentlich out-of-the-box funktioniert.

von Frank J. (glorfindel)


Lesenswert?

Danke für den Hinweis - habs dann nach der Anleitung in der Doku 
hinbekommen.
Ich betreibe hier nenn ATSAM3SD8B mit der ASF 3.39.0 und dem AS7.

Für die dies interessiert hier nenn bissl code:
1
#define SPI_CHIP_SEL      0
2
#define SPI_CLK_POLARITY    1
3
#define SPI_CLK_PHASE      0
4
#define SPI_DLYBS      0x01
5
#define SPI_DLYBCT      0x01
6
#define SPI_BAUDRATE      24000000
7
8
void OLED_SPI_Init(){
9
  volatile uint32_t temp;
10
  temp = sysclk_get_cpu_hz() ;
11
  UNUSED(temp);
12
13
  spi_enable_clock(SPI_MASTER_BASE);
14
  spi_disable(SPI_MASTER_BASE);
15
  spi_reset(SPI_MASTER_BASE);
16
  spi_set_master_mode(SPI_MASTER_BASE);
17
  spi_set_clock_polarity(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_POLARITY);
18
  spi_set_clock_phase(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CLK_PHASE);
19
  spi_set_bits_per_transfer(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_CSR_BITS_8_BIT);
20
  spi_set_baudrate_div(SPI_MASTER_BASE, SPI_CHIP_SEL, (sysclk_get_peripheral_hz() / SPI_BAUDRATE));
21
  spi_set_transfer_delay(SPI_MASTER_BASE, SPI_CHIP_SEL, SPI_DLYBS, SPI_DLYBCT);
22
  spi_enable(SPI_MASTER_BASE);
23
}
24
25
void OLED_SPI_Write(uint8_t value){
26
  spi_write(SPI_MASTER_BASE, value, 0, 0);
27
  while ((spi_read_status(SPI) & SPI_SR_TXEMPTY) == 0);
28
}

Dann die Anpassungen der U8G2 Callbacks:
1
uint8_t u8x8_arm_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr){
2
  switch(msg)  {
3
    case U8X8_MSG_DELAY_NANO:
4
      Nop();
5
    break;
6
    
7
    case U8X8_MSG_DELAY_100NANO:
8
      Nop();  // #tbd
9
    break;
10
    
11
    case U8X8_MSG_DELAY_10MICRO:
12
      delay_us(arg_int * 10);
13
    break;
14
    
15
    case U8X8_MSG_DELAY_MILLI:
16
      delay_ms(arg_int);
17
    break;
18
    
19
    default:
20
      return 0;
21
  }
22
  return 1;
23
}
24
uint8_t u8x8_arm_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr){
25
  switch(msg)  {
26
    case U8X8_MSG_GPIO_AND_DELAY_INIT:
27
    break;
28
29
    case U8X8_MSG_GPIO_SPI_CLOCK:
30
    break;
31
    
32
    case U8X8_MSG_GPIO_SPI_DATA:
33
    break;
34
    
35
    case U8X8_MSG_GPIO_CS:
36
      ioport_set_pin_level(SPI_OLED_CS_GPIO, arg_int);
37
    break;
38
    
39
    case U8X8_MSG_GPIO_DC:
40
      ioport_set_pin_level(OLED_DC_GPIO, arg_int);
41
    break;
42
    
43
    case U8X8_MSG_GPIO_RESET:
44
      ioport_set_pin_level(OLED_RST_GPIO, arg_int);
45
    break;
46
    
47
    default:
48
      if (u8x8_arm_delay(u8x8, msg, arg_int, arg_ptr))  
49
        return 1;
50
      u8x8_SetGPIOResult(u8x8, 1);      // default return value
51
    break;
52
  }
53
  return 1;
54
}
55
uint8_t u8x8_byte_arm_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
56
  uint8_t *data;
57
58
  switch(msg) {
59
    case U8X8_MSG_BYTE_SEND:
60
      data = (uint8_t *)arg_ptr;
61
      while( arg_int > 0 ) {
62
        OLED_SPI_Write((uint8_t)*data);
63
        data++;
64
        arg_int--;
65
      }
66
    break;
67
68
    case U8X8_MSG_BYTE_INIT:
69
      OLED_SPI_Init();
70
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
71
    break;
72
73
    case U8X8_MSG_BYTE_SET_DC:
74
      u8x8_gpio_SetDC(u8x8, arg_int);
75
    break;
76
77
    case U8X8_MSG_BYTE_START_TRANSFER:
78
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);
79
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);
80
    break;
81
82
    case U8X8_MSG_BYTE_END_TRANSFER:
83
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);
84
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
85
    break;
86
87
    default:
88
      return 0;
89
  }
90
  return 1;
91
}

Dann mal zu Testzwecken was ausgeben :
1
#include <USER.h>
2
3
BOARD_STATUS   vBoard_Status;
4
5
void BOARD_Init(){
6
 board_init();
7
8
...
9
10
 MEMORY_Init();
11
 GENERIC_USART_Init();
12
 CMDHANDLER_Init();
13
14
...
15
16
17
 BOARD_Start();
18
}
19
20
void BOARD_Start(){
21
  u8g2_Setup_ssd1309_128x64_noname0_f( &u8g2, U8G2_R0, u8x8_byte_arm_hw_spi, u8x8_arm_gpio_and_delay);
22
  u8g2_InitDisplay(&u8g2);
23
  u8g2_SetPowerSave(&u8g2, 0);
24
  u8g2_SetDisplayRotation(&u8g2,U8G2_R2);
25
26
  u8g2_ClearBuffer(&u8g2);
27
  u8g2_ClearDisplay(&u8g2);
28
  u8g2_SetFont(&u8g2, u8g2_font_ncenB08_tf);
29
30
  u8g2_DrawStr(&u8g2, 1, 10, "Demo Text");
31
  u8g2_UpdateDisplay(&u8g2);
32
33
  while(1){
34
    u8g2_ClearBuffer(&u8g2);
35
    u8g2_DrawStr(&u8g2, 1, 10, "Demo Text");
36
37
    for (int i = 0;i < 40;i++)  {
38
      u8g2_DrawFrame(&u8g2,10,20,i*2,10);
39
      u8g2_DrawFrame(&u8g2,10,50,80 - (i*2),10);
40
      u8g2_UpdateDisplay(&u8g2);
41
    }
42
  }
43
}
44
void BOARD_ProcessIO(){
45
...
46
}

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.