0.96OLED软件实现DMA串口接收数据模拟滚屏效果

实现的滚屏效果是当一屏写满时,则清空从开头接着写,不是上移的滚屏,虽然OLED有滚屏命令,但是会带水平位移效果,并且只能提前写好数据,类似于广告牌循环播放的那种.

  首先是为OLED屏划分区域. 我选择显示的字体是6*8大小的,这样可以显示多点内容,(这是最小的尺寸了,我试过4*8,太畸形了) 所以一块屏最多显示大约168个字,然后每行可显示21,共8行

用两个坐标来定位就行.显示的时候遇到回车换行也需要跳到下一行显示

 


#define DIS_ONE_PAGE_BUF 168 //一页的字数
#define DIS_BUFFER DIS_ONE_PAGE_BUF*8 //一页的字数 size()6*8


void
OLED_Screen_Show(void){ unsigned int i,tmp,len; len=Cal_Recv_Len(OLED_Assistant.OLED_ShowBuf_Last_DLen,OLED_Assistant.OLED_ShowBuf_DLen); for(i=0;i<len;i++){ tmp=OLED_Screen_Buff[OLED_Assistant.OLED_ShowBuf_RPtr]; if(tmp=='\r'){//换行 OLED_Assistant.OLED_ShowBuf_RPtr+=2; tmp=Cal_Line_Remain(OLED_Assistant.OLED_Screen_WPtr)+1; OLED_Assistant.OLED_Screen_WPtr+=tmp; i+=2; continue; } if(OLED_Assistant.OLED_Screen_WPtr>=DIS_ONE_PAGE_BUF){ OLED_Assistant.OLED_Flush_EN=1;//屏幕写满则刷新 OLED_Assistant.OLED_Screen_WPtr=0; } if(OLED_Assistant.OLED_Flush_EN){ OLED_Clear(); OLED_Assistant.OLED_Flush_EN=0; } Cal_Screen_Axis(OLED_Assistant.OLED_Screen_WPtr,&OLED_Assistant.OLED_X,&OLED_Assistant.OLED_Y);//计算坐标 OLED_ShowChar_6x8_Pos(OLED_Assistant.OLED_X,OLED_Assistant.OLED_Y,tmp); OLED_Assistant.OLED_Screen_WPtr++; OLED_Assistant.OLED_ShowBuf_RPtr++; if(OLED_Assistant.OLED_ShowBuf_RPtr>=DIS_BUFFER){ OLED_Assistant.OLED_ShowBuf_RPtr=0; } } }

 

  第二个就是显存大小了,视情况而定,我是在串口空闲中断中把DMA接收的数据复制到显存中的,复制的时候通过显存已有长度来接着存储,当达到最大时,直接置0就行。因为前面的数据都显示过了,覆盖就覆盖了。

void USART2_IRQHandler()
{
    if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)     //接收中断
    {
        //根据应用来定接收长度,每一次空闲中断,则可以通过DMA_GetCurrDataCounter拿到本次接收数据长度
        USART_ReceiveData(USART2);
        DMA_Cmd(DMA1_Channel6,DISABLE);/* 关闭接收DMA  */
        DMA_ClearFlag(DMA1_FLAG_TC6);/* 清除标志位 */
        /* 重新设置传输数据长度, 要比真实数据长,防止覆盖*/
        udma.Recv_Len=RECV_BUF_SIZE-DMA_GetCurrDataCounter(DMA1_Channel6);
//        MemClear(udma.Send_Buffer,SEND_BUF_SIZE);
//        MemCopy(udma.Recv_Buffer,udma.Send_Buffer,udma.Recv_Len);
        OLED_Assistant.OLED_ShowBuf_Last_DLen=OLED_Assistant.OLED_ShowBuf_DLen;
        Copy_To_ShowBuff(udma);
        OLED_Assistant.OLED_Write_EN=1;
        DMA_SetCurrDataCounter(DMA1_Channel6,RECV_BUF_SIZE);  
        /* 打开DMA */
        DMA_Cmd(DMA1_Channel6,ENABLE);
//        DMA_Send_EN=1;
    }
     
}

  第三个就是对于数据的处理了,关键是要能不定长的接收,并且能不定长的显示,然后什么时候刷新,什么显示接收到的内容,就需要几个控制的变量了

typedef struct{
    /*
    OLED显存指针:用于定位显存读取位置//此指针非彼指针,只是比喻,下同
    OLED显存数据长度:用于数据存储(到达最大值,变为0,直接覆盖)
    OLED显存上一次数据长度:用于计算接收的数据长度
    OLED屏幕位置指针:用于定位当前显示位置
    OLED屏幕位置X坐标 COLUMN
    OLED屏幕位置Y坐标    ROW
    OLED刷新使能标识
    OLED显示内容使能标识
    */
    unsigned int OLED_ShowBuf_RPtr;
    unsigned int OLED_ShowBuf_DLen;
    unsigned int OLED_ShowBuf_Last_DLen;
    unsigned int OLED_Screen_WPtr;
    unsigned char OLED_X;//column (0-20)
    unsigned char OLED_Y;//row  (0-7)
    unsigned char OLED_Flush_EN;
    unsigned char OLED_Write_EN;
}OLED_Show_Handler;

extern OLED_Show_Handler OLED_Assistant;//初始化全为0就可
void Copy_To_ShowBuff(U_DMA udma){//复制DMA接收的串口数据,在空闲中断中调用
    unsigned int i;
    for(i=0;i<udma.Recv_Len;i++){
        OLED_Screen_Buff[OLED_Assistant.OLED_ShowBuf_DLen]=udma.Recv_Buffer[i];//每次接收多少数据就写入多少数据
        OLED_Assistant.OLED_ShowBuf_DLen++;
        if(OLED_Assistant.OLED_ShowBuf_DLen==DIS_BUFFER){
            OLED_Assistant.OLED_ShowBuf_DLen=0;  //重新从头开始覆盖写入,因为最前面的内容显示过了,可以覆盖
        }
    }
    
}

unsigned char Cal_Line_Remain(unsigned int arg){//计算不足一行的数据的长度,用于换行时增加屏幕指针偏移的
    unsigned char remain;
    remain=arg-(unsigned char)(arg/20)*20;
    remain=20-remain;
    return remain;
}
void Cal_Screen_Axis(unsigned int wptr,unsigned char *x,unsigned char *y){//计算需要显示的坐标
    *y=(unsigned char)(wptr/20);
    *x=wptr-(*y)*20;
}


unsigned int Cal_Recv_Len(unsigned int last_len,unsigned int new_len){//计算接收到的数据长度,用于不定长显示
    unsigned int recv_len;
    if(new_len>=last_len){  //防止循环接收时的接收指针跑到上一次接收指针前面
        recv_len=new_len-last_len;
    }
    else{
        recv_len=(DIS_BUFFER-last_len)+recv_len;
    }
    return recv_len;
}

 

posted @ 2021-03-17 23:20  pie_thn  阅读(606)  评论(0编辑  收藏  举报