注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题
lugl4313820的个人空间 http://home.eeworld.com.cn/space-uid-1269709.html [收藏] [复制] [分享] [RSS]
日志

【CW32L052测评】硬件I2C驱动OLED屏

已有 328 次阅读2023-7-21 08:01

一直以来,大家对硬件I2C的驱动与使用有点不大支持,因为I2C硬件驱动,相比模拟的时序掌握比较难入手,移植也不是很方便,所以I2C模拟时序大行其道。

经过几天的学习CW32L052的用户手册,我发现其硬件的I2C的驱动的掌握难点在于,对其过 I2C 状态寄存器 I2Cx_STAT的掌握是一个难点,在其用户手册中,他的状态达28个之多,其中的26个为正常接收或发送状态,2个特殊状态(0xF8:I2C总线无可用信息;0x00: 总线错误)。其I2C状态码如下表所示:

 

经过学习官方的cw32l052_i2c.c中的函数,结合我以住驱动SSD1306的经验,成细的驱动了OLED屏,现在驱动方法分享如下:

1、选取合适的硬件I2C驱动管脚, 由于我原来在L083开发板上面使用了与LCD段码屏的管脚导致不起时序,所以这次我避免用到有可能起冲突的管脚。经查看原理图,开发板上的PB8,PB9是接到的开发板的EEPROM上的,原理图如下:

 于是,我选取PB8为SCL,PB9为SDA,经查看用户手册,这两个管脚为I2C1,复用管脚代码如下:

  PB08_AFx_I2C1SCL();
  PB09_AFx_I2C1SDA();

初始化的次序为:使能GPIOB的时钟——使能I2C1时钟——复用GPIO为I2C1——配置GPIO为GPIO_MODE_OUTPUT_OD模式——配置I2C的波特率——配置I2C1总线——使用能I2C1,具体代码如下:

void OLED_I2C_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure = {0};
	I2C_InitTypeDef I2C_InitStruct = {0};
	
//	__RCC_GPIOB_CLK_ENABLE();
//	__RCC_I2C1_CLK_ENABLE();
	CW_SYSCTRL->AHBEN_f.GPIOB  = 1;
	CW_SYSCTRL->APBEN1_f.I2C1 = 1U;    //
	
	PB08_AFx_I2C1SCL();
  PB09_AFx_I2C1SDA();
	GPIO_InitStructure.Pins = I2C1_SCL_GPIO_PIN | I2C1_SDA_GPIO_PIN;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
	GPIO_Init(I2C1_SCL_GPIO_PORT, &GPIO_InitStructure);
	
	I2C_InitStruct.I2C_Baud = 0x1; // 48000 000/(8*(1+11) = 500k
	I2C_InitStruct.I2C_BaudEn = ENABLE;
  I2C_InitStruct.I2C_FLT = DISABLE;
  I2C_InitStruct.I2C_AA =  DISABLE;
	
	I2C1_DeInit();
  I2C_Master_Init(CW_I2C1,&I2C_InitStruct);//初始化模块

	I2C_Cmd(CW_OLED_I2C, ENABLE);
}

此次I2C不起用中断,采取循环获取I2C状态来决定下一步数据写入的方法。

需要驱动OLED,首先发出起始信号,然后判断STA状态寄存器的状态来做下一步的动作。而驱动SSD1306最基本的函数为向其写入一个byte的数据,其他的都是可以通用的,具体实现的代码如下:

//向OLED寄存器地址写一个byte的数据
int I2C_WriteByte(uint8_t addr,uint8_t data)
{
	uint8_t u8i = 0, u8State;
  uint16_t timeout = 0xffff;
	I2C_GenerateSTART(CW_OLED_I2C, ENABLE);
	//获取状态
	while(1)
	{
		
		while((0 == I2C_GetIrq(CW_OLED_I2C)) && timeout--);
		if(timeout == 0) return 1;
		u8State = I2C_GetState(CW_OLED_I2C);
		switch(u8State) 
		{
			case 0x08:  //发送完START信号
				I2C_GenerateSTART(CW_OLED_I2C, DISABLE);
				I2C_Send7bitAddress(CW_OLED_I2C, OLED_ADDR, 0x00);
				break;
			case 0x18:  //发送完SLA+W信号,ACK已收到
				I2C_SendData(CW_OLED_I2C, addr);
				break;
			case 0x28:
				I2C_SendData(CW_OLED_I2C, data);
				u8i ++;
				break;
			case 0x20: //发送完SLA+W后从机返回NACK
				break;
			case 0x38: //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲载  或者  主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
				I2C_GenerateSTART(CW_OLED_I2C, ENABLE);
        break;
			case 0x30:
				I2C_GenerateSTOP(CW_OLED_I2C, ENABLE);
				break;
			default:
				break;		
		}
		if(u8i>1)
		{
			I2C_GenerateSTOP(CW_OLED_I2C, ENABLE);
			I2C_ClearIrq(CW_OLED_I2C);
			break;
		}
		I2C_ClearIrq(CW_OLED_I2C);
		
	}
	return 0;
}

实现好这个函数后,我们使用以住的驱动库,就可以顺利的点亮OLED屏了,在主函数中我们写入测试函数:

	OLED_Init();
	SysTickDelay(500);
	OLED_Fill(0xff);
	SysTickDelay(500);
	OLED_Fill(0x00);
	SysTickDelay(500);
	OLED_ShowStr(31,2,"Hello World",1);
	OLED_ShowStr(10,5,"Hello CW32L052",2);

顺利的点亮OLED屏,效果如下:

 其余的代码我附上工程源码,有不足之处,请大家多多指教:

 

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

热门文章