Uno 驱动SPI OLED中文显示-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 13795|回复: 7

Uno 驱动SPI OLED中文显示

[复制链接]
发表于 2017-2-27 13:23 | 显示全部楼层 |阅读模式
本帖最后由 希岩 于 2017-2-27 13:23 编辑
采用1.3寸带中文字库的OLED白屏,显示一些中文字符。
所需材料:杜邦线、Arduino UNO R3、1.3寸OLED屏(带中文芯片)
接线:代码中包含。
所显示结果如图所示。


[kenrobot_code] #include <Arduino.h>
#include <avr/pgmspace.h>
   
const unsigned char table[]  PROGMEM ={"清风过境,带着浓浓的秋意催开了将绽未绽的寒菊,逗笑了娃娃的笑脸,秋后,枝头缓缓飘落的秋叶铺满了整条不算宽敞的小路,偶尔三两片落叶调皮的跳着名为永不再见的舞曲,眨眼间,眉宇便染上淡淡的哀愁。\
  雨后的天总是带有微微的潮意,雨后的天空拥有着不同以往的清新的气息,独自一个人漫步在那落叶铺就的叶之路上,一路上听着那声音,一切仿佛都安静下来了,在这里,能感受到每一朵的呼吸声和水滴一滴一滴的低落声,这里的一切都静得那般不真实,哪怕不真,也不愿意打破心中这片刻的宁静,只想在这里一世长安,静观浮生。\
  轻捧着盛放的花朵,寻一僻静之地,嗅着那清浅的花香,斜依着参天古树,将手中的百花散落在古树旁,身下是枯软的草地,那种舒适的感觉不禁想让人快点进入梦乡。\
  随手拾起手边的一片残叶,发现残叶上那清晰的纹路,是如此的浑然天成,那是用怎样的笔才可以勾勒出这清晰的细枝末节,我们这个繁华的世界与这个微妙的世界到底有什么微妙的关系呢?\
  枯藤、老树、昏鸦,夕阳的余晖静静地斜映而下,那轮将隐未隐的红日,在这般温暖和安静的环境里,竟然明白了一些许久未明白的,有时,不是我们不够好,只是我们不愿意的往前看,我们只愿意在那个封闭的世界里,在原地踏步,总是在自怨自艾.\
  在逐渐的成长中是我们给自己给自己造成了过多的枷锁,终于到自己不能承受的地步,只得缩在一方天地,苟延残喘的活着.\
  我们总是在自己逼自己,总是在自己的世界蒙上一层薄纱,看不清前路的自己,总是在一圈圈的打转……\
  人,聪明吧,却又是极笨的,有时间一个聪明的人倒不如一个愚人活的自在,活的快乐无忧,我们无时无刻的都在羡慕别人,却怎么也不肯还自己一个清晰的世界和生活,固执的活着那个迷茫的世界,受尽苦楚.\
  有些人,有些事,该散就散,毕竟天下无不散的宴席,又或者说这次的分离,这次的失望与挫败,都是为了下一次最美的相逢。\
  与其苦苦的困在曾经的回忆里,倒不如抬头望望天,垂眸赏三两只锦鲤,与月常伴,画船听雨眠。\
  白光过隙,花开半夏,其实,人的一生极其短暂,与其为难自己,倒不如做一个手执书卷,阅经抚琴的闲雅之人,于落花深处,静候半夏"};






/****************************************************************************   
//!!本程序只供学习使用,未经作者许可,不得用于其它任何用途
//  作    者   : 希岩
//  生成日期   : 2017-2-27
//  功能描述   : 字库版 OLED SPI接口演示例程(Atmega328系列)
//  说    明:
               OLED驱动芯片:  SSH1106
               单片机      : ATMEGA328P-PU
               晶振        : 片外16Mhz石英晶振
               工作频率    : 8Mhz
//========================以下为OLED显示所用到的接口===========================
//              GND       电源地
//              VCC       接5V或3.3v电源
//              CLK       数字引脚13 (PB5)
//              MOSI      数字引脚11 (PB3)
//              DC        数字引脚8  (PB0  数据/指令)
//              CS1       数字引脚10 (PB2  OLED片选)
//              FSO       数字引脚12 (PB4  MISO)
//              CS2       数字引脚9  (PB1  字库片选)              

********************************************************************************/
#include <Arduino.h>
#include <avr/pgmspace.h>
#include "chartable.c"

//数据类型宏定义
typedef unsigned char       uint08;
typedef signed   char       sint08;
typedef unsigned int        uint16;
typedef signed   int        sint16;
typedef unsigned long       uint32;
typedef const unsigned char cuint08;



#define CS1L  PORTB&=0xfb      //CS1低
#define CS1H  PORTB|=0x04      //CS1高
#define CS2L  PORTB&=0xfd      //CS2低
#define CS2H  PORTB|=0x02      //CS2高
#define DCL() PORTB&=0xfe;     //DC=0 PB0=0
#define DCH() PORTB|=0x01;     //DC=1 PB0=1

//-----------------------------------------------------------------------------
//初始化时钟-------------------------------------------------------------------
void Init_Clock(void)
{
//OSCCAL = 0x96;                 //时钟矫正至8.0Mhz
CLKPR  = 0x80;                   //时钟分频器使能CLKPR_CLKPCE  
CLKPR  = 0x01;                   //系统时钟2分频,p39(0>1;1>2;...8>256)
}

//-----------------------------------------------------------------------
void Init_Port(void)
{ DDRB   = (1<<DDB2)|(1<<DDB1)|(1<<DDB0);              //PB2 PB1作为片选输出
// DDRB  |= 0x01;                             //DC输出
  PORTB  = 0xff;
// PORTB  = 0xff;
}
//初始化定时器------------------------------------------------------------
void Init_Timer0(void)
{ PRR =0b01001001;         //TC1,2抑制,adc抑制,page34,功耗抑制寄存器
  TCCR0A  = 0x00;          //正常模式
  TCCR0B  = 0x04;          //预分频器256分频,每次计数32us,page91
}
void SPI_High_Rate(void)
{
  //最高操作速率不能高于25Mbps
  SPCR &= 0x5C;   //BR=busclk/(SPPR *SPR )=8M/4=2M ,page147
   //SPCR = (1<<SPE)|(1<<MSTR);              
}
//SPI主机初始化,低速模式125k==========================================================
void SPI_Init_M0(void)
{                                          //SPI模式0
  DDRB|= (1<<DDB3)|(1<<DDB5);              //设置MOSI 和SCK 为输出,其他为输入
  SPCR = 0b01010001;                       //使能SPI 主机模式,时钟模式为下降沿,结束沿采样,设置时钟速率为fck/16
}
//SPI主机初始化,低速模式125k==========================================================
void SPI_Init_M3(void)
{                                           //SPI模式3
  DDRB|= (1<<DDB3)|(1<<DDB5);               //设置MOSI 和SCK 为输出,其他为输入
  SPCR = 0b01011001;                        //使能SPI 主机模式,时钟模式为下降沿,结束沿采样,设置时钟速率为fck/16
}

//======================================================================
//延时函数
void delayms(uint16 u16count)
{uint16 count,i;
for(i=0;i<u16count;i++)
  {count=TCNT0;                             //读取计数器0
   while((TCNT0-count)<32) asm("wdr");      //每次约1ms
  }
}

//SPI数据传输,采用轮询=========================================================
uint08 SPI_Tran_Byte(uint08 data)
{
SPDR = data;                                //启动数据传输
while(!(SPSR & (1<<SPIF)));                 //等待传输结束
return SPDR;                               //接收字符
}

//写指令到oled模块==============================================================
void tran_cmd_oled(uint08 data)   
{
  DCL();
  _NOP();
  (void)SPI_Tran_Byte(data);
}

//写数据到oled模块==============================================================
void tran_dat_oled(uint08 data)   
{
  DCH();
  _NOP();      
  (void)SPI_Tran_Byte(data);
}

//OLED模块初始化================================================================
void Initial_oled(void)
{
  CS1L;                 //CS1低
  CS2H;                 //CS2高
   
  delayms(20);        
  tran_cmd_oled(0xAE); //显示关
  tran_cmd_oled(0x20); //设置内存寻址模式
  tran_cmd_oled(0x10); //00,水平寻址模式;01,垂直寻址模式;10,页寻址模式(RESET);11,无效
  tran_cmd_oled(0xb0); //设置页寻址模式的页起始地址,0-7
  tran_cmd_oled(0xc8); //设置COM口输出扫描方向
  tran_cmd_oled(0x02); //设置y的低地址
  tran_cmd_oled(0x10); //设置y的高地址
  tran_cmd_oled(0x40); //设置开始行地址
  tran_cmd_oled(0x81); //设置对比控制寄存器
  tran_cmd_oled(0x7f);
  tran_cmd_oled(0xa1); //设置段 0 - 127
  tran_cmd_oled(0xa6); //设置正常显示
  tran_cmd_oled(0xa8); //设置多路复用率(1 -64)
  tran_cmd_oled(0x3F); //
  tran_cmd_oled(0xa4); //0xa4,输出跟踪存储器内容;0xa5,输出忽略存储器内容
  tran_cmd_oled(0xd3); //设置显示偏移
  tran_cmd_oled(0x00); //不偏移
  tran_cmd_oled(0xd5); //设置显示时钟分频系数/晶振频率
  tran_cmd_oled(0xf0); //设置分频系数
  tran_cmd_oled(0xd9); //设置预充电周期
  tran_cmd_oled(0x22); //
  tran_cmd_oled(0xda); //设置端口硬件配置
  tran_cmd_oled(0x12);
  tran_cmd_oled(0xdb); //设置vcomh
  tran_cmd_oled(0x20); //0x20,0.77xVcc
  tran_cmd_oled(0x8d); //DC-DC使能
  tran_cmd_oled(0x14); //
  tran_cmd_oled(0xaf); //-打开Oled面板
  CS1H;                    
}

//设置行和列================================================================
void Oled_Position(uint08 x,uint08 y)
{
  tran_cmd_oled(0xb0+x);                    //设置行
  tran_cmd_oled(((y&0xf0) >> 4)|0x10);    //设置列地址的高4位
  tran_cmd_oled((y&0x0f)|0x00);           //设置列地址的低4位
}

//全屏填充=================================================================
void Fill_screen(uint08 dat)
{
  uint08 i,j;
  CS2H;
  CS1L;              
  for(i=0;i<8;i++)
  {
    tran_cmd_oled(0xb0+i);
    tran_cmd_oled(0x02);
    tran_cmd_oled(0x10);
    for(j=0;j<128;j++)
     tran_dat_oled(dat);        //全部写dat
   
  }
    // Oled_Position(0,1);               //初始化位置
  CS1H;
}

/*从相关地址(addrHigh:地址高字节,addrMid:地址中字节,addrLow:地址低字节)中连续读出DataLen个字节的数据到 pBuff的地址*/
/*连续读取*/
void Get_bytes_from_ROM(uint08 addrHigh,uint08 addrMid,uint08 addrLow,uint08 *pBuff,uint08 DataLen )
{  
  uint08 i;
  CS2L;                        //字库芯片片选
  CS1H;
  (void)SPI_Tran_Byte(0x03);   //提取数据命令
  (void)SPI_Tran_Byte(addrHigh);
  (void)SPI_Tran_Byte(addrMid);
  (void)SPI_Tran_Byte(addrLow);
  for(i=0;i<DataLen;i++)
      *(pBuff++) =SPI_Tran_Byte(0xff);
  CS2H;

}
//显示16x16点阵图像、汉字、生僻字或16x16点阵的其他图标
//x表示行,y表示列列变化不连续
void Dis_grap_16x16(uint08 x,uint08 y,uint08 *dp)
{
  uint08 i,j;
  CS1L;
  for(j=0;j<2;j++,x++)
  {
    Oled_Position(x,y+1);
    for(i=0;i<16;i++,dp++)
    tran_dat_oled(*dp);         //写数据到LCD,每写完一个8位的数据后列地址自动加1
  }
  CS1H;
}
/*显示8x16点阵图像、ASCII, 或8x16点阵的自造字符、其他图标*/
void Dis_grap_8x16(uint08 x,uint08 y,uint08 *dp)
{
  uint08 i,j;
  CS1L;                  
  for(j=0;j<2;j++,x++)
  {
   Oled_Position(x,y+1);
   for(i=0;i<8;i++,dp++)
    tran_dat_oled(*dp);         /*写数据到LCD,每写完一个8位的数据后列地址自动加1*/
   
  }
  CS1H;
}

/*****************************************************************
功能:显示GB2312汉字字符
x行,取值范围:0,2,4,6
y列,取值范围:1—128
ch[] 需要输出的字符串
StrAdd 需要输出的字符地址
返回值 0 完成,非0 页满
*******************************************************************/


uint16 Dis_GB2312_Str(uint08 x,uint08 y,cuint08 ch2[] PROGMEM,uint16 StrAdd)
{ uint32 fontadd=0;                     //32位数据地址
  uint08 addrHigh,addrMid,addrLow ;
  uint08 fontbuf[32];
  uint08 CharNow,CharNext;
  
  CharNow =  pgm_read_byte_near(table+StrAdd);
  CharNext=  pgm_read_byte_near(table+StrAdd+1);

  while(CharNow)                       //数据值不为0
  {
    if((CharNow>175)&&(CharNow<248)&&(CharNext>160))
    {           
  //国标简体(GB2312)汉字在晶联讯字库IC中的地址由以下公式来计算:
  //Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;BaseAdd=0
  //由于担心8位单片机有乘法溢出问题,所以分三部取地址
      fontadd = (CharNow-176)*94;
      fontadd += (CharNext-161)+846;
      fontadd = fontadd*32;
      
      addrHigh =(fontadd&0xff0000)>>16;  //地址的高8位,共24位
      addrMid =(fontadd&0xff00)>>8;      //地址的中8位,共24位
      addrLow =fontadd&0xff;              //地址的低8位,共24位
      
      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,32 );//取32个字节的数据,存到"fontbuf[32]"
      Dis_grap_16x16(x,y,fontbuf);                             //显示汉字到OLED上,y为页地址,x为列地址,fontbuf[]为数据
      StrAdd+=2;                        //地址加2
      if(y<=112) y+=16;
      else if(x<=4) {x+=2;y=1;}         //到头了换行
          else return StrAdd;           //返回当前地址
    }
    else if((CharNow>160)&&(CharNow<164)&&(CharNext>160))
    {           
     //Address = ((MSB - 0xa1) * 94 + (LSB - 0xA1))*32+ BaseAdd;BaseAdd=0
      fontadd = (CharNow- 0xa1)*94;
      fontadd += (CharNext-0xa1);
      fontadd = fontadd*32;
      
      addrHigh = (fontadd&0xff0000)>>16;  
      addrMid = (fontadd&0xff00)>>8;     
      addrLow = fontadd&0xff;   
            
      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,32 );
      Dis_grap_16x16(x,y,fontbuf);
      StrAdd+=2;
      if(y<=112) y+=16;
      else if(x<=4) {x+=2;y=1;}     //到头了换行
           else return StrAdd;       //返回当前地址
    }
    else if((CharNow>31)&&(CharNow<127))  
    {           
      fontadd = CharNow- 0x20;
      fontadd = fontadd*16;
      fontadd = fontadd+0x3cf80;      
      addrHigh = (fontadd&0xff0000)>>16;
      addrMid = (fontadd&0xff00)>>8;
      addrLow = fontadd&0xff;

      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,16 );
      Dis_grap_8x16(x,y,fontbuf);
            StrAdd++;
      if(y<=120) y+=8;
      else if(x<=4) {x+=2;y=1;}        //到头了换行
          else return StrAdd;          //返回当前地址
    }
   CharNow =  pgm_read_byte_near(table+StrAdd);              //读取下个数据
   CharNext=  pgm_read_byte_near(table+StrAdd+1);

  }
  return 0;
  
}
/*
uint16 Dis_GB2312_Str(uint08 x,uint08 y,uint08 ch[],uint16 StrAdd)
{   uint32 fontadd=0;                     //32位数据地址
  uint08 addrHigh,addrMid,addrLow ;
  uint08 fontbuf[32];
      
  while(ch[StrAdd])                       //数据值不为0
  {
    if((ch[StrAdd]>175)&&(ch[StrAdd]<248)&&(ch[StrAdd+1]>160))
    {           
                                              //国标简体(GB2312)汉字在晶联讯字库IC中的地址由以下公式来计算:
                                              //Address = ((MSB - 0xB0) * 94 + (LSB - 0xA1)+ 846)*32+ BaseAdd;BaseAdd=0
                                              //由于担心8位单片机有乘法溢出问题,所以分三部取地址
      fontadd = (ch[StrAdd]-176)*94;
      fontadd += (ch[StrAdd+1]-161)+846;
      fontadd = fontadd*32;
      
      addrHigh =(fontadd&0xff0000)>>16;  //地址的高8位,共24位
      addrMid =(fontadd&0xff00)>>8;      //地址的中8位,共24位
      addrLow =fontadd&0xff;              //地址的低8位,共24位
      
      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,32 );//取32个字节的数据,存到"fontbuf[32]"
      Dis_grap_16x16(x,y,fontbuf);                             //显示汉字到OLED上,y为页地址,x为列地址,fontbuf[]为数据
      StrAdd+=2;                        //地址加2
      if(y<=112) y+=16;
      else if(x<=4) {x+=2;y=1;}         //到头了换行
          else return StrAdd;           //返回当前地址
    }
    else if((ch[StrAdd]>160)&&(ch[StrAdd]<164)&&(ch[StrAdd+1]>160))
    {           
                                               //Address = ((MSB - 0xa1) * 94 + (LSB - 0xA1))*32+ BaseAdd;BaseAdd=0
      fontadd = (ch[StrAdd]- 0xa1)*94;
      fontadd += (ch[StrAdd+1]-0xa1);
      fontadd = fontadd*32;
      
      addrHigh = (fontadd&0xff0000)>>16;  
      addrMid = (fontadd&0xff00)>>8;     
      addrLow = fontadd&0xff;   
            
      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,32 );
      Dis_grap_16x16(x,y,fontbuf);
      StrAdd+=2;
      if(y<=112) y+=16;
      else if(x<=4) {x+=2;y=1;}     //到头了换行
          else return StrAdd;       //返回当前地址
    }
    else if((ch[StrAdd]>31)&&(ch[StrAdd]<127))  
    {           
      fontadd = ch[StrAdd]- 0x20;
      fontadd = fontadd*16;
      fontadd = fontadd+0x3cf80;      
      addrHigh = (fontadd&0xff0000)>>16;
      addrMid = (fontadd&0xff00)>>8;
      addrLow = fontadd&0xff;

      Get_bytes_from_ROM(addrHigh,addrMid,addrLow,fontbuf,16 );
      Dis_grap_8x16(x,y,fontbuf);
            StrAdd++;
      if(y<=120) y+=8;
      else if(x<=4) {x+=2;y=1;}        //到头了换行
          else return StrAdd;          //返回当前地址
    }

  }
  return 0;
  
}

*/
uint08 mychar;
uint16 add=0;               //位置

void setup() {

  asm("cli");                                             //中断禁用
  Init_Clock();
  Init_Port();
  Init_Timer0();
  SPI_Init_M0();                                          //SPI初始化时钟格式3  
  Initial_oled();
  //asm("sei");                                           //中断使能
  
  CS2H;
  CS1L;
  asm("nop");
  Fill_screen(0xff);                                    //全屏填充
  delayms(500);
  Fill_screen(0);                                       //清屏  
  delayms(100);  
  //Serial.begin(38400);


}

void loop() {
     asm("wdr");
    add=0;
    do
    {
      
   
    add=Dis_GB2312_Str(0,1,table,add);           //在第1行,第1列,显示表中的数据
    delayms(2000);
    Fill_screen(0);                                    //清屏
    //Serial.println(add);
    } while(add);


}[/kenrobot_code]


IMG_20170227_130708.jpg
IMG_20170227_130654.jpg
 楼主| 发表于 2017-2-27 13:26 | 显示全部楼层
需要注意的是,代码的FLASH存储的数据是包含在另一个文件中的,编译时不要用Arduino IDE打开,保留其ANSI编码形式
发表于 2017-2-27 15:25 | 显示全部楼层
谢谢分享                        
发表于 2017-3-6 10:29 | 显示全部楼层
楼主这个是中景园电子买的吗?
 楼主| 发表于 2017-3-7 21:55 | 显示全部楼层
Qunicy 发表于 2017-3-6 10:29
楼主这个是中景园电子买的吗?

是的呢----
发表于 2017-6-1 10:54 | 显示全部楼层
楼主代码注释很全 谢谢分享
 楼主| 发表于 2017-6-10 15:16 | 显示全部楼层
派灃 发表于 2017-6-1 10:54
楼主代码注释很全 谢谢分享

不客气
发表于 2017-7-26 17:02 | 显示全部楼层
想问一下楼主,汉字是用字模提取的点阵来表示的吗?还是直接把你写的汉字保存成.c文件?
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 13:55 , Processed in 0.124981 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表