在 WindowMobile 上的模拟LED 显示屏插件(转)

源:在 WindowMobile 上的模拟LED 显示屏插件

我在给一个对话框上的控件查找翻看合适的图标时,无形中看到了一个LED显示屏的图标,这里所说的LED显示屏是指由很多LED灯密集排列组成的点阵式LED屏,比如在股市交易所,公交车上,银行门口,我们经常能看到这样的滚动式显示屏。我不禁忽然想到,如果把它放在手机上显示,那效果是不是很别致呢?而且我在很久以前用 C# 模拟了这种LED显示屏的效果,因此技术上没有什么问题。不过现在,我想把它在手机上实现,而且我的想法是做成今日插件,因为相比普通的应用程序,插件更方便用户启用,禁用,可以在桌面上展示。然后,现在我要考虑使用 C++ 实现,在技术上的实现会和上一次在C#实现上略有不同,但本质原理是一致的,并且这一次实现的效果将更符合现实中我们看到的 LED 屏。这里我将会讲述一点 LED 屏在滚动时的原理和细节技巧。

首先来看,这是插件在手机上滚动显示的效果:

关于windows mobile 上的今日插件开发,这里就不认真讲解,可以参考我前几天发的文章。这里再额外提的一些内容是关于 LED 屏的滚动原理和细节,这些我们要针对的平台,开发的应用程序类型是无关的。

(1)在本范例中 Led 点阵屏幕的实现原理;

      a)首先在根据要显示的内容,测量出字符串在指定DC上绘制的宽度和高度。( GetTextExtentPoint32 )
      b)在内存中创建相应大小的一个设备无关的临时位图(DIB),并得到该它的位图数据地址。(CreateDIBSection)
      c)在该临时位图上绘制字符串,这时临时位图成为一个白底黑字的实际大小的位图。它也是我们生成 LED 图像的基础。
      d)根据每个LED灯占据的尺寸(LEDSIZE),生成一个相应大小的 LED 位图。同时得到它的位图数据地址。这个位图相比临时位图在高度和宽度上都扩大了 LEDSIZE 倍数,这张图是用于最终显示到屏幕上的位图,它将长期维持在内存中,直到用户改变显示内容。
      这里,LEDSIZE 是实际LED灯的尺寸(3*3像素)加上 LED灯的间距(1像素)。因此,本例中 LEDSIZE = 4 像素;
      e)根据我们得到的位图数据地址,遍历临时位图的所有像素,并根据临时位图的像素,设置LED位图的LED灯;这里实际上是一种映射,也就是把临时位图中的黑或者白像素映射到 LED位图上的 (LEDSIZE * LEDSIZE)的矩形上。
      这里,注意的细节是,像素定位必须注意下面几个因素:
            (i) 位图的扫描行是逆序的。也就是说,假设该图像有10行像素,那么在起始地址出现的行序是:10,9,8,7,...
            (ii) 单个像素在内存中的地址从低到高依次是:Blue,Green,Red,Alpha;(假如存在Alpha通道)
            (iii) 单个像素占据的字节数是 bpp/8;通常普通RGB位图也就是占据3个字节/像素。
            (iv) 在行间导航的关键信息是扫描行宽度(stride);因为存在位图数据的扫描行必须以 DWORD 对齐。

f)我们设置好LED位图以后,临时位图就不需要了,这时可以清除临时位图。在绘制时,把LED位图的相应区域复制到 指定DC 的相关位置即可。可以用全局变量记录LED屏的位置,用定时器更新此位置,即实现了滚动效果。

 (2)滚屏时的细节;

      考虑实际的LED屏,在滚动起来时,是同样的一段字符串首尾相接的方式,在屏上连续从右向左滚动。因此为了实现的方便,我们实际上建立的逻辑模型是如下图所示:两个相同LED字符串,以一定的像素间距(MEMODISTANCE)首尾连接,我们分别用两个变量记录两个LED字符串的位置:
      g_LedPos1: 第一个字符串的起始位置(LED屏幕客户区中的坐标);
      g_LedPos2: 第二个字符串的起始位置(LED屏幕客户区中的坐标);
      g_LedWidth: 字符串的像素宽度;(LED屏幕坐标)
      MEMODISTANCE:两个字符串之间的像素距离;(LED屏幕坐标);
      MEMOCOUNT:逻辑模型中首尾相接的字符串数量;(在本例中 = 2);
      SCREENWIDTH:LED屏的像素宽度(LED屏幕坐标),在本例中= 60;
      LED 屏幕坐标:在LED屏幕中,每个像素都是在实际位图中占据的是(4*4)尺寸;例如显示屏在该坐标体系下,是60像素宽度,但是实际位图是240像素宽度。

(a)连续滚动原理;

======================================================================================
      ●. 连续滚动需要满足下列条件: MEMODISTANCE  >=  SCREENWIDTH / MEMOCOUNT;
      =======================================================================

      上面的不等式用语言来描述就是:“相邻字符串 的 “首尾间距” 大于等于 LED屏幕像素宽度 /  屏幕上可同时显示的字符串最大个数”;假如我们把“字符串”看做汽车,LED屏看作“单行道”,那么这里提到的也就是对“车距”的要求。

屏幕上可同时显示的字符串个数(MEMOCOUNT)实际上就是我们的代码中在维护的逻辑模型的字符串个数,也就是说我们的代码中一共有多少个 g_LedPos 变量,每一个 g_LedPos 变量可以维护一个字符串;在本例中,我们维护了两个 g_LedPos; 这两个字符串首尾相接后成为无限长序列的一个基本单位(basic unit of the memo queue);无限长队列是从这个基本单位扩展而成。因此,MEMOCOUNT 是 这个基本单位的容量(容纳字符串的数量);

为什么我们会对“车距”有需求呢,想象下如果汽车的宽度(字符串长度)非常小,如果不控制车距,就会导致单行道上可同时容纳的汽车数量增加,而如果这个数量超过了我们代码中的模型(组成无限长字符串队列的基本单位的容量)的字符串个数,那么我们的代码就无法同时控制这么多汽车的出现位置;这将会导致帧突变,体现在视觉上就是,滚动过程中,屏幕中间本来是黑色背景,但是在下一帧忽然在中间冒出“文本内容”;

在本例中,我们的逻辑模型是两个字符串相接,因此因此我将 MEMODISTANCE 定义为 30 像素;假如不满足这个条件并且输入字符串极短(例如“1”),那么在连续滚动的过程中就可能造成帧突变,在某些时刻的帧和上一帧无法衔接,即在屏幕中的黑暗部分凭空闪现字符串内容。

(b)定时器中的处理:
      由于是从右向左滚动,所以我们在定时器中只需要把 g_LedPos1, g_LedPos2 递减即可,当发现第一个字符串离开屏幕时,我们立即两个坐标的指向向后移动到它后面的那个字符串,即把 g_LedPos2 赋值给 g_LedPos1; g_LedPos2 根据 g_LedPos1 重新赋值;这样就以两个字符串为一个基本单位,向后逐一进行无穷扩展,形成无数字符串首尾相连的虚拟场景;
      定时器中的代码如下所示:

case WM_TIMER:
    g_LedPos1--;
    g_LedPos2--;
    //第一条已经完全离开屏幕左侧了吗?如果是,则向后更替!
    if(g_LedPos1 + g_LedWidth < 0)
    {
        g_LedPos1 = g_LedPos2;
        g_LedPos2 = g_LedPos1 + g_LedWidth + MEMODISTANCE;
    }
    InvalidateRect(hwnd, &rcLed, FALSE);
    break;

(c)在绘制时的处理:
      在绘制时,主要使用 BitBlt 函数来完成。但是需要注意的是,字符串的起始坐标(g_LedPos)是否在 LED 屏内部,将决定了 BitBlt 中的参数有所区别(这里我不详细分析原因)。
      我使用一个辅助函数:GetBltInfo 来帮助我判断 BitBlt 函数中应该使用的参数;它可以计算出 BitBlt 中应该把 LedBitmap 中的何处 拷贝到 DC 的何处,拷贝多少宽度;相关代码如下:

//根据当前位置,决定贴图的相对位置
void GetBltInfo(int pos, int ledWidth, RECT* lpRcLed, int* lpDestX, int* lpSrcX, int* lpWidth)
{
    //头部在显示区
    if(pos>=0)
    {
        *lpDestX = pos * LEDSIZE;
        *lpSrcX = 0;
        *lpWidth = min(lpRcLed->right, (pos + ledWidth)*LEDSIZE) - *lpDestX;
    }
    else
    {
        *lpDestX = 0;
        *lpSrcX = -pos*LEDSIZE;
        *lpWidth = min(lpRcLed->right, (pos + ledWidth)*LEDSIZE);
    }
}

//========================================
//这里是窗口函数中对 WM_PAINT 的处理:
//========================================
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            RECT rc;
            //===============================================
            //        NOW WE START TO GET DC !
            //===============================================
            hDC = BeginPaint(hwnd, &ps);
            HDC    hMemDC = CreateCompatibleDC(hDC);
            HBITMAP hOldBm = (HBITMAP)SelectObject(hMemDC, g_BitmapLed);

            //贴第一个字符串
            int destX, srcX, width;
            GetBltInfo(g_LedPos1, g_LedWidth, &rcLed, &destX, &srcX, &width);

            BitBlt(hDC,destX,rcLed.top, width, (rcLed.bottom - rcLed.top),
                hMemDC,srcX, 0,
                SRCCOPY);

            //贴第二个字符串
            if(g_LedPos2 * LEDSIZE <= rcLed.right)
            {
                GetBltInfo(g_LedPos2, g_LedWidth, &rcLed, &destX, &srcX, &width);
                BitBlt(hDC,destX,rcLed.top, width, (rcLed.bottom - rcLed.top),
                    hMemDC,srcX, 0,
                    SRCCOPY);
            }

            SelectObject(hMemDC, hOldBm);
            DeleteDC(hMemDC);
            EndPaint(hwnd, &ps);
        }
        break;

(3)最重要的函数:生成LED位图(CreateLedBitmap);

      这个函数的作用是:根据指定的字符串,生成 LedBitmap;并返回 LedWidth (该图片的LED屏幕坐标的像素宽度);在这个函数里,临时位图实际上在使用完毕后清除,但我为了在PC上的范例的对话框上展示它,所以没有立即清除。
      如下图所示,为 4 个 LED 的放大图形,上面两个为熄灭,下面两个为点亮状态;

【注意】我们绘制的原始图像特点是“白底黑字”,因此黑色映射到LED图像的“点亮”状态,白色映射到“熄灭”状态;在下面代码中可看到这一点;

下面是 CreateLedBitmap 的代码,在这段代码中,包含了前面所讲的对“像素定位”的非常重要的方法和信息,涉及了扫描行宽度,位图中的逆序扫描,在图像数据中的像素定位,在像素中的RGB通道定位,这些都是通过指针来完成的,需要务必小心以确保指针的指向合法和正确。

//根据指定的字符串创建实际的用于滚动的位图(比实际图像会增大4倍)
//返回创建的位图的未LED化像素长度(也就是实际字符串的绘制长度)
int CreateLedBitmap(HWND hWnd, TCHAR* text)
{
    //创建字体
    LOGFONT lf;
    HFONT hFont = NULL, hOldFont = NULL;
    SIZE size = {0,0};
    BITMAPINFO bminfo;
    HBITMAP hOldBitmap; //临时位图(为非LED化的实际像素大小)
    HBRUSH hBrush =NULL;
    RECT rc;
    int i, j, stride, strideTemp;

    //两个位图的像素数据地址
    BYTE *lpBytes, *lpBytesTemp, *lpLed;

    //设置位图信息
    memset(&bminfo.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
    bminfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); //40
    bminfo.bmiHeader.biPlanes = 1;
    bminfo.bmiHeader.biBitCount = 24;

    //测量字符串的大小,其决定作用的还有当前HDC中的字体
    HDC hWndDC = GetDC(hWnd);
    HDC hDC = CreateCompatibleDC(hWndDC);
    ReleaseDC(hWnd, hWndDC);

    HFONT hSysFont = (HFONT)GetStockObject(DEVICE_DEFAULT_FONT);
    GetObject(hSysFont, sizeof(LOGFONT), &lf);
    lf.lfWeight = FW_NORMAL;
    lf.lfHeight = (long) -((11.0 * (double)GetDeviceCaps(hDC, LOGPIXELSY) / 72.0) + .5);
    //lf.lfHeight = 11;
    wcscpy(lf.lfFaceName, _T("宋体"));

    // create the font
    hFont = CreateFontIndirect(&lf);
    //创建背景刷
    hBrush = CreateSolidBrush(BKCOLOR_LED);

    hOldFont = (HFONT)SelectObject(hDC, hFont);

    //测量字符串,得到的高度应该是14,宽度取决于字符串
    GetTextExtentPoint32(hDC, text, wcslen(text), &size);

    //计算扫描行宽度
    stride = (24 * size.cx * LEDSIZE + 31)/32 * 4;
    strideTemp = (24* size.cx + 31)/32 * 4;

    bminfo.bmiHeader.biWidth = size.cx * LEDSIZE;
    //去掉上下的2行像素
    bminfo.bmiHeader.biHeight = size.cy * LEDSIZE; //(14-2)*4=48pixels,
    bminfo.bmiHeader.biSizeImage = bminfo.bmiHeader.biHeight * stride;

    //创建位图
    if(g_BitmapLed != NULL)
        DeleteObject(g_BitmapLed);
    if(g_BitmapTemp != NULL)
        DeleteObject(g_BitmapTemp);

    //注意WinCe平台上不支持内存映射,所以无法提供我们自己的位图数据,而是由系统负责分配
    g_BitmapLed = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytes, NULL, 0);
    //是否创建成功?
    if(g_BitmapLed == NULL)
    {
        goto CLEARNUP_EXIT;
    }

    //创建实际大小的位图
    bminfo.bmiHeader.biWidth = size.cx;
    bminfo.bmiHeader.biHeight = size.cy;
    g_BitmapTemp = CreateDIBSection(hDC, &bminfo, DIB_RGB_COLORS, (VOID**)&lpBytesTemp, NULL, 0);
    if(g_BitmapTemp == NULL)
    {
        goto CLEARNUP_EXIT;
    }

    //在实际绘图上进行绘制
    hOldBitmap = (HBITMAP)SelectObject(hDC, g_BitmapTemp);
    rc.left = rc.top = 0;
    rc.right = size.cx;
    rc.bottom = size.cy;
    //填充白色背景
    FillRect(hDC, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
    SetTextColor(hDC, RGB(0,0,0)); //黑色字体
    SetBkMode(hDC, TRANSPARENT);
    //绘制字符串
    DrawText(hDC, text, wcslen(text), &rc, DT_LEFT | DT_VCENTER| DT_NOCLIP | DT_SINGLELINE);

    //现在再把LED图选入DC中去刷一次背景
    SelectObject(hDC, g_BitmapLed);
    //rc设置为LED图的大小
    rc.right = size.cx * LEDSIZE;
    rc.bottom = size.cy * LEDSIZE;
    FillRect(hDC, &rc,hBrush);

    //复原DC
    SelectObject(hDC, hOldBitmap);
    SelectObject(hDC, hOldFont);

    //现在根据它进行拷贝到新的位图中,去掉了上下两行像素,i,j是在 bitmaptemp中的坐标
    //每个LED灯实际上是 4 * 4像素的小矩形块!设置6个像素即可
    for(j = 0; j< size.cy; j++)
    {
        for(i = 0; i < size.cx; i++)
        {
            //把指针定位到LED灯的左上角!
            //注意这里行是倒序的!(所以距离内存起始点的最远的哪一行才是LED第一行,逐行向内存起始处返回)
            lpLed = lpBytes + stride*(j*LEDSIZE+3) + i*3*LEDSIZE;

            if(lpBytesTemp[strideTemp * j + i*3] == 0) //即,该通道为黑色,表示这里是字迹(应该点亮这个灯)
            {
                //点亮 9 * 9
                //第一行 0-Blue, 1-Green, 2-Red
                lpLed[0]= 76, lpLed[1]=154, lpLed[2]=186;
                lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
                lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;

                //第二行
                lpLed -= stride;
                lpLed[0]=127, lpLed[1]=214, lpLed[2]=245;
                lpLed[3]=193, lpLed[4]=232, lpLed[5]=247;
                lpLed[6]= 43, lpLed[7]=167, lpLed[8]=241;

                //第三行
                lpLed -= stride;
                lpLed[0]= 56, lpLed[1]=144, lpLed[2]=204;
                lpLed[3]= 43, lpLed[4]=160, lpLed[5]=231;
                lpLed[6]= 43, lpLed[7]=125, lpLed[8]=207;

            }
            else
            {
                //熄灭 9 * 9
                //第一行 0-Blue, 1-Green, 2-Red
                lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
                lpLed[3]= 84, lpLed[4]= 84, lpLed[5]= 84;
                lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;

                //第二行
                lpLed -= stride;
                lpLed[0]= 84, lpLed[1]= 84, lpLed[2]= 84;
                lpLed[3]= 66, lpLed[4]= 66, lpLed[5]= 66;
                //lpLed[6]= 65, lpLed[7]= 65, lpLed[8]= 65;

                //第三行
                lpLed -= stride;
                lpLed[0]= 65, lpLed[1]= 65, lpLed[2]= 65;
                //lpLed[3]=130, lpLed[4]=207, lpLed[5]=236;
                //lpLed[6]= 56, lpLed[7]=145, lpLed[8]=204;
            }
        }
    }

CLEARNUP_EXIT:
    //清理
    if(hBrush !=NULL)
        DeleteObject(hBrush);
    if(hFont != NULL)
        DeleteObject(hFont);
    if(hDC != NULL)
        DeleteDC(hDC);
    return size.cx;
}

(4)PC上的演示程序;

      最后,本范例的代码是运行在 PDA (WINDOWS MOBILE)上的今日插件,但这里提到的LED滚屏技术是不局限平台的,因此我也做了一个在PC可以运行的对话框程序,来演示上面的代码和技术:对话框的截图如下:

     (5)下载连接;
      
      代码下载连接(包含PC端演示程序,和 WindowMobile 插件源代码):
      http://files.cnblogs.com/hoodlum1980/JRL_LedScreen.rar
      
      同时,我开发的插件也发布到 pdafans 论坛供网友测试和使用,因此也可以在 pdafans 论坛的以下帖子下载到本文提到的今日插件的最新 CAB 包,在我的博客上仅保留插件的较早版本。
      
      一个模拟LED显示屏的桌面今日插件

by hoodlum1980 @ 2009

时间: 2024-09-30 15:13:01

在 WindowMobile 上的模拟LED 显示屏插件(转)的相关文章

当把12306验证码换成LED显示屏

年关将至,大部分伙伴们已经抢购到了一张回家的火车票.But,在这个寒冷的冬天有一种神物已经达到了人神共愤的地步,那就是咱们不得 不说的12306坑爹验证码!当验证码还是简单数字的时候小编还是很乐于帮助小伙伴们抢票的,但是,直到它们的出现我才明白不是所有的忙都是你想帮就能帮的! 尤其在见过它的升级版之后,我只能大呼:Kill me! 在全民吐槽12306验证码火爆刷屏之后恶搞版也层出不穷.各种贴合自己行业版本的恶搞版本也顺势推出.“既然玩,请带上我”,LED显示屏举手高喊.当验证码换成LED显示屏

浅析LED显示屏的节能原理

LED显示屏的节能概念悄然掀起,成为它最为吸引消费者眼球的亮点,也是近年来火爆增长的原因.当然,经过近几年的疯狂式增长,目前这个行业也陷入了一种僵局以重新洗牌的困局.在这一困境之中,必然有许多企业受此影响而元气大伤,甚至倒闭破产,当然也会有许多企业因此而走出阴霾,获得更为长远的发展.这是行业的发展规律,任何企业都逃脱不了这一劫难,如何在劫难中重生,是每个企业当前比较关注的问题. 在这个节能呼声极高的时代,LED显示屏的进一步节能又无可厚非的成为了这个行业追逐的支撑点.很多企业在这一点上进行各方面

最新 Arduino 驱动 12接口/户外 LED显示屏/LED点阵屏/LED单元板

起因 现有的驱动LED显示屏的资料,比较好的只有这个.但是它驱动的是08接口的室内显示屏,而我要驱动的是12接口的户外显示屏.两种屏幕的区别在于户外屏幕点阵比较稀疏,而且二者的扫描方式,驱动方式都不太一样.我花了一个下午才摸索出了它的使用方式,因此分享给大家,希望大家不必再绕弯路. 材料准备 12接口(户外)LED单元板一块:比如我是从[这里]买的,大家搜索“半户外 LED 单元板”就可以了.我的板是16x32大小. Arduino Uno一块 5V电源一个(比如充电宝.给手机充电的头) 硬件搭

不得不知的LED显示屏选购的常用术语

LED显示屏: LED显示屏(LED panEL):LED就是light emitting diode ,发光二极管的英文缩写,简称LED.它是一种通过控制半导体发光二极管的显示方式,用来显示文字.图形.图像.动画.行情.视频.录像信号等各种信息的显示屏幕. LED显示屏分为图文显示屏和视频显示屏,均由LED矩阵块组成.图文显示屏可与计算机同步显示汉字.英文文本和图形;视频显示屏采用微型计算机进行控制,图文.图像并茂,以实时.同步.清晰的信息传播方式播放各种信息,还可显示二维.三维动画.录像.电

LED显示屏厂家的目光将聚焦在哪里?(一)

当下,LED电子屏在一些传统领域,如户外广告.舞台租赁等方面的应用已经越来越成熟的了,LED显示屏厂家的差异化也越来越少,导致市场竞争日益激烈.为了开辟新的战场,拓展LED电子屏的应用领域,各种创新层出不穷,以此来挑战并迎接新的商机.与此同时,配套厂商以及上游产业链的技术更新,如更小的间距.更小的灯珠.更具备操作性的控制系统,也是LED显示屏厂家积极开辟新应用领域的导火线.下面我们来盘点一下未来几年LED显示屏厂家的目光将聚焦在哪些领域. 酒店宴会 在以往,酒店行业很少会配备LED显示屏,因为成

LED显示屏厂家的目光将聚焦在哪里?(二)

上文说到,配套厂商以及上游产业链的技术更新,如更小的间距.更小的灯珠.更具备操作性的控制系统,将会导致LED显示屏厂家积极开辟新应用领域.那么,除了酒店行业.酒吧夜店等,还有哪些呢? 居民的社区媒体 社区媒体(Community Media)是指根植于各中高档社区内.形成网络化布局的新型媒体.此处所说的社区,是"大社区"的概念,按社区属性分为生活社区.商务社区和校园社区三大类.相应地,社区媒体按分布地点不同可分为生活社区媒体.商务社区媒体和校园社区媒体三大类.无论分布在何地,根据社区媒

csuoj 1507: 超大型LED显示屏

http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1507 1507: 超大型LED显示屏 时间限制: 1 Sec  内存限制: 128 MB 提交: 124  解决: 61 [提交][状态][讨论版] 题目描述 输入 输入包含不超过100组数据.每组数据第一行为"START hh:mm:ss",表示比赛开始时刻为hh:mm:ss.最后一行为"END hh:mm:ss",即比赛结束时刻.二者之间至少会有一个SCORE信

LED显示屏灰度等级的提高及非线性纠正

http://www.doc88.com/p-09193698596.html之原文 作者:杨赓 上海中大高新电子技术有限公司 摘要:本文从增加LED元色调灰深度着手对LED显示屏的非线性灰度纠正提出了不同的解决方法,从而彻底改变LED显示屏显示效果不佳的现状,深入实施可望带动中国整个LED产业,使LED产品的图象品质步入新的时代 关键词: 显示:LED:非线性纠正: 一.引言 LED显示屏作为大型显示设备的一种,具有亮度高.价格低.寿命长.维护简便等优点.带有灰度效果的LED显示屏较之普通LE

LED显示屏要预防夏季雷雨天气

全国各地已全面步入夏季,而夏季的雷雨天气常常给户外LED显示屏钢架结构带来雷击隐患,看以下几个防雷措施. (1)针对直击雷的防护,在LED电子显示屏(户外)的支撑结构上安装避雷针,室内全彩LED显示屏或者旁边有高大建筑物的LED电子屏,可以不用考虑安装避雷针. (2)针对钢结构的反击,将钢结构与LED全彩屏的外壳相连,做好等电位,并且做好接地,接地阻值通常要求小于10欧姆,阻值如果达不到要求,就要做附加的人工接地网.防雷器也要做好接地. (3)针对电源线上感应的雷电流,在电源线上加装单相或者三相