最近一直在找可以用的VGA核来使Nios ii中的视频解码程序的解码数据能够显示出来。我首先想到了DE2和DE2-70中的TERASIC_Binary_VGA_Controller的实例,便仔细读了一下,发现其并不能显示一幅真彩的图像,只能显示两种颜色,而这两种颜色是可以选定的,这是它的其"Binary"的含义正是在此。虽然不能显示真彩图像,但它是可以显示一幅二值图像的。比方说汉字和ASCII码。
最近一直在找可以用的VGA核来使Nios ii中的视频解码程序的解码数据能够显示出来。我首先想到了DE2和DE2-70中的TERASIC_Binary_VGA_Controller的实例,便仔细读了一下,发现其并不能显示一幅真彩的图像,只能显示两种颜色,而这两种颜色是可以选定的,这是它的其"Binary"的含义正是在此。这是由于它用M4k的存储器做视频显示的缓存,只有640*480=307200 bit,一比特的信息只能显示一个二值的像素了。不过这640*480bit中0与1代表什么样的颜色是可以自己设定的,其原理恰如bmp图像的索引色。
虽然不能显示真彩图像,但它是可以显示一幅二值图像的。比方说汉字和ASCII码。以下是一些
LCD字符显示就是将字库(汉字字库、英文字库或者其他语言字库)中的字模以图形方式显示在LCD上,其显示原理和图形显示没有差别,只要把汉字当成一幅画,画在显示屏上就可以了。关键在于如何取得字符的图形,也就是字符的点阵字模。
在常用的汉字点阵字库文件。例如常用的16×16点阵HZK16文件,按汉字区位码从小到大依次存有国标区位码表中的所有汉字。
每个汉字占用32个字节,每个区为94个汉字。
在计算机中,汉字是以机内码的形式存储的,每个汉字占用两个字节:第一个字节为区码(qh),为了与ASCII码区别,范围从十六进制的0A1H开始(小于80H的为ASCII码字符),对应区位码中区码的第一区;第二个字节为位码(wh),范围也是从0A1H开始,对应某区中的第一个位码。
这样,将汉字机内码减去0A0AH就得该汉字的区位码。因此,汉字在汉字库中的具体位置计算公式为:
location = (94*(qh-1)+wh-1)*一个汉字字模占用字节数
一个汉字字模占用的字节数根据汉字库的汉字大小不同而不同。以HZK16点阵字库为例,字模中每一点使用一个二进制位(Bit)表示,如果是1,则说明此处有点,若是0,则说明没有。这样,一个16×16点阵的汉字总共需要16*16/8=32个字节表示。
字模的表示顺序为:先从左到右,再从上到下,也就是先画左上方的8个点,再是右上方的8个点,然后是第二行左边8个点,右边8个点,依此类推,画满16×16个点。因此,HZK16中汉字在汉字库中具体位置的计算公式为:(94*(qh-1)+(wh-1))*32。
汉字“房”的机内码为十六进制的“B7BF”,其中“B7”表示区码,“BF”表示位码。所以“房”的区位码为0B7BFH-0A0A0H=171FH。将区码和位码分别转换为十进制得汉字“房”的区位码为“2331”,即“房”的点阵位于第23区的第31个字的位置,相当于在文件HZK16中的位置为第32×[(23-1) ×94+(31-1)]=67136B以后的32个字节为“房”的显示点阵。
显示源代码如下:
Code
#include <stdio.h>
#include "stdlib.h"
#include "system.h"
#include "io.h"
#include "VGA.h"
#include "ascii.h"
#include "hzk16.h"
/**//*****************************************************************************
// Function name : Glib_disp_hzk16
// Description : 在(x,y)坐标处显示s中的汉字
// Return type : void
// Argument : int x : x坐标
// Argument : int y : y坐标
// Argument : char *s : 待显示字符串
*****************************************************************************/
void Glib_disp_hzk16(int x,int y,char *s)
{
char buffer[32]; // 32字节的字模缓冲区
int i,j,k;
unsigned char qh,wh;
unsigned long location;
while(*s)
{
qh=*s-0xa0; // 计算区码
wh=*(s+1)-0xa0; // 计算位码
location=(94*(qh-1)+(wh-1))*32L; // 计算字模在文件中的位置
//memcpy(buffer, &__HZK16X16__[location], 32); // 获取汉字字模
for(i=0;i<32;i++){
buffer[i]=__HZK16X16__[location+i];
}
for(i=0;i<16;i++) // 每一行
{
for(j=0;j<2;j++) // 一行两个字节
{
for(k=0;k<8;k++) // 每个字节按位显示
{
if(((buffer[i*2+j]>>(7-k)) & 0x1) != 0)
//PutPixel(x+k,y+i,colour); // 显示一位
Vga_Set_Pixel(VGA_BASE,(x+i),(y+8*(j)+k));
else
Vga_Clr_Pixel(VGA_BASE,(x+i),(y+8*(j)+k));
}
}
}
s+=2; // 下一个汉字
y+=16; // 汉字间距
}
}
/**//*****************************************************************************
// Function name : Glib_disp_ascii16x8
// Description : 在(x,y)坐标处显示s中的ASCII码字符,其余同上
*****************************************************************************/
void Glib_disp_ascii16x8(int x,int y,char *s)
{
char buffer[16]; // 16字节的字模缓冲区
int i,k;
unsigned long location;
while(*s)
{
location=(*s)*16L;
//memcpy(buffer, &__ASCII8X16__[location], 16); // 获取ASCII码字模
for(i=0;i<16;i++){
buffer[i]=__ASCII8X16__[location+i];
}
for(i=0;i<16;i++) // 每一行
{
for(k=0;k<8;k++) // 每个字节按位显示
{
if(((buffer[i]>>(7-k)) & 0x1) != 0)
//PutPixel(x+k,y+i,colour); // 显示一位
Vga_Set_Pixel(VGA_BASE,(x+i),(y+k));
else
Vga_Clr_Pixel(VGA_BASE,(x+i),(y+k));
}
}
s+=1; // 下一个字符
y+=8; // 字符间距
}
}
int main()
{
Vga_Write_Ctrl(VGA_BASE,7);//光标关
Set_Cursor_XY(100,200);//光标位置,关光标的情况下,此句可删除
Set_Cursor_Color(255,0,0);//光标颜色
Set_Pixel_On_Color(255,0,0);//前景色
Set_Pixel_Off_Color(255,255,255);//背景色
unsigned int i,j;
for(i=0;i<480;i++){//清零
for(j=0;j<640;j++){
Vga_Clr_Pixel(VGA_BASE,i,j);
}
}
Glib_disp_ascii16x8(0,0,"Hello world!!!");
Glib_disp_hzk16(16,0,"你好,中国!!!");
return 0;
}
显示效果图: