You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言中通过SocketCAN接口创建含非8位数据的CAN帧

CAN帧数据拆分与位级存储实操方案

先给你指出原代码里的明显错误:

  • 给frame2设置ID后,错误修改了frame.can_dlc,应该是frame2.can_dlc = 6(三个16位数据正好占6字节)
  • 直接把16位数值赋值给8位的frame.data[x]会被截断,比如32766是0x7FFE,赋值后只剩0xFE,完全丢失高字节数据

一、处理16位数据:拆分高低字节

CAN帧的data是8位字节数组,16位数据必须拆成高字节和低字节分别存入相邻的数组元素,用C语言位操作就能实现:

  • 低字节:取数值的最后8位,用 val & 0xFF
  • 高字节:把数值右移8位后取最后8位,用 (val >> 8) & 0xFF

不管是无符号uint16_t还是有符号int16_t,这个方法都适用(有符号数的算术移位不影响字节拆分,还原时按对应类型读取即可)。

比如你要发送的三个16位数值,拆分后存储的代码:

// 用stdint.h的明确类型避免歧义
int16_t val1 = 32766;
int16_t val2 = -32765;
int16_t val3 = 20542;

// 拆分存入frame2的data数组
frame2.data[0] = val1 & 0xFF;       // val1的低字节
frame2.data[1] = (val1 >> 8) & 0xFF; // val1的高字节
frame2.data[2] = val2 & 0xFF;
frame2.data[3] = (val2 >> 8) & 0xFF;
frame2.data[4] = val3 & 0xFF;
frame2.data[5] = (val3 >> 8) & 0xFF;

二、处理小于8位的位级数据:位打包

把多个小数值塞进一个字节,需要用位掩码和移位操作打包。举个实际例子:

  • 把一个4位数值(0-15)和一个3位数值(0-7)塞进同一个字节:4位数值放高4位,3位放低3位,剩余1位留空或设0。

打包代码示例:

uint8_t four_bit_val = 12; // 二进制0b1100
uint8_t three_bit_val = 5; // 二进制0b101

// 打包:4位值左移4位,3位值直接放低3位,合并结果
frame3.data[0] = (four_bit_val << 4) | (three_bit_val & 0x07);
// 最终结果是0b11000101 = 0xC5

如果要还原数据,反向操作即可:

uint8_t packed = frame3.data[0];
uint8_t extracted_four = (packed >> 4) & 0x0F; // 提取高4位
uint8_t extracted_three = packed & 0x07;       // 提取低3位

三、完整修正后的代码

包含三个帧的实现,同时修复原代码错误:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
#include <stdint.h> // 引入明确的整数类型

int main(int argc, char **argv)
{
    int s; 
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame_8bit;   // 8个8位数值的帧
    struct can_frame frame_16bit;  // 三个16位数值的帧
    struct can_frame frame_bit;    // 位级存储的帧

    if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
        perror("Socket");
        return 1;
    }

    strcpy(ifr.ifr_name, "vcan0" );
    ioctl(s, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("Bind");
        return 1;
    }

    // 第一帧:8个8位数值(原代码正确部分)
    frame_8bit.can_id = 0x500;
    frame_8bit.can_dlc = 8;
    frame_8bit.data[0] = 122;
    frame_8bit.data[1] = 150;
    frame_8bit.data[2] = -27;
    frame_8bit.data[3] = -22;
    frame_8bit.data[4] = 160;
    frame_8bit.data[5] = 160;
    frame_8bit.data[6] = -60;
    frame_8bit.data[7] = 90;

    // 第二帧:三个16位数值
    frame_16bit.can_id = 0x501;
    frame_16bit.can_dlc = 6; // 3*2=6字节
    int16_t val1 = 32766;
    int16_t val2 = -32765;
    int16_t val3 = 20542;
    // 拆分高低字节存入
    frame_16bit.data[0] = val1 & 0xFF;
    frame_16bit.data[1] = (val1 >> 8) & 0xFF;
    frame_16bit.data[2] = val2 & 0xFF;
    frame_16bit.data[3] = (val2 >> 8) & 0xFF;
    frame_16bit.data[4] = val3 & 0xFF;
    frame_16bit.data[5] = (val3 >> 8) & 0xFF;

    // 第三帧:位级存储示例(一个字节存4位+3位数值,另一个字节存2个2位数值)
    frame_bit.can_id = 0x502;
    frame_bit.can_dlc = 2;
    uint8_t four_bit = 12;
    uint8_t three_bit = 5;
    uint8_t two_bit1 = 3;
    uint8_t two_bit2 = 1;
    // 打包第一个字节
    frame_bit.data[0] = (four_bit << 4) | (three_bit & 0x07);
    // 打包第二个字节:两个2位数值,分别占高2位和低2位
    frame_bit.data[1] = (two_bit1 << 6) | (two_bit2 & 0x03);

    // 发送三个帧
    if (write(s, &frame_8bit, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
        perror("Write frame_8bit");
        return 1;
    }
    if (write(s, &frame_16bit, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
        perror("Write frame_16bit");
        return 1;
    }
    if (write(s, &frame_bit, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
        perror("Write frame_bit");
        return 1;
    }

    if (close(s) < 0) {
        perror("Close");
        return 1;
    }

    return 0;
}

内容的提问来源于stack exchange,提问作者willem12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:40:23