这一季,我们来玩=====>> 字符~~~~~~~~~~~~
首先我们来说一下现在大多字模是怎么样来取的.在没有OLD之前,我们大多用的是LCD液晶屏.其中大名鼎鼎 1602屏,我想大多人51入门就会玩的屏了.只要我们
搞清它的显存地址就会用了.因为大多1206屏都会有字库,所以很少人用取字模的方式取字,到了后来,带中文字库的屏越来越多应用在产中后,我们越来越多的51er
们才开始玩画图模式.如用的最多的12864屏.
好了,费话不说了,我们来说说取模后,一个字符的结构 如下图:
先说一下LCD屏用的最多的16x8结构,0到7表示一个一个8位的二进制数,从上到下,7表示最高位bit7,0表示最低位bit0.
7 7 7 7 7 7 7 7
6 6 6 6 6 6 6 6
5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
range;display:inline-block;">7 7 7 7 7 7 7 7
range;display:inline-block;">6 6 6 6 6 6 6 6
range;display:inline-block;">5 5 5 5 5 5 5 5
range;display:inline-block;">4 4 4 4 4 4 4 4
range;display:inline-block;">3 3 3 3 3 3 3 3
range;display:inline-block;">2 2 2 2 2 2 2 2
range;display:inline-block;">1 1 1 1 1 1 1 1
range;display:inline-block;">0 0 0 0 0 0 0 0
看上边,显示一个字符用16个8位的二进制字来表示.绿色底的是第一个字节,橙色底的是第二个字节,蓝色底的就是第三个字节,如些类推,红色底的就是第16个字节了,如原子老大
取的字模我标上对应的着色  打字累.偶不想全上色,只上4种大家明白就OK)
{0x00,range;display:inline-block;">0x04,0x00,0x3C,0x03,0xC4,0x1C,0x40,0x07,0x40,0x00,0xE4,0x00,0x1C,0x00,0x04},/*"A",33*/
比如这个A字,我标上颜色给大家看:
6 6 6 6 6 6 6 6
5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
6 6 6 6 6 6 6 6
5 5 5 5 5 5 5 5
4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
看!!!!!!!!!!!!!!!! 一个A字就是用八进制数里的"1"画出来的."0"代表空白.
有一个重点,如果我们取的是12X6怎么办呢??也一样,只是只有6列而已.如下:
6 6 6 6 6 6
5 5 5 5 5 5
4 4 4 4 4 4
3 3 3 3 3 3
2 2 2 2 2 2
1 1 1 1 1 1
0 0 0 0 0 0
6 6 6 6 6 6
5 5 5 5 5 5
4 4 4 4 4 4
3 3 3 3 3 3
2 2 2 2 2 2
1 1 1 1 1 1
0 0 0 0 0 0
有人问了,不是坚向不是12个bit吗?怎么有两个字节共16个bit呢? 是这样的,应为我们的寄存器只有8个位的,没有4个位的,那么用8位加4位来组是组不成的.
那怎么办呢,有办法的,我们没用4位就拿8位的来组.最下边(也就是最低3位)不用,全填上0就OK了,0是不显示的嘛.那12个字的排列也和16个字的排列一样.
如下:
如原子老大12x6的字模来做例子:
{0x00,range;display:inline-block;">0x40,0x07,0xC0,0x39,0x00,0x0F,0x00,0x01,0xC0,0x00,0x40},/*"A",33*/
6 6 6 6 6 6
5 5 5 5 5 5
4 4 4 4 4 4
3 3 3 3 3 3
2 2 2 2 2 2
1 1 1 1 1 1
0 0 0 0 0 0
6 6 6 6 6 6
5 5 5 5 5 5
4 4 4 4 4 4
3 3 3 3 3 3
2 2 2 2 2 2
1 1 1 1 1 1
0 0 0 0 0 0
黄色的部全里边填的全是0,有兴趣的同学可以安我上边的点陈画个A出来.
好了,知道结构了,写字符的代码就好写了.我写了显示的代码,如下.
//画一个12x6的字符
void asc2_1206t(u8 x,u8 y,u8 asc)
{
u8 i;
for (i=0;i<12;i=i+2)
{
OLED_GRAM[x+i][y] |= asc2_1206[asc-' '];
OLED_GRAM[x+i][y-1] |= asc2_1206[asc-' '][i+1];
}
}
//画一个16x8的字符
void asc2_1608t(u8 x,u8 y,u8 asc)
{
u8 i;
for (i=0;i<16;i=i+2)
{
OLED_GRAM[x+i][y] |= asc2_1608[asc-' '];
OLED_GRAM[x+i][y-1] |= asc2_1608[asc-' '][i+1];
}
}
//写一串 12x6的字符
void linechar_1206t(u8 x,u8 y,u8 *pd)
{
while((*pd)!='\0')
{
asc2_1206t(x,y,*pd);
pd++;
x=x+12;
}
}
//写一串 16x8的字符
void linechar_1608t(u8 x,u8 y,u8 *pd)
{
while((*pd)!='\0')
{
asc2_1608t(x,y,*pd);
pd++;
x=x+16;
}
}
哈哈,是不是很简单呀. OLED_GRAM[x+i][y] |= asc2_1608[asc-' ']; 里的==>> asc-' ' <<== 我要说一下,原子老大搞的字库是从
空格开始的,所以你要画的字符如不减去空格号的SCSI码,就会乱的,因为空格之前还有些码,但不显示我们没有用.
好,主程式,如下:
int main(void)
{
u8 i;
Stm32_Clock_Init(9);
delay_init(72);
uart_init(72,9600);
IO_Init();
while (1)
{
OLED_WR_Byte (0x81,OLED_CMD);
OLED_WR_Byte (0xff,OLED_CMD);
X_line(0,127,47);
X_line(0,127,0);
Y_line(0,0,47);
Y_line(127,0,47); //以上4行是画个框,上一季留下的.
asc2_1206t(32,7,' '+i);
asc2_1608t(82,7,' '+i);
i=i+1;
if (i>=95)i=0; //因为只有96个(从0到95)字模.
linechar_1206t(25,4,"LQQ.FOX\0");
linechar_1608t(1,2,"CHINAFOX\0");
OLED_Refresh_Gram();
delay_ms(500);
OLED_Clear();
}
}
说明一下:
OLED_WR_Byte (0x81,OLED_CMD);
OLED_WR_Byte (0xff,OLED_CMD);
是控制亮度的,我试过,很搓,做呼吸灯不明显,所以让它点到最亮而已,OLED_WR_Byte (0xff,OLED_CMD);里的0xff是最大值,如变成0x00是最小值,也就是最不亮.
好吧,丢进板子里运行.哈哈,好玩吧.全部代码你帖出来,大家可以改来玩玩.所需要的头文件原子的例程里有.
#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "FONT.h"
#define OLED_CS PCout(9)
//#define OLED_RST    Bout(14)//在MINISTM32上直接接到了STM32的复位脚!
#define OLED_RS PCout(8)
#define OLED_WR PCout(7)
#define OLED_RD PCout(6)
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
//PB0~7,作为数据线
#define DATAOUT(x) GPIOB->ODR=(GPIOB->ODR&0xff00)|(x&0x00FF); //输出
u8 OLED_GRAM[128][8];
void OLED_WR_Byte(u8 dat,u8 cmd)
{
DATAOUT(dat);
OLED_RS=cmd;
OLED_CS=0;
OLED_WR=0;
OLED_WR=1;
OLED_CS=1;
OLED_RS=1;
}
void OLED_Refresh_Gram(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
OLED_WR_Byte (0xb0+i,OLED_CMD); //设置页地址(0~7)
OLED_WR_Byte (0x00,OLED_CMD); //设置显示位置—列低地址
OLED_WR_Byte (0x10,OLED_CMD); //设置显示位置—列高地址
for(n=0;n<128;n++)OLED_WR_Byte(OLED_GRAM[n],OLED_DATA);
}
}
void OLED_Clear(void)
{
u8 i,n;
for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n]=0X00;
OLED_Refresh_Gram();//更新显示
}
void IO_Init(void)
{
RCC->APB2ENR|=1<<3; //使能PORTB时钟
RCC->APB2ENR|=1<<4; //使能PORTC时钟
JTAG_Set(SWD_ENABLE);
GPIOB->CRL=0X33333333;
GPIOB->ODR|=0XFFFF;
GPIOC->CRH&=0XFFFFFF00;
GPIOC->CRL&=0X00FFFFFF;
GPIOC->CRH|=0X00000033;
GPIOC->CRL|=0X33000000;
GPIOC->ODR|=0X03C0;
OLED_WR_Byte(0xAE,OLED_CMD); //关闭显示
OLED_WR_Byte(0xD5,OLED_CMD); //设置时钟分频因子,震荡频率
OLED_WR_Byte(80,OLED_CMD); //[3:0],分频因子;[7:4],震荡频率
OLED_WR_Byte(0xA8,OLED_CMD); //设置驱动路数
OLED_WR_Byte(0X3F,OLED_CMD); //默认0X3F(1/64)
OLED_WR_Byte(0xD3,OLED_CMD); //设置显示偏移
OLED_WR_Byte(0X00,OLED_CMD); //默认为0
OLED_WR_Byte(0x40,OLED_CMD); //设置显示开始行 [5:0],行数.
OLED_WR_Byte(0x8D,OLED_CMD); //电荷泵设置
OLED_WR_Byte(0x14,OLED_CMD); //bit2,开启/关闭
OLED_WR_Byte(0x20,OLED_CMD); //设置内存地址模式
OLED_WR_Byte(0x02,OLED_CMD); //[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
OLED_WR_Byte(0xA1,OLED_CMD); //段重定义设置,bit0:0,0->0;1,0->127;
OLED_WR_Byte(0xC0,OLED_CMD); //设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
OLED_WR_Byte(0xDA,OLED_CMD); //设置COM硬件引脚配置
OLED_WR_Byte(0x12,OLED_CMD); //[5:4]配置
OLED_WR_Byte(0x81,OLED_CMD); //对比度设置
OLED_WR_Byte(0xEF,OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮)
OLED_WR_Byte(0xD9,OLED_CMD); //设置预充电周期
OLED_WR_Byte(0xf1,OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2;
OLED_WR_Byte(0xDB,OLED_CMD); //设置VCOMH 电压倍率
OLED_WR_Byte(0x30,OLED_CMD); //[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
OLED_WR_Byte(0xA4,OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
OLED_WR_Byte(0xA6,OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示
OLED_WR_Byte(0xAF,OLED_CMD); //开启显示
OLED_Clear();
}
//画点函数
void dot(u8 x,u8 y)
{
u8 Y_page,Y_data,temp;
Y_page = y/8; //将Y座标值变在页的的数值
temp = y%8; //所在点的位置
Y_data = 1<<temp; //得到这个点在这页里的Y坐标
//OLED_GRAM[128][8];
OLED_GRAM[x][Y_page] |= Y_data;
}
//画横线函数
void X_line(u8 x0,u8 x1,u8 y) // x0为从左到右的起始点x1为结束点 注意x1一定要比x0大 否则会出错 可以在代码里做成不论大小,但复杂会影响大家的学习热情
{
u8 i;
for (i=x0;i<x1;i++)
{
dot(i,y);
}
}
//画竖线函数
void Y_line(u8 x,u8 y0,u8 y1) // 原理同画横线
{
u8 i;
for (i=y0;i<y1;i++)
{
dot(x,i);
}
}
//画一个12x6的字符
void asc2_1206t(u8 x,u8 y,u8 asc)
{
u8 i;
for (i=0;i<12;i=i+2)
{
OLED_GRAM[x+i][y] |= asc2_1206[asc-' '];
OLED_GRAM[x+i][y-1] |= asc2_1206[asc-' '][i+1];
}
}
//画一个16x8的字符
void asc2_1608t(u8 x,u8 y,u8 asc)
{
u8 i;
for (i=0;i<16;i=i+2)
{
OLED_GRAM[x+i][y] |= asc2_1608[asc-' '];
OLED_GRAM[x+i][y-1] |= asc2_1608[asc-' '][i+1];
}
}
//写一串 12x6的字符
void linechar_1206t(u8 x,u8 y,u8 *pd)
{
while((*pd)!='\0')
{
asc2_1206t(x,y,*pd);
pd++;
x=x+12;
}
}
//写一串 16x8的字符
void linechar_1608t(u8 x,u8 y,u8 *pd)
{
while((*pd)!='\0')
{
asc2_1608t(x,y,*pd);
pd++;
x=x+16;
}
}
int main(void)
{
u8 i;
Stm32_Clock_Init(9);
delay_init(72);
uart_init(72,9600);
IO_Init();
while (1)
{
OLED_WR_Byte (0x81,OLED_CMD);
OLED_WR_Byte (0xff,OLED_CMD);
X_line(0,127,47);
X_line(0,127,0);
Y_line(0,0,47);
Y_line(127,0,47);
asc2_1206t(32,7,' '+i);
asc2_1608t(82,7,' '+i);
i=i+1;
if (i>=95)i=0; //因为只有96个(从0到95)字模.
linechar_1206t(25,4,"LQQ.FOX\0");
linechar_1608t(1,2,"CHINAFOX\0");
OLED_Refresh_Gram();
delay_ms(500);
OLED_Clear();
}
}
好了,OLED我们可以毕业了,今天太晚老婆睡觉了,打开电铬铁味不好闻,就搞不成SPI模式了.反正下下季我们要学NRF24L01,到时再玩SPI吧 .
明天我们开搞更大的屏 TFT屏~~~~~~~~~~~~~~~~~~~~~~~~~~~ |