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 时,表示一个汉字的全部列已经滚动完毕,正常情况下,此时会切换到下一个汉字,并显示。但在实际滚动过程中,汉字是一边滚动同时,会切换成下一个汉字,四个汉字循环切换
是什么原因,如何解决?
没太明白你想要什么效果. 如果你是想显示完第一汉字清空,然后再显示第二个汉字. 那在切换时候清空一下buffer就可以了.
您好,我这边是这样的,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,显示一次,
现在面临的问题是,汉字是一边滚动的同时,又循环切换成下一个汉字
动图中,这个汉字是在滚动的同时,又立马切换成下一个汉字了
麻烦您帮忙看一下具体是什么原因?十分感谢
@新手村中打大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: 感谢,十分感谢,现在问题解决了,您方便加个微信吗,表示感谢一下
@新手村中打大boss: 问题解决了就行,感谢就不用了_