Jetson 使用IIC接口读取TFmini-plus等传感器数据
最近使用Jetson nx/nano的IIC接口来读取单点激光雷达TFmini-plus的数据,将开发过程记录如下。
首先需要了解IIC通信协议的基本原理,可参考我之前所写的博客:
https://blog.csdn.net/w1050321758/article/details/108956570?spm=1001.2014.3001.5501
我们将Jetson nano定为主机,TFmini-plus定为从机。根据IIC协议,我们需要知道从机地址以及从机的通信协议。
这里我们直接参考北醒光子TFmini-plus的说明书,可以看见其通信协议的格式。要想从从机读数据,我们需要发送5A 05 00 01 60这样的连续几个16进制数据来引起从机的反馈。
从机返回数据后,我们需要按照下面的协议来解析从机发送过来的数据:
另外,需要知道从机的IIC地址,如下图可见从机的默认地址为0x10。
最后我们还可以从产品说明书了解到它的数据时序,(其实这部分东西从机不给也可以,这是标准的IIC协议的通信时序)。
我们可以观察到,主机要想和TFmini-plus从机间进行通信,需要按照以下方式发送数据:
(1)首先发送起始位。如果我们使用标准Linux自带的IIC函数,那么起始位一般不需要我们自己发送,在调用函数的时候,系统会自动把起始位加上。
(2)从机地址+读写位。从机地址一般为7位,再加1位的“读”或者“写”形成8位,即IIC的一个数据帧,一并通过IIC发送给所有从机,从机接收到和自己地址相同的数据帧后,便会和主机建立连接,进行通信。读为1,写为0。所以这一个字节可以表示成:从机地址<<1|0(写,0加不加都可以);或者:从机地址<<1|1(读)。
(3)从机应答位。确认通信正常的位,该位由从机发出,我们调用系统函数的话一般不需要管。
(4)通过上面三步,主机便和从机建立了IIC通信渠道。此时主机一个字节一个字节往从机发送消息就可以了。为了实现有意义的通信,这里发送的字节需要参考上面从机的通信协议来发。
最后,如何使用Jetson来实现IIC通信呢,首先我们需要知道Jetson的IIC接口,下图可以看见Jetson nano一共2个IIC外部接口,BOARD 3号和5号以及BOARD 27号和28号,选一个接口把自己的从设备接上去就可以了。
其次,我们需要知道Jetson nx调用IIC的函数接口。
Jetson nano内部安装的是Linux系统,Linux系统内部提供了IIC相关的函数。
#include "/usr/include/linux/i2c.h"
#include <linux/i2c-dev.h>
那如何用这些函数呢,其实Linux可以通过操作文件的方式来操作IIC设备,因为Linux系统内,一切皆文件。只需要将普通文件通过ioctl函数声明为IIC设备即可,然后按照操作文件的方式来对IIC设备进行读写。
具体的代码如下,参考我上面的讲解,应该可以看懂了,如果不懂的话可以去翻看一下上面的讲解或者补一下IIC通信协议和Linux系统编程相关的知识,然后你一定会恍然大悟:
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "/usr/include/linux/i2c.h"
#include <linux/i2c-dev.h>
#define LIDAR_SENSOR 0x10
int writeReg(int pt, unsigned char add){
unsigned char w_buff[5];
//地址+写位,写位为0,所以只需要地址左移1位即可
w_buff[0] = add<<1;
//从机的读取协议
w_buff[1] = 0x5a;
w_buff[2] = 0x05;
w_buff[3] = 0x00;
w_buff[4] = 0x01;
w_buff[5] = 0x60;
//文件写操作
if (write(pt, w_buff, 6) != 6){
perror("Failed write to device");
return 1;
}
return 0;
}
int readReg(int pt, unsigned char s_add, char buf[], int size){
char writeBuff[1];
//读,从机地址+读位
writeBuff[0]= (s_add<<1)|1;
//先把读的命令发给从机
if(write(pt, writeBuff, 1)!=1) {
perror("Failed to reset the read address\n");
return 1;
}
//然后利用文件读操作,把读到的数据全部放到buf数组里面
if(read(pt, buf, size)!=size){
perror("Failed to read in the buffer\n");
return 1;
}
return 0;
}
float distance=0;
int main(){
int file;
//打开文件
if((file=open("/dev/i2c-1", O_RDWR)) < 0) {
perror("failed to open the bus\n");
return 1;
}
//声明为IIC设备文件
if(ioctl(file, I2C_SLAVE, LIDAR_SENSOR) < 0) {
perror("Failed to connect to the sensor\n");
return 1;
}
//存储接收到的数据
char distance_buff[9]={0};
while(1)
{
//先写,引起从机的反馈
if(writeReg(file, LIDAR_SENSOR)==1){
return 1;
}
//再读取从机反馈
if(readReg(file, LIDAR_SENSOR, distance_buff,9)==1){
return 1;
}
//从从机反馈的数据里面根据从机的协议提取我们想要的东西
distance=short(distance_buff[2]|(distance_buff[3]<<8))*0.01;
printf("distance: %.2f\n",distance);
}
close(file);
return 0;
}