1996|3

20

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

[N32L43X评测] 2.模拟I2C驱动OLED [复制链接]

 

I2C总线是我们开发过程中经常使用的一种串行总线。

I2C主要功能特点包括:

    1、只需要两条总线;

    2、所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一地址进行软件寻址;

    3、是真正的多主设备总线,可提供仲裁和冲突检测;

    4、最大从机数:理论上是127;

    5、传输速度:

        标准模式:Standard Mode = 100 Kbps

        快速模式:Fast Mode = 400 Kbps

        高速模式:High speed mode = 3.4 Mbps

        超快速模式:Ultra fast mode = 5 Mbps

I2C总线协议详解可参考以下资料:

UM10204_I2C-bus specification and user manual-Rev.7.0.pdf (733.36 KB, 下载次数: 2)

I2C总线规范.pdf (918.13 KB, 下载次数: 3)

此篇主要介绍GPIO软件模拟实现I2C来驱动0.96寸OLED显示屏,OLED显示屏资料: 0.96寸OLED使用文档V3.0.pdf (1.68 MB, 下载次数: 3)

硬件连接

GND  ——  GND

VCC  ——  3.3V

SCL  ——  PB8

SDA  ——  PB9

软件代码

I2C代码:

  • //设置SDA输入模式
  • void SDA_IN(void)
  • {
  • GPIO_InitType GPIO_InitStructure;
  • RCC_EnableAPB2PeriphClk( SDA_GPIO_CRM_CLK, ENABLE);
  • GPIO_InitStruct(&GPIO_InitStructure);
  • GPIO_InitStructure.Pin = SDA_PIN;
  • GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  • GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
  • GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
  • GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Input;
  • GPIO_InitPeripheral(SDA_GPIO, &GPIO_InitStructure);
  • }
  • //设置SDA为输出模式
  • void SDA_OUT(void)
  • {
  • GPIO_InitType GPIO_InitStructure;
  • RCC_EnableAPB2PeriphClk( SDA_GPIO_CRM_CLK, ENABLE);
  • GPIO_InitStruct(&GPIO_InitStructure);
  • GPIO_InitStructure.Pin = SDA_PIN;
  • GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  • GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
  • GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
  • GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  • GPIO_InitPeripheral(SDA_GPIO, &GPIO_InitStructure);
  • }
  • //设置SCL电平
  • void I2C_SCL(int n)
  • {
  • if(n == 1)
  • {
  • GPIO_WriteBit(SCL_GPIO, SCL_PIN, Bit_SET); //设置SCL为高电平
  • }
  • else
  • {
  • GPIO_WriteBit(SCL_GPIO, SCL_PIN, Bit_RESET); //设置SCL为低电平
  • }
  • }
  • //设置SDA电平
  • void I2C_SDA(int n)
  • {
  • if(n == 1)
  • {
  • GPIO_WriteBit(SDA_GPIO, SDA_PIN, Bit_SET); //设置SDA为高电平
  • }
  • else
  • {
  • GPIO_WriteBit(SDA_GPIO, SDA_PIN, Bit_RESET); //设置SDA为低电平
  • }
  • }
  • //读取SDA电平
  • unsigned char READ_SDA(void)
  • {
  • return GPIO_ReadInputDataBit(SDA_GPIO, SDA_PIN); //读取SDA电平
  • }
  • //I2C初始化
  • void I2C_Initial(void)
  • {
  • GPIO_InitType GPIO_InitStructure;
  • //根据GPIO组初始化GPIO时钟
  • RCC_EnableAPB2PeriphClk( SCL_GPIO_CRM_CLK, ENABLE);
  • RCC_EnableAPB2PeriphClk( SDA_GPIO_CRM_CLK, ENABLE);
  • //GPIO_SCL初始化设置
  • GPIO_InitStruct(&GPIO_InitStructure);
  • GPIO_InitStructure.Pin = SCL_PIN;
  • GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  • GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
  • GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
  • GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  • GPIO_InitPeripheral(SCL_GPIO, &GPIO_InitStructure);
  • //GPIO_SDA初始化设置
  • GPIO_InitStruct(&GPIO_InitStructure);
  • GPIO_InitStructure.Pin = SDA_PIN;
  • GPIO_InitStructure.GPIO_Slew_Rate = GPIO_Slew_Rate_High;
  • GPIO_InitStructure.GPIO_Current = GPIO_DC_4mA;
  • GPIO_InitStructure.GPIO_Pull = GPIO_Pull_Up;
  • GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  • GPIO_InitPeripheral(SDA_GPIO, &GPIO_InitStructure);
  • //SCL、SDA的初始化均为高电平
  • I2C_SCL(1);
  • I2C_SDA(1);
  • }
  • //I2C Start
  • void I2C_Start(void)
  • {
  • SDA_OUT();
  • I2C_SDA(1);
  • I2C_SCL(1);
  • Delay_Us(4);
  • I2C_SDA(0); //START:when CLK is high,DATA change form high to low
  • Delay_Us(4);
  • I2C_SCL(0); //钳住I2C总线,准备发送或接收数据
  • }
  • //I2C Stop
  • void I2C_Stop(void)
  • {
  • SDA_OUT();
  • I2C_SCL(0);
  • I2C_SDA(0); //STOP:when CLK is high DATA change form low to high
  • Delay_Us(4);
  • I2C_SCL(1);
  • I2C_SDA(1); //发送I2C总线结束信号
  • Delay_Us(4);
  • }
  • //I2C_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
  • unsigned char I2C_Wait_Ack(void) //IIC_Wait_ack,返回wait失败或是成功
  • {
  • unsigned char ucErrTime = 0;
  • SDA_IN();
  • I2C_SDA(1);
  • Delay_Us(1);
  • I2C_SCL(1);
  • Delay_Us(1);
  • while(READ_SDA())
  • {
  • ucErrTime++;
  • if(ucErrTime > 250)
  • {
  • I2C_Stop();
  • return HAL_ERROR;
  • }
  • }
  • I2C_SCL(0);
  • return HAL_OK;
  • }
  • //产生ACK应答
  • void I2C_Ack(void)
  • {
  • I2C_SCL(0);
  • SDA_OUT();
  • I2C_SDA(0);
  • Delay_Us(2);
  • I2C_SCL(1);
  • Delay_Us(2);
  • I2C_SCL(0);
  • }
  • //产生NACK应答
  • void I2C_NAck(void)
  • {
  • I2C_SCL(0);
  • SDA_OUT();
  • I2C_SDA(1);
  • Delay_Us(2);
  • I2C_SCL(1);
  • Delay_Us(2);
  • I2C_SCL(0);
  • }
  • //I2C_Send_Byte,入口参数为要发送的字节
  • void I2C_Send_Byte(unsigned char txd)
  • {
  • unsigned char cnt = 0;
  • SDA_OUT();
  • I2C_SCL(0);
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • I2C_SDA((txd & 0x80) >> 7);
  • txd <<= 1;
  • Delay_Us(2);
  • I2C_SCL(1);
  • Delay_Us(2);
  • I2C_SCL(0);
  • Delay_Us(2);
  • }
  • }
  • //I2C_Read_Byte,入口参数为是否要发送ACK信号
  • unsigned char I2C_Read_Byte(unsigned char ack)
  • {
  • unsigned char cnt, rec = 0;
  • SDA_IN();
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • I2C_SCL(0);
  • Delay_Us(2);
  • I2C_SCL(1);
  • rec <<= 1;
  • if(READ_SDA())
  • {
  • rec++;
  • }
  • Delay_Us(1);
  • }
  • if(!ack)
  • {
  • I2C_NAck();
  • }
  • else
  • {
  • I2C_Ack();
  • }
  • return rec;
  • }

OLED代码:

  • //向设备写控制命令
  • static void OLED_Write_CMD(unsigned char cmd)
  • {
  • I2C_Start();
  • I2C_Send_Byte(0x78);
  • I2C_Wait_Ack();
  • I2C_Send_Byte(0x00);
  • I2C_Wait_Ack();
  • I2C_Send_Byte(cmd);
  • I2C_Wait_Ack();
  • I2C_Stop();
  • }
  • //向设备写数据
  • static void OLED_Write_Date(unsigned char date)
  • {
  • I2C_Start();
  • I2C_Send_Byte(0x78);
  • I2C_Wait_Ack();
  • I2C_Send_Byte(0x40);
  • I2C_Wait_Ack();
  • I2C_Send_Byte(date);
  • I2C_Wait_Ack();
  • I2C_Stop();
  • }
  • //坐标设置
  • static void OLED_Set_Pos(unsigned char x, unsigned char y)
  • {
  • OLED_Write_CMD(0xB0 + y);
  • OLED_Write_CMD(((x & 0xF0) >> 4) | 0x10);
  • OLED_Write_CMD(x & 0x0F);
  • }
  • //开启OLED显示
  • static void OLED_Display_On(void)
  • {
  • OLED_Write_CMD(0x8D); //SET DCDC命令
  • OLED_Write_CMD(0x14); //DCDC ON
  • OLED_Write_CMD(0xAF); //DISPLAY ON
  • }
  • //关闭OLED显示
  • static void OLED_Display_Off(void)
  • {
  • OLED_Write_CMD(0x8D); //SET DCDC命令
  • OLED_Write_CMD(0x10); //DCDC OFF
  • OLED_Write_CMD(0xAE); //DISPLAY OFF
  • }
  • //OLED清屏
  • void OLED_Clear(void)
  • {
  • unsigned char cnt, count;
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • OLED_Write_CMD(0xB0 + cnt);
  • OLED_Write_CMD(0x00);
  • OLED_Write_CMD(0x10);
  • for(count = 0; count < 128; count++)
  • {
  • OLED_Write_Date(0x00);
  • }
  • }
  • }
  • //OLED清行
  • void OLED_Clear_Row(unsigned char n)
  • {
  • unsigned char count;
  • OLED_Write_CMD(0xB0 + n);
  • OLED_Write_CMD(0x00);
  • OLED_Write_CMD(0x10);
  • for(count = 0; count < 128; count++)
  • {
  • OLED_Write_Date(0x00);
  • }
  • }
  • //OLED填满屏幕
  • void OLED_Fill(void)
  • {
  • unsigned char cnt, count;
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • OLED_Write_CMD(0xB0 + cnt); //设置页地址(0~7)
  • OLED_Write_CMD(0x00); //设置显示位置—列低地址
  • OLED_Write_CMD(0x10); //设置显示位置—列高地址
  • for(count = 0; count < 128; count++)
  • {
  • OLED_Write_Date(0x01);
  • }
  • }
  • }
  • //指定位置显示一个字符
  • //x:0~127
  • //y:0~63
  • //chr:字符
  • //size:选择字体 16/12
  • void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
  • {
  • unsigned char offset = 0, cnt = 0;
  • offset = chr - ' '; //计算偏移量
  • if(x > 128 - 1)
  • {
  • x = 0;
  • y = y + 2;
  • }
  • if(size == 16)
  • {
  • OLED_Set_Pos(x, y);
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • OLED_Write_Date(F8x16[offset * 16 + cnt]);
  • }
  • OLED_Set_Pos(x, y + 1);
  • for(cnt = 0; cnt < 8; cnt++)
  • {
  • OLED_Write_Date(F8x16[offset * 16 + cnt + 8]);
  • }
  • }
  • else
  • {
  • OLED_Set_Pos(x, y);
  • for(cnt = 0; cnt < 6; cnt++)
  • {
  • OLED_Write_Date(F6x8[offset][cnt]);
  • }
  • }
  • }
  • unsigned int oled_pow(unsigned char m, unsigned char n)
  • {
  • unsigned int result = 1;
  • while(n--)
  • {
  • result *= m;
  • }
  • return result;
  • }
  • //指定位置显示一个数字
  • //x,y:起点坐标
  • //num:数值(0~4294967295)
  • //len:数字的位数
  • //size:字体大小
  • void OLED_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
  • {
  • unsigned char cnt, temp;
  • unsigned char show = 0;
  • for(cnt = 0; cnt < len; cnt++)
  • {
  • temp = (num / oled_pow(10, len - cnt - 1)) % 10;
  • if(show == 0 && cnt < (len - 1))
  • {
  • if(temp == 0)
  • {
  • OLED_ShowChar(x + (size / 2) * cnt, y, ' ', size);
  • continue;
  • }
  • else
  • {
  • show = 1;
  • }
  • }
  • OLED_ShowChar(x + (size / 2) * cnt, y, temp + '0', size);
  • }
  • }
  • //指定位置显示字符串
  • void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)
  • {
  • unsigned char cnt = 0;
  • while(chr[cnt] != '\0')
  • {
  • OLED_ShowChar(x, y, chr[cnt], size);
  • x += 8;
  • if(x > 120)
  • {
  • x = 0;
  • y += 2;
  • }
  • cnt++;
  • }
  • }
  • //显示汉字
  • void OLED_ShowCHinese(unsigned char x, unsigned char y, unsigned char no)
  • {
  • unsigned char cnt, addr = 0;
  • OLED_Set_Pos(x, y);
  • for(cnt = 0; cnt < 16; cnt++)
  • {
  • OLED_Write_Date(Hzk[2 * no][cnt]);
  • addr++;
  • }
  • OLED_Set_Pos(x, y + 1);
  • for(cnt = 0; cnt < 16; cnt++)
  • {
  • OLED_Write_Date(Hzk[2 * no + 1][cnt]);
  • addr++;
  • }
  • }
  • //显示图片
  • /*
  • [url=home.php?mod=space&uid=159083]@brief[/url] 显示图片
  • @param x0:起始列地址
  • y0:起始页地址
  • x1:终止列地址
  • y1:终止页地址
  • BMP[]:存放图片代码的数组
  • @retval 无
  • */
  • void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, const unsigned char BMP[])
  • {
  • unsigned int j = 0; //定义变量
  • unsigned char x, y; //定义变量
  • if(y1 % 8 == 0)
  • {
  • y = y1 / 8; //判断终止页是否为8的整数倍
  • }
  • else
  • {
  • y = y1 / 8 + 1;
  • }
  • for(y = y0; y < y1; y++) //从起始页开始,画到终止页
  • {
  • OLED_Set_Pos(x0, y); //在页的起始列开始画
  • for(x = x0; x < x1; x++) //画x1 - x0 列
  • {
  • OLED_Write_Date(BMP[j++]); //画图片的点
  • }
  • }
  • }
  • //显示动图
  • /*
  • @brief 显示动图
  • @param x0:起始列地址
  • y0:起始页地址
  • x1:终止列地址
  • y1:终止页地址
  • k: 帧个数
  • m: 单帧数组大小
  • BMP[][m]:存放动图代码的数组
  • @retval 无
  • */
  • void OLED_DrawGIF(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[][m])
  • {
  • unsigned int j = 0; //定义变量
  • unsigned char x, y, i; //定义变量
  • if(y1 % 8 == 0)
  • {
  • y = y1 / 8; //判断终止页是否为8的整数倍
  • }
  • else
  • {
  • y = y1 / 8 + 1;
  • }
  • for (i = 0; i < k; i++) //从第一帧开始画
  • {
  • j = 0;
  • for(y = y0; y < y1; y++) //从起始页开始,画到终止页
  • {
  • OLED_Set_Pos(x0, y); //在页的起始列开始画
  • for(x = x0; x < x1; x++) //画x1 - x0 列
  • {
  • OLED_Write_Date(GIF[i][j++]); //画图片的点
  • }
  • }
  • //Delay_Ms(80);
  • }
  • }
  • //OLED初始化
  • void OLED_Init(void)
  • {
  • I2C_Initial();
  • Delay_Ms(200);
  • OLED_Write_CMD(0xAE); //display off
  • OLED_Write_CMD(0x00); //set low column address
  • OLED_Write_CMD(0x10); //set high column address
  • OLED_Write_CMD(0x40); //set start line address
  • OLED_Write_CMD(0xB0); //set page address
  • OLED_Write_CMD(0x81); //contract control
  • OLED_Write_CMD(0xFF); //128
  • OLED_Write_CMD(0xA1); //set segment remap
  • OLED_Write_CMD(0xA6); //normal / reverse
  • OLED_Write_CMD(0xA8); //set multiplex ratio(1 to 64)
  • OLED_Write_CMD(0x3F); //1/32 duty
  • OLED_Write_CMD(0xC8); //Com scan direction
  • OLED_Write_CMD(0xD3); //set display offset
  • OLED_Write_CMD(0x00); //
  • OLED_Write_CMD(0xD5); //set osc division
  • OLED_Write_CMD(0x80); //
  • OLED_Write_CMD(0xD8); //set area color mode off
  • OLED_Write_CMD(0x05); //
  • OLED_Write_CMD(0xD9); //Set Pre-Charge Period
  • OLED_Write_CMD(0xF1); //
  • OLED_Write_CMD(0xDA); //set com pin configuartion
  • OLED_Write_CMD(0x12); //
  • OLED_Write_CMD(0xDB); //set Vcomh
  • OLED_Write_CMD(0x30); //
  • OLED_Write_CMD(0x8D); //set charge pump enable
  • OLED_Write_CMD(0x14); //
  • OLED_Write_CMD(0xAF); //turn on oled panel
  • }

 

运行测试

鎾斁鍣ㄥ姞杞藉け璐�: 鏈娴嬪埌Flash Player锛岃鍒�瀹夎
SW_I2C_OLED

测试代码
N32L43x_SW_I2C_OLED.zip (516.95 KB, 下载次数: 31)

 

 

最新回复

其实他的硬件I2C速度会提高很多,这样刷新率就会好很多,有机会试一下。   详情 回复 发表于 2022-8-14 22:07
点赞 关注
 
 

回复
举报

7794

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

谢谢分享,期待后续。

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

7211

帖子

11

TA的资源

版主

板凳
 

其实他的硬件I2C速度会提高很多,这样刷新率就会好很多,有机会试一下。

点评

硬件I2C速度确实快,有测试过https://bbs.eeworld.com.cn/thread-1212208-1-1.html#pid3164870  详情 回复 发表于 2022-8-14 23:20
 
 
 

回复

20

帖子

0

TA的资源

一粒金砂(中级)

4
 
lugl4313820 发表于 2022-8-14 22:07 其实他的硬件I2C速度会提高很多,这样刷新率就会好很多,有机会试一下。

硬件I2C速度确实快,有测试过https://bbs.eeworld.com.cn/thread-1212208-1-1.html#pid3164870

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条
【干货上新】电源解决方案和技术第二趴 | DigiKey 应用探索站
当月好物、电源技术资源、特色活动、DigiKey在线实用工具,干货多多~

查看 »

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网 14

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表