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

关于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);	
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值