视展LED屏幕RS485对接(C语言)

简介: 视展LED屏幕RS485对接(C语言)

一个项目上用到视展LED全彩屏,第一次对接这种LED屏幕,发现这个屏幕的功能还是很强大的!技术支持也挺给力的,两天时间对接完成;这里把主要的东西记录一下,做个笔记。


RS485通信协议


刚开始拿到通信协议,有点懵了,虽然我对十六进制的协议并不陌生,但是协议里涉及到的字段、专用名词比较多,不容易理解,段时间去开发一套通信协议代码也不现实,还好提供了c语言的通信代码,虽然是Windows平台下的代码,稍微改了下就可以用在Linux下了。


开发包


视展提供的sdk代码与软件:


App: 是LED屏幕配套的软件;


EasyProtocolDemo是一个比较精简的内码发送显示的实例,我也是基于这个代码做的;

├── LedUtils.c --- led通信协议打包函数实现
├── LedUtils.h
├── crc.c    ----- 校验计算函数实现
├── main.c   ----- 测试main函数
├── packet.c ----- 网络接口实现,主要是用于网络通信,串口的话就不需要这个文件了
├── packet.h ----- 部分协议结构体定义
├── system.h ----- 协议中的宏定义、枚举、结构体定义
├── uart.c   ----- 串口读写接口实现
└── uart.h


如上,首先我们要弄清楚屏幕通信控制的执行流程,可以先看下main.c里面的函数:

#include <windows.h>
#include <stdio.h>
#include <conio.h>

#include "system.h"
#include "packet.h"
#include "uart.h"
#include "LedUtils.h"

char CardHost[16] = "192.168.1.99";
int com_port=1;

void doprint(BYTE* buffer, DWORD size)
{
/
  DWORD i;
  printf("------------------------------------------\n");
  for (i=1; i<=size; i++)
  {
    printf("0x%0.2X,", buffer[i-1]);
    
    if ((i % 8)==0) printf(" ");
    if ((i % 16)==0) printf("\n");
  }
  printf("\n----------------------------------------\n");
/
}

void net_send(BYTE* stream)
{
  DWORD size;
  BYTE buffer[6144];
  SOCKET s;
  DWORD i, K;
  char ip[16];
  WORD port;

  s=InitSocket(9011);

  //----generate and send the begin packet----
  size=DoBeginPacket(buffer, 0);
  SocketWrite(s, buffer, size, CardHost, 6666);
  Sleep(100);
  size=SocketRead(s, buffer, 6144, ip, &port);
  if (size>0) printf("==>begin packet ok\n\n"); else printf("==>begin packet time out\n\n");

  //----generate and send the data packet----

  // first, get the count of data packet
  K=GetDataPacketCount(stream);

  // second, generate and send the data packet one by one
  for (i=1; i<=K; i++)
  {
    size=DoDataPacket(stream, i, buffer, 0);
    SocketWrite(s, buffer, size, CardHost, 6666);
    Sleep(100);
    size=SocketRead(s, buffer, 6144, ip, &port);
    if (size>0) printf("==>data packet %d ok\n\n", i); else printf("==>data packet %d time out\n\n", i);
  }

  //----generate and send the end packet----
  size=DoEndPacket(buffer, K+1, 0);
  SocketWrite(s, buffer, size, CardHost, 6666);
  Sleep(100);
  size=SocketRead(s, buffer, 6144, ip, &port);
  if (size>0) printf("==>end packet ok\n\n"); else printf("==>end packet time out\n\n");

  CloseSocket(s);
}

void com_send(BYTE* stream)
{
  DWORD size;
  BYTE buffer[6144];
  DWORD i, K;
  TDeviceInfo dev;

  uart_Initialize(com_port);

  //----generate and send the begin packet----
  size=DoBeginPacket(buffer, 0);
  uart_Write(buffer, size, NULL);
  Sleep(100);
  size=uart_Read(buffer, 6144, &dev);
  if (size>0) printf("==>begin packet ok\n\n"); else printf("==>begin packet time out\n\n");

  //----generate and send the data packet----

  // first, get the count of data packet
  K=GetDataPacketCount(stream);

  // second, generate and send the data packet one by one
  for (i=1; i<=K; i++)
  {
    size=DoDataPacket(stream, i, buffer, 0);
    uart_Write(buffer, size, NULL);
    Sleep(100);
    size=uart_Read(buffer, 6144, &dev);
    if (size>0) printf("==>data packet %d ok\n\n", i); else printf("==>data packet %d time out\n\n", i);
  }

  //----generate and send the end packet----
  size=DoEndPacket(buffer, K+1, 0);
  uart_Write(buffer, size, NULL);
  Sleep(100);
  size=uart_Read(buffer, 6144, &dev);
  if (size>0) printf("==>end packet ok\n\n"); else printf("==>end packet time out\n\n");

  uart_Destroy();
}

void demo_string()
{
  BYTE stream[65536];
  char str1[]="HELLO WORLD!";
  char str2[]="你好";

  //genrate the display program data
  //this is a demo that there are two text display in the screen
  //one display in the rect (0, 0, 256, 16), another display in the rect (0, 16, 256, 32)
  //  /---------------\
  //  | HELLOW WORLD! |
  //  | hellow world! |
  //  \---------------/
  MakeRoot(stream);
  AddChapter(0x7fffffff, PLAY_MODE_WAIT);
  AddRegion(0, 0, 256, 32);
  AddLeaf(0x7fffffff, PLAY_MODE_WAIT);
  AddString(0, 0, 256, 16, str1, FONT_SET_16, RGB(255,255,0), 1, 0, 2, 0, 0, 0, 5000);  //display in yellow
  AddString(0, 16, 256, 16, str2, FONT_SET_16, RGB(0,255,0), 1, 0, 2, 0, 0, 0, 5000);  //display in green

  //send to screen
  //net_send(stream); //this is the network (udp)
  com_send(stream);  //this is the rs232
}


void main(void)
{
  while (1)
  {
    demo_string();
    Sleep(5000);
  }
}


代码比较简单,我们关注的函数就是:demo_string(),这个函数在这里就是实现了向屏幕发送要显示的内容:

image.png

AddString就是添加要显示的文字内码,LED屏幕支持内码发送和文本发送,内码发送就是发送的国标内码,文本文字就是将文字转换为点阵数据发送,这种方式数据量比较大,建议直接用内码发送。


如果想要发送的文字过长时滚动,需要将AddString的第8个参数(inmethod)传入2。


如果需要屏幕自动保存内码,则需要使用下载内码的方式(ROOT_DOWNLOAD):

void MakeRoot(BYTE* buffer)
{
  DWORD size;
  root=(PRoot)buffer;

  size=sizeof(TRoot);
  memset(root, 0, size);
  root->id=ROOT_PLAY;  //修改这里ROOT_DOWNLOAD可以让下载的内码保存
  root->color=LED_THREE;
  root->count=0;
  root->survive=0xffffffff;
  root->Reserved=0;
  root->size=size;
  BufferSeek=size;
}


屏幕显示位置与区域划分


AddStringDateTime: 是添加显示时间,这个时间是屏幕自己计时,需要屏幕控制卡的程序支持;

AddStringWeek : 是添加显示“星期”;

AddString: 就是添加显示内码文字;


这几个函数每调用一次就相当于产生一个显示对象(区域),LED屏幕上根据传入的坐标显示在不同的位置。


屏幕可以指定更新一个局部的显示对象,比如使用AddString添加的一个内容,需要定时更新,那么只需要调用定时更新对象的函数即可(我把sdk里的函数改造了一下):

void demo_update_object(int objectindex, const char* str, long left, long top, long width, long height)
{
  BYTE stream[65536];

  char outStr[512]={0};
  u2g((char *)str,strlen(str),outStr,sizeof(outStr));

  MakeObject(stream, 0, 0, 0, (long)objectindex);
  AddStrings(left, top, width, height, 1);
  AddChildString(outStr, FONT_SET_16, RGB(240,0,0), 2, 0, 0, 0, 0, 0, 5000);

  com_send(stream);
}

串口收发函数替换


Windows下与Linux下主要串口部分需要改下,别的代码不用修改,我把uart.c里的串口收发函数的实现改为了我这边Linux里的封装:

int uart_send(const void *pdata, int len)
{
    int ret = -1;
#ifdef _TEST_
    ret = 0;
    dPrint(WARN, ">%s\n", hexdump(pdata, len).c_str());
#else
    ret = gs_pSerial->sendData(pdata, len);
    if ( ret <= 0 )
    {
        dPrint(ERROR, "led screen send error.\n");
    }
#endif
    return ret;
}

int uart_read(void *pdata, int len)
{
    int ret = -1;
#ifdef _TEST_
    ret = 0;
#else
    ret = readable_timeo(gs_pSerial->getObjectFd(), 10);
    if ( ret <= 0 )
    {
        dPrint(ERROR, "led screen read timeout : %d.\n",ret);
        return ret;
    }

    ret = gs_pSerial->readData(pdata, len);
    if ( ret <= 0 )
    {
        dPrint(WARN, "led screen send error.\n");
    }
#endif
    return ret;
}


我函数名字改了,如果函数名字不改的话,com_send函数就不用动了。


汉字编码


LED是采用的GB2312编码,使用内码进行通信时,需要LED事先下载好内码,下载内码是使用配套的软件进行下载的。


字库下载


如下图,点击下载字库,就会弹出让选择字库文件的对话框,然后下载即可。


编码转换


我们设备里面采用的UTF8编码,所以汉字的显示需要做一下内码转换,这里没有自己去写函数转换,直接移植了iconv库,用iconv库函数去做的转换,下面是从网上查找到的封装代码:

/*代码转换:从一种编码转为另一种编码*/
static int code_convert(char *from_charset,char *to_charset,char *inbuf,int inlen,char *outbuf,int outlen)
{
    iconv_t cd;
    int rc;
    char **pin = &inbuf;
    char **pout = &outbuf;

    cd = iconv_open(to_charset,from_charset);
    if (cd==0) return -1;
    memset(outbuf,0,outlen);
    if (iconv(cd,pin,(size_t*)&inlen,pout,(size_t*)&outlen)==-1) return -1;
    iconv_close(cd);
    return 0;
}

/*UTF8码转为GB2312码*/
static int u2g(char *inbuf,int inlen,char *outbuf,int outlen)
{
    return code_convert("utf-8","gb2312",inbuf,inlen,outbuf,outlen);
}
/*GB2312码转为UTF8码*/
static int g2u(char *inbuf,size_t inlen,char *outbuf,size_t outlen)
{
    return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen);
}


这样在发送内码的地方统一做一次转换即可。


遇到的问题


问题1-无法分行


我用的这个屏幕是32032的,汉字是1616点阵,只能在第一行显示,即使把y坐标设置为16,还是在0处显示,这个问题是控制卡软件的问题,找技术支持升级后解决了。


问题2-颜色问题


即RGB的染色值顺序变了,这个在软件上可以配置R、G、B的顺序,可以配置为RGB、RBG、GBR等


问题3-没有显示星期的函数


这个首先可以通过发内码来解决,如果自己不想写这部分代码,可以找找支持,我这边找技术支持给加了一个函数~


整体上调试还是比较顺利的。


LED显示照片


目录
相关文章
|
2月前
|
Oracle 关系型数据库 MySQL
MySQL 9.0安装教程 Windows版:详细步骤+安装路径修改+root密码设置+快捷方式创建指南
MySQL是Oracle旗下开源关系型数据库,以高性能、高可靠、标准化和多语言兼容著称,广泛用于网站后台、企业系统及数据分析。本文详解MySQL 9.0的下载、自定义安装(含路径修改)、root密码配置及快捷方式创建与验证步骤,操作清晰易上手。(238字)
|
机器学习/深度学习 人工智能 算法
深度强化学习中实验环境-开源平台框架汇总
深度强化学习中实验环境-开源平台框架汇总
972 0
|
传感器
基于Arduino的自动浇灌系统
基于Arduino的自动浇灌系统
1253 1
|
缓存 编译器 Go
构建理想容器镜像——以CSI为例
本文围绕阿里云CSI(Container Storage Interface)镜像构建的实际案例,探讨了一系列优化容器镜像的最佳实践。
837 106
|
存储 缓存 负载均衡
高并发系统架构的设计挑战与应对策略
【8月更文挑战第18天】高并发系统架构设计是一项复杂而重要的任务。面对性能瓶颈、稳定性与可靠性、并发控制和可扩展性等挑战,开发人员需要采取一系列有效的策略和技术手段来应对。通过负载均衡、缓存技术、数据库优化、异步处理、并发控制、弹性设计及监控与调优等手段,可以设计出高性能、高可用和高可扩展性的高并发系统架构,为用户提供优质的服务体验。
|
人工智能 数据可视化 JavaScript
打造动态数据可视化:JavaScript与AI的完美结合
本文介绍如何通过JavaScript和AI技术实现动态数据可视化,以实时股票数据为例。使用JavaScript动态更新网页内容,Chart.js绘制股票价格走势图,并通过DeepSeek API进行趋势预测。用户输入股票代码后,网页展示历史价格并预测未来走势,增强用户体验。结合AI技术,不仅提升网页功能性,还为用户提供智能化的数据洞察。
|
安全 测试技术 数据安全/隐私保护
猫头虎分享:鸿蒙生态带给开发者的全新机遇!轻松实现按需加载与多端适配,开发效率翻倍
猫头虎分享:鸿蒙生态带来的全新机遇!华为在原生鸿蒙之夜发布会上,推出了全新的鸿蒙系统和焕新升级的应用市场。此次升级在用户体验和隐私保护方面实现了重大突破,提供了自动化检测前移、按需加载和多端适配等服务,帮助开发者提高开发效率和应用质量。
618 6
|
数据可视化 大数据 API
低代码可视化开发-uniapp新闻跑马灯组件-代码生成器
低代码可视化开发-uniapp新闻跑马灯组件-代码生成器
710 2
|
PHP 开发者
slowlog 和 request_slowlog_timeout
slowlog 和 request_slowlog_timeout
586 4

热门文章

最新文章