本帖最后由 希岩 于 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]
|