Linux I2C 应用编程

Linux I2C 应用编程

设备文件

/dev/i2c-x
x是I2C总线号,即一组SCL和SDA,一条总线上可以挂接多个I2C设备,通信时以设备地址区分,不体现在系统设备文件上。
对于树莓派,启用I2C功能后,有一条总线,即一个设备:
/dev/i2c-1
i2c-0被eeprom占用未导出
树莓派I2C相关PIN定义参考https://pinout.xyz/pinout/i2c#

用户空间设备操作ioctl

相关头文件

 #include <linux/i2c-dev.h>

I2C设置:

  1. 地址位数
  2. 设备地址
  3. 时钟频率
功能cmdarg参数说明
设置I2C寻址重试次数I2C_RETRIESulong
设置超时时间I2C_TIMEOUTulong单位是10ms
设置I2C设备地址I2C_SLAVEulong低位为设备地址
强制使用一个地址I2C_SLAVE_FORCEulong低位为设备地址
指定使用10位地址I2C_TENBITulong
查询主设备功能I2C_FUNCSulong*
读写数据I2C_RDWRi2c_rdwr_ioctl_data[]仅发送一次停止信号
SMBUS方式下使用PECI2C_PECulong
使用SMBUS方式读写数据I2C_SMBUSi2c_smbus_ioctl_data[]
  • 使用时如果设备地址为10位,先设置为10位地址模式,再设置设备地址;
  • 没有看到设置时钟频率的方法,应该是在驱动中设置,应用不可修改;

数据传输

使用ioctl的I2C_RDWR或者I2C_SMBUS指令
使用read/write
read/wirte一次最多传输不超过8k bytes
I2C_RDWR单消息最大长度不超过8k bytes

应用举例

以树莓派操作PN532 NFC模块为例:
PN532采用微雪的PN532 NFC HAT模块,使用HAT方式连接。

PN532的通信参数:
在这里插入图片描述注意:

  • 文档里的地址0x48用的是高7位,而程序中传递的地址通常是参数的低7位,所以我们时间使用的地址是0x24;
  • 采用的MSB的位序,树莓派I2C硬件和驱动也是MSB位序,无需做位序转换;
  • 最大400kHz的时钟频率,参考硬件手册看是否支持,经过测试可以支持。

PN532 I2C交互流程
在这里插入图片描述

注意:

  • 每次读都会读到Status,就是说,在读到Satus为1后,再去读ACK或Response的时候还会再次读到Status,需要在结果中移除。

以下程序读取NFC tag的UID:

使用libnfc

// To compile this simple example:
// $ gcc -o quick_start_example1 quick_start_example1.c -lnfc
#include <stdlib.h>
#include <nfc/nfc.h>
static void
print_hex(const uint8_t *pbtData, const size_t szBytes)
{
  size_t  szPos;
  for (szPos = 0; szPos < szBytes; szPos++) {
    printf("%02x  ", pbtData[szPos]);
  }
  printf("\n");
}
int
main(int argc, const char *argv[])
{
  nfc_device *pnd;
  nfc_target nt;
  // Allocate only a pointer to nfc_context
  nfc_context *context;
  // Initialize libnfc and set the nfc_context
  nfc_init(&context);
  if (context == NULL) {
    printf("Unable to init libnfc (malloc)\n");
    exit(EXIT_FAILURE);
  }
  // Display libnfc version
  const char *acLibnfcVersion = nfc_version();
  (void)argc;
  printf("%s uses libnfc %s\n", argv[0], acLibnfcVersion);
  // Open, using the first available NFC device which can be in order of selection:
  //   - default device specified using environment variable or
  //   - first specified device in libnfc.conf (/etc/nfc) or
  //   - first specified device in device-configuration directory (/etc/nfc/devices.d) or
  //   - first auto-detected (if feature is not disabled in libnfc.conf) device
  pnd = nfc_open(context, "pn532_i2c:/dev/i2c-1");
  if (pnd == NULL) {
    printf("ERROR: %s\n", "Unable to open NFC device.");
    exit(EXIT_FAILURE);
  }
  // Set opened NFC device to initiator mode
  if (nfc_initiator_init(pnd) < 0) {
    nfc_perror(pnd, "nfc_initiator_init");
    exit(EXIT_FAILURE);
  }
  printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
  // Poll for a ISO14443A (MIFARE) tag
  const nfc_modulation nmMifare = {
    .nmt = NMT_ISO14443A,
    .nbr = NBR_106,
  };
  if (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) > 0) {
    printf("The following (NFC) ISO14443A tag was found:\n");
    printf("    ATQA (SENS_RES): ");
    print_hex(nt.nti.nai.abtAtqa, 2);
    printf("       UID (NFCID%c): ", (nt.nti.nai.abtUid[0] == 0x08 ? '3' : '1'));
    print_hex(nt.nti.nai.abtUid, nt.nti.nai.szUidLen);
    printf("      SAK (SEL_RES): ");
    print_hex(&nt.nti.nai.btSak, 1);
    if (nt.nti.nai.szAtsLen) {
      printf("          ATS (ATR): ");
      print_hex(nt.nti.nai.abtAts, nt.nti.nai.szAtsLen);
    }
  }
  // Close NFC device
  nfc_close(pnd);
  // Release the context
  nfc_exit(context);
  exit(EXIT_SUCCESS);
}

使用ioctl/read/write
pn532.h

#pragma once

#include <memory>

namespace pn532
{

const uint8_t TFI_H2P = 0xD4;
const uint8_t TFI_P2H = 0xD5;

#pragma pack(1)
struct ni_frame_header
{
        uint8_t preamble;
        uint8_t start_code[2];
        uint8_t len;
        uint8_t lcs;
        uint8_t tfi;
};

struct frame_tailer
{
        uint8_t dcs;
        uint8_t postamble;
};
#pragma pack()

const uint8_t ACK_FRAME[6] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};
const uint8_t NACK_FRAME[6] = {0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00};
const uint8_t ERROR_FRAME[8] = {0x00, 0x00, 0xFF, 0x01, 0xFF, 0x7F, 0x81, 0x00};

typedef std::shared_ptr<uint8_t[]> byte_ptr;
typedef byte_ptr data_ptr;
typedef byte_ptr frame_ptr;

uint8_t checksum(const uint8_t * data, int data_len);
uint8_t checksum(data_ptr data, int data_len);
bool validate_checksum(const uint8_t * data, int data_len);
bool validate_checksum(data_ptr data, int data_len);
int cal_frame_len(int data_len);
frame_ptr new_frame(int frame_len);
int make_frame(uint8_t* buf, int buf_len, uint8_t tfi, const uint8_t* data, int data_len);
int make_frame(frame_ptr& frame, uint8_t tfi, const uint8_t* data, int data_len);
int make_frame(frame_ptr& frame, uint8_t tfi, data_ptr data, int data_len);

}

pn532.cpp

#include "pn532.h"
#include <cassert>

namespace pn532
{

uint8_t checksum(const uint8_t * data, int data_len)
{
        assert(data_len >= 0);
        uint8_t cs = 0;
        const uint8_t* p = data;
        for (int i = 0; i < data_len; ++i)
        {
                cs += *p++;
        }
        cs = (~cs) + 1;
        return cs;
}

uint8_t checksum(data_ptr data, int data_len)
{
        return checksum(data.get(), data_len);
}

bool validate_checksum(const uint8_t* data, int data_len)
{
        assert(data_len >= 0);
        uint8_t cs = 0;
        const uint8_t* p = data;
        for (int i = 0; i < data_len; ++i)
        {
                cs += *p++;
        }
        return (cs == 0);
}

bool validate_checksum(data_ptr data, int data_len)
{
        return validate_checksum(data.get(), data_len);
}

int cal_frame_len(int data_len)
{
        assert(data_len >= 0);
        return sizeof(ni_frame_header) + data_len + sizeof(frame_tailer);
}

frame_ptr new_frame(int frame_len)
{
        assert(frame_len >= 0);
        frame_ptr frame(new uint8_t[frame_len]);
        return frame;
}

int make_frame(uint8_t* buf, int buf_len, uint8_t tfi, const uint8_t* data, int data_len)
{
        assert(data_len >= 0);
        int frame_len = cal_frame_len(data_len);
        if (buf == NULL || buf_len == 0)
        {
                return frame_len;
        }
        if (buf_len < frame_len)
        {
                return -1;
        }
        uint8_t* f = buf;
        ni_frame_header* h = (ni_frame_header*)f;
        uint8_t* d = f + sizeof(ni_frame_header);
        frame_tailer* t = (frame_tailer*)(d + data_len);
        h->preamble = 0x00;
        h->start_code[0] = 0x00;
        h->start_code[1] = 0xFF;
        h->len = data_len + 1;
        h->lcs = checksum(&h->len, 1);
    h->tfi = tfi;
        uint8_t dcs = tfi;
        const uint8_t* dd = data;
        for (int i = 0; i < data_len; ++i)
        {
                *d++ = *dd;
                dcs += *dd++;
        }
        dcs = (~dcs) + 1;
        t->dcs = dcs;
        t->postamble = 0x00;
        return frame_len;
}

int make_frame(frame_ptr & frame, uint8_t tfi, data_ptr data, int data_len)
{
        assert(data_len >= 0);
        int frame_len = cal_frame_len(data_len);
        frame = new_frame(frame_len);
        return make_frame(frame.get(), frame_len, tfi, data.get(), data_len);
}

}

get_uid_i2c.cpp

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <string.h>
#include "pn532.h"

using namespace pn532;

#define IOCTL(fd, cmd, arg) \
        do\
        {\
                if (ioctl(fd, cmd, arg) == -1)\
                {\
                        perror(#cmd);\
                        close(fd);\
                        return -1;\
                }\
        }\
        while(0)

int init_i2c(const char * dev)
{
        static const ulong addr = 0x24;

        int fd = open(dev, O_RDWR);
        if (fd == -1)
        {
                perror("open device:");
                return -1;
        }

        IOCTL(fd, I2C_SLAVE, addr);

        return fd;
}

int send_i2c(int fd, const uint8_t* sendbuf, int sendlen)
{
        usleep(5000);
        return write(fd, sendbuf, sendlen);
}

int recv_i2c(int fd, uint8_t* recvbuf, int recvlen)
{
        usleep(5000);
        return read(fd, recvbuf, recvlen);
}

void print_hex(const uint8_t * buf, int len)
{
        const uint8_t * p = buf;
        for (int i = 0; i < len; ++i)
        {
                printf("%02X ", *p++);
                if ((i+1) % 16 == 0)
                {
                        printf("\n");
                }
        }
        if (len % 16 != 0)
        {
                printf("\n");
        }
}

int wait_for_ready(int fd)
{
        uint8_t buf[1];

        while (1)
        {
                usleep(10000);
                if (recv_i2c(fd, buf, 1) != 1)
                {
                        printf("recv status error!\n");
                        return -1;
                }
                if (buf[0] == 0x01)
                        return 0;
        }
        return -1;
}

#define BUF_SIZE 264

int do_cmd(int fd, const uint8_t* cmd, int cmdlen, const char* cmdname, int answerlen)
{
        if (cmdname != NULL)
        {
                printf("do command: %s\n", cmdname);
        }

        if (answerlen < 0)
        {
                answerlen = BUF_SIZE;
        }

        uint8_t buf[BUF_SIZE+1];
        frame_ptr cmd_frame;
        int cmd_frame_len = make_frame(cmd_frame, TFI_H2P, cmd, cmdlen);
        print_hex(cmd_frame.get(), cmd_frame_len);
        if (send_i2c(fd, cmd_frame.get(), cmd_frame_len) != cmd_frame_len)
        {
                perror("write cmd");
                return -1;
        }
        if (wait_for_ready(fd) != 0)
        {
                return -1;
        }
        printf("ready for ack!\n");
        if (recv_i2c(fd, buf, 7) < 0)
        {
                perror("read ack error");
                return -1;
        }
        printf("read ack: ");
        print_hex(buf+1, 6);
        if (wait_for_ready(fd) != 0)
        {
                return -1;
        }
        printf("ready for answer!\n");
        int alen = 0;
        if ((alen = recv_i2c(fd, buf, answerlen+1)) < 0)
        {
                perror("read answer error");
                return -1;
        }
        printf("read answer: ");
        print_hex(buf+1, alen-1);

        return 0;
}

int main(int argc, char *argv[])
{
        int fd = init_i2c("/dev/i2c-1");
        if (fd < 0)
        {
                fprintf(stderr, "init device error!\n");
                return -1;
        }

#if 0
        uint8_t gfv_data[] = {0x02};
        if (do_cmd(fd, gfv_data, sizeof(gfv_data), "get fireware version", 13) < 0)
        {
                return -1;
        }
#endif

#if 1
        uint8_t set_normal_mode_data[] = {0x14, 0x01, 0x00, 0x00};
        if (do_cmd(fd, set_normal_mode_data, sizeof(set_normal_mode_data), "set normal mode", 9) < 0)
        {
                return -1;
        }
#endif

#if 1
        uint8_t autopoll_data[] = {0x60, 0xff, 0x02, 0x00, 0x01, 0x02, 0x03, 0x04, 0x10, 0x11, 0x12, 0x20, 0x23};
        if (do_cmd(fd, autopoll_data, sizeof(autopoll_data), "auto poll", 32) < 0)
        {
                return -1;
        }
#endif

#if 0
        uint8_t lpt_data[] = {0x4A, 0x01, 0x00};
        if (do_cmd(fd, lpt_data, sizeof(lpt_data), "list passive target", 32) < 0)
        {
                return -1;
        }
#endif

        close(fd);
        return 0;
}

参考

https://www.waveshare.net/wiki/PN532_NFC_HAT
https://pinout.xyz/pinout/i2c#
https://github.com/nfc-tools/libnfc

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞花丝雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值