前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【开源】硬件/软件i2c两种方式移植u8g2单色图形库驱动0.96吋OLED

【开源】硬件/软件i2c两种方式移植u8g2单色图形库驱动0.96吋OLED

作者头像
ManInRoad
发布2022-05-25 15:41:25
1.7K1
发布2022-05-25 15:41:25
举报

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_1u8g2_Setup_ssd1306_128x64_noname_2u8g2_Setup_ssd1306_128x64_noname_f, 这些都是spi接口的; u8g2_Setup_ssd1306_i2c_128x64_noname_1u8g2_Setup_ssd1306_i2c_128x64_noname_2u8g2_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_cbgpio_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));
      //i2c_write_byte(u8x8, 0x078);
      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: // delay arg_int * 100 nano seconds
        __NOP();
        break;
    case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
        for (uint16_t n = 0; n < 320; n++)
        {
            __NOP();
        }
        break;
    case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
        delay_ms(1);
        break;
    case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
        delay_us(5);
        break;                    // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
		if(arg_int == 1) 
		{
			gpio_bits_set(SCL_PORT,SCL_PIN);//SCL=1
		}
		else if(arg_int == 0)
		{
			gpio_bits_reset(SCL_PORT,SCL_PIN);//SCL=0
		}  
        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
        if(arg_int == 1) 
		{
			gpio_bits_set(SDA_PORT,SDA_PIN);  //SDA=1
		}
		else if(arg_int == 0)
		{
			gpio_bits_reset(SDA_PORT,SDA_PIN);  //SDA=0
		} 
        break;                    // arg_int=1: Input dir with pullup high for I2C data pin
    case U8X8_MSG_GPIO_MENU_SELECT:
        u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
        break;
    default:
        u8x8_SetGPIOResult(u8x8, 1); // default return value
        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];		/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
	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:
		  /* add your custom code to init i2c subsystem */
		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 ;

	/* wait for the busy falg to be reset */
	while(i2c_flag_get(I2C1, I2C_BUSYF_FLAG) );

	/* start transfer */
	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; // command processed successfully.
}

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); // send init sequence to the display, display is in sleep mode after this,
	u8g2_SetPowerSave(&u8g2, 0); // wake up display	
	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、现象

http://mpvideo.qpic.cn/0bc3u4aaqaaa5qan5wthefrfbj6dbctqacaa.f10002.mp4?dis_k=8603e6445ee51c13614c81dbe6b57ff7&dis_t=1653464414&vid=wxv_2371962195292438528&format_id=10002&support_redirect=0&mmversion=false

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-04-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 物联网思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、前言
  • 2、关于u8g2
  • 3、移植
  • 4、代码测试
  • 5、现象
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档