目录
- SPI版
- OLED SPI 端口定义
- 七针OLED引脚定义
- 六针OLED引脚定义
- 软件SPI
- 硬件SPI
- 启用DMA
- 帧率测试
- I2C 版
- 软件I2C
- 硬件I2C
- DMA
STM32F103VET6
STM32 Cube IDE
SPI版
OLED SPI 端口定义
本节引自STM32驱动0.96寸OLED液晶屏(12864液晶屏) —— 小牧同学
两种屏幕的引脚数不一样,左边的有7个引脚,而右边的只有6个。其次,端口的标号也不完全一样,第一个分别标为GND,VCC,D0,D1,RES,DC和CS第二个分别标为GND,VCC,SCL,SDA,RST,D/C。
七针OLED引脚定义
GND — 接地端口
VCC — 接3.3V电源端口
D0 — CLK时钟信号(等同于上面的SCL)
D1 — 数据端口(等同于上面的SDA)
RES — 复位端口(等同于上面的RST)
DC — 数据/命令选择引脚(等同于上面的D/C)
CS — 片选引脚(低电平有效,也就是所需要接低电平,我实际试验过不接该引脚也是可以正常使用的)
六针OLED引脚定义
GND — 接地端口
VCC — 接3.3V电源端口
SCL — CLK时钟信号端口
SDA — MOSI数据端口
RST — 复位端口
D/C — 数据/命令选择引脚
软件SPI
指令解读见【51单片机快速入门指南】4.2: SSD1306 OLED屏(0.96寸、1.3寸)的I2C控制详解
各引脚初始化如下,均为推挽输出:
从【51单片机快速入门指南】5:软件SPI获取软件SPI程序。
修改控制电平的函数:
GPIOx->BSRR = GPIO_Pin;GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
由于我们需要的只是单向的、半双工的SPI,只需修改部分函数即可
从SPI驱动0.96/1.3寸 OLED屏幕,易修改为DMA控制获取SPI版驱动程序。
将51特色的code改为const
修改对应引脚
修改延时函数
在主函数中添加测试程序:
如图,屏幕已轻松点亮。
帧率为180(见后文)
使用虚拟显存是时为46帧
硬件SPI
如图,使用硬件SPI
移除软件SPI的程序,修改OLED_WR_Byte函数
extern SPI_HandleTypeDef hspi1;
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{OLED_CS_L();if (cmd)OLED_DC_H();elseOLED_DC_L();
// SOFT_SPI_RW_MODE2(dat);HAL_SPI_Transmit(&hspi1, &dat, 1, 10);OLED_DC_H();OLED_CS_H();
}
再次测试:
成功点亮
帧率为410
在oled.h中启用虚拟显存
修改OLED_Refresh_Gram函数,实现1024 Byte 显存连续写入
extern SPI_HandleTypeDef hspi1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
// uint16_t i;OLED_Set_Pos(0, 128);
// for (i = 0; i < Max_Row / 8 * Max_Column; i++)
// {
// OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
// }OLED_CS_L();OLED_DC_H();HAL_SPI_Transmit(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column, 10);OLED_DC_H();OLED_CS_H();
#endif
}
再次测试,仍能成功点亮,帧率为969。
启用DMA
再次修改OLED_Refresh_Gram函数
extern SPI_HandleTypeDef hspi1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
// uint16_t i;OLED_Set_Pos(0, 128);
// for (i = 0; i < Max_Row / 8 * Max_Column; i++)
// {
// OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
// }OLED_CS_L();OLED_DC_H();
// HAL_SPI_Transmit(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column, 10);HAL_SPI_Transmit_DMA(&hspi1, OLED_GRAM[0], Max_Row / 8 * Max_Column);while(hspi1.State != HAL_SPI_STATE_READY);OLED_DC_H();OLED_CS_H();
#endif
}
帧率测试
帧率的显示:
在1ms中断中统计1s内的帧数
在每次更新屏幕内容时FPS_Count自加1
这个帧率已经远远超过屏幕本身所能提供的刷新率了。
I2C 版
软件I2C
将SCL设为推挽输出,SDA设为开漏上拉输出
从【51单片机快速入门指南】4.2: SSD1306 OLED屏(0.96寸、1.3寸)的I2C控制详解和【51单片机快速入门指南】4: 软件 I2C获取控制程序。
修改对应引脚,其他部分同SPI的步骤。
//SCL拉高 移植时需修改
void I2C_SCL_H(void)
{OLED_SCL_GPIO_Port->BSRR = OLED_SCL_Pin;
}//SCL拉低 移植时需修改
void I2C_SCL_L(void)
{OLED_SCL_GPIO_Port->BSRR = (uint32_t)OLED_SCL_Pin << 16u;
}//SDA拉高 移植时需修改
void I2C_SDA_H(void)
{OLED_SDA_GPIO_Port->BSRR = OLED_SDA_Pin;
}//SDA拉低 移植时需修改
void I2C_SDA_L(void)
{OLED_SDA_GPIO_Port->BSRR = (uint32_t)OLED_SDA_Pin << 16u;
}//读取SDA 移植时需修改
uint8_t I2C_SDA_Read(void)
{OLED_SDA_GPIO_Port->BSRR = (uint32_t)OLED_SDA_Pin << 16u;i2c_delay();return ((OLED_SDA_GPIO_Port->IDR & OLED_SDA_Pin) == 0?0:1);
}
测试:
帧率约为60帧
硬件I2C
修改OLED_WR_Byte函数
extern I2C_HandleTypeDef hi2c1;
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{if (cmd)HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, &dat, 1, 10);elseHAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteCom_Addr, I2C_MEMADD_SIZE_8BIT, &dat, 1, 10);
}
此时帧率为49
开虚拟显存模式,修改OLED_Refresh_Gram函数
extern I2C_HandleTypeDef hi2c1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
// uint16_t i;OLED_Set_Pos(0, 128);
// for (i = 0; i < Max_Row / 8 * Max_Column; i++)
// {
// OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
// }HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column, 1000);
#endif
}
发现会更慢
DMA
开中断
再次修改OLED_Refresh_Gram函数
extern I2C_HandleTypeDef hi2c1;
void OLED_Refresh_Gram(void)
{
#if OLED_BUFFER_MODE
// uint16_t i;OLED_Set_Pos(0, 128);
// for (i = 0; i < Max_Row / 8 * Max_Column; i++)
// {
// OLED_WR_Byte(OLED_GRAM[0][i], OLED_DATA);
// }
// HAL_I2C_Mem_Write(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column, 1000);while(hi2c1.State != HAL_I2C_STATE_READY);HAL_I2C_Mem_Write_DMA(&hi2c1, OLED_ADDRESS << 1, OLED_WriteData_Addr, I2C_MEMADD_SIZE_8BIT, OLED_GRAM[0], Max_Row / 8 * Max_Column);
#endif
}
如图,瓶颈应为400kHz的I2C速度