[ ST NUCLEO-U575ZI-Q 测评] HAL移植U8G2 - stm32/stm8 - 电子工程世界-论坛 (eeworld.com.cn)
[ ST NUCLEO-U575ZI-Q 测评] 时钟——U8g2 - stm32/stm8 - 电子工程世界-论坛 (eeworld.com.cn)
上面的帖子是用u8g2实现OLED的显示,为了更进一步的熟悉ssd1306的使用,以及对hal库i2c的数据发送,这段时间又继续研究了ssd1306的数据发送。
STM32U575的HAL_I2C库的输出操作函数有两种模式,一种是阻塞模式,另一种是非阻塞模式。
阻塞模式:在轮询模式下执行通信。所有数据处理的状态由同一函数返回完成转移后。
无阻塞模式:使用中断进行通信或DMA。这些函数返回传输启动的状态。数据处理的结束将通过
阻塞模式功能发送有:
HAL_I2C_Master_Transmit()
HAL_I2C_Slave_Transmit()
HAL_I2C_Mem_Write()
带中断的无阻塞模式发送有:
HAL_I2C_Master_Transmit_IT()
HAL_I2C_Slave_Transmit_IT()
HAL_I2C_Mem_Write_IT()
DMA无阻塞模式功能:
HAL_I2C_Master_Transmit_DMA()
HAL_I2C_Slave_Transmit_DMA()
HAL_I2C_Mem_Write_DMA()
因为ssd1306的驱动没有做读取函数,所以只需要用到发送即可。
1、首先创建一个ssd1306的缓存,屏幕总共有128*8 = 1024字节,所以建立一个u8 OLED_GRAM[8][128];的缓存,用于存放显存数据。
2、把缓存数据发送给ssd1306的第一版函数:
//更新显存到OLED
void OLED_Refresh(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
for(n=0;n<128;n++)
{
OLED_WR_Byte(OLED_GRAM[i][n],OLED_DATA);
}
}
}
这样的话,总共要执行设置行起始地址、设置低列起始地址、设置高列起始地址8次,执行写入从机地址、数据位、位数8*128次。等待超时时间8*3+8*128次。
3、优化为发送数据128位时用一个函数来发送:
//更新显存到OLED
void OLED_Refresh(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
//for(n=0;n<128;n++)
//{
// OLED_WR_Byte(OLED_GRAM[i][n],OLED_DATA);
//}
修改为一次发送128数据,减少了127个发送从机地址、命令位的时间。
HAL_I2C_Mem_Write(&hi2c1, oled_addr, 0x00,1, &OLED_GRAM[i][n], 128, 100);
}
}
这样的话就优化到少了8*127次发送从机地址+数据位命令的时间。
4、优化页写入命令,将所有数据做一次发送:
//在OLED初始化中修改页面寻址方式
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
//OLED_WR_Byte(0x02,OLED_CMD);//把页地址修改为水平方式(页地址0x02,水平0x00,垂直0x01)
OLED_WR_Byte(0x00,OLED_CMD);//
//更新显存到OLED
void OLED_Refresh(void)
{
/*
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
OLED_WR_Byte(0x00,OLED_CMD); //设置低列起始地址
OLED_WR_Byte(0x10,OLED_CMD); //设置高列起始地址
//for(n=0;n<128;n++)
//{
// OLED_WR_Byte(OLED_GRAM[i][n],OLED_DATA);
//}
修改为一次发送128数据,减少了127个发送从机地址、命令位的时间。
HAL_I2C_Mem_Write(&hi2c1, oled_addr, 0x00,1, &OLED_GRAM[i][n], 128, 100);
}
*/
//修改为一次发送1024数据。减少了8*4个地址位发送时间+8*4个数据位令发送时间。
HAL_I2C_Mem_Write(&hi2c1, oled_addr, 0x00,1, &OLED_GRAM[i][n], 128, 100);
}
这样的话更进一步少了发送时间。
5、上面为阻塞式发送数据,下面修改为中断发送,首先在stm32cubeMax下配置为中断事件。
然后修改发送函数为:
//更新显存到OLED
void OLED_Refresh(void)
{
HAL_I2C_Mem_Write_IT(&hi2c1, oled_addr, 0x40,1, &OLED_GRAM[0][0], 1024);
}
6、更换为DMA发送模式:
配置好后修改发送函数为:
//更新显存到OLED
void OLED_Refresh(void)
{
HAL_I2C_Mem_Write_DMA(&hi2c1, oled_addr, 0x40,1, &OLED_GRAM[0][0], 1024);
}
【总结】经过以上的估化,刷新速度明显提高,而且代码量也变得少了。