STM32F103VET6
STM32 Cube IDE
本节引自STM32驱动0.96寸OLED液晶屏(12864液晶屏) —— 小牧同学
两种屏幕的引脚数不一样,左边的有7个引脚,而右边的只有6个。其次,端口的标号也不完全一样,第一个分别标为GND,VCC,D0,D1,RES,DC和CS第二个分别标为GND,VCC,SCL,SDA,RST,D/C。
GND — 接地端口
VCC — 接3.3V电源端口
D0 — CLK时钟信号(等同于上面的SCL)
D1 — 数据端口(等同于上面的SDA)
RES — 复位端口(等同于上面的RST)
DC — 数据/命令选择引脚(等同于上面的D/C)
CS — 片选引脚(低电平有效,也就是所需要接低电平,我实际试验过不接该引脚也是可以正常使用的)
GND — 接地端口
VCC — 接3.3V电源端口
SCL — CLK时钟信号端口
SDA — MOSI数据端口
RST — 复位端口
D/C — 数据/命令选择引脚
指令解读见【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的程序,修改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();
else
OLED_DC_L();
// SOFT_SPI_RW_MODE2(dat);
HAL_SPI_Transmit(&hspi1, &dat, 1, 10);
OLED_DC_H();
OLED_CS_H();
}
在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
}
再次修改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
这个帧率已经远远超过屏幕本身所能提供的刷新率了。
从【51单片机快速入门指南】4.2: SSD1306 OLED屏(0.96寸、1.3寸)的I2C控制详解和【51单片机快速入门指南】4: 软件 I2C获取控制程序。
//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);
}
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);
else
HAL_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
}
再次修改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
}