在万能的淘宝上买了一个P4室内全彩单元板,64x32,可以用来挂在客厅里用来显示时间,温湿度。
买的时候非常担心,问了好几家,想知道用的是什么扫描方案,用什么器件可以驱动起来,不过老板都拒绝回答,最终寂寞难耐之下,找了一家约了。
拿到屏后,庆幸芯片没有打磨,抱着屏直接找资料,居然是JXI5020,搭配D7528作为行选,这比几年前普遍用74HC595高大上多了。
用万用表测了线,控制也很简单。
U13_R---1 2---U13_G
U13_B---3 4---GND
U14_R---5 6---U14_G
U14_B---7 8---GND
A ---9 10---B
C ---11 12---EN_L/EN_H
DCLK---13 14---LE
GCLK---15 16---GND
现在问题来了,这个芯片适合FPGA驱动,因为需要用FPGA的高速IO才有机会实现它的SPWM,实现真彩的显示。
不过多年不用FPGA的我,表示爱不起,不过可以先用STM32F103显示7彩再说。
显示屏分上下两屏,上下两屏各有两片D7528,控制着16行。4片JXI5020级联,够成16*4共64个像素。
以下是实现扫描的代码:
void dispaly_process(void) //共7us
{
unsigned char i, j;
unsigned char k;
unsigned char rdata1[64];
unsigned int wdata1;
unsigned int wdata2;
for (j = 0; j < 64; j++)
{
JXI5020_PORT ->BRR = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
JXI5020_PORT ->BSRR = JXI5020_SCK;
JXI5020_PORT -> BRR = JXI5020_SCK;
}
JXI5020_PORT -> BSRR = JXI5020_LE;
JXI5020_PORT -> BRR = JXI5020_LE;
//JXI5020_PORT -> BSRR = JXI5020_OE;
if(led_dis_cnt < 8) D7258_PORT -> BSRR = D7258_EN;
else D7258_PORT -> BRR = D7258_EN;
switch(led_dis_cnt)
{
case 7:
case 15:
//D7258_PORT -> BRR = D7258_A | D7258_B | D7258_C;
D7258_PORT -> BSRR = (D7258_A | D7258_B | D7258_C)<<16;
break;
case 6:
case 14:
D7258_PORT -> BSRR = D7258_A | ((D7258_B | D7258_C)<<16);
//D7258_PORT -> BRR = D7258_B | D7258_C;
break;
case 5:
case 13:
//D7258_PORT -> BRR = D7258_A | D7258_C;
D7258_PORT -> BSRR = D7258_B | ((D7258_A | D7258_C)<<16);
break;
case 4:
case 12:
D7258_PORT -> BSRR = D7258_A | D7258_B |((D7258_C)<<16);;
//D7258_PORT -> BRR = D7258_C;
break;
case 3:
case 11:
//D7258_PORT -> BRR = D7258_A | D7258_B;
D7258_PORT -> BSRR = D7258_C | ((D7258_A | D7258_B)<<16);
break;
case 2:
case 10:
D7258_PORT -> BSRR = D7258_A | D7258_C | (D7258_B<<16);
//D7258_PORT -> BRR = D7258_B;
break;
case 1:
case 9:
//D7258_PORT -> BRR = D7258_A;
D7258_PORT -> BSRR = D7258_B | D7258_C | (D7258_A<<16);
break;
case 0:
case 8:
D7258_PORT -> BSRR = D7258_A | D7258_B | D7258_C;
break;
}
for (j = 64; j > 0; j--)
{
if(leddisbuf[64*led_dis_cnt + j - 1] & 0x01)JXI5020_PORT ->BSRR = UR14_SDO;
else JXI5020_PORT ->BRR = UR14_SDO;
if(leddisbuf[64*led_dis_cnt + j - 1] & 0x02)JXI5020_PORT ->BSRR = UG14_SDO;
else JXI5020_PORT ->BRR = UG14_SDO;
if(leddisbuf[64*led_dis_cnt + j - 1] & 0x04)JXI5020_PORT ->BSRR = UB14_SDO;
else JXI5020_PORT ->BRR = UB14_SDO;
//xia
if(leddisbuf[64*(16+led_dis_cnt) + j - 1] & 0x01)JXI5020_PORT ->BSRR = UR13_SDO;
else JXI5020_PORT ->BRR = UR13_SDO;
if(leddisbuf[64*(16+led_dis_cnt) + j - 1] & 0x02)JXI5020_PORT ->BSRR = UG13_SDO;
else JXI5020_PORT ->BRR = UG13_SDO;
if(leddisbuf[64*(16+led_dis_cnt) + j - 1] & 0x04)JXI5020_PORT ->BSRR = UB13_SDO;
else JXI5020_PORT ->BRR = UB13_SDO;
JXI5020_PORT ->BSRR = JXI5020_SCK;
JXI5020_PORT -> BRR = JXI5020_SCK;
}
JXI5020_PORT -> BSRR = JXI5020_LE;
JXI5020_PORT -> BRR = JXI5020_LE;
//JXI5020_PORT -> BRR = JXI5020_OE;
led_dis_cnt++;
if (led_dis_cnt == 16)
{
led_dis_cnt = 0;
}
}
代码中使用了leddisbuf[64*32]作为显存,代码实现了这一部分显存从上到下,从左到右的扫描,要更新显示内容,直接把数据填充到这个显存里就好了。
但是我并不满足于这样的显示,继续找资料,发现MBI5052居然脚序完全一致。
关键是MBI5052提供了SRAM,自己就可以显示真彩色,只要刷数据和提供垂直同步信号就好,相信用单片机就可以搞定。
于是我果断在淘宝上买了50片,花了一百。
同时也加购了两个P4的显示屏。
拿到芯片的第一件事就是把P4上原来的JXI5020取下来,再把新买的MBI5052焊上去,这个对于我来说还是基本没有难度的。
还有就是芯片也从STM32F103升级到了STM32F407,这个的IO速度要快许多。
不过令人头疼的是MBI5052的资料太难懂了,而且也没有对应的参考设计。
这个图里基本上说明了对MBI的操作,只是不够详细,比如倍频时怎么操作,帧与帧之间怎么操作。
以下是扫描的代码:
{
unsigned int sck_cnt;
unsigned short i,j,k,m;
unsigned short state_reg=(1<<GCLK_FROM_DCLK)|(15<<LINE_16_NUMS)|(0<<PWM_GCLKS16BIT)|(0<<GCLK_DOUBLE)|0x2b;
unsigned short red1;
unsigned short green1;
unsigned short blue1;
unsigned short red2;
unsigned short green2;
unsigned short blue2;
unsigned int mask;
unsigned int bufaddA,bufaddB;
//1写入状态寄存器
//a写状态寄存器之前的前置设定指定
JXI5020_PORT -> BSRRL = JXI5020_LE;//LE置为高电平
for(sck_cnt = 14;sck_cnt > 0;sck_cnt --)
{
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(2);
JXI5020_PORT -> BSRRH = JXI5020_SCK;
delay(1);
}
JXI5020_PORT -> BSRRH = JXI5020_LE;//LE置为低电平
//b写状态寄存器
for(j = 0;j < 4;j++)//每个通道由4片MBI5052级联,每个通道锁存一次数据
{
for(sck_cnt = 0;sck_cnt < 16;sck_cnt ++)
{
mask = 0x8000 >> sck_cnt;
if(j == 3)
if(sck_cnt == 12)JXI5020_PORT -> BSRRL = JXI5020_LE;//LE置为高电平
if(state_reg & mask)JXI5020_PORT -> BSRRL = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
else JXI5020_PORT ->BSRRH = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
delay(1);
JXI5020_PORT -> BSRRL = JXI5020_SCK;
delay(1);
JXI5020_PORT -> BSRRH = JXI5020_SCK;
//delay(1);
}
}
JXI5020_PORT -> BSRRH = JXI5020_LE;//LE置为低电平
//2写入16*2*16的数据
for(m = 0;m < 16;m++)//共有16行
{
for(i = 0;i < 16;i++)//每个MBI5052有16个通道,
//for(i = 0;i < 1;i++)
{
for(j = 0;j < 4;j++)//每个通道由4片MBI5052级联,每个通道锁存一次数据
//for(j = 0;j < 1;j++)
{
#if 0
bufaddA = 0x000000ff;
bufaddB = 0x0000ff00;;//8位原始数据对应到16位PWM数据
#else
bufaddA = leddisbuf[((m+1)*64-1) - j*16 - i];
bufaddB = leddisbuf[((m+1)*64-1) - j*16 - i + 64*16];//8位原始数据对应到16位PWM数据
#endif
#if 0
if((m == 1) && (i == 0) && (j == 0))//m == 4 //用于测试数据显示的位置
{
red1 = (bufaddA & 0xff0000) >> 8;//2 * (leddisbuf[i*64 + j] & 0xff0000>>16)上半屏数据
green1 = (bufaddA & 0x00ff00) >> 0;//2 * (leddisbuf[i*64 + j] & 0x00ff00>>8)上半屏数据
blue1 = (bufaddA & 0x0000ff) << 8;//上半屏数据
red2 = (bufaddB & 0xff0000) >> 8;//上半屏数据
green2 = (bufaddB & 0x00ff00) >> 0;//上半屏数据
blue2 = (bufaddB & 0x0000ff) << 8;//上半屏数据
}
else
{
red1 = 0x0000;
green1 = 0x0000;
blue1 = 0x0000;//(leddisbuf[bufaddA] & 0x0000ff) << 1;//上半屏数据
red2 = 0x0000;//(leddisbuf[bufaddB] & 0xff0000) >> 15;//上半屏数据
green2 = 0x0000;//(leddisbuf[bufaddB] & 0x00ff00) >> 7;//上半屏数据
blue2 = 0x0000;//(leddisbuf[bufaddB] & 0x0000ff) << 1;//上半屏数据
}
#else
red1 = (bufaddA & 0xff0000) >> 8;//2 * (leddisbuf[i*64 + j] & 0xff0000>>16)上半屏数据
green1 = (bufaddA & 0x00ff00) >> 0;//2 * (leddisbuf[i*64 + j] & 0x00ff00>>8)上半屏数据
blue1 = (bufaddA & 0x0000ff) << 8;//上半屏数据
red2 = (bufaddB & 0xff0000) >> 8;//上半屏数据
green2 = (bufaddB & 0x00ff00) >> 0;//上半屏数据
blue2 = (bufaddB & 0x0000ff) << 8;//上半屏数据
#endif
//if(m == 0)red1 = 255;
for(k = 0;k < 16;k++)
{
mask = 0x8000 >> k;
if(j == 3)
if(k == 15)JXI5020_PORT -> BSRRL = JXI5020_LE;//最后一个位LE置为高电平
#if 1
if(red1 & mask)JXI5020_PORT -> BSRRL = UR14_SDO;
else JXI5020_PORT -> BSRRH = UR14_SDO;
if(green1 & mask)JXI5020_PORT -> BSRRL = UG14_SDO;
else JXI5020_PORT -> BSRRH = UG14_SDO;
if(blue1 & mask)JXI5020_PORT -> BSRRL = UB14_SDO;
else JXI5020_PORT -> BSRRH = UB14_SDO;
if(red2 & mask)JXI5020_PORT -> BSRRL = UR13_SDO;
else JXI5020_PORT -> BSRRH = UR13_SDO;
if(green2 & mask)JXI5020_PORT -> BSRRL = UG13_SDO;
else JXI5020_PORT -> BSRRH = UG13_SDO;
if(blue2 & mask)JXI5020_PORT -> BSRRL = UB13_SDO;
else JXI5020_PORT -> BSRRH = UB13_SDO;
delay(1);
JXI5020_PORT -> BSRRL = JXI5020_SCK;
#else
JXI5020_PORT -> BSRRL = UR14_SDO | UG14_SDO;
#endif
delay(1);
//delayins++;
JXI5020_PORT -> BSRRH = JXI5020_SCK;
}
}
JXI5020_PORT -> BSRRH = JXI5020_LE;//LE置为低电平
}
}
//3发送至少50个GCLK 最后3个CLK拉高LE,发送VSYNC
for(sck_cnt = 0;sck_cnt < 60;sck_cnt ++)
{
if(sck_cnt == 60 - 3)JXI5020_PORT -> BSRRL = JXI5020_LE;//LE置为高电平
//if(state_reg & mask)JXI5020_PORT ->BSRRL = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
JXI5020_PORT ->BSRRH = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
delay(1);
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(1);
if(sck_cnt < 59)JXI5020_PORT -> BSRRH = JXI5020_SCK;//最后一个不要拉低
}
JXI5020_PORT -> BSRRH = JXI5020_LE;//LE置为低电平
delay(2);//LE下降沿与GCLK上升沿满足要求
//4消隐
for(sck_cnt = 0;sck_cnt < 512;sck_cnt ++)
{
JXI5020_PORT ->BSRRH = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(1);
//JXI5020_PORT -> BSRRH = JXI5020_SCK;
}
JXI5020_PORT -> BSRRH = JXI5020_SCK;//SCK置为低电平
//5开始显示
for(i = 0;i < 16; i++)
{
//设置行电平
if(i < 8) D7258_PORT -> BSRRL = D7258_EN;
else D7258_PORT -> BSRRH = D7258_EN;
switch(i)
{
case 7:
case 15:
D7258_PORT_BSRR = (D7258_A | D7258_B | D7258_C)<<16;
//D7258_PORT -> BSRRH = D7258_A | D7258_B | D7258_C;
//D7258_PORT -> BSRRL = 0;
break;
case 6:
case 14:
D7258_PORT_BSRR = D7258_A | ((D7258_B | D7258_C)<<16);
//D7258_PORT -> BSRRH = D7258_B | D7258_C;
//D7258_PORT -> BSRRL = D7258_A;
break;
case 5:
case 13:
D7258_PORT_BSRR = D7258_B | ((D7258_A | D7258_C)<<16);
//D7258_PORT -> BSRRH = D7258_A | D7258_C;
//D7258_PORT -> BSRRL = D7258_B;
break;
case 4:
case 12:
D7258_PORT_BSRR = D7258_A | D7258_B |((D7258_C)<<16);
//D7258_PORT -> BSRRH = D7258_C;
//D7258_PORT -> BSRRL = D7258_A | D7258_B;
break;
case 3:
case 11:
D7258_PORT_BSRR = D7258_C | ((D7258_A | D7258_B)<<16);
//D7258_PORT -> BSRRH = D7258_A | D7258_B;
//D7258_PORT -> BSRRL = D7258_C;
break;
case 2:
case 10:
D7258_PORT_BSRR = D7258_A | D7258_C | (D7258_B<<16);
//D7258_PORT -> BSRRH = D7258_B;
//D7258_PORT -> BSRRL = D7258_A | D7258_C;
break;
case 1:
case 9:
D7258_PORT_BSRR = D7258_B | D7258_C | (D7258_A<<16);
//D7258_PORT -> BSRRH = D7258_A;
//D7258_PORT -> BSRRL = D7258_B | D7258_C;
break;
case 0:
case 8:
D7258_PORT_BSRR = D7258_A | D7258_B | D7258_C;
//D7258_PORT -> BSRRH = 0;
//D7258_PORT -> BSRRL = D7258_A | D7258_B | D7258_C;
break;
}
for(sck_cnt = 0;sck_cnt < 1025;sck_cnt ++)//发送1024个脉冲,第1024个脉冲时停止 占时应约为1000/60/16=1.25ms
{
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(2);
if(sck_cnt < 1025-1)JXI5020_PORT -> BSRRH = JXI5020_SCK;
delay(1);
}
for(sck_cnt = 0;sck_cnt < 5;sck_cnt ++)//等待50个脉冲的消隐时间
{
JXI5020_PORT ->BSRRH = UR14_SDO | UG14_SDO | UB14_SDO | UR13_SDO | UG13_SDO | UB13_SDO;
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(1);
//JXI5020_PORT -> BSRRH = JXI5020_SCK;
}
JXI5020_PORT -> BSRRH = JXI5020_SCK;
}
//0复位
#if 1
JXI5020_PORT -> BSRRL = JXI5020_LE;//LE置为高电平
for(sck_cnt = 10;sck_cnt > 0;sck_cnt --)
{
JXI5020_PORT ->BSRRL = JXI5020_SCK;
delay(1);
JXI5020_PORT -> BSRRH = JXI5020_SCK;
delay(0);
}
JXI5020_PORT -> BSRRH = JXI5020_LE;//LE置为低电平
//for(sck_cnt = 10;sck_cnt > 0;sck_cnt --)
//{
// JXI5020_PORT -> BSRRH = JXI5020_SCK;
//}
#endif
}
加上RTC就可以实现时间和日期的显示了。
RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);
RTC_GetDate(RTC_Format_BIN, &RTC_DateStruct);
LED_P8x16Char(0,0,RTC_TimeStruct.RTC_Hours/10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +1*8,0,RTC_TimeStruct.RTC_Hours%10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Str(0 +2*8,0,":",crcolortmp,bkcolortmp);
LED_P8x16Char(0 +3*8,0,RTC_TimeStruct.RTC_Minutes/10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +4*8,0,RTC_TimeStruct.RTC_Minutes%10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Str(0 +5*8,0,":",crcolortmp,bkcolortmp);
LED_P8x16Char(0 +6*8,0,RTC_TimeStruct.RTC_Seconds/10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +7*8,0,RTC_TimeStruct.RTC_Seconds%10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +3*8,16,RTC_DateStruct.RTC_Month/10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +4*8,16,RTC_DateStruct.RTC_Month%10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Str(0 +5*8,16,"-",crcolortmp,bkcolortmp);
LED_P8x16Char(0 +6*8,16,RTC_DateStruct.RTC_Date/10 + '0',crcolortmp,bkcolortmp);
LED_P8x16Char(0 +7*8,16,RTC_DateStruct.RTC_Date%10 + '0',crcolortmp,bkcolortmp);
secold = RTC_TimeStruct.RTC_Seconds;
OSTimeDly(50, OS_OPT_TIME_DLY, &err);
我把扫描的任务放在低优先级的任务里,循环执行,这样就可以在高优先级的任务里执行一些周期性特别长的任务,比如更新显存,读取时间、温湿度等。
不过不要问我为什么不用FPGA,因为我觉得可以用单片机就解决的问题还是用单片机比较简单方便,包括硬件系统和软件系统。
以上的程序还不完善,比如时序的优化,去掉一些无用的delay。
还有就是原始数据是24bit的,每个基色为8bit,但是MBI5052是16bit的PWM,可以通过更加专业的颜色校正算法把8bit原始数据对应到这16bit的PWM值上,提升显示效果。
接下来要做的就是用PTC CREO设计一个壳子,3D打印出来,把主板装进去,挂起来了,全部弄好后再上图。
|
共1人点赞
|