前言:I2C驱动OLED屏幕是年前开始研究的,一直困于驱动函数的编写,这里卡了大约一个星期,从一开始的若干报错到bug全改好但屏幕乱码,一直到昨天晚上才用上正确的驱动函数。由于困在不会编写,中间借用了网上许多大佬的函数,也是通过这次I2C的测评,深刻体会到驱动的重要性,未来还有许多需要学习。以下为此次的开发步骤:
1.引脚选择与配置
两个I2C1的引脚分别对应板子上的D15和D14,把SCL接屏幕的SCL引脚,SDA接屏幕的SDA引脚,VCC接板子上的3V3,GND接GND
接着配置ICACHE,SMPS,RCC,时钟树(默认配置),这里偷个懒,和串口那一篇配置一模一样
来到I2C,配置如图,只用选择I2c1,其他的默认就ok
接着生成代码
2.代码
main函数(注意添加头文件)
#include "main.h"
#include "i2c.h"
#include "icache.h"
#include "memorymap.h"
#include "gpio.h"
#include "oled.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void SystemPower_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* Configure the System Power */
SystemPower_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_ICACHE_Init();
/* USER CODE BEGIN 2 */
OLED_Init();
OLED_ShowString(1, 1, "Hello eeWorld!!!");//显示字符串
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
屏幕驱动函数
/**
* 基于硬件IIC通信的OLED驱动程序
* API完全打包好的直接选择你的硬件IIC口即可
*
*/
#include "main.h"
#include "i2c.h"
#include "stdio.h"
#include "oled_font.h"
#include "oled.h"
uint8_t OLED_Buffer[8][128]={0};
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
uint8_t *pData;
pData = &Command;
HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x00,I2C_MEMADD_SIZE_8BIT,pData,1,100);
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
uint8_t *pData;
pData = &Data;
HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x40,I2C_MEMADD_SIZE_8BIT,pData,1,100);
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置低4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置高4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED显示模拟量
* @param Line起始位置,范围0~3
* @param Colum 起始列位置,0~7
* @param CHN 汉字序列
*/
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t CHN)
{
uint8_t i;
OLED_SetCursor(Line*2, Column*16); //设置光标位置在上半部分
for (i = 0; i < 16; i++)
{
OLED_WriteData(GBK[CHN][i]); //显示上半部分内容
}
OLED_SetCursor((Line*2)+1,Column*16); //设置光标位置在下半部分
for (i = 0; i < 16; i++)
{
OLED_WriteData(GBK[CHN][i + 16]); //显示下半部分内容
}
}
/**
* @brief OLED显示模拟量
* @param Pic第几张图片,范围0~3
* @param Colum 起始列位置,0~7
*
*/
void OLED_ShowPicture(uint8_t Pic)
{
uint8_t i,j;
Pic=Pic*8;
for(j=0;j<8;j++)
{
OLED_SetCursor(j,0);//开始显示
for (i = 0; i < 128; i++)
{
OLED_WriteData(Picture[j+Pic][i]); //发送数据
}
}
}
/*
* 快速显示照片
*/
void OLED_FastShowPicture(uint8_t Pic)
{
uint8_t i,j;
Pic=Pic*8;
for(j=0;j<8;j++)
{
for (i = 0; i < 128; i++)
{
OLED_Buffer[j][i]=(Picture[j+Pic][i]); //发送数据
}
}
}
/*
* @brief 向缓冲区写入字符数据
* Line 1~4;
* Column 1~16;
*/
void OLED_FastShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_Buffer[(Line- 1) * 2][(Column-1)*8+i] = OLED_F8x16[Char - ' '][i];//显示上半部分内容
}
for (i = 0; i < 8; i++)
{
OLED_Buffer[((Line ) * 2) - 1][(Column-1)*8+i]= OLED_F8x16[Char - ' '][i+8]; //显示下半部分内容
}
}
/**
* @brief 快速的OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_FastShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_FastShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief 快速的OLED显示数字
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的字数字,范围:数字,变量
* @param Length 数字长度
* @retval 无
*/
void OLED_FastShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_FastShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/*
* @brief 向OLED屏幕发送缓存区数据到OLED的0x40寄存器位置用以显示;
*/
void Refresh()
{
uint8_t j;
uint8_t *pData;
for(j=0;j<8;j++)
{
pData = &OLED_Buffer[j][0];
OLED_SetCursor(j,0);//开始显示
HAL_I2C_Mem_Write(&Hardware_IIC_No,0x78,0x40,I2C_MEMADD_SIZE_8BIT,pData,128,0xff);
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
HAL_Delay(200);
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
.h文件 使用的时候要选择自己使用的I2C号码,这里我用的是I2C1
#ifndef __OLED_H
#define __OLED_H
#include "main.h"
#define Hardware_IIC_No hi2c1 /*选择自己使用的IIC号码*/
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);//显示字符
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);//字符串
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);//数字
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);//负数
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);//16进制数
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);//二进制数
void OLED_ShowChinese(uint8_t Line, uint8_t Column, uint8_t CHN);
void OLED_ShowPicture(uint8_t Pic);//满屏幕显示照片
void OLED_FastShowPicture(uint8_t Pic);
void OLED_FastShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_FastShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_FastShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void Refresh();
void DMA_Refresh();
uint32_t OLED_Pow(uint32_t X, uint32_t Y);
#endif
把文件添加进工程,在IDE点击左上角的文件,再点击导入,会出现下面的选项,选择文件夹(因为要把你存放的OLED驱动文件的.h和.c文件添加进来),点击下一步
然后把文件放到对应的工程目录,按照图示位置添加
这里也有汉字的驱动,感兴趣的小伙伴可以用汉字取模软件试试显示汉字
最终的效果: