[imx6ull]Linux下的SocketCAN通信

文章目录

  • 一、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.简介

控制器局域网络(Controller Area Network,简称CAN)是一种为实时应用精心设计的串行通信协议总线。它基于双绞线传输机制,已在全球范围内得到广泛认可,并在众多场合中得到应用,成为现场总线通信技术的国际标准化方案。

CAN协议最初为汽车工业所设计,目的是在车辆内部实现不同电子控制单元(ECUs)之间的高效通信,它有效地取代了成本高昂、体积庞大的传统布线系统。得益于其卓越的健壮性和可靠性,CAN协议已经成功地扩展到自动化和工业控制等多个领域。该协议的核心特点包括完备的串行数据通信功能、实时性支持、最高1Mbps的数据传输速率、11位的地址空间,以及先进的错误检测机制。

CAN作为一种多主体串行通信总线,支持多个单元通过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,竞争失败,转为接收方

在这里插入图片描述

④数据保护

CAN总线(Controller Area Network)在设计时特别强调了数据的安全性和可靠性,采取了多种机制来保护传输数据的完整性和准确性。以下是一些关键的数据保护特性:

错误检测:CAN总线集成了循环冗余校验(CRC),这使得CAN节点能够检测数据包中的任何错误。CRC校验确保了数据包的完整性,任何不符合CRC校验的数据包将被丢弃,从而避免了错误数据的传播。

优先级编码:CAN总线的每个数据帧都有一个优先级字段,允许高优先级消息优先传输,确保了关键任务数据的及时传递。

仲裁机制:在多个节点同时发送数据时,通过仲裁机制确保了数据传输的有序性,防止了数据冲突。

确认机制:发送节点发送数据后,接收节点将发送确认,确保数据被正确接收。

重发机制:如果数据帧未被成功接收,发送节点将自动重发数据,增强了数据传输的可靠性。

位填充:CAN协议使用位填充技术,通过在数据帧中插入额外的位以确保数据帧的同步。

总线监控:CAN总线节点可以处于监听模式,实时监控总线状态,及时发现并响应总线错误。

错误计数器:CAN控制器包含错误计数器,用于跟踪总线错误,如果错误超过阈值,节点将进入错误被动模式或总线关闭,防止错误扩散。

物理层保护:CAN总线的物理层设计包括了对电磁干扰的防护,确保了数据在恶劣电磁环境下的稳定传输。

数据加密:虽然不是CAN协议本身的一部分,但可以通过上层应用层实现数据加密,增加数据传输的安全性。

通过这些机制,CAN总线确保了在汽车、工业自动化和其他实时系统中数据传输的高可靠性和安全性。

二、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 16unspec 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 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0device interrupt 35can1: flags=193<UP,RUNNING,NOARP>  mtu 16unspec 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 0TX packets 0  bytes 0 (0.0 B)TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0device 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 uproot@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 can0can0  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	    10struct 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 = socketCANall:clean@${CC} *.c -o ${APP_NAME} -lpthread -lgpiodinstall:@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 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/26476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

window系统下为django自动绘制模型类关系图

Django 提供第三方包 django-extensions&#xff0c;可以用来将 Django 中的 Models 生成 E-R 图。 1 安装包 pip install django-extensions 2 配置 在 Django settings.py 文件&#xff0c; INSTALLED_APPS 中添加 django_extensions INSTALLED_APPS (django_extension…

【知识整理】软件版本号的定义及规范

版本号简述 在软件开发项目中&#xff0c;版本号是一个非常重要的概念&#xff0c;它能够告诉用户软件的功能、质量和安全性等信息&#xff0c;同时也可以帮助开发者追踪软件的历史和进展&#xff0c;并做好版本控制工作。在本文中&#xff0c;我们将介绍版本号的定义及规范&a…

Java基础面试重点-3

41. 简述线程生命周期(状态) 其它参考《多线程重点》中的说法。三种阻塞&#xff1a; 等待阻塞&#xff1a; 运行的线程执行o.wait()方法&#xff08;该线程已经持有锁&#xff09;&#xff0c;JVM会把该线程放入等待队列中。同步阻塞&#xff1a; 运行的线程在获取对象的同步…

【Android面试八股文】sleep、wáit、yield与join的区别,wait 的线程如何唤醒它?

在 Java 多线程编程中,sleep、wait、yield 和 join 是四个常用的方法,它们在控制线程行为方面各有不同的用途。以下是它们之间的区别以及 wait 方法如何唤醒的详细说明: 1. sleep 方法 作用:释放cpu资源,但不会释放锁。 如果线程进入sleep休眠状态,释放cpu资源,如果外层…

美团测开转正实习—到家研发部

美团测开转正实习——到家研发部 一面 1h20min 自我介绍 做项目实习前对测试开发如何了解学习的 问项目工作&#xff0c;实习内容&#xff0c;收获&#xff0c;使用场景等 接触python语言大概多久&#xff0c;对于java了解的深度 对于seleium的底层原理了解多少 写python自动化…

数据挖掘丨轻松应用RapidMiner机器学习内置数据分析案例模板详解(下篇)

RapidMiner 案例模板 RapidMiner 机器学习平台提供了一个可视化的操作界面&#xff0c;允许用户通过拖放的方式构建数据分析流程。RapidMiner目前内置了 13 种案例模板&#xff0c;这些模板是预定义的数据分析流程&#xff0c;可以帮助用户快速启动和执行常见的数据分析任务。 …

【无标题】计算机网络 4.2同轴电缆

第二节 同轴电缆 一、认识同轴电缆 1.描述&#xff1a;同轴电缆以铜线为芯&#xff0c;外包一层绝缘材料。 2.优点&#xff1a;抗干扰、高宽带。 3.连接器件&#xff1a;RF连接器、AUI&#xff08;粗&#xff09;、BNC&#xff08;细&#xff09;。 4.分类&#xff1a; 分…

jsp 实验20

三、源代码以及执行结果截图&#xff1a; NewFile.jsp <% page import "java.io.*" %> <% page contentType"text/html" %> <% page pageEncoding "utf-8" %> <jsp:useBean id"english" class "web.Engli…

QT--DAY1

不使用图形化界面实现一个登陆界面 #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("登录界面");//设置窗口大小this->resize(535,410);//固定窗口大小this->setFixedSize(535,410)…

代理流覽器的功能和具體使用方法

代理流覽器是一種通過代理伺服器中轉用戶請求的流覽器工具。當用戶通過代理流覽器訪問網站時&#xff0c;流覽器並不會直接與目標網站伺服器進行通信&#xff0c;而是先將請求發送到代理伺服器&#xff0c;由代理伺服器代為轉發請求並獲取回應數據。這樣一來&#xff0c;目標網…

181.二叉树:验证二叉树(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* Tre…

Flink任务如何跑起来之 2.算子 StreamOperator

Flink任务如何跑起来之 2.算子 StreamOperator 前文介绍了Transformation创建过程&#xff0c;大多数情况下通过UDF完成DataStream转换中&#xff0c;生成的Transformation实例中&#xff0c;核心逻辑是封装了SimpleOperatorFactory实例。 UDF场景下&#xff0c;DataStream到…

Spring Boot整合hibernate-validator实现数据校验

文章目录 概念基本概念常用校验注解 前置内容整合Hibernate Validator快速入门优雅处理参数校验异常其余注解校验自定义校验注解 参考来源 概念 基本概念 Hibernate Validator 是一个参数校验框架&#xff0c;可以非常方便地帮助我们校验应用程序的入参&#xff0c;实现了参数…

MySQL怎么为表添加描述

1.MySQL为表添加描述的方法 在MySQL中&#xff0c;表本身并没有直接的“描述”字段或属性来存储关于表的额外信息&#xff0c;如用途、创建者、备注等。但是&#xff0c;我们可以通过几种方式来实现这一需求&#xff1a; 1.1使用COMMENT关键字为表或列添加注释 虽然这不是一…

英伟达SSD视觉算法模型训练代码解析

一、官方源代码 #!/usr/bin/env python3 # # train an SSD detection model on Pascal VOC or Open Images datasets # https://github.com/dusty-nv/jetson-inference/blob/master/docs/pytorch-ssd.md # import os import sys import logging import argparse import dateti…

NLP数据集的标注笔记0.1.000

NLP数据集的标注0.1.000 命名实体识别(Named Entity Recognition,简称NER)关系抽取(Relation Extraction,简称RE)事件抽取 (Event Extraction, 简称EE)评价观点抽取参考链接命名实体识别(Named Entity Recognition,简称NER) 抽取的类别没有限制,用户可以自己定义,如…

解决:selenium运行时driver初始化失败 DevToolsActivePort file doesn‘t exist的问题

解决&#xff1a;selenium运行时driver初始化失败 DevToolsActivePort file doesn‘t exist的问题 DevToolsActivePort file doesnt exist报错信息&#xff1a;![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/b3f8acc1c47d45e3912575896e421567.png)现象&#xff1…

10.4 Go 并发模式

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

【Android】基于webView打造富文本编辑器(H5)

目录 前言一、实现效果二、具体实现1. 导入网页资源2. 页面设计3. 功能调用4. 完整代码 总结 前言 HTML5是构建Web内容的一种语言描述方式。HTML5是Web中核心语言HTML的规范&#xff0c;用户使用任何手段进行网页浏览时看到的内容原本都是HTML格式的&#xff0c;在浏览器中通过…

幸狐RV1106开发板烧录Ubuntu系统与配置SDK,RV1106 LuckFox Pico Max——最新的操作

资料&#xff1a;上手教程 | LUCKFOX WIKI 以及SDK内的文档资料 开发板型号&#xff1a;RV1106 LuckFox Pico Max 烧录系统&#xff1a; Ubuntu 虚拟机系统&#xff1a;Ubuntu 20.04&&Ubuntu22.04 PC系统&#xff1a;win11 占用空间&#xff1a;大概15G 本文主要记…