Getting Started with Embedded Linux 3-5-I2C

I2C protocol and SMBus protocol

Since I am quite familiar with the I2C protocol, the notes do not involve the explanation of the I2C protocol itself. Here are two blogs for understanding the I2C and SMBus protocols:
Understanding the I2C Bus

SMBus Quick Start Guide

i2c-tools

Using i2c-tools can easily debug I2C devices, refer to the following blog for usage methods, it is very simple:

Using the Linux I2C-Tools Software

But just reading the above blog is not enough. If you want to master more usages (such as reading data directly without specifying the register address), you can install this tool under linux and read the man document. Generally, the man document on the development board is castrated.

Moreover, i2c-tools before version 4.0 has its limitations. When using the i2cget command to read, it can only read the first 2 bytes. The new version of the i2ctransfer command can already read the specified number of bytes. Unfortunately, My development board is an old version and does not support this command. For ATH10, a temperature and humidity sensor that returns many bytes at a time, this cannot be used to read. Its timing is as follows:

image-20220417165154601

If you use i2cget to read, you can only get the status bit and the highest bit of humidity data. By directly using the i2c access interface under linux, we can write read and write instructions by ourselves.

i2c programming

The experimental platform is liceepi nano.

It is recommended to read this blog written by teacher Wei Dongshan first: Linux application development [Chapter 12] Section 12.3.2 of I2C programming application development

The following is a small program to control the ATH10 temperature and humidity sensor, which is connected to the hardware and mounted on i2c-0:

#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <math.h>

int execute_cmd(int fd, char *cmd);
void parse_data(unsigned char *buffer, float *temperature, float * humidness);

int main(int args, char *argv[])
{
    
    
    const unsigned char ath10_i2c_addr = 0x38; /* ATH10温湿度传感器地址 */
    int fd_i2c;

    /* 检查输入参数 */
    if (args == 1 || strcmp(argv[0], "--help") == 0)
    {
    
    
        printf("usage:\n");
        printf("%s I2C CMD...\n", argv[0]);
        printf("operation ath10 using I2C.\n");
        printf("CMD :\n");
        printf("\tinit\t- initialize sensor.\n");
        printf("\tsamp\t- sampling once.\n");
        printf("\tread\t- read data.\n");
        exit(EXIT_FAILURE);
    }

    /* 打开传入的I2C设备 */
    fd_i2c = open(argv[1], O_RDWR);
    if (fd_i2c == -1)
    {
    
    
        fprintf(stderr, "Unable to open %s - ", argv[1]);
        fflush(stderr);
        perror(NULL);
        exit(EXIT_FAILURE);
    }

    /* 设置I2C从机地址 */
    if (ioctl(fd_i2c, I2C_SLAVE_FORCE, ath10_i2c_addr) == -1)
    {
    
    
        perror("Unable to set slave address - ");
    }

    /* 遍历并处理传入的命令 */
    for (int i = 2; i < args; i++)
    {
    
    
        if (execute_cmd(fd_i2c, argv[i]) == -1)
        {
    
    
            fprintf(stderr, "Unable to recognize cmd \"%s\"\n", argv[i]);
            fflush(stderr);
            close(fd_i2c);
            exit(EXIT_FAILURE);
        }
    }

    close(fd_i2c);
    exit(EXIT_SUCCESS);
}

/* 执行指令,指令识别失败时返回-1 */
int execute_cmd(int fd, char *cmd)
{
    
    
    const int cmd_read_recv_len = 6; /* 读取时需要读取的字节数 */

    const unsigned char cmd_init[] = {
    
    0xF1, 0x08, 0x00}; /* 初始化指令 */
    const unsigned char cmd_samp[] = {
    
    0xAC, 0x00, 0x00}; /* 采样指令 */

    unsigned char read_buffer[cmd_read_recv_len];

    if (strcmp(cmd, "init") == 0)
    {
    
    
        /* 初始化指令 */
        if (write(fd, cmd_init, sizeof(cmd_init)) == -1)
        {
    
    
            perror("Unable to send cmd \"init\" - ");
        }
    }
    else if (strcmp(cmd, "samp") == 0)
    {
    
    
        /* 采样指令 */
        if (write(fd, cmd_samp, sizeof(cmd_samp)) == -1)
        {
    
    
            perror("Unable to send cmd \"samp\" - ");
        }
    }
    else if (strcmp(cmd, "read") == 0)
    {
    
    
        /* 读取指令 */
        int read_size;
        read_size = read(fd, read_buffer, cmd_read_recv_len);
        if (read_size == -1)
        {
    
    
            perror("Unable to read data - ");
        }

        /* 打印获取到的数据 */
        if (read_size == cmd_read_recv_len)
        {
    
    
            float temperature, humidness;
            parse_data(read_buffer, &temperature, &humidness);
            printf("T = %.2f℃, H = %.2f%%\n", temperature, humidness);
        }
        else
        {
    
    
            fprintf(stderr, "Invalid data\n");
        }
    }
    else
    {
    
    
        /* 未分辨的命令 */
        return -1;
    }
    return 0;
}

/* 解析收到的数据,计算出温湿度 */
void parse_data(unsigned char *buffer, float *temperature, float * humidness)
{
    
    
    unsigned int t_data = 0;
    unsigned int h_data = 0;

    /* 所收数据的第4字节低4位以及第5、6字节为温度数据 */
    t_data = ((buffer[3] & 0x0F) << 16) | (buffer[4] << 8) | buffer[5];

    /* 所收数据的第2、3字节以及第4字节的高4位为湿度数据 */
    h_data = (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] >> 4);

    /* 使用数据手册的换算公式进行换算 */
    *temperature = t_data / powf(2, 20) * 200 - 50;
    *humidness = h_data / powf(2, 20);
}

Simple makefile for compilation:

CC := arm-linux-gcc

all : i2c_ath10.o
	${CC} -o ath10 $^

%.o : %c %h
	${CC} -c -o $@ $<

.PHONY clean :
	rm ath10 *.o

After compiling and sending the generated ath10 file to the development board, use chmod +x to add executable permissions, and then you can directly execute to obtain the temperature and humidity information of the sensor. The execution results are as follows:

# 使用i2c-0,先init初始化,然后smap采样,最后read读取结果
> ./ath10 /dev/i2c-0 init samp read
T = 21.66℃, H = 9.65%

Trample record

  1. When the compiled program runs on the development board, it prompts Segmentation fault.
    Note that the cross-compilation toolchain used when compiling the kernel is not necessarily suitable for compiling applications. The root file system of licheepi nano is built using buildroot, and the root file system in the root file system The system dynamic link library is generated by the cross-compilation toolchain downloaded by buildroot. This toolchain is located under buildroot/output/host. You need to use this cross-compilation toolchain to compile the application. If the application program is compiled using the cross-compilation toolchain of the compiled kernel, the version of the dynamic link library in the root file system of the development board may not match the version of the dynamically linked runtime library during compilation, which will cause the corresponding dynamic link to not be found when the program is running. file failed to run.
    For licheepi nano, the cross tool chain when compiling the kernel is arm-linux-gnueabi-gcc, and buildroot puts the dynamic link library of arm-linux-musleabi-gcc in it by default when building the root file system, so compile The application uses the arm-linux-musleabi-gcc downloaded by buildroot.

  2. Running the compiler: error while loading shared libraries: libmpfr.so.4: cannot open shared object file: No such file or directory.
    I thought at first that the program I wrote was to link this dynamic link library, but the compiler could not find it. Arrived, but there is this file in the lib link directory of the compiler, which is puzzling. Later, I realized that, damn it, the compiler itself couldn’t find this dynamic link library when it was running, and it had nothing to do with the program I wrote (TAT blinded me for a long time). Generally, there is a dynamic link library libmpfr.so.6 in the computer. The solution is to create a soft link of this file and name it libmpfr.so.4

    sudo ln -s /usr/lib/x86_64-linux-gnu/libmpfr.so.6 /usr/lib/x86_64-linux-gnu/libmpfr.so.4
    

おすすめ

転載: blog.csdn.net/lczdk/article/details/124263686