汉字点阵及OLED屏显

点阵汉字的字模读取与显示
(1)原理
汉字字模通常以点阵的形式存储,每个点表示一个像素。字模文件中的二进制数据可按照特定格式解析,以获取每个汉字的点阵信息。使用C++的文件操作功能,打开字库文件,读取其中的字模数据。解析文件,提取汉字的点阵信息。 将读取的字模数据转换为可用的数据结构,例如数组或二维数组,表示每个汉字的像素布局。

汉字的区位码是按照汉字内码的顺序存储在16×16的点阵字库中,共有94区,每区94个汉字,总计94×94个汉字。区位码由四个阿拉伯数字组成,前两位为区号,后两位为位号。
机内码与区位码略有不同,为了避免与基本ASCII码冲突,对区码和位码进行处理:先加上20H,再加上80H或A0H。机内码用两个字节表示,高位字节为区码处理后的值,低位字节为位码处理后的值,范围为A1H-FEH。
点阵字库存储汉字的字模信息,每个字节的每个位表示一个汉字的点,横向矩阵字库常用的形式。对于16×16的矩阵,每个汉字需要32个字节来表示,其中每两个字节代表一行的16个点,共16行。在Ubuntu中,可以使用C/C++或Python调用OpenCV库来显示一张图片。

通过汉字的机内码高位字节和低位字节,可以获取对应的区码和位码。具体关系为:

机内码高位字节 = 区码 + 20H + 80H(或区码 + A0H)
机内码低位字节 = 位码 + 20H + 80H(或位码 + AOH)
反过来,如果已知机内码,可以根据以下关系获取区位码:

区码 = 机内码高位字节 - A0H
位码 = 机内码低位字节 - AOH
有了区位码,就可以在汉字库中找到对应的字模。具体方式是计算该汉字的偏移地址:
[ \text{偏移地址} = (\text{区码}-1) \times 94 \times \text{一个字占用的字节数} + \text{位码} \times \text{一个字占用的字节数} ]

这样,就能根据机内码在汉字库中找到相应汉字的字模数据。
在ubuntu下用C++调用OpenCV显示图片文字(这里之前另一门课已经下载过OpenCV,这里就不详细介绍了)
将需要显示的图片和需要的文件存在一个新的文件夹里
在文件夹终端里创建word.cpp文件编写代码并加入
代码如下(logo.txt文件需要改成ANSI)

#include<iostream>
#include<opencv/cv.h>
#include"opencv2/opencv.hpp"
#include<opencv/cxcore.h>
#include<opencv/highgui.h>
#include<math.h>

using namespace cv;
using namespace std;

void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset);
void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset);
void put_text_to_image(int x_offset,int y_offset,String image_path,char* logo_path);

int main(){
    String image_path="123.jpg";//图片的名字
    char* logo_path="logo.txt";//汉字文件的名字
    put_text_to_image(200,350,image_path,logo_path);//change txt place
    return 0;
}

void paint_ascii(Mat& image,int x_offset,int y_offset,unsigned long offset){
    //绘制的起点坐标
	Point p;
	p.x = x_offset;
	p.y = y_offset;
	 //存放ascii字膜
	char buff[16];           
	//打开ascii字库文件
	FILE *ASCII;

	if ((ASCII = fopen("Asci0816.zf", "rb")) == NULL){
		printf("Can't open ascii.zf,Please check the path!");
		//getch();
		exit(0);
	}

	fseek(ASCII, offset, SEEK_SET);
	fread(buff, 16, 1, ASCII);

	int i, j;
	Point p1 = p;
	for (i = 0; i<16; i++)                  //十六个char
	{
		p.x = x_offset;
		for (j = 0; j < 8; j++)              //一个char八个bit
		{
			p1 = p;
			if (buff[i] & (0x80 >> j))    /*测试当前位是否为1*/
			{
				/*
					由于原本ascii字膜是8*16的,不够大,
					所以原本的一个像素点用4个像素点替换,
					替换后就有16*32个像素点
					ps:感觉这样写代码多余了,但目前暂时只想到了这种方法
				*/
				circle(image, p1, 0, Scalar(0, 0, 255), -1);
				p1.x++;
				circle(image, p1, 0, Scalar(0, 0, 255), -1);
				p1.y++;
				circle(image, p1, 0, Scalar(0, 0, 255), -1);
				p1.x--;
			   circle(image, p1, 0, Scalar(0, 0, 255), -1);
			}						
            p.x+=2;            //原来的一个像素点变为四个像素点,所以x和y都应该+2
		}
		p.y+=2;
	}
}
void paint_chinese(Mat& image,int x_offset,int y_offset,unsigned long offset){//在图片上画汉字
    Point p;
    p.x=x_offset;
    p.y=y_offset;
    FILE *HZK;
    char buff[72];//72个字节,用来存放汉字的

    if((HZK=fopen("HZKf2424.hz","rb"))==NULL){
        printf("Can't open HZKf2424.hz,Please check the path!");
        exit(0);//退出
    }
    fseek(HZK, offset, SEEK_SET);/*将文件指针移动到偏移量的位置*/
    fread(buff, 72, 1, HZK);/*从偏移量的位置读取72个字节,每个汉字占72个字节*/
    bool mat[24][24];//定义一个新的矩阵存放转置后的文字字膜
    int i,j,k;
    for (i = 0; i<24; i++)                 /*24x24点阵汉字,一共有24行*/
	{
        	for (j = 0; j<3; j++)                /*横向有3个字节,循环判断每个字节的*/
			for (k = 0; k<8; k++)              /*每个字节有8位,循环判断每位是否为1*/
				if (buff[i * 3 + j] & (0x80 >> k))    /*测试当前位是否为1*/
				{
					mat[j * 8 + k][i] = true;          /*为1的存入新的字膜中*/
				}
				else {
					mat[j * 8 + k][i] = false;
				}
	}
	
    for (i = 0; i < 24; i++)
	{
		p.x = x_offset;
		for (j = 0; j < 24; j++)
		{		
			if (mat[i][j])
				circle(image, p, 1, Scalar(255, 0, 0), -1);		  //写(替换)像素点
			p.x++;                                                //右移一个像素点
		}
		p.y++;                                                    //下移一个像素点
	}
}

void put_text_to_image(int x_offset,int y_offset,String image_path,char* logo_path){//将汉字弄上图片
//x和y就是第一个字在图片上的起始坐标
    //通过图片路径获取图片
    Mat image=imread(image_path);
    int length=19;//要打印的字符长度(打印多少字节长度就为多少,根据自己的情况调整)
    unsigned char qh,wh;//定义区号,位号
    unsigned long offset;//偏移量
    unsigned char hexcode[30];//用于存放记事本读取的十六进制,记得要用无符号
    FILE* file_logo;

    if ((file_logo = fopen(logo_path, "rb")) == NULL){
		printf("Can't open txtfile,Please check the path!");
		//getch();
		exit(0);
	}

    fseek(file_logo, 0, SEEK_SET);
    fread(hexcode, length, 1, file_logo);
    int x =x_offset,y = y_offset;//x,y:在图片上绘制文字的起始坐标

    for(int m=0;m<length;){
        if(hexcode[m]==0x23){
            break;//读到#号时结束
        }
        else if(hexcode[m]>0xaf){
            qh=hexcode[m]-0xaf;//使用的字库里是以汉字啊开头,而不是以汉字符号开头
            wh=hexcode[m+1] - 0xa0;//计算位码
            offset=(94*(qh-1)+(wh-1))*72L;
            paint_chinese(image,x,y,offset);
            /*
            计算在汉字库中的偏移量
            对于每个汉字,使用24*24的点阵来表示的
            一行有三个字节,一共24行,所以需要72个字节来表示
            */

            m=m+2;//一个汉字的机内码占两个字节,
            x+=24;//一个汉字为24*24个像素点,由于是水平放置,所以是向右移动24个像素点
        }

        else{
        //当读取的字符为ASCII码时
        wh=hexcode[m];
        offset=wh*16l;//计算英文字符的偏移量
        paint_ascii(image,x,y,offset);
        m++;//英文字符在文件里表示只占一个字节,所以往后移一位就行了
        x+=16;
        }

    }

    cv::imshow("image", image);
    cv::waitKey();
}




将生成的代码,配置文件,显示图片全部放在一个文件夹里
就可以进行编辑了
编译指令如下:

g++ word.cpp -o word `pkg-config --cflags --libs opencv`

成功之后输入 ./word
运行成功
OLED屏幕模块
(1)实验目的

通过本实验,学习并深入理解OLED屏幕的工作原理、显示原理以及驱动方式,为在嵌入式系统中更高级的应用提供基础。
掌握高级通信接口:进一步学习和掌握STM32单片机的高级通信接口,如SPI(Serial Peripheral Interface)或IIC(Inter-Integrated Circuit),并了解如何与OLED屏幕通过这些接口进行高效通信。
实现复杂的数据显示: 在OLED屏幕上显示更复杂的数据,如实时图形、动画或自定义字体,以提升对STM32单片机图形处理和数据显示的掌握能力。
结合其他传感器:整合其他传感器模块,例如温湿度传感器或光敏传感器,将其数据实时显示在OLED屏幕上,拓展实际应用场景。
学习优化与性能提升: 通过对程序的优化,了解如何提升系统性能,减小资源占用,并学习在嵌入式系统中的效能调优技巧。
拓展硬件与软件应用: 利用实验中的数据显示和传感器数据,拓展硬件与软件的应用,例如制作简单的可视化监控系统或小型嵌入式仪器。
通过这些目标,进一步提升学习者对STM32单片机及嵌入式系统的应用理解和实践能力,为更高级的嵌入式项目和物联网应用做好准备。

(2)OLED模块简介
OLED(Organic Light-Emitting Diode)屏幕采用一种有机发光二极管技术,其原理机制较传统液晶显示(LCD)屏幕有所不同。以下是OLED屏幕模块的基本原理机制:

发光原理: OLED屏幕是由一系列有机发光材料组成的薄膜构成的。当电流通过这些有机层时,有机材料会发光。这是一种自发光技术,与液晶屏幕不同,不需要背光源。
在这里插入图片描述

组成结构: OLED屏幕由多个层次组成,其中包括发光层、导电层、基板等。有两种主要类型的OLED:PMOLED(Passive Matrix OLED)和AMOLED(Active Matrix OLED)。AMOLED屏幕使用薄膜晶体管(TFT)来控制每个像素,提供更高的分辨率和更好的图像质量。

在这里插入图片描述

像素控制: 每个像素都是一个独立的发光点。在AMOLED中,每个像素由一个薄膜晶体管(TFT)和一个电容器组成,这些组件可以被调节以控制像素的亮度和颜色。这种单独控制每个像素的能力使得OLED屏幕能够实现更高的对比度和更快的响应速度。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/4b37c8fc253d4fecab08cbc13d0f5d2d.png

无需背光: 与LCD不同,OLED屏幕不需要背光源。这意味着在显示黑色时,OLED可以完全关闭相应的像素,实现真正的深黑色,而LCD需要通过阻挡背光来达到相似的效果。

柔性显示: 由于OLED屏幕是由柔性的有机材料构成,因此可以制造成弯曲和柔性的形态,使得它们可以适应更多的应用场景,如弯曲屏幕、可穿戴设备等。
在这里插入图片描述

OLED屏幕通过有机发光材料的自发光原理,实现了对每个像素的独立控制,无需背光,从而提供了更高的对比度、更广的视角、更快的响应速度和更真实的颜色表现。这使得OLED技术在移动设备、电视、监视器等各种显示应用中广泛应用。

(3)项目实现
我们需要利用取模软件

PCtoLCD2002是一款专业的OLED取模软件,提供多种取模方式,包括逐行、逐列、行列、列行,用户可根据需要选择字体、大小、文字的长宽,并独立调整文字的尺寸,生成各种形状的字符。此软件支持中英文数字混合字符串的字模数据生成,可选择字体、大小,允许调整每行输出数据个数,支持自定义取模方式和输出格式。
打开软件
点击字符模式进行取模
生成16x16汉字需要修改为3.1字宽,16字高

输入需要取模的文字
之后将生成的字模复制到代码里
显示温度与湿度
步骤如上任务
main函数如下:

void TEST_MainPage1(int c1,int t1)
{	
		
    GUI_ShowCHinese(5,30,16,"ʪ¶È",1);
    GUI_ShowCHinese(5,45,16,"ζÈ",1);
	GUI_ShowNum(35,30,c1/10,4,16,1);
	GUI_ShowNum(35,45,t1/10,4,16,1);
	Delay_1ms(1000);
}
volatile int  c1,t1;
uint32_t CT_data[2]={0,0};
u8 temp[10];  
u8 hum[10];
int main(void)
{	
	delay_init();	    	       //ÑÓʱº¯Êý³õʼ»¯	
	OLED_Init();			         //³õʼ»¯OLED  
	OLED_Clear(0);             //ÇåÆÁ£¨È«ºÚ£©
	
	/***********************************************************************************/
	/**///¢Ù¸ÕÉϵ磬²úƷоƬÄÚ²¿¾ÍÐ÷ÐèҪʱ¼ä£¬ÑÓʱ100~500ms,½¨Òé500ms
	/***********************************************************************************/
	AHT20_Init();
	Delay_1ms(500);
	/***********************************************************************************/
	/**///¢ÚÉϵçµÚÒ»´Î·¢0x71¶Áȡ״̬×Ö£¬ÅжÏ״̬×ÖÊÇ·ñΪ0x18,Èç¹û²»ÊÇ0x18,½øÐмĴæÆ÷³õʼ»¯
	/***********************************************************************************/
  
	if((AHT20_Read_Status()&0x18)!=0x18)
	{
		AHT20_Start_Init(); //ÖØгõʼ»¯¼Ä´æÆ÷
		Delay_1ms(10);
	}
	
	  
	//NVIC_Configuration(); 	   //ÉèÖÃNVICÖжϷÖ×é2:2λÇÀÕ¼ÓÅÏȼ¶£¬2λÏìÓ¦ÓÅÏȼ¶ 	
   OLED_WR_Byte(0x2E,OLED_CMD); //¹Ø±Õ¹ö¶¯
 
	OLED_WR_Byte(0x27,OLED_CMD); //ˮƽÏò×ó»òÕßÓÒ¹ö¶¯ 26/27
 
	OLED_WR_Byte(0x00,OLED_CMD); //ÐéÄâ×Ö½Ú
 
	OLED_WR_Byte(0x00,OLED_CMD); //Æðʼҳ 0
 
	OLED_WR_Byte(0x07,OLED_CMD); //¹ö¶¯Ê±¼ä¼ä¸ô
 
	OLED_WR_Byte(0x09,OLED_CMD); //ÖÕÖ¹Ò³ 2
 
	OLED_WR_Byte(0x00,OLED_CMD); //ÐéÄâ×Ö½Ú
 
	OLED_WR_Byte(0xFF,OLED_CMD); //ÐéÄâ×Ö½Ú
	while(1) 
	{		
		//AHT20_Read_CTdata(CT_data);       //²»¾­¹ýCRCУÑ飬ֱ½Ó¶ÁÈ¡AHT20µÄζȺÍʪ¶ÈÊý¾Ý    ÍƼöÿ¸ô´óÓÚ1S¶ÁÒ»´Î
    AHT20_Read_CTdata_crc(CT_data);  //crcУÑéºó£¬¶ÁÈ¡AHT20µÄζȺÍʪ¶ÈÊý¾Ý 
	  c1 = CT_data[0]*100*10/1024/1024;  //¼ÆËãµÃµ½Êª¶ÈÖµc1£¨·Å´óÁË10±¶£©
	  t1 = CT_data[1]*200*10/1024/1024-500;//¼ÆËãµÃµ½Î¶ÈÖµt1£¨·Å´óÁË10±¶£©	
		TEST_MainPage1(c1,t1);         //½çÃæÏÔʾ
		OLED_WR_Byte(0x2F,OLED_CMD); //¿ªÆô¹ö¶¯
	  Delay_1ms(2000);
          
	}
}

在这里插入图片描述

显示滑动字符
main函数如下:

#include "delay.h"
#include "sys.h"
#include "oled.h"
#include "gui.h"
#include "test.h"
int main(void)
{	
	delay_init();	    	       //ÑÓʱº¯Êý³õʼ»¯	  
	NVIC_Configuration(); 	   //ÉèÖÃNVICÖжϷÖ×é2:2λÇÀÕ¼ÓÅÏȼ¶£¬2λÏìÓ¦ÓÅÏȼ¶ 	
	OLED_Init();			         //³õʼ»¯OLED  
	OLED_Clear(0);             //ÇåÆÁ£¨È«ºÚ£©
	OLED_WR_Byte(0x2E,OLED_CMD);        //¹Ø±Õ¹ö¶¯
  OLED_WR_Byte(0x27,OLED_CMD);        //ˮƽÏò×ó»òÕßÓÒ¹ö¶¯ 26/27
  OLED_WR_Byte(0x00,OLED_CMD);        //ÐéÄâ×Ö½Ú
	OLED_WR_Byte(0x00,OLED_CMD);        //Æðʼҳ 0
	OLED_WR_Byte(0x07,OLED_CMD);        //¹ö¶¯Ê±¼ä¼ä¸ô
	OLED_WR_Byte(0x07,OLED_CMD);        //ÖÕÖ¹Ò³ 7
	OLED_WR_Byte(0x00,OLED_CMD);        //ÐéÄâ×Ö½Ú
	OLED_WR_Byte(0xFF,OLED_CMD);        //ÐéÄâ×Ö½Ú
	TEST_MainPage();
	OLED_WR_Byte(0x2F,OLED_CMD);        //¿ªÆô¹ö¶¯
	while(1) 
	{	
		//TEST_MainPage();         //Ö÷½çÃæÏÔʾ²âÊÔ
		
 
		//TEST_MainPage();
		/*OLED_Clear(0); 
		Test_Color();            //Ë¢ÆÁ²âÊÔ
		OLED_Clear(0); 
		Test_Rectangular();      //¾ØÐλæÖƲâÊÔ
		OLED_Clear(0); 
		Test_Circle();           //Ô²ÐλæÖƲâÊÔ
		OLED_Clear(0); 
		Test_Triangle();         //Èý½ÇÐλæÖƲâÊÔ
		OLED_Clear(0);  
		
		TEST_English();          //Ó¢ÎÄÏÔʾ²âÊÔ
		OLED_Clear(0);
		TEST_Number_Character(); //Êý×ֺͷûºÅÏÔʾ²âÊÔ
		OLED_Clear(0); 
		
		TEST_Chinese();          //ÖÐÎÄÏÔʾ²âÊÔ
		OLED_Clear(0); 
		TEST_BMP();              //BMPµ¥É«Í¼Æ¬ÏÔʾ²âÊÔ
		OLED_Clear(0); 
		TEST_Menu1();            //²Ëµ¥1ÏÔʾ²âÊÔ
		OLED_Clear(0); 
		TEST_Menu2();            //²Ëµ¥2ÏÔʾ²âÊÔ
		OLED_Clear(0); */
	}
}

在这里插入图片描述

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32控制OLED屏幕显示时,可以使用以下函数来实现: 1. `OLED_Init()`函数用于初始化OLED屏幕。 2. `OLED_Clear()`函数用于清除屏幕上的内容。 3. `OLED_ShowChar()`函数用于在指定位置显示一个字符。 4. `OLED_ShowNum()`函数用于在指定位置显示一个数字。 5. `OLED_ShowString()`函数用于在指定位置显示一个字符串。 此外,还可以使用`OLED_DrawPoint()`函数来在指定位置绘制一个点,使用`OLED_Fill()`函数来填充一个矩形区域,使用`OLED_Refresh_Gram()`函数来刷新屏幕显示。 在控制OLED屏幕显示时,需要使用SPI接口来与屏幕进行通信。可以使用`OLED_WR_Byte()`函数来向SSD1306写入一个字节的数据或命令。其中,`dat`参数表示要写入的数据,`cmd`参数表示数据或命令的标志,0表示命令,1表示数据。 综上所述,你可以使用以上提到的函数来控制STM32控制OLED屏幕的显示。 #### 引用[.reference_title] - *1* [使用STM32实现OLED屏显](https://blog.csdn.net/qq_52362275/article/details/127573490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [基于stm32oled显示](https://blog.csdn.net/ababababa23/article/details/126438483)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值