加入星计划,您可以享受以下权益:

  • 创作内容快速变现
  • 行业影响力扩散
  • 作品版权保护
  • 300W+ 专业用户
  • 1.5W+ 优质创作者
  • 5000+ 长期合作伙伴
立即加入
  • 正文
    • can.c
    • main.c
  • 相关推荐
  • 电子产业图谱
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-CAN编程示例之socket CAN例程

11/18 10:23
277
阅读需 38 分钟
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

注意:学习资料可在ElfBoard官方网站“资料”专区获取。

本节用到的源码路径:ELF 1开发板资料包3-例程源码3-1 命令行例程源码6_elf1_cmd_can

can.c

#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <termios.h> /*PPSIX 终端控制定义*/
#include "mycan.h"

/*
 * @description        : 字符串按格式输出
 * @param - *destpoint : 字符串格式缓冲区
 * @param - *fmt       : 多个参数
 * @return		       : 按格式输出字符串缓冲区首指针
 */
char func_dprintf(char *destpoint, char *fmt, ...)
{
    va_list arg_ptr;
    char ulen, *tmpBuf;

    tmpBuf = destpoint;
    va_start(arg_ptr, fmt);
    ulen = vsprintf(tmpBuf, fmt, arg_ptr);

    va_end(arg_ptr);
    return ulen;
}

/*
 * @description     : 打开can外设,设置波特率,创建文件描述符
 * @param - *device : 设备名称
 * @param - para    : can应用参数
 * @return		    : 打开设备执成功返回文件描述符,失败返回-1
 */
int func_open_can(char *device, struct_can_param para)
{
    FILE *fstream = NULL;
    char buff[300] = {0}, command[50] = {0};
    int fd = -1;
    struct sockaddr_can addr;
    struct ifreq ifr;

    /*关闭can设备 ifconfig can0 down*/
    memset(buff, 0, sizeof(buff));
    memset(command, 0, sizeof(command));
    func_dprintf(command, "ifconfig %s down", device);
    printf("%s n", command);

    if (NULL == (fstream = popen(command, "w")))
    {
        fprintf(stderr, "execute command failed: %s", strerror(errno));
    }
    while (NULL != fgets(buff, sizeof(buff), fstream))
    {
        printf("%sn", buff);
        if (strstr(buff, "No such device") || strstr(buff, "Cannot find device"))
            return -1;
    }
    pclose(fstream);
    sleep(1);

    /*设置can波特率 ip link set can0 up type can bitrate 250000 triple-sampling on*/
    memset(buff, 0, sizeof(buff));
    memset(command, 0, sizeof(command));
    if (para.loopback_mode == 1)
    {
        func_dprintf(command, "ip link set %s up type can bitrate %d triple-sampling on loopback on", device, para.baudrate);
    }
    else
    {
        func_dprintf(command, "ip link set %s up type can bitrate %d triple-sampling on loopback off", device, para.baudrate);
    }
    printf("%s n", command);

    if (NULL == (fstream = popen(command, "w")))
    {
        fprintf(stderr, "execute command failed: %s", strerror(errno));
    }
    while (NULL != fgets(buff, sizeof(buff), fstream))
    {
        printf("%sn", buff);
        if (strstr(buff, "No such device") || strstr(buff, "Cannot find device"))
            return -1;
    }
    pclose(fstream);
    sleep(1);

    /*打开can设备 ifconfig can0 up*/
    memset(buff, 0, sizeof(buff));
    memset(command, 0, sizeof(command));
    func_dprintf(command, "ifconfig %s up", device);
    printf("%s n", command);

    if (NULL == (fstream = popen(command, "w")))
    {
        fprintf(stderr, "execute command failed: %s", strerror(errno));
    }
    while (NULL != fgets(buff, sizeof(buff), fstream))
    {
        printf("%sn", buff);
        if (strstr(buff, "No such device") || strstr(buff, "Cannot find device"))
            return -1;
    }
    pclose(fstream);
    sleep(3);

    /* 创建 socket */
    fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (fd < 0)
    {
        printf("socket:%s", strerror(errno));
        return -1;
    }
    /* 设置接口设备名称 */
    strcpy(ifr.ifr_name, device);
    /* 确定接口 index */
    if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0)
    {
        printf("SIOCGIFINDEX:%sn", strerror(errno));
        return -1;
    }

    memset(&addr, 0, sizeof(addr));
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;
    /* 绑定 socket到 CAN 接口 */
    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
    {
        printf("bind:%sn", strerror(errno));
        return -1;
    }

    return fd;
}

/*
 * @description : 设置can回环模式和过滤规则
 * @param - fd  : 文件描述符
 * @param - para: can应用参数
 * @return		: 设置成功返回1,失败返回-1
 */
int func_set_can(int fd, struct_can_param para)
{
    int loopback = 1;
    int reciveown = 1;

    if (para.loopback_mode == 1)
    {
        // 回环设置 0 关闭回环  1 打开回环
        //  setsockopt(fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
        // 接收自己的帧 0 不接收自己帧 1 接收自己帧
        //  setsockopt(fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &reciveown, sizeof(reciveown));
    }

    /* 设置过滤规则 */
    if (setsockopt(fd, SOL_CAN_RAW, CAN_RAW_FILTER, &para.filter, sizeof(para.filter)) < 0)
    {
        printf("error when set filtern");
        return -1;
    }
    return 1;
}

/*
 * @description    : can接收一个can帧
 * @param - fd     : 文件描述符
 * @param - *pframe: 一个can帧结构体指针
 * @return		   : 接收数据长度
 */
int func_receive_can_frame(int fd, struct can_frame *pframe)
{
    int rx_count = 0;

    rx_count = recv(fd, pframe, sizeof(*pframe), 0);
    if (rx_count <= 0)
    {
        return rx_count;
    }
    else
    {
        if (pframe->can_id & CAN_EFF_FLAG) /*如果是扩展帧,清除扩展帧标识*/
        {
            pframe->can_id &= (~CAN_EFF_FLAG);
        }
        else
        {
            pframe->can_id &= (~CAN_SFF_MASK);
        }
    }

    return pframe->can_dlc;
}

/*
 * @description  : can接收一组数据包
 * @param - fd   : 文件描述符
 * @param - *buff: 要接收can一组数据的缓冲区首指针
 * @param - len  : 接收到一组数据的长度
 * @return		 : 接收数据长度
 */
int func_receive_can_buff(int fd, unsigned char *buff, int len)
{
    int receive_len = 0, total_receive_len = 0;
    struct can_frame frame;
    int i = 0;

    while (1)
    {
        receive_len = func_receive_can_frame(fd, &frame);
        for (i = 0; i < receive_len; i++)
        {
            *(buff + total_receive_len) = frame.data[i];
            total_receive_len++;
        }
        if ((receive_len < 8) || (total_receive_len > (len - 8)))
        {
            return total_receive_len;
        }
    }
    return total_receive_len;
}

/*
 * @description    : can发送一个can帧
 * @param - fd     : 文件描述符
 * @param - *pframe: 一个can帧结构体指针
 * @param - param  : can应用参数
 * @return		   : 发送数据长度,发送失败返回-1
 */
int func_send_can_frame(int fd, struct can_frame *pframe, struct_can_param param)
{
    int result = 0;

    if (param.extend == 1) /*扩展帧增加扩展帧标志*/
    {
        pframe->can_id &= CAN_EFF_MASK;
        pframe->can_id |= CAN_EFF_FLAG;
    }
    else
    {
        pframe->can_id &= CAN_SFF_MASK;
    }
    result = send(fd, pframe, sizeof(struct can_frame), 0);
    if (result == -1)
    {
        printf("send:%sn", strerror(errno));
        return -1;
    }
    return result;
}

/*
 * @description  : can发送一组数据包
 * @param - fd   : 文件描述符
 * @param - *buff: 要发送can一组数据的缓冲区首指针
 * @param - len  : 发送数据的长度
 * @param - param: can应用参数
 * @return		 : 实际发送数据长度
 */
int func_send_can_buff(int fd, unsigned char *buff, int len, struct_can_param param)
{
    int remain_frame_len = 0, frame_numb = 0;
    struct can_frame frame;
    int i = 0;

    remain_frame_len = len;
    while (1)
    {
        if (remain_frame_len >= 8)
        {
            frame.can_dlc = 8;     /*填充发送长度*/
            remain_frame_len -= 8; /*剩余数据长度*/
        }
        else
        {
            frame.can_dlc = remain_frame_len; /*填充发送长度*/
            remain_frame_len = 0;             //
        }

        frame.can_id = param.id; /*填充发送id*/

        for (i = 0; i < frame.can_dlc; i++)
        {
            frame.data[i] = buff[frame_numb * 8 + i]; /*填充发送数据*/
        }
        func_send_can_frame(fd, &frame, param);
        frame_numb++;
        if (remain_frame_len == 0)
        {
            return len;
        }
    }
    return len;
}

main.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h> /*PPSIX 终端控制定义*/
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include "mycan.h"

char ver[20] = {"ver01.001"};

int sock_fd;
struct_can_param can_param = {250000, 0x103, {
                                                 0x100,
                                                 0x00,
                                             },
                              0,
                              0,
                              0,
                              2000000};

unsigned char send_buff[1024], receive_buff[1024];
unsigned int send_num = 0, receive_num = 0;

//数据收发应用
char active_send_buff[500] = "test can";
int active_send_mode = 0, active_send_num = 8, active_send_time = 1000, active_send_time_count = 0;
int real_send_num = 0;
int loopback_send_mode = 0;

char dev[20];

/*
 * @description : 自定义打印函数
 * @param - buff:  打印数据缓冲区
 * @param - lens:  打印数据长度
 * @param - mode:  打印格式
 * @return		: 无
 */
void func_my_print(unsigned char *buff, unsigned int lens, unsigned char mode)
{
    int i = 0;

    switch (mode)
    {
    case 'H': //按照16进制打印数据
        for (i = 0; i < lens; i++)
        {
            printf("0X%02X  ", buff[i]);
        }
        break;
    case 'h': //按照16进制打印数据
        for (i = 0; i < lens; i++)
        {
            printf("0x%02x  ", buff[i]);
        }
        break;
    case 'd': //按照10进制打印数据
        for (i = 0; i < lens; i++)
        {
            printf("%02d  ", buff[i]);
        }
        break;
    case 'c': //按照字符打印数据
        for (i = 0; i < lens; i++)
        {
            printf("%c", buff[i]);
        }
        break;
    default:
        break;
    }
    printf("n");
}

/*
 * @description  : 打印参数设置格式
 * @param - pname: 函数名
 * @return		 : 无
 */
static void print_usage(const char *pname)
{
    printf("Usage: %s device [-b baudrate] [-id id_numb] [-filt id mask] [-e] [-fd dbaudrate] [-t data time] [-l] [-L] [-v]"
           "nt'-b baudrate' for different baudrate, range 5k to 5M,unit is Kbps"
           "nt'-id id_numb' for nonzero can send id"
           "nt'-filt id mask' for receive can_id and can_mask"
           "nt'-e' for extend id frame"
           "nt'-fd dbaudrate' for can fd mode , dbaudrate range 250k to 5M,unit is Kbps"
           "nt'-t data time'  for interval set time actively sends the data, unit is s"
           "nt'-l' for can loopback mode"
           "nt'-L' for app loopback receive data"
           "nt'-rf path' save the data received by the serial port into the file, path for file to save"
           "nt'-v' show version"
           "ntexample : can0 baudrate 250k id 123 filt_id 105 filt_mask 1f0 extend id string data was send every 2s--> ./can_demo can0 -b 250 -id 123 -filt 105 1f0 -e -t abcdef 2"
           "ntexample : can1 baudrate 250k id 105 filt_id 123 filt_mask 120 extend id --> ./can_demo can1 -b 250 -id 105 -filt 123 1f0"
           "ntexample : can0 canfd mode baudrate 250k id 123 filt_id 105 filt_mask 1f0 extend id string data was send every 2s--> ./can_demo can0 -b 250 -id 123 -filt 105 1f0 -e -t 12345678 2 -fd 4000"
           "ntexample : can1 canfd mode baudrate 250k id 105 filt_id 123 filt_mask 120 extend id --> ./can_demo can1 -b 250 -id 105 -filt 123 1f0 -e -fd 4000"
           "ntexample : can0 canfd mode baudrate 500k extend id dbaudrate 4000k loopback mode 19 data was send every 1s --> ./can_demo can0 -b 500 -filt 0 0 -fd 4000 -t abcdefg 1 -e -ln ",
           pname);
}

/*
 * @description : 解析函数带入参数
 * @param - numb: 参数个数
 * @param - *param: 带入参数数组指针
 * @param - *canparam: can应用参数
 * @return		: 无
 */
void get_param(int numb, char *param[], struct_can_param *canparam)
{
    int i = 0, len = 0;
    unsigned int baudrate = 0, id = 0, mask = 0, active_param = 0;

    if (numb <= 2)
        return;

    for (i = 2; i < numb; i++)
    {
        if (!strcmp(param[i], "-b"))
        {
            i++;
            baudrate = atoi(param[i]);
            switch (baudrate)
            {
            case 5:
            case 10:
            case 20:
            case 40:
            case 50:
            case 80:
            case 100:
            case 125:
            case 200:
            case 250:
            case 400:
            case 500:
            case 666:
            case 800:
            case 1000:
            case 2000:
            case 4000:
            case 5000:
                canparam->baudrate = baudrate * 1000;
                break;
            }
            continue;
        }
        if (!strcmp(param[i], "-id"))
        {
            i++;
            id = strtoul(param[i], NULL, 16);
            if (id)
            {
                canparam->id = (unsigned int)id;
            }
            continue;
        }
        if (!strcmp(param[i], "-filt"))
        {
            i++;
            id = 0;
            id = strtoul(param[i], NULL, 16);
            canparam->filter.can_id = (canid_t)id;
            i++;
            mask = strtoul(param[i], NULL, 16);
            canparam->filter.can_mask = (canid_t)mask;
            continue;
        }
        if (!strcmp(param[i], "-e"))
        {
            canparam->extend = 1;
            continue;
        }
        if (!strcmp(param[i], "-fd"))
        {
            canparam->canfd_mode = CAN_FD_MODE;

            i++;
            baudrate = atoi(param[i]);
            switch (baudrate)
            {
            case 250:
            case 500:
            case 1000:
            case 2000:
            case 4000:
            case 5000:
                canparam->data_baudrate = baudrate * 1000;
                break;
            }
            continue;
        }
        if (!strcmp(param[i], "-t"))
        {
            active_send_mode = 1;

            i++;
            len = strlen(param[i]);
            if (len > 0)
            {
                active_send_num = len;
                memcpy(active_send_buff, param[i], len); //要发送的字符串

                i++;
                len = atoi(param[i]); //发送间隔
                if (len > 0)
                {
                    active_send_time = len * 1000; //转换为ms单位
                    active_send_time_count = active_send_time;
                }
            }
            continue;
        }
        if (!strcmp(param[i], "-l"))
        {
            canparam->loopback_mode = 1;
            continue;
        }
        if (!strcmp(param[i], "-L"))
        {
            loopback_send_mode = 1;
            continue;
        }
        if (!strcmp(param[i], "-v"))
        {
            printf("can_demo ver:  %sn", ver);
            continue;
        }
    }
}

/*
 * @description  : 主函数
 * @param - argc : 参数个数
 * @param - *argv: 带入参数数组指针
 * @return		 : 执行结果
 */
int main(int argc, char *argv[])
{
    int i = 0, result = 0;

    //检测是否有参数
    if (argc < 2 || strncmp(argv[1], "can", 3))
    {
        print_usage(argv[0]);
        exit(1);
    }

    //检测是否有--h或--help
    if ((!strcmp(argv[1], "--h")) || (!strcmp(argv[1], "--help")))
    {
        print_usage(argv[0]);
        exit(1);
    }

    strcpy(dev, argv[1]);

    //从main函数带来的参数解析为CAN口参数
    get_param(argc, argv, &can_param);

    //当知道设备名称时可以直接赋值dev,例strcpy(dev, "can0");
    //打开CAN口 创建socket 绑定socket
    sock_fd = func_open_can(dev, can_param);
    if (sock_fd < 0)
    {
        printf("Can't Open deveice %s n", dev);
        exit(0);
    }
    else
    {
        printf("baudrate = %ldK,can_id = 0x%x,can_filter_id = 0x%x,can_filter_mask = 0x%x,extend id = %d,loopback mode = %d,canfd mode = %d,dbaudrate = %ldKn",
               can_param.baudrate / 1000, can_param.id, can_param.filter.can_id, can_param.filter.can_mask,
               can_param.extend, can_param.loopback_mode, can_param.canfd_mode, can_param.data_baudrate / 1000);
        //设置CAN口过滤
        result = func_set_can(sock_fd, can_param);
        if (result < 0)
        {
            perror("set_opt error");
            exit(0);
        }
        //设置CAN口为非阻塞方式
        fcntl(sock_fd, F_SETFL, O_NONBLOCK); //非阻塞
    }

    while (1)
    {
        //接收数据
        receive_num = func_receive_can_buff(sock_fd, receive_buff, sizeof(receive_buff));
        if (receive_num > 0)
        {
            printf("[%s nread=%d]  ", dev, receive_num);
            func_my_print(receive_buff, receive_num, 'c'); //将收到的数据打印出来
        }

        //组织发送数据
        if ((1 == loopback_send_mode) && (receive_num > 0)) //数据回环处理
        {
            send_num = receive_num;
            memcpy(send_buff, receive_buff, receive_num);
        }
        else if (1 == active_send_mode)
        {
            if (active_send_time_count >= active_send_time)
            {
                active_send_time_count = 0;
                send_num = active_send_num;
                memcpy(send_buff, active_send_buff, active_send_num);
            }
            else
            {
                active_send_time_count++;
            }
        }

        //发送数据
        if (send_num > 0)
        {
            real_send_num = func_send_can_buff(sock_fd, send_buff, send_num, can_param);
            if (real_send_num > 0)
            {
                printf("[%s nwrite=%d] ", dev, real_send_num);
                func_my_print(send_buff, real_send_num, 'c');
            }
            memset(send_buff, 0, send_num);
            send_num = 0;
        }
        usleep(1000); // 1ms
    }
    close(sock_fd);
    exit(0);
}

进程名:elf1_cmd_can

使用方法:./elf1_cmd_can 设备名 [参数选项]… …

相关推荐

电子产业图谱