打印

硬件SPI驱动OLED出现奇怪的BUG

[复制链接]
1326|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Pohlen|  楼主 | 2023-5-6 10:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
设备是0.96寸OLED屏幕,7脚。商家给的资料是用软件模拟SPI通信,没有出现问题。想尝试自己啃手册,写硬件SPI驱动OLED。遇到各种奇奇怪怪的问题。
问题一
代码第一次烧录进STM32,显示内容的位置正常。但是,当设备断电再重新上电,OLED会出现字符串向左移一位,最靠前的一个字符会被挤到最后面。但是如果重新上电后,用按钮触发显示的新内容位置却是正确的。这种问题有人遇到过吗?怎么排查问题?
问题二
设置SPI波特率的问题,如果分频过多,16到256这个范围都不行,会出现花屏。只有分频值在2到8之间,才能正常点亮OLED。请问,我在OLED手册里找关键词Baud Rate,没有相关参数,这部分怎么设置才是正确的?

使用特权

评论回复

相关帖子

沙发
fxyc87| | 2023-5-6 12:06 | 只看该作者
SPI模式不对,有两个配置,一个是CLK空闲是低电平还是高电平,还有一个是第一个时钟上升沿还是下降沿,
有示波器没?分析下就行了。

使用特权

评论回复
评论
Pohlen 2023-5-7 10:47 回复TA
@Pohlen :嘴瓢了,是左移一位 
Pohlen 2023-5-7 10:38 回复TA
而且目前手上没有示波器 ,头大 Π _ Π 
Pohlen 2023-5-7 10:22 回复TA
其实CPHA和CPOL的配置我都试了一遍。闲置高电平有效,低电平无效。但是CPHA不管是1还是0,都能驱动OLED,而且还都会出现重新上电后显示内容向右移。这个地方我也很疑惑,按照OLED手册上的时序图,需要CLK上升沿有效,闲置高电平。 
板凳
henangongda123| | 2023-5-6 17:31 | 只看该作者
我遇到过OLED断电情况了,如果单片机给它发信号,它通电后会出现乱码、速度慢等问题,要保证先给OLED正常通电后单片机再操作OLED屏

使用特权

评论回复
评论
Pohlen 2023-5-7 10:25 回复TA
您的意思是在初始化OLED之前先延时一段时间,对吗?但我加了1000ms延时后还是会出现重新上电内容右移现象 
地板
Pohlen|  楼主 | 2023-5-7 11:03 | 只看该作者

硬件SPI驱动OLED出现奇怪的BUG

void OLED_CheckStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG, uint8_t status)
{
    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);        
}  

使用特权

评论回复
5
xch| | 2023-5-8 10:54 | 只看该作者
商家给的驱动代码贴上来看看

使用特权

评论回复
评论
Pohlen 2023-5-9 23:17 回复TA
感谢兄弟提醒了我,我回去跑了之前代码,也出现了类似的情况。 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++; } } }  
6
Pohlen|  楼主 | 2023-5-9 23:34 | 只看该作者

硬件SPI驱动OLED出现奇怪的BUG

终于解决的这个奇怪的BUG,敲代码不认真 真的会害死人的!!!我之前一直以为是因为修改了硬件SPI驱动后才是出现的问题,所以盯着写入数据的部分不放。
结果经过老哥提醒了以下,回去跑了之前没有改为硬件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++;
                }
        }
}


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

9

帖子

0

粉丝