本帖最后由 干簧管 于 2023-12-24 19:53 编辑
一、首先了解SDK中的i2c
https://bbs.ai-thinker.com/forum ... 1895&extra=page%3D1 泽哥这篇文章写的也很详细,首先参考这个对SDK中的i2c有个初步的认识
二、硬件连接
这里参考泽哥的代码,使用IO0和IO1作为i2c功能使用,后来发现按照其注释连接有问题,sda和scl注释反了
如下,IO0是SCl,IO1是SDA
此oled模组引脚较为简单,只有4p,从左到右依次是GND,VDD,SCK,SDA,于是按照如下连接
oled Ai-M61-32S
GND <---> GND
VDD <---> 3V3
SCK <---> IO0
SDA <---> IO1
三、oled工程创建
1.复制i2c_eeprom目录,并将其命名为i2c_oled
2.添加oled.c和oled.h文件,主要用于写oled初始化,清屏,刷图等函数供main函数调用,h文件中则用来声明这些函数以及定义一些宏,如:从机地址等
3.CMakeLists.txt 中的 project 名称换成 i2c_oled,并且添加 target_sources(app PRIVATE oled.c)
四、oled初始化代码编写
从淘宝商家拿到的init code如下,需要设置
- static unsigned char OLED_init_cmd[25]=
- {
- /*0xae,0X00,0X10,0x40,0X81,0XCF,0xff,0xa1,0xa4,
- 0xA6,0xc8,0xa8,0x3F,0xd5,0x80,0xd3,0x00,0XDA,0X12,
- 0x8d,0x14,0xdb,0x40,0X20,0X02,0xd9,0xf1,0xAF*/
- 0xAE,//关闭显示
- 0xD5,//设置时钟分频因子,震荡频率
- 0x80, //[3:0],分频因子;[7:4],震荡频率
- 0xA8,//设置驱动路数
- 0X3F,//默认0X3F(1/64)
- 0xD3,//设置显示偏移
- 0X00,//默认为0
- 0x40,//设置显示开始行 [5:0],行数.
- 0x8D,//电荷泵设置
- 0x14,//bit2,开启/关闭
- 0x20,//设置内存地址模式
- 0x02,//[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
- 0xA1,//段重定义设置,bit0:0,0->0;1,0->127;
- 0xC8,//设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
- 0xDA,//设置COM硬件引脚配置
- 0x12,//[5:4]配置
- 0x81,//对比度设置
- 0xEF,//1~255;默认0X7F (亮度设置,越大越亮)
- 0xD9,//设置预充电周期
- 0xf1,//[3:0],PHASE 1;[7:4],PHASE 2;
- 0xDB,//设置VCOMH 电压倍率
- 0x30,//[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
- 0xA4,//全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
- 0xA6,//设置显示方式;bit0:1,反相显示;0,正常显示
- 0xAF,//开启显示
- };
复制代码 然后需要根据SDK中的发送api将这些code发送到屏端,然后先找个例程参考下发送时序
- uint8_t I2C_TxData_Polling(uint8_t Address, uint8_t *Buffer, uint8_t Length)
- {
- uint16_t i = 0;
- sI2C_GenerateStart();
- sI2C_WriteByte(OLED_I2C_ADDRESS);
- if (sI2C_GetACK())
- {
- sI2C_GenerateStop();
- return (1);
- }
- sI2C_WriteByte(Address);
- if (sI2C_GetACK())
- {
- sI2C_GenerateStop();
- return (1);
- }
- for (i = 0; i < Length; i++)
- {
- sI2C_WriteByte(*Buffer++);
- if (sI2C_GetACK())
- {
- break;
- }
- }
- sI2C_GenerateStop();
- if (i == Length)
- {
- return (0);
- }
- else
- {
- return (1);
- }
- }
复制代码 如上是从网上找的单片机上驱动oled屏幕的i2c发送时序:start->写从机地址->等从机ack->发寄存器地址->发数据->等从机ack ..... ->发数据->等从机ack->stop,于是根据泽哥的讲解以及SDK中的demo,尝试编写以下函数
- static void oled_write(unsigned char reg_addr,unsigned char *buff, char buf_size)
- {
- struct bflb_i2c_msg_s msgs[2];
-
- msgs[0].addr = OLED_I2C_ADDRESS;
- msgs[0].flags = I2C_M_NOSTOP;
- msgs[0].buffer = ®_addr;
- msgs[0].length = 1;
-
- msgs[1].addr = OLED_I2C_ADDRESS;
- msgs[1].flags = 0;
- msgs[1].buffer = buff;
- msgs[1].length = buf_size;
- bflb_i2c_transfer(i2c0, msgs, 2);
- //bflb_mtimer_delay_ms(100); //此处影响刷屏速度,先直接去掉后面查看是否存在其他问题
- }
- void OLED_Init(void)
- {
- oled_write(OLED_CMD,OLED_init_cmd, sizeof(OLED_init_cmd) /sizeof(unsigned char));
- printf("OLED_Init over\r\n");
- }
复制代码 上述中的 OLED_I2C_ADDRESS ,从其他例程中看到是 0x78,于是我在此也填的这个,后面因为这事浪费了很久,此处应该是 0x3c,左移一位然后最低位为读写控制位
五、取模软件取模
参照这个博客进行取模操作,博主讲解得十分详细 https://blog.csdn.net/qq_39400113/article/details/108036400
六、取模显示效果图
七、实际使用中发现如果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是否存在问题
- int bflb_i2c_transfer(struct bflb_device_s *dev, struct bflb_i2c_msg_s *msgs, int count)
- {
- uint16_t subaddr = 0;
- uint16_t subaddr_size = 0;
- bool is_addr_10bit = false;
- int ret = 0;
- bflb_i2c_disable(dev);
- for (uint16_t i = 0; i < count; i++) {
- if (msgs[i].flags & I2C_M_TEN) {
- is_addr_10bit = true;
- } else {
- is_addr_10bit = false;
- }
- if (msgs[i].flags & I2C_M_NOSTOP) {
- subaddr = 0;
- for (uint8_t j = 0; j < msgs[i].length; j++) {
- subaddr += msgs[i].buffer[j] << (j * 8);
- }
- subaddr_size = msgs[i].length;
- bflb_i2c_addr_config(dev, msgs[i].addr, subaddr, subaddr_size, is_addr_10bit);
- i++;
- } else {
- subaddr = 0;
- subaddr_size = 0;
- bflb_i2c_addr_config(dev, msgs[i].addr, subaddr, subaddr_size, is_addr_10bit);
- }
- if (msgs[i].length > 256) {
- return -EINVAL;
- }
- bflb_i2c_set_datalen(dev, msgs[i].length);
- if (msgs[i].flags & I2C_M_READ) {
- bflb_i2c_set_dir(dev, 1);
- if ((msgs[i].flags & I2C_M_DMA) == 0) {
- ret = bflb_i2c_read_bytes(dev, msgs[i].buffer, msgs[i].length);
- if (ret < 0) {
- return ret;
- }
- } else {
- bflb_i2c_enable(dev);
- }
- } else {
- bflb_i2c_set_dir(dev, 0);
- if ((msgs[i].flags & I2C_M_DMA) == 0) {
- ret = bflb_i2c_write_bytes(dev, msgs[i].buffer, msgs[i].length);
- if (ret < 0) {
- ++ printf("i2c bflb_i2c_transfer error:%d\r\n",ret);
- return ret;
- }
- ++ //printf("i2c bflb_i2c_transfer Ok\r\n");
- } else {
- bflb_i2c_enable(dev);
- }
- }
- }
- return 0;
- }
复制代码
i2c_oled.zip
(4.84 KB, 下载次数: 8)
|
|