本文使用软硬件I2C驱动0.96吋OLED,先上一张图:
1、关于N32L43x的I2C接口
2、开发板上I2C的引脚
4、代码实现
(1)硬件i2c
void I2cInit(void)
{
I2C_InitType i2c1_master;
GPIO_InitType i2c1_gpio;
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_I2C1, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
/*PB8 -- SCL; PB9 -- SDA*/
GPIO_InitStruct(&i2c1_gpio);
i2c1_gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9;
i2c1_gpio.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
i2c1_gpio.GPIO_Mode = GPIO_Mode_AF_OD;
i2c1_gpio.GPIO_Alternate = GPIO_AF4_I2C1;
i2c1_gpio.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitPeripheral(GPIOB, &i2c1_gpio);
I2C_DeInit(I2C1);
i2c1_master.BusMode = I2C_BUSMODE_I2C;
i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2c1_master.OwnAddr1 = I2C_OWN_ADDRESS7;
i2c1_master.AckEnable = I2C_ACKEN;
i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_master.ClkSpeed = 100000; // 100K
I2C_Init(I2C1, &i2c1_master);
I2C_Enable(I2C1, ENABLE);
}
void OledWriteCmd(uint8_t var)
{
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY));
I2C_GenerateStart(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)); // EV5
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDRESS7, I2C_DIRECTION_SEND);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG));// EV6
I2C_SendData(I2C1, 0x00);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
I2C_SendData(I2C1, var);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED));// EV8-2
I2C_GenerateStop(I2C1, ENABLE);
}
void OledWriteData(uint8_t var)
{
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY));
I2C_GenerateStart(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)); // EV5
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDRESS7, I2C_DIRECTION_SEND);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG));// EV6
I2C_SendData(I2C1, 0x40);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
I2C_SendData(I2C1, var);
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)); // EV8
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED));// EV8-2
I2C_GenerateStop(I2C1, ENABLE);
}
(2)软件i2c
#define _SCL_PORT GPIOB
#define _SCL_PIN GPIO_PIN_8
#define _SDA_PORT GPIOB
#define _SDA_PIN GPIO_PIN_9
void _I2C_Init(void)
{
//PB8:SCL PB9:SDA
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
GPIO_InitType i2c1_gpio;
GPIO_InitStruct(&i2c1_gpio);
i2c1_gpio.Pin = GPIO_PIN_8 | GPIO_PIN_9;
i2c1_gpio.GPIO_Current = GPIO_DC_12mA;
i2c1_gpio.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
i2c1_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
i2c1_gpio.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitPeripheral(GPIOB, &i2c1_gpio);
}
void _SDA_IN(void)
{
GPIO_InitType i2c1_gpio;
GPIO_InitStruct(&i2c1_gpio);
i2c1_gpio.Pin = GPIO_PIN_9;
i2c1_gpio.GPIO_Current = GPIO_DC_12mA;
i2c1_gpio.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
i2c1_gpio.GPIO_Mode = GPIO_Mode_Input;
i2c1_gpio.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitPeripheral(GPIOB, &i2c1_gpio);
}
void _SDA_OUT(void)
{
GPIO_InitType i2c1_gpio;
GPIO_InitStruct(&i2c1_gpio);
i2c1_gpio.Pin = GPIO_PIN_9;
i2c1_gpio.GPIO_Current = GPIO_DC_12mA;
i2c1_gpio.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
i2c1_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
i2c1_gpio.GPIO_Pull = GPIO_Pull_Up;
GPIO_InitPeripheral(GPIOB, &i2c1_gpio);
}
void _I2C_Start(void)
{
_SDA_OUT();
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //SDA=1
DelayUs(10);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(10);
GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
DelayUs(10);
GPIO_ResetBits(_SCL_PORT,_SCL_PIN);//SCL=0
DelayUs(10);
}
void _I2C_Stop(void)
{
_SDA_OUT();
GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
DelayUs(10);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(10);
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //SDA=1
DelayUs(10);
}
void _I2C_Ack(void)
{
_SDA_OUT();
GPIO_ResetBits(_SDA_PORT,_SDA_PIN);//SDA=0
DelayUs(5);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(5);
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
}
void _I2C_NAck(void)
{
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //SDA=1
DelayUs(10);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(10);
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
DelayUs(10);
}
uint8_t _I2C_Wait_Ack(void)
{
uint8_t ucErrTime=0;
#if 0
SDA_IN();
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //释放总线
#else
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //释放总线
_SDA_IN();
#endif
DelayUs(5);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(5);
while(GPIO_ReadInputDataBit(_SDA_PORT,_SDA_PIN))
{
ucErrTime++;
if(ucErrTime>250)
{
_I2C_Stop();
return 1;
}
}
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
DelayUs(5);
return 0;
}
uint8_t _I2C_Read_Byte(uint8_t ack)
{
uint8_t i,rxdata=0;
#if 0
_SDA_IN();
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //释放总线
#else
GPIO_SetBits(_SDA_PORT,_SDA_PIN); //释放总线
_SDA_IN();
#endif
for(i=0;i<8;i++ )
{
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
DelayUs(5);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(5);
rxdata<<=1;
if(GPIO_ReadInputDataBit(_SDA_PORT,_SDA_PIN))
{
rxdata|=0x01;
}
DelayUs(5);
}
if (!ack)
_I2C_NAck();//nACK
else
_I2C_Ack(); //ACK
return rxdata;
}
void _I2C_Send_Byte(uint8_t txd)
{
uint8_t i;
_SDA_OUT();
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
for(i=0;i<8;i++)
{
if((txd&0x80)==0x80)
GPIO_SetBits(_SDA_PORT,_SDA_PIN);
else
GPIO_ResetBits(_SDA_PORT,_SDA_PIN);
txd<<=1;
DelayUs(5);
GPIO_SetBits(_SCL_PORT,_SCL_PIN); //SCL=1
DelayUs(5);
GPIO_ResetBits(_SCL_PORT,_SCL_PIN); //SCL=0
DelayUs(5);
}
}
void _SSD1306_WriteCmd(uint8_t var)
{
_I2C_Start();
_I2C_Send_Byte(I2C_SLAVE_ADDRESS7); //write addr
_I2C_Wait_Ack();
_I2C_Send_Byte(0x00);
_I2C_Wait_Ack();
_I2C_Send_Byte(var);
_I2C_Wait_Ack();
_I2C_Stop();
}
void _SSD1306_WriteData(uint8_t var)
{
_I2C_Start();
_I2C_Send_Byte(I2C_SLAVE_ADDRESS7); //write addr
_I2C_Wait_Ack();
_I2C_Send_Byte(0x40);
_I2C_Wait_Ack();
_I2C_Send_Byte(var);
_I2C_Wait_Ack();
_I2C_Stop();
}
(3)OLED驱动相关
//SSD1306初始化
void OledInit(void)
{
DelayMs(300);
DelayMs(300);
//SSD1306复位之后,默认的是页寻址方式
SSD1306_WriteCmd(0xAE);//--display off
SSD1306_WriteCmd(0x00);//--set low column address
SSD1306_WriteCmd(0x10);//--set high column address
SSD1306_WriteCmd(0x40);//--set start line address
SSD1306_WriteCmd(0xB0);//--set page address
SSD1306_WriteCmd(0x81);// contract control
SSD1306_WriteCmd(0xFF);//--128
SSD1306_WriteCmd(0xA1);//set segment re-map 0 to 127
SSD1306_WriteCmd(0xA6);//set normal display
SSD1306_WriteCmd(0xA8);//set multiplex ratio(1 to 64)
SSD1306_WriteCmd(0x3F);//--1/32 duty
SSD1306_WriteCmd(0xC8);//Com scan direction
SSD1306_WriteCmd(0xD3);//set display offset
SSD1306_WriteCmd(0x00);//no offset
SSD1306_WriteCmd(0xD5);//set display clock divide ratio/oscillator frequency
SSD1306_WriteCmd(0x80);//
SSD1306_WriteCmd(0xD8);//set area color mode off
SSD1306_WriteCmd(0x05);//
SSD1306_WriteCmd(0xD9);//Set Pre-Charge Period
SSD1306_WriteCmd(0xF1);//
SSD1306_WriteCmd(0xDA);//set com pin hardware configuartion
SSD1306_WriteCmd(0x12);//
SSD1306_WriteCmd(0xDB);//set Vcomh
SSD1306_WriteCmd(0x30);//0x20,0.77xVcc
SSD1306_WriteCmd(0x8D);//set charge pump enable
SSD1306_WriteCmd(0x14);//
SSD1306_WriteCmd(0xAF);//--turn on Oled panel
}
//坐标设置:也就是在哪里显示
void OledSetPos(uint8_t x, uint8_t y)
{
//以下3个寄存器只在页寻址的模式下有效
SSD1306_WriteCmd(0xb0+y); //页地址设置 0xb0~0xb7
SSD1306_WriteCmd(((x&0xf0)>>4)|0x10); //列高位地址设置
SSD1306_WriteCmd((x&0x0f)); //列低位地址设置
}
//开启Oled显示
void OledDisplayOn(void)
{
SSD1306_WriteCmd(0X8D); //SET DCDC命令
SSD1306_WriteCmd(0X14); //DCDC ON
SSD1306_WriteCmd(0XAF); //DISPLAY ON
}
//关闭Oled显示
void OledDisplayOff(void)
{
SSD1306_WriteCmd(0X8D); //SET DCDC命令
SSD1306_WriteCmd(0X10); //DCDC OFF
SSD1306_WriteCmd(0XAE); //DISPLAY OFF
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样
void OledClear(void)
{
uint8_t i,n;
for(i=0;i<8;i++)
{
SSD1306_WriteCmd (0xb0+i); //设置页地址(0~7)
SSD1306_WriteCmd (0x00); //设置显示位置—列低地址
SSD1306_WriteCmd (0x10); //设置显示位置—列高地址
for(n=0;n<128;n++)
SSD1306_WriteData(0);
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127,y:0~7
//Char_Size:选择字体 16/12
////8*16的点阵,取模方式:阴码、逆向、列行式
void OledShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size)
{
uint8_t c=0,i=0;
c=chr-' ';//得到偏移后的值
if(x>MAX_COLUMN-1)
{
x=0;
y=y+2;
}
if(Char_Size ==16)
{
for(i=0;i<8;i++)
{
OledSetPos(x+i,y);
SSD1306_WriteData(Ascii_8x16[c]);//先写上半部分
}
for(i=0;i<8;i++)
{
OledSetPos(x+i,y+1);
SSD1306_WriteData(Ascii_8x16[c][i+8]);//后写下半部分
}
}
else
{
OledSetPos(x,y);
for(i=0;i<6;i++)
{
SSD1306_WriteData(Ascii_6x12[c]);
}
}
}
//显示一个字符串
void OledShowString(uint8_t x,uint8_t y,char *str,uint8_t Char_Size)
{
unsigned char j=0;
while (str[j]!='\0')
{
OledShowChar(x,y,str[j],Char_Size);
x+=8;
if(x>120)
{
x=0;
y+=2;
}
j++;//移动一次就是一个page,取值0-7
}
}
//显示汉字
//由于汉字是16*16大小的,所以最多显示4行汉字
//index:在汉字取模中的索引
void OledShowCN(uint8_t x,uint8_t y,uint8_t index)
{
uint8_t t;
OledSetPos(x,y);
for(t=0;t<16;t++)
{
SSD1306_WriteData(Hzk[index][t]);
}
OledSetPos(x,y+1);
for(t=0;t<16;t++)
{
SSD1306_WriteData(Hzk[index][t+16]);
}
}
(4)OLED显示测试
void OledDisplayTest(void)
{
#if SOFT_I2C
_I2C_Init();
#else
I2cInit();
#endif
OledInit();
OledClear();
OledShowString(40,2,(char *)("Nation"),16);
OledShowString(40,4,(char *)("N32L43x"),16);
OledShowCN(24,6,0);
OledShowCN(40,6,1);
OledShowCN(56,6,2);
OledShowCN(72,6,3);
}
5、现象
本帖最后由 freeelectron 于 2022-7-13 20:02 编辑上传完整代码和字库
是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?
引用: 秦天qintian0303 发表于 2022-7-14 09:33 是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?
测试的时候是没有卡死的
引用: freeelectron 发表于 2022-7-14 10:17 测试的时候是没有卡死的
明天参考楼主的程序测试一下,今天对照官方示例程序,I2C不稳定,会有卡住的情况;后面还专门给I2C引脚焊接了上拉电阻,也没有改善;同样的功能,使用其它MCU的硬件I2C就完全没有问题;问题还是在底层I2C这一块,上层的配置,应用没问题……
引用: 秦天qintian0303 发表于 2022-7-14 09:33 是否有遇到ST的IIC通信卡死现象?国民科技的IIC过程是循环等待标志位的策略吗?
应该需要加入等超时的参数,楼主这里可能没有加进这个参数。
引用: lugl4313820 发表于 2022-9-6 16:13 应该需要加入等超时的参数,楼主这里可能没有加进这个参数。
IIC的速度不开,感觉确实没有模拟IIC方便,细节修改会直观一点
引用: xld0932 发表于 2022-7-28 22:44 明天参考楼主的程序测试一下,今天对照官方示例程序,I2C不稳定,会有卡住的情况;后面还专门给I2C引脚焊 ...
请问官方的示例程序是在什么地方下的