新闻中心

PRESS CENTER 纵横智控
你的位置: 首页 新闻 产品资讯
纵横智控

EC100工业计算机工控机双N设计-开发指南

2025-08-01 15:00:28 阅读: 发布人:纵横智控

开发准备

SSH工具

推荐使用MobaXterm,或者选择自己习惯的SSH工具

https://iotrouter.yuque.com/attachments/yuque/0/2025/zip/40387797/1751447985727-10cb8014-e57d-4f95-85f2-722b2f822d6f.zip

串口调试工具

推荐使用XCOM,或者选择自己习惯的串口调试工具

https://iotrouter.yuque.com/attachments/yuque/0/2025/zip/40387797/1751448098345-cc351ada-aa82-4de7-b0bf-b6a909e637bc.zip

硬件工具

常用的调试工具有:CAN转USB工具 / 485转USB工具 / 网线等,请自行准备

交叉编译工具链

https://iotrouter.yuque.com/attachments/yuque/0/2025/tar/40387797/1751447691686-3001e3bf-2f16-43d3-a4d7-55c4705dff97.tar

软件环境

软件

版本

OS

Ubuntu 20.04.1;部分裁剪

NodeJs

v22.16.0

Python

python3.8

Shell

bash

GLIBC

2.31

外设接口

1. 串口

EC100自带两路自收发RS485

硬件接口

设备文件

RS485-1

/dev/ttyS4

RS485-2

/dev/ttyS3

注:自收发RS485,用户不需要关心收发切换,硬件会自动处理

1.1. 快速测试

使用minicom工具测试,具体使用方法可以百度或者咨询DeepSeek

1.2. 代码测试样例


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <termios.h>

#include <errno.h>

#include <sys/select.h>


#define BUFFER_SIZE 256


typedef struct {

    int baud_rate;      // 波特率

    int data_bits;      // 数据位 (5,6,7,8)

    int stop_bits;      // 停止位 (1,2)

    char parity;        // 校验位 (N:无校验, O:奇校验, E:偶校验)

} SerialConfig;


int set_serial_attr(int fd, SerialConfig *config)

{

    struct termios tty;


    if (tcgetattr(fd, &tty) < 0) {

        perror("tcgetattr");

        return -1;

    }


    // 设置波特率

    speed_t speed;

    switch (config->baud_rate) {

        case 9600:   speed = B9600; break;

        case 19200:  speed = B19200; break;

        case 38400:  speed = B38400; break;

        case 57600:  speed = B57600; break;

        case 115200: speed = B115200; break;

        default:

            fprintf(stderr, "Unsupported baud rate, using 115200\n");

            speed = B115200;

    }

    cfsetispeed(&tty, speed);

    cfsetospeed(&tty, speed);


    // 设置数据位

    tty.c_cflag &= ~CSIZE;

    switch (config->data_bits) {

        case 5: tty.c_cflag |= CS5; break;

        case 6: tty.c_cflag |= CS6; break;

        case 7: tty.c_cflag |= CS7; break;

        case 8: tty.c_cflag |= CS8; break;

        default:

            fprintf(stderr, "Unsupported data bits, using 8\n");

            tty.c_cflag |= CS8;

    }


    // 设置停止位

    if (config->stop_bits == 2) {

        tty.c_cflag |= CSTOPB;

    } else {

        tty.c_cflag &= ~CSTOPB;

    }


    // 设置校验位

    switch (config->parity) {

        case 'N': case 'n':

            tty.c_cflag &= ~PARENB;  // 无校验

            break;

        case 'O': case 'o':

            tty.c_cflag |= PARENB;   // 奇校验

            tty.c_cflag |= PARODD;

            break;

        case 'E': case 'e':

            tty.c_cflag |= PARENB;   // 偶校验

            tty.c_cflag &= ~PARODD;

            break;

        default:

            fprintf(stderr, "Unsupported parity, using N\n");

            tty.c_cflag &= ~PARENB;

    }


    // 其他设置

    tty.c_cflag |= (CLOCAL | CREAD);    // 启用接收和本地模式

    tty.c_cflag &= ~CRTSCTS;            // 无硬件流控


    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 原始输入模式

    tty.c_oflag &= ~OPOST;              // 原始输出模式


    tty.c_cc[VMIN] = 1;                 // 最小读取字符数

    tty.c_cc[VTIME] = 0;                // 读取超时时间(单位:0.1秒)


    if (tcsetattr(fd, TCSANOW, &tty) < 0) {

        perror("tcsetattr");

        return -1;

    }


    return 0;

}


int main(int argc, char *argv[])

{

    int fd;

    char *portname;


    if (argc < 2) {

        fprintf(stderr, "Usage: %s <serial_port>\n", argv[0]);

        exit(EXIT_FAILURE);

    }


    portname = argv[1];


    // 配置串口参数

    SerialConfig config = {

        .baud_rate = 115200,  // 波特率

        .data_bits = 8,       // 数据位

        .stop_bits = 1,       // 停止位

        .parity = 'N'         // 校验位 (N:无校验, O:奇校验, E:偶校验)

    };


    fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);

    if (fd < 0) {

        perror("open");

        exit(EXIT_FAILURE);

    }


    if (set_serial_attr(fd, &config)) {

        close(fd);

        exit(EXIT_FAILURE);

    }


    printf("Serial port echo test running on %s\n", portname);

    printf("Configuration: %d baud, %d data bits, %d stop bit, %c parity\n",

           config.baud_rate, config.data_bits, config.stop_bits, config.parity);

    printf("Press Ctrl+C to exit.\n");


    fd_set readfds;

    char buffer[BUFFER_SIZE];

    int n;


    while (1) {

        FD_ZERO(&readfds);

        FD_SET(fd, &readfds);


        // 无限等待数据到达(移除了超时设置)

        if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) {

            perror("select");

            break;

        }


        if (FD_ISSET(fd, &readfds)) {

            n = read(fd, buffer, BUFFER_SIZE - 1);

            if (n > 0) {

                buffer[n] = '\0';

                printf("Received %d bytes: %s\n", n, buffer);


                // 回显数据

                write(fd, buffer, n);

            } else if (n < 0) {

                if (errno != EAGAIN && errno != EWOULDBLOCK) {

                    perror("read");

                    break;

                }

            }

        }

    }


    close(fd);

    return 0;

}

编译: gcc rs485_example.c -o rs485_example

RS485-1测试运行: ./rs485_example/dev/ttyS4  

2. LED

EC100 总共4个指示灯,其中2个可用户编程控制

硬件接口

IO索引

IO编号

chip组

备注

POW

/

/

/

电源指示灯

RUN

39

GPIO1_A7

gpiochip1

内部程序占用;不可编程;运行后呈闪烁状态

LED1

38

GPIO1_A6

gpiochip1

用户可编程

LED2

36

GPIO1_A4

gpiochip1

用户可编程

注:IO索引是由IO编号计算得出

2.1. 快速测试

#!/bin/bash


# 检查是否传入了GPIO索引

if [ -z "$1" ]; then

    echo "Usage: $0 <gpio_number>"

    exit 1

fi


GPIO=$1

GPIO_PATH="/sys/class/gpio/gpio$GPIO"

EXPORT_PATH="/sys/class/gpio/export"


# 判断GPIO是否已经导出

if [ ! -d "$GPIO_PATH" ]; then

    echo "Exporting GPIO $GPIO..."

    echo "$GPIO" > "$EXPORT_PATH"

    if [ $? -ne 0 ]; then

        echo "Failed to export GPIO $GPIO. Check permissions or if it's in use."

        exit 1

    fi

else

    echo "GPIO $GPIO is already exported."

fi


# 设置GPIO方向为输出

echo "out" > "$GPIO_PATH/direction"


# 设置初始值为低电平

echo 0 > "$GPIO_PATH/value"


echo "Toggling GPIO $GPIO every 1 second. Press Ctrl+C to stop."


# 开始循环翻转

while true; do

    echo 1 > "$GPIO_PATH/value"

    sleep 1

    echo 0 > "$GPIO_PATH/value"

    sleep 1

done

测试运行:bash led_example.sh 38

2.2. 代码测试样例

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/ioctl.h>

#include <linux/gpio.h>

#include <errno.h>


#define GPIO_CHIP "/dev/gpiochip1"  // 使用gpiochip1

#define GPIO_LINE 38%32             // 要控制的GPIO在chip1上的偏移

#define PERIOD_US 500000            // 周期时间(微秒)


int main() {

    struct gpiohandle_request req;

    struct gpiohandle_data data;

    int fd, ret;


    // 打开GPIO字符设备

    fd = open(GPIO_CHIP, O_RDWR);

    if (fd < 0) {

        perror("无法打开GPIO设备");

        return EXIT_FAILURE;

    }


    // 设置GPIO请求

    memset(&req, 0, sizeof(req));

    req.lineoffsets[0] = GPIO_LINE;  // GPIO线号

    req.flags = GPIOHANDLE_REQUEST_OUTPUT;  // 设置为输出模式

    req.default_values[0] = 0;       // 初始值设为低电平

    strcpy(req.consumer_label, "gpio-toggle");  // 消费者标签

    req.lines = 1;                   // 要控制的GPIO线数量


    // 请求GPIO控制

    ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);

    if (ret < 0) {

        perror("无法获取GPIO控制权");

        close(fd);

        return EXIT_FAILURE;

    }


    // 关闭GPIO字符设备(我们已经获得了线句柄)

    close(fd);


    printf("正在控制GPIO chip1 line %d,周期为%.1f秒...\n", 

           GPIO_LINE, PERIOD_US/1000000.0);


    // 周期切换GPIO电平

    while (1) {

        data.values[0] = 1;  // 高电平

        ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);

        if (ret < 0) {

            perror("设置GPIO高电平失败");

            break;

        }

        usleep(PERIOD_US/2);


        data.values[0] = 0;  // 低电平

        ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);

        if (ret < 0) {

            perror("设置GPIO低电平失败");

            break;

        }

        usleep(PERIOD_US/2);

    }


    // 关闭GPIO线句柄

    close(req.fd);


    return EXIT_SUCCESS;

}

编译: gcc led_example.c -o led_example

测试运行: ./led_example

3. CAN FD

EC100 有两路高速CAN接口

硬件接口

网络接口

CAN1 [H1 L1]

can0

CAN2 [H2 L2]

can1

边缘计算网关/物联网平台/网关模块/纵横智控

3.1. 快速测试

将can0和can1互接;命令行测试

#首先初始化 can0 与 can1,波特率选择 500k,使用 canfd,然后选择 can0 接受数据,can1 发送数据


ip link set can0 down

ip link set can1 down


ip link set can0 type can bitrate 500000 sample-point 0.8 dbitrate 2000000 sample-point 0.8 fd on

ip link set can1 type can bitrate 500000 sample-point 0.8 dbitrate 2000000 sample-point 0.8 fd on


ip link set can0 up

ip link set can1 up


echo 4096 > /sys/class/net/can0/tx_queue_len

echo 4096 > /sys/class/net/can1/tx_queue_len


candump can0 &


cansend can1 001234EF#00.11.22.33.44.55.66.77

3.2. 代码测试样例

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <net/if.h>

#include <sys/ioctl.h>

#include <sys/socket.h>

#include <linux/can.h>

#include <linux/can/raw.h>

#include <fcntl.h>

#include <errno.h>

#include <stdint.h>  


#define CAN_INTERFACE "can0"

#define BITRATE 500000

#define DATA_BITRATE 2000000

#define SAMPLE_POINT 0.8

#define TX_QUEUE_LEN 4096


// 配置CAN FD接口

int setup_can_fd_interface(const char *ifname) {

    char cmd[512];

    FILE *fp;

    

    // 关闭接口

    snprintf(cmd, sizeof(cmd), "ip link set %s down", ifname);

    system(cmd);

    

    // 设置CAN FD参数

    snprintf(cmd, sizeof(cmd), 

             "ip link set %s type can bitrate %d sample-point %.1f "

             "dbitrate %d dsample-point %.1f fd on", 

             ifname, BITRATE, SAMPLE_POINT, DATA_BITRATE, SAMPLE_POINT);

    if (system(cmd) != 0) {

        fprintf(stderr, "Failed to configure CAN FD interface %s\n", ifname);

        return -1;

    }

    

    // 设置TX队列长度

    snprintf(cmd, sizeof(cmd), "/sys/class/net/%s/tx_queue_len", ifname);

    fp = fopen(cmd, "w");

    if (fp == NULL) {

        perror("Failed to open tx_queue_len");

        return -1;

    }

    fprintf(fp, "%d", TX_QUEUE_LEN);

    fclose(fp);

    

    // 启用接口

    snprintf(cmd, sizeof(cmd), "ip link set %s up", ifname);

    if (system(cmd) != 0) {

        fprintf(stderr, "Failed to bring up CAN interface %s\n", ifname);

        return -1;

    }

    

    printf("CAN FD interface %s configured:\n", ifname);

    printf("  Bitrate: %d\n", BITRATE);

    printf("  Data bitrate: %d\n", DATA_BITRATE);

    printf("  Sample point: %.1f\n", SAMPLE_POINT);

    printf("  TX queue length: %d\n", TX_QUEUE_LEN);

    

    return 0;

}


// 发送CAN FD帧

int send_can_fd_frame(int sock, uint32_t can_id, uint8_t *data, uint8_t len) {

    struct canfd_frame frame;

    

    memset(&frame, 0, sizeof(frame));

    frame.can_id = can_id;

    frame.len = len;

    memcpy(frame.data, data, len);

    frame.flags = CANFD_BRS; // 启用比特率切换

    

    int nbytes = write(sock, &frame, sizeof(frame));

    if (nbytes != sizeof(frame)) {

        perror("CAN FD frame send failed");

        return -1;

    }

    

    printf("Sent CAN FD frame: ID 0x%08X, data: ", can_id);

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

        printf("%02X ", data[i]);

    }

    printf("\n");

    

    return 0;

}


// 接收CAN帧(支持经典CAN和CAN FD)

void receive_can_messages(int sock) {

    struct canfd_frame frame;

    int nbytes;

    

    printf("Starting to receive CAN messages (Ctrl+C to stop)...\n");

    

    while (1) {

        nbytes = read(sock, &frame, sizeof(frame));

        if (nbytes < 0) {

            if (errno == EAGAIN || errno == EWOULDBLOCK) {

                continue;

            }

            perror("CAN frame read failed");

            break;

        }

        

        if (nbytes == CAN_MTU) {

            // 经典CAN帧

            printf("Received CAN frame: ID 0x%08X, DLC %d, data: ", 

                   frame.can_id, frame.len);

        } else if (nbytes == CANFD_MTU) {

            // CAN FD帧

            printf("Received CAN FD frame: ID 0x%08X, DLC %d, data: ", 

                   frame.can_id, frame.len);

        } else {

            printf("Received frame with unexpected size %d\n", nbytes);

            continue;

        }

        

        for (int i = 0; i < frame.len; i++) {

            printf("%02X ", frame.data[i]);

        }

        printf("\n");

    }

}


int main() {

    int s;

    struct sockaddr_can addr;

    struct ifreq ifr;

    

    // 配置CAN FD接口

    if (setup_can_fd_interface(CAN_INTERFACE) < 0) {

        return 1;

    }

    

    // 创建socket

    if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {

        perror("Socket creation failed");

        return 1;

    }

    

    // 启用CAN FD支持

    int enable_canfd = 1;

    if (setsockopt(s, SOL_CAN_RAW, CAN_RAW_FD_FRAMES, 

                  &enable_canfd, sizeof(enable_canfd)) < 0) {

        perror("Failed to enable CAN FD support");

        close(s);

        return 1;

    }

    

    // 指定CAN接口

    strcpy(ifr.ifr_name, CAN_INTERFACE);

    if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {

        perror("IOCTL SIOCGIFINDEX failed");

        close(s);

        return 1;

    }

    

    // 绑定socket到CAN接口

    addr.can_family = AF_CAN;

    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {

        perror("Bind failed");

        close(s);

        return 1;

    }

    

    // 发送测试CAN FD帧 (ID 0x001234EF, 数据 00.11.22.33.44.55.66.77)

    uint8_t test_data[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};

    if (send_can_fd_frame(s, 0x001234EF, test_data, sizeof(test_data)) < 0) {

        close(s);

        return 1;

    }

    

    // 开始接收CAN消息

    receive_can_messages(s);

    

    close(s);

    return 0;

}

编译: gcc can_example.c -o can_example

测试运行: ./can_example

4. 按键

EC100按键:内部程序占用,用于重启或者复位设备的web网页相关配置

重启:按下1s抬起

复位+重启:按下>5s

5. 硬件看门狗

EC100默认启用硬件看门狗,由内部程序管理喂狗

喂狗周期:<30s

注:如果停止内部程序,必须自行接管看门狗喂狗服务,否则设备会定期重启

6. 网口

网口分为WAN+LAN口,由内部网络程序管理;需在web网页配置,详情请见:

此处为语雀内容卡片,点击链接查看:https://iotrouter.yuque.com/zn3vdn/ec/go5fnig7b5xumq79?singleDoc

7. 蜂窝无线

EC100支持4G通信(4G版本),模组的识别+拨号由内部程序管理,用户无需关心;详情请见:

此处为语雀内容卡片,点击链接查看:https://iotrouter.yuque.com/zn3vdn/ec/go5fnig7b5xumq79?singleDoc

软件应用

1. 内部管理程序

EC100为了简化用户使用,内置管理程序iotrouter,开机自启动。管理程序包括但不限于下列功能:

设备初始化

网络管理

看门狗

按键监听

防火墙

设备配置服务(默认80端口,监听端口可更改;文件:/usr/local/src/iotrouter/web/user-config.js)

用户文件浏览功能(默认/home路径;路径可更改;文件:/usr/local/src/iotrouter/web/user-config.js)

注:内部程序必须保活,否则会造成设备开机运行异常。如果用户一定需要关闭内部管理程序,必须自行接管上述管理程序服务。

2. NeuronEX

EC100内置NeuronEX-Lite,服务开机自启动,监听端口8085;介绍详见:

此处为语雀内容卡片,点击链接查看:https://iotrouter.yuque.com/zn3vdn/ec/hdsb71i79vmfr8wd?singleDoc

查看服务状态:systemctl status neuronex

重启服务:systemctl restart neuronex

停止服务:systemctl stop neuronex

关闭服务自启:systemctl disable neuronex

3. Node-Red

EC100内置Node-Red,服务开机自启动,监听端口1880;介绍详见:

此处为语雀内容卡片,点击链接查看:https://iotrouter.yuque.com/zn3vdn/ec/hdsb71i79vmfr8wd?singleDoc

查看服务状态:systemctl status node-red

重启服务:systemctl restart node-red

停止服务:systemctl stop node-red

关闭服务自启:systemctl disable node-red

4. 用户自开发程序

用户程序由用户自由开发,用户程序独立运行,但须注意内存和空间管理避免导致系统异常。软件可加入系统自启动方式:

/etc/rc.local

systemd服务

/etc/init.d系统

我司也提供软件开发定制服务;如有需求请咨询销售人员

镜像烧录

什么时候会使用到镜像烧录:用户在使用设备时,可能因误删系统核心文件、错误修改系统配置参数(如注册表关键项),或误执行格式化系统分区等操作,导致系统无法启动、功能异常。此时,烧录对应设备的系统镜像,能快速将系统恢复到初始正常状态。

1. 烧录工具

烧录工具包较大请联系技术支持或销售获取

Driver:驱动

tool:烧录工具

*.img:出厂镜像

边缘计算网关/物联网平台/网关模块/纵横智控

2. 驱动安装

安装\DriverDriverInstall.exe

边缘计算网关/物联网平台/网关模块/纵横智控

3. 烧录

打开\tool\RKDevTool.exe

点击Upgrade Firmware跳转到烧录界面

点击Firmware选择镜像

边缘计算网关/物联网平台/网关模块/纵横智控

短接EC100的看门狗(烧录过程必须短接看门狗否则设备会无限重启)

按住下载按钮给EC100上电

EC100芯片图

烧录下方会出现Found One MASKROM Device表示已经识别到设备

点击Upgrade开始烧录,右侧日志窗口出现烧录日志

最后一行出现Success表示烧录成功

边缘计算网关/物联网平台/网关模块/纵横智控

热门产品