[活动专区] 【AT-START-F425测评】IO模拟IIC驱动OLED

[复制链接]
 楼主| zhouminjie 发表于 2022-5-23 23:36 | 显示全部楼层 |阅读模式
本帖最后由 zhouminjie 于 2022-5-23 23:40 编辑

1、关于IIC协议的分析介绍,可参考https://bbs.21ic.com/icview-2969754-1-1.html,此处不再赘述。
2、本次使用AT32F425 I/O模拟IIC来实现驱动0.96OLED液晶屏,接线方式如下:PF4连接OLED屏SDA引脚、PF5连接OLED屏SCL引脚。
3、软件设计在之前工程上增加hal_i2c.c、hal_i2c.h、hal_0.96oled.c、hal_0.96oled.h等.c.h文件,具体如下:
hal_i2c.c代码:
  1. #include "hal_i2c.h"
  2. #include "delay.h"

  3. //设置SDA输入模式
  4. void SDA_IN(void)
  5. {
  6.         gpio_init_type gpio_init_struct;
  7.         
  8.         //根据GPIO组初始化GPIO时钟
  9.         crm_periph_clock_enable(SDA_GPIO_CRM_CLK, TRUE); //使能GPIOF时钟
  10.         gpio_init_struct.gpio_pins = SDA_PIN;
  11.         gpio_init_struct.gpio_mode = GPIO_MODE_INPUT;
  12.         gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  13.         gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  14.         gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  15.         gpio_init(SDA_GPIO, &gpio_init_struct);
  16. }

  17. //设置SDA为输出模式
  18. void SDA_OUT(void)
  19. {
  20.         gpio_init_type gpio_init_struct;
  21.         
  22.         //根据GPIO组初始化GPIO时钟
  23.         crm_periph_clock_enable(SDA_GPIO_CRM_CLK, TRUE); //使能GPIOF时钟
  24.         gpio_init_struct.gpio_pins = SDA_PIN;
  25.         gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  26.         gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  27.         gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  28.         gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  29.         gpio_init(SDA_GPIO, &gpio_init_struct);
  30. }

  31. //设置SCL电平
  32. void I2C_SCL(int n)
  33. {
  34.         if(n == 1)
  35.         {
  36.                 gpio_bits_write(SCL_GPIO, SCL_PIN, TRUE); //设置SCL为高电平
  37.         }
  38.         else
  39.         {
  40.                 gpio_bits_write(SCL_GPIO, SCL_PIN, FALSE); //设置SCL为低电平
  41.         }
  42. }

  43. //设置SDA电平
  44. void I2C_SDA(int n)
  45. {
  46.         if(n == 1)
  47.         {
  48.                 gpio_bits_write(SDA_GPIO, SDA_PIN, TRUE); //设置SDA为高电平
  49.         }
  50.         else
  51.         {
  52.                 gpio_bits_write(SDA_GPIO, SDA_PIN, FALSE); //设置SDA为低电平
  53.         }
  54. }

  55. //读取SDA电平
  56. unsigned char READ_SDA(void)
  57. {
  58.         return gpio_input_data_bit_read(SDA_GPIO, SDA_PIN); //读取SDA电平
  59. }

  60. //I2C初始化
  61. void I2C_Init(void)
  62. {
  63.         gpio_init_type gpio_init_struct;
  64.         
  65.         //根据GPIO组初始化GPIO时钟
  66.         crm_periph_clock_enable(SCL_GPIO_CRM_CLK, TRUE); //使能GPIOF时钟
  67.         crm_periph_clock_enable(SDA_GPIO_CRM_CLK, TRUE); //使能GPIOF时钟
  68.         
  69.         //GPIO_SCL初始化设置
  70.         gpio_init_struct.gpio_pins = SCL_PIN;
  71.         gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT; //gpio output mode
  72.         gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL; //output push-pull
  73.         gpio_init_struct.gpio_pull = GPIO_PULL_UP; //pull-up
  74.         gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER; //stronger sourcing/sinking strength
  75.         gpio_init(SCL_GPIO, &gpio_init_struct);
  76.         //GPIO_SDA初始化设置
  77.         gpio_init_struct.gpio_pins = SDA_PIN;
  78.         gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
  79.         gpio_init_struct.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  80.         gpio_init_struct.gpio_pull = GPIO_PULL_UP;
  81.         gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  82.         gpio_init(SDA_GPIO, &gpio_init_struct);
  83.         //SCL、SDA的初始化均为高电平
  84.         I2C_SCL(1);
  85.         I2C_SDA(1);
  86. }

  87. //I2C Start
  88. void I2C_Start(void)
  89. {
  90.         SDA_OUT();
  91.         I2C_SDA(1);
  92.         I2C_SCL(1);
  93.         Delay_Us(4);
  94.         I2C_SDA(0); //START:when CLK is high,DATA change form high to low
  95.         Delay_Us(4);
  96.         I2C_SCL(0); //钳住I2C总线,准备发送或接收数据
  97. }

  98. //I2C Stop
  99. void I2C_Stop(void)
  100. {
  101.         SDA_OUT();
  102.         I2C_SCL(0);
  103.         I2C_SDA(0); //STOP:when CLK is high DATA change form low to high
  104.         Delay_Us(4);
  105.         I2C_SCL(1);
  106.         I2C_SDA(1); //发送I2C总线结束信号
  107.         Delay_Us(4);
  108. }

  109. //I2C_Wait_ack 返回HAL_OK表示wait成功,返回HAL_ERROR表示wait失败
  110. unsigned char I2C_Wait_Ack(void) //IIC_Wait_ack,返回wait失败或是成功
  111. {
  112.         unsigned char ucErrTime = 0;
  113.         
  114.         SDA_IN();
  115.         I2C_SDA(1);
  116.         Delay_Us(1);
  117.         I2C_SCL(1);
  118.         Delay_Us(1);
  119.         while(READ_SDA())
  120.         {
  121.                 ucErrTime++;
  122.                 if(ucErrTime>250)
  123.                 {
  124.                         I2C_Stop();
  125.                         return HAL_ERROR;
  126.                 }
  127.         }
  128.         I2C_SCL(0);
  129.         return HAL_OK;
  130. }

  131. //产生ACK应答
  132. void I2C_Ack(void)      
  133. {
  134.         I2C_SCL(0);
  135.         SDA_OUT();
  136.         I2C_SDA(0);
  137.         Delay_Us(2);
  138.   I2C_SCL(1);
  139.   Delay_Us(2);
  140.   I2C_SCL(0);
  141. }

  142. //产生NACK应答
  143. void I2C_NAck(void)      
  144. {
  145.         I2C_SCL(0);
  146.         SDA_OUT();
  147.         I2C_SDA(1);
  148.         Delay_Us(2);
  149.   I2C_SCL(1);
  150.   Delay_Us(2);
  151.   I2C_SCL(0);
  152. }

  153. //I2C_Send_Byte,入口参数为要发送的字节
  154. void I2C_Send_Byte(unsigned char txd)     
  155. {
  156.         unsigned char cnt = 0;
  157.         
  158.         SDA_OUT();
  159.         I2C_SCL(0);
  160.         for(cnt = 0; cnt < 8; cnt++)
  161.         {
  162.                 I2C_SDA((txd & 0x80) >> 7);
  163.                 txd <<= 1;
  164.                 Delay_Us(2);
  165.                 I2C_SCL(1);
  166.                 Delay_Us(2);
  167.                 I2C_SCL(0);
  168.                 Delay_Us(2);
  169.         }  
  170. }

  171. //I2C_Read_Byte,入口参数为是否要发送ACK信号
  172. unsigned char I2C_Read_Byte(unsigned char ack)     
  173. {
  174.         unsigned char cnt, rec = 0;
  175.         
  176.         SDA_IN();
  177.         for(cnt = 0; cnt < 8; cnt++)
  178.         {
  179.                 I2C_SCL(0);
  180.                 Delay_Us(2);
  181.                 I2C_SCL(1);
  182.                 rec <<= 1;
  183.                 if(READ_SDA())
  184.                 {
  185.                         rec++;
  186.                 }
  187.                 Delay_Us(1);
  188.         }
  189.         if(!ack)
  190.         {
  191.                 I2C_NAck();
  192.         }
  193.         else
  194.         {
  195.                 I2C_Ack();
  196.         }
  197.         return rec;
  198. }
hal_0.96oled.c代码:
  1. #include "hal_0.96oled.h"
  2. #include "oledfont.h"
  3. #include "delay.h"

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

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

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

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

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

  49. //OLED清屏
  50. void OLED_Clear(void)
  51. {
  52.         unsigned char cnt, count;
  53.         
  54.         for(cnt = 0; cnt < 8; cnt++)
  55.         {
  56.                 OLED_Write_CMD(0xB0 + cnt);
  57.                 OLED_Write_CMD(0x00);
  58.                 OLED_Write_CMD(0x10);
  59.                 for(count = 0; count < 128; count++)
  60.                 {
  61.                         OLED_Write_Date(0x00);
  62.                 }
  63.         }
  64. }

  65. //OLED清行
  66. void OLED_Clear_Row(unsigned char n)
  67. {
  68.         unsigned char count;
  69.         
  70.         OLED_Write_CMD(0xB0 + n);
  71.         OLED_Write_CMD(0x00);
  72.         OLED_Write_CMD(0x10);
  73.         for(count = 0; count < 128; count++)
  74.         {
  75.                 OLED_Write_Date(0x00);
  76.         }
  77. }

  78. //OLED填满屏幕
  79. void OLED_Fill(void)
  80. {
  81.         unsigned char cnt, count;
  82.         
  83.         for(cnt = 0; cnt < 8; cnt++)
  84.         {
  85.                 OLED_Write_CMD(0xB0 + cnt); //设置页地址(0~7)
  86.                 OLED_Write_CMD(0x00); //设置显示位置—列低地址
  87.                 OLED_Write_CMD(0x10); //设置显示位置—列高地址
  88.                 for(count = 0; count < 128; count++)
  89.                 {
  90.                         OLED_Write_Date(0x01);
  91.                 }
  92.         }
  93. }

  94. //指定位置显示一个字符
  95. //x:0~127
  96. //y:0~63
  97. //chr:字符
  98. //mode:0,反白显示;1,正常显示                                 
  99. //size:选择字体 16/12
  100. void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char chr, unsigned char size)
  101. {
  102.         unsigned char offset = 0, cnt = 0;
  103.         
  104.         offset = chr - ' '; //计算偏移量
  105.         if(x > 128 - 1)
  106.         {
  107.                 x = 0;
  108.                 y = y + 2;
  109.         }
  110.         if(size == 16)
  111.         {
  112.                 OLED_Set_Pos(x, y);
  113.                 for(cnt = 0; cnt < 8; cnt++)
  114.                 {
  115.                         OLED_Write_Date(F8x16[offset * 16 + cnt]);
  116.                 }
  117.                 OLED_Set_Pos(x, y + 1);
  118.                 for(cnt = 0; cnt < 8; cnt++)
  119.                 {
  120.                         OLED_Write_Date(F8x16[offset * 16 + cnt + 8]);
  121.                 }
  122.         }
  123.         else
  124.         {
  125.                 OLED_Set_Pos(x, y);
  126.                 for(cnt = 0; cnt < 6; cnt++)
  127.                 {
  128.                         OLED_Write_Date(F6x8[offset][cnt]);
  129.                 }
  130.         }
  131. }

  132. unsigned int oled_pow(unsigned char m, unsigned char n)
  133. {
  134.         unsigned int result = 1;
  135.         while(n--)
  136.         {
  137.                 result *= m;   
  138.         }
  139.         return result;
  140. }

  141. //指定位置显示一个数字
  142. //x,y:起点坐标         
  143. //num:数值(0~4294967295)
  144. //len:数字的位数
  145. //size:字体大小
  146. //mode:模式        0,填充模式;1,叠加模式
  147. void OLED_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len, unsigned char size)
  148. {
  149.         unsigned char cnt, temp;
  150.         unsigned char show = 0;
  151.         
  152.         for(cnt = 0; cnt < len; cnt++)
  153.         {
  154.                 temp = (num / oled_pow(10, len - cnt - 1)) % 10;
  155.                 if(show == 0 && cnt < (len - 1))
  156.                 {
  157.                         if(temp == 0)
  158.                         {
  159.                                 OLED_ShowChar(x + (size / 2) * cnt, y, ' ', size);
  160.                                 continue;
  161.                         }
  162.                         else
  163.                         {
  164.                                 show = 1;
  165.                         }
  166.                 }
  167.                 OLED_ShowChar(x + (size / 2) * cnt, y, temp + '0', size);
  168.         }
  169. }        

  170. //指定位置显示字符串
  171. void OLED_ShowString(unsigned char x, unsigned char y, unsigned char *chr, unsigned char size)        
  172. {
  173.         unsigned char cnt = 0;
  174.         
  175.         while(chr[cnt] != '\0')
  176.         {
  177.                 OLED_ShowChar(x, y, chr[cnt], size);
  178.                 x += 8;
  179.                 if(x > 120)
  180.                 {
  181.                         x = 0;
  182.                         y += 2;
  183.                 }
  184.                 cnt++;
  185.         }
  186. }

  187. //显示汉字
  188. void OLED_ShowCHinese(unsigned char x, unsigned char y, unsigned char no)
  189. {
  190.         unsigned char cnt, addr = 0;
  191.         
  192.         OLED_Set_Pos(x, y);
  193.         for(cnt = 0; cnt < 16; cnt++)
  194.         {
  195.                 OLED_Write_Date(Hzk[2 * no][cnt]);
  196.                 addr++;
  197.         }
  198.         OLED_Set_Pos(x, y + 1);
  199.         for(cnt = 0; cnt < 16; cnt++)
  200.         {
  201.                 OLED_Write_Date(Hzk[2 * no + 1][cnt]);
  202.                 addr++;
  203.         }
  204. }

  205. //OLED初始化
  206. void OLED_Init(void)
  207. {
  208.         I2C_Init();
  209.         Delay_Ms(200);
  210.         OLED_Write_CMD(0xAE); //display off
  211.         OLED_Write_CMD(0x00); //set low column address
  212.         OLED_Write_CMD(0x10); //set high column address
  213.         OLED_Write_CMD(0x40); //set start line address  
  214.         OLED_Write_CMD(0xB0); //set page address
  215.         OLED_Write_CMD(0x81); //contract control
  216.         OLED_Write_CMD(0xFF); //128
  217.         OLED_Write_CMD(0xA1); //set segment remap
  218.         OLED_Write_CMD(0xA6); //normal / reverse
  219.         OLED_Write_CMD(0xA8); //set multiplex ratio(1 to 64)
  220.         OLED_Write_CMD(0x3F); //1/32 duty
  221.         OLED_Write_CMD(0xC8); //Com scan direction
  222.         OLED_Write_CMD(0xD3); //set display offset
  223.         OLED_Write_CMD(0x00); //
  224.         OLED_Write_CMD(0xD5); //set osc division
  225.         OLED_Write_CMD(0x80); //
  226.         OLED_Write_CMD(0xD8); //set area color mode off
  227.         OLED_Write_CMD(0x05); //
  228.         OLED_Write_CMD(0xD9); //Set Pre-Charge Period
  229.         OLED_Write_CMD(0xF1); //
  230.         OLED_Write_CMD(0xDA); //set com pin configuartion
  231.         OLED_Write_CMD(0x12); //
  232.         OLED_Write_CMD(0xDB); //set Vcomh
  233.         OLED_Write_CMD(0x30); //
  234.         OLED_Write_CMD(0x8D); //set charge pump enable
  235.         OLED_Write_CMD(0x14); //
  236.         OLED_Write_CMD(0xAF); //turn on oled panel
  237. }
模拟i2c时,delay.c代码:
  1. #include "at32f425.h"
  2. #include "hal_timer.h"

  3. static unsigned char fac_us = 0; //us延时倍乘数
  4. static unsigned short fac_ms = 0; //ms延时倍乘数

  5. //初始化延迟函数
  6. //SYSTICK的时钟固定为HCLK时钟的1/8
  7. //SYSCLK:系统时钟
  8. void Delay_Init(void)
  9. {
  10.         crm_clocks_freq_get(&crm_clocks_freq_struct);
  11.         fac_us = crm_clocks_freq_struct.sclk_freq / (8000000U); //systick为HCLK/8,fac_us为systick的1/8/1000000,即每1us systick的VAL减的数目
  12.         fac_ms = (unsigned short)fac_us * (1000U); //每1ms需要的systick时钟数,即每1ms systick的VAL减的数目
  13. }

  14. //延时nus
  15. //nus为要延时的us数
  16. void Delay_Us(unsigned int nus)
  17. {
  18.         unsigned int temp;                     

  19.         SysTick->LOAD = (unsigned int)(nus * fac_us); //时间加载                           
  20.         SysTick->VAL = 0x00; //清空计数器
  21.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始倒数         
  22.         do
  23.         {
  24.                 temp = SysTick->CTRL;
  25.         }
  26.         while((temp & 0x01) && !(temp & (1 << 16))); //等待时间到达   
  27.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
  28.         SysTick->VAL = 0x00; //清空计数器
  29. }

  30. //延时nms
  31. //注意nms的范围
  32. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  33. //nms<=0xffffff*8*1000/SYSCLK
  34. //SYSCLK单位为Hz,nms单位为ms
  35. //对96M条件下,nms<=1398
  36. void Delay_Ms(unsigned short nms)
  37. {
  38.         unsigned int temp;                  

  39.         SysTick->LOAD = (unsigned int)(nms * fac_ms); //时间加载(SysTick->LOAD为24bit)
  40.         SysTick->VAL = 0x00; //清空计数器
  41.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开始倒数
  42.         do
  43.         {
  44.                 temp = SysTick->CTRL;
  45.         }
  46.         while((temp & 0x01) && !(temp & (1 << 16))); //等待时间到达,看CTRL的第16位(COUNTFLAG)是否为1,看STRL的第0位(ENABLE)是否为1
  47.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; //关闭计数器
  48.         SysTick->VAL = 0x00; //清空计数器                     
  49. }
4、显示效果:
微信图片_20220523225158.jpg
5、工程代码:

AT32F425_SW_I2C_0.96OLED.zip

352.86 KB, 下载次数: 21

sadicy 发表于 2022-6-3 08:57 | 显示全部楼层
这开发板都是白色的么?看着挺清新
简单happy 发表于 2022-6-12 23:33 | 显示全部楼层
感谢楼主分享,借鉴了一下I2C
yangxiaor520 发表于 2022-6-13 19:13 来自手机 | 显示全部楼层
很多时候IIC都是用IO模拟的。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

140

帖子

3

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