STM32 HAL 硬件IIC+DMA控制OLED


目录

  • 前言
  • 一、建立工程
  • 二、编写和移植
    • 前期准备
    • 驱动部分修改
  • 三、使用和验证
  • 结论


本文参考例程
提取码:1e64

前言

本文将介绍在STM32 HAL库下实现硬件IIC+DMA控制0.96寸 OLED屏,以及OLED图形库的移植。


STM32 HAL 硬件IIC+DMA控制OLED_第1张图片

一、建立工程

平台:野火指南者STM32F103VET6 + STM32Cube MX + Keil MDK 5

(示例)
1.选择所用MCU。
STM32 HAL 硬件IIC+DMA控制OLED_第2张图片
2.选择启用晶振STM32 HAL 硬件IIC+DMA控制OLED_第3张图片
3.选择Debug方式STM32 HAL 硬件IIC+DMA控制OLED_第4张图片
4.设置时钟树
STM32 HAL 硬件IIC+DMA控制OLED_第5张图片
5.选择合适的引脚开启I2CSTM32 HAL 硬件IIC+DMA控制OLED_第6张图片
6.加入I2Cx_TX的DMASTM32 HAL 硬件IIC+DMA控制OLED_第7张图片
7.开启12Cx_event_interruptSTM32 HAL 硬件IIC+DMA控制OLED_第8张图片
8.配置完后建立工程
STM32 HAL 硬件IIC+DMA控制OLED_第9张图片

二、编写和移植

本节主要介绍移植标准库编写的oledlib图形库过程。
oledlib库原作者:MjGame
原作例程开源地址:MjGame 的例程
本文使用的oledlib库修订者:一只程序缘
本文所要移植库的开源地址:一只程序缘 的例程

(示例):

前期准备

1.先导入所需的文件
所用Delay文件参考《无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)》

STM32 HAL 硬件IIC+DMA控制OLED_第10张图片
STM32 HAL 硬件IIC+DMA控制OLED_第11张图片
2.修改原oledlib库中的u8、u16类型;注释掉没用到的usart.h文件;将delay.h改为我们所用的Delay.h文件
STM32 HAL 硬件IIC+DMA控制OLED_第12张图片
STM32 HAL 硬件IIC+DMA控制OLED_第13张图片
STM32 HAL 硬件IIC+DMA控制OLED_第14张图片
STM32 HAL 硬件IIC+DMA控制OLED_第15张图片
STM32 HAL 硬件IIC+DMA控制OLED_第16张图片
STM32 HAL 硬件IIC+DMA控制OLED_第17张图片
在这里插入图片描述

驱动部分修改

1.在oled_driver.c、oled_driver.h中注释掉我们用不到的SPI_Configuration()、OLED_WR_Byte()、WriteCmd()、WriteDat()、OLED_ON、OLED_OFF函数
STM32 HAL 硬件IIC+DMA控制OLED_第18张图片
修改oled_driver.h:

#ifndef __OLED_DRIVER_H
#define	__OLED_DRIVER_H

//#include "stm32f10x.h"
#include "main.h"
#include "oled_config.h"
#include "oled_basic.h"

extern I2C_HandleTypeDef hi2c1;			//修改为所用对应的I2C
#define Scr12864_HI2C hi2c1

#define OLED_ADDRESS 0x78
#define OLED_WriteCom_Addr	0x00	
#define OLED_WriteData_Addr	0x40	

//#define OLED_SCLK_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_4)	//CLK
//#define OLED_SCLK_Set() GPIO_SetBits(GPIOA,GPIO_Pin_4)

//#define OLED_SDIN_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_5)	//DIN
//#define OLED_SDIN_Set() GPIO_SetBits(GPIOA,GPIO_Pin_5)

//#define OLED_RST_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_6)		//RES
//#define OLED_RST_Set() GPIO_SetBits(GPIOA,GPIO_Pin_6)

//#define OLED_DC_Clr() GPIO_ResetBits(GPIOA,GPIO_Pin_7)		//DC
//#define OLED_DC_Set() GPIO_SetBits(GPIOA,GPIO_Pin_7)
// 		     
//#define OLED_CS_Clr()  GPIO_ResetBits(GPIOA,GPIO_Pin_8)		//CS
//#define OLED_CS_Set()  GPIO_SetBits(GPIOA,GPIO_Pin_8)

//#define OLED_CMD  0	//写命令
//#define OLED_DATA 1	//写数据

//void I2C_Configuration(void);
//void I2C_WriteByte(uint8_t addr,uint8_t data);
//void SPI_Configuration(void);
//void SPI_WriterByte(unsigned char dat);
//void WriteCmd(unsigned char cmd);
//void WriteDat(unsigned char Dat);
void OLED_Init(void);
void OLED_CLS(void);
//void OLED_ON(void);
//void OLED_OFF(void);
void OLED_FILL(unsigned char BMP[]);

#endif

在oled_driver.c开头添加如下代码:

//...
extern unsigned char ScreenBuffer[SCREEN_PAGE_NUM][SCREEN_COLUMN];

uint8_t OLED_Init_CMD[] =
{
     
	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_Init_CMD里存放了OLED的初始化指令,前27个与中景园官方例程的一致,后2个指令将SSD1306的寻址方式修改为了水平寻址,能够一次性对OLED内部Graphic Display Data RAM (GDDRAM)整体更新。
(引自firestaradmin大佬的
《STM32 DMA-IIC刷新OLED屏(理论可达42+帧率)》
和Coder_BCM大佬的
《基于STM32F407的 中景园0.96寸OLED(IIC)的程序升级(DMA+IIC + 显存Buffer)》)
修改下列函数

//...
void OLED_FILL(unsigned char BMP[])
{
     
//	unsigned char i,j;
//	unsigned char *p;
//	p=BMP;

//	for(i=0;i<8;i++)
//	{
     
//		WriteCmd(0xb0+i);		//page0-page1
//		WriteCmd(0x00);			//low column start address
//		WriteCmd(0x10);	

//		for(j=0;j<128;j++)
//		{
     
//			WriteDat(*p++);
//		}
//	}
	HAL_I2C_Mem_Write_DMA(&Scr12864_HI2C, OLED_ADDRESS, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, BMP, 1024);
}


void OLED_Init(void)
{
     
//	WriteCmd(0xAE); //display off
//	WriteCmd(0x20);	//Set Memory Addressing Mode	
//	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
//	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
//	WriteCmd(0xc8);	//Set COM Output Scan Direction
//	WriteCmd(0x00); //---set low column address
//	WriteCmd(0x10); //---set high column address
//	WriteCmd(0x40); //--set start line address
//	WriteCmd(0x81); //--set contrast control register
//	WriteCmd(0xff); //亮度调节 0x00~0xff
//	WriteCmd(0xa1); //--set segment re-map 0 to 127
//	WriteCmd(0xa6); //--set normal display
//	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
//	WriteCmd(0x3F); //
//	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
//	WriteCmd(0xd3); //-set display offset
//	WriteCmd(0x00); //-not offset
//	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
//	WriteCmd(0xf0); //--set divide ratio
//	WriteCmd(0xd9); //--set pre-charge period
//	WriteCmd(0x22); //
//	WriteCmd(0xda); //--set com pins hardware configuration
//	WriteCmd(0x12);
//	WriteCmd(0xdb); //--set vcomh
//	WriteCmd(0x20); //0x20,0.77xVcc
//	WriteCmd(0x8d); //--set DC-DC enable
//	WriteCmd(0x14); //
//	WriteCmd(0xaf); //--turn on oled panel

//	WriteCmd(0x20); 
//	WriteCmd(0x00);

	HAL_I2C_Mem_Write_DMA(&Scr12864_HI2C, OLED_ADDRESS, OLED_WriteCom_Addr, I2C_MEMADD_SIZE_8BIT, OLED_Init_CMD, 29);

	OLED_CLS();
}


void OLED_CLS(void)//清屏 全部发送0x00
{
     
	unsigned char m,n;
	for(m=0;m<SCREEN_PAGE_NUM;m++)
	{
     
//		WriteCmd(0xb0+m);		//page0-page1
//		WriteCmd(0x00);			//low  column start address
//		WriteCmd(0x10);			//high column start address

		for(n=0;n<SCREEN_COLUMN;n++)
		{
     
//			WriteDat(0x00);
			ScreenBuffer[m][n] = 0;
		}
	}

	HAL_I2C_Mem_Write_DMA(&Scr12864_HI2C, OLED_ADDRESS, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, ScreenBuffer[0], 1024);
}
//...

在oled_config.c中修改DriverInit函数

//...
//初始化图形库,请将硬件初始化信息放入此中
void DriverInit(void)
{
     
//	SPI_Configuration();	//初始化接口
	OLED_Init();			//初始化配置oled
}
//...

注意到

//请将此函数放入1ms中断里,为图形提供时基
//系统时间基准主要用于FrameRateUpdateScreen()中固定帧率刷新屏幕
void OledTimeMsFunc(void)
{
     
	if(OledTimeMs != 0x00)
	{
      
		OledTimeMs--;
	}
}

故到stm32f1xx_it.c中声明并添加此函数
若未使用FreeRTOS则在滴答定时器中断中添加,反之在对应定时器中断中添加:

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
     
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
  OledTimeMsFunc();
  /* USER CODE END SysTick_IRQn 1 */
}

三、使用和验证

添加原例程中的test.c代码,并修改部分演示函数的延时部分:
STM32 HAL 硬件IIC+DMA控制OLED_第19张图片在main.c文件中包含必要的头文件

//...
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "../oled_test/test.h"
#include "../Delay/Delay.h"
#include "../oledlib/draw_api.h"
/* USER CODE END Includes */
//...

添加初始化函数并测试demo

//...
  /* USER CODE BEGIN 2 */
  delay_init();		//延时函数初始化
  InitGraph();		//OLED初始化

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
     
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	demo();			//测试demo
  }
//...

效果如下
STM32 HAL 硬件IIC+DMA控制OLED_第20张图片
STM32 HAL 硬件IIC+DMA控制OLED_第21张图片
STM32 HAL 硬件IIC+DMA控制OLED_第22张图片


结论

相比于普通的硬件IIC控制屏幕,改用DMA驱动后,刷新率得到了很大提升。其速度提升的原理为《STM32 DMA-IIC刷新OLED屏(理论可达42+帧率)》

你可能感兴趣的:(stm32,单片机,嵌入式,c语言,dma)