打印
[uCOS/RTOS]

【RTOS】RT-Thread创新大赛——麻雀一号LCD显示各种大小字体

[复制链接]
4016|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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文件:
游客,如果您要查看本帖隐藏内容请回复

实现代码:
游客,如果您要查看本帖隐藏内容请回复



使用特权

评论回复

相关帖子

沙发
downloadtext| | 2020-6-7 17:59 | 只看该作者
优秀!想请教下,字库bin文件如何生成?比如就是想定制多少区到多少区的字符。

使用特权

评论回复
板凳
WoodData|  楼主 | 2020-6-7 18:17 | 只看该作者
downloadtext 发表于 2020-6-7 17:59
优秀!想请教下,字库bin文件如何生成?比如就是想定制多少区到多少区的字符。 ...

有个软件生成的。很方便

使用特权

评论回复
地板
gyh974| | 2020-6-13 15:51 | 只看该作者
这个一定要用SD卡吗?

使用特权

评论回复
5
WoodData|  楼主 | 2020-6-13 16:43 | 只看该作者
gyh974 发表于 2020-6-13 15:51
这个一定要用SD卡吗?

SD卡方便些,你要是有其他存储器把字库文件存进去也行啊

使用特权

评论回复
6
电子禅石| | 2020-6-18 00:30 | 只看该作者
同问 字体是怎么生成的呢

使用特权

评论回复
7
Ultraman0803| | 2021-2-19 20:20 | 只看该作者
感谢分享,学习进步

使用特权

评论回复
8
zhdm| | 2021-2-25 09:53 | 只看该作者
谢谢分享

使用特权

评论回复
9
7235580| | 2022-5-11 16:30 | 只看该作者
不错,不错,看看的

使用特权

评论回复
10
shen520| | 2022-10-4 17:42 | 只看该作者
感谢分享,学习进步

使用特权

评论回复
11
lzx2475| | 2022-12-14 09:10 | 只看该作者
赞一个

使用特权

评论回复
12
lzx2475| | 2022-12-15 15:10 | 只看该作者
本帖最后由 lzx2475 于 2022-12-15 15:14 编辑

请问对ASC24.bin 怎么根据ascii读取字节

使用特权

评论回复
13
WoodData|  楼主 | 2022-12-15 21:08 | 只看该作者
lzx2475 发表于 2022-12-15 15:10
请问对ASC24.bin 怎么根据ascii读取字节


#define     ASCII_W             12
#define     ASCII_H             24
#define     ASCII_NAME          "0:/font/ASC24.bin"
#define     ASCII_BUFFER_LEN    48


#define     HZK_W               24
#define     HZK_H               24
#define     HZK_FILENAME        "0:/font/HZK24.bin"
#define     HZK_BUFFER_LEN      72

/**
* 在 lcd 上显示ASCII字符
* x : 在特定扫描方向下字符串的起始 X 坐标
* y : 在特定扫描方向下字符串的起始 Y 坐标
* usChar : 要显示的字符
*
*/
void lcd_disp_ascii_24x12(uint16_t x, uint16_t y, uint16_t usChar)
{
    uint8_t ucPage, ucColum, ucPoint8, *ucPtr;

    if((usChar >= 0x20)&&(usChar<=0x20+94))    //ASCII 码
    {
        lcd_open_window(x, y, ASCII_W, ASCII_H);
        ucPtr = &ASCII_NAME[(usChar-0x20)*ASCII_BUFFER_LEN];
        for (ucPage = 0; ucPage < ASCII_H; ucPage++)
        {
            for (ucColum = 0;ucColum < ASCII_W;)
            {            
                for(ucPoint8 = 0;ucPoint8 < 8;ucPoint8++)
                {
                    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 //不支持字符
    {
        
    }
}
#endif

/**
* 在 lcd 上显示ASCII字符
* x : 在特定扫描方向下字符串的起始 X 坐标
* y : 在特定扫描方向下字符串的起始 Y 坐标
* usChar : 要显示的字符
*
*/
void lcd_disp_ascii_24x12(uint16_t x, uint16_t y, uint16_t usChar)
{
    uint8_t ucBuffer[ASCII_BUFFER_LEN] = {0};

    if(usChar < 0x7f)    //ASCII 鐮?
    {  
        if((x>=LCD_W) || (y>=LCD_H)) return;
        GetGBKCode_from_sd(ASCII_NAME, usChar, ucBuffer, ASCII_BUFFER_LEN); //鍙栧瓧妯℃暟鎹?  
                lcd_char_dot(x,y,ASCII_W,ASCII_H,ucBuffer,cur_lcd_font_display.font_fore,cur_lcd_font_display.font_back);
    }else
    {
        
    }
}

uint8_t                ascii24_get_font_w(uint16_t ch)                //获取指定字符宽度
{
    ch = ch;
    return ASCII_W;

}

const lcd_font_t Font_ASCII24X12 =
{
    ASCII_H,
    ASCII_NAME,
    NULL,
    ascii24_get_font_w,
    lcd_disp_ascii_24x12,
};


/**
* 在 lcd 上显示中文字符
* x : 在特定扫描方向下字符串的起始 X 坐标
* y : 在特定扫描方向下字符串的起始 Y 坐标
* usChar : 要显示的字符
*
*/
void lcd_disp_ch24x24(uint16_t x, uint16_t y, uint16_t usChar)
{
    uint8_t ucPage, ucColum;
    uint8_t ucBuffer[HZK_BUFFER_LEN] = {0};

    ucPage =  usChar >> 8;      /* 取高8位数据 */
    ucColum = usChar & 0x00FF; /* 取低8位数据 */
    if((usChar >= 0x20)&&(usChar <= (0x20+94)))    //ASCII 码
    {
        lcd_disp_ascii_24x12(x,y,usChar);
    }else if((ucPage > 0xa0)&&(ucColum >0xa0))  //中文字符
    {   
        if((x>=LCD_W) || (y>=LCD_H)) return;  
        GetGBKCode_from_sd(HZK_FILENAME, usChar, ucBuffer, HZK_BUFFER_LEN); //取字模数据  
        lcd_char_dot(x,y,HZK_W,HZK_H,ucBuffer,cur_lcd_font_display.font_fore,cur_lcd_font_display.font_back);       
    }else //不支持字符
    {
        
    }
}

uint8_t                CH24_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_CH24X24 =
{
    HZK_H,
    ASCII_NAME,
    HZK_FILENAME,
    CH24_get_font_w,
    lcd_disp_ch24x24,
};

使用特权

评论回复
14
sanzi666| | 2023-4-24 09:55 | 只看该作者

厉害,

使用特权

评论回复
15
ysf| | 2023-6-1 18:34 | 只看该作者
进来好好学习一下

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

119

主题

4603

帖子

27

粉丝