【CW32L052测评】硬件I2C驱动OLED屏
[复制链接]
一直以来,大家对硬件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屏,效果如下:
其余的代码我附上工程源码,有不足之处,请大家多多指教:
|