首页 新闻 会员 周边 捐助

P10的LED屏,如何消除重影,鬼影??

1
[已解决问题] 解决于 2025-08-26 16:42

<details>
<summary>点击查看代码</summary>

 
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "Delay.h"
#include "LED_Driver.h"
#include "stm32f10x_it.h"
#include "Handlers.h"
#include "hanzi.h"
/*
屏幕大小为16*64,0-15行,0-63列
一、屏幕数据流向,箭头方向的左下角,到右上角
二、点阵字模要求
1,点阵字模软件:PCtoLCD2002
2,字模选项
(1)点阵格式:阴码
(2)取模方式:逐行式
(3)每行显示数据:点阵16,索引16
(4)取模走向:逆向
(5)输出进制:16进制
(6)格式:C51
            
*/
#define HC245_A_PORT GPIOA
#define HC245_B_PORT GPIOB
#define HC245_C_PORT GPIOC
 
/*
HC245    
DIR接5V   OE接GND   A->B
HUB12
第一列引脚,从上到下为OE,N,N,N,N,N,N,N
第二列引脚,从上到下为A,B,C,SCK,LAT,R,G,D
*/
 
#define A_A1_PA0   GPIO_Pin_0
#define B_A2_PA1   GPIO_Pin_1
#define C_A3_PB5   GPIO_Pin_5
#define D_A4_PB4   GPIO_Pin_4
#define OE_A5_PA8  GPIO_Pin_8
#define LAT_A6_PC8 GPIO_Pin_8
#define SCK_A7_PC9 GPIO_Pin_9
#define R_A15_PB8     GPIO_Pin_8
#define G_A16_PC0     GPIO_Pin_0
 
 
void HC245_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
    
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;    
    
    GPIO_InitStructure.GPIO_Pin=A_A1_PA0|B_A2_PA1|OE_A5_PA8;    
  GPIO_Init(HC245_A_PORT,&GPIO_InitStructure);    
    
    GPIO_InitStructure.GPIO_Pin=C_A3_PB5|D_A4_PB4|R_A15_PB8;    
  GPIO_Init(HC245_B_PORT,&GPIO_InitStructure);
    
  GPIO_InitStructure.GPIO_Pin=LAT_A6_PC8|SCK_A7_PC9|G_A16_PC0;    
  GPIO_Init(HC245_C_PORT,&GPIO_InitStructure);
 
}
 
 
// 数据线 - 红色 (R -> PB8)
#define HIGH_HUB12_DR     (GPIOB->BSRR = GPIO_Pin_8)    
#define LOW_HUB12_DR      (GPIOB->BRR  = GPIO_Pin_8)
 
// 数据线 - 绿色 (G -> PC0)
#define HIGH_HUB12_DG     (GPIOC->BSRR = GPIO_Pin_0)     
#define LOW_HUB12_DG      (GPIOC->BRR  = GPIO_Pin_0)
 
// 时钟线 (SCK / SH_CP -> PC9)
#define HIGH_HUB12_CLK    (GPIOC->BSRR = GPIO_Pin_9)     
#define LOW_HUB12_CLK     (GPIOC->BRR  = GPIO_Pin_9)
 
// 锁存线 (LAT / ST_CP -> PC8)
#define HIGH_HUB12_LAT    (GPIOC->BSRR = GPIO_Pin_8)     
#define LOW_HUB12_LAT     (GPIOC->BRR  = GPIO_Pin_8)
 
// 输出使能 (OE -> PA8, 低电平有效)
#define HIGH_HUB12_OE     (GPIOA->BSRR = GPIO_Pin_8)     
#define LOW_HUB12_OE      (GPIOA->BRR  = GPIO_Pin_8)     
 
// 行地址线 A (A -> PA0)
#define HIGH_HUB12_A      (GPIOA->BSRR = GPIO_Pin_0)
#define LOW_HUB12_A       (GPIOA->BRR  = GPIO_Pin_0)
 
// 行地址线 B (B -> PA1)
#define HIGH_HUB12_B      (GPIOA->BSRR = GPIO_Pin_1)
#define LOW_HUB12_B       (GPIOA->BRR  = GPIO_Pin_1)
 
// 行地址线 C (C -> PB5)
#define HIGH_HUB12_C      (GPIOB->BSRR = GPIO_Pin_5)
#define LOW_HUB12_C       (GPIOB->BRR  = GPIO_Pin_5)
 
// 行地址线 D (D -> PB4)
#define HIGH_HUB12_D      (GPIOB->BSRR = GPIO_Pin_4)
#define LOW_HUB12_D       (GPIOB->BRR  = GPIO_Pin_4)
 
/* 显示汉字总数 */
#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}; 
uint8_t copy_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;
                                }
                                                                Delay_us(1); 
                                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(uint16_t bright,uint8_t *buffer)
{
    for(u8 s=0; s<4; s++){  // 4个行组(1/4扫描)
            Delay_us(2);
              HIGH_HUB12_OE; 
            Delay_us(1);            
        hub12SelectRows(s);
                        Delay_us(2);
        LOW_HUB12_LAT;
                        Delay_us(2);
      for(int i=0; i<SCAN_NUM; i++) {
            hub12DataSerialInput(buffer[s*SCAN_NUM + i]); //128字节
        }
           
        HIGH_HUB12_LAT;
                Delay_us(2); 
        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;
}
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 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 display_buffer[16][10]; // 扩展为10字节,容纳5个汉字
u8 hanzi_data[4][32]; // 4个汉字的缓冲区
 
// 初始化显示缓冲区
void InitDisplayBuffer() {
    memset(display_buffer, 0, sizeof(display_buffer));
    // 初始化前4个汉字
    for(u8 i = 0; i < 4; i++) {
        for(u8 row = 0; row < 16; row++) {
            display_buffer[row][i*2] = Chinese[i][row*2];
            display_buffer[row][i*2+1] = Chinese[i][row*2+1];
        }
    }
}
 
void ScrollTextLeft(u8 Chinese[][32], u8 hanzi_num) {
    static u8 start_index = 0;
    static u8 shift_count = 0;
    u8 next_index = 4 % hanzi_num; // 下一个汉字索引
    
    // 移动一列
    for (u8 row = 0; row < 16; row++) {
        // 将当前字节左移1位,然后从下一个字节的最高位取1位,放到当前字节的最低位
        for (u8 col = 0; col < 9; col++) {
            display_buffer[row][col] = (display_buffer[row][col] << 1) |  
                                      ((display_buffer[row][col+1] & 0x80) ? 1 : 0);
        }
        //将第一个字节左移8位到高8位,将第二个字节放到低8位
        u16 row_data = (Chinese[next_index][row*2] << 8) | Chinese[next_index][row*2+1];
                //从16位数据中按列提取相应的位
        u8 bit_value = (row_data >> (15 - shift_count)) & 0x01;
                //将取到的位放到显示缓冲区最右侧字节的最低位
        display_buffer[row][9] = (display_buffer[row][9] << 1) | bit_value;
    }
    
    // 每次循环提取一个完整的汉字送到 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]);
    }
    
    __disable_irq();
        memset(copy_dispram, 0, sizeof(copy_dispram));
    memcpy(copy_dispram, dispram, sizeof(dispram));
    __enable_irq();
    
    // 更新计数和索引
    shift_count++;
    if (shift_count >= 16) {
        shift_count = 0;
        start_index = (start_index + 1) % hanzi_num;
        next_index = (start_index + 4) % hanzi_num;
        
        // 将下一个汉字加载到缓冲区
        for(u8 row = 0; row < 16; row++) {
            display_buffer[row][8] = Chinese[next_index][row*2];
            display_buffer[row][9] = Chinese[next_index][row*2+1];
        }
    }
    
    Delay_ms(30);
}
 
 

</details>

<details>
<summary>点击查看代码</summary>

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Serial.h"
#include "LED_Driver.h"
#include "ChineseFont.h"
#include "flash.h"
#include "hanzi.h"
 
volatile uint8_t TYPE=2;
 
//TYPE=1静态显示
void Static_Type(void)
{
    if(TYPE)
    {
        StaticDisplay(Chinese);
    }
}
 
int main(void)
{
 
    Serial_Init();    
 
    HC245_Init();
    FLASH_Init();
    printf("init\n");
    
//TYPE=1静态显示
Static_Type();
 
InitDisplayBuffer();
 while (1) {
      hub12Display(300,copy_dispram);
     switch(TYPE)
{
      case 2:
        ScrollTextLeft(Chinese,4);
        //Delay_ms(30);
        break;
        case 3:
 
        break;
        
}    
 
    }
 
}
 

</details>

当TYPE=1的时候,因为只往dispram写了一次数据,所以dispram内容是固定的,所以在main中循环hub12Display(300,copy_dispram);的时候,是一直在读取dispram,所以LED屏幕在显示的时候,是稳定的u8 Chinese[4][32] 中的四个汉字,没有鬼影,重叠的现象。

当TYPE=2的时候,ScrollTextLeft循环,然后循环执行WriteScreen(i, hanzi_data);计算出dispram,所以dispram在不断地被写入,数据在一直更新,所以再调用hub12Display写LED屏的时候,屏幕上面有鬼影,重影。
在void ScrollTextLeft(u8 Chinese[][32], u8 hanzi_num)中,添加了双重缓存区,
__disable_irq();
memset(copy_dispram, 0, sizeof(copy_dispram));
memcpy(copy_dispram, dispram, sizeof(dispram));
__enable_irq();,也没有用,各位,有没有什么办法?

新手村中打大boss的主页 新手村中打大boss | 初学一级 | 园豆:26
提问于:2025-08-25 17:22
< >
分享
最佳答案
1

你这也不是双缓冲区吧,单线程的,没看到两个缓冲区起到什么作用.
应该还是更新逻辑有问题.
你可以试试每次输出之前,把所有的led都关了,看看还有没有重影这些问题.
如果输出之前把led全关了不会重影,而是会闪一下,那就得调整更新逻辑了.

奖励园豆:5
www378660084 | 小虾三级 |园豆:1521 | 2025-08-26 13:09

感谢,感谢,确实是更新逻辑有问题.修改后,重影消失

新手村中打大boss | 园豆:26 (初学一级) | 2025-08-26 16:41
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册