查看: 2422|回复: 3

[i.MX6ULL竞赛专区] 【我的项目666】+通讯管理(6)通讯管理项目结项贴

[复制链接]
  • TA的每日心情
    开心
    2024-4-5 11:15
  • 签到天数: 1061 天

    [LV.10]以坛为家III

    29

    主题

    1517

    帖子

    31

    金牌会员

    Rank: 6Rank: 6

    积分
    4972
    最后登录
    2024-4-21
    发表于 2018-6-28 12:56:07 | 显示全部楼层 |阅读模式
    本帖最后由 story_xjj 于 2018-6-28 13:12 编辑

    基于实时数据库的多串口多协议通讯管理机

        本项目是基于米尔科技MYS-6ULX-IOT主板为基础,开发的具有实时数据库功能的多串口多协议通讯管理机项目。

        由于时间的原因,目前完成的项目内容包括2个串口和2种协议的支持。

        项目主要完成目标:

    l  基于以太网接口的实时数据库访问;

    l  基于串口(可扩展为RS485接口)的modbus-RTU设备通讯管理功能;

    l  基于串口(可扩展为RS485接口)的CDT设备通讯管理功能;

    注:由于个人能力、时间等原因,所实现的内容难免出现错误,敬请指正。

    注:本次软件所实现的功能用于参与米尔科技产品试用活动,不可用于商业用途。


    1.   实时数据库
    为了达成本次项目的需求,选择了较为常用的免费开源实时数据库redis。Redis是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API,包括Java,C/C++,C#,PHP,Python等,使用很方便。提供多种语言API这个非常重要,可以基于这个数据库开发丰富的上位软件功能。

    1.1.   实时数据库安装
    为了可以使用Redis数据库,我们必须下载源码,编译后,在IOT板上运行。

    1)源代码下载


    我这里选择的是redis-stable版本。

    2)编译

    编译过程还是比较顺利的。我是在ubuntu16.04 64位系统上完成的。

    第一步:解压源码

         tar xzf redis-stalbe.tar.gz

    第二步:编译准备

         为了可以正确的编译redis,首先必须先要编译一下目录下的deps文件夹下的几个组件,要不编译的时候可能提示缺少静态库的支持。

         编译的命令,因为要支持imx6ull,所以我用linaro编译器。

    进入deps目录中,执行

         make CC=arm-linux-gnueabihf-gcc hiredis linenoiselua

    编译结束后回到redis-stable的主目录下,执行真正的编译过程

         make CC=arm-linux-gnueabihf-gcc MALLOC=libc

    正常情况下可以编译我们需要的所有文件了。

    第三步:导出执行文件

         make install PREFIX=~/redis-install

    执行完后,在redis-install目录中就是我们准备在imx6ull上使用的所有可执行文件了。

    3)安装

    将ubuntu系统中我们准备好的redis-install目录下的所有文件复制到imx6ull系统中/usr/local/bin目录下,这个目录不是必须的,什么目录都可以,因为这个目录是redis官方推荐的,另外在PATH中默认有这个路径,所以比较省事。

    4)修改执行属性

    通常情况下,这些文件都不是可执行文件,必须增加可执行属性。

    进入/usr/local/bin目录中,执行

    chmod a+x *

            这样,整个安装工作就基本结束了。

    5) 测试

    为了验证所编译的redis是否可以在目标机器上使用,我们进行简单的测试。测试方法:

            在终端中执行命令

    redis-server &

           在执行

                redis-cli

    使用“PING”的命令测试服务器是否已经进入就绪状态,如果正确的回复“PONG”的话,恭喜,安装完成,服务器正确的启动了。

    1.2.   实时数据库基本配置
    l  绑定的IP地址,指定可以访问数据库的网络。

    bind 127.0.0.1 192.168.7.2

    l  端口指定

    port 6379

    l  默认redis不是以后台进程的方式启动,如果需要在后台运行,需要将这个值设置成yes,以后台方式启动的时候,redis会写入默认的进程文件/var/run/redis.pid

    daemonize yes

    l  数据库固化时间

    save "" 表示不进行固化,即不写入硬盘

        还有其它的可配置参数,如果需要大家可以进一步研究。

    1.3.   实时数据库基本使用方法
    这里不详细说明数据库的具体使用方法,请大家上网搜索,资料超级多。

    1.4.   实时数据库C语言接口
    为了通过我们自己的应用程序操作数据库,必须使用数据库的API接口,前面提到了,Redis的API接口又多又全。我的程序为C语言的,所以我也选择C语言接口API——hiredis。

    Hiredis是Redis数据库的简约C客户端库。它是简约的,因为它只是增加了对协议的最小支持,但是同时它使用了一个高级别的printf-likeAPI,所以对于习惯了printf风格的C编程用户来说,其非常容易使用,而且API中没有明确的绑定每个Redis命令。除了支持发送命令和接收回复之外,它还附带了一个与I/O层分离的回复解析器。为了方便复用,其设计成为一个流解析器,例如可以在更高级别的语言 绑定中使用,以实现高效的回复解析。

    Hiredis仅支持二进制安全(binary-safe)的Redis协议,因此您可以将其与任何 Redis版本(> = 1.2.0)配合使用。该库带有多个API。 有同步API,异步API和答复解析API。

        简单的介绍在程序中使用的几个hiredis的使用方法。

    1)  建立连接

    redisContext *c = redisConnect("127.0.0.1", 6379);

    if (c == NULL || c->err) {

        if (c) {

            printf("Error: %s\n", c->errstr);

            // handle error

        } else {

            printf("Can't allocate redis context\n");

        }



    }

    2)  发送命令

    void(redisAsyncContext *c, void *reply, void*privdata);

    3)  使用事务型处理

    这个很重要。我的所有数据写入数据库都是采用此种方法实现的。实际测试效果还是挺好的。

    Redis 事务可以一次执行多个命令,并且带有以下两个重要的保证:

    n  批量操作在发送 EXEC 命令前被放入队列缓存。

    n  收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

    n  在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

    n  一个事务从开始到执行会经历以下三个阶段:

    开始事务 MULTI。

    命令入队。

    执行事务 EXEC。

                例子:

    reply = redisCommand(edis_con,"MULTI");

             for (i=0; i<nb; i++)

                    {

    redisCommand(edis_con,"SET %s %d", key_name, tab_rp_registers);

                    }

               reply = redisCommand(edis_con,"EXEC");

                    freeReplyObject(reply);


    4)  关闭连接

    /* Disconnects and frees thecontext */

        redisFree(redisContext *c);

    1.5.   实时数据库windows管理器

    通过在网上查找,我发现一个国内设计的管理器挺好用的,软件名叫treeNMS。使用挺简单,是基于浏览器的,不用安装一堆的软件,我喜欢。

    treenms.JPG
    关于软件的设置和使用请大家上网去搜索一下,其实挺容易的,至少我现在用的内容足够了。

    2.   Modbus-RTU协议的实现
    2.1.   协议栈描述

    通过网络搜索,linux下比较推崇的modbus库是libmodbus,他支持主站和子站两种模式,我决定选用这个源码开源的libmodbus作为Modbus-RTU的开发基础。

    2.2.   libmodbus安装

    要想正确的使用这个库,可以直接编译源码方式,也可以采用库的方式。

    下载libmodbus源代码 http://libmodbus.org/download/

    本次采用稳定版libmodbus-3.0.6.tar.gz

    1) 拷贝到相应目录,解压

    tar -zxvf libmodbus-3.0.6.tar.gz

    2) 创建安装目录

    mkdirinstall

    3) 进入解压的目录

    cd libmodbus-3.0.6

    4) 配置编译选项(注:--prefix选项是安装目录,根据自己环境确定)

    ./configure --host=arm-fsl-linux-gnueabi--enable-static --prefix=[安装路径]/install/

    5) 编译

    make

    6) 安装

    makeinstall

    7) 在install生成三个目录:include  lib  share

    2.3.   libmodbus的应用

    1) 按要求打开端口

        /* RTU */

        ctx = modbus_new_rtu("/dev/ttymxc1", 19200, 'N', 8, 1);

    2) 建立连接

    modbus_set_slave(ctx, Modbus_scan_str[Modbus_scan_No].sta_no);

    if(modbus_connect(ctx) == -1) {

                fprintf(stderr, "Connection failed:%s\n",

                        modbus_strerror(errno));

                modbus_free(ctx);

                return -1;

         }

    在打开端口后,为了可以巡检到我们需要的子站,必须首先调用这个函数将准备管理的子站ID提供给协议栈。

        这个很重要,要不然你不会读取到正确的子站数据。

    3) 读取数据

    针对命令读保持寄存器-3

                               连接 地址  数量    返回数据

    rc= modbus_read_registers(ctx, addr, nb, tab_rp_registers);

    命令执行后,tab_rp_registers[]中将保存我读取到的子站保持寄存器中的数据。我们可以针对这个数组进行操作。

    还有其它命令,此处不一一介绍,请大家看库函数或我前面的帖子中又一些说明。

    4) 关闭连接

    /* Close the connection */

    modbus_free(ctx);

    2.4.   针对多个子站的组态

    为了方便管理多个子站,我创建了一个结构体,用来存储子站信息。

    typedef struct

    {

        unsigned char sta_no;   /*子站ID*/

        unsigned char modbus_cmd;   /*巡检指令*/

        unsigned int src_addr; /*起始地址*/

        unsigned char get_num;  /*读取数量*/

        char data_type[10];/*数据类型*/

        unsigned long dest_addr;/*目的地址*/

    }MODBUS_SCAN_STR;

    通过组态这个结构体的内容,我们可以读取多个子站数据,和单个子站中的多种数据。

    MODBUS_SCAN_STR Modbus_scan_str[MODBUS_SCAN_NUM] =

    {

           {1,3, 0,10,"int",0},

           {1,3,10,10,"int",0},

           {2,3, 0,10,"int",0},

               {2,3,10,20,"int",0},

               {2,3,50,10,"int",0}

    };

    3.   CDT协议的实现
    3.1.   协议栈描述

    CDT的全名为循环式远动规约。该标准为电力行业通讯标准之一,规定了电网数据采集和监控系统之间数据交换的传输规则。

    CDT协议采用可变帧长度、多种帧类别循环传送、变位遥信优先传递,重要遥测量更新循环时间较短,区分循环量、随机量和插入量采用不同形式传递信息,以满足电网调度安全监控系统对远动信息的实时性和可靠性的要求。

    CDT协议帧的基本结构为:

    同步字
    控制字
    信息字
    信息字…  …
    EB90EB90EB90
    71610A0101CRC8
    00xxxxxxxxCRC8
    01xxxxxxxxCRC8…  …


    3.2.   CRC校验

    CRC校验的基本思想是利用线性编码理论,在发送端根据要传送的k位二进制码序列,以一定的规则产生一个校验用的监督码(既CRC码)r位,并附在信息后边,构成一个新的二进制码序列数共(k+r)位,最后发送出去。在接收端,则根据信息码和CRC码之间所遵循的规则进行检验,以确定传送中是否出错。

    CDT协议采用CRC8校验形式。

    校验码实现代码:

    unsignedchar CalCRC(unsigned char *data, int len)

    {

        int i;

        unsigned char result = 0;

        for(i=0; i<len; i++)

        {

           Crcget(data, result);

        }

        result = (unsigned char)~result;

    }


        voidCrcget(unsigned char data, unsigned char *result)

        {

           *result= crctable[*result ^ data];

        }

        //CRC8表,X8+X2+X+1

    unsigned char crctable[] =

    {
    crc1.JPG
    crc2.JPG

    file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image003.jpg
    3.3.   针对多个子站的组态
    为了方便管理多个子站,通过创建一个结构体来存储子站信息。主要包括每个子站上送的遥测量、遥信量和电度量。

    typedef struct

    {

        unsigned char sta_no;   /*子站ID*/

        unsigned char YC_num;   /*遥测量数量*/

        unsigned char YX_num;   /*遥信量数量*/

        unsigned char DD_num;   /*电度量数量*/

    }CDT_SCAN_STR;

    通过组态这个结构体的内容,我们可以获取每个子站上送信息的内容和数量。这里以两个子站为例,列举了子站信息的配置方法。

    #define CDT_SCAN_NUM     2

    CDT_SCAN_STR cdt_scan_str[CDT_SCAN_NUM]=

    {

           {.sta_no = 1,

            .YC_num = 20,

            .YX_num = 128,

            .DD_num = 4},


           {.sta_no = 2,

            .YC_num = 20,

            .YX_num = 64,

            .DD_num = 0}

    };


    4.   运行效果检验
    4.1.   仿真环境建立

    根据组态内容,建立仿真环境,基本硬件实现结构如下图所示:

    iot.jpg
    目前使用仅实现了2个串行总线和两种协议的转换,不过可以扩展更多总线和协议的转换,例如CAN总线、其它行业标准协议或自定义协议等。

    串口2对应设备号为/dev/ttymxc1;

    串口3对应设备号为/dev/ttymxc2。

            注:关于使能IOT板卡多个串口功能,请参考我的贴子。

    【我的项目666】+通讯管理(4)使能串口功能


    4.2.   实际运行效果

    1)  启动redis的服务器

    命令:

           redis-server redis.conf

    运行效果检查
    top.JPG
    2)  启动通讯管理应用程序

    命令:

           ./commser

    运行效果
    commser.JPG
    3)  启动模拟子站软件

    命令:

           模拟子站采用两个windows下的软件,一个是Modsim32,一个是PMA。Modsim32模拟两个子站,PMA分别模拟两个子站。

    运行效果:
    file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image012.jpg
    file:///C:/Users/jiyong/AppData/Local/Temp/msohtmlclip1/01/clip_image014.jpg
    modsim.JPG
    cdt.JPG
    4)  启动实时数据库监视软件

    命令:

        启动TreeNMS,打开浏览器并输入127.0.0.1:8085打开实时数据库监视。

    运行效果:
    treenms-1.JPG treenms-2.JPG
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-4-5 11:15
  • 签到天数: 1061 天

    [LV.10]以坛为家III

    29

    主题

    1517

    帖子

    31

    金牌会员

    Rank: 6Rank: 6

    积分
    4972
    最后登录
    2024-4-21
     楼主| 发表于 2018-6-28 14:43:02 | 显示全部楼层
    多谢顶贴鼓励
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2018-4-12 10:22
  • 签到天数: 1 天

    [LV.1]初来乍到

    15

    主题

    386

    帖子

    0

    高级会员

    Rank: 4

    积分
    541
    最后登录
    2018-6-28
    发表于 2018-6-28 15:09:41 | 显示全部楼层
    666,好文
    falajf
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2018-10-17 09:43
  • 签到天数: 47 天

    [LV.5]常住居民I

    21

    主题

    135

    帖子

    0

    中级会员

    Rank: 3Rank: 3

    积分
    487
    最后登录
    2022-10-24
    发表于 2018-6-28 16:47:27 | 显示全部楼层
    不错不错
    该会员没有填写今日想说内容.
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /4 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2024-4-26 14:52 , Processed in 0.129648 second(s), 22 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表