AD161扩展灯板说明文档
📎 原始文档:https://www.kdocs.cn/l/cp7abZRrK2u7 AD161扩展灯板说明文档
工程链接:
demo仓库地址:
module / LED_IO屏 / demo · GitLab (zh-jieli.com)
子模块的更新参考:
module / LED扩展 / demo · GitLab (zh-jieli.com)
硬件资料:AD161A推12X72.zip
- 软件调试指南 开打印以及打印配置: uart.h app_config.h TCFG_UART_TX_PORT
灯板基础配置修改: info.h
字库生成:LvglFontTool.exe
- 如何更换字库,并查看字模大小 AD161依赖于LVGL字库工具做转换和取模: fw-AD16N\led_io_drive\font\字库工具\LvglFontTool_V0.4\LvglFontTool\LvglFontTool.exe
编码信息获取网址:https://www.haomeili.net/Code/DaQuan
更换外国字库方式:通过上述网址获取需要的字符,输入到LvglFontTool工具生成
清除重复字体后,点击生成内部字体:
宋体 12号字对应 1616字模
宋体 9号字对应 1212字模
字模高度会在生成的文件中体现出来,直接用生成的myFont.c替换工程路径中的myFont.c即可替换字库。
如果有多个语言种类要显示,只要UTF8编码范围没有冲突,就可以只用生成一个字库文件,在输入框加入不同语种字符即可,工具会自动去搜索字符并生成对应字库。 - 字库查询原理 歌词UTF8数据->转换UNICODE编码->根据UNICODE数据获取到字模dsc数据(长宽,字模位置等)->根据字模DSC数据获取字模的位图数据->填充在显示buf上
- UTF8转UNICODE数据
// UTF-8转Unicode函数
int utf8ToUnicode(const ONE_DIM *String,ONE_DIM *unicode) {
char *utf8String = String->data;
u8 buf_len = String->len;
int unicodeIndex = 0;
u8 byte_cnt = 0;
// while (*utf8String != '\0') {
while (buf_len>byte_cnt){
uint16_t unicodeValue = 0;
int byteCount = 0;
// 根据UTF-8编码规则计算Unicode码点
if ((*utf8String & 0x80) == 0x00) {
unicodeValue = *utf8String & 0x7F;
byteCount = 1;
} else if ((*utf8String & 0xE0) == 0xC0) {
unicodeValue = *utf8String & 0x1F;
byteCount = 2;
} else if ((*utf8String & 0xF0) == 0xE0) {
unicodeValue = *utf8String & 0x0F;
byteCount = 3;
}else
{
break;
}
// } else if ((*utf8String & 0xF8) == 0xF0) {
// unicodeValue = *utf8String & 0x07;
// byteCount = 4;
// }
// 处理多字节序列 还没有处理四字节
for (int i = 1; i < byteCount; ++i) {
unicodeValue = (unicodeValue << 6) | (*(utf8String + i) & 0x3F);
}
// 存储Unicode码点到数组中
if (unicode->data != NULL && unicodeIndex<unicode->len) {
((uint16_t *)(unicode->data))[unicodeIndex] = unicodeValue;
}
// 移动到下一个字符
utf8String += byteCount;
byte_cnt += byteCount;
unicodeIndex++;
}
// 在数组末尾添加NULL结尾字符
if (unicode->data != NULL && unicodeIndex<unicode->len) {
((uint16_t *)(unicode->data))[unicodeIndex] = 0;
}
return unicodeIndex;
}
- 获取字库DSC数据 DSC数据是存储了字符位图的大小,偏移,像素深度数据
/* 一 /
0xff,0xe0, //@@@@@@@@@@@.
{.bitmap_index = 978, .adv_w = 12, .box_h = 1, .box_w = 16, .ofs_x = 0, .ofs_y = 5},/(一)/
以字符“一"为例,其位图位置在存储数组偏移978字节开始,占用空间为box_wbox_h/8=2byte,字模大小为:adv_w,位图在实际压缩后的纵向偏移为5.
注意这里的5为从底部开始计算,可以根据程序对应看具体实现过程。
- 获取字库位图数据 将字符unicode码传入后,会使用二分法寻找对应的字符DSC数据,根据数据里面的bitmap_index找到对应字库位图数据,并将位图指针传输出去

- 填充在显示buf中 根据上述获取到的数据信息,将位图数据根据DSC数据的位置和压缩情况填入暂存buf之中。

- 歌词显示串口通信协议 AD161这边只支持UTF8格式歌词显示,所以主机传输过来的歌词的需要转换成UTF8格式。
文本格式查看工具:使用ascii.exe程序选择不同的编码对比数据内容。
u8 data[]= "深圳市杰理微电子科技有限公司"; 这样的编码就是UTF8格式,可以直接使用%s打印字符串看是否传输正确。
串口协议以及波特率可以自己定义,保证歌词编码传输正确即可
蓝牙和LRC文件输出UTF8格式歌词参考:AC695_310文件歌词读取输出.7z( 不跑内置UI系统)
上述示例工程中含有GB2312转UTF8格式对应表,可以移植在任意SDK中。 - 灯板显示原理 灯板组成是由矩阵灯板进行裁剪拼接后组成,N个IO口组成的灯数量为 NN-N(LED两端IO相同无法被点亮)
1272灯板:1272 = 864 3030-30 = 870
参考文档:12X72真值表.xlsx
参考真值表中的按照步骤一二三四顺序获取即可得到最终排列效果
/***
* SCAN_IO_NUME*SCAN_IO_NUME 矩阵
* 扫描row
*/
___interrupt
AT_LED_RAM_CODE
void led_drive_time_isr(void){
#if(LED_DRVIE_TIME_NUM==0)
_TIMER_SFR(0)->CON |= BIT(14);
#elif(LED_DRVIE_TIME_NUM==1)
_TIMER_SFR(1)->CON |= BIT(14);
#elif(LED_DRVIE_TIME_NUM==2)
_TIMER_SFR(2)->CON |= BIT(14);
#endif
// wdt_clear();
// JL_PORTB->OUT ^= BIT(3);
ugpio_gp_pro_set(&drvie_info.gpio[drvie_info.scan_row],USER_GPIO_HI,0); //50
drvie_info.scan_row=(drvie_info.scan_row+1)%SCAN_IO_NUME;
for(int list=0,offset=0;list<SCAN_LIST_BUFF_SIZE;){
for(int bit=0;bit<8&&offset<SCAN_IO_NUME;bit++){
if(drvie_info.scan_row!=offset){
ugpio_gp_pro_set(&drvie_info.gpio[offset],
(drvie_info.scanbuff[drvie_info.scan_cur][drvie_info.scan_row][list]&BIT(bit))?USER_GPIO_HD01_OUT_H:USER_GPIO_HI,0);
}
offset++;
}
list++;
}
ugpio_gp_pro_set(&drvie_info.gpio[drvie_info.scan_row],USER_GPIO_HD01_OUT_L,0);
}
在定时器中断中会对转换后的数据进行共阴逐行扫描。
void bmp_to_gpio_data_dim(u8 indata[SINGLE_HEIGHT][DISPLAY_FRAME_LIST_SIZE]){
// printf(">>>scan_next:%d,scan_cur:%d \n",drvie_info.scan_next,drvie_info.scan_cur);
if(((drvie_info.scan_next+1)%SCAN_FRAME_NUMBER)==drvie_info.scan_cur)return;
memset(drvie_info.scanbuff[drvie_info.scan_next],0,sizeof(drvie_info.scanbuff[0]));
//map_table 范围与 indata的范围 小的为准
int width=DISPLAY_FRAME_LIST_SIZE;
int height=SINGLE_HEIGHT;
int map_table_0_size=SINGLE_HEIGHT*SINGLE_WIDTH;
if((width*8)>SINGLE_WIDTH){
width=((SINGLE_WIDTH+4)/8);
}
if(height>SINGLE_HEIGHT){
height=SINGLE_HEIGHT;
}
// printf(" tog gpio data dim [%d][%d][%d]\r\n",width,height,map_table_0_size);
int map_h,map_w,map_bit,offset=0;
for(int h=0;h<SINGLE_HEIGHT;h++){
for(int w=0;w<width;w++){
for(int bit=0;bit<8 && offset<map_table_0_size;bit++){
if(indata[h][w]&BIT(7-bit)){
map_h=map_table[h][w*8+bit][0];
map_w=map_table[h][w*8+bit][1]/8;
map_bit=map_table[h][w*8+bit][1]%8;
drvie_info.scanbuff[drvie_info.scan_next][map_h][map_w]|=BIT(map_bit);
// printf("*");
}else{
// printf(" ");
}
offset++;
}
}
// printf("---lllll \r\n");
}
drvie_info.scan_cur = drvie_info.scan_next;
}
在这个函数中会将整个要显示的1272像素点大小的数据根据map_tab[]转换为要显示的3030数据。
操作示例:
给定程序是1212的字库和取模填充,应当如何更改为1616的字体?
- 更换字库,根据上述第一点选择字体大小即可,生成出来之后要看具体字模高度为多少。
- 在程序中已经做了12和16号字体的适配,只需要更改屏驱和字体大小的定义即可。

示例程序的字库只有中英文,还有一些中文字没有的情况,如果要解决多国语言一起的问题和某些字没有的情况,应当怎么操作? 字库并不是全字库,如果遇到没有的字在myFont.c中搜索一下,没找到就是没有添加这个字,在lvglFonttool中添加即可。

如果需要加外国字库,如日韩,阿拉伯等参考第一点的网址,下载对应的字库编码表txt文件,导入工具之后,清除重复即可,注意有的表中有很多图标和无用的字符,需要手动删除,不然会影响整个文件生成的大小。