` 本帖最后由 朔风韧 于 2020-6-18 13:53 编辑
前言:
最近在考虑换工作的事情,工作交接也进行到最后的一个阶段了。时间真心不够用,本想赶在昨天将帖子发出来,可是一回来就睡着了。虽然现在不早了,还是挤点时间写点东西,也是对现在状态的一个记录,也是感谢厂商和社区给到的试用机会。
为了方便大家的移植,这里给出的是一个模拟IIC的驱动方式。这样最大的好处是,如果看透了可以在很短的时间内移植到别的开发板上。下面给大家一个演示的视频:
1、硬件准备
这里就使用活动中的 MM32L373PS 来驱动我们的OLED12864,因为采用的是模拟IIC的驱动方式,所以实际上要移植到其他的IC也是非常简单、快捷的。下面是板子的靓照:
根据板子的原理图,能够清楚的看到其中的SDA、SCL,其相对应的引脚为SDA---PB7,SCL---PB6,按照相关的IIC协议连接到我们的OLED就行。
这里简单说一下,为什么要这么来连接,其实也很简单,既然这个IO能够在采用模拟的驱动方式下点亮我们的OLED12864,那么后面使用硬件IIC肯定也是可以的。另外注意OLED12864的VCC和GND的连接,正在实际的使用中OLED12864的VCC使用3.3V也是可以点亮的。
2、软件设计
我们要移植这个GUI,首先得下载资源,网址:https://github.com/olikraus/u8g2 。
通过说明文档(readme.md)可以知道u8g2是一个支持嵌入式设备的显示驱动库,它包含了两种驱动,其中u8g2支持像12864这种常见的点阵型LCD,u8x8支持像1602这种字符型LCD,此外它所支持的所以驱动IC如下所示:
Supported Display Controller: SSD1305, SSD1306, SSD1309, SSD1322, SSD1325, SSD1327, SSD1329, SSD1606, SSD1607, SH1106, T6963, RA8835, LC7981, PCD8544, PCF8812, UC1601, UC1604, UC1608, UC1610, UC1611, UC1701, ST7565, ST7567, ST7588, ST75256, NT7534, IST3020, ST7920, LD7032, KS0108, SED1520, SBN1661, IL3820, MAX7219 (see here for a full list)
既然包含了SSD1306那这样就好办了,开始准备移植。
2.1、准备基本工程:
在移植U8G2的时候,官方推荐的步骤并没有说要咱们实现OLED的正常显示之后再去移植GUI。但是,毕竟这是采用的模拟驱动方式,和这个本身是为ARDUINO开发的GUI有点嫁接的意思,所以还是步步为营。
根据自己选择的OLED型号实现基本的显示,这点不难,很多厂商已经给我们提供了驱动。这里也分享一个OLED下的驱动:
注意一下,这里采用的IIC两线 通信方式,OLED的硬复位采用的是上电复位,因为复位时间的原因,我在初始化里面增加了延时。这个延时时间可以依照具体时间修改,如果觉得OLED的复位时间太长,可以修改上电复位 电路的电阻--改小。
2.2、移植U8G2的基础代码:
将csrc下的文件加入到项目目录,其中u8x8_d_器件名.c的文件只需选择自己对应的显示器驱动芯片即可,比如我的是OLED12864显示器,显示器驱动为SSD1306,分辨率为128x64,选择文件为u8x8_d_ssd1306_128x64_noname.c就行,但是我这里为了方便全部选上了。
2.3、修改配置文件 u8g2_d_setup.c
修改u8g2_d_setup.c文件,只保留自己使用的芯片对应的setup文件。比如我选择的是u8g2_Setup_ssd1306_128x64_noname_1,控制芯片SSD1306,分辨率128x64,128字节页大小。
2.4、实现u8x8_byte_sw_i2c、u8x8_ STM32_gpio_and_delay_cb---这是GUI和OLED交互的接口。
u8x8_stm32_gpio_and_delay_cb 的主要框架是使用switch case 实现各种情况下的延时,但是这里直接全部略过。
- uint8_t u8x8_stm32_gpio_and_delay_cb(U8X8_UNUSED u8x8_t *u8x8, U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, U8X8_UNUSED void *arg_ptr)
- {
- switch(msg) // there is no need for any delay
- {
- case U8X8_MSG_GPIO_AND_DELAY_INIT: // called once during init phase of u8g2/u8x8 -> Make with HAL_init
- //__NOP();
- break; // can be used to setup pins
- case U8X8_MSG_DELAY_NANO: // delay arg_int * 1 nano second
- //__NOP();
- break;
- case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
- //__NOP();
- //__NOP();
- //__NOP();
- break;
- case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
- // for (uint16_t n = 0; n < (320*arg_int); n++)
- // {
- // __NOP();
- // }
- break;
- case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
- // HAL_Delay(arg_int);
- break;
- case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
- //for (uint16_t n = 0; n < (160*arg_int); n++)
- // {
- // __NOP();
- // }
- break; // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
- case U8X8_MSG_GPIO_D0: // D0 or SPI clock pin: Output level in arg_int
- //case U8X8_MSG_GPIO_SPI_CLOCK:
- break;
- case U8X8_MSG_GPIO_D1: // D1 or SPI data pin: Output level in arg_int
- //case U8X8_MSG_GPIO_SPI_DATA:
- break;
- case U8X8_MSG_GPIO_D2: // D2 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_D3: // D3 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_D4: // D4 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_D5: // D5 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_D6: // D6 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_D7: // D7 pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_E: // E/WR pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_CS: // CS (chip select) pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_DC: // DC (data/cmd, A0, register select) pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_RESET: // Reset pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_CS1: // CS1 (chip select) pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_CS2: // CS2 (chip select) pin: Output level in arg_int
- break;
- case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
- break; // arg_int=1: Input dir with pullup high for I2C clock pin
- case U8X8_MSG_GPIO_I2C_DATA: // arg_int=0: Output low at I2C data pin
- break; // arg_int=1: Input dir with pullup high for I2C data pin
- }
- return 1;
- }
复制代码
u8x8_byte_sw_i2c 的方式实现,其实也有例子代码可以参考,但是一定要注意应答信号。我这里采用的是默认从设备正常回应了,有不好的地方,但是这样移植的时候方便。等到OLED正常显示的时候再去优化不迟到,先完成再完美。
- uint8_t u8x8_byte_sw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
- {
- uint8_t *ptr;
- static uint8_t buffer_count;
- static uint8_t buffer[DATA_BUFFER_SIZE+1]; //the size of buffer depends on how many pages are transfered at once
- //e.g. one page are 128byte and one byte more for command
- switch(msg)
- {
- case U8X8_MSG_BYTE_SEND: //collect
- {
- ptr = arg_ptr;
- while(arg_int>0)
- {
- Send_Byte(*(ptr++));
- I2C_WaitAck();
- arg_int--;
- }
- }
-
- break;
- case U8X8_MSG_BYTE_INIT:
- break;
- case U8X8_MSG_BYTE_SET_DC:
-
- break;
- case U8X8_MSG_BYTE_START_TRANSFER:
- I2C_Start();
- Send_Byte(0x78);
- I2C_WaitAck();
-
- break;
- case U8X8_MSG_BYTE_END_TRANSFER: // send all at once
- I2C_Stop();
- break;
- default:
- return 0;
- }
- return 1;
- }
复制代码
2.4、注释掉u8g2_d_memory.c文件里面的所有函数,文件中的函数是给显示器分配显存的,在u8g2_Setup_ssd1306_128x64_noname_1 中有调用此文件中的函数。注释掉后编译,查看哪个函数未定义就去掉相应函数的注释 。不然,可能编译器会报空间不足的错误。
2.5、初始化函数、测试函数:
初始化
- u8g2_Setup_ssd1306_i2c_128x64_noname_1(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_stm32_gpio_and_delay_cb); // init u8g2 structure
- u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
- u8g2_SetPowerSave(&u8g2, 0); // wake up display
复制代码
测试函数---这一段是引用(STM32下的硬件IIC实现):https://mbb.eet-china.com/forum/topic/74077_1_1.html
- u8g2_FirstPage(&u8g2);
- do
- {
- draw();
- }
- while ( u8g2_NextPage(&u8g2) ); // 8 times running
-
- if (step <= max)
- step += 3;
- else
- {
- step = 0;
- picture++;
- if ( picture >= 6)
- picture = 0;
- }
复制代码
程序到这里基本就完结了,主要的还是注意I2C_WaitAck()的调用。好了,话不多说,下面为整个工程的源码:
最后的话:
最近已经在交接工作了,如果有时间会在这个GUI的基础上做一些小项目。各位,后会有期,江湖再见。 `
|