首页 新闻 会员 周边 捐助

如何任意调节屏幕宽度像素的同时,还能使16*16的汉字点阵正确映射到LED屏幕上???

0
悬赏园豆:5 [待解决问题]

有偿解决
麻烦请各位大佬帮忙看一下

目前的情况是
在官方例程的的程序上,实现调节屏幕像素的功能,但是发现原有的程序,只能适用于1664大小的屏幕和1616的汉字点阵,我试图找到LED屏幕中关于汉字点阵的的映射逻辑,但是发现输出的日志,有点混乱,找不到对应的逻辑,这一点需要协助解决一下

源代码如下:

/* 显示汉字总数 */
#define WORD_NUM 4
/* 每行显示的字节数 */
#define SCAN_NUM (8 * WORD_NUM)


u8 Chinese[4][32] =
{
    { 
0x00,0x01,0x00,0x01,0x3F,0x01,0x20,0x3F,0xA0,0x20,0x92,0x10,0x54,0x02,0x28,0x02,
0x08,0x02,0x14,0x05,0x24,0x05,0xA2,0x08,0x81,0x08,0x40,0x10,0x20,0x20,0x10,0x40,
    },

    {

			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },

    {
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },

    {
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },


};


uint8_t dispram[128] = {0}; 

void hub12DataSerialInput(uint8_t data){
                uint16_t         i;
                for( i = 0; i < 8; i++){
                                if(data & 0x80){
                                        HIGH_HUB12_DR;
                                       
                                }else{
                                        LOW_HUB12_DR;
                                }
                                LOW_HUB12_CLK;
																Delay_us(2);     
                                HIGH_HUB12_CLK;
                                data        = data << 1;
                }
}


// 行选择 (1/4扫描)
void  hub12SelectRows(uint8_t rows){
	LOW_HUB12_C;
	LOW_HUB12_D;
                switch(rows){
                        case 0:
                                        LOW_HUB12_A;
                                        LOW_HUB12_B;
                                        break;
                        case 1:
                                        HIGH_HUB12_A;
                                        LOW_HUB12_B;
                                        break;                                               
                        case 2:
                                        LOW_HUB12_A;
                                        HIGH_HUB12_B;
                                        break;                                               
                        case 3:
                                        HIGH_HUB12_A;
                                        HIGH_HUB12_B;
                                        break;                       
                        default:
                                        break;
                }
}




// 行选择 (1/4扫描)
void hub12Display(uint16_t bright,uint8_t *buffer)
{
    for(u8 s=0; s<4; s++){  // 4个行组(1/4扫描)
		  	HIGH_HUB12_OE; 
			
        hub12SelectRows(s);
       // Delay_us(20);
        LOW_HUB12_LAT;
			
      for(int i=0; i<SCAN_NUM; i++) {
            hub12DataSerialInput(buffer[s*SCAN_NUM + i]); //128字节
        }
		   
        HIGH_HUB12_LAT;
        LOW_HUB12_OE;
        Delay_us(bright);
        HIGH_HUB12_OE;	
  
    }
}

//高低字节交换,0b10110000 → 0b00001101
u8 SwapByte(u8 data)
{
	data=(data<<4)|(data>>4);
	data=((data<<2)&0xcc)|((data>>2)&0x33);
	data=((data<<1)&0xaa)|((data>>1)&0x55);
	return data;
}
// 打印buffer内容
void PrintBuffer(const char* name, uint8_t* buffer, uint32_t size) {
    for(uint32_t i = 0; i < size; i++) {
        printf("0x%02X ", buffer[i]);
        if((i + 1) % 16 == 0) {
            printf("\n");
        }
    }
    printf("\n");
}

// 打印dispram的数据结构
void PrintDispramStructure(void) {
    printf("\n Dispram 数据结构 \n");
    for(uint8_t group = 0; group < 4; group++) {
        printf("Group %d:\n", group);
        for(uint8_t scan_line = 0; scan_line < 4; scan_line++) {
            printf("  Scan Line %d: ", scan_line);
            uint16_t start_idx = group * SCAN_NUM + scan_line * 8;
            for(uint8_t i = 0; i < 8; i++) {
                printf("0x%02X ", dispram[start_idx + i]);
            }
            printf("\n");
        }
    }
}

void WriteScreen(u8 location, u8 *text)
{
    u8 i; // 原始的扫描行组
    u8 j; // 显示缓冲区的标号
    u8 k;
    u8 buffer[32];
    
    const u8 row_map[4] = {1,2,3, 0}; //行扫描修正

    printf("\n\n=== Writing Hanzi at Location %d ===\n", location);
    
    /* 字模处理 */
    for(k = 0; k < 32; k++) {
        buffer[k] = text[31 - k];   //上下左右交换、高低字节交换
        buffer[k] = SwapByte(buffer[k]);
    }
    
    
    /* 写缓冲区 */
    for(i = 0; i < 4; i++) // 4组扫描行 (i=0,1,2,3 对应 4/1,4/2,4/3,4/4)
    {
        // 通过查找表,获取用于行修正计算的 "new_i"
        u8 new_i = row_map[i]; 

        for(j = (SCAN_NUM * i); j < SCAN_NUM * (i + 1); j++)
        {
            if((j >= ((SCAN_NUM * i) + 8 * location)) && (j <= ((SCAN_NUM * i + 3) + 8 * location)))//0-3列
            {
                // 使用 new_i 来计算行修正项,但 j 的范围仍基于原始的 i
                dispram[j] = buffer[(7 - 2 * new_i) + 8 * (j - 8 * location - SCAN_NUM * i)]; //右边字节
                //printf("Write: dispram[%d] = buffer[%d] = 0x%02X\n",  j, (7 - 2 * new_i) + 8 * (j - 8 * location - SCAN_NUM * i), dispram[j]);
            }

            if((j >= ((SCAN_NUM * i + 4) + 8 * location)) && (j <= ((SCAN_NUM * i + 7) + 8 * location)))//4-7列
            {
                dispram[j] = buffer[(6 - 2 * new_i) + 8 * (j - 8 * location - SCAN_NUM * i - 4)]; //左边字节
               // printf("Write: dispram[%d] = buffer[%d] = 0x%02X\n", j, (6 - 2 * new_i) + 8 * (j - 8 * location - SCAN_NUM * i - 4), dispram[j]);
            }
        }
    }
    
    PrintDispramStructure();
}

//静态显示汉字
void StaticDisplay(uint8_t font[4][32])
{

    memset(dispram, 0, sizeof(dispram));  // 清空显存

    //locatin,0-3,表示第几个汉字的位置
    for(u8 location=0; location<4; location++)
    {
        WriteScreen(location, font[location]);
    }
    

}

程序逻辑为:
给void StaticDisplay(uint8_t font[4][32])传入Chinese汉字点阵数组,
然后调用WriteScreen(location, font[location]);,先将Chinese汉字点阵数组中的字节进行上下左右交换、高低字节交换,映射顺序调整后,存入buffer数组,然后将数组通过,写缓存的映射逻辑,将数据存入dispram。
在main中循序调用hub12Display(200,dispram);将dispram。中的数据写映射到屏幕上

函数PrintDispramStructure();输出的日志如下:

UARTinit
init



 Dispram 数据结构 
Group 0:
  Scan Line 0: 0x02 0x28 0x49 0x00 0x08 0xA0 0x08 0x80 
  Scan Line 1: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 2: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 3: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
Group 1:
  Scan Line 0: 0x04 0x24 0x2A 0xFC 0x04 0xA0 0x40 0x80 
  Scan Line 1: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 2: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 3: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
Group 2:
  Scan Line 0: 0x08 0x45 0x14 0x04 0x02 0x10 0x40 0xFC 
  Scan Line 1: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 2: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 3: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
Group 3:
  Scan Line 0: 0x81 0x10 0x05 0x00 0x10 0x40 0x04 0x80 
  Scan Line 1: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 2: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Scan Line 3: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 

Dispram 为屏幕的映射逻辑,应该是一一对应16*64大小的屏幕中的每个像素
从日志中,看不出Chinese数组中的汉字点阵,调整到Dispram 后,有什么规律

我自己写了一个映射逻辑

// 屏幕尺寸配置
#define SCREEN_WIDTH       64      // 屏幕宽度(列数)
#define SCREEN_HEIGHT      16      // 屏幕高度(行数)
// 扫描方式配置
#define SCAN_MODE         4       // 扫描方式:4=1/4扫描,8=1/8扫描,16=1/16扫描
//一组多少行
#define ROW_NUM_GROUP      SCREEN_HEIGHT/SCAN_MODE
#define HANZI_WIDTH        16      // 单个汉字宽度
#define HANZI_HEIGHT       16      // 单个汉字高度
//汉字一行的字节量
#define HANZI_SIZE           HANZI_WIDTH/8

#define MAX_HANZI_NUM      (SCREEN_WIDTH / HANZI_WIDTH)  // 最大显示汉字数
#define BYTES_PER_ROW      (SCREEN_WIDTH / 8)           // 每行字节数
#define BUFFER_SIZE        (ROW_NUM_GROUP * BYTES_PER_ROW * SCAN_MODE) // 显存大小

u8 Chinese[4][32] =
{
    { 
0x00,0x01,0x00,0x01,0x3F,0x01,0x20,0x3F,0xA0,0x20,0x92,0x10,0x54,0x02,0x28,0x02,
0x08,0x02,0x14,0x05,0x24,0x05,0xA2,0x08,0x81,0x08,0x40,0x10,0x20,0x20,0x10,0x40,
    },

    {

			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },

    {
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },

    {
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
			
			0x00, 0x00,  
			0x00, 0x00, 
			0x00, 0x00, 
			0x00, 0x00,
    },


};



// 动态显存
uint8_t dispram[BUFFER_SIZE] = {0};

void hub12DataSerialInput(uint8_t data){
                uint16_t         i;
                for( i = 0; i < 8; i++){
                                if(data & 0x80){
                                        HIGH_HUB12_DR;
                                        
                                }else{
                                       LOW_HUB12_DR;
                                }
                                LOW_HUB12_CLK;
																Delay_us(2);     
                                HIGH_HUB12_CLK;
                                data        = data << 1;
                }
}


void hub12SelectRows(uint8_t row_address) {
    switch(SCAN_MODE) {
        case 4:  // 1/4扫描 - 使用A,B两根线
            switch(row_address & 0x03) {  // 只取低2位
                case 0: LOW_HUB12_A;  LOW_HUB12_B;  break;
                case 1: HIGH_HUB12_A; LOW_HUB12_B;  break;
                case 2: LOW_HUB12_A;  HIGH_HUB12_B; break;
                case 3: HIGH_HUB12_A; HIGH_HUB12_B; break;
            }
            break;
            
        case 8:  // 1/8扫描 - 使用A,B,C三根线
            if (row_address & 0x01) HIGH_HUB12_A; else LOW_HUB12_A;
            if (row_address & 0x02) HIGH_HUB12_B; else LOW_HUB12_B;
            if (row_address & 0x04) HIGH_HUB12_C; else LOW_HUB12_C;
            break;
            
        case 16: // 1/16扫描 - 使用A,B,C,D四根线
            if (row_address & 0x01) HIGH_HUB12_A; else LOW_HUB12_A;
            if (row_address & 0x02) HIGH_HUB12_B; else LOW_HUB12_B;
            if (row_address & 0x04) HIGH_HUB12_C; else LOW_HUB12_C;
            if (row_address & 0x08) HIGH_HUB12_D; else LOW_HUB12_D;
            break;
    }
}

void hub12Display(uint8_t *buffer) {
    for(uint8_t group = 0; group < ROW_NUM_GROUP; group++) {
        HIGH_HUB12_OE;  
        

        uint8_t row_address = group;
        hub12SelectRows(row_address);
        
        LOW_HUB12_LAT;  
        
        for(uint16_t i = 0; i < BYTES_PER_ROW * SCAN_MODE; i++) {
            hub12DataSerialInput(buffer[group * BYTES_PER_ROW * SCAN_MODE + i]);
        }
        
        HIGH_HUB12_LAT;  
        LOW_HUB12_OE;    
        Delay_us(300);  
    }
}
void PrintDispram(void) {
    printf("\n=== dispram dump (size=%d) ===\n", BUFFER_SIZE);
    for (int i = 0; i < BUFFER_SIZE; i++) {
        if (i % 8 == 0) {
            printf("\nRow %02d: ", i / 8);
        }
        printf("0x%02X ", dispram[i]);
    }
    printf("\n==============================\n");
}
//高低字节交换,0b10110000 → 0b00001101
u8 SwapByte(u8 data)
{
	data=(data<<4)|(data>>4);
	data=((data<<2)&0xcc)|((data>>2)&0x33);
	data=((data<<1)&0xaa)|((data>>1)&0x55);
	return data;
}

void WriteHanzi(uint8_t *hanzi_data, uint8_t hanzi_index) {

    uint8_t buffer[32];
    uint16_t dispram_index;
	//const u8 row_map[4] = {1,2,3, 0}; //行扫描修正
    uint8_t start_col_byte = hanzi_index * (HANZI_WIDTH / 8);
    printf("\n\rstart_col_byte=%d,\n\r",start_col_byte);		
	
			printf("buffer=\n\r");
    for(uint8_t k = 0; k < 32; k++) {
        buffer[k] = hanzi_data[31 - k];   // 左右反转
        buffer[k] = SwapByte(buffer[k]);  // 字节反转

			printf("0x%2X  ",buffer[k]);
			if(k%8==0)
				printf("\n\r");
    }
   
		printf("\n\rdispram=\n\r");
    for(uint8_t group = 0; group < ROW_NUM_GROUP; group++) {
        for(uint8_t scan_line = 0; scan_line < SCAN_MODE; scan_line++) {

            // 计算当前行在汉字数据中的行号
            uint8_t hanzi_row = group * SCAN_MODE + scan_line;
            // 处理该行的每个字节
            for(uint8_t col_byte = 0; col_byte < (HANZI_WIDTH / 8); col_byte++) {
                // 计算显存中的目标位置
                dispram_index = group * BYTES_PER_ROW * SCAN_MODE + 
                              scan_line * BYTES_PER_ROW + 
                              (start_col_byte + col_byte);
							//printf("dispram_index=%d,\n\r",dispram_index);
                // 计算汉字数据中的源位置
                uint8_t buf_index = hanzi_row * (HANZI_WIDTH / 8) + col_byte;
                //printf("buf_index=%d,\n\r",buf_index);

                if(dispram_index < BUFFER_SIZE && buf_index < 32) {
                    uint8_t hanzi_byte = buffer[buf_index];
                  dispram[dispram_index] = hanzi_byte;
									


									
                }
            }
        }
    }
		 PrintDispram();
}



// 静态显示示例
void StaticDisplayDemo(void) {
    memset(dispram, 0, BUFFER_SIZE);
	
    for(uint8_t i = 0; i < MAX_HANZI_NUM; i++) {
        WriteHanzi(Chinese[i],i);
    }
}

函数 PrintDispram();输出的日志如下

init


start_col_byte=0,

buffer=

0x 2  

0x 8  0x 4  0x 4  0x 8  0x 2  0x10  0x81  0x10  

0x45  0xA0  0x24  0xA0  0x28  0x40  0x10  0x40  

0x14  0x40  0x2A  0x 8  0x49  0x 4  0x 5  0xFC  

0x 4  0x80  0xFC  0x80  0x 0  0x80  0x 0  

dispram=

=== dispram dump (size=128) ===

Row 00: 0x02 0x08 0x00 0x00 0x00 0x00 0x00 0x00 
Row 01: 0x04 0x04 0x00 0x00 0x00 0x00 0x00 0x00 
Row 02: 0x08 0x02 0x00 0x00 0x00 0x00 0x00 0x00 
Row 03: 0x10 0x81 0x00 0x00 0x00 0x00 0x00 0x00 
Row 04: 0x10 0x45 0x00 0x00 0x00 0x00 0x00 0x00 
Row 05: 0xA0 0x24 0x00 0x00 0x00 0x00 0x00 0x00 
Row 06: 0xA0 0x28 0x00 0x00 0x00 0x00 0x00 0x00 
Row 07: 0x40 0x10 0x00 0x00 0x00 0x00 0x00 0x00 
Row 08: 0x40 0x14 0x00 0x00 0x00 0x00 0x00 0x00 
Row 09: 0x40 0x2A 0x00 0x00 0x00 0x00 0x00 0x00 
Row 10: 0x08 0x49 0x00 0x00 0x00 0x00 0x00 0x00 
Row 11: 0x04 0x05 0x00 0x00 0x00 0x00 0x00 0x00 
Row 12: 0xFC 0x04 0x00 0x00 0x00 0x00 0x00 0x00 
Row 13: 0x80 0xFC 0x00 0x00 0x00 0x00 0x00 0x00 
Row 14: 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
Row 15: 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
==============================

我需要的是能显示16*16的汉字,然后屏幕高度固定为16,然后可调节屏幕的宽度
但是屏幕实际现实的是混乱的,问题点就在于,实际的屏幕映射逻辑是怎样的??如何任意调节屏幕像素尺寸的同时,还能使汉字点阵正确映射到LED屏幕上???

新手村中打大boss的主页 新手村中打大boss | 初学一级 | 园豆:-3
提问于:2025-10-18 18:07
< >
分享
所有回答(1)
0

还没搞定呢. 这个输出逻辑看起来很简单.
就是一行一行扫描,一个bit一个bit输出.
你按bit去看,不按字节,可能好理解点.
跟我之前给的这个是一样的逻辑

// 简化的显示函数
void simple_display(uint8_t *buffer) {
    clear_screen();

    char output[16][16*4+2] = {0};

    for(int k=0;k<4;k++){
        int bit = 0;
        for(int i=0;i<16;i++){
            uint16_t value = buffer[2*i + 32*k]  | buffer[32*k + 2*i+1]<<8;
            for(int j=0;j<16;j++){
                if(value & (1<<j)){
                    output[i][j + 16*k] = '*';  // 使用星号表示点亮的像素
                }else{
                    output[i][j+ 16*k] = ' ';
                }
            }
        }
    }

    for(int i=0;i<16;i++){
        printf("%s\n", output[i]);
    }
    fflush(stdout);
}
www378660084 | 园豆:1563 (小虾三级) | 2025-10-20 14:40

惭愧,惭愧,这个项目有点小复杂,总的这个项目主要是实现一个LED控制协议,通过串口发送16进制指令,发送屏幕显示1616和3232的汉字,设置字体颜色,屏幕分区,屏幕像素尺寸,还有4G 远程等等,这段时间在忙其他功能,都做的差不多了,然后调试的时候,发现原例程只能用于1664的屏幕,里面的参数设置的很刁钻,现在我要实现的是能适应任意16行N列的屏幕,您说的一行一行扫描,一个bit一个bit输出,这个我是明白的,但他这个单块LED屏幕并不是有序的一行接一行,
我往预计的位置写入数据,但是与实际显示的位置,差的挺远,所以我在试图摸清里面的规则

支持(0) 反对(0) 新手村中打大boss | 园豆:-3 (初学一级) | 2025-10-20 15:05

再补充一下,程序还要能适应各种合适的扫描方式1/4、1/8、1/16这些,然后我手上又没有数据手册,所以实现起来,有一定难度

支持(0) 反对(0) 新手村中打大boss | 园豆:-3 (初学一级) | 2025-10-20 15:40

@新手村中打大boss: 有3个事情
a.16列16行用4行输出, 数组被转换成了 (16 4) * 4, 每4行拼成了一个新的行.
b.拼接后的4行又有乱序: new_i = row_map[i];
c.奇数列在一起显示,偶数列在一起显示
用二维数组可能好理解一点

支持(0) 反对(0) www378660084 | 园豆:1563 (小虾三级) | 2025-10-20 15:50
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册