一个简单的HAL库STM32使用DMA+硬件IIC驱动0.96寸OLED的方法

这篇具有很好参考价值的文章主要介绍了一个简单的HAL库STM32使用DMA+硬件IIC驱动0.96寸OLED的方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

自己在刚入坑嵌入式的时候,加入学校科协的一道免试题是开发一个简易的示波器,当时萌新不会做,中间又在准备比赛没时间,最近帮女朋友做课设需要做一个简易的交流电压表,而且终于有空做一下自己感兴趣的项目了,就想到了之前想做有没得做的一个简易示波器。

然后在开发示波器的时候自己写了一个画点的函数,后来发现画了的点只使用一小块屏幕,不刷新整屏,就会导致不同位置的点共同出现在屏幕上,后来我想到了整屏刷新的方式,后来又自己写了一个不使用DMA的方式驱动,发现帧率实在太低,没法用,就想到了用DMA的方式来刷屏。

在学习使用DMA的方式驱动OLED的时候上网查了查前人做过的教学发现不尽人意,中间也踩了很多坑,就想在这做一个简单易懂的教学,手把手教怎么从简单的CPU刷新部分屏幕转DMA推流刷整屏。

实践部分

说在前头,本篇文章仅限一些已经学会基础的使用CPU驱动OLED的同志们,接下来写的将以这为基础进行升级。
如果不会怎么驱动OLED的同志们可以去其他地方稍微搜一下怎么驱动0.96寸OLED,教学很多,写的也很好,我的代码在这篇文章的基础上加以改进的,本篇文章也将基于这个库来写

4针0.96寸OLED的HAL库代码(硬件I2C/全代码/stm32f1/CubeMX配置/包含有正负浮点数/100%一次点亮)

cubeMX配置

首先最简单的,按照一般的配置驱动OLED的屏幕
stm32 iic dma,stm32,嵌入式硬件,单片机

然后添加发送DMA请求
stm32 iic dma,stm32,嵌入式硬件,单片机

这里需要注意的是,如果只到上一步,生成的代码使用HAL_I2C_Mem_Write_DMA函数的时候会产生HAL_BUSY,然后只能刷一次屏,解决方法可以在代码中改状态,也可以像这样打开I2C event interrupt,这样在生成的代码中,HAL会自动产生I2C中断然后进入HAL的中断服务函数清除响应的一些标志,否则由于DMA是异步的,关闭I2C的常规中断导致发送完成后没有对发送结束进行处理,导致状态没有清空。
stm32 iic dma,stm32,嵌入式硬件,单片机
接下来就可以生成代码了

代码编写

第一步: 在OLED.h中定义屏幕的长和宽
stm32 iic dma,stm32,嵌入式硬件,单片机

#define OLED_WIDE    	8
#define	OLED_LENGTH 	128

第二步: 在OLED.c中开辟一块显存,这里的显存是一个8位的8*128的二维数组,8位竖着放,与8行共同组成64个像素的宽,即y,128列代表着横着的128个像素,即x
stm32 iic dma,stm32,嵌入式硬件,单片机

uint8_t show_mem[OLED_WIDE][OLED_LENGTH];

第三步: 重写OLED.c中的OLED_ShowChar函数
这里图片太大了就不放了,直接贴代码

void OLED_ShowChar(uint8_t x,uint8_t y,uint8_t chr,uint8_t Char_Size,uint8_t Color_Turn)
{
//	unsigned char c=0,i=0;
//		c=chr-' ';//得到偏移后的值
//		if(x>128-1){x=0;y=y+2;}
//		if(Char_Size ==16)
//		{
//			OLED_Set_Pos(x,y);
//			for(i=0;i<8;i++)
//				{
//				  if(Color_Turn)
//					  OLED_WR_DATA(~F8X16[c*16+i]);
//				  else
//					  OLED_WR_DATA(F8X16[c*16+i]);
//				}
//			OLED_Set_Pos(x,y+1);
//			for(i=0;i<8;i++)
//			    {
//				  if(Color_Turn)
//					  OLED_WR_DATA(~F8X16[c*16+i+8]);
//				  else
//					  OLED_WR_DATA(F8X16[c*16+i+8]);
//			    }

//			}
//	     else
//	     {
//				OLED_Set_Pos(x,y);
//				for(i=0;i<6;i++)
//			    {
//				  if(Color_Turn)
//					  OLED_WR_DATA(~F6x8[c][i]);
//				  else
//					  OLED_WR_DATA(F6x8[c][i]);
//			    }
//		  }
	unsigned char c=0,i=0;
	c = chr - ' ';//得到偏移后的值
	if(x>128-1)
	{
		x=0;
		y = y + 2;
	}
	if(Char_Size == 16)
	{
		for(i=0;i<8;i++)
		{
			if(Color_Turn)
				show_mem[y][x+i] = ~F8X16[c*16+i];
			else
				show_mem[y][x+i] = F8X16[c*16+i];
		}
		for(i=0;i<8;i++)
		{
			if(Color_Turn)
				show_mem[y+1][x+i] = ~F8X16[c*16+i+8];
			else
				show_mem[y+1][x+i] = F8X16[c*16+i+8];
		}
	}
	else
	{
		for(i=0;i<6;i++)
		{
			if(Color_Turn)
				show_mem[y][x+i] = ~F6x8[c][i];
			else
				show_mem[y][x+i] = F6x8[c][i];
		}
	}
}

其中的注释部分是原来的显示代码,我重写的部分只是将原先发出去的数据存到显存的相应位置,暂时不发出去。并且删掉函数中设置光标的函数OLED_Set_Pos(x,y);
即:将 OLED_WR_DATA(F6x8[c][i]);改成show_mem[y][x+i] = F6x8[c][i];

而且我的OLED_ShowNum之类的函数均是基于该函数写的,因此这里只需修改OLED_ShowChar即可,若你的代码不是这样的,那么就需要按照类似的方法重写函数

第四步: 修改初始化数据,在屏幕初始化之后加上两个写向控制寄存器的数据0x20,0x00,可以将原来的页寻址模式修改成水平寻址模式(详情可见ssd1306数据手册)
stm32 iic dma,stm32,嵌入式硬件,单片机

uint8_t CMD_Data[]={
0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1,0xA6, 0xA8, 0x3F,

0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1,0xDA, 0x12,
	
0xDB,0x30,0x8D,0x14, 0xAF,0x20,0x00};

这里是oled初始化的指令数据数组,可以看见在倒数第三个0xAF(打开显示)的后面增加了两个数据0x20, 0x00。(需要注意的是,我这里修改了我使用的库的初始化数组,使其新增了几个初始化数据,根据测试,直接在原先的库后面加上0x20,0x00也可以达到同样的效果)

相应的OLED初始化函数也需要修改:
stm32 iic dma,stm32,嵌入式硬件,单片机

void OLED_Init(void)
{
	HAL_Delay(200);
	uint8_t i = 0;
	for(i=0; i<29; i++)
	{
		OLED_WR_CMD(CMD_Data[i]);
	}
}

这次需要发送29个数据

第五步: 写刷新函数
stm32 iic dma,stm32,嵌入式硬件,单片机

void OLED_Refresh(void)
{
	refresh_flag = 1;
	HAL_I2C_Mem_Write_DMA(&hi2c1,0x78,0x40,I2C_MEMADD_SIZE_8BIT,(uint8_t *)show_mem,128*8);
}

这里直接将1024个字节的数据全部推到屏幕上,需要注意的是DMA是异步的,在推数据的过程中CPU也可以执行其他的代码,这里的refresh_flag作用就是提供一个正在刷新的标志,而且这个标志不能在HAL_I2C_Mem_Write_DMA函数之后直接清除,否则CPU在清除该标志的时候DMA仍在发数据。有可能会导致接下来的显存刷新错误,导致显示错误

第六步 在main.c中添加刷新结束中断回调函数
stm32 iic dma,stm32,嵌入式硬件,单片机

void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
	uint8_t i,j;
	for(i = 0; i < OLED_WIDE; i++)
	{
		for(j = 0; j < OLED_LENGTH; j++)
		{
			show_mem[i][j] = 0;
		}
	}
	refresh_flag = 0;
}

我这里添加在了USER CODE BEGIN 4的后面,理论上添加在哪个地方都可以。我这里的回调函数用于清空显存等待下一次刷新,并置发送结束的标志位

HAL的中断回调函数格式可以去这个文件夹下的.c文件中爬 (本来这里不想讲了,发现之前我写的文章居然没提,那我还是讲一下吧qwq)

接下来的部分不影响最终的结果,只是突然想到了讲一下0w0
stm32 iic dma,stm32,嵌入式硬件,单片机
比如这里我想看I2C的发送完成中断回调的函数格式就去stm32f4xx_hal_i2c.c文件中找,下滑找到类似这样的注释
stm32 iic dma,stm32,嵌入式硬件,单片机
例如我们这里需要写一个整块内存发送完成中断,那么就可以定义一个名为HAL_I2C_MemTxCpltCallback的函数。在HAL库刚生成的时候这个函数是弱定义,现在由用户定义了HAL库会自动调用这个函数,然后在这个文件中 ctrl+f 查找这个函数,找到该函数的弱定义
stm32 iic dma,stm32,嵌入式硬件,单片机
发现这个函数的入口参数应该是I2C_HandleTypeDef *hi2c,那么把这节函数复制粘贴到main.c中就得到了上面所定义的中断服务函数void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)

测试代码

到这里为止铺垫部分终于完成啦!接下来就是使用
在main函数中调用初始化函数
stm32 iic dma,stm32,嵌入式硬件,单片机

	OLED_Init();
	OLED_Clear();

然后再while中写一下测试用的函数
stm32 iic dma,stm32,嵌入式硬件,单片机

if(refresh_flag == 0)
{
	OLED_Showdecimal(0,0,V_value[1],1,2,12,0);
	OLED_ShowNum(50,0,adc_data[1],4,12,0);
	for(i = 0; i < ADC_BUFFER; i++)
		OLED_DrawPoint(i * 127.0f / 1024, -V_value[i] / 3.2 * 25 + 35);
	OLED_Refresh();
}

首先判断正在发送标志是否为0,接着再刷新显存
我的OLED_ShowNum之类的函数均是基于OLED_ShowChar函数写的,因此只需修改OLED_ShowChar即可,若你的代码不是这样的,那么就需要按照上述类似的方法重写函数,否则这里会发生显示错误。具体是什么样的我也不知道
不知道的同志们可以先用OLED_ShowChar函数测试一下是否成功

结束语

这是现实效果,还没得测试帧率(视频中展示的速率感觉有点慢,实际没那么慢)成功的同志们可以自己测试一下帧率噢。(我的屏幕是坏掉的,显示起来会缺行,不要在意)
stm32 iic dma,stm32,嵌入式硬件,单片机
到这里为止,我中间踩过了很多坑,例如DMA只刷新一次、刷屏全部刷在第一行、DMA刷新刷到一半显存被清空之类的,这篇文章中提到的需要注意的点、一些米奇妙妙步骤其实都是我踩的坑,也希望给不知道怎么使用DMA刷屏幕的同志们一个简单上手的机会吧。

如果发现本篇文章有错漏的,欢迎指出,我会在后面进行修改
也欢迎一些复现不了的、有不同意见的、或者有更好更优雅的方案的同志们来和我交流 0v0文章来源地址https://www.toymoban.com/news/detail-827751.html

到了这里,关于一个简单的HAL库STM32使用DMA+硬件IIC驱动0.96寸OLED的方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32(HAL库)软件IIC驱动OLED

    目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置  2.1.2 RCC配置 2.2 软件IIC引脚配置 2.3 项目生成  3、KEIL端程序整合 3.1 OLED驱动添加 3.3 主函数代 3.4 效果展示 本文通过STM32F103C8T6单片机(HAL库)通过软件IIC方式对OLED进行驱动。 2.1.1 SYS配置  2.1.2 RCC配置 首先在建立Ha

    2024年02月14日
    浏览(14)
  • [HAL]STM32 SPI+DMA驱动WS2812

    该程序是纯手敲,非Cube生成!所有代码均注释。 源码在文章后面获取 Keyword: 单线通讯、归零码、Reset、RGB顺序 RGB一共有24bit位 -相当于驱动一个灯要24bit位 -驱动若干个灯要24* n bit位,通过Reset码决定数据终止(保持) 24bit位应该如何发送? 可见: 表示低电平需要 T0H和T0L的配

    2024年02月09日
    浏览(16)
  • U8g2库的STM32硬件SPI(DMA)移植教程(HAL、OLED显示、四线SPI、DMA)

    本文教你把U8g2图形库移植到STM32上,基于STM32的硬件SPI、CubeMX U8g2库Github网址:https://github.com/olikraus/u8g2 U8g2库CSDN镜像网址:https://gitcode.net/mirrors/olikraus/u8g2?utm_source=csdn_github_accelerator 硬件准备:STM32C8T6(STM32系列芯片)、0.96寸OLED(128×64)、J-Link(或其他) 引脚连接: 出自此

    2024年02月09日
    浏览(23)
  • STM32Cubemxhal库硬件IIC驱动SHT40温湿度传感器

    STM32cubemx配置硬件IIC如图所示  SHT40的驱动代码 注意:在读sht40的寄存器之后一定要加10几个ms的延时

    2024年02月12日
    浏览(10)
  • 【STM32】 硬件IIC 驱动SSD1306(0.96 OLED模块) -- 1/4 库函数 学习

    经过了对于STM32的学习,但是大多数时候都还是以移植别人的历程然后修修改改为主。为了更好的巩固自己的学习能力。 所以此篇文章将会以记录我个人学习 硬件IIC 为案例,学习驱动IIC总线设备的能力。 1.《STM32F10x-中文参考手册》 2.《SSD1306芯片手册》 《STM32F10x-中文参考手

    2024年02月01日
    浏览(20)
  • STM32 HAL库 PWM+DMA 驱动WS2812B彩灯(STM32F030F4P6)

    博主使用STM32驱动WS2812B主要参考了这位佬的文章,因为需求问题,采用了Cortex-M0的stm32f030f4p6(16k的flash,4k的sram)来驱动,原文中写的是stm32f103c8t6,个人认为其实区别并不是很大,需要修改部分参数即可移植(cv战士申请出战)。 上图是我的一圈灯,一共8个,第一个LED的数

    2024年02月06日
    浏览(31)
  • HAL STM32 HW I2C DMA + SSD1306/SH1106驱动示例

    📍硬件I2C DMA驱动参考: https://blog.csdn.net/weixin_45065888/article/details/118225993 🔖本工程基于 STM32F103VCT6 ,驱动程序独立,可以移植到任意STM32型号上使用。 📑字体大小说明 🌿该驱动程序ASCII字符集类型大小包含:6X8、8X16,12X6 三种。其中6X8和8X16共用一个API函数调用,形参不同

    2024年02月22日
    浏览(20)
  • STM32硬件IIC使用

    虽然STM32的硬件IIC据说有设计缺陷,但是经过我的实践,至少STM32F103的硬件IIC是没问题的。这里给出STM32的硬件IIC的使用以及编程思路。 在这里给出STM32F103的硬件IIC引脚,方便查阅使用 STM32的硬件IIC我认为是非常具有借鉴意义的,它使用了事件组的一个流程。以stm32为主设备,

    2024年03月14日
    浏览(23)
  • 使用STM32CubeMX创建STM32工程(串口,硬件IIC配置)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 最近教研室的项目需要使用STM32做处理器,对环境数据进行采集处理,在拿到板子后,需要根据单片机具体型号(STM32F411CEU6)创建工程,再进行上层应用的开发。因为以前都是用的F1系列的开发板,用的

    2023年04月18日
    浏览(19)
  • stm32 hal库硬件spi(软件spi)驱动1.8寸tft—lcd屏幕

    屏幕是嵌入式开发中的一个重要的部分,cdsn上有许多解释原理的,还有很多是采用正点原子的屏幕来驱动的,对于刚刚入门不久的我们可能没有资金去购买较为昂贵的屏幕。而对于底层原理我们暂时也不必了解的那么深入,能点亮屏幕就是我们最大的快乐。 除了中景园的资

    2024年02月03日
    浏览(21)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包