21ic问答首页 - 硬件SPI驱动OLED出现奇怪的BUG
硬件SPI驱动OLED出现奇怪的BUG
Pohlen2023-05-06
设备是0.96寸OLED屏幕,7脚。商家给的资料是用软件模拟SPI通信,没有出现问题。想尝试自己啃手册,写硬件SPI驱动OLED。遇到各种奇奇怪怪的问题。
问题一
代码第一次烧录进STM32,显示内容的位置正常。但是,当设备断电再重新上电,OLED会出现字符串向左移一位,最靠前的一个字符会被挤到最后面。但是如果重新上电后,用按钮触发显示的新内容位置却是正确的。这种问题有人遇到过吗?怎么排查问题?
问题二
设置SPI波特率的问题,如果分频过多,16到256这个范围都不行,会出现花屏。只有分频值在2到8之间,才能正常点亮OLED。请问,我在OLED手册里找关键词Baud Rate,没有相关参数,这部分怎么设置才是正确的?
问题一
代码第一次烧录进STM32,显示内容的位置正常。但是,当设备断电再重新上电,OLED会出现字符串向左移一位,最靠前的一个字符会被挤到最后面。但是如果重新上电后,用按钮触发显示的新内容位置却是正确的。这种问题有人遇到过吗?怎么排查问题?
问题二
设置SPI波特率的问题,如果分频过多,16到256这个范围都不行,会出现花屏。只有分频值在2到8之间,才能正常点亮OLED。请问,我在OLED手册里找关键词Baud Rate,没有相关参数,这部分怎么设置才是正确的?
赞0
结果经过老哥提醒了以下,回去跑了之前没有改为硬件SPI驱动OLED的代码,结果也出现相同的情况。于是就锁定到了一段代码上。
其实,在改为硬件驱动时,我已经修改了显示数字的函数,因为之前的函数要传输入数字多少位的参数。在函数中添加一个循环,遍历出整数多少位。
for循环里uint8_t i = 0, j ;其中j忘记初始化了,这个罪魁祸首!下面是源代码
void OLED_ShowNum2(uint8_t Line, uint8_t Column, u32 num)
{
//写一个不用输入数字长短,就能显示数字的版本
uint8_t flag = 0, temp;
for(uint8_t i = 0,j ;i < 10; i++ ){ //j忘记初始化
temp = num / oled_pow(10, 10 - i - 1) % 10;
if(flag == 0){
if(temp == 0) continue;
else flag = 1;
}
if(flag == 1){
OLED_ShowChar(Line, Column + j, temp + '0');
j++;
}
}
}
评论
2023-05-09
赞0
评论
2023-05-08
感谢兄弟提醒了我,我回去跑了之前代码,也出现了类似的情况。 void OLED_ShowNum2(u8 Line, u8 Column, u32 num) { //写一个不用输入数字长短,就能显示数字的版本 uint8_t flag = 0, temp; for(uint8_t i = 0,j = 0;i < 10; i++ ){ //j忘记初始化 temp = num / oled_pow(10, 10 - i - 1) % 10; if(flag == 0){ if(temp == 0) continue; else flag = 1; } if(flag == 1){ OLED_ShowChar(Line, Column + j, temp + '0'); j++; } } }
赞0
{
uint32_t Timeout;
Timeout = 10000;
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == status)
{
Timeout --;
if (Timeout == 0)
{
//在实际项目中,当然不式直接退出就行了,应该做一些相应的错误处理操作
break;
}
}
}
//向SSD1106写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
{
//CS低电平有效
OLED_CS_Clr();
if(cmd) //命令0还是数据1
OLED_DC_Set();
else
OLED_DC_Clr();
// u8 i=0; // 测试
// while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) != RESET) //等待BSY为0,发送下一个数据
// {
// i++;
// if(i>200) break;
// }
//OLED_CheckStatus(SPI1, SPI_I2S_FLAG_TXE, RESET);//放在这上面,屏幕点不亮
//OLED_CheckStatus(SPI1, SPI_I2S_FLAG_BSY, RESET);//放在这上面,屏幕点不亮
SPI_I2S_SendData(SPI1, dat);//不太明白发送的是uint8_t数据,但是函数用uint16_t类型接收
OLED_CheckStatus(SPI1, SPI_I2S_FLAG_BSY, RESET);//只有放下面这两个才生效
//OLED_CheckStatus(SPI1, SPI_I2S_FLAG_TXE, RESET);
OLED_DC_Set();
OLED_CS_Set();
}
void OLED_SPI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE);//使能A端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;//初始化CS DC和RES
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_1|GPIO_Pin_2);
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;//2到8可以,16到256就不行了
SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;//CPHA=0,CPOL=0 或 CPHA=1,CPOL=0 试了下都可以
SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
SPI_InitStruct.SPI_CRCPolynomial = 0;//看了好多代码都填7,不明白
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;//看时序图,应该是高位先行
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;//手动
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
//SPI_SSOutputCmd(SPI1, ENABLE);
//Delay_ms(100);
OLED_RST_Set();
Delay_ms(100);
OLED_RST_Clr();
Delay_ms(200);
OLED_RST_Set();
OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel
OLED_WR_Byte(0x02,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping 0xa0左右反置 0xa1正常
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction 0xc0上下反置 0xc8正常
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
OLED_WR_Byte(0X7F,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
//OLED_WR_Byte(0xAF,OLED_CMD); /*display ON*/
OLED_Clear();
Delay_ms(100);
OLED_Set_Pos(0,0);
}
评论
2023-05-07
赞0
评论
2023-05-06
您的意思是在初始化OLED之前先延时一段时间,对吗?但我加了1000ms延时后还是会出现重新上电内容右移现象
赞0
有示波器没?分析下就行了。
评论
2023-05-06
@Pohlen :嘴瓢了,是左移一位
而且目前手上没有示波器 ,头大 Π _ Π
其实CPHA和CPOL的配置我都试了一遍。闲置高电平有效,低电平无效。但是CPHA不管是1还是0,都能驱动OLED,而且还都会出现重新上电后显示内容向右移。这个地方我也很疑惑,按照OLED手册上的时序图,需要CLK上升沿有效,闲置高电平。
您需要登录后才可以回复 登录 | 注册