嵌入式开发-STM32硬件I2C驱动OLED屏

news2024/11/27 15:32:37

嵌入式开发-STM32硬件I2C驱动OLED屏

I2C简介

I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。

STM32的I2C

坊间流传STM32的硬件I2C很容易死机,所以不能使用硬件I2C,正点原子也在教程中强调了这一点。个人猜想由于Philips拥有专利,而ST为了绕开专利,而将硬件I2C弄得异常复杂(从相关的寄存器数量及设置可见一斑),造成硬件I2C很是难用,也容易出现异常死机。

今天我就来挑战一下。尝试使用STM32F103C8T6用硬件I2C的方式来驱动OLED屏。

MCU与OLED的硬件连接

都是常规设置不啰嗦:开启外部时钟,开启SWD调试接口,开启I2C2,配置默认即可,LED可用可不用。
在这里插入图片描述

OLED驱动函数

这个是参照正点原子的软件驱动I2C例程修改的,将其中的软件驱动IO口电平的相关代码改为HAL_I2C_Mem_Write()函数来驱动。

GRAM的定义

有一个问题,正点原子的代码中,GRAM的定义有点问题,如下:

OLED_GRAM[144][8];	//行定义是Y值,列定义是X值

144是X的值,8是Y的值,符合常规,没有问题。
OLED的驱动时,是要求连续发送X的值,一行发完再发一行,正点原子的代码中也是这样做的,他不是按数组的行来取的,而是按数组的列来取值,对于软件I2C来说没有问题。
但是通过HAL_I2C_Mem_Write()函数连续发送数据时,其发送过程是提供数组首地址,然后地址自增,也就是先发送Y的那8个数据,再连续发送整列,因为数组就是这样配置的,这样就不能用。
于是将X和Y的定义换一下,成下面这样

OLED_GRAM[8] [144];  //行定义改为X值,列定义改为Y值

这样再使用HAL_I2C_Mem_Write()函数连续发送数据时,就正常了。

当然,画点画线写字符等所有相关函数都要做配合性的修改,这里不再一一列出,需要的可以下载完整的工程文档,文末有链接。

显示内容

显示的内容很简单,就是交替显示2行字符,这样如果程序死机的话画面就肯定不动了
在这里插入图片描述
在这里插入图片描述

人为模拟干扰

将SCL和SDA两根线经由120欧电阻引出,经过120欧电阻可以对该引脚强制拉高或拉低,方便测试。然后分别对地进行触碰,和互相碰触,绝对不可以直接焊线引出然后直接碰触,否则会由于电源对地短路烧电源。
可以发现一旦强制拉低,画面立刻不再切换显示,或者直接黑屏,这取决于卡死点的程序运行位置。
但是此时程序仍然在正常运行,可以打断点和跟踪调试,于是判断故障为刚才的强制拉低导致OLED屏幕运行错乱。

修改代码

检查代码发现,是下面这个语句出了问题

void oled_write_onebyte(u8 data, u8 cmd)
{
  HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
}


void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
}

也就是说,在STM32往OLED寄存器中写入数据时,由于人为操作导致数据异常,OLED内部寄存器参数乱了,导致工作不正常。
针对性的解决方法也是简单粗暴,将OLED重新初始化,并重新上电,改代码如下:

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

重复人为模拟干扰

发现显示仍然没有正常,检查发现程序在I2C_RequestMemoryWrite()这个函数出了问题,简单粗暴的直接把I2C再来一次初始化搞定。

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(10);
    OLED_DisPlay_On();
  }
}

著名的HardFault_Handler错误

此时程序工作基本正常,可以从故障中恢复,但长时间维持人为异常状态(十多秒)后,仍有小几率卡死,程序会跳转到HardFault_Handler,这个排查起来花了点时间,最后发现是
堆栈溢出,见下图:
在这里插入图片描述

注意看箭头所指的这个拖动条,oled_init和oled_write_onebyte这两个函数已经重复了N次,继续下去肯定是堆栈溢出跑不了的。
这个问题是由于在oled_init函数中调用了oled_write_onebyte这个函数,而在这个函数运行时,如果仍然处于故障状态的话,就又会调用oled_init,如此便会形成嵌套,嵌套多了就会导致堆栈溢出。
解决起来还是简单粗暴,加延时。
你不是时间长了,重复次数多,导致堆栈溢出么,我给你加延时,让你在几十分钟内跑不了那么多次不就行了。
当然这个搞法治标不治本,但是谁没事把个OLED屏幕短路那么久。如果哪位有更好的解决思路,请在下方留言讨论。

void oled_write_onebyte(u8 data, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, 0, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
HAL_Delay(100);
oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(100);
    OLED_DisPlay_On();
  }
}

void oled_write_bytes(u8* data, u8 len, u8 cmd)
{
  u32 ret;
  ret = HAL_I2C_Mem_Write(&hi2c2, OLED_ADDR, cmd, I2C_MEMADD_SIZE_8BIT, data, len, 1000);
  if(ret!=0)
  {
    MX_I2C2_Init();
    HAL_Delay(100);
    oled_init();
    OLED_DisPlay_Off();
    HAL_Delay(100);
    OLED_DisPlay_On();
  }
}

至此故障解决。
咦,传说中的I2C不好用的问题在哪里呀?出了问题复位重来不就行了么?ST这么大的公司,如果连个I2C都做得不能用的话,他的片子还能卖得这么火么?可能做到每年几百亿的销售额么?
所以不要人云亦云,有问题还是自己过一遍,尝试一下也许就自己解决了。

完整工程链接

完整工程,包括CubeMX工程,Keil工程,链接如下:嵌入式开发-STM32硬件I2C驱动OLED屏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/3942.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

zlMediaKit 10 http相关

HttpRequestSplitter.h HttpRequestSplitter 结构 ssize_t _content_len 0;size_t _remain_data_size 0;toolkit::BufferLikeString _remain_data;input 上次还有剩余的数据,就把这次的数据和上次的数据接上。 分包: const char *ptr data;if(!_re…

大数据之Hive(二)

文章目录前言一、Hive数据库和表操作(一)数据库操作1. 创建数据库2. 删除数据库(二)数据表操作1. 内部表和外部表的操作1.1 内部表操作1.2 外部表操作2. 复杂类型操作2.1 Array类型2.2 map类型2.3 struct类型前言 #博学谷IT学习技…

【数据结构与算法】二叉排序树平衡二叉树哈夫曼树

🔥 本文由 程序喵正在路上 原创,CSDN首发! 💖 系列专栏:数据结构与算法 🌠 首发时间:2022年11月7日 🦋 欢迎关注🖱点赞👍收藏🌟留言🐾…

Oracle 处理json数据

文章目录备注:一. Json数据存储二. Json数据insert三. json数据update四. json数据查询五. 常用的json函数5.1 json_array5.2 JSON_ARRAYAGG5.3 JSON_DATAGUIDE5.4 JSON_MERGEPATCH5.5 JSON_OBJECT5.6 JSON_OBJECTAGG5.7 JSON_QUERY5.8 json_serialize5.9 JSON_TABLE5.10 JSON_…

作为前端还在使用GIF动画吗?换一种更优雅的方案吧

Web-Editor 前言 动画需求在业务开发中是很常见的功能,无论是客户端开发、Web 开发、还是桌面端开发,为了产品有更好的用户体验,UED 设计的视觉效果也愈发的复杂,一般些简单的淡入淡出,旋转效果开发花费些时间即可搞…

三只松鼠,“跑”不动了?

【潮汐商业评论/ 原创】 编辑部的Lisa是个典型的吃货,而坚果零食绝对是她的心头好。用她的话来说“坚果提供优质脂肪,每天吃点,解馋又健康啊。” 而作为网红坚果零食“开山鼻祖”之一的三只松鼠,最近的日子似乎并不好过。 近日…

阿里云SLB之:基于HTTPS协议的SLB应用场景(十二)

文章目录 1.配置域名解析2.配置HTTPS协议类型的SLB七层负载2.1.点击监听配置向导2.2.配置负载均衡类型2.3.设置负载均衡算法2.4.设置域名使用的SSL证书2.5.设置后端虚拟服务器组2.6.开机健康检查2.7.审核配置完成创建3.配置HTTP强转HTTPS4.配置ECS中的Nginx支持HTTPS协议5.通过…

文件传输协议

1、FTP 文件传送协议FPT(File Transfer Protocol)是互联网上使用的最广泛的文件传输协议。FTP提供交互式访问,允许客户指明文件的类型与格式,并允许文件具有存取权限(如访问文件的用户必须经过授权,如输入有…

pg故障修复记录

一个线上实例,用户数据大概300g 400g的样子,由于用户自己修改了最大连接数,超过了我们设置的合理范围,导致自动恢复失败,现在需要我们手动搭建起来新的主从关系。 但是在搭建的过程中,出现了两个比较麻烦的…

我用python分析买房数据

首先说明,这是一篇技术文章。 明年打算买房,媳妇这段时间总去看房子,这种状态持续了两个月,最近终于消停了。现在整个市场不明朗,我们也不确定换到哪里。不如先整理点数据,至少能监控一些区域价格&#xf…

Vue利用flex布局实现TV端城市列表

Vue利用flex布局实现TV端城市列表 vue中城市列表和搜索很常见&#xff0c;这篇博客就来说说咋实现搜索和城市列表 1.实现搜索布局代码&#xff1a; <div class"search-bar"><input class"search-input" v-model"citySearchResult" :…

Java【数组】定义与使用,什么是引用类型你知道吗

文章目录前言一、数组的基本概念1.什么是数组2.数组的创建和初始化1.数组的创建2. 数组的初始化3.数组的使用4.遍历数组&#xff08;两种方式&#xff09;二、数组是引用类型1.初识JVM内存分配2.引用类型3.认识null三、数组的应用场景1.保存数据2.作为方法的参数1.参数传基本数…

STM32F407ZGT6|SPI主从模式

功能&#xff1a;主机发送数据0x34–>从机接收数据–>通过串口将数据发送出去–>串口猎人显示0x34 必备知识点 1、SPI串行外设接口特点 高速、全双工、同步、串行高速&#xff1a;发送数据的速度很快全双工&#xff1a;两设备可同时双向通信&#xff08;接收与发送&…

STM32 CAN过滤器标识符学习笔记

最近看了下STM32 CAN 通讯其中标示符过滤器设置大有讲究。特别是你要使用ST库函数时&#xff0c;当过滤器工作在屏蔽模式下&#xff0c;并且你把屏蔽位设了1也就是标示符对应位必须全部匹配才能通过&#xff0c;这是由其要小心。 举个例子吧&#xff0c;过滤器长度为32位&…

【MindSpore易点通】在开发环境中如何使用MindInsight在线调试器

背景信息 在使用开发环境训练任务过程中&#xff0c;为方便开发人员更形象地观察到实时训练任务中的数值变化情况以分析精度问题&#xff0c;ModelArts在线调节器应运而生。与离线调试器的区别在于&#xff0c;离线调试器只能待整个任务运行完成后&#xff08;收集到整个任务过…

【机器学习大杀器】Stacking堆叠模型

1.前言 Kaglle比赛中使用Stacking模型是非常常见的大杀器&#xff0c;这是为什么呢&#xff1f; 【机器学习大杀器】Stacking堆叠模型 1.前言 2.Model 3: Stacking model 2.1 description of the algorithms: 2.2 interpretation of the estimated models: 3. Extend 3.1 …

终于盼到了,Python 数据科学速查表中文版来了

近几年以来&#xff0c;Python 的应用场景越来越多&#xff0c;几乎可以应用于自然科学、工程技术、金融、通信和商业等各种领域。究其原因在于 Python 的简单易学、功能强大。 想系统地学点东西&#xff0c;发现很多不错的技术文档都是英文资料&#xff0c;发现英文竟然成为了…

数据结构考研第六章——图(内含动图)

大纲要求&#xff1a;图的相关算法相对较多&#xff0c;通常只要求掌握其基本思想和实现步骤&#xff0c;而算法的具体实现不是重点。 一、图的基本概念 图的概念&#xff1a;图G由顶点集V和边集E组成的&#xff0c;记为G&#xff08;V&#xff0c;E&#xff09;有向图&#x…

6_显示登录信息以及上传图片

目录一 显示登录信息二 账号设置修改用户图片一 显示登录信息 实现思路 书写一个拦截器 loginTicketInterceptor 在 preHandle 方法中获取用户发送请求时携带的 cookie书写一个专门获取cookie的工具类 CookieUtil查数据库,数据库是否存在该 ticket,判断该凭证是否有效,-凭证是…

【论文阅读】EDPLVO: Efficient Direct Point-Line Visual Odometry

一、公式及符号约定 这篇论文是将直接法的残差计算从点扩展到了线段&#xff0c;所以一些符号在第三章的部分提前做了约定。用Π表示投影的函数&#xff0c;也就是用像素坐标和内参矩阵以及深度信息&#xff0c;投影出点的空间坐标&#xff0c;反之Π-1表示的是将空间坐标投影…