找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6378|回复: 11
收起左侧

关于OLED12864用IIC通讯显示受晶振频率影响解决方案 附单片机程序

  [复制链接]
ID:210139 发表于 2020-5-17 13:41 | 显示全部楼层 |阅读模式
关于OLED12864用IIC通讯显示受晶振频率影响解决方案
采用的单片机机STC8A8K64S4A12,同时STC15系列的也使用,测试过的
之前用软件模拟IIC驱动OLED12864时,当把单片机晶振频率调高时,OLED就不能正常显示了
后来找到了一些IIC程序,采用了一下,得以解决这一问题

问题还是IIC驱动程序没有写好,用卖家的例程也不行,还是的自己修改一下

此版程序,通过测试,就算把晶振调到27MHZ,也能正常显示

附件中有SPI通讯,软件模拟IIC,基于STC8A8K的硬件IIC,驱动OLED12864
不过STC8的硬件IIC通讯,还是受到晶振频率的影响,稍微修改了一下,比之前要好一点

贴出来的程序是单片机软件模拟IIC驱动OLED12864
  1. #include "oled.h"
  2. #include "Font.h"
  3. #include "intrins.h"

  4. void Delay_OLED(u16 t)
  5. {
  6.         u16 i,j;
  7.         for(j=0;j<t;j++)
  8.         {
  9.                 for(i=0;i<1;i++)        //根据晶振频率调整延时
  10.                 {
  11.                         _nop_();
  12.                         //_nop_();_nop_();
  13.                 }
  14.         }
  15. }

  16. /*********通过IIC写一字节数据******/
  17. void IIC_Write_Byte(u8 Byte)
  18. {
  19.         u8 i;
  20.         for(i=0;i<8;i++)
  21.         {
  22.                 OLED_SDA = Byte &0X80;        ////改变数据
  23.                 OLED_SCL = 1;        //传输数据
  24.                 Delay_OLED(1);
  25.                 OLED_SCL = 0;
  26.                 Byte<<=1;
  27.                 Delay_OLED(1);
  28.         }
  29. }

  30. /********起始信号*********/
  31. void IIC_Start()
  32. {
  33.         OLED_SDA = 1;
  34.         OLED_SCL = 1;
  35.         Delay_OLED(1);
  36.         OLED_SDA = 0;
  37.         Delay_OLED(1);
  38.         OLED_SCL = 0;
  39.         Delay_OLED(1);
  40. }

  41. /********停止信号*********/
  42. void IIC_Stop()
  43. {
  44.         OLED_SCL = 0;
  45.         OLED_SDA = 0;
  46.         Delay_OLED(1);
  47.         OLED_SDA = 1;
  48.         Delay_OLED(1);
  49.         OLED_SCL = 1;
  50.         Delay_OLED(1);
  51. }

  52. /*********ACK信号********/
  53. bit IIC_ACK()
  54. {
  55.         u8 errtime=255;
  56.         OLED_SCL = 0;
  57.         OLED_SDA = 1;        //释放总线
  58.         Delay_OLED(1);
  59.         OLED_SCL = 1;
  60.         Delay_OLED(1);
  61.         while(OLED_SDA)        //读取 SDA上的电平,变为低电平,即有从机回复ACK信号
  62.         {
  63.                 errtime--;
  64.                 if(!errtime)        //超时退出
  65.                 {
  66.                         OLED_SCL=0;
  67.                         return 0;
  68.                 }
  69.         }
  70.         OLED_SCL=0;
  71.         return 1;
  72. }

  73. /***********向SSD1306写一字节数据***********
  74. Dat:要写入的数据/命令
  75. Cmd:数据/命令标志 0,表示命令;1,表示数据;
  76. ********************************************/
  77. void Write_OLED_Byte(u8 Dat,u8 Cmd)
  78. {
  79.         IIC_Start();
  80.         IIC_Write_Byte(0X78);
  81.         IIC_ACK();
  82.         if(Cmd ==1)        //数据
  83.                 IIC_Write_Byte(0X40);
  84.         else        //命令
  85.                 IIC_Write_Byte(0X00);
  86.         IIC_ACK();
  87.         IIC_Write_Byte(Dat);
  88.         IIC_ACK();
  89.         IIC_Stop();
  90. }

  91. /**********IIC_IO口初始化***********/
  92. void IIC_InitIO()
  93. {
  94.         OLED_SDA = 1;
  95.         OLED_SCL = 1;
  96. }
  97. //---------IIC 通讯----------------------------------------------------------
  98. /*********设置显示坐标*********/
  99. void OLED_Set_PoS(u8 page,u8 column)
  100. {
  101.         Write_OLED_Byte(0XB0+page,OLED_CMD);
  102.         Write_OLED_Byte( ((column&0XF0)>>4)|0X10,OLED_CMD);
  103.         Write_OLED_Byte( column&0X0F,OLED_CMD);
  104. }

  105. ////开启OLED显示   
  106. //void OLED_Display_On(void)
  107. //{
  108. //        Write_OLED_Byte(0X8D,OLED_CMD);  //SET DCDC命令
  109. //        Write_OLED_Byte(0X14,OLED_CMD);  //DCDC ON
  110. //        Write_OLED_Byte(0XAF,OLED_CMD);  //DISPLAY ON
  111. //}


  112. ////关闭OLED显示     
  113. //void OLED_Display_Off(void)
  114. //{
  115. //        Write_OLED_Byte(0X8D,OLED_CMD);  //SET DCDC命令
  116. //        Write_OLED_Byte(0X10,OLED_CMD);  //DCDC OFF
  117. //        Write_OLED_Byte(0XAE,OLED_CMD);  //DISPLAY OFF
  118. //}

  119. /************操作整个屏幕************
  120. Input: Ins=0        清除整个屏幕
  121.            Ins=0XFF        全部显示整个屏幕
  122. *************************************/
  123. void OLED_Screen_All(u8 Ins)
  124. {
  125.         u8 page,seg;
  126.         for(page=0;page<8;page++)
  127.         {
  128.                 OLED_Set_PoS(page,0);
  129.                 for(seg=0;seg<128;seg++)
  130.                 {
  131.                         Write_OLED_Byte(Ins,OLED_DAT);
  132.                         Delay_OLED(1);
  133.                 }
  134.                 Delay_OLED(1);
  135.         }
  136. }


  137. /*********OLED初始化***********/
  138. void OLED_Init()
  139. {
  140.         Delay_OLED(2000);  
  141.         
  142.         Write_OLED_Byte(0XD5,OLED_CMD);        //晶振频率
  143.         Write_OLED_Byte(0X80,OLED_CMD);
  144.         Write_OLED_Byte(0XD9,OLED_CMD);        //
  145.         Write_OLED_Byte(0XF1,OLED_CMD);
  146.         Write_OLED_Byte(0XDB,OLED_CMD);        //
  147.         Write_OLED_Byte(0X40,OLED_CMD);
  148. //--地址设置
  149.         Write_OLED_Byte(0X20,OLED_CMD);        //设置寻址模式
  150.         Write_OLED_Byte(0X02,OLED_CMD);        //0X00--水平寻址;0X01--垂直寻址;0X02--页面寻址
  151. //--硬件设置
  152.         Write_OLED_Byte(0X40,OLED_CMD);        //起始行
  153.         Write_OLED_Byte(0XA8,OLED_CMD);        //设置显示比列;
  154.         Write_OLED_Byte(0X3F,OLED_CMD);        //1/64
  155.         Write_OLED_Byte(0XD3,OLED_CMD);        //设置补偿
  156.         Write_OLED_Byte(0X00,OLED_CMD);
  157.         //显示方向-引脚向上为正方向
  158.         Write_OLED_Byte(0XA1,OLED_CMD);        //段寻址,add0-->seg0
  159.         Write_OLED_Byte(0XC8,OLED_CMD);        //com扫描方向,com0-com63
  160.         //引脚在下为正方向
  161.         //Write_OLED_Byte(0XA0,OLED_CMD);        //段寻址,add127-->seg0
  162.         //Write_OLED_Byte(0XC0,OLED_CMD);        //com扫描方向,com63-com0
  163.         Write_OLED_Byte(0X8D,OLED_CMD);        //电荷泵设置
  164.         Write_OLED_Byte(0X14,OLED_CMD);        //REST=0X10,        0X14才能显示内容
  165. //--基本命令
  166.         Write_OLED_Byte(0X81,OLED_CMD);        //设置对比度
  167.         Write_OLED_Byte(0XCF,OLED_CMD);        //0X00-0XFF,与数值成正比        RES=0X7F
  168.         Write_OLED_Byte(0XA4,OLED_CMD);        //整体显示开启,可用来测试液晶是否显示正常
  169.                                                                         //0XA4跟随RAM内容,0XA5不跟随RAM的内容,一直是白屏全部显示。
  170.         Write_OLED_Byte(0XA6,OLED_CMD);        //设置正显0XA6,反显0XA7
  171.         Write_OLED_Byte(0XAF,OLED_CMD);        //0XAE显示关,0XAF显示开
  172.         Write_OLED_Byte(0X2E,OLED_CMD);        //关闭滚屏
  173. //---清屏
  174.         OLED_Screen_All(0);        
  175.         Delay_OLED(100);
  176.         OLED_Screen_All(0XFF);        
  177.         Delay_OLED(2000);
  178.         OLED_Screen_All(0);
  179.         Delay_OLED(100);
  180. }

  181. ///***********垂直+水平滚屏***************/
  182. //void Ver_Lev_Screen()
  183. //{
  184. ////--垂直+水平滚屏-------------------------------------------------------------------
  185. //        Write_OLED_Byte(0XA3,OLED_CMD);        //设置垂直滚动区域
  186. //        Write_OLED_Byte(0X10,OLED_CMD);        //固定不动的行数,从顶部开始数
  187. //        Write_OLED_Byte(0X20,OLED_CMD);        //垂直滚动的行数,从固定行的下一行开始数
  188. //        
  189. //        Write_OLED_Byte(0X29,OLED_CMD);        //垂直+右移///0X2A=垂直+左移
  190. //        Write_OLED_Byte(0X00,OLED_CMD);        //空字节
  191. //        Write_OLED_Byte(0X06,OLED_CMD);        //水平移动起始页
  192. //        Write_OLED_Byte(0X05,OLED_CMD);        //控制垂直+水平速率
  193. //        Write_OLED_Byte(0X07,OLED_CMD);        //水平移动结束页
  194. //        Write_OLED_Byte(0X02,OLED_CMD);        //垂直滚动偏移量,只控制垂直速率,=0只有水平移动
  195. //        Write_OLED_Byte(0X2F,OLED_CMD);        //启动滚屏
  196. ////---以上效果为,第一行为固定,第二三行垂直滚屏,第四行右移-------------------------
  197. //}

  198. /**************水平滚屏*****************/
  199. void Lev_Screen()
  200. {
  201.         Write_OLED_Byte(0X26,OLED_CMD);        //右移///0X27=左移
  202.         Write_OLED_Byte(0X00,OLED_CMD);        //空字节
  203.         Write_OLED_Byte(0X06,OLED_CMD);        //水平移动起始页
  204.         Write_OLED_Byte(0X05,OLED_CMD);        //控制垂直+水平速率
  205.         Write_OLED_Byte(0X07,OLED_CMD);        //水平移动结束页
  206.         Write_OLED_Byte(0X00,OLED_CMD);        //虚拟字节
  207.         Write_OLED_Byte(0XFF,OLED_CMD);        //虚拟字节
  208.         Write_OLED_Byte(0X2F,OLED_CMD);        //启动滚屏
  209. }

  210. ///**************清除N个8*16区域**********/
  211. //void Clear8X16(u8 page, u8 column ,u8 N)
  212. //{
  213. //        u8 i,j,k;
  214. //        for(k=0;k<N;k++)
  215. //        {
  216. //                for(i=0;i<2;i++)
  217. //                {
  218. //                        OLED_Set_PoS(page+i,column+k*8);
  219. //                        for(j=8;j>0;j--)
  220. //                                Write_OLED_Byte(0X00,OLED_DAT);
  221. //                }
  222. //        }
  223. //}

  224. /*****************在(16行/2页)*Wide列区域写数据****************
  225. Input:        
  226.         *Buff@点阵数据        Num@字符个数,Wide@字体宽度
  227.         Wide=8时,为写8*16的数据,字母,符号
  228.         Wide=16时,为写宋体2号汉字
  229. **************************************************************/
  230. void Write_16xWide(u8 page,u8 column ,u8 *Buff,u8 Num,u8 Wide)
  231. {
  232.         u8 i,j,k;
  233.         for(k=0;k<Num;k++)
  234.         {
  235.                 for(i=0;i<2;i++)
  236.                 {
  237.                         OLED_Set_PoS(page+i,column+k*8);
  238.                         for(j=Wide;j>0;j--)
  239.                         {
  240.                                 Write_OLED_Byte(*Buff,OLED_DAT);
  241.                                 Buff++;
  242.                         }
  243.                 }
  244.         }
  245. }

  246. /**************写宋体12号汉字 16*Wide ***********
  247. Input:        page@页                column@列        *CH@字符串
  248.         可以显示汉字,数字,字母,符号
  249.         page:0 2 4 6        column:0~15
  250. ************************************************/
  251. void Write_GB16X16(u8 page,u8 column ,u8 *CH)
  252. {
  253.         u8 a,k;
  254.         if( (*CH>=0X20) && (*CH<=0X7E) )        column=column;
  255.         else column*=8;
  256.         while( *CH != '\0')
  257.         {
  258.                 if( (*CH>=0X20) && (*CH<=0X7E) )        //数字,字母,符号        一行16个
  259.                 {
  260.                         k=*CH-0X20;
  261.                         for(a=0;a<2;a++)
  262.                         {
  263.                                 Write_16xWide(page,column*8, &ASCII_8X16[k][0],1,8);
  264.                         }
  265.                         CH+=1;
  266.                         column+=1;
  267.                 }        
  268.                 else        //汉字,一行 8个
  269.                 {
  270.                         for(a=0;a<FontNum;a++)
  271.                         {
  272.                                 if( (*CH == GB12Hanzi[a].Index[0]) && (*(CH+1)==GB12Hanzi[a].Index[1]) )
  273.                                         Write_16xWide(page,column,&GB12Hanzi[a].Msk[0],1,16);
  274.                         }
  275.                         CH+=2;
  276.                         column+=16;
  277.                 }
  278.         }
  279. }

  280. ///**************在8行/1页**6列的区域写字符***************
  281. //Input:        
  282. //        *CH@字符串        能写21个字符
  283. //        page:0~7        column:0~20
  284. //*******************************************************/
  285. //void Write_GB6X8(u8 page,u8 column ,u8 *CH)
  286. //{
  287. //        u8 j,k;
  288. //        while( *CH != '\0')
  289. //        {
  290. //                if( (*CH>=0X20) && (*CH<=0X7E) )        //数字,字母,符号        一行16个
  291. //                {
  292. //                        k=*CH-0X20;
  293. //                        OLED_Set_PoS(page,column*6);
  294. //                        for(j=0;j<6;j++)
  295. //                                Write_OLED_Byte(ASCII_6X8[k][j],OLED_DAT);
  296. //                        CH+=1;
  297. //                        column+=1;
  298. //                }
  299. //        }
  300. //}


复制代码

OLED12864

OLED12864
Z%%CJ_LKS2YH_G7S850E4S9.png

D-0.96OLED驱动程序.zip

141.08 KB, 下载次数: 219, 下载积分: 黑币 -5

IIC驱动OLED程序

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:446175 发表于 2021-4-14 22:22 | 显示全部楼层
用楼主的程序直接点亮屏了,哈哈,谢谢
回复

使用道具 举报

ID:94330 发表于 2021-4-19 08:55 | 显示全部楼层
感谢楼主分享,测试成功
回复

使用道具 举报

ID:963221 发表于 2021-9-2 16:06 | 显示全部楼层
还是受速度影响.比如27M时候容易卡死,得断电才可以不管怎么复位都不行
回复

使用道具 举报

ID:137590 发表于 2021-10-9 21:29 | 显示全部楼层
跟着芯片手册驱动IIC OLED屏幕
回复

使用道具 举报

ID:965325 发表于 2021-10-10 12:42 | 显示全部楼层
没啥特殊的,就是读写OLED的底层函数中加了延时。

回复

使用道具 举报

ID:90718 发表于 2021-11-22 00:59 | 显示全部楼层
有参考价值,非常感谢!
回复

使用道具 举报

ID:1013122 发表于 2022-3-28 21:01 | 显示全部楼层
谢谢。一直在找,非常棒的资源。
回复

使用道具 举报

ID:630338 发表于 2022-4-5 16:13 | 显示全部楼层
非常好的资源
回复

使用道具 举报

ID:471636 发表于 2022-4-8 17:09 | 显示全部楼层
对于软件模拟I2C通讯来说,读写的延时很重要,更换单片机或者更改频率,都会影响延时函数的实际运行值,可能会引起I2C通讯故障,楼主所谓的解决方案只是对某种单片机的某些频率段试验可行,并不能彻底解决问题。
5、6楼说的都对,其实就是按照手册调延时。
回复

使用道具 举报

ID:297903 发表于 2022-4-9 09:34 | 显示全部楼层
要用I/O摸似II2C,只能自己调整延时来适应不的CPU频率!!
回复

使用道具 举报

ID:744744 发表于 2022-5-22 22:42 来自手机 | 显示全部楼层
单片机运行时间不一样,时序要调整,这个是正常的。
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表