[imx6ull]Linux下的SocketCAN通信

2023-05-16

文章目录

  • 一、CAN总线协议
    • 1.简介
    • 2.电气属性
    • 3.通信原理
      • ①数据帧的帧格式:
      • ②总线同步
      • ③总线竞争
      • ④数据保护
  • 二、Linux下CAN的操作
    • 1.硬件连接
      • ①CAN电平转换器
      • ②扩展板使用CAN
    • 2.查询 can 信息
    • 3.开启/关闭 can
    • 4.发送/接收 can 数据
    • 5.设置 can 参数
  • 三、CAN的回环测试
  • 四、CAN的应用编程
    • 1.程序代码
    • 2.makefile
    • 3.程序测试


一、CAN总线协议

1.简介

控制器局域网总线(CAN,Controller Area Network)是一种用于实时应用的串行通讯协议总线,它可以使用双绞线来传输信号,是世界上应用最广泛的现场总线之一,是 ISO 国际标准化的串行通信协议。

CAN协议用于汽车中各种不同元件之间的通信,以此取代昂贵而笨重的配电线束。该协议的健壮性使其用途延伸到其他自动化和工业应用。CAN协议的特性包括完整性的串行数据通讯、提供实时支持、传输速率高达1Mb/s、同时具有11位的寻址以及检错能力。

CAN是一种多主方式的串行通讯总线,各个单元通过CAN总线连接在一起,每个单元都是独立的CAN节点,同一CAN网络中所有单元的通信速度必须一致,不同网络之间通信速度可以不同。

CAN总线协议参考了OSI七层模型,但是实际上CAN协议只定义了两层物理层和数据链路层:

序号层次描述
7应用层主要定义CAN应用层
2数据链路层数据链路层分为逻辑链接控制子层 LLC 和介质访问控制子层MAC,MAC 子层是 CAN 协议的核心,它把接收到的报文提供给 LLC 子层,并接收来自 LLC 子层的报文 MAC 子层负责报文分帧,仲裁,应答,错误检测和标定,MAC 子层也被称作故障界定的管理实体监管LLC 子层涉及报文滤波,过载通知,以及恢复管理 LLC = Logical Link Control MAC = Medium Access Control
1物理层物理层,为物理编码子层PCS 该层定义信号是如何实际地传输的,因此涉及到位时间,位编码,同步

CAN总线还具有多主控制、系统的柔软性、通信速度快、具有错误检测、故障封闭功能、连接节点多等众多优点。

2.电气属性

在这里插入图片描述
1)两根差分线,CAN_H 和 CAN_L
2)两个电平,显性电平和隐性电平
显性电平:逻辑 0,CAN_H 比 CAN_L 高,分别是 3.5v 和 1.5v,电位差为 2v
隐性电平:逻辑 1,CAN_H 和 CAN_L 都是 2.5v,电位差为 0v
在这里插入图片描述

3)途中所有的节点单元都采用 CAN_H 和 CAN_L 这两根线连接在一起, CAN_H 接 CAN_H、 CAN_L 接 CAN_L, CAN 总线两端要各接一个 120Ω 的端接电阻,用于匹配总线阻抗,吸收信号反射及回拨,提高数据通信的抗干扰能力以及可靠性
4)can 控制器,控制器用于将欲收发的消息,转换为符合 can 规范的 can 帧,通过 can 收发器,在 can 总线上上交换信息
5)can 收发器是 can 控制器和物理总线之间的接口,将 can 控制器的逻辑电平转换为 can 总线的差分电平,在两条有差分电压的总线电缆上传输数据

3.通信原理

①数据帧的帧格式:

在这里插入图片描述

  • sof :1bit,发出一个显性位边沿,网络节点以此开始同步
  • id :11bit,定义消息的优先级/总线竞争力,数字越低优先级越高
  • rtr :1bit,显性表示数据帧,隐性表示远程帧
  • ide :1bit,扩展帧标识符,扩展帧的 id 可以是 29 位,如下所示(扩展帧和标准帧格式不同,不能存在于同一 can 网络
  • r :1bit,保留位
  • dlc :4bit,表示数据场的长度,最大表示 8,因此 data field 最多 9 字节
  • data field :64 bit
  • crc field :16 bit,含 1 bit 隐性位的 crc 界定符,crc 是从 sof 开始计算的
  • ack field :2 bit,由 ack 和 del(ack 界定符) 组成,由接收方进行确认,收到消息给出一个显性位,如果一个节点都没有确认收到消息,发送方监听此位为隐性位就会报错
  • eof :7bit,结束标识符,7bit 隐性位即结束
  • itm :3bit,帧间隔,实际不属于帧内区域,必须等待帧间隔才能发送消息

②总线同步

  • 首次同步由 sof 发起
  • 同步的原因,因为没有单独的时钟线,编码形式是 NRZ,不带时钟同步
  • 重同步,位填充机制(不允许发 6 个相同的极性,如果有则会插入一个相反的极性,因此用示波器查看时,会发现波形不对劲,需要人为修改,但用有 can 功能的示波器则可以获取正确序列),利用隐性位到显性位的边沿进行同步

③总线竞争

解决多个节点同一时间发送消息的问题,通过 id 来竞争每个节点在发送时,都会对总线电平进行监控

  • send 0 总线上出现 1,则报错
  • send 0 总线上出现 0,则继续竞争
  • send 1 总线上出现 1,则继续竞争
  • send 1 总线上出现 0,竞争失败,转为接收方

在这里插入图片描述

④数据保护

二、Linux下CAN的操作

1.硬件连接

①CAN电平转换器

在这里插入图片描述
转换器也需要5V供电:

在这里插入图片描述

②扩展板使用CAN

在这里插入图片描述

2.查询 can 信息

查询 can 的详细信息,包括波特率,标志设置等信息:

root@igkboard:~# ip -details link show can0

查询 can 的工作状态:

root@igkboard:~# ip -details -statistics link show can0

查询 can 的收发数据包情况,以及中断号:

root@igkboard:~# ifconfig -a

查询 can 的详细物理信息,包括电压,寄存器,中断等(需要启动can后才可以):

root@igkboard:~# dmesg | grep “can”

在这里插入图片描述

3.开启/关闭 can

关闭 can:

root@igkboard:~# ifconfig can0 down

打开 can:

root@igkboard:~# ifconfig can0 up

打开 can 网络:

root@igkboard:~# ip link set can0 up type can

关闭 can 网络:

root@igkboard:~# ip link set can0 down

在这里插入图片描述

4.发送/接收 can 数据

发送默认 id 为 0x1 的 can 标准帧,数据为 0x11 22 33 44 55 66 77 88 每次最大 8 个 byte:

root@igkboard:~# cansend can0 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88

-e 表示扩展帧,can_id 最大 29bit,标准帧 CAN_ID 最大 11bit,-I 表示 can_id:

root@igkboard:~# cansend can0 -i 0x800 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 -e

–loop 表示发送 20 个包:

root@igkboard:~# cansend can0 -i 0x02 0x11 0x12 --loop=20

接收数据:

root@igkboard:~# candump can0

发送数据,123 是发送到的 can 设备 id 号,后面接发送内容 :

root@igkboard:~# cansend can0 123#1122334455667788

5.设置 can 参数

can 参数设置详解:

root@igkboard:~# ip link set can0 type can --help

设置 can0 的波特率为 800kbps,can 网络波特率最大值为 1mbps:

root@igkboard:~# ip link set can0 up type can bitrate 800000

设置回环模式,自发自收,用于测试是硬件是否正常,loopback 不一定支持:

root@igkboard:~# ip link set can0 up type can bitrate 800000 loopback on

在这里插入图片描述

三、CAN的回环测试

首先通过ifconfig检查两路can:

can0: flags=193<UP,RUNNING,NOARP>  mtu 16
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 35

can1: flags=193<UP,RUNNING,NOARP>  mtu 16
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 10  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 36

使用 ip 命令对两路 can 进行参数配置,配置先必须先禁用该设备:

root@igkboard:~/40_pin_test# ip link set can0 down
root@igkboard:~/40_pin_test# ip link set can0 type can bitrate 500000
root@igkboard:~/40_pin_test# ip link set can0 up

root@igkboard:~/40_pin_test# ip link set can1 down
root@igkboard:~/40_pin_test# ip link set can1 type can bitrate 500000
root@igkboard:~/40_pin_test# ip link set can1 up

使用 candump 命令进行 can0 的数据接收,使用 cansend 命令进行 can1 的数据发送:

root@igkboard:~# candump can0
root@igkboard:~# cansend can1 123#01020304050607

测试效果如下,can0 接收到 can1 发送的数据:

root@igkboard:~/40_pin_test# candump can0
  can0  123   [7]  01 02 03 04 05 06 07

四、CAN的应用编程

1.程序代码

sht20.h

#ifndef SHT20_H
#define SHT20_H

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <sys/stat.h>
#include <linux/i2c-dev.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#define SOFTRESET                       0xFE
#define TRIGGER_TEMPERATURE_NO_HOLD     0xF3
#define TRIGGER_HUMIDITY_NO_HOLD        0xF5

#define SHT20_PATH                      "/dev/i2c-0"

static inline void msleep(unsigned long ms);
int sht2x_init(void);
int sht2x_get_temp_humidity(int fd, unsigned char *buf_temp, unsigned char *buf_rh, float *temp, float *rh);

#endif

sht20.c

/* i2c 的应用编程相关内容 */
#include "sht20.h"

static inline void msleep(unsigned long ms)
{
    struct timespec cSleep;
    unsigned long ulTmp;

    cSleep.tv_sec = ms / 1000;
    if (cSleep.tv_sec == 0)
    {
        ulTmp = ms * 10000;
        cSleep.tv_nsec = ulTmp * 100;
    }
    else
    {
       cSleep.tv_nsec = 0;
    }

    nanosleep(&cSleep, 0);
}

/*sht20命令复位*/
int sht2x_softreset(int fd)
{
    uint8_t           buf[4];

    if(fd < 0)
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    memset(buf, 0, sizeof(buf));

    buf[0] = SOFTRESET;
    write(fd, buf, 1);

    msleep(50);

    return 0;
}

/*初始化sht20*/
int sht2x_init(void)
{
    int          fd;

    if((fd=open(SHT20_PATH, O_RDWR)) < 0)
    {
        printf("fail to open sht20\n");
        return -1;
    }

    ioctl(fd, I2C_TENBIT, 0);    
    ioctl(fd, I2C_SLAVE, 0x40); 

    if( sht2x_softreset(fd) < 0 )
    {
        printf("fail to softreset sht20\n");
        return -2;
    }

    return fd;
}

/*sht20获取温度与湿度*/
int sht2x_get_temp_humidity(int fd, unsigned char *buf_temp, unsigned char *buf_rh, float *temp, float *rh)
{
    uint8_t           buf[4];

    if(fd<0 || !temp || !rh)
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    memset(buf, 0, sizeof(buf));
    buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
    write(fd, buf, 1);

    msleep(85); 

    memset(buf, 0, sizeof(buf));
    read(fd, buf, 3);
    *buf_temp = *buf;
    *(buf_temp+1) = *(buf+1);
    *(buf_temp+2) = *(buf+2);
    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;

    memset(buf, 0, sizeof(buf));
    buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
    write(fd, buf, 1);

    msleep(29); 
    memset(buf, 0, sizeof(buf));

    read(fd, buf, 3);
    *buf_rh = *buf;
    *(buf_rh+1) = *(buf+1);
    *(buf_rh+2) = *(buf+2);
    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;

    return 0;
}

led.h

#ifndef LED_H
#define LED_H

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/types.h>
#include <string.h>
#include <gpiod.h>

#define LED_ON          1
#define LED_OFF         0
#define LED_OFFSET	    10

struct gpiod_chip *chip;
struct gpiod_line *line_led;

int led_init(unsigned char gpio_chip, unsigned char gpio_offset);
int led_control(unsigned char cmd);
int led_release(void);

#endif

led.c

/*用gpiod控制led亮灭*/
#include "led.h"

int led_init(unsigned char gpio_chip, unsigned char gpio_offset)
{
    int             ret;
    unsigned char   buf[16];

    if(gpio_chip<0 || gpio_chip>5 || gpio_offset>32)
    {
        printf("%s line [%d] %s() get invalid input arguments\n", __FILE__, __LINE__, __func__ );
        return -1;
    }

    memset(buf, 0x0, sizeof(buf));
    snprintf(buf, sizeof(buf), "/dev/gpiochip%d", gpio_chip-1);

    chip = gpiod_chip_open(buf);
    if(!chip)
    {
        printf("fail to open chip0\n");
        return -1;
    }

    line_led = gpiod_chip_get_line(chip, LED_OFFSET);
    if(!line_led)
    {
        printf("fail to get line_led\n");
        return -2;
    }

	ret = gpiod_line_request_output(line_led, "led_out", 1);
    if(ret < 0)
    {
        printf("fail to request line_led for output mode\n");
        return -3;
    }

    return ret;
}

int led_control(unsigned char cmd)
{
    int ret; 

    if(cmd == LED_OFF)
    {
        ret = gpiod_line_set_value(line_led, 0);
    }
    else if(cmd == LED_ON)
    {
        ret = gpiod_line_set_value(line_led, 1);
    }

    if(ret < 0)
    {
        printf("fail to set line_led value\n");
        return -1;
    }

    return ret;
}

int led_release(void)
{
    gpiod_line_release(line_led);
    gpiod_chip_close(chip);
}

can.h

#ifndef CAN_H
#define CAN_H

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <pthread.h>


#define	CMD_SET_CAN0_BITRATE     "ip link set can0 type can bitrate 500000 triple-sampling on"
#define CMD_CAN0_UP              "ifconfig can0 up"
#define CMD_CAN0_DOWN            "ifconfig can0 down"

#define CMD_SET_CAN1_BITRATE     "ip link set can1 type can bitrate 500000 triple-sampling on"
#define CMD_CAN1_UP              "ifconfig can1 up"
#define CMD_CAN1_DOWN            "ifconfig can1 down"

struct can_controller
{
	char	*can_name;
	int		can_fd;
};

int can_set_controller(char *can_name);
int can_write(int can_fd, struct can_frame tx_frame);
int can_read(int can_fd, struct can_frame *rx_frame);

#endif

can.c

#include "can.h"

/*设置can控制器的函数*/
int can_set_controller(char *can_name)
{
	struct ifreq			ifr;//网络接口信息结构体,可以用ioctrl来指定CAN设备只要是指定ifreq结构体中的ifr_name成员
	struct sockaddr_can		can_addr;//用于套接字和CAN设备绑定
	int						sock_fd = -1;//socket函数创建can套接字文件,返回的文件描述符

	/*确定使能can0还是can1 
	 *使用system函数执行我们宏定义好的shell命令
	 *可以设置波特率、使能和禁用can
	 * */
	if(strcmp(can_name, "can0")==0)
	{
		system(CMD_CAN0_DOWN);
		system(CMD_SET_CAN0_BITRATE);
		system(CMD_CAN1_UP);
	}

	/*创建套接字文件*/
	sock_fd = socket(AF_CAN, SOCK_RAW, CAN_RAW);
	if(sock_fd < 0)
	{
		printf("fail to create socket for %s\n", can_name);
		return -1;
	}

	/*指定设备为can_name的can设备*/
	strcpy(ifr.ifr_name, can_name);

	/*获取网络接口*/
	ioctl(sock_fd, SIOCGIFINDEX, &ifr);

	/*向can_addr中填充数据*/
	can_addr.can_family = AF_CAN;
	can_addr.can_ifindex = ifr.ifr_ifindex;

	/*将套接字与can0进行绑定*/
	if(bind(sock_fd, (struct sockaddr *)&can_addr, sizeof(can_addr)) < 0)
	{
		printf("fail to bind %s to socket\n", can_name);
		return -1;
	}

	return sock_fd;
}

/*通过can发送数据的函数*/
int can_write(int can_fd, struct can_frame tx_frame)
{
	/*调用write函数,向can_fd写入数据*/
	int ret = write(can_fd, &tx_frame, sizeof(struct can_frame));

	/*打印信息,用于查看发出的数据*/
	if(ret > 0)
	{
		printf("[send data]\n");
		printf("id=%03x dlc=%d", tx_frame.can_id, tx_frame.can_dlc);
		printf("data=");
		for(int i=0; i<tx_frame.can_dlc; i++)
		{
			printf("0x%02x", tx_frame.data[i]);
		}
		printf("\n");

		return 0;
	}
	else
	{
		return ret;
	}
}

/*通过can接收数据的函数*/
int can_read(int can_fd, struct can_frame *rx_frame)
{
	int ret = read(can_fd, rx_frame, sizeof(struct can_frame));

	if(ret > 0)
	{
		printf("[recieve data]\n");
		if(rx_frame->can_id & CAN_ERR_FLAG)
		{
			printf("error frame\n");
		}

		if(rx_frame->can_id & CAN_RTR_FLAG)
		{
			printf("remote request\n");
		}

		if(rx_frame->can_id & CAN_EFF_FLAG)
		{
			printf("extened frame id=0x%08x ", rx_frame->can_id & CAN_EFF_MASK);
		}
		else
        {
            printf("standard frame id=0x%03x ", rx_frame->can_id & CAN_SFF_MASK);
        }

		/*打印信息,查看接收的数据*/
		printf("dlc=%d", rx_frame->can_dlc);
		printf("data=");
		for(int i=0; i<rx_frame->can_dlc; i++)
		{
			printf("0x%02x", rx_frame->data[i]);
		}
		printf("\n");

		return 0;
	}
	else
	{
		return ret;
	}
}

main.c

/*********************************************************************************
 *      Copyright:  (C) 2023 Deng Yonghao<dengyonghao2001@163.com>
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file main
 *                 
 *        Version:  1.0.0(2023年04月02日)
 *         Author:  Deng Yonghao <dengyonghao2001@163.com>
 *      ChangeLog:  1, Release initial version on "2023年04月02日 17时27分04秒"
 *                 
 ********************************************************************************/
#include "sht20.h"
#include "can.h"
#include "led.h"

/*此结构体用于之后接收数据时为创建的线程传入参数*/
struct thread_para
{
	int can_fd;
	struct can_frame rx_frame;
	char* device_name;
};

void print_usage(char *arg)
{
	printf("This is a can test program\n");
	printf("usage: %s can0/can1 send/recieve sht20/led\n", arg);
}

int parse_sht20_data(struct can_frame rx_frame)
{
	float				temp;
	float				rh;

	temp = 175.72 * (((((int) rx_frame.data[0]) << 8) + rx_frame.data[1]) / 65536.0) - 46.85;
	rh = 125 * (((((int) rx_frame.data[3]) << 8) + rx_frame.data[4]) / 65536.0) - 6;
	printf("temperature=%lf relative humidity=%lf\n", temp, rh);
}

int parse_led_data(struct can_frame rx_frame)
{
	if(rx_frame.data[0] == 0x0)
	{
		led_control(0);
	}
	else if(rx_frame.data[0] == 0x1)
	{
		led_control(1);
	}
}

void *recieve_thread(void *arg)
{
	int					can_fd;
	struct				can_frame rx_frame;
	char				*device_name;

	struct thread_para	*para = (struct thread_para *)arg;

	can_fd = para->can_fd;
	rx_frame = para->rx_frame;
	device_name = para->device_name;

	/*判断参数判断执行sht20和led对应的解析函数*/
	for( ; ; )
	{
		if(can_read(can_fd, &rx_frame) == 0)
        {
            if(strcmp(device_name, "sht20") == 0)
            {
                if(rx_frame.can_dlc < 6)
                {
                    printf("the length of data is incorrect\n\n");
                    continue;
                }
                parse_sht20_data(rx_frame);
            }
            else if(strcmp(device_name, "led") == 0)
            {
                parse_led_data(rx_frame);
            }
        }

		printf("\n");
	}
}


int main (int argc, char **argv)
{
	struct can_controller   can_ctrl;		// can 控制器结构体
    struct can_frame 		frame;		   // can 帧数据,用于数据传输
    struct can_filter       rfilter[1];	    // can 过滤器
    int                     ret;

    int         			sht20_fd;
    float       			temp;
    float       			rh;
	unsigned char 			buf_temp[3];
    unsigned char 			buf_rh[3];

    /* 创建线程所需要的参数 */
    pthread_t               ntid;
    struct thread_para      para;
    int                     *thread_ret = NULL;

	if( argc<3 || (strcmp(argv[1], "can0") !=0 && strcmp(argv[1], "can1") !=0))
	{
		print_usage(argv[0]);
		return 0;
	}

	/* 通过传入的参数,来使能 can 设备,并设置 can 控制器*/
    can_ctrl.can_name = argv[1];
    can_ctrl.can_fd = can_set_controller(can_ctrl.can_name);
    if(can_ctrl.can_fd < 0)
    {
        printf("fail to set can controller");
        return -1;
    }

	/*应用程序用作接收数据*/
	if(0 == strcmp(argv[2], "recieve"))
	{
		/*设置过滤器,用于过滤掉除id为0x123 can从设备发出的数据*/
		rfilter[0].can_id = 0x123;
		rfilter[0].can_mask = 0x7ff;
		setsockopt(can_ctrl.can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));

		/* 设置创建的新线程所需的参数 */
        para.can_fd = can_ctrl.can_fd;
        para.rx_frame = frame;
        para.device_name = argv[3];

        /* 创建新线程,并开始接受数据 */
        if(0 == strcmp(argv[3], "sht20"))
        {
            ret = pthread_create(&ntid, NULL, recieve_thread, (void*)(&para));
            if(ret != 0)
            {
                printf("fail to create sht20 recieve thread\n");
            }

            pthread_join(ntid, (void**)&thread_ret);
        }
		else if(0 == strcmp(argv[3], "led"))
		{
			/*测试用gpio1_10进行测试*/
			ret = led_init(1, 10);
            if(ret < 0)
            {
                printf("fail to excute led_init\n");
            }

            ret = pthread_create(&ntid, NULL, recieve_thread, (void*)(&para));
            if(ret != 0)
            {
                printf("fail to create led recieve thread\n");
            }

            pthread_join(ntid, (void**)&thread_ret);
		}
		else
		{
			print_usage(argv[0]);
			return 0;
		}
	}
	/*应用程序用作发送数据*/
	else if(0 == strcmp(argv[2], "send"))
	{
		/*设置socket为只接受模式*/
		setsockopt(can_ctrl.can_fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);

		/*发送sht20数据:初始化sht20、获取数据、使用can_write发送数据*/
		if(0 == strcmp(argv[3], "sht20"))
		{
			sht20_fd = sht2x_init();
			if(sht20_fd < 0)
			{
				printf("fail to initializa sht20");
				return -1;
			}

			frame.can_dlc = 6;
			frame.can_id = 0x123;

			for( ; ; )
			{
				if(sht2x_get_temp_humidity(sht20_fd, buf_temp, buf_rh, &temp, &rh) < 0)
				{
                    printf("fail to get data from sht2x\n");
                    return -1;
                }
                frame.data[0] = buf_temp[0];
                frame.data[1] = buf_temp[1];
                frame.data[2] = buf_temp[2];
                frame.data[3] = buf_rh[0];
                frame.data[4] = buf_rh[1];
                frame.data[5] = buf_rh[2];

                can_write(can_ctrl.can_fd, frame);
                printf("\n");

                sleep(1);
			}
		}
		else if(0 == strcmp(argv[3], "led"))
		{
			frame.can_dlc = 1;
			frame.can_id = 0x123;

			while(1)
			{
				frame.data[0] = 0x1;
				can_write(can_ctrl.can_fd, frame);
				sleep(1);

				frame.data[0] = 0x0;
				can_write(can_ctrl.can_fd, frame);
				sleep(1);

				printf("\n");
			}
		}
		else
		{
			print_usage(argv[0]);
			return 0;
		}
	}
	else
	{
		print_usage(argv[0]);
		return 0;
	}
}

2.makefile

CC = arm-linux-gnueabihf-gcc
APP_NAME = socketCAN

all:clean
    @${CC} *.c -o ${APP_NAME} -lpthread -lgpiod

install:
    @cp ${APP_NAME} ../tftpboot/

clean:
    @rm -f ${APP_NAME}

3.程序测试

首先编译好我们的可执行程序:
在这里插入图片描述

然后通过tftp命令下载到开发板上,并赋予权限:

在这里插入图片描述

使用 can0 发送 sht20 数据

root@igkboard:~# ./can_test can0 send sht20       
[send data]
id=111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 

[send data]
id=111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 

[send data]
id=111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 

[send data]
id=111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a

在另一端看到的接受数据

root@igkboard:~# ./can_test can0 recieve sht20
[recieve data]
standard frame id=0x111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 
temperature=26.734896 relative humidity=62.302155

[recieve data]
standard frame id=0x111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 
temperature=26.734896 relative humidity=62.302155

[recieve data]
standard frame id=0x111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 
temperature=26.734896 relative humidity=62.302155

[recieve data]
standard frame id=0x111 dlc=6 data=0x6b 0x34 0x4e 0x8b 0xe2 0x9a 
temperature=26.734896 relative humidity=62.302155

同理,使用 can0 控制 led 灯的亮灭

root@igkboard:~# ./can_test can0 send led       
[send data]
id=222 dlc=1 data=0x01 

[send data]
id=222 dlc=1 data=0x00 

[send data]
id=222 dlc=1 data=0x01 

[send data]
id=222 dlc=1 data=0x00 

在另一端可以看到 led 灯闪烁,并且终端可以接受到如下信息

root@igkboard:~# ./can_test can0 recieve led  
[recieve data]
standard frame id=0x222 dlc=1 data=0x01 

[recieve data]
standard frame id=0x222 dlc=1 data=0x00 

[recieve data]
standard frame id=0x222 dlc=1 data=0x01 

[recieve data[]
standard frame id=0x222 dlc=1 data=0x00 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[imx6ull]Linux下的SocketCAN通信 的相关文章

  • 编写多个mysql脚本

    是否可以在复合脚本中包含其他 mysql 脚本 理想情况下 我不想为包含的脚本创建存储过程 对于较大的项目 我想分层维护几个较小的脚本 然后根据需要组合它们 但现在 我很乐意学习如何包含其他脚本 source是一个内置命令 您可以在 MyS
  • 使用命令行将 MediaWiki 维基文本格式转换为 HTML

    我倾向于编写大量文档 因此 MediaWiki 格式对我来说很容易理解 而且比编写传统 HTML 节省了我很多时间 然而 我也写了一篇博客 发现一直从键盘切换到鼠标来输入正确的 HTML 标签会增加很多时间 我希望能够使用 Mediawik
  • Linux 中的电源管理通知

    在基于 Linux 的系统中 我们可以使用哪些方法 最简单的方法 来获取电源状态更改的通知 例如 当计算机进入睡眠 休眠状态等时 我需要这个主要是为了在睡眠前保留某些状态 当然 在计算机唤醒后恢复该状态 您只需配置即可获得所有这些事件acp
  • Linux命令列出所有可用命令和别名

    是否有一个 Linux 命令可以列出该终端会话的所有可用命令和别名 就好像您输入 a 并按下 Tab 键一样 但针对的是字母表中的每个字母 或者运行 别名 但也返回命令 为什么 我想运行以下命令并查看命令是否可用 ListAllComman
  • 如何从类似于 eclipse 的命令行创建可运行的 jar 文件

    我知道 eclipse 会生成一个可运行的 jar 文件 其中提取并包含在该 jar 文件中的所有库 jar 文件 从命令提示符手动创建 jar 文件时如何执行类似的操作 我需要将所有 lib jar 解压到类文件夹中吗 目前我正在使用 j
  • CentOS目录结构是树形的吗?

    CentOS 上有相当于树的东西吗 如果你的 Centos 系统上没有安装 tree 无论如何我通常建议服务器设置使用最小安装磁盘 你应该在命令行中输入以下内容 yum install tree y 如果没有安装 那是因为您没有正确的存储库
  • 如何从 Linux 命令行获取视频文件的分辨率(宽度和高度)?

    我一直在挖掘 mplayer mencoder 和 ffmpeg 文档 但我似乎无法想出anything 我对输出格式不是特别挑剔 因为我可以使用正则表达式将其拉出来 我只是似乎无法首先获取数据 Use ffprobe https ffmp
  • touch命令在一个目录下创建多个文件(不同名称)

    我想制作一个在 bash 中创建目录和文件结构的脚本 我尝试过这样的事情 mkdir p 1 2 touch 1 2 a b c a b c 应该是在一个命令或其他命令中创建的文件 但由于某种原因 结构是这样的 current folder
  • 如何成功使用RDAP协议代替whois

    我对新的 RDAP 协议有点困惑 也不知道何时进一步追求它有意义 在我看来 每个人都同意它是 whois 的继承者 但他们的数据库似乎是空的 在 ubuntu 上我尝试了 rdapper nicinfo 甚至他们的 RESTful API
  • 操作系统什么时候清除进程的内存

    进程在某些操作系统上成功或异常终止 操作系统何时决定擦除分配给该进程的内存 数据 代码等 在退出时或当它想为新进程分配内存时 这个清除内存分配过程在所有操作系统 winXP Win7 linux Mac 上都相同吗 据我了解 页表具有该进程
  • numpy 未定义符号:PyFPE_jbuf

    我正在尝试使用一百万首歌曲数据集 为此我必须安装 python 表 numpy cython hdf5 numexpr 等 昨天我设法安装了我需要的所有内容 在使用 hdf5 遇到一些麻烦之后 我下载了预编译的二进制包并将它们保存在我的 b
  • 如何获取 linux 实用程序 tail 的源代码?

    这个命令确实非常有用 但是我可以在哪里获取源代码以查看内部发生的情况 thanks tail 实用程序是 Linux 上 coreutils 的一部分 源压缩包 ftp ftp gnu org gnu coreutils coreutils
  • 伊迪德信息

    重新定义问题 有什么方法可以获取所连接显示器的序列号吗 我想收集显示器的Eid信息 当我使用 logverbose 选项运行 X 时 我可以从 xorg 0 log 文件中获取它 但问题是 如果我切换显示器 拔出当前显示器 然后插入另一个显
  • 在 LINUX 上使用 Python 连接到 OLAP 多维数据集

    我知道如何在 Windows 上使用 Python 连接到 MS OLAP 多维数据集 嗯 至少有一种方法 通常我使用 win32py 包并调用 COM 对象进行连接 import win32com client connection wi
  • Linux 中热插拔设备时检测设备是否存在

    我正在运行 SPIcode http lxr free electrons com source drivers spi spi omap2 mcspi c在熊猫板上 我想知道其中的哪个功能code http lxr free electr
  • 如何在Python中独立于语言安装(linux)获取用户桌面路径

    我找到了 如何找到用户桌面的路径 的几个问题和答案 但在我看来它们都已失效 至少我找到的那些 原因是 如果用户安装的 Linux 不是英语 他或她的桌面很可能位于除 Desktop 例如 对于瑞典语 我相信它是在 Skrivbord 谁知道
  • 在Linux中断上下文中运行用户线程

    我正在编写一些定制的应用程序 并允许更改 Linux 内核中的中断处理程序代码 我有一个用户线程正在等待中断发生 如果发生中断 那么我要做的第一件事就是执行该用户线程 有什么办法让它发挥作用吗 Thanks 创建一个字符设备 这就是内核所做
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst
  • 为什么 Linux 对目录使用 getdents() 而不是 read()?

    我浏览 K R C 时注意到 为了读取目录中的条目 他们使用了 while read dp gt fd char dirbuf sizeof dirbuf sizeof dirbuf code Where dirbuf是系统特定的目录结构
  • 如何查找连接到 AF_INET 套接字的客户端的 UID?

    有什么方法或类似的东西ucred for AF UNIX如果是AF INET插座 TCP在我的例子中 找出连接到我的套接字的客户端的UID 还有 proc net tcp但它显示了UID of the creator插座的而不是连接的cli

随机推荐