【本文发布地址https://blog.youkuaiyun.com/Stack_/article/details/120432945,未经许可不得转载,转载须注明出处】
这个字库芯片的厂家叫高通,但不是美国那个高通,旧名叫集通。它在新版本的手册中删除了详细的读取方法,以下程序按照旧版手册编写。
【字库芯片GT20L16S1Y和GT30L32S4W带地址运算版手册】
[GT20L16S1Y所有字体数据都是竖置横排]
排置: Y(竖置横排)
点阵大小为 8X16的字母"A"的
点阵数据: 00 E0 9C 82 9C E0 00 00 0F 00 00 00 00 00 0F 00
[GT30L32S4W所有字体数据都是横置横排]
排置: W(横置横排)
点阵大小为 8X16的字母"A"的
点阵数据: 00 10 28 28 28 44 44 7C 82 82 82 82 00 00 00 00
[一、SPI配置]
/**
* @brief
* @note SPI1
NSS - PA4
CLK - PA5
MI - PA6
MO - PA7
* @param
* @retval
* @author PWH
* @ 优快云 Tyrion.Mon
* @date
*/
void Spi1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //NSS
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //CLK
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //MI
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //MO
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //2线全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // SPI模式 0
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //SPI1由APB2分频而来,最高18M, SPI2由APB1分频而来,最高18M
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_Cmd(SPI1, ENABLE);
}
/**
* @brief
* @note SPI1
NSS - PA4
CLK - PA5
MI - PA6
MO - PA7
* @param
* @retval
* @author PWH
* @date
*/
uint8_t Spi1_SendByte(uint8_t data)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
[二、各类型字体的起始地址]
@ 优快云 Tyrion.Mon
#define GT20L16S1Y_GB2312_15x16_BASE_ADDR 0x00000 // 15x16点GB2312标准点阵字库
#define GT20L16S1Y_ASCII_7x8_BASE_ADDR 0x66C0 // 7x8点ascii字符
#define GT20L16S1Y_GB2312_EXTEND_8x16_BASE_ADDR 0x3B7D0 // 8x16点国标扩展字符
#define GT20L16S1Y_ASCII_8x16_BASE_ADDR 0x3B7C0 // 8x16点ASCII字符
#define GT20L16S1Y_ASCII_5x7_BASE_ADDR 0x3BFC0 // 5x7点ASCII字符
#define GT20L16S1Y_ASCII_ARIAL_16_BASE_ADDR 0x3C2C0 // 16点阵不等宽ASCII方头(Arial)字符
#define GT20L16S1Y_ASCII_BOLD_8x16_BASE_ADDR 0x3CF80 // 8x16点ASCII粗体字符
#define GT20L16S1Y_ASCII_TIMESNEWROMEAN_16_BASE_ADDR 0x3D580 // 16点阵不等宽ASCII白正(TimesNewRomean)字符
[三、计算某个字符在不同字体中的起始地址]
@ 优快云 Tyrion.Mon
/**
* @brief 获取该ascii字符在5x7库中的起始位置
* @note
该库每个字符占8字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/1
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_5x7(char *asciiCode)
{
/* 0x20 - 0x7E ascii中可显示的字符 */
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_5x7_BASE_ADDR + (*asciiCode - ' ') * 8;
}
return -1;
}
/**
* @brief 获取该ascii字符在7x8库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_7x8(char *asciiCode)
{
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_7x8_BASE_ADDR + (*asciiCode - ' ') * 8;
}
return -1;
}
/**
* @brief 获取该ascii字符在8x16库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_8x16(char *asciiCode)
{
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_8x16_BASE_ADDR + (*asciiCode - ' ') * 16;
}
return -1;
}
/**
* @brief 获取该ascii字符在16点阵不等宽ASCII方头(Arial)库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_Arial_16(char *asciiCode)
{
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_ARIAL_16_BASE_ADDR + (*asciiCode - ' ') * 34;
}
return -1;
}
/**
* @brief 获取该ascii字符在8x16粗体库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_Bold_8x16(char *asciiCode)
{
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_BOLD_8x16_BASE_ADDR + (*asciiCode - ' ') * 16;
}
return -1;
}
/**
* @brief 获取该ascii字符在16点阵不等宽ASCII白正(TimesNewRoman)库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_Ascii_TimesNewRoman_16(char *asciiCode)
{
if (*asciiCode >= ' ' && *asciiCode <= '~')
{
return GT20L16S1Y_ASCII_TIMESNEWROMEAN_16_BASE_ADDR + (*asciiCode - ' ') * 34;
}
return -1;
}
/**
* @brief 获取该汉字字符在15x16点GB2312标准库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_GB2312_15x16(uint8_t *GB2312Code)
{
uint8_t MSB = *GB2312Code;
uint8_t LSB = *(GB2312Code + 1);
if (MSB == 0xA9 && LSB >= 0xA1) {
return GT20L16S1Y_GB2312_15x16_BASE_ADDR + (282 + (LSB - 0xA1)) * 32;
}
else if (MSB >= 0xA1 && MSB <= 0xA3 && LSB >= 0xA1) {
return GT20L16S1Y_GB2312_15x16_BASE_ADDR + ((MSB - 0xA1) * 94 + (LSB - 0xA1)) * 32;
}
else if (MSB >= 0xB0 && MSB <= 0xF7 && LSB >= 0xA1) {
return GT20L16S1Y_GB2312_15x16_BASE_ADDR + ((MSB - 0xB0) * 94 + (LSB - 0xA1) + 846) * 32;
}
return -1;
}
/**
* @brief 获取该汉字字符在8x16点GB2312标准库中的起始位置
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
static int32_t GT20L16S1Y_Get_Addr_GB2312_Extend_8x16(uint8_t *GB2312Code)
{
uint16_t FontCode = *(GB2312Code + 1);
FontCode = (FontCode << 8) | *GB2312Code;
if (FontCode >= 0xAAA1 && FontCode <= 0xAAFE) {
return GT20L16S1Y_GB2312_EXTEND_8x16_BASE_ADDR + (FontCode - 0xAAA1) * 16;
}
else if (FontCode >= 0xABA1 && FontCode <= 0xABC0) {
return GT20L16S1Y_GB2312_EXTEND_8x16_BASE_ADDR + (FontCode - 0xABA1 + 95) * 16;
}
return -1;
}
[四、获取某个字符的各类型字体的点阵数据]
/**
* @brief 发送三字节地址
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/1
*/
static void GT20L16S1Y_Send_Address(uint32_t addr)
{
Spi1_SendByte(0x03); //一般读取
Spi1_SendByte(addr >> 16);
Spi1_SendByte(addr >> 8);
Spi1_SendByte(addr);
}
/**
* @brief 接收字符数据
* @note
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/1
*/
static void GT20L16S1Y_Read_FontBytes(int32_t addr, uint8_t *bytesArray, uint8_t numOfBytes)
{
uint8_t i;
SPI1_CS_RESET;
GT20L16S1Y_Send_Address(addr);
for (i = 0; i < numOfBytes; i++)
{
*bytesArray++ = Spi1_SendByte(0xff);
}
SPI1_CS_SET;
}
/**
* @brief 获取该汉字字符在15x16点GB2312
* @note 一个字符占32字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GT20L16S1Y_Get_Bytes_GB2312_15x16(uint8_t *GB2312Code, uint8_t *bytesArray)
{
int32_t addr;
addr = GT20L16S1Y_Get_Addr_GB2312_15x16(GB2312Code);
if (addr == -1) return;
GT20L16S1Y_Read_FontBytes(addr, bytesArray, 32);
}
/**
* @brief 获取该ASCII字符在7x8点
* @note 一个字符占8字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GT20L16S1Y_Get_Bytes_ASCII_5x7(uint8_t *ASCIICode, uint8_t *bytesArray)
{
int32_t addr;
addr = GT20L16S1Y_Get_Addr_Ascii_5x7(ASCIICode);
if (addr == -1) return;
GT20L16S1Y_Read_FontBytes(addr, bytesArray, 8);
}
/**
* @brief 获取该ASCII字符在7x8点
* @note 一个字符占8字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GT20L16S1Y_Get_Bytes_ASCII_7x8(uint8_t *ASCIICode, uint8_t *bytesArray)
{
int32_t addr;
addr = GT20L16S1Y_Get_Addr_Ascii_7x8(ASCIICode);
if (addr == -1) return;
GT20L16S1Y_Read_FontBytes(addr, bytesArray, 8);
}
/**
* @brief
* @note 一个字符占16字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GT20L16S1Y_Get_Bytes_ASCII_8x16(uint8_t *ASCIICode, uint8_t *bytesArray)
{
int32_t addr;
addr = GT20L16S1Y_Get_Addr_Ascii_8x16(ASCIICode);
if (addr == -1) return;
GT20L16S1Y_Read_FontBytes(addr, bytesArray, 16);
}
/**
* @brief
* @note 一个字符占16字节
* @param None
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GT20L16S1Y_Get_Bytes_ASCII_Bold_8x16(uint8_t *ASCIICode, uint8_t *bytesArray)
{
int32_t addr;
addr = GT20L16S1Y_Get_Addr_Ascii_Bold_8x16(ASCIICode);
if (addr == -1) return;
GT20L16S1Y_Read_FontBytes(addr, bytesArray, 16);
}
[五、当LCD需要横排横置的数据,需要做一下转换。从手册中研究一下各字体竖置横排和横置横排的区别,得到下面的算法]
@ 优快云 Tyrion.Mon
void GB2312_15x16_ShuZhiHengPai_to_HengZhiHengPai(uint8_t *szhp, uint8_t *hzhp)
{
uint8_t i, j;
for (j = 0; j <= 14; j += 2) //扫描竖置横排的byte0-byte7到横置横排的byte0 byte2 byte4 ... byte14
{
for (i = 0; i <= 7; i++)
{
if (*(szhp + i) & (0x01 << (j / 2))) //
*(hzhp + j) |= (0x01 << (7 - i));
else
*(hzhp + j) &= ~(0x01 << (7 - i));
}
}
for (j = 1; j <= 15; j += 2) //扫描竖置横排的byte8-byte15到横置横排的byte1 byte3 byte5 ... byte15
{
for (i = 8; i <= 15; i++)
{
if (*(szhp + i) & (0x01 << ((j - 1) / 2))) //
*(hzhp + j) |= (0x01 << (7 - (i - 8)));
else
*(hzhp + j) &= ~(0x01 << (7 - (i - 8)));
}
}
for (j = 16; j <= 30; j += 2) //扫描竖置横排的byte16-byte23到横置横排的byte16 byte18 byte20 ... byte30
{
for (i = 16; i <= 23; i++)
{
if (*(szhp + i) & (0x01 << ((j - 16) / 2))) //
*(hzhp + j) |= (0x01 << (7 - (i - 16)));
else
*(hzhp + j) &= ~(0x01 << (7 - (i - 16)));
}
}
for (j = 17; j <= 31; j += 2) //扫描竖置横排的byte24-byte31到横置横排的byte17 byte19 byte21 ... byte31
{
for (i = 24; i <= 31; i++)
{
if (*(szhp + i) & (0x01 << ((j - 17) / 2))) //
*(hzhp + j) |= (0x01 << (7 - (i - 24)));
else
*(hzhp + j) &= ~(0x01 << (7 - (i - 24)));
}
}
}
void ASCII_8x16_ShuZhiHengPai_to_HengZhiHengPai(uint8_t *szhp, uint8_t *hzhp)
{
uint8_t i, j;
for (j = 0; j <= 7; j += 1) //扫描竖置横排的byte0-byte7到横置横排的byte0-byte7
{
for (i = 0; i <= 7; i++)
{
if (*(szhp + i) & (0x01 << j)) //
*(hzhp + j) |= (0x01 << (7 - i));
else
*(hzhp + j) &= ~(0x01 << (7 - i));
}
}
for (j = 8; j <= 15; j += 1) //扫描竖置横排的byte8-byte15到横置横排的byte8-byte15
{
for (i = 8; i <= 15; i++)
{
if (*(szhp + i) & (0x01 << (j - 8))) //
*(hzhp + j) |= (0x01 << (7 - (i - 8)));
else
*(hzhp + j) &= ~(0x01 << (7 - (i - 8)));
}
}
}
[六、向LCD写入字符串数据。向LCD写数据的部分仅适用于我的屏,请按自己的屏提供的接口去修改]
/**
* @brief
* @note LCD显示汉字字符串
* @param x:显示起始X坐标
y:显示起始Y坐标
*text:要显示的字符串
wordColor:字体的颜色
backColor:该字体背景颜色
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GUI_Display_GB2312_15x16(uint16_t x, uint16_t y, uint8_t *text, uint16_t wordColor, uint16_t backColor)
{
uint8_t szhpArray[32];
uint8_t hzhpArray[32];
uint8_t i, j, m;
while (*text != '\0')
{
GT20L16S1Y_Get_Bytes_GB2312_15x16(text, szhpArray);
GB2312_15x16_ShuZhiHengPai_to_HengZhiHengPai(szhpArray, hzhpArray);
TFT_SetWindow(x, y, x + 15, y + 15); //设置字符描点起始位置
for(i = 0; i < 32; i++) //一个汉字32字节
{
for(j=0;j<8;j++) //每字节的8位数据
{
if(hzhpArray[i] & (0x80 >> j)) //为1时着前景色,前景色的点将显现出字符
{
TFT_WriteData(wordColor);
}
else //为0时着后景色
{
TFT_WriteData(backColor);
}
}
}
text += 2; //下一个字
x += 16; //下一个字偏移16点
}
}
/**
* @brief
* @note LCD显示英文字符串
* @param x:显示起始X坐标
y:显示起始Y坐标
*text:要显示的字符串
wordColor:字体的颜色
backColor:该字体背景颜色
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GUI_Display_ASCII_Bold_8x16(uint16_t x, uint16_t y, uint8_t *text, uint16_t wordColor, uint16_t backColor)
{
uint8_t szhpArray[16];
uint8_t hzhpArray[16];
uint8_t i, j, m;
while (*text != '\0')
{
GT20L16S1Y_Get_Bytes_ASCII_Bold_8x16(text, szhpArray);
ASCII_8x16_ShuZhiHengPai_to_HengZhiHengPai(szhpArray, hzhpArray);
TFT_SetWindow(x, y, x + 7, y + 15);
for(i = 0; i < 16; i++)
{
for(j=0;j<8;j++)
{
if(hzhpArray[i] & (0x80 >> j))
{
TFT_WriteData(wordColor);
}
else
{
TFT_WriteData(backColor);
}
}
}
text += 1; //下一个字
x += 8;
}
}
[七、当字符串有汉字也有英文]
/**
* @brief
* @note LCD显示一个汉字字符
* @param x:显示起始X坐标
y:显示起始Y坐标
*text:要显示的字符串
wordColor:字体的颜色
backColor:该字体背景颜色
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GUI_Display_One_GB2312_15x16(uint16_t x, uint16_t y, uint8_t *text, uint16_t wordColor, uint16_t backColor)
{
uint8_t szhpArray[32];
uint8_t hzhpArray[32];
uint8_t i, j;
GT20L16S1Y_Get_Bytes_GB2312_15x16(text, szhpArray);
GB2312_15x16_ShuZhiHengPai_to_HengZhiHengPai(szhpArray, hzhpArray);
TFT_SetWindow(x, y, x + 15, y + 15);
for(i = 0; i < 32; i++)
{
for(j=0;j<8;j++)
{
if(hzhpArray[i] & (0x80 >> j))
{
TFT_WriteData(wordColor);
}
else
{
TFT_WriteData(backColor);
}
}
}
}
/**
* @brief
* @note LCD显示一个英文字符
* @param x:显示起始X坐标
y:显示起始Y坐标
*text:要显示的字符串
wordColor:字体的颜色
backColor:该字体背景颜色
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GUI_Display_One_ASCII_Bold_8x16(uint16_t x, uint16_t y, uint8_t *text, uint16_t wordColor, uint16_t backColor)
{
uint8_t szhpArray[16];
uint8_t hzhpArray[16];
uint8_t i, j, m;
GT20L16S1Y_Get_Bytes_ASCII_Bold_8x16(text, szhpArray);
ASCII_8x16_ShuZhiHengPai_to_HengZhiHengPai(szhpArray, hzhpArray);
TFT_SetWindow(x, y, x + 7, y + 15);
for(i = 0; i < 16; i++)
{
for(j=0;j<8;j++)
{
if(hzhpArray[i] & (0x80 >> j))
{
TFT_WriteData(wordColor);
}
else
{
TFT_WriteData(backColor);
}
}
}
}
/**
* @brief
* @note LCD汉字和英文混合输出
* @param x:显示起始X坐标
y:显示起始Y坐标
*text:要显示的字符串
wordColor:字体的颜色
backColor:该字体背景颜色
* @retval None
* @author PWH @ 优快云 Tyrion.Mon
* @date 2021/4
*/
void GUI_Display_AutoSelect_GB2312_ASCII(uint16_t x, uint16_t y, uint8_t *text, uint16_t wordColor, uint16_t backColor)
{
while (*text != '\0')
{
if (*text & 0x80)
{
GUI_Display_One_GB2312_15x16(x, y, text, wordColor, backColor);
text += 2;
x += 16;
}
else
{
GUI_Display_One_ASCII_Bold_8x16(x, y, text, wordColor, backColor);
text += 1;
x += 8;
}
}
}
[不知何原因,读出的第一个字符是乱码,需要在初始化后读出一个字符数据丢弃]
[效果图]