首页 新闻 会员 周边 捐助

hub12,4/1扫的led屏幕中的汉字,一边滚动,一边切换成下一个汉字,是什么原因?有偿请教

0
悬赏园豆:20 [已解决问题] 解决于 2025-09-09 09:40

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,0x04,0x01,0xC8,0x3C,0x48,0x24,0x40,0x24,0x40,0x24,0x4F,0x24,0x48,0x24,
0x48,0x24,0x48,0x2D,0xC8,0x14,0x48,0x04,0x08,0x04,0x14,0x04,0xE2,0x7F,0x00,0x00,
    },

    {
0x80,0x00,0x84,0x10,0x88,0x10,0x90,0x08,0x90,0x04,0x80,0x00,0xFF,0x7F,0x20,0x02,
0x20,0x02,0x20,0x02,0x20,0x02,0x10,0x42,0x10,0x42,0x08,0x42,0x04,0x7C,0x03,0x00,
    },

    {
0x10,0x01,0x10,0x01,0x10,0x01,0x92,0x7F,0x92,0x02,0x52,0x04,0x32,0x04,0x12,0x00,
0x92,0x3F,0x92,0x24,0x92,0x24,0x92,0x24,0x92,0x24,0x90,0x3F,0x90,0x20,0x10,0x00,
    },

};



uint8_t dispram[128] = {0}; 

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

                                LOW_HUB12_CLK;
																Delay_us(1);     
                                HIGH_HUB12_CLK;
																Delay_us(1); 
                                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(uint8_t *buffer)
{
      for(u8 s=0; s<4; s++){  // 4个行组(1/4扫描)
			  HIGH_HUB12_OE;  // 关显示
        hub12SelectRows(s);
        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(300);
       
    }
}

//高低字节交换,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 WriteScreen(u8 locatin, u8 *text)
{
    u8 i; // 原始的扫描行组
    u8 j; // 显示缓冲区的标号
    u8 k;
    u8 buffer[32];
    
    const u8 row_map[4] = {1,2,3, 0}; //行扫描修正

    /* 字模处理 */
    for(k = 0; k < 32; k++) buffer[k] = text[31 - k];   //从左到右,从上到下依次显示
    for(k = 0; k < 32; 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 * locatin)) && (j <= ((SCAN_NUM * i + 3) + 8 * locatin)))//0-3列
            {
                // 使用 new_i 来计算行修正项,但 j 的范围仍基于原始的 i
                dispram[j] = buffer[(7 - 2 * new_i) + 8 * (j - 8 * locatin - SCAN_NUM * i)]; //右边字节				
            }

            if((j >= ((SCAN_NUM * i + 4) + 8 * locatin)) && (j <= ((SCAN_NUM * i + 7) + 8 * locatin)))//4-7列
            {
                dispram[j] = buffer[(6 - 2 * new_i) + 8 * (j - 8 * locatin - SCAN_NUM * i - 4)]; //左边字节
            }
        }
    }
		
}



void TIM3_Display (void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
	NVIC_InitTypeDef NVIC_InitStructure;

	TIM_TimeBaseStructure.TIM_Prescaler = 719;                    // 预分频器:72MHz / 72 = 1MHz
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  // 向上计数
	TIM_TimeBaseStructure.TIM_Period =1999;                    
	TIM_TimeBaseStructure.TIM_ClockDivision = 1;//时钟分频系数
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;//重复计数器

	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	// 3. 清除中断标志,开启中断
	TIM_ClearFlag(TIM3, TIM_FLAG_Update);
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

	// 4. 配置 NVIC
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	// 5. 启动定时器
	TIM_Cmd(TIM3, ENABLE);
}
uint8_t update_display = 0;
uint8_t scroll_flag = 0;
uint8_t count=0;
void TIM3_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
    {
			update_display = 1; 
			
			count++;
			if(count>=5)//count调节滚动速度
			{
			 scroll_flag = 1;  
				count =0;
			}
			
			TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
           
    }
}

//静态显示汉字
void StaticDisplay(uint8_t font[4][32])
{
	memset(dispram, 0, sizeof(dispram));  // 清空显存
	//locatin,0-3,表示第几个汉字的位置
	for(u8 locatin=0;locatin<4;locatin++)
	{
    WriteScreen(locatin, font[locatin]);
	}

}

u8 hanzi_data[4][32]; // 4个汉字的缓冲区
u8 display_buffer[16][8]; 
void ScrollTextLeft(u8 Chinese[][32], u8 hanzi_num) {
    static u8 start_index = 0;
    static u8 shift_count = 0;
	  static u8 next_index = 0;
	
	//将Chinese中的字节,一位一位的移入display_buffer最后一个字节的最低位
		  for(u8 num =0;num<16;num++)
		{
		    //将第一个字节左移8位到高8位,将第二个字节放到低8位
        u16 row_data = ((u16)Chinese[next_index][num*2] << 8) | Chinese[next_index][num*2+1];
				//从16位数据中按列提取相应的位
        u8 bit_value = (row_data >> (15 - shift_count)) & 0x01;
				//将取到的位放到显示缓冲区最右侧字节的最低位
        display_buffer[num][7] = (display_buffer[num][7] << 1) | bit_value;
			row_data=0x0000;
			bit_value=0x0000;
	
		}

    // 一列一列往左移动
    for (u8 row = 0; row < 16; row++)//16行
	 {
        // 将当前字节左移1位,然后从下一个字节的最高位取1位,放到当前字节的最低位
        for (u8 Bit = 0; Bit <7; Bit++) //7个字节
		    {
           display_buffer[row][Bit] = (display_buffer[row][Bit] << 1) |  ((display_buffer[row][Bit+1] & 0x80) ? 1 : 0);
        }
				
    }
		// 每次循环提取一个完整的汉字送到 WriteScreen 函数进行显示处理
    for (u8 i = 0; i < 4; i++) {
        for (u8 row = 0; row < 16; row++) {
            hanzi_data[i][row*2] = display_buffer[row][i*2];
            hanzi_data[i][row*2+1] = display_buffer[row][i*2+1];
        }
        WriteScreen(i, hanzi_data[i]);
    }
    // 更新计数和索引
    shift_count++;
    if (shift_count >= 15) {
        shift_count = 0;
			  start_index = (start_index + 1) % hanzi_num;
        //下一个汉字
        next_index = (start_index + 4) % hanzi_num;
    }		
}
、、、


ScrollTextLeft在main中循环的时候,
函数		  for(u8 num =0;num<16;num++)
		{
		    //将第一个字节左移8位到高8位,将第二个字节放到低8位
        u16 row_data = (Chinese[next_index][num*2] << 8) | Chinese[next_index][num*2+1];
				//从16位数据中按列提取相应的位
        u8 bit_value = (row_data >> (15 - shift_count)) & 0x01;
				//将取到的位放到显示缓冲区最右侧字节的最低位
        display_buffer[num][7] = (display_buffer[num][7] << 1) | bit_value;
			row_data=0x0000;
			bit_value=0x0000;
	
		}
将Chinese中的字节,一位一位的移入display_buffer最后一个字节的最低位,
然后函数
 for (u8 row = 0; row < 16; row++)//16行
	 {
        // 将当前字节左移1位,然后从下一个字节的最高位取1位,放到当前字节的最低位
        for (u8 Bit = 0; Bit <7; Bit++) //7个字节
		    {
            display_buffer[row][Bit] = (display_buffer[row][Bit] << 1) |  
                                      ((display_buffer[row][Bit+1] & 0x80) ? 1 : 0);
        }
				
    }
 将数据一列一列往左移动,最后每次循环提取一个完整的汉字送到 WriteScreen 函数进行显示处理
    for (u8 i = 0; i < 4; i++) {
        for (u8 row = 0; row < 16; row++) {
            hanzi_data[i][row*2] = display_buffer[row][i*2];
            hanzi_data[i][row*2+1] = display_buffer[row][i*2+1];
        }
        WriteScreen(i, hanzi_data[i]);
    }
,但是出现一个问题
当 shift_count 达到 16 时,表示一个汉字的全部列已经滚动完毕,正常情况下,此时会切换到下一个汉字,并显示。但在实际滚动过程中,汉字是一边滚动同时,会切换成下一个汉字,四个汉字循环切换
是什么原因,如何解决?
新手村中打大boss的主页 新手村中打大boss | 初学一级 | 园豆:3
提问于:2025-09-02 17:22
< >
分享
最佳答案
0

没太明白你想要什么效果. 如果你是想显示完第一汉字清空,然后再显示第二个汉字. 那在切换时候清空一下buffer就可以了.

收获园豆:20
www378660084 | 小虾三级 |园豆:1561 | 2025-09-08 19:01

您好,我这边是这样的,LED屏幕大小是16行64列的,汉字大小是1616的,也就是一个屏幕,正好可以显示四个汉字,程序中的Chinese数组保存的是四个测试的汉字,我现在想做的是使这四个汉字,在屏幕上面,循环滚动起来,跟户外广告牌的汉字滚动一样

ScrollTextLeft 函数实现了汉字的左滚动显示功能
当在main中循环执行ScrollTextLeft的时候
这段函数中,

	  for(u8 num =0;num<16;num++)
	{
    u16 row_data = ((u16)Chinese[next_index][num*2] << 8) | Chinese[next_index][num*2+1];
			//从16位数据中按列提取相应的位
    u8 bit_value = (row_data >> (15 - shift_count)) & 0x01;
			//将取到的位放到显示缓冲区16行的最后一字节的最后一位
    display_buffer[num][7] = (display_buffer[num][7] << 1) | bit_value;
	}

将Chinese中的字节,每个汉字的两个字节一组合,将第一个字节左移8位到高8位,将第二个字节放到低8位,成16位,将这16位全部移入display_buffer[num][7]中,然后再进行 16行,8个字节,每个字节往左移动一位
for (u8 row = 0; row < 16; row++)//16行
{
for (u8 Bit = 0; Bit <7; Bit++) //7个字节
{
display_buffer[row][Bit] = (display_buffer[row][Bit] << 1) | ((display_buffer[row][Bit+1] & 0x80) ? 1 : 0);
}

}

最后 每次循环,就位移1位,然后调用 WriteScreen 函数进行显示处理
for (u8 i = 0; i < 4; i++) {
    for (u8 row = 0; row < 16; row++) {
        hanzi_data[i][row*2] = display_buffer[row][i*2];
        hanzi_data[i][row*2+1] = display_buffer[row][i*2+1];
    }
    WriteScreen(i, hanzi_data[i]);
			memset(hanzi_data,0,sizeof(hanzi_data));
}

每次循环1次,就位移1位,每次位移一位,就调用WriteScreen,显示一次,

现在面临的问题是,汉字是一边滚动的同时,又循环切换成下一个汉字6367995f43cfed336dce -small-original
动图中,这个汉字是在滚动的同时,又立马切换成下一个汉字了
麻烦您帮忙看一下具体是什么原因?十分感谢

新手村中打大boss | 园豆:3 (初学一级) | 2025-09-08 19:22

@新手村中打大boss: 你的滚动算法不太对. 我弄了个linux模拟显示的,你可以对比下看看.里面有个正常的,还有一个跟你原来的滚动时一样的

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>

typedef unsigned char u8;
typedef unsigned short u16;

/* 显示汉字总数 */
#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,0x04,0x01,0xC8,0x3C,0x48,0x24,0x40,0x24,0x40,0x24,0x4F,0x24,0x48,0x24,
        0x48,0x24,0x48,0x2D,0xC8,0x14,0x48,0x04,0x08,0x04,0x14,0x04,0xE2,0x7F,0x00,0x00,
    },
    {
        0x80,0x00,0x84,0x10,0x88,0x10,0x90,0x08,0x90,0x04,0x80,0x00,0xFF,0x7F,0x20,0x02,
        0x20,0x02,0x20,0x02,0x20,0x02,0x10,0x42,0x10,0x42,0x08,0x42,0x04,0x7C,0x03,0x00,
    },
    {
        0x10,0x01,0x10,0x01,0x10,0x01,0x92,0x7F,0x92,0x02,0x52,0x04,0x32,0x04,0x12,0x00,
        0x92,0x3F,0x92,0x24,0x92,0x24,0x92,0x24,0x92,0x24,0x90,0x3F,0x90,0x20,0x10,0x00,
    },
};

uint8_t dispram[128] = {0};

// 延时函数(微秒级)
void delay_us(int us) {
    usleep(us);
}

// 延时函数(毫秒级)
void delay_ms(int ms) {
    usleep(ms * 1000);
}

// 清屏函数
void clear_screen() {
    printf("\033[2J\033[H");
}

// 高低字节交换
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 WriteScreen(u8 locatin, u8 *text) {
    memcpy(dispram + locatin * 32, text, 32);
}

// 简化的显示函数
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);
}

//静态显示汉字
void StaticDisplay(uint8_t font[4][32]) {
    memset(dispram, 0, sizeof(dispram));  // 清空显存
    //locatin,0-3,表示第几个汉字的位置
    for(u8 locatin = 0; locatin < 4; locatin++) {
        WriteScreen(locatin, font[locatin]);
    }
}

u8 hanzi_data[4][32]; // 4个汉字的缓冲区
u8 display_buffer[16][8];

void ScrollTextLeft(u8 Chinese[][32], u8 hanzi_num) {
    static u8 start_index = 0;
    static u8 shift_count = 0;
    static u8 next_index = 0;

    //将Chinese中的字节,一位一位的移入display_buffer最后一个字节的最低位
    for(u8 num = 0; num < 16; num++) {
        u16 row_data = (u16)Chinese[next_index][num*2] | Chinese[next_index][num*2+1] << 8;
        //从16位数据中按列提取相应的位
        u8 bit_value = (row_data >> shift_count) & 0x01;
        //将取到的位放到显示缓冲区最右侧字节的最低位
        display_buffer[num][7] = (display_buffer[num][7] >> 1) | (bit_value << 7);
    }

    // 一列一列往左移动
    for (u8 row = 0; row < 16; row++) { //16行
        // 将当前字节左移1位,然后从下一个字节的最高位取1位,放到当前字节的最低位
        for (u8 byte = 0; byte < 7; byte++) { //7个字节
            display_buffer[row][byte] = (display_buffer[row][byte] >> 1) | ((display_buffer[row][byte+1] & 0x01) ? 0x80 : 0);
        }
    }

    //每次循环提取一个完整的汉字送到 WriteScreen 函数进行显示处理
    for (u8 i = 0; i < 4; i++) {
        for (u8 row = 0; row < 16; row++) {
            hanzi_data[i][row*2] = display_buffer[row][i*2];
            hanzi_data[i][row*2+1] = display_buffer[row][i*2+1];
        }
        WriteScreen(i, hanzi_data[i]);
    }


    // 更新计数和索引
    shift_count++;
    if (shift_count >= 16) {  // 修正:每个汉字16位,应该是0-15共16次
        shift_count = 0;
        start_index = (start_index + 1) % hanzi_num;
        //下一个汉字
        next_index = (start_index + 4) % hanzi_num;
    }
}
void ScrollTextLeftOld(u8 Chinese[][32], u8 hanzi_num) {
    static u8 start_index = 0;
    static u8 shift_count = 0;
	  static u8 next_index = 0;

	//将Chinese中的字节,一位一位的移入display_buffer最后一个字节的最低位
		  for(u8 num =0;num<16;num++)
		{
		    //将第一个字节左移8位到高8位,将第二个字节放到低8位
        u16 row_data = ((u16)Chinese[next_index][num*2] << 8) | Chinese[next_index][num*2+1];
				//从16位数据中按列提取相应的位
        u8 bit_value = (row_data >> (15 - shift_count)) & 0x01;
				//将取到的位放到显示缓冲区最右侧字节的最低位
        display_buffer[num][7] = (display_buffer[num][7] << 1) | bit_value;
			row_data=0x0000;
			bit_value=0x0000;

		}

    // 一列一列往左移动
    for (u8 row = 0; row < 16; row++)//16行
	 {
        // 将当前字节左移1位,然后从下一个字节的最高位取1位,放到当前字节的最低位
        for (u8 Bit = 0; Bit <7; Bit++) //7个字节
		    {
           display_buffer[row][Bit] = (display_buffer[row][Bit] << 1) |  ((display_buffer[row][Bit+1] & 0x80) ? 1 : 0);
        }

    }
		// 每次循环提取一个完整的汉字送到 WriteScreen 函数进行显示处理
    for (u8 i = 0; i < 4; i++) {
        for (u8 row = 0; row < 16; row++) {
            hanzi_data[i][row*2] = display_buffer[row][i*2];
            hanzi_data[i][row*2+1] = display_buffer[row][i*2+1];
        }
        WriteScreen(i, hanzi_data[i]);
    }
    // 更新计数和索引
    shift_count++;
    if (shift_count >= 15) {
        shift_count = 0;
			  start_index = (start_index + 1) % hanzi_num;
        //下一个汉字
        next_index = (start_index + 4) % hanzi_num;
    }
}

int main() {
    printf("LED点阵显示程序 - Linux版本\n");
    printf("支持两种显示模式:\n");
    printf("1. 静态显示\n");
    printf("2. 滚动显示\n");
    printf("2. old滚动显示\n");
    printf("选择模式 (1或2): ");

    int mode;
    scanf("%d", &mode);

    if(mode == 1) {
        // 静态显示模式
        printf("\n静态显示汉字...\n");
        StaticDisplay(Chinese);
        simple_display(dispram);

        printf("按回车键退出...");
        getchar();
        getchar();
    } else if(mode == 2) {
        // 滚动显示模式
        printf("\n滚动显示汉字...\n");
        printf("按Ctrl+C退出\n");
        delay_ms(1000);

        // 初始化显示缓冲区
        memset(display_buffer, 0, sizeof(display_buffer));
        memset(dispram, 0, sizeof(dispram));

        while(1) {
            ScrollTextLeft(Chinese, 4);
            simple_display(dispram);
            delay_ms(100);  // 控制滚动速度
        }
    }else if(mode == 3) {
        // 滚动显示模式
        printf("\nold滚动显示汉字...\n");
        printf("按Ctrl+C退出\n");
        delay_ms(1000);

        // 初始化显示缓冲区
        memset(display_buffer, 0, sizeof(display_buffer));
        memset(dispram, 0, sizeof(dispram));

        while(1) {
            ScrollTextLeftOld(Chinese, 4);
            simple_display(dispram);
            delay_ms(100);  // 控制滚动速度
        }
    }

    return 0;
}

www378660084 | 园豆:1561 (小虾三级) | 2025-09-08 22:12

@www378660084: 感谢,十分感谢,现在问题解决了,您方便加个微信吗,表示感谢一下

新手村中打大boss | 园豆:3 (初学一级) | 2025-09-09 09:19

@新手村中打大boss: 问题解决了就行,感谢就不用了_

www378660084 | 园豆:1561 (小虾三级) | 2025-09-09 16:19
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册