STM32 硬件IIC 控制OLED I2C卡死问题

2023-10-27

#更新通知:2023-09-06 STM32L151 固件库 使用I2C 太难了,又宕机了,建议不要在固件库版本上尝试硬件IIC 了,一般人真用不了,直接使用软件模拟的,或者不要使用固件库了,用HAL 库吧,据说HAL 库没这么多问题,不死心的我还是死心了,等有空再研究吧

1. STM32L151C8T6 硬件IIC 控制OLED 屏,OLED 驱动IC CH1116G, 查阅OLED 数据手册

在这里插入图片描述

2. STM32 硬件IIC 初始化,用的标准库,固件库

// stm32l151c8t6 as master, oled control ic (CH1116G) as slave, and communicate by master iic2
void STM32L151C8T6_IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    I2C_InitTypeDef I2C_InitStruct;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // GPIO_OType_OD, GPIO_OType_PP
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_400KHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct); // IIC2 SCL - PB10, SDA - PB11

    GPIO_ResetBits(GPIOB, GPIO_Pin_11);
    delay_xms(20);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2); // set PB10 as IIC2 SCL
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2); // set PB11 as IIC2 SDA

    I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStruct.I2C_ClockSpeed = iic_clockSpeed_400Khz; // must be less than 100 Khz
    I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStruct.I2C_Mode = I2C_Mode_SMBusHost; // 这里很重要
    I2C_InitStruct.I2C_OwnAddress1 = IIC2_NOT_USE_OWN_ADDR; // do not use own address

    I2C_Init(I2C2, &I2C_InitStruct);

    I2C_Cmd(I2C2, ENABLE);

    I2C_AcknowledgeConfig(I2C2, ENABLE);
}

3. GPIO 引脚速率要和 I2C 速率匹配,这很重要

在这里插入图片描述

3.1 I2C模式,我这里选的是主机模式,选其它模式就会出问题

在这里插入图片描述

4. OLED 硬件I2C 发送函数封装,给OLED发送的东西分两种:①发的是数据,②发的是命令

// master (STM32L151C8T6) send cmd instruction to oled screen control ic (CH1116G)
void OLED_SendCmd(uint8_t cmd)
{
    WaitFor_IIC_ReadyToWorking();

    I2C_GenerateSTART(I2C2, ENABLE); // iic start signal

    IIC_SendStartSignal_CheckEvent();

    I2C_Send7bitAddress(I2C2, OLED_ADDRESS, I2C_Direction_Transmitter); // send device addr and write bit

    I2C_SendDeviceAddrWaitAck();

    IIC_SendByteToOLED(iic_transmitType_Cmd);

    IIC_Delay(IIC_TIMEOUT_COUNTER);
    I2C_SendData(I2C2, cmd);
    I2C_SendByteDataWaitAck();

    I2C_GenerateSTOP(I2C2, ENABLE);
    IIC_Delay(IIC_TIMEOUT_COUNTER);
}

5. IIC 发送数据之前先检查I2C 是否有空,STM32 为了规避飞利浦的发明专利,把硬件I2C 搞的很复杂,导致固件库会有卡死的问题,据说HAL 库解决了这问题,本人没用过HAL库

static void WaitFor_IIC_ReadyToWorking(void)
{
    while (I2C2->SR2 & 0x02)
    {
        INFO_LOG("[WaitFor_IIC_ReadyToWorking] i2c2 is busy\r\n");
    }
}

6. 发送完起始信号后,要检查起始信号事件, 不要用固件库检查事件函数,会卡死的

static void IIC_SendStartSignal_CheckEvent(void)
{
    while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0001)))
    {
        printf("[IIC_SendStartSignal_CheckEvent][] I2C_SR1=0x%04x, I2C2_SR2=0x%04x\r\n", I2C2->SR1, I2C2->SR2);
    }
    while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0003)) == 0x0003)
    {
        printf("[IIC_SendStartSignal_CheckEvent][] I2C_SR1=0x%04x, I2C2_SR2=0x%04x\r\n", I2C2->SR1, I2C2->SR2);
    }
}

7. 发送完设备地址也要检查发送设备地址这个事件,同样不能用固件库函数

static void I2C_SendDeviceAddrWaitAck(void)
{
    while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0082)) == 0x0082)
    {
    }
    while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0007)) == 0x0007)
    {
    }
}

8. 发送命令给OLED 屏,这里要发两次,我也暂时没弄明白为什么,这个地方搞死我了,好惨

static void IIC_SendByteToOLED(uint8_t mode)
{
    IIC_Delay(IIC_TIMEOUT_COUNTER);
    I2C_SendData(I2C2, mode); // 0x00, high 8-bits, cmd code, iic_transmitType_Cmd
    I2C_SendByteDataWaitAck();

    IIC_Delay(IIC_TIMEOUT_COUNTER);
    I2C_SendData(I2C2, mode); // 0x00, low 8-bits, cmd code
    I2C_SendByteDataWaitAck();
}

9. 发送完命令后要检查标志位, while 里面可以啥都不写,空转,死等,也可以加一个超时退出,但是超时退出只能解决卡死的问题,并不能解决I2C busy 卡死导致发不出去数据的问题,治标不治本,这一点被别人说的坑死了,网上有人说超时退出可以解决卡死问题,但仅仅只解决了卡死问题,I2C 还是没有工作起来,我们的最终目的是让I2C 工作起来

static void I2C_SendByteDataWaitAck(void)
{
    while (!((uint16_t)(I2C2->SR1) & (uint16_t)(0x0080)) == 0x0080)
    {
    }
    while (!((uint16_t)(I2C2->SR2) & (uint16_t)(0x0007)) == 0x0007)
    {
    }
}

10. OLED 初始化,不同的驱动IC 会有所区别,但是可以先用我这个套用一下试试,我是在STM32L151C8T6 芯片上还跑了FreeRTOS 实时操作系统的

void OLED_Init(void)
{
    delay_xms(20); // oled startup slowly than stm32l151c8t6
    INFO_LOG("[OLED_Init] init start\r\n");

    OLED_SendCmd(0xAE); // display off

    OLED_SendCmd(0x02); // set colum start address
    OLED_SendCmd(0x10); // set colum end address

    OLED_SendCmd(0x40); // set start line (first row)

    OLED_SendCmd(0xB0); // set page address

    OLED_SendCmd(0x81); // set contrast ratio
    OLED_SendCmd(0xCF); // 128

    OLED_SendCmd(0xA1); // set segment remapping, from right to left

    OLED_SendCmd(0xA6); // forward display, normal or reverse

    OLED_SendCmd(0xA8); // multiple reuse rate, multiple ratio
    OLED_SendCmd(0x3F); // duty = 1 / 64

    OLED_SendCmd(0xAD); // set charge pump enable
    OLED_SendCmd(0x8B); // enable DC-DC

    OLED_SendCmd(0x33); // set VPP = 10V

    OLED_SendCmd(0xC8); // set output scan direction, COM[N - 1] to COM[0], COM scan direction

    OLED_SendCmd(0xD3); // set display offset
    OLED_SendCmd(0x00); // 0x00

    OLED_SendCmd(0xD5); // set internal clock frequence, set osc frequency
    OLED_SendCmd(0xC0);

    OLED_SendCmd(0xD9); // set pre-charge period
    OLED_SendCmd(0x1F); // 0x22

    OLED_SendCmd(0xDA); // set COM pins, pin layout
    OLED_SendCmd(0x12);

    OLED_SendCmd(0xDB); // set electrical level, set VCOMH
    OLED_SendCmd(0x40);

    OLED_SendCmd(0xAF); // enable display, display on

    INFO_LOG("[OLED_Init] init complete\r\n");
}

11. OLED 测试函数封装,B 站博主keysking

void OLED_Test(void)
{
    OLED_SendCmd(0xB0); // page 0
    OLED_SendCmd(0x00); // colume 0 low 4-bits
    OLED_SendCmd(0x10); // colume 0 high 8-bits

    OLED_SendCmd(0x40);
    OLED_SendCmd(0xAA);
}

12. 主函数调用OLED 初始化函数和测试函数,先延时一会再初始化,因为STM32 比OLED的控制IC 起来的快很多,CH1116G

int main(void)
{
	delay_xms(1000);
    OLED_Init();
    OLED_Test();
}

13. IIC_Delay 函数

#define IIC_TIMEOUT_COUNTER    0x1000 // iic transmit timeout

static void IIC_Delay(uint32_t delay_time)
{
    uint32_t delayTime;
    for (delayTime = 0; delayTime < delay_time; delayTime++)
    {
    }
}

14. 用逻分仪抓的数据看不出来细节,可以用示波器抓一下波形,看细节,这个波形都变形了,查看原理图,上面接了两个电容,把SCL 和SDA 当成高频信号,给我滤掉了,导致这波形乱七八糟的,直接把原理图上的两个对地电容去掉,或者更好其它容量的电容,我这里是直接去掉了

在这里插入图片描述

15. 接了两个对地电容,把正常信号当成高频滤掉了

在这里插入图片描述

16. 正常波形,应该是这样的,方波才对,正弦波是有问题的,这里还有一个小台阶,待处理

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 硬件IIC 控制OLED I2C卡死问题 的相关文章

  • 使用开散列实现对字典的查找插入删除(C++实现)

    一 需求分析 问题描述 实现对字典的查找 基本要求 在分块查找 AVL树 哈希查找 B树或者B 树查找中选择一种你认为最高效的动态查找方法对字典 单词 词性加释义 在内存中的动态查找结构或者在外存的字典文件的构造 查找 插入 删除 逻辑操作
  • JVM常见命令之JPS

    1 JPS Java Virtual Machine Process Status JDK1 5提供的显示当前所有进程pid的命令 2 jps q 只输出pid 3 jps l 输出应用程序main class 的完整package名或者应

随机推荐

  • Spring底层组件xxxAware家族

    搞懂xxxAware家族对理解Spring源码和提高代码能力也有帮助 Spring中常见xxxAware接口列举如下 ApplicationContextAware BeanNameAware EmbeddedValueResolverAw
  • 实时数仓实践以及架构

    前言 数据智能 Data Intelligence 有一个必须且基础的环节 就是数据仓库的建设 同时 数据仓库也是公司数据发展到一定规模后必然会提供的一种基础服务 从智能商业的角度来讲 数据的结果代表了用户的反馈 获取结果的及时性就显得尤为
  • IDEA在Debug模式下 对象转JSON

    如果文章对你有帮助欢迎 关注 点赞 收藏 一键三连 一起努力 IDEA在debug模式下面是不能直接复制被调试的Object为Json字符串的 但是在工作中经常会用到json入参 这里用两种方式实现这个操作 方便进行其他操作和工作交流 一
  • eds能谱图分析实例_EDS那些事儿

    什么是EDS 我们通常所说的EDS全称为能量色散X射线谱仪 简称能谱仪 可同时记录所有X射线谱 用以测量X射线强度与X射线能量的函数关系 是一种不损坏试样的快速微区成分分析方法 通过测量材料被激发的特征X射线能量进行元素的定性分析 测量特征
  • fake-useragent,python爬虫伪装请求头

    在编写爬虫进行网页数据的时候 大多数情况下 需要在请求是增加请求头 下面介绍一个python下非常好用的伪装请求头的库 fake useragent 具体使用说明如下 安装fake useragent库 pip install fake u
  • 复杂美区块链溯源系统架构

    从功能架构上 复杂美将区块链存证溯源系统按照功能划分为区块链核心层 接口层 运维管理层 溯源平台层和用户端层 1 区块链基础层 面向整个存证溯源平台提供基础信息服务 主要是为上层架构组件提供基础设施 保证上层服务可靠运行 源数据从IOT设备
  • 数据结构-链式存储

    数据结构 一 数据结构的定义 一组用来保存一种或者多种特定关系的数据集合 二 数据与数据之间的关系 lt 1 gt 数据的逻辑结构 数据元素与元素之间的关系 集合 关系平等 线性结构 元素之间一对一的关系 表 队列 栈 树形结构 元素之间一
  • 模式分类识别

    模式分类识别 DBN深度置信网络数据多特征分类预测 Matlab完整程序 目录 模式分类识别 DBN深度置信网络数据多特征分类预测 Matlab完整程序 分类结果 基本介绍 程序设计 参考资料 分类结果
  • Host文件

    linux中 etc目录 配置文件 etc目录包含了系统特有的配置文件 所谓配置文件 就是用于控制程序运行的本地文件 它绝大多情况下都说 只读 的私有文件 而且是可编辑的 这里的可编辑是指能直接看懂的 所以那些二进制可执行文件是不能作为配置
  • springboot多数据源---2事务

    一 多数据源事务控制 在多数据源下 由于涉及到数据库的多个读写 一旦发生异常就可能会导致数据不一致的情况 在这种情况希望使用事务 进行回退 但是Spring的声明式事务在一次请求线程中只能使用一个数据源进行控制 但是对于多源数据库 1 单一
  • 在webstorm 中直接运行ts文件

    原文链接 在webstorm 中直接运行ts文件 上一篇 ubuntu 使用 Apache Bench 进行并发测试 下一篇 使用js解数独难题 安装插件后重启IDE Run Configuration for TypeScript
  • Notice: Use of undefined constant submit - assumed 'submit'

    Notice Use of undefined constant submit assumed submit in D wamp www ECMS insert monitors php on line 66 Notice Undefine
  • vue期望值与实际值比较:折线图

    效果图 点击上方对应按钮 下方相应的数据图可隐藏 显示 代码 一 下载echarts包 终端运行 npm install echarts 二 components HelloWorld vue
  • Python3,一行代码解析地址信息,原来物流单的地址是这样拆分。

    1行代码解析地址信息 1 引言 2 代码示例 2 1 简介 2 2 安装 2 3 实战 2 3 1 提取省市区信息 2 3 2 提取街镇乡 村或居委会信息 2 3 3 自动补全省市信息 3 总结 1 引言 小屌丝 鱼哥 你说咱们发快递时填写
  • 页式存储,段式存储,段页式存储,引入快表等访存次数

    王道的说法 页式存储 2次 第一次 访问内存中的页表 利用逻辑地址中的页号查找到页帧号 与逻辑地址中的页内偏移拼接形成物理地址 第二次 得到物理地址后 再一次访问内存 存取指令或者数据 段式存储 2次 同上 段页式存储 3次 第一次 访问内
  • 【译】Rust 实现一个 DNS 客户端,我从中学到什么

    What I learned from making a DNS client in Rust 译文 Rust 实现一个 DNS 客户端 我从中学到什么 原文链接 https blog adamchalmers com making a d
  • 大津算法的matlab实现

    大津算法详解 一 算法功能 图像分割就是把图像分成若干个特定的 具有独特性质的区域并提出感兴趣目标的技术和过程 它是由图像处理到图像分析的关键步骤 大津算法也称最大类间差法 由大津于1979年提出 被认为是图像分割中阈值选取的最佳算法 计算
  • 用开卡工具重生SSD,SM2246XT一步一步开卡成功教程

    故障现象 不能进系统 用U盘从PE进入 过程很慢 卡住 进不了PE 直接拆下硬盘 用硬盘盒连接电脑 能识别 发现C盘还已经标红 D盘正常 还不错 文件都在 直接拷贝出来 接下来就是对他直接格式化 这里出现了问题 无论是用PE的还是windo
  • 前端知识题整理第二期

    1 js中的闭包指什么 有权访问另一个函数作用域中的变量的函数 创建闭包的常见方式 就是一个函数内部创建另一个函数 2 v if和v show的区别是什么 v if是动态的向DOM树内添加或删除DOM元素 v show本质是标签displa
  • STM32 硬件IIC 控制OLED I2C卡死问题

    更新通知 2023 09 06 STM32L151 固件库 使用I2C 太难了 又宕机了 建议不要在固件库版本上尝试硬件IIC 了 一般人真用不了 直接使用软件模拟的 或者不要使用固件库了 用HAL 库吧 据说HAL 库没这么多问题 不死心