本文参考例程
提取码:1e64
本文将介绍在STM32 HAL库下实现硬件IIC+DMA控制0.96寸 OLED屏,以及OLED图形库的移植。
平台:野火指南者STM32F103VET6 + STM32Cube MX + Keil MDK 5
(示例)
1.选择所用MCU。
2.选择启用晶振
3.选择Debug方式
4.设置时钟树
5.选择合适的引脚开启I2C
6.加入I2Cx_TX的DMA
7.开启12Cx_event_interrupt
8.配置完后建立工程
本节主要介绍移植标准库编写的oledlib图形库过程。
oledlib库原作者:MjGame
原作例程开源地址:MjGame 的例程
本文使用的oledlib库修订者:一只程序缘
本文所要移植库的开源地址:一只程序缘 的例程
(示例):
1.先导入所需的文件
所用Delay文件参考《无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)》
2.修改原oledlib库中的u8、u16类型;注释掉没用到的usart.h文件;将delay.h改为我们所用的Delay.h文件
1.在oled_driver.c、oled_driver.h中注释掉我们用不到的SPI_Configuration()、OLED_WR_Byte()、WriteCmd()、WriteDat()、OLED_ON、OLED_OFF函数
修改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代码,并修改部分演示函数的延时部分:
在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
}
//...
相比于普通的硬件IIC控制屏幕,改用DMA驱动后,刷新率得到了很大提升。其速度提升的原理为《STM32 DMA-IIC刷新OLED屏(理论可达42+帧率)》