1、前言
关于0.96吋OLED的详细操作,可查看一文彻底了解SSD1306驱动0.96寸OLED,本文移植u8g2图形库来驱动0.96吋OLED。
2、关于u8g2
u8g2是单色显示库的第二个版本。支持lcd和oled,支持众多驱动芯片,比如SSD1305, SSD1306, SSD1309, SSD1312, SSD1316, SSD1320, SSD1322, SSD1325, SSD1327, SSD1329, SSD1606, SSD1607, SH1106, SH1107, SH1108, SH1122, T6963, RA8835, LC7981, PCD8544, PCF8812, HX1230, UC1601, UC1604, UC1608, UC1610, UC1611, UC1617, UC1638, UC1701, ST7511, ST7528, ST7565, ST7567, ST7571, ST7586, ST7588, ST75256, ST75320, NT7534, ST7920, IST3020, IST7920, LD7032, KS0108, KS0713, HD44102, T7932, SED1520, SBN1661, IL3820, MAX7219 等。 另外,u8g2是开源的,可直接下载代码移植:https://github.com/olikraus/u8g2。
3、移植
(1)直接从github下载代码,u8g2支持c/c++ ,cppsrc是c++的,csrc文件夹下面是c的源码,在单片机上移植就只需关注csrc里面的文件: (2)u8g2里面支持多种驱动芯片,以u8x8_d_xxx.c 命名的就是驱动文件,本文使用的是0.96吋oled,芯片是ssd1306,因此只需将u8x8_d_ssd1312_128x64_noname.c 这个驱动文件添加到工程中: (3)修改"u8g2_d_setup.c "这个文件,里面有各种驱动芯片的初始化函数,删除其他函数,只留下与使用的驱动芯片相关的函数。 本文使用的ssd1306,但是与ssd1306相关的有多个函数,例如: u8g2_Setup_ssd1306_128x64_noname_1 、 u8g2_Setup_ssd1306_128x64_noname_2 、 u8g2_Setup_ssd1306_128x64_noname_f , 这些都是spi接口的; u8g2_Setup_ssd1306_i2c_128x64_noname_1 、 u8g2_Setup_ssd1306_i2c_128x64_noname_2 、 u8g2_Setup_ssd1306_i2c_128x64_noname_f , 这些都是i2c接口的; 后缀1、2、f代表缓冲区大小的不同: 1代表128字节, 2代表256字节, f代表1024字节; 根据单片机空间的大小选择合适的接口,缓冲区小的,刷新lcd/oled的时候就比较耗时,反之。 本文使用u8g2_Setup_ssd1306_i2c_128x64_noname_f 这个接口: (4)修改“u8g2_d_memory.c”文件,这个文件里面其实就是“u8g2_d_setup.c”文件对应的缓冲区,同上面一样,屏蔽掉没用到的,留下用到的: (5)关于字库 “u8g2_fonts.c ”文件中定义了各种字库,这些字库比较占用空间,根据使用情况屏蔽掉没有使用的。
(6)两个回调函数 void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) ;
参数byte_cb 和gpio_and_delay_cb 是需要编写的两个回调函数。
byte_cb :是通信相关的函数,比如i2c写数据, gpio_and_delay_cb :是延时相关的函数。 关于回调函数的写法,官方也给出了参考例子:https://github.com/olikraus/u8g2/wiki/Porting-to-new-MCU-platform 通信函数分为硬件接口和软件模拟方式,软件模拟方式官方基本写好了,只需要简单的指定io口即可。 (7)软件模拟i2c接口 通信函数:直接使用官方的这个u8x8_byte_sw_i2c
uint8_t u8x8_byte_sw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
uint8_t *data;
switch(msg)
{
case U8X8_MSG_BYTE_SEND:
data = (uint8_t *)arg_ptr;
while( arg_int > 0 )
{
i2c_write_byte(u8x8, *data);
data++;
arg_int--;
}
break;
case U8X8_MSG_BYTE_INIT:
i2c_init(u8x8);
break;
case U8X8_MSG_BYTE_SET_DC:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
i2c_start(u8x8);
i2c_write_byte(u8x8, u8x8_GetI2CAddress(u8x8));
break;
case U8X8_MSG_BYTE_END_TRANSFER:
i2c_stop(u8x8);
break;
default:
return 0;
}
return 1;
}
延时函数:在这个函数里面,根据传递的参数,拉低或者拉高SCL以及SDA。
uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
switch (msg)
{
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_DELAY_10MICRO:
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
case U8X8_MSG_DELAY_MILLI:
delay_ms(1);
break;
case U8X8_MSG_DELAY_I2C:
delay_us(5);
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
if(arg_int == 1)
{
gpio_bits_set(SCL_PORT,SCL_PIN);
}
else if(arg_int == 0)
{
gpio_bits_reset(SCL_PORT,SCL_PIN);
}
break;
case U8X8_MSG_GPIO_I2C_DATA:
if(arg_int == 1)
{
gpio_bits_set(SDA_PORT,SDA_PIN);
}
else if(arg_int == 0)
{
gpio_bits_reset(SDA_PORT,SDA_PIN);
}
break;
case U8X8_MSG_GPIO_MENU_SELECT:
u8x8_SetGPIOResult(u8x8, 0);
break;
case U8X8_MSG_GPIO_MENU_NEXT:
u8x8_SetGPIOResult(u8x8, 0);
break;
case U8X8_MSG_GPIO_MENU_PREV:
u8x8_SetGPIOResult(u8x8, 0);
break;
case U8X8_MSG_GPIO_MENU_HOME:
u8x8_SetGPIOResult(u8x8, 0);
break;
default:
u8x8_SetGPIOResult(u8x8, 1);
break;
}
return 1;
}
(8)硬件i2c接口 通讯函数:
uint8_t u8x8_byte_at32f425_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
static uint8_t buffer[32];
static uint8_t buf_idx;
uint8_t *data;
switch(msg)
{
case U8X8_MSG_BYTE_SEND:
data = (uint8_t *)arg_ptr;
while( arg_int > 0 ){
buffer[buf_idx++] = *data;
data++;
arg_int--;
}
break;
case U8X8_MSG_BYTE_INIT:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
buf_idx = 0;
break;
case U8X8_MSG_BYTE_END_TRANSFER:
HW_I2cWrite(buffer,buf_idx);
break;
default:
return 0;
}
return 1;
}
这里面主要的函数接口是HW_I2cWrite ,具体实现如下: i2c写一个缓存区数据,缓冲区为buf,数据长度为len
void HW_I2cWrite(uint8_t *buf,uint8_t len)
{
if(len<=0)
return ;
while(i2c_flag_get(I2C1, I2C_BUSYF_FLAG) );
i2c_transmit_set(I2C1, I2C_SLAVE_ADDRESS7, len, I2C_SOFT_STOP_MODE, I2C_GEN_START_WRITE);
i2c_start_generate(I2C1);
while(i2c_flag_get(I2C1, I2C_ADDRF_FLAG) );
for(uint8_t i=0;i<len;i++)
{
while(!i2c_flag_get(I2C1, I2C_TDIS_FLAG) );
i2c_data_send(I2C1, buf[i]);
}
i2c_stop_generate(I2C1);
while(!i2c_flag_get(I2C1, I2C_STOPF_FLAG) );
i2c_flag_clear(I2C1, I2C_STOPF_FLAG);
}
延时函数:
uint8_t u8g2_gpio_and_delay_at32f425(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:
break;
case U8X8_MSG_DELAY_MILLI:
delay_ms(arg_int);
break;
case U8X8_MSG_GPIO_I2C_CLOCK:
break;
case U8X8_MSG_GPIO_I2C_DATA:
break;
default:
return 0;
}
return 1;
}
4、代码测试
代码中使用了两种方式来测试,可选择软件或硬件i2c方式。
static u8g2_t u8g2;
void U8g2Init(void)
{
#if 0
SW_I2cInit();
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2,U8G2_R0,u8x8_byte_sw_i2c,u8x8_gpio_and_delay);
#else
HW_I2cInit();
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2,U8G2_R0,u8x8_byte_at32f425_hw_i2c,u8g2_gpio_and_delay_at32f425);
#endif
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
u8g2_ClearBuffer(&u8g2);
DrawLogo(&u8g2);
delay_ms(500);
delay_ms(500);
}
void U8g2Test(void)
{
DrawProcess(&u8g2);
DrawPoint(&u8g2);
DrawLine(&u8g2);
DrawBox(&u8g2);
DrawCircle(&u8g2);
DrawEllipse(&u8g2);
}
5、现象
测试视频:
工程源码:https://download.csdn.net/download/freemote/85232359
|