#申请原创#   @21小跑堂  
 
 
 
 
简介  
 OLED,是一种有机电质发光器件,又称为有机电激光显示。与之前提到的LCD1602、LCD1284相比,具有更低的功耗、更快的响应速度、更宽的可视角度、更高的显示分辨率、更轻的质量等等优点,而被广泛使用。淘宝上找到一款常见的是由SSD1306驱动的0.96寸的OLED,驱动接口有3线串行、4线串行和I2C这3种接口类型;另外OLED还有支持并行接口驱动的,这个会在后面的帖子来给大家分享。  
 u8g2是用于嵌入式设备支持单色OLED或LCD的图形库软件,支持市面上决大多数的显示屏驱动控制器,SSD1306就包含在内。u8g2图形库包含3个部分:u8g2、u8x8和u8log,其中u8g2包括了所有的图形功能,支持多种字体;u8x8可以直接将显示内容输出到显示屏显示,无需显示缓存;u8log则是一个输出终端的功能。另外u8g2还支持3种不同的绘图模式:全屏缓冲模式、页面模式和u8x8仅字符模式;全屏缓冲模式和页面模式支持所有的图形,区别在于所占用的显示缓存大小不同,从而致使刷新速度的快慢;而u8x8仅字符模式不需要显示缓存,可以直接将需要显示的内容输出显示,缺点就是不支持u8g8的图形显示,同时也依赖于显示驱动芯片是否支持该模式。  
  
 实现功能  
 1、OLED底层驱动:分别使用硬件I2C、软件模拟I2C、硬件SPI、软件模拟SPI这4种方式来实现对OLED显示屏的驱动,并实现一幅动画效果的演示。 2、u8g2移植:基于I2C和SPI这两种不同接口的驱动方式,分别移植u8g2的GUI,对移植过程和注意事项进行说明。 3、u8g2演示:通过一个简单的示例完成u8g2的效果演示。  
1、OLED底层驱动  
 使用自制的MM32F0140最小系统板,结合I2C和SPI接口的两块0.96寸的OLED模块,实现驱动显示。在上图中SPI接口的OLED模块中,CS是片选信号连接PA4、DC是数据或命令切换连接PB0、RES是复位连接PB1、DI是SPI数据线连接PB5、DO是SPI时钟线连接PB7;在I2C接口的OLED中,SCL和SDA与MM32F0140的硬件I2C相连接,分别连接PB10和PB11。按照这样线序连接方式,一方面是按照MCU引脚I2C/SPI复用功能连接的,可以通过硬件I2C/SPI接口来驱动OLED显示;另一方面可以直接使用GPIO来模拟I2C/SPI时序来驱动OLED显示,如下图所示:  
 在OLED初始化过程中先根据宏定义对MCU的I2C和SPI进行初始化配置,然后配置OLED显示屏有参数并清空显示内容,最后通过TASK调用OLED_Handler来显示一个动画效果。对于使用硬件I2C还是软件模拟I2C、硬件SPI还是软件模拟SPI可以通过OLED_HW_I2C和OLED_HW_SPI宏来配置,参考代码如下所示: - void OLED_I2C_SendData(uint8_t Address, uint8_t Data)
 
 - {
 
 - #if OLED_HW_I2C /* 硬件I2C */
 
  
-     I2C_SendData(I2C1, Address);
 
 -     while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
 
  
-     I2C_SendData(I2C1, Data);
 
 -     while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));
 
  
-     I2C_GenerateSTOP(I2C1, ENABLE);
 
 -     while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));
 
  
- #else           /* 模拟I2C */
 
  
-     sI2C_GenerateStart(&sI2C_OLED);
 
  
-     sI2C_WriteByte(&sI2C_OLED, 0x78);
 
  
-     if(sI2C_GetACK(&sI2C_OLED))
 
 -     {
 
 -         sI2C_GenerateStop(&sI2C_OLED);    return;
 
 -     }
 
  
-     sI2C_WriteByte(&sI2C_OLED, Address);
 
  
-     if(sI2C_GetACK(&sI2C_OLED))
 
 -     {
 
 -         sI2C_GenerateStop(&sI2C_OLED);    return;
 
 -     }
 
  
-     sI2C_WriteByte(&sI2C_OLED, Data);
 
  
-     if(sI2C_GetACK(&sI2C_OLED))
 
 -     {
 
 -         sI2C_GenerateStop(&sI2C_OLED);    return;
 
 -     }
 
  
-     sI2C_GenerateStop(&sI2C_OLED);
 
  
- #endif
 
 - }
 
  
- void OLED_SPI_SendData(uint8_t Data)
 
 - {
 
 - #if OLED_HW_SPI /* 硬件SPI */
 
  
-     uint32_t Timeout = 0;
 
  
-     SPI_SendData(SPI1, Data);
 
 -     while(!SPI_GetFlagStatus(SPI1, SPI_FLAG_TXEPT))
 
 -     {
 
 -         if(Timeout++ > 0xFFFF) break;
 
 -     }
 
  
- #else           /* 模拟SPI */
 
  
-     for(uint8_t i = 0; i < 8; i++)
 
 -     {
 
 -         if((Data << i) & 0x80) OLED_DI_H();
 
 -         else                   OLED_DI_L();
 
  
-         OLED_DO_H();
 
 -         OLED_DO_L();
 
 -     }
 
  
- #endif
 
 - }
 
  
- void OLED_WriteCMD(uint8_t Command)
 
 - {
 
 -     OLED_I2C_SendData(0x00, Command);
 
  
-     OLED_DC_L();
 
 -     OLED_CS_L();
 
 -     OLED_SPI_SendData(Command);
 
 -     OLED_CS_H();
 
 - }
 
  
- void OLED_WriteDAT(uint8_t Data)
 
 - {
 
 -     OLED_I2C_SendData(0x40, Data);
 
  
-     OLED_DC_H();
 
 -     OLED_CS_L();
 
 -     OLED_SPI_SendData(Data);
 
 -     OLED_CS_H();
 
 - }
 
  
- void OLED_WriteBuffer(uint8_t *Buffer, uint8_t Length)
 
 - {
 
 -     for(uint8_t i = 0; i < Length; i++)
 
 -     {
 
 -         OLED_WriteDAT(*Buffer++);
 
 -     }
 
 - }
 
  
- void OLED_InitI2C(void)
 
 - {
 
 - #if OLED_HW_I2C /* 硬件I2C */
 
  
-     GPIO_InitTypeDef GPIO_InitStructure;
 
 -     I2C_InitTypeDef  I2C1_InitStructure;
 
  
-     RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);
 
  
-     I2C_StructInit(&I2C1_InitStructure);
 
 -     I2C1_InitStructure.I2C_Mode       = I2C_Mode_MASTER;
 
 -     I2C1_InitStructure.I2C_OwnAddress = 0;
 
 -     I2C1_InitStructure.I2C_Speed      = I2C_Speed_FAST;
 
 -     I2C1_InitStructure.I2C_ClockSpeed = 400000;
 
 -     I2C_Init(I2C1, &I2C1_InitStructure);
 
  
-     I2C_Send7bitAddress(I2C1, 0x78, I2C_Direction_Transmitter);
 
  
-     I2C_Cmd(I2C1, ENABLE);
 
  
-     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
 
  
-     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
 
 -     GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_1);
 
  
-     GPIO_StructInit(&GPIO_InitStructure);
 
 -     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
 
 -     GPIO_Init(GPIOB, &GPIO_InitStructure);
 
  
- #else           /* 模拟I2C */
 
  
-     sI2C_Init(&sI2C_OLED);
 
  
- #endif
 
 - }
 
  
- void OLED_InitSPI(void)
 
 - {
 
 - #if OLED_HW_SPI /* 硬件SPI */
 
  
-     GPIO_InitTypeDef GPIO_InitStructure;
 
 -     SPI_InitTypeDef  SPI_InitStructure;
 
  
-     RCC_APB2PeriphClockCmd(RCC_APB2ENR_SPI1, ENABLE);
 
  
-     SPI_StructInit(&SPI_InitStructure);
 
 -     SPI_InitStructure.SPI_Mode              = SPI_Mode_Master;
 
 -     SPI_InitStructure.SPI_DataSize          = SPI_DataSize_8b;
 
 -     SPI_InitStructure.SPI_DataWidth         = 8;
 
 -     SPI_InitStructure.SPI_CPOL              = SPI_CPOL_Low;
 
 -     SPI_InitStructure.SPI_CPHA              = SPI_CPHA_1Edge;
 
 -     SPI_InitStructure.SPI_NSS               = SPI_NSS_Soft;
 
 -     SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
 
 -     SPI_InitStructure.SPI_FirstBit          = SPI_FirstBit_MSB;
 
 -     SPI_Init(SPI1, &SPI_InitStructure);
 
  
-     SPI_Cmd(SPI1, ENABLE);
 
  
-     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
 
  
-     GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_0);   /* PA4 SPI1_NSS  */
 
 -     GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_0);   /* PA5 SPI1_SCK  */
 
 -     GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_0);   /* PA6 SPI1_MISO */
 
 -     GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_0);   /* PA7 SPI1_MOSI */
 
  
-     GPIO_StructInit(&GPIO_InitStructure);
 
 -     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;
 
 -     GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  
-     GPIO_StructInit(&GPIO_InitStructure);
 
 -     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IPU;
 
 -     GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  
-     SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
 
 -     SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
 
  
-     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
 
  
-     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
 
 -     GPIO_Init(GPIOB, &GPIO_InitStructure);
 
  
- #else           /* 模拟SPI */
 
  
-     GPIO_InitTypeDef GPIO_InitStructure;
 
  
-     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
 
  
-     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
 
 -     GPIO_Init(GPIOA, &GPIO_InitStructure);
 
  
-     RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
 
  
-     GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1;
 
 -     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 
 -     GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;
 
 -     GPIO_Init(GPIOB, &GPIO_InitStructure);
 
  
-     OLED_CS_H();  OLED_DC_L();
 
 -     OLED_DI_L();  OLED_DO_L();
 
  
- #endif
 
  
-     OLED_RES_H(); SysTick_DelayMS(100);
 
 -     OLED_RES_L(); SysTick_DelayMS(100);
 
 -     OLED_RES_H(); SysTick_DelayMS(100);  
 
 - }
 
  
- void OLED_InitCFG(void)
 
 - {
 
 -     SysTick_DelayMS(1000);  /* 这里的延时很重要,上电后延时,没有错误的冗余设计 */
 
  
-     OLED_WriteCMD(0xAE);    /* Display Off */
 
 -     OLED_WriteCMD(0x20);    /* Set Memory Addressing Mode */
 
 -     OLED_WriteCMD(0x10);    /* 00  Horizontal Addressing Mode
 
 -                                01  Vertical Addressing Mode
 
 -                                10  Page Addressing Mode (RESET)
 
 -                                11  Invalid */
 
 -     OLED_WriteCMD(0xB0);    /* Set Page Start Address for Page Addressing Mode,0-7 */
 
 -     OLED_WriteCMD(0xC8);    /* Set COM Output Scan Direction */
 
 -     OLED_WriteCMD(0x00);    /* Set Low  Column Address */
 
 -     OLED_WriteCMD(0x10);    /* Set High Column Address */
 
 -     OLED_WriteCMD(0x40);    /* Set Start Line Address */
 
 -     OLED_WriteCMD(0x81);    /* Set Contrast Control Register */
 
 -     OLED_WriteCMD(0xFF);    /* Brightness 0x00~0xff */
 
 -     OLED_WriteCMD(0xA1);    /* Set Segment Re-map 0 to 127 */
 
 -     OLED_WriteCMD(0xA6);    /* Set Normal Display */
 
 -     OLED_WriteCMD(0xA8);    /* Set Multiplex Ratio(1 to 64) */
 
 -     OLED_WriteCMD(0x3F);
 
 -     OLED_WriteCMD(0xA4);    /* 0xA4: Output Follows RAM Content
 
 -                                0xA5: Output Ignores RAM Content */
 
 -     OLED_WriteCMD(0xD3);    /* Aet Display offset */
 
 -     OLED_WriteCMD(0x00);    /* Not Offset */
 
 -     OLED_WriteCMD(0xD5);    /* Set Display Clock Divide Ratio/Oscillator Frequency */
 
 -     OLED_WriteCMD(0xF0);    /* Set Divide Ratio */
 
 -     OLED_WriteCMD(0xD9);    /* Set Pre-charge Period */
 
 -     OLED_WriteCMD(0x22);
 
 -     OLED_WriteCMD(0xDA);    /* Set Com Pins Hardware Configuration */
 
 -     OLED_WriteCMD(0x12);
 
 -     OLED_WriteCMD(0xDB);    /* Set VCOMH */
 
 -     OLED_WriteCMD(0x20);    /* 0x20=0.77*VCC */
 
 -     OLED_WriteCMD(0x8D);    /* Set DC-DC Enable */
 
 -     OLED_WriteCMD(0x14);
 
 -     OLED_WriteCMD(0xAF);    /* Turn On OLED Panel */
 
 - }
 
  
- void OLED_SetPosition(uint8_t x, uint8_t y)
 
 - {
 
 -         OLED_WriteCMD(0xB0 + y);
 
 -         OLED_WriteCMD(((x & 0xF0) >> 4) | 0x10);
 
 -         OLED_WriteCMD(((x & 0x0F) >> 0) | 0x00);
 
 - }
 
  
- void OLED_Clear(uint8_t Data)
 
 - {
 
 -     uint8_t x = 0, y = 0;
 
  
-     for(y = 0; y < 8; y++)
 
 -     {
 
 -         OLED_SetPosition(0, y);
 
  
-         for(x = 0; x < 128; x++)
 
 -         {
 
 -             OLED_WriteDAT(Data);
 
 -         }
 
 -     }
 
 - }
 
  
- void OLED_Init(void)
 
 - {
 
 -     OLED_InitI2C();
 
  
-     OLED_InitSPI();
 
  
-     OLED_InitCFG();
 
  
-     OLED_Clear(0x00);
 
  
-     TASK_Append(TASK_ID_OLED, OLED_Handler, 50);
 
 - }
 
  
- #include "DOG02.c"
 
 - #include "DOG03.c"
 
 - #include "DOG04.c"
 
 - #include "DOG05.c"
 
 - #include "DOG06.c"
 
 - #include "DOG07.c"
 
 - #include "DOG08.c"
 
 - #include "DOG09.c"
 
 - #include "DOG10.c"
 
 - #include "DOG11.c"
 
 - #include "DOG12.c"
 
 - #include "DOG13.c"
 
 - #include "DOG14.c"
 
 - #include "DOG15.c"
 
 - #include "DOG16.c"
 
 - #include "DOG17.c"
 
 - #include "DOG18.c"
 
 - #include "DOG19.c"
 
 - #include "DOG20.c"
 
 - #include "DOG21.c"
 
 - #include "DOG22.c"
 
 - #include "DOG23.c"
 
 - #include "DOG24.c"
 
 - #include "DOG25.c"
 
 - #include "DOG26.c"
 
 - #include "DOG27.c"
 
  
- void OLED_DrawImage(uint8_t Width, uint8_t Height, const uint8_t *ImageData)
 
 - {
 
 -     uint8_t  x = 0, y = 0;
 
 -     uint8_t  Buffer[0x80];
 
 -     uint16_t Index    = 0;
 
  
-     for(y = 0; y < Height / 8; y++)
 
 -     {
 
 -         OLED_SetPosition(32, y);
 
  
-         for(x = 0; x < Width; x++)
 
 -         {
 
 -             Buffer[x] = ~ImageData[Index++];
 
 -         }
 
  
-         OLED_WriteBuffer(Buffer, Width);
 
 -     }
 
 - }
 
  
- void OLED_Handler(void)
 
 - {
 
 -     static uint8_t Index = 0;
 
  
-     switch(Index)
 
 -     {
 
 -         case  0 : OLED_DrawImage(64, 64, gImage_DOG02); break;
 
 -         case  1 : OLED_DrawImage(64, 64, gImage_DOG03); break;
 
 -         case  2 : OLED_DrawImage(64, 64, gImage_DOG04); break;
 
 -         case  3 : OLED_DrawImage(64, 64, gImage_DOG05); break;
 
 -         case  4 : OLED_DrawImage(64, 64, gImage_DOG06); break;
 
 -         case  5 : OLED_DrawImage(64, 64, gImage_DOG07); break;
 
 -         case  6 : OLED_DrawImage(64, 64, gImage_DOG08); break;
 
 -         case  7 : OLED_DrawImage(64, 64, gImage_DOG09); break;
 
 -         case  8 : OLED_DrawImage(64, 64, gImage_DOG10); break;
 
 -         case  9 : OLED_DrawImage(64, 64, gImage_DOG11); break;
 
 -         case  10: OLED_DrawImage(64, 64, gImage_DOG12); break;
 
 -         case  11: OLED_DrawImage(64, 64, gImage_DOG13); break;
 
 -         case  12: OLED_DrawImage(64, 64, gImage_DOG14); break;
 
 -         case  13: OLED_DrawImage(64, 64, gImage_DOG15); break;
 
 -         case  14: OLED_DrawImage(64, 64, gImage_DOG16); break;
 
 -         case  15: OLED_DrawImage(64, 64, gImage_DOG17); break;
 
 -         case  16: OLED_DrawImage(64, 64, gImage_DOG18); break;
 
 -         case  17: OLED_DrawImage(64, 64, gImage_DOG19); break;
 
 -         case  18: OLED_DrawImage(64, 64, gImage_DOG20); break;
 
 -         case  19: OLED_DrawImage(64, 64, gImage_DOG21); break;
 
 -         case  20: OLED_DrawImage(64, 64, gImage_DOG22); break;
 
 -         case  21: OLED_DrawImage(64, 64, gImage_DOG23); break;
 
 -         case  22: OLED_DrawImage(64, 64, gImage_DOG24); break;
 
 -         case  23: OLED_DrawImage(64, 64, gImage_DOG25); break;
 
 -         case  24: OLED_DrawImage(64, 64, gImage_DOG26); break;
 
 -         case  25: OLED_DrawImage(64, 64, gImage_DOG27); break;
 
 -     }
 
  
-     Index = (Index + 1) % 26;
 
 - }
 
  
OLED底层驱动显示效果:  
  
 2、u8g2移植  
 首先我们到https://github.com/olikraus/u8g2这个网址下载开源代码,当前版本中有基于C和C++这两种代码库,并提供了示例参考例程;我们使用的是基于C的开发库,所以将cscr文件中的源代码添加到工程中,根据文件类型和功能,对其进行分文件夹归类,最终如附件源码工程中所示;然后将u8g2的源代码添加到工程中,其中包含了u8g2的图形实现的功能程序,也有根据显示驱动来添加的底层驱动文件,最后对工程头文件包含路径进行设置,具体参考附件中的源代码工程;接下来我们就可以通过代码来实现,因为我们同时是驱动两个OLED,分别是I2C和SPI接口的,所以在移植的时候,也同时包含了这两种实现方式;我们在main.c文件中实现接口和功能,先根据不同的接口定义2个u8g2结构体变量并进行初始化: - u8g2_t u8g2_spi;
 
 - u8g2_t u8g2_i2c;
 
  
- void u8g2_UserInit(void)
 
 - {
 
 -     /* 初始化u8g2 */
 
 -     u8g2_Setup_ssd1306_128x64_noname_f(&u8g2_spi, U8G2_R0, u8x8_byte_4wire_sw_spi, u8x8_mm32_spi_gpio_and_delay);
 
  
-     /* 初始化显示器 */
 
 -     u8g2_InitDisplay(&u8g2_spi);
 
  
-     /* 唤醒显示器 */
 
 -     u8g2_SetPowerSave(&u8g2_spi, 0);
 
  
-     /* 设置英文字体 */
 
 -     u8g2_SetFont(&u8g2_spi, u8g2_font_6x12_mr);
 
  
 
-     /* 初始化u8g2 */
 
 -     u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2_i2c, U8G2_R0, u8x8_byte_sw_i2c, u8x8_mm32_i2c_gpio_and_delay);
 
  
-     /* 初始化显示器 */
 
 -     u8g2_InitDisplay(&u8g2_i2c);
 
  
-     /* 唤醒显示器 */
 
 -     u8g2_SetPowerSave(&u8g2_i2c, 0);
 
  
-     /* 设置英文字体 */
 
 -     u8g2_SetFont(&u8g2_i2c, u8g2_font_6x12_mr);
 
  
 
-     TASK_Append(TASK_ID_U8G2, u8g2_UserHandler, 50);
 
 - }
 
  
在初始化的时候,我们发现I2C和SPI调用的接口函数是不一样的,SPI接口调用的是u8g2_Setup_ssd1306_128x64_noname_f函数,I2C接口调用的是u8g2_Setup_ssd1306_i2c_128x64_noname_f函数;在这两个函数中形参功能都是类似的,第一个参数是全局u8g2结构体变量,第二个参数用于指示显示方向,第3个参数用于指明u8g2驱动操作函数,第4个参数用于指明u8g2底层GPIO操作的回调函数;不管是SPI还是I2C,对于u8g2底层驱动来说,都是用模拟时序的方式来实现的,所以在移植u8g2时,对之前的OLED_HW_I2C和OLED_HW_SPI这两个宏都要设置为0。  
 u8g2_Setup_ssd1306_128x64_noname_f和u8g2_Setup_ssd1306_i2c_128x64_noname_f这两个函数在u8g2_d_setup.c文件中,仅需要留下这两个函数就可以,将其它的函数进行屏蔽。在u8g2_d_memory.c文件中定义memory操作功能函数,我们这边选用u8g2_m_16_8_f这个函数,将其它的函数进行屏蔽。在后面示例中需要显示字符,所以就需要选择一个系统字库,通过调用u8g2_SetFont来进行设置;字体的选择在u8g2_fonts.c文件中,保留需要使用的字库,对其它的字库进行屏蔽。如上这些对没有使用的函数或者字体进行屏蔽是根据显示驱动、MEMORY管理和字体选择来决定的,屏蔽也是为了节省代码空间和未使用到的报错。  
 最后就是要实现底层对GPIO操作和延时的回调函数,供u8g2底层驱动来调用;在SPI接口调用时使用到SPI_DATA、SPI_CLOCK、SPI_CS、DC、RESET和DELAY_NANO,在I2C接口调用时使用到了I2C_DATA、I2C_CLOCK和DELAY_I2C;具体的函数接口实现如下所示: - uint8_t u8x8_mm32_spi_gpio_and_delay(
 
 -     U8X8_UNUSED u8x8_t *u8x8,
 
 -     U8X8_UNUSED uint8_t msg,
 
 -     U8X8_UNUSED uint8_t arg_int,
 
 -     U8X8_UNUSED void   *arg_ptr)
 
 - {
 
 -     switch (msg)
 
 -     {
 
 -         case U8X8_MSG_GPIO_AND_DELAY_INIT:
 
 -             OLED_Init();                    
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_SPI_DATA:
 
 -             if(arg_int) OLED_DI_H();
 
 -             else        OLED_DI_L();
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_SPI_CLOCK:
 
 -             if(arg_int) OLED_DO_H();
 
 -             else        OLED_DO_L();
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_CS:
 
 -             if(arg_int) OLED_CS_H();
 
 -             else        OLED_CS_L();
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_DC:
 
 -             if(arg_int) OLED_DC_H();
 
 -             else        OLED_DC_L();
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_RESET:
 
 -             if(arg_int) OLED_RES_H();
 
 -             else        OLED_RES_L();
 
 -             break;
 
  
-         case U8X8_MSG_DELAY_NANO:
 
 -             __nop();
 
 -             break;
 
  
-         default: return 0;
 
 -     }
 
  
-     return 1;
 
 - }
 
  
- uint8_t u8x8_mm32_i2c_gpio_and_delay(
 
 -     U8X8_UNUSED u8x8_t *u8x8,
 
 -     U8X8_UNUSED uint8_t msg,
 
 -     U8X8_UNUSED uint8_t arg_int,
 
 -     U8X8_UNUSED void   *arg_ptr)
 
 - {
 
 -     switch (msg)
 
 -     {
 
 -         case U8X8_MSG_GPIO_AND_DELAY_INIT:
 
 -             OLED_Init();                    
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_I2C_DATA:
 
 -             if(arg_int) OLED_SDA_H();
 
 -             else        OLED_SDA_L();
 
 -             break;
 
  
-         case U8X8_MSG_GPIO_I2C_CLOCK:
 
 -             if(arg_int) OLED_SCL_H();
 
 -             else        OLED_SCL_L();
 
 -             break;
 
  
-         case U8X8_MSG_DELAY_I2C:
 
 -             __nop();
 
 -             break;
 
  
-         default: return 0;
 
 -     }
 
  
-     return 1;
 
 - }
 
  
至此u8g2移植和初始化就完成了。  
  
 3、u8g2演示 演示的功能是分别在I2C和SPI接口的OLED显示屏上显示3个半径相同坐标不同的圆,圆半径由小到大,再由大到小进行变化显示,在屏幕顶端居中的位置显示当前的圆半径数值,具体代码实现如下: - void u8g2_UserHandler(void)
 
 - {
 
 -     static int rad = 0, direction = 0, select = 0;
 
  
-     char str[10];
 
 -     memset(str, 0, sizeof(str));
 
  
-     if(direction == 0)
 
 -     {
 
 -         if(++rad == 0x1F) direction = 1;
 
 -     }
 
 -     else
 
 -     {
 
 -         if(--rad == 0x01) direction = 0;
 
 -     }
 
  
-     sprintf(str, "%02d", rad);
 
  
-     if(select == 0)
 
 -     {
 
 -         /* 清空缓冲区的内容 */
 
 -         u8g2_ClearBuffer(&u8g2_spi);
 
  
-         /* 画圆 */
 
 -         u8g2_DrawCircle(&u8g2_spi, 32, 32, rad, U8G2_DRAW_ALL);
 
 -         u8g2_DrawCircle(&u8g2_spi, 64, 32, rad, U8G2_DRAW_ALL);
 
 -         u8g2_DrawCircle(&u8g2_spi, 96, 32, rad, U8G2_DRAW_ALL);
 
  
-         u8g2_DrawStr(&u8g2_spi, 58, 10, str);
 
  
-         /* 绘制缓冲区的内容 */
 
 -         u8g2_SendBuffer(&u8g2_spi);
 
 -     }
 
 -     else
 
 -     {
 
 -         /* 清空缓冲区的内容 */
 
 -         u8g2_ClearBuffer(&u8g2_i2c);
 
  
-         /* 画圆 */
 
 -         u8g2_DrawCircle(&u8g2_i2c, 32, 32, rad, U8G2_DRAW_ALL);
 
 -         u8g2_DrawCircle(&u8g2_i2c, 64, 32, rad, U8G2_DRAW_ALL);
 
 -         u8g2_DrawCircle(&u8g2_i2c, 96, 32, rad, U8G2_DRAW_ALL);
 
  
-         u8g2_DrawStr(&u8g2_i2c, 58, 10, str);
 
  
-         /* 绘制缓冲区的内容 */
 
 -         u8g2_SendBuffer(&u8g2_i2c);
 
 -     }
 
  
-     if((direction == 0) && (rad == 1))
 
 -     {
 
 -         if(select == 0) select = 1;
 
 -         else            select = 0;
 
 -     }
 
 - }
 
  
GUI演示效果:  
 附件  
 
 
 
  |