Ai-M61-32S i2c点亮oled详细教程及踩坑记录

[复制链接]
查看2070 | 回复11 | 2023-12-10 17:32:31 | 显示全部楼层 |阅读模式
本帖最后由 干簧管 于 2023-12-24 19:53 编辑
一、首先了解SDK中的i2c
https://bbs.ai-thinker.com/forum ... 1895&extra=page%3D1 泽哥这篇文章写的也很详细,首先参考这个对SDK中的i2c有个初步的认识
二、硬件连接
这里参考泽哥的代码,使用IO0和IO1作为i2c功能使用,后来发现按照其注释连接有问题,sda和scl注释反了

5.PNG

如下,IO0是SCl,IO1是SDA
6.PNG
此oled模组引脚较为简单,只有4p,从左到右依次是GND,VDD,SCK,SDA,于是按照如下连接
oled         Ai-M61-32S
GND <---> GND
VDD <---> 3V3
SCK  <---> IO0
SDA <---> IO1


三、oled工程创建
1.复制i2c_eeprom目录,并将其命名为i2c_oled
1.PNG
2.添加oled.c和oled.h文件,主要用于写oled初始化,清屏,刷图等函数供main函数调用,h文件中则用来声明这些函数以及定义一些宏,如:从机地址等
2.PNG

3.CMakeLists.txt 中的 project 名称换成 i2c_oled,并且添加 target_sources(app PRIVATE oled.c)
3.PNG
四、oled初始化代码编写
从淘宝商家拿到的init code如下,需要设置
  1. static unsigned char OLED_init_cmd[25]=
  2. {
  3.   /*0xae,0X00,0X10,0x40,0X81,0XCF,0xff,0xa1,0xa4,
  4.   0xA6,0xc8,0xa8,0x3F,0xd5,0x80,0xd3,0x00,0XDA,0X12,
  5.   0x8d,0x14,0xdb,0x40,0X20,0X02,0xd9,0xf1,0xAF*/
  6.        0xAE,//关闭显示
  7.        0xD5,//设置时钟分频因子,震荡频率
  8.        0x80,  //[3:0],分频因子;[7:4],震荡频率

  9.        0xA8,//设置驱动路数
  10.        0X3F,//默认0X3F(1/64)
  11.        0xD3,//设置显示偏移
  12.        0X00,//默认为0
  13.        0x40,//设置显示开始行 [5:0],行数.                              
  14.        0x8D,//电荷泵设置
  15.        0x14,//bit2,开启/关闭
  16.        0x20,//设置内存地址模式
  17.        0x02,//[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
  18.        0xA1,//段重定义设置,bit0:0,0->0;1,0->127;
  19.        0xC8,//设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
  20.        0xDA,//设置COM硬件引脚配置
  21.        0x12,//[5:4]配置            
  22.        0x81,//对比度设置
  23.        0xEF,//1~255;默认0X7F (亮度设置,越大越亮)
  24.        0xD9,//设置预充电周期
  25.        0xf1,//[3:0],PHASE 1;[7:4],PHASE 2;
  26.        0xDB,//设置VCOMH 电压倍率
  27.        0x30,//[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
  28.        0xA4,//全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
  29.        0xA6,//设置显示方式;bit0:1,反相显示;0,正常显示        
  30.        0xAF,//开启显示     
  31. };
复制代码
然后需要根据SDK中的发送api将这些code发送到屏端,然后先找个例程参考下发送时序
  1. uint8_t I2C_TxData_Polling(uint8_t Address, uint8_t *Buffer, uint8_t Length)
  2. {
  3.     uint16_t i = 0;

  4.     sI2C_GenerateStart();

  5.     sI2C_WriteByte(OLED_I2C_ADDRESS);

  6.     if (sI2C_GetACK())
  7.     {
  8.         sI2C_GenerateStop();
  9.         return (1);
  10.     }

  11.     sI2C_WriteByte(Address);

  12.     if (sI2C_GetACK())
  13.     {
  14.         sI2C_GenerateStop();
  15.         return (1);
  16.     }

  17.     for (i = 0; i < Length; i++)
  18.     {
  19.         sI2C_WriteByte(*Buffer++);

  20.         if (sI2C_GetACK())
  21.         {
  22.             break;
  23.         }
  24.     }

  25.     sI2C_GenerateStop();

  26.     if (i == Length)
  27.     {
  28.         return (0);
  29.     }
  30.     else
  31.     {
  32.         return (1);
  33.     }
  34. }
复制代码
如上是从网上找的单片机上驱动oled屏幕的i2c发送时序:start->写从机地址->等从机ack->发寄存器地址->发数据->等从机ack ..... ->发数据->等从机ack->stop,于是根据泽哥的讲解以及SDK中的demo,尝试编写以下函数
  1. static void oled_write(unsigned char reg_addr,unsigned char *buff, char buf_size)
  2. {
  3.     struct bflb_i2c_msg_s msgs[2];
  4.   
  5.     msgs[0].addr = OLED_I2C_ADDRESS;
  6.     msgs[0].flags = I2C_M_NOSTOP;
  7.     msgs[0].buffer = &reg_addr;
  8.     msgs[0].length = 1;
  9.    
  10.     msgs[1].addr = OLED_I2C_ADDRESS;
  11.     msgs[1].flags = 0;
  12.     msgs[1].buffer = buff;
  13.     msgs[1].length = buf_size;

  14.     bflb_i2c_transfer(i2c0, msgs, 2);
  15.     //bflb_mtimer_delay_ms(100); //此处影响刷屏速度,先直接去掉后面查看是否存在其他问题
  16. }


  17. void OLED_Init(void)
  18. {
  19.     oled_write(OLED_CMD,OLED_init_cmd, sizeof(OLED_init_cmd) /sizeof(unsigned char));
  20.     printf("OLED_Init over\r\n");
  21. }
复制代码
上述中的 OLED_I2C_ADDRESS ,从其他例程中看到是 0x78,于是我在此也填的这个,后面因为这事浪费了很久,此处应该是 0x3c,左移一位然后最低位为读写控制位

五、取模软件取模

参照这个博客进行取模操作,博主讲解得十分详细 https://blog.csdn.net/qq_39400113/article/details/108036400

4.PNG
六、取模显示效果图
8cf4b9e3c7445e09fd6410bfbaf67b7.jpg

七、实际使用中发现如果i2c通讯失败,官方的SDK并没有报错log,于是在 bflb_i2c_transfer 函数中添加以下打印,来判断i2c通讯失败
由于上面从机地址的问题,导致屏幕一直不亮,添加log流程也都跑完了,也没看出什么问题,幸好手头有一个逻辑分析仪,踩一下波形发现,发送完从机地址后没有回ack,配置的0x78但是波形解析是0xF0,于是改为0x3c再试试就ok了,于是想SDk里面通讯失败咋没有报错的log呢,比如 rx timeout啥的,于是就查看 bflb_i2c_transfer 函数的定义,然后找到了异常时的return,但是此处没有log打印出问题,所以出问题时并没有提示,于是加上以下log,判断下i2c transfer是否存在问题
  1. int bflb_i2c_transfer(struct bflb_device_s *dev, struct bflb_i2c_msg_s *msgs, int count)
  2. {
  3.     uint16_t subaddr = 0;
  4.     uint16_t subaddr_size = 0;
  5.     bool is_addr_10bit = false;
  6.     int ret = 0;

  7.     bflb_i2c_disable(dev);

  8.     for (uint16_t i = 0; i < count; i++) {
  9.         if (msgs[i].flags & I2C_M_TEN) {
  10.             is_addr_10bit = true;
  11.         } else {
  12.             is_addr_10bit = false;
  13.         }
  14.         if (msgs[i].flags & I2C_M_NOSTOP) {
  15.             subaddr = 0;
  16.             for (uint8_t j = 0; j < msgs[i].length; j++) {
  17.                 subaddr += msgs[i].buffer[j] << (j * 8);
  18.             }
  19.             subaddr_size = msgs[i].length;
  20.             bflb_i2c_addr_config(dev, msgs[i].addr, subaddr, subaddr_size, is_addr_10bit);
  21.             i++;
  22.         } else {
  23.             subaddr = 0;
  24.             subaddr_size = 0;
  25.             bflb_i2c_addr_config(dev, msgs[i].addr, subaddr, subaddr_size, is_addr_10bit);
  26.         }

  27.         if (msgs[i].length > 256) {
  28.             return -EINVAL;
  29.         }
  30.         bflb_i2c_set_datalen(dev, msgs[i].length);
  31.         if (msgs[i].flags & I2C_M_READ) {
  32.             bflb_i2c_set_dir(dev, 1);
  33.             if ((msgs[i].flags & I2C_M_DMA) == 0) {
  34.                 ret = bflb_i2c_read_bytes(dev, msgs[i].buffer, msgs[i].length);
  35.                 if (ret < 0) {
  36.                     return ret;
  37.                 }
  38.             } else {
  39.                 bflb_i2c_enable(dev);
  40.             }
  41.         } else {
  42.             bflb_i2c_set_dir(dev, 0);
  43.             if ((msgs[i].flags & I2C_M_DMA) == 0) {
  44.                 ret = bflb_i2c_write_bytes(dev, msgs[i].buffer, msgs[i].length);
  45.                 if (ret < 0) {
  46. ++                  printf("i2c bflb_i2c_transfer error:%d\r\n",ret);
  47.                     return ret;
  48.                 }
  49. ++                //printf("i2c bflb_i2c_transfer Ok\r\n");
  50.             } else {
  51.                 bflb_i2c_enable(dev);
  52.             }
  53.         }
  54.     }

  55.     return 0;
  56. }
复制代码
i2c_oled.zip (4.84 KB, 下载次数: 8)

本帖被以下淘专辑推荐:

回复

使用道具 举报

qhsj | 2023-12-10 18:59:26 | 显示全部楼层
感谢分享
回复

使用道具 举报

WT_0213 | 2023-12-10 22:20:44 | 显示全部楼层
回复

使用道具 举报

爱笑 | 2023-12-11 08:49:37 | 显示全部楼层
优秀~
用心做好保姆工作
回复

使用道具 举报

心云 | 2023-12-11 14:29:50 | 显示全部楼层
优秀
回复

使用道具 举报

沈夜 | 2023-12-11 19:48:56 | 显示全部楼层
感恩
回复

使用道具 举报

abs421209023 | 2023-12-12 16:15:36 | 显示全部楼层
回复

使用道具 举报

1084504793 | 2024-1-2 08:36:39 | 显示全部楼层
回复

使用道具 举报

is麟儿 | 2024-1-11 14:54:16 | 显示全部楼层
佬 你这个程序清屏一次需要多少ms? 有用逻辑分析仪测量一下时间吗
回复 支持 反对

使用道具 举报

干簧管 | 2024-1-16 09:16:01 | 显示全部楼层
is麟儿 发表于 2024-1-11 14:54
佬 你这个程序清屏一次需要多少ms? 有用逻辑分析仪测量一下时间吗

具体没有呢,速度挺慢的
回复 支持 反对

使用道具 举报

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

本版积分规则