bastelhalde.de


Ben,

Using the u8glib on the STM32

The u8glib is a really nice library to use when you don't want to mess around with different diplay protocols or drawing routines. It is well-conceived and easy to use. Unfortunately there has been no port for STM32 microcontrollers so I tried to do this. In the following example I will use the library to display some text on a SSD1306 display via I2C. The controller I use is the STM32F101CBT6 and the IDE will be Atollic True Studio for ARM Lite. You should start by downloading the u8glib for arm. This is the C version of the library which I will use since you can only compile C code with the Lite version of my IDE. The next step is to implement the entire project into your code. So I created a new folder and just draged in the "src" folder from the archive. Now some low level routines have to be implemented to allow the library to talk to the display device. The following routines have to be created:

  • Delay by n microseconds
  • Delay by 10 microseconds
  • Delay by 1 microsecond
  • Doing a Reset on the display
  • Writing a byte to the display
  • Writing a sequence of bytes to the display
  • Switching from data to command mode or vice versa
    To do so we create a new source file called "u8g_arm.c" and a header file "u8g_arm.h". In the header file you should include the original u8g.h header file so basically just do:
#ifndef _U8G_ARM_H
#define _U8G_ARM_H

//adjust this path:
#include "u8glib/u8g.h"

//main com function. read on...
uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); 

#endif

The most important procedure in these files will be the one that is called by the u8g library with data to send to the display device. This communication procedure looks like this. I added comments for every important statement. Add it to the file "u8g_arm.c".

uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
    {

      switch(msg)
      {
        case U8G_COM_MSG_STOP:
          //STOP THE DEVICE
          break;

        case U8G_COM_MSG_INIT:
          //INIT HARDWARE INTERFACES, TIMERS, GPIOS...
          break;

        case U8G_COM_MSG_ADDRESS:  
          //SWITCH FROM DATA TO COMMAND MODE (arg_val == 0 for command mode)
         break;

        case U8G_COM_MSG_RESET:
          //TOGGLE THE RESET PIN ON THE DISPLAY BY THE VALUE IN arg_val
          break;

        case U8G_COM_MSG_WRITE_BYTE:
          //WRITE BYTE TO DEVICE
          break;

        case U8G_COM_MSG_WRITE_SEQ:
        case U8G_COM_MSG_WRITE_SEQ_P:
          //WRITE A SEQUENCE OF BYTES TO THE DEVICE
          break;

      }
      return 1;
    }

Now just add code to every case of the switch statement. For my communication with I2C it looks like this:

uint8_t u8g_com_hw_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr)
    {

      switch(msg)
      {
        case U8G_COM_MSG_STOP:
          break; //do nothing...

        case U8G_COM_MSG_INIT:
          i2c_init(); //inits the i2c hardware, gpios and timers
          u8g_MicroDelay();      
          break;

        case U8G_COM_MSG_ADDRESS: 
          //the control byte switches the mode on the device and is set here
          if (arg_val == 0)
          {
              control = 0;
          }
          else
          {
              control = 0x40;
          }
          u8g_10MicroDelay();
         break;

        case U8G_COM_MSG_RESET:
          //pin 9 is my reset pin
          GPIO_WriteBit(GPIOB, GPIO_Pin_9, arg_val);
          u8g_10MicroDelay();
          break;

        case U8G_COM_MSG_WRITE_BYTE:
          //simple: just write one byte
          i2c_out(arg_val);
          u8g_MicroDelay();
          break;

        case U8G_COM_MSG_WRITE_SEQ:
        case U8G_COM_MSG_WRITE_SEQ_P:
          {
            register uint8_t *ptr = arg_ptr;
            //send the control byte (to switch from command to data mode)
            I2C_start(SSD1306_I2C_ADDRESS, I2C_Direction_Transmitter);
            I2C_SendData(I2C2, control);
            while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

            //now send the rest of data
            while( arg_val > 0 )
            {
                I2C_SendData(I2C2, *ptr++);
                arg_val--;
                while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
            }
            //done
            I2C_stop();
            u8g_MicroDelay();
          }
          break;

      }
      return 1;
    }

My routines to  write and read data via I2C are basically copied from this nice article. If you want the full code please contact me or write a comment since I don't want to publish it because of bad style. Ok so now we have the low level hardware communication set up. Now it is time to really enjoy the amazing u8g library. To tell u8glib to use our communication function we just have to call it like this.

    #include "u8g_arm.h"

    u8g_t u8g;

    u8g_InitComFn(&u8g, &u8g_dev_ssd1306_128x64_i2c, u8g_com_hw_i2c_fn);

I hope you noticed that the third parameter to this function is our hardware routine. That is basically all you have to do. Now you can use all functions of the u8glib. Please remind that you use the C library of the project so you cannot directly copy code from the tutorials.


Comments:


olikraus wrote on

Thanks a lot for this tutorial! Oliver

jyb wrote on

thank you very much,from china

max713 wrote on

Thank you for this tutorial, is there also a possibility to use U8glib with Energia? (Arduino-clone from Texas Instruments)

Paolo wrote on

hi, i try to mod the library to use it with stm32f103 and IAR for ARM but when i try to compile there is this error " no definition for u8g_InitComFn" and "no definition for u8g_dev_ssd1351_128x128_hicolor_hw_spi". thanks

Ben wrote on

Hi! You can use the library. Just add the routines to interface the display like in the example above :) .

ran wrote on

Thank you very much, It is most appreciated. Do you think that I can use u8glib with ATOM 64 bit ? Which porting do you think I need to do ? I also need to use it with ssd1305 which is not supported yet ? We have ssd1305 driver (u8glib does not support it). Is using u8glib graphic library for menus/display programming is advisable or I can use plain driver ? Do you think I can tailor the driver inside u8glib (so that the lib will use it )? Thank you!

Ben wrote on

Hi! Maybe you did not include the right files? Make sure you include u8g.h (this is where the missing symbols were defined). Also make sure you downloaded the right library (for ARM) from the u8glib site. You can send me your code for review if you want. If I find anything wrong with the article I will update it.

Ben wrote on

Hello, it depends on your device. If it sends ACK, then I think you can ignore the U8G_I2C_OPT_NO_ACK. Which display are you exactly using?

Ben wrote on

https://gist.github.com/bkarl/ac6a7d9afea057f29c25 maybe this helps you?

Jan wrote on

I bought a 4-wire OLED display (heltec). For arduino they made a modification in the u8glib : U8G_I2C_OPT_NO_ACK. (The device cannot send an ACK) I'm new to stm32 and have limited skills. I'm trying to adapt your script. in the configuration of the stm32 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; //should I put here I2C_ACK_DISABLE ? In the code there is this line : while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); So the master is asking the OLED to confirm (=ack?) In the arduino equivalent, they skip the check. Can I do the same by leaving out (checkevent) ? ( u8g_i2c_opt & U8G_I2C_OPT_NO_ACK ) { /* do not check for ACK */ } else { status = TW_STATUS; /* check status after sla */ if ( status != TW_MT_SLA_ACK ) { u8g_i2c_set_error(U8G_I2C_ERR_BUS, 2); return 0; } Kind Regards, Jan

Thilina wrote on

Hi, Thank you very much for your wonderful effort. I'm trying to compile u8glib for SSD1325 display on an arm based UDOO board. Being a novice I would be respectfully thankful if I could have your source code so that I can understand better. I appreciate the extreme hard work that you've done and the time you've spent on this. Thank you very much.

Thilina wrote on

Hi, Thank you very much for your wonderful effort. I'm trying to compile u8glib for SSD1325 display on an arm based UDOO board. Being a novice I would be respectfully thankful if I could have your source code so that I can understand better. I appreciate the extreme hard work that you've done and the time you've spent on this. Thank you very much.

Ben wrote on

Hey, I don't really have more code... maybe you could point out what your basic problem is? Unfortunately I have heard of your development environment but it should be possible. I put up the full code here, maybe it helps but I think it is to special for my application. https://gist.github.com/bkarl/ac6a7d9afea057f29c25 Greetings B

hamed wrote on

Hi Ben Thanks for this please send me full code ,i need that for lpc17xx port , hamed.t@live.com

radiomanoff wrote on

Hello. Thanks for the article. I was able to connect to the LCD ST7920 - STM32F4 DISCOVERY (u8glib). Details on my blog http://radiomanoff.at.ua/index/stm32f4_discovery_lcd_st7920_u8glib/0-71

Ben wrote on

Sorry I have no experience on the mentioned platforms. But if you want to use an embedded system the best thing is to get the interface you want to use for the display working at first. Then look around for software you could use...

radiomanoff wrote on

Hello. Prompt possible to unite stm32f4 Discovery and display sst7920 & U8glyb.Connection to use SPI

Ben wrote on

Hello, according to https://code.google.com/p/u8glib/wiki/device the ST7920 should be supported by u8glib so you can use it :)

Omar wrote on

Hi Ben - thanks for writing this. I'd like to implement u8glib on an OpenWrt (AR9331 based Linux) router with SPI (UC1608 based display), if you are familiar with Arduino Yun, I would basically like to use the Linux side of the board directly (without programming the Atmel MCU). Any ideas on how to approach this? Or is this not possible...any guidance would be greatly appreciated.

Ben wrote on

Well when I look through pawels errors I mostly see stuff that is not u8glib related. It looks like he is trying to compile C++ code with a C compiler (for example look at the "bool" and "class" keyword errors). You have the exact same output?

Patrick wrote on

Hi I'm getting the same errorr as Paolo when I try to compile. I'm using Keil version 5.0 and I ported this over to work on the nrf51822. I believe there is some linker issue but I just cant see it.Any advice? Thanks -Patrick

Artem wrote on

Hi! i try to port u8glib to stm32f1 using NEW hal drivers. How you make microseconds delay function? Can you show this function?

Ben wrote on

Hello, did you have a look at the lines 98 to 130 (see link below)? It should be the same for the full stm32 family (as far as I know). https://gist.github.com/bkarl/ac6a7d9afea057f29c25

Artem wrote on

thank for reply. my stm have 48 lines stm32f100c8 i supousse that this project will be ok but i want to port this lib on new hal drivers. in educatin purpose -)

kvik wrote on

Hi! I'm trying to apply your solution to my project (STM32F103 and SSD1306 i2c only) but I get weird results. The display works only about 1 out of 2-5 resets. What I see is that the microcontroller stucks into the event checks for i2c. The events I wait for never occur. I tried adding timeouts but then again the whole initialization process is disrupted... Have you encountered this before?

Ben wrote on

Maybe your cables are to long or you forgot to add pull up resistors?

pawel wrote on

Hi, could you explain in which file exactly I have to be put your code from above to make STM32 with u8glib work? I'll tryied but can't compile it. I use maple mini 2, I have SSD1306 library for STM32 which is work but would like to run also u8glib as there is plenty of OLED displays 1.3" with SH1106 driver, which nicely work with AVR's but not with STM, at the moment. I'll made two files u8g_arm.c and u8g_arm.h and put code you described above, but arduinoIDE can't compile this code. Could you explain more step by step which portion of the code shoulde be placed where to make this work? I appreciate your help

Ben wrote on

I didn't work with the Arduino IDE for a long time, but maybe it just does not find your include/header files? You also need to pass your source files to the compiler. Did you use the Arduino style low level routines to access the I2C or did you write them in the ST Standard Periphal style? It should be as simple as creating a c and header file having the given structure, implementing the low level routines and passing them to the u8g lib. Or do you have any specific compile error?

pawel wrote on

Hi, I have this error info In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:52:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/boards.h:111:1: error: unknown type name 'bool' bool boardUsesPin(uint8 pin); ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:53:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/io.h:184:32: error: expected ';', ',' or ')' before '=' token uint8 isButtonPressed(uint8 pin=BOARD_BUTTON_PIN, ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/io.h:204:47: error: expected ';', ',' or ')' before '=' token uint8 waitForButtonPress(uint32 timeout_millis=0); ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:56:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/ext_interrupts.h:90:6: error: conflicting types for 'attachInterrupt' void attachInterrupt(uint8 pin, voidArgumentFuncPtr handler, void *arg, ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/ext_interrupts.h:69:6: note: previous declaration of 'attachInterrupt' was here void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode); ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:58:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish_math.h:58:6: error: conflicting types for 'random' long random(long min, long max); ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish_math.h:49:6: note: previous declaration of 'random' was here long random(long max); ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:38:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:66, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Print.h:37:1: error: unknown type name 'class' class Print { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Print.h:37:13: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token class Print { ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:40:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:66, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Stream.h:38:1: error: unknown type name 'class' class Stream : public Print ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Stream.h:38:14: error: expected '=', ',', ';', 'asm' or '__attribute__' before ':' token class Stream : public Print ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:66:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:83:1: error: unknown type name 'class' class HardwareSerial : public Stream { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:83:22: error: expected '=', ',', ';', 'asm' or '__attribute__' before ':' token class HardwareSerial : public Stream { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:143:2: error: unknown type name 'HardwareSerial' extern HardwareSerial Serial1; ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:146:2: error: unknown type name 'HardwareSerial' extern HardwareSerial Serial2; ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareSerial.h:149:2: error: unknown type name 'HardwareSerial' extern HardwareSerial Serial3; ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:67:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:44:1: error: unknown type name 'class' class HardwareTimer { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:44:21: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token class HardwareTimer { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:303:1: error: unknown type name 'HardwareTimer' extern HardwareTimer Timer1; ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:309:1: error: unknown type name 'HardwareTimer' extern HardwareTimer Timer2; ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:315:1: error: unknown type name 'HardwareTimer' extern HardwareTimer Timer3; ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/HardwareTimer.h:321:1: error: unknown type name 'HardwareTimer' extern HardwareTimer Timer4; ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:68:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30, from /home/pawel/Pobrane/arduino-1.6.1/libraries/U8glib/utility/u8g_com_arduino_common.c:46: /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/usb_serial.h:41:1: error: unknown type name 'class' class USBSerial : public Stream { ^ /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/usb_serial.h:41:17: error: expected '=', ',', ';', 'asm' or '__attribute__' before ':' token class USBSerial : public Stream { ^ In file included from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/wirish.h:68:0, from /home/pawel/Pobrane/arduino-1.6.1/hardware/Arduino_STM32/STM32F1XX/cores/maple/Arduino.h:30,

Nick Williams wrote on

Wow awesome, I can't wait to try this. Thanks so much for putting this together.

Patrick wrote on

I am also getting the same error, as Paolo. I have ported this over to NRF51822. I think I am getting linking errors since there is no u8g.c file? Any ideas as to what I can try? Thanks, -Patrick

Terraviper-5 wrote on

Has anyone tried to use m2tklib on top of that with STM32F4?

Terraviper5 wrote on

While trying to compile this I get an error Error[Pe144]: a value of type "uint8_t (*)(void *, void *, uint8_t, void *)" cannot be used to initialize an entity of type "u8g_dev_fnptr" ...\u8glib\src\u8g_rot.c 48 Does anybody know how to solve this? Thanks!

Leave Comment: