Declaring global variables in header file for multiple source files

Hi,

I have put my global variables in the main source file for the library.

I want to split the source file and put group of functions in another source file.

But there are global variables that are used in the functions I'm moving to the second source file.

What to do ? I know I can use "extern" keyword. But what if the variables are initialized ? Like this:

uint8_t rotation, width = 128, height = 64, _i2caddr, _vccstate;

Can I just put them like:

extern uint8_t rotation, width = 128, height = 64, _i2caddr, _vccstate;

Would that work ?

Would that work ?

Have you tried it ?

you should initialize them only at the definition, not at extern

UKHeliBob:
Have you tried it ?

I just tired it, it doesn't work. If initialized cause error, and if I only declare it, it gives me warnings.

I don't know what to do now. I returned the variables to the main source file.

Maybe a struct would work.

Juraj:
you should initialize them only at the definition, not at extern

How ? Could you give me a simple example ?

Post a short but complete MRE of what you've tried.

wolfrose:
How ? Could you give me a simple example ?

extern uint8_t rotation, width, height, _i2caddr, _vccstate;

OK, I have 3 files, 2 source files and 1 header file that contain all global variables.

  1. Header file:
#ifndef ssd1306_stm32_h
#define ssd1306_stm32_h

#define SSD1306_ADR 0x3C

// Scrolling #defines
#define SSD1306_ACTIVATE_SCROLL 0x2F

///////////////////////////////////////////////////////////////////
//////////////////////////// variables ////////////////////////////
extern uint8_t rotation, width = 128, height = 64, _i2caddr, _vccstate; // of course this would produce errors

///////////////////////////////////////////////////////////////////
////////////// functions >> in C programming structure ////////////
// lcd control functions
void ssd1306_start(uint8_t vccstate, uint8_t i2caddr);
void ssd1306_cmd(uint8_t c);
.
.
.
void ssd1306_display(void); // this is a function that it has to be called ...
// lcd control functions
void ssd1306_scrl_right(uint8_t start, uint8_t stop);
.
.
.
// lcd draw functions
void ssd1306_drawPixel(int16_t x, int16_t y, uint16_t color);


#endif
  1. Main source file contains part of library functions:
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <Wire.h>
#include "ssd1306_stm32.h"

#ifndef swap
#define swap(a, b) { int16_t t = a; a = b; b = t; }
#endif

uint8_t rotation, width = 128, height = 64, _i2caddr, _vccstate;

static uint8_t buffer[1024] = {0};

void ssd1306_start(uint8_t vccstate, uint8_t i2caddr) {
...
}

void ssd1306_cmd(uint8_t c){
    Wire.beginTransmission(_i2caddr);
    WIRE_WRITE(CMD_MSK);
    WIRE_WRITE(c);
    Wire.endTransmission(); 
}

   // ... rest of 1st part functions
    
}
  1. source file contains 2nd part of library functions:
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <Wire.h>
#include "ssd1306_stm32.h"

#ifndef swap
#define swap(a, b) { int16_t t = a; a = b; b = t; }
#endif

// lcd draw functions
void ssd1306_(){}

.
.
.
rest of 2nd part functions

wolfrose:
OK, I have 3 files, 2 source files and 1 header file that contain all global variables.

The "M" in MRE means "Minimal". 90% of that is unrelated to the problem at hand. Consider:

Main .ino file:

#include "Globals.h"

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.print("globalVar = ");
  Serial.println(globalVar);

  Serial.print("globalVar = ");
  printGlobal();
}

void loop() {
}

Globals.h

#ifndef GLOBALS_H
#define GLOBALS_H

extern uint8_t globalVar;  // Declare the global variable as extern
void printGlobal();

#endif

Globals.cpp:

#include <Arduino.h>
#include "Globals.h"

uint8_t globalVar = 7;  // Define the global varible with an initial value.

void printGlobal() {
  Serial.println(globalVar);
}

This is my program setup:

  1. run.ino
#include "ssd1306_stm32.h"


void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  ssd1306_start(SSD1306_SWITCHCAPVCC, 0x3C);
  
  ssd1306_display();
  delay(2000);
  ssd1306_clearDisplay();
  ssd1306_drawPixel(10, 10, WHITE);
  ssd1306_display();
  delay(2000);
  ssd1306_clearDisplay();
}

void loop() {
  // put your main code here, to run repeatedly:

}
  1. ssd1306_stm32.h
#ifndef ssd1306_stm32_h
#define ssd1306_stm32_h

///////////////////////////////////////////////////////////////////
//////////////////////////// definitions //////////////////////////
.
.
.
///////////////////////////////////////////////////////////////////
//////////////////////////// variables ///////////////////////////
extern uint8_t rotation, width, height, _i2caddr, _vccstate;
extern static uint8_t buffer[1024];
///////////////////////////////////////////////////////////////////
////////////////////////// functions /////////////////////////////
// lcd control functions
.
.
.
#endif
  1. ssd1306_stm32.cpp << functions part1
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <Wire.h>
#include "ssd1306_stm32.h"

uint8_t rotation, width = 128, height = 64, _i2caddr, _vccstate;

static uint8_t buffer[1024] = { adafruit logo};
  1. ssd1306_stm32_draw.cpp << functions part2
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <Wire.h>
#include "ssd1306_stm32.h"

// the most basic function, set a single pixel
void ssd1306_drawPixel(int16_t x, int16_t y, uint16_t color){
.
.
.
}

When I compile the code in Arduino IDE I get this:

Arduino: 1.8.12 (Windows 10), Board: "Maple Mini, Original (17k RAM,108k Flash), 72MHz (Normal), Smallest (default)"

In file included from C:\Users\wopre\OneDrive\Desktop\task_display_driver\SSD1306\my_code\sketch_may30a\sketch_may30a.ino:1:0:

F:\ozone_territory\Programming\Program_Files\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\stm32_ssd1306/ssd1306_stm32.h:83:34: error: conflicting specifiers in declaration of 'buffer'

 extern static uint8_t buffer[1024];

                                  ^

Multiple libraries were found for "Wire.h"
 Used: F:\ozone_territory\Programming\Program_Files\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\Wire
 Not used: F:\ozone_territory\Programming\Program_Files\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\WireSlave
exit status 1
Error compiling for board Maple Mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

remove static from
extern static uint8_t buffer[1024];

static at global variable is "accessible only in this unit"
extern is "somewhere this globally accessible variable exists"

so extern static is "conflicting specifiers"

Yep I removed static and it worked.

But I still have a question.

Now the code setting is:

  1. ssd1306_stm32.h
extern uint8_t buffer[1024];
  1. ssd1306_stm32.cpp
uint8_t buffer[1024] = { adafruit logo};
  1. ssd1306_stm32_draw.cpp
void ssd1306_drawPixel(int16_t x, int16_t y, uint16_t color){
	
  if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
    return;

  // check rotation, move pixel around if necessary
  switch (rotation) {
  case 1:
    swap(x, y);
    x = width - x - 1;
    break;
  case 2:
    x = width - x - 1;
    y = height - y - 1;
    break;
  case 3:
    swap(x, y);
    y = height - y - 1;
    break;
  }  

  // x is which column
    switch (color) 
    {
      case WHITE:   buffer[x+ (y/8)*SSD1306_WIDTH] |=  (1 << (y&7)); break;
      case BLACK:   buffer[x+ (y/8)*SSD1306_WIDTH] &= ~(1 << (y&7)); break; 
      case INVERSE: buffer[x+ (y/8)*SSD1306_WIDTH] ^=  (1 << (y&7)); break; 
    } 
}

So, the 2nd source file is using buffer but I didn't declare it at the top of the file like the 1st source file and it worked, why ?

wolfrose:
Yep I removed static and it worked.

But I still have a question.

Now the code setting is:

  1. ssd1306_stm32.h
extern uint8_t buffer[1024];
  1. ssd1306_stm32.cpp
uint8_t buffer[1024] = { adafruit logo};
  1. ssd1306_stm32_draw.cpp
void ssd1306_drawPixel(int16_t x, int16_t y, uint16_t color){

if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
    return;

// check rotation, move pixel around if necessary
  switch (rotation) {
  case 1:
    swap(x, y);
    x = width - x - 1;
    break;
  case 2:
    x = width - x - 1;
    y = height - y - 1;
    break;
  case 3:
    swap(x, y);
    y = height - y - 1;
    break;
  }

// x is which column
    switch (color)
    {
      case WHITE:  buffer[x+ (y/8)*SSD1306_WIDTH] |=  (1 << (y&7)); break;
      case BLACK:  buffer[x+ (y/8)*SSD1306_WIDTH] &= ~(1 << (y&7)); break;
      case INVERSE: buffer[x+ (y/8)*SSD1306_WIDTH] ^=  (1 << (y&7)); break;
    }
}




So, the 2nd source file is using `buffer` but I didn't declare it at the top of the file like the 1st source file and it worked, why ?

is is local in that unit

Juraj:
is is local in that unit

ok, so you mean after declaring it "extern" in the header file, then it would be local in each source file, right ?

But I didn't declare it in the second source file, so does that mean that declaring it in the 1st source file is enough ?

I'm developing this function to output a graphics to the LCD

void ssd1306_bitmap(const unsigned char * bitmap){
 ssd1306_cmd(SSD1306_COLUMNADDR);
 ssd1306_cmd(0);   // Column start address (0 = reset)
 ssd1306_cmd(127); // Column end address (127 = reset)

 ssd1306_cmd(SSD1306_PAGEADDR);
 ssd1306_cmd(0); // Page start address (0 = reset)
 ssd1306_cmd(7); // Page end address
 strcpy(buffer, bitmap);
 // I2C
 for (uint16_t i=0; i<1024; i++) {
 // send a bunch of data in one xmission
 Wire.beginTransmission(_i2caddr);
 WIRE_WRITE(DAT_MSK);
 for (uint8_t x=0; x<16; x++) {
 WIRE_WRITE(buffer[i]);
 i++;
 }
 i--;
 Wire.endTransmission();
 }
}

Where:
buffer: is a global variable for anything going to the lcd
bitmap: argument from PROGMEM consts in a header file

My questions:

  1. Is it a good way to first copy the content from the passed pointer to the buffer then put that in the lcd ?
  2. Or should I read directly from the passed pointer without copying the stuff to the buffer ?

What processor? If it's an AVR and 'bitmap' is a pointer into PROGMEM, then this won't work:

strcpy(buffer, bitmap);

Look at strcpy_P.

Processor is STM32 running in Arduino IDE from:

\Arduino\hardware\Arduino_STM32-master\STM32F1\libraries\stm32_ssd1306

Would strcpy_P function work with this path ?

OK, now I have another problem.

How to print bitmaps on this module ?

I have "LCDAssistant" software that I used with my previous LCD128x64 to convert bitmaps to arrays.

converting in horizontal pattern didn't work, but converting in vertical worked a bit as the photo appears pulled to the left. What should I do ?

I wouldn't use strcpy (or the PROGMEM variant) on something that isn't a string. That function stops at the first zero byte. Use memcpy instead.

christop:
I wouldn't use strcpy (or the PROGMEM variant) on something that isn't a string. That function stops at the first zero byte. Use memcpy instead.

I'm actually now a beginner in uploading frames for a GUI system; like, uploading clocks, temp scale .. etc.

But I think that need a buffer in the RAM. But what stuff that doesn't need to be variable and I better store them in the flash memory to save space in the RAM ?

I'm now learning how to call a bitmap from the flash using PROGMEM. But how "memcpy" is useful ? Can you tell me how to use it ? And where the bitmap would be stored ?