本帖最后由 WoodData 于 2020-6-7 15:40 编辑
在麻雀一号开发包里面有字符显示字体和中文字体,其中中文是通过SD卡内文件字库显示的。但是字体只有16X16点阵的,不够用。我就在参考这个的基础上添加了多个字库,存储在SD卡中。有16X16,24X24,32X32,40X40,48X48中文的,以及16X8,24X12,32X16,40X20,48X24的ASCII字符。这些基本够用了。其实还可以很简单再加各种字体。
下面就分享一下如何实现这些字库显示。
/*
* Copyright (c) 2020. By WoodData
*
*
* Change Logs:
* Date Author Notes
*/
#ifndef LCD_FONT__H_
#define LCD_FONT__H_
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "drv_lcd.h"
#define lcd_color_t uint16_t
/* type define ------------------------------------------------------------*/
typedef struct _lcd_font
{
uint8_t font_height; //字库通用高度
uint8_t * pfont_buff; //字库buff
uint8_t * pfont_filename; //字库文件
uint8_t (* get_font_w)(uint16_t ch); //获取指定字符宽度
void (* draw_char)(uint16_t x,uint16_t y,uint16_t ch); //指定字符描点
}lcd_font_t;
typedef struct _lcd_font_display
{
lcd_font_t *pfont; //字库索引
lcd_color_t font_fore; //字体前景颜色
lcd_color_t font_back; //字体背景颜色
uint16_t font_sx; //字符显示范围起点x
uint16_t font_sy; //字符显示范围起点y
uint16_t font_ex; //字符显示范围终点x
uint16_t font_ey; //字符显示范围终点y
}lcd_font_display_t;
//common
int GetGBKCode_from_sd(uint8_t *filename, uint16_t ch, uint8_t *pBuffer, uint16_t len);
int GetASCCode_from_sd(uint8_t *filename, uint16_t ch, uint8_t *pBuffer, uint16_t len);
//API
lcd_font_t * lcd_set_font( const lcd_font_t *newfont);
lcd_font_t * lcd_get_font(void);
void lcd_set_font_color(lcd_color_t fore, lcd_color_t back);
lcd_color_t lcd_get_font_color_fore(void);
lcd_color_t lcd_get_font_color_back(void);
void lcd_set_font_window(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey);
void lcd_disp_char_at(uint16_t usX, uint16_t usY, uint16_t cChar);
void lcd_disp_str_at(uint16_t usX, uint16_t usY, const char *pStr);
void lcd_font_init(void);
//字库
extern const lcd_font_t Font_CH16X16;
extern const lcd_font_t Font_ASCII16X8;
extern const lcd_font_t Font_CH24X24;
extern const lcd_font_t Font_ASCII24X12;
extern const lcd_font_t Font_CH32X32;
extern const lcd_font_t Font_ASCII32X16;
extern const lcd_font_t Font_CH40X40;
extern const lcd_font_t Font_ASCII40X20;
extern const lcd_font_t Font_CH48X48;
extern const lcd_font_t Font_ASCII48X24;
#endif //LCD_FONT__H_
/*
* Copyright (c) 2020. By WoodData
*
*
* Change Logs:
* Date Author Notes
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "drv_lcd.h"
#include "lcd_font.h"
lcd_font_display_t cur_lcd_font_display;
/**
* 设置当前字体
* para: *newfont 新字体
* return: none
*/
lcd_font_t * lcd_set_font(const lcd_font_t * newfont)
{
lcd_font_t *oldfont;
oldfont = cur_lcd_font_display.pfont;
cur_lcd_font_display.pfont = newfont;
return oldfont;
}
/**
* 获取当前字体
* para: none
* return: 当前字体
*/
lcd_font_t * lcd_get_font(void)
{
return cur_lcd_font_display.pfont;
}
/**
* 设置当前字体颜色
* para1: fore 前景色
* para2: back 背景色
* return: none
*/
void lcd_set_font_color(lcd_color_t fore, lcd_color_t back)
{
cur_lcd_font_display.font_fore = fore;
cur_lcd_font_display.font_back = back;
}
/**
* 获取当前字体前景色
* para: none
* return: 当前字体前景色
*/
lcd_color_t lcd_get_font_color_fore(void)
{
return cur_lcd_font_display.font_fore;
}
/**
* 获取当前字体背景色
* para: none
* return: 当前字体背景色
*/
lcd_color_t lcd_get_font_color_back(void)
{
return cur_lcd_font_display.font_back;
}
/**
* 设置字符显示范围
* para1: sx
* para2: sy
* para3: ex
* para4: ey
* return: none
*/
void lcd_set_font_window(uint16_t sx,uint16_t sy,uint16_t ex,uint16_t ey)
{
cur_lcd_font_display.font_sx = sx;
cur_lcd_font_display.font_sy = sy;
cur_lcd_font_display.font_ex = ex;
cur_lcd_font_display.font_ey = ey;
}
/**
* LCD 上显示字符
* usX : 在特定扫描的方向下字符的起始 X 坐标
* usY : 在特定扫描的方向下字符的起始 Y 坐标
* cChar : 要显示的字符
* 返回值 : 无
*/
void lcd_disp_char_at(uint16_t usX, uint16_t usY, uint16_t cChar)
{
if((cur_lcd_font_display.pfont != NULL) && (cur_lcd_font_display.pfont->draw_char != NULL))
{
cur_lcd_font_display.pfont->draw_char(usX, usY, cChar);
}
}
/**
* 在 LCD 上显示英文字符串
* usX : 在特定扫描方向下字符串的起始 X 坐标
* usY : 在特定扫描方向下字符串的起始 Y 坐标
* pStr : 要显示的英文字符串的首地址
* 返回值 : 无
*/
void lcd_disp_str_at(uint16_t usX, uint16_t usY, const char *pStr)
{
uint16_t w,ch;
if(cur_lcd_font_display.pfont == NULL) return;
while (*pStr != '\0')
{
if(*pStr > 126)
{
ch = (*pStr << 8);
pStr++;
ch += *pStr;
}
else
{
ch = *pStr;
}
w = cur_lcd_font_display.pfont->get_font_w(ch); //获取字符宽度
#if 0
if ((usX + w) > cur_lcd_font_display.font_ex)
{
usX = cur_lcd_font_display.font_sx;
usY += cur_lcd_font_display.pfont->font_height;
}
if ((usY + cur_lcd_font_display.pfont->font_height) > cur_lcd_font_display.font_ey)
{
usX = cur_lcd_font_display.font_sx;
usY = cur_lcd_font_display.font_sy;
}
#endif
lcd_disp_char_at(usX, usY, ch);
pStr++;
usX += w;
}
}
void lcd_font_init(void)
{
lcd_set_font(&Font_ASCII16X8);
lcd_set_font_color(BLACK,WHITE);
lcd_set_font_window(0,0,LCD_W-1,LCD_H-1);
}
下面是各个字库实现基础。以16X16点阵为例:
/*
* Copyright (c) 2020. By WoodData
*
*
* Change Logs:
* Date Author Notes
*/
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
#include "drv_lcd.h"
#include "lcd_font.h"
#define ASCII_W 8
#define ASCII_H 16
#define ASCII_NAME "/sd/font/ASC16.bin"
#define ASCII_BUFFER_LEN 16
#define HZK_W 16
#define HZK_H 16
#define HZK_FILENAME "/sd/font/HZK16.bin"
#define HZK_BUFFER_LEN 32
extern lcd_font_display_t cur_lcd_font_display;
/**
* �? lcd 上显示ASCII字符
* x : 在特定扫描方向下字符串的起始 X 坐标
* y : 在特定扫描方向下字符串的起始 Y 坐标
* usChar : 要显示的字符
*
*/
void lcd_disp_ascii_16x8(uint16_t x, uint16_t y, uint16_t usChar)
{
uint8_t ucPage, ucColum, ucPoint8, *ucPtr;
uint8_t ucBuffer[ASCII_BUFFER_LEN] = {0};
if(usChar < 0x7f) //ASCII �?
{
if((x>=LCD_W) || (y>=LCD_H)) return;
lcd_open_window(x, y, (x+ASCII_W >= LCD_W)?(LCD_W-x):ASCII_W, (y+ASCII_H >= LCD_H)?(LCD_H-y): ASCII_H);
GetASCCode_from_sd(ASCII_NAME, usChar, ucBuffer, ASCII_BUFFER_LEN); //取字模数�?
ucPtr = ucBuffer;
for (ucPage = 0; ucPage < ASCII_H; ucPage++)
{
if((y+ucPage) >= LCD_H) break;
for (ucColum = 0; ucColum < (ASCII_W); )
{
for(ucPoint8 = 0;ucPoint8 < 8;ucPoint8++)
{
if((x+ucColum) < LCD_W)
{
if( *ucPtr & (0x80 >> ucPoint8)) //高位在前
{
lcd_write_half_word(cur_lcd_font_display.font_fore);
}
else
{
lcd_write_half_word(cur_lcd_font_display.font_back);
}
}
ucColum++;
if(ucColum == ASCII_W) break;
}
ucPtr++;
}
}
}else
{
}
}
uint8_t ascii16_get_font_w(uint16_t ch) //获取指定字符宽度
{
ch = ch;
return ASCII_W;
}
const lcd_font_t Font_ASCII16X8 =
{
ASCII_H,
ASCII_NAME,
NULL,
ascii16_get_font_w,
lcd_disp_ascii_16x8,
};
/**
* �? lcd 上显示中文字�?
* x : 在特定扫描方向下字符串的起始 X 坐标
* y : 在特定扫描方向下字符串的起始 Y 坐标
* usChar : 要显示的字符
*
*/
void lcd_disp_ch16x16(uint16_t x, uint16_t y, uint16_t usChar)
{
uint8_t ucPage, ucColum, ucPoint8, *ucPtr;
uint8_t ucBuffer[HZK_BUFFER_LEN] = {0};
ucPage = usChar >> 8; /* 取高8位数�? */
ucColum = usChar & 0x00FF; /* 取低8位数�? */
if(usChar < 0x7f) //ASCII �?
{
lcd_disp_ascii_16x8(x,y,usChar);
}else if((ucPage > 0xa0)&&(ucColum >0xa0)) //中文字符
{
if((x>=LCD_W) || (y>=LCD_H)) return;
lcd_open_window(x, y, (x+HZK_W >= LCD_W)?(LCD_W-x):HZK_W, (y+HZK_H >= LCD_H)?(LCD_H-y): HZK_H);
GetGBKCode_from_sd(HZK_FILENAME, usChar, ucBuffer, HZK_BUFFER_LEN); //取字模数�?
ucPtr = ucBuffer;
for (ucPage = 0; ucPage < HZK_H; ucPage++)
{
if((y+ucPage) >= LCD_H) break;
for (ucColum = 0; ucColum < (HZK_W); )
{
for(ucPoint8 = 0;ucPoint8 < 8;ucPoint8++)
{
if((x+ucColum) < LCD_W)
{
if( *ucPtr & (0x80 >> ucPoint8)) //高位在前
{
lcd_write_half_word(cur_lcd_font_display.font_fore);
}
else
{
lcd_write_half_word(cur_lcd_font_display.font_back);
}
}
ucColum++;
if(ucColum == HZK_W) break;
}
ucPtr++;
}
}
}else //不支持字�?
{
}
}
uint8_t CH16_get_font_w(uint16_t ch) //获取指定字符宽度
{
uint8_t ucPage, ucColum;
ucPage = ch >> 8; /* 取高8位数�? */
ucColum = ch & 0x00FF; /* 取低8位数�? */
if(ch < 0x7f) //ASCII �?
{
return ASCII_W;
}else if((ucPage > 0xa0)&&(ucColum >0xa0)) //中文字符
{
return HZK_W;
}else
{
return 0;
}
}
const lcd_font_t Font_CH16X16 =
{
HZK_H,
ASCII_NAME,
HZK_FILENAME,
CH16_get_font_w,
lcd_disp_ch16x16,
};
测试:
#include <rtthread.h>
#include <rtdevice.h>
#if defined RT_USING_LCD_TEST
//添加 lcd 显示使用的头文件
#include "drv_lcd.h"
#include "lcd_font.h"
int test_lcd_ch(int argc, char *argv[])
{
rt_err_t ret = RT_EOK;
lcd_clear(BLUE);
lcd_set_font(&Font_CH16X16);
lcd_set_font_color(BLACK, RED);
lcd_disp_str_at(0, 0, "RT-Thread物联网操作系统");
rt_thread_mdelay(1000);
lcd_set_font(&Font_CH24X24);
lcd_set_font_color(GREEN, BLACK);
lcd_disp_str_at(20,20, "小而美的物联网操作系统");
rt_thread_mdelay(1000);
lcd_set_font(&Font_CH32X32);
lcd_set_font_color(GREEN, BLACK);
lcd_disp_str_at(10, 50, "RT-Thread物联网操作系统");
rt_thread_mdelay(1000);
lcd_set_font(&Font_CH40X40);
lcd_set_font_color(WHITE, BLACK);
lcd_disp_str_at(3, 90, "RT-Thread物联网操作系统");
rt_thread_mdelay(1000);
lcd_set_font(&Font_CH48X48);
lcd_set_font_color(RED, BLUE);
lcd_disp_str_at(0, 140, "Hi,你好,小而美的物联网操作系统.");
return ret;
}
MSH_CMD_EXPORT(test_lcd_ch, test_lcd_ch);
#endif
最终效果:
字库bin文件:
实现代码:
|