/dev/i2c-x
x是I2C总线号,即一组SCL和SDA,一条总线上可以挂接多个I2C设备,通信时以设备地址区分,不体现在系统设备文件上。
对于树莓派,启用I2C功能后,有一条总线,即一个设备:
/dev/i2c-1
i2c-0被eeprom占用未导出
树莓派I2C相关PIN定义参考https://pinout.xyz/pinout/i2c#
相关头文件
#include
I2C设置:
- 地址位数
- 设备地址
- 时钟频率
| 功能 | cmd | arg | 参数说明 |
|---|---|---|---|
| 设置I2C寻址重试次数 | I2C_RETRIES | ulong | |
| 设置超时时间 | I2C_TIMEOUT | ulong | 单位是10ms |
| 设置I2C设备地址 | I2C_SLAVE | ulong | 低位为设备地址 |
| 强制使用一个地址 | I2C_SLAVE_FORCE | ulong | 低位为设备地址 |
| 指定使用10位地址 | I2C_TENBIT | ulong | |
| 查询主设备功能 | I2C_FUNCS | ulong* | |
| 读写数据 | I2C_RDWR | i2c_rdwr_ioctl_data[] | 仅发送一次停止信号 |
| SMBUS方式下使用PEC | I2C_PEC | ulong | |
| 使用SMBUS方式读写数据 | I2C_SMBUS | i2c_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#include 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 %sn", 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: %sn", "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 openedn", 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 #includenamespace 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 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" #includenamespace 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参考#include #include #include #include #include #include #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: %sn", 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



