[PIC®/AVR®/dsPIC®产品] [PIC18F57Q43Curiosity Nano] 2.软件模拟I2C驱动OLED

[复制链接]
1426|6
 楼主| zhouminjie 发表于 2022-8-22 22:20 | 显示全部楼层 |阅读模式
本片介绍在PIC18F57Q43上实现软件模拟I2C驱动0.96OLED新建工程,打开MCC配置
配置系统时钟频率:时钟源选择HFINTOSC,时钟频率选择64MHz,分频设置选择不分频,得到系统时钟频率为64MHz

配置引脚:在Package View中设置RC3——SCL、RC4——SDA


添加DELAY Foundation services

点击Generate生成配置代码

新建.c(.h)文件,添加sw_i2c代码
  1. #include "sw_i2c.h"


  2. //设置SDA输入模式
  3. void SDA_IN(void)
  4. {
  5.     SDA_SetDigitalMode();
  6.     SDA_SetDigitalInput();
  7.     //SDA_SetPullup();
  8. }

  9. //设置SDA为输出模式
  10. void SDA_OUT(void)
  11. {
  12.     SDA_SetDigitalMode();
  13.     SDA_SetDigitalOutput();
  14.     SDA_SetPushPull();
  15.     //SDA_SetPullup();
  16. }

  17. //设置SCL电平
  18. void I2C_SCL(int n)
  19. {
  20.     if(n == 1)
  21.     {
  22.         SCL_SetHigh(); //设置SCL为高电平
  23.     }
  24.     else
  25.     {
  26.         SCL_SetLow(); //设置SCL为低电平
  27.     }
  28. }

  29. //设置SDA电平
  30. void I2C_SDA(int n)
  31. {
  32.     if(n == 1)
  33.     {
  34.         SDA_SetHigh(); //设置SDA为高电平
  35.     }
  36.     else
  37.     {
  38.         SDA_SetLow(); //设置SDA为低电平
  39.     }
  40. }

  41. //读取SDA电平
  42. unsigned char READ_SDA(void)
  43. {
  44.     return SDA_GetValue(); //读取SDA电平
  45. }

  46. //I2C初始化
  47. void SW_I2C_Initial(void)
  48. {
  49.     SCL_SetDigitalMode();
  50.     SDA_SetDigitalMode();
  51.     SCL_SetDigitalOutput();
  52.     SDA_SetDigitalOutput();
  53.     SCL_SetPushPull();
  54.     SDA_SetPushPull();
  55.     //SCL_SetPullup();
  56.     //SDA_SetPullup();
  57.     SCL_SetHigh();
  58.     SDA_SetHigh();
  59. }

  60. //I2C Start
  61. void I2C_Start(void)
  62. {
  63.     SDA_OUT();
  64.     I2C_SDA(1);
  65.     I2C_SCL(1);
  66.     //DELAY_microseconds(4);
  67.     I2C_SDA(0); //START:when CLK is high,DATA change form high to low
  68.     //DELAY_microseconds(4);
  69.     I2C_SCL(0); //钳住I2C总线,准备发送或接收数据
  70. }

  71. //I2C Stop
  72. void I2C_Stop(void)
  73. {
  74.     SDA_OUT();
  75.     I2C_SCL(0);
  76.     I2C_SDA(0); //STOP:when CLK is high DATA change form low to high
  77.     //DELAY_microseconds(4);
  78.     I2C_SCL(1);
  79.     I2C_SDA(1); //发送I2C总线结束信号
  80.     //DELAY_microseconds(4);
  81. }

  82. //I2C_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
  83. unsigned char I2C_Wait_Ack(void) //IIC_Wait_ack,返回wait失败或是成功
  84. {
  85.     unsigned char ucErrTime = 0;

  86.     SDA_IN();
  87.     I2C_SDA(1);
  88.     //DELAY_microseconds(1);
  89.     I2C_SCL(1);
  90.     //DELAY_microseconds(1);
  91.     while(READ_SDA())
  92.     {
  93.         ucErrTime++;

  94.         if(ucErrTime > 250)
  95.         {
  96.             I2C_Stop();
  97.             return HAL_ERROR;
  98.         }
  99.     }
  100.     I2C_SCL(0);
  101.     return HAL_OK;
  102. }

  103. //产生ACK应答
  104. void I2C_Ack(void)
  105. {
  106.     I2C_SCL(0);
  107.     SDA_OUT();
  108.     I2C_SDA(0);
  109.     //DELAY_microseconds(2);
  110.     I2C_SCL(1);
  111.     //DELAY_microseconds(2);
  112.     I2C_SCL(0);
  113. }

  114. //产生NACK应答
  115. void I2C_NAck(void)
  116. {
  117.     I2C_SCL(0);
  118.     SDA_OUT();
  119.     I2C_SDA(1);
  120.     //DELAY_microseconds(2);
  121.     I2C_SCL(1);
  122.     //DELAY_microseconds(2);
  123.     I2C_SCL(0);
  124. }

  125. //I2C_Send_Byte,入口参数为要发送的字节
  126. void I2C_Send_Byte(unsigned char txd)
  127. {
  128.     unsigned char cnt = 0;

  129.     SDA_OUT();
  130.     I2C_SCL(0);
  131.     for(cnt = 0; cnt < 8; cnt++)
  132.     {
  133.         I2C_SDA((txd & 0x80) >> 7);
  134.         txd <<= 1;
  135.         //DELAY_microseconds(2);
  136.         I2C_SCL(1);
  137.         //DELAY_microseconds(2);
  138.         I2C_SCL(0);
  139.         //DELAY_microseconds(2);
  140.     }
  141. }

  142. //I2C_Read_Byte,入口参数为是否要发送ACK信号
  143. unsigned char I2C_Read_Byte(unsigned char ack)
  144. {
  145.     unsigned char cnt, rec = 0;

  146.     SDA_IN();
  147.     for(cnt = 0; cnt < 8; cnt++)
  148.     {
  149.         I2C_SCL(0);
  150.         //DELAY_microseconds(2);
  151.         I2C_SCL(1);
  152.         rec <<= 1;

  153.         if(READ_SDA())
  154.         {
  155.             rec++;
  156.         }
  157.         //DELAY_microseconds(1);
  158.     }
  159.     if(!ack)
  160.     {
  161.         I2C_NAck();
  162.     }
  163.     else
  164.     {
  165.         I2C_Ack();
  166.     }
  167.     return rec;
  168. }


新建.c(.h)文件,添加oled代码
  1. #include"oled.h"

  2. //向设备写控制命令
  3. static void OLED_Write_CMD(unsigned char cmd)
  4. {
  5.     I2C_Start();
  6.     I2C_Send_Byte(0x78);
  7.     I2C_Wait_Ack();
  8.     I2C_Send_Byte(0x00);
  9.     I2C_Wait_Ack();
  10.     I2C_Send_Byte(cmd);
  11.     I2C_Wait_Ack();
  12.     I2C_Stop();
  13. }

  14. //向设备写数据
  15. static void OLED_Write_Date(unsigned char date)
  16. {
  17.     I2C_Start();
  18.     I2C_Send_Byte(0x78);
  19.     I2C_Wait_Ack();
  20.     I2C_Send_Byte(0x40);
  21.     I2C_Wait_Ack();
  22.     I2C_Send_Byte(date);
  23.     I2C_Wait_Ack();
  24.     I2C_Stop();
  25. }

  26. //坐标设置
  27. static void OLED_Set_Pos(unsigned char x, unsigned char y)
  28. {
  29.     OLED_Write_CMD(0xB0 + y);
  30.     OLED_Write_CMD(((x & 0xF0) >> 4) | 0x10);
  31.     OLED_Write_CMD(x & 0x0F);
  32. }

  33. //开启OLED显示
  34. static void OLED_Display_On(void)
  35. {
  36.     OLED_Write_CMD(0x8D); //SET DCDC命令
  37.     OLED_Write_CMD(0x14); //DCDC ON
  38.     OLED_Write_CMD(0xAF); //DISPLAY ON
  39. }

  40. //关闭OLED显示
  41. static void OLED_Display_Off(void)
  42. {
  43.     OLED_Write_CMD(0x8D); //SET DCDC命令
  44.     OLED_Write_CMD(0x10); //DCDC OFF
  45.     OLED_Write_CMD(0xAE); //DISPLAY OFF
  46. }

  47. //OLED清屏
  48. void OLED_Clear(void)
  49. {
  50.     unsigned char cnt, count;

  51.     for(cnt = 0; cnt < 8; cnt++)
  52.     {
  53.         OLED_Write_CMD(0xB0 + cnt);
  54.         OLED_Write_CMD(0x00);
  55.         OLED_Write_CMD(0x10);

  56.         for(count = 0; count < 128; count++)
  57.         {
  58.             OLED_Write_Date(0x00);
  59.         }
  60.     }
  61. }

  62. //OLED清行
  63. void OLED_Clear_Row(unsigned char n)
  64. {
  65.     unsigned char count;

  66.     OLED_Write_CMD(0xB0 + n);
  67.     OLED_Write_CMD(0x00);
  68.     OLED_Write_CMD(0x10);

  69.     for(count = 0; count < 128; count++)
  70.     {
  71.         OLED_Write_Date(0x00);
  72.     }
  73. }

  74. //OLED填满屏幕
  75. void OLED_Fill(void)
  76. {
  77.     unsigned char cnt, count;

  78.     for(cnt = 0; cnt < 8; cnt++)
  79.     {
  80.         OLED_Write_CMD(0xB0 + cnt); //设置页地址(0~7)
  81.         OLED_Write_CMD(0x00); //设置显示位置—列低地址
  82.         OLED_Write_CMD(0x10); //设置显示位置—列高地址

  83.         for(count = 0; count < 128; count++)
  84.         {
  85.             OLED_Write_Date(0x01);
  86.         }
  87.     }
  88. }

  89. //指定位置显示一个字符
  90. //x:0~127
  91. //y:0~63
  92. //chr:字符
  93. //mode:0,反白显示;1,正常显示
  94. //size:选择字体 16/12
  95. void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
  96. {
  97.     unsigned char offset = 0, cnt = 0;

  98.     offset = chr - ' '; //计算偏移量

  99.     if(x > 128 - 1)
  100.     {
  101.         x = 0;
  102.         y = y + 2;
  103.     }

  104.     if(size == 16)
  105.     {
  106.         OLED_Set_Pos(x, y);

  107.         for(cnt = 0; cnt < 8; cnt++)
  108.         {
  109.             OLED_Write_Date(F8x16[offset * 16 + cnt]);
  110.         }

  111.         OLED_Set_Pos(x, y + 1);

  112.         for(cnt = 0; cnt < 8; cnt++)
  113.         {
  114.             OLED_Write_Date(F8x16[offset * 16 + cnt + 8]);
  115.         }
  116.     }
  117.     else
  118.     {
  119.         OLED_Set_Pos(x, y);

  120.         for(cnt = 0; cnt < 6; cnt++)
  121.         {
  122.             OLED_Write_Date(F6x8[offset][cnt]);
  123.         }
  124.     }
  125. }

  126. unsigned int oled_pow(unsigned char m, unsigned char n)
  127. {
  128.     unsigned int result = 1;

  129.     while(n--)
  130.     {
  131.         result *= m;
  132.     }

  133.     return result;
  134. }

  135. //指定位置显示一个数字
  136. //x,y:起点坐标
  137. //num:数值(0~4294967295)
  138. //len:数字的位数
  139. //size:字体大小
  140. //mode:模式        0,填充模式;1,叠加模式
  141. void OLED_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
  142. {
  143.     unsigned char cnt, temp;
  144.     unsigned char show = 0;

  145.     for(cnt = 0; cnt < len; cnt++)
  146.     {
  147.         temp = (num / oled_pow(10, len - cnt - 1)) % 10;

  148.         if(show == 0 && cnt < (len - 1))
  149.         {
  150.             if(temp == 0)
  151.             {
  152.                 OLED_ShowChar(x + (size / 2) * cnt, y, ' ', size);
  153.                 continue;
  154.             }
  155.             else
  156.             {
  157.                 show = 1;
  158.             }
  159.         }

  160.         OLED_ShowChar(x + (size / 2) * cnt, y, temp + '0', size);
  161.     }
  162. }

  163. //指定位置显示字符串
  164. void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)
  165. {
  166.     unsigned char cnt = 0;

  167.     while(chr[cnt] != '\0')
  168.     {
  169.         OLED_ShowChar(x, y, chr[cnt], size);
  170.         x += 8;

  171.         if(x > 120)
  172.         {
  173.             x = 0;
  174.             y += 2;
  175.         }

  176.         cnt++;
  177.     }
  178. }

  179. //显示汉字
  180. void OLED_ShowCHinese(unsigned char x, unsigned char y, unsigned char no)
  181. {
  182.     unsigned char cnt, addr = 0;

  183.     OLED_Set_Pos(x, y);

  184.     for(cnt = 0; cnt < 16; cnt++)
  185.     {
  186.         OLED_Write_Date(Hzk[2 * no][cnt]);
  187.         addr++;
  188.     }

  189.     OLED_Set_Pos(x, y + 1);

  190.     for(cnt = 0; cnt < 16; cnt++)
  191.     {
  192.         OLED_Write_Date(Hzk[2 * no + 1][cnt]);
  193.         addr++;
  194.     }
  195. }

  196. //显示图片
  197. /*
  198.         @brief                        显示图片
  199.         @param                        x0:起始列地址
  200.                                         y0:起始页地址
  201.                                         x1:终止列地址
  202.                                         y1:终止页地址
  203.                                         BMP[]:存放图片代码的数组
  204.         @retval                        无
  205. */
  206. void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, const unsigned char BMP[])
  207. {
  208.     unsigned int j = 0; //定义变量
  209.     unsigned char x, y; //定义变量

  210.     if(y1 % 8 == 0)
  211.     {
  212.         y = y1 / 8; //判断终止页是否为8的整数倍
  213.     }
  214.     else
  215.     {
  216.         y = y1 / 8 + 1;
  217.     }

  218.     for(y = y0; y < y1; y++) //从起始页开始,画到终止页
  219.     {
  220.         OLED_Set_Pos(x0, y); //在页的起始列开始画

  221.         for(x = x0; x < x1; x++) //画x1 - x0 列
  222.         {
  223.             OLED_Write_Date(BMP[j++]); //画图片的点
  224.         }
  225.     }
  226. }

  227. //显示动图
  228. /*
  229.         @brief                        显示动图
  230.         @param                        x0:起始列地址
  231.                                 y0:起始页地址
  232.                                 x1:终止列地址
  233.                                 y1:终止页地址
  234.                                 k: 帧个数
  235.                                 m: 单帧数组大小
  236.                                 BMP[][m]:存放动图代码的数组
  237.         @retval                        无
  238. */
  239. void OLED_DrawGIF(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char k, int m, const unsigned char GIF[29][1024])
  240. {
  241.     unsigned int j = 0; //定义变量
  242.     unsigned char x, y, i; //定义变量

  243.     if(y1 % 8 == 0)
  244.     {
  245.         y = y1 / 8; //判断终止页是否为8的整数倍
  246.     }
  247.     else
  248.     {
  249.         y = y1 / 8 + 1;
  250.     }

  251.     for (i = 0; i < k; i++) //从第一帧开始画
  252.     {
  253.         j = 0;

  254.         for(y = y0; y < y1; y++) //从起始页开始,画到终止页
  255.         {
  256.             OLED_Set_Pos(x0, y); //在页的起始列开始画

  257.             for(x = x0; x < x1; x++) //画x1 - x0 列
  258.             {
  259.                 OLED_Write_Date(GIF[i][j++]); //画图片的点//GIF[i][j++]
  260.             }
  261.         }
  262.         //Delay_Ms(80);
  263.     }
  264. }

  265. //OLED初始化
  266. void OLED_Init(void)
  267. {
  268.     SW_I2C_Initial();
  269.     DELAY_milliseconds(800);
  270.     OLED_Write_CMD(0xAE); //display off
  271.     OLED_Write_CMD(0x00); //set low column address
  272.     OLED_Write_CMD(0x10); //set high column address
  273.     OLED_Write_CMD(0x40); //set start line address
  274.     OLED_Write_CMD(0xB0); //set page address
  275.     OLED_Write_CMD(0x81); //contract control
  276.     OLED_Write_CMD(0xFF); //128
  277.     OLED_Write_CMD(0xA1); //set segment remap
  278.     OLED_Write_CMD(0xA6); //normal / reverse
  279.     OLED_Write_CMD(0xA8); //set multiplex ratio(1 to 64)
  280.     OLED_Write_CMD(0x3F); //1/32 duty
  281.     OLED_Write_CMD(0xC8); //Com scan direction
  282.     OLED_Write_CMD(0xD3); //set display offset
  283.     OLED_Write_CMD(0x00); //
  284.     OLED_Write_CMD(0xD5); //set osc division
  285.     OLED_Write_CMD(0x80); //
  286.     OLED_Write_CMD(0xD8); //set area color mode off
  287.     OLED_Write_CMD(0x05); //
  288.     OLED_Write_CMD(0xD9); //Set Pre-Charge Period
  289.     OLED_Write_CMD(0xF1); //
  290.     OLED_Write_CMD(0xDA); //set com pin configuartion
  291.     OLED_Write_CMD(0x12); //
  292.     OLED_Write_CMD(0xDB); //set Vcomh
  293.     OLED_Write_CMD(0x30); //
  294.     OLED_Write_CMD(0x8D); //set charge pump enable
  295.     OLED_Write_CMD(0x14); //
  296.     OLED_Write_CMD(0xAF); //turn on oled panel
  297. }
编译调试运行
测试效果如下

测试工程

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
pzsh 发表于 2022-9-5 14:42 | 显示全部楼层
显示效果很炫
稳稳の幸福 发表于 2023-6-25 21:16 | 显示全部楼层
硬件实现I2C更方便啊。
zhuomuniao110 发表于 2023-10-21 12:13 | 显示全部楼层
软件搞定的,硬件的一直搞不懂。。。
598330983 发表于 2023-10-21 19:55 | 显示全部楼层
https://bbs.21ic.com/icview-3334816-1-1.html
硬件的也搞定了。
dongnanxibei 发表于 2023-10-23 17:27 | 显示全部楼层
不如用硬件做的好啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

140

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部