at24c02 user mode io read / write md

1. at24c02 user status io read / write

In line with the idea that everything is a file, the IO function can be used to read and write at24c02 under linux, which is usually read and written as a single byte. Due to the small amount of at24c02 data storage, the impact on performance is weak. Therefore, this article will take the lead in the single byte read / write example. Of course, reading and writing by page should also be realized in case of need.

It is necessary to refer to the header file before reading and writing:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

And define some macros according to the characteristics of at24c02:

#define AT24C02_PAGE_SIZE 16 / * page size*/
#define AT24C02_PAGE_MASK (AT24C02_PAGE_SIZE - 1) / * page mask*/
#define AT24C02_PAGE_SHIFT 4 / * page shift*/

In order to facilitate the open operation of i2c device nodes, preliminary packaging is also carried out:

static int drv_i2c_open(int adapter_nr)
{
    int fd;
    char filename[20];

    /* open such as "/dev/i2c-%d" device node */
    sprintf(filename, "/dev/i2c-%d", adapter_nr);
    fd = open(filename, O_RDWR);
    if (fd >= 0) {
        return fd;
    }

    /* open such as "/dev/i2c/%d" device node */
    if (errno == ENOENT) {
        filename[8] = '/';
        fd = open(filename, O_RDWR);
    }

    return fd;
}

As above, DRV_ I2C_ The open function considers the device node structure of "/dev/i2c-%d" and "/dev/i2c/%d", which plays a role in improving code adaptability.

1.1 AT24C02 single byte read / write

1.1.1 AT24C02 single byte read

Since there is no delay in at24c02 read operation, IO read and write can be performed directly, as follows:

int drv_i2c_read8(int adapter_nr, const uint8_t i2c_addr,
          const uint8_t reg_addr, uint8_t * const data)
{
    int fd;
    int ret = 0;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        if ((ret = write(fd, &reg_addr, 1)) != 1)
            break;

        if ((ret = read(fd, data, 1)) != 1)
            break;
    } while (0);

    close(fd);

    return ret;
}

As above, first use drv_i2c_open open the i2c device node, then use ioctl to set the device address, then use the write function to set the register address to be operated, and finally use the read function to read 1 byte of data.

Generally, such low-level functions are not exposed to the user, and the user does not have to read each byte, so a layer of encapsulation is made:

int drv_i2c_read_bytes(int adapter_nr, const uint8_t i2c_addr,
               const uint8_t reg_addr, uint8_t * const data,
               const uint8_t cnt)
{
    uint8_t i;
    int ret = 0;

    for (i = 0; i < cnt; i++) {
        ret = drv_i2c_read8(adapter_nr, i2c_addr,
                    reg_addr + i, data + i);
        if (ret < 0)
            break;
    }

    return ret;
}

As mentioned above, the user only needs to set the i2c bus number, i2c device address, register address and the amount of read back data to easily complete the read operation.

1.1.2 AT24C02 single byte write

The write operation is a little troublesome. After a single write operation of at24c02 is completed, enough time should be allowed for at24c02 to synchronize data. Therefore, it encapsulates a retry mechanism. If it fails to write successfully within the specified time and times, it will be discarded. Refer to the following:

static int i2c_try_write(int fd, uint8_t * data, uint8_t data_len,
             uint8_t retry_times)
{
    int ret = 0;

    while (retry_times--) {
        ret = write(fd, data, data_len);
        if (ret < 0) {
            /* ENXIO Usually, the chip is busy and cannot respond to external operations */
            if (ENXIO == errno) {
                usleep(1000);   /* Sleep for 1ms each time */
                continue;
            }

            perror("write");
            break;
        } else if (ret != data_len) {
            ret = -1;
            perror("write");
            break;
        }

        break;
    }

    return ret;
}

As above, set retry_ The times parameter can specify the number of retries. The interval between retries of the above code is 1ms. The reason is that the task scheduling interval of the running platform is exactly 1ms(1000Hz), which can reduce the CPU blind waiting caused by too short sleep time. In actual use, it can be slightly modified by referring to the specific platform.

Since it is a write operation, there are not so many things in the process as a read operation. The read operation first needs to write the register to set the data reading position, and then read the data, which needs to be divided into two steps. The write operation can be completed in one step by directly adding data after the register to be written, as follows:

int drv_i2c_write8(const int adapter_nr, const uint8_t i2c_addr,
           const uint8_t reg_addr, const uint8_t data)
{
    int fd;
    int ret = 0;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        uint8_t cnt = 5;    /* retry count */
        uint8_t msg_payload[] = { reg_addr, data };

        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        ret = i2c_try_write(fd, msg_payload, sizeof(msg_payload), cnt);
        if (ret < 0)
            break;
    } while (0);

    close(fd);

    return ret;
}

Similarly, for ease of operation, this function is encapsulated as follows:

int drv_i2c_write_bytes(const int adapter_nr, const uint8_t i2c_addr,
            const uint8_t reg_addr, uint8_t * const data,
            const uint8_t cnt)
{
    uint8_t i;
    int ret = 0;

    for (i = 0; i < cnt; i++) {
        ret = drv_i2c_write8(adapter_nr, i2c_addr,
                     reg_addr + i, *(data + i));
        if (ret < 0)
            break;
    }

    return ret;
}

Usage and drv_i2c_read_bytes is similar.

1.1.3 AT24C02 single byte comprehensive test

The test method is very simple. Starting from 0x11 register of at24c02, use the write function to write a shameless love word: "I Love You, Baby!", Then use the read function to read it out. If it is read out successfully, the test is qualified. The example code is as follows:

    int adapter_nr = 1; /* probably dynamically determined */
    int ret;
    uint8_t reg = 0x11; /* Device register to access */
    uint8_t i2c_addr = 0x50;    /* Device address to access */
    
    uint8_t buf[] = "I Love You, Baby !";
    
    /* Recover the area to be written */
    system("i2ctransfer -y 1 w255@0x50 0x10 0xff=");
    system("i2ctransfer -y 1 w255@0x50 0x20 0xff=");
    
    ret = drv_i2c_write_bytes(adapter_nr, i2c_addr,
                  reg, buf, sizeof(buf));
    if (ret < 0) {
        printf("ERROR HANDLING: i2c transaction failed\n");
    }
    usleep(10000);
    
    system("i2cdump -y 1 0x50 b");
    memset(buf, 0, sizeof(buf));
    ret = drv_i2c_read_bytes(adapter_nr, i2c_addr, reg,
                 buf, sizeof(buf));
    if (ret < 0) {
        printf("ERROR HANDLING: i2c transaction failed\n");
    }
    buf[sizeof(buf) - 1] = '\0';
    
    printf("reg %d -> %s\n", reg, buf);

Although the love talk was shameless, the experiment was successful, as follows:

# ./at24c02a_app_io
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
10: ff 49 20 4c 6f 76 65 20 59 6f 75 2c 20 42 61 62    .I Love You, Bab
20: 79 20 21 00 ff ff ff ff ff ff ff ff ff ff ff ff    y !.............
30: 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
reg 17 -> I Love You, Baby !

1.2 AT24C02 read / write by page

The efficiency of using single byte to read and write at24c02 is obviously not high. At the same time, the file is opened and closed repeatedly, which is specially for patients with obsessive-compulsive disorder. Therefore, consider reading and writing at24c02 by page.

1.2.1 AT24C02 read by page

Similarly, since there is no delay in reading at24c02, it can be read at will with confidence.

int drv_i2c_read(int adapter_nr, const uint8_t i2c_addr,
         const uint8_t reg_addr, uint8_t * const data,
         const uint8_t data_len)
{
    int fd;
    uint8_t addr = reg_addr;
    int ret = 0;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        if ((ret = write(fd, &addr, 1)) != 1)
            break;

        if ((ret = read(fd, data, data_len)) != data_len)
            ret = -EINVAL;
    } while (0);

    close(fd);

    return ret;
}

As above, read operation and drv_i2c_read8 is the same, so I won't repeat it.

1.2.2 AT24C02 write by page

The write operation is more troublesome. At the same time, the write delay and page change should be considered, but how can I be as smart as I am.

int drv_i2c_write(const int adapter_nr, const uint8_t i2c_addr,
          const uint8_t reg_addr, uint8_t * const data,
          const uint8_t data_len)
{
    int fd;
    int ret = 0;
    int loop;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    /* Calculate page spreads */
    loop = (reg_addr & AT24C02_PAGE_MASK) + data_len;
    loop = (loop + AT24C02_PAGE_MASK) / AT24C02_PAGE_SIZE;

    do {
        int i;

        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        for (i = 0; i < loop; i++) {
            int j;
            uint8_t cnt = 5;    /* retry count */
            uint8_t start_addr;
            uint8_t xfer_len;
            uint8_t xfer_cnt;
            uint8_t *xfer_offset;
            uint8_t transfer_data[AT24C02_PAGE_SIZE + 1];

            /* First time with reg_addr shall prevail, and the subsequent page start and end addresses shall prevail */
            if (0 == i) {
                start_addr = reg_addr;
            } else {
                start_addr = (reg_addr + i * AT24C02_PAGE_SIZE);
                start_addr &= ~AT24C02_PAGE_MASK;
            }

            /* Amount of data transferred */
            xfer_cnt = start_addr - reg_addr;

            /* Calculate the amount of data to be transmitted this time */
            xfer_len = AT24C02_PAGE_SIZE -
                (start_addr & AT24C02_PAGE_MASK);

            /* If the last time is less than one page, it shall be transmitted according to the actual situation */
            if ((data_len - xfer_cnt) < AT24C02_PAGE_SIZE)
                xfer_len = data_len - xfer_cnt;

            xfer_offset = data + xfer_cnt;

            /* Fill register address and data to be written */
            transfer_data[0] = start_addr;
            for (j = 1; j <= xfer_len; j++)
                transfer_data[j] = *(xfer_offset + j - 1);

            /* write */
            ret = i2c_try_write(fd, transfer_data,
                        xfer_len + 1, cnt);
            if (ret < 0)
                break;
        }
    } while (0);

    close(fd);

    return ret;
}

The key point of this function is the processing of the first page and the last page. The first page may not be in the page header, so the position and length of the first write need additional processing; The last page may contain less than one page of data, and subsequent data may be overwritten by careless processing.

1.2.3 AT24C02 page by page reading and writing comprehensive test

The test method is also simple. It starts from 0x11 storage of at24c02. However, this time, a more shameless love sentence is written: "I Love You more than I can say!", Because this love sentence just skips the page header, write one page completely and write only one data on the last page. One love word meets the test requirements of three scenarios at the same time. Wonderful!

    int adapter_nr = 1; /* probably dynamically determined */
    int ret;
    uint8_t reg = 0x11; /* Device register to access */
    uint8_t i2c_addr = 0x50;    /* Device address to access */
    
    uint8_t buf[] = "I Love You more than I can say!";

    /* Recover the area to be written */
    system("i2ctransfer -y 1 w255@0x50 0x10 0xff=");
    system("i2ctransfer -y 1 w255@0x50 0x20 0xff=");

    ret = drv_i2c_write(adapter_nr, i2c_addr, reg,
                buf, sizeof(buf));
    if (ret < 0) {
        printf("ERROR HANDLING: i2c transaction failed\n");
    }

    usleep(10000);
    system("i2cdump -y 1 0x50 b");

    memset(buf, 0, sizeof(buf));
    ret = drv_i2c_read(adapter_nr, i2c_addr, reg, buf, sizeof(buf));
    if (ret < 0) {
        printf("ERROR HANDLING: i2c transaction failed\n");
    }
    buf[sizeof(buf) - 1] = '\0';
    printf("reg %d -> %s\n", reg, buf);

As above, although "I love you in my heart", the success of the experiment is necessary, as follows:

# ./at24c02a_app_io
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
10: ff 49 20 4c 6f 76 65 20 59 6f 75 20 6d 6f 72 65    .I Love You more
20: 20 74 68 61 6e 20 49 20 63 61 6e 20 73 61 79 21     than I can say!
30: 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
reg 17 -> I Love You more than I can say!

1.3 Summary

Using IO functions to read and write at24c02 is obviously the easiest to understand and relatively easy, but without the soul of device driven operation, it is somewhat nondescript. The complete code is as follows:

/* aarch64-linux-gnu-gcc at24c02a_app_io.c -o at24c02a_app_io -Wall */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

#define AT24C02_PAGE_SIZE 16 / * page size*/
#define AT24C02_PAGE_MASK (AT24C02_PAGE_SIZE - 1) / * page mask*/
#define AT24C02_PAGE_SHIFT 4 / * page shift*/

static int drv_i2c_open(int adapter_nr)
{
    int fd;
    char filename[20];

    /* open such as "/dev/i2c-%d" device node */
    sprintf(filename, "/dev/i2c-%d", adapter_nr);
    fd = open(filename, O_RDWR);
    if (fd >= 0) {
        return fd;
    }

    /* open such as "/dev/i2c/%d" device node */
    if (errno == ENOENT) {
        filename[8] = '/';
        fd = open(filename, O_RDWR);
    }

    return fd;
}

static int i2c_try_write(int fd, uint8_t * data, uint8_t data_len,
             uint8_t retry_times)
{
    int ret = 0;

    while (retry_times--) {
        ret = write(fd, data, data_len);
        if (ret < 0) {
            /* ENXIO Usually, the chip is busy and cannot respond to external operations */
            if (ENXIO == errno) {
                usleep(1000);   /* Sleep for 1ms each time */
                continue;
            }

            perror("write");
            break;
        } else if (ret != data_len) {
            ret = -1;
            perror("write");
            break;
        }

        break;
    }

    return ret;
}

int drv_i2c_write8(const int adapter_nr, const uint8_t i2c_addr,
           const uint8_t reg_addr, const uint8_t data)
{
    int fd;
    int ret = 0;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        uint8_t cnt = 5;    /* retry count */
        uint8_t msg_payload[] = { reg_addr, data };

        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        ret = i2c_try_write(fd, msg_payload, sizeof(msg_payload), cnt);
        if (ret < 0)
            break;
    } while (0);

    close(fd);

    return ret;
}

int drv_i2c_read8(int adapter_nr, const uint8_t i2c_addr,
          const uint8_t reg_addr, uint8_t * const data)
{
    int fd;
    int ret = 0;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        if ((ret = write(fd, &reg_addr, 1)) != 1)
            break;

        if ((ret = read(fd, data, 1)) != 1)
            break;
    } while (0);

    close(fd);

    return ret;
}

int drv_i2c_write_bytes(const int adapter_nr, const uint8_t i2c_addr,
            const uint8_t reg_addr, uint8_t * const data,
            const uint8_t cnt)
{
    uint8_t i;
    int ret = 0;

    for (i = 0; i < cnt; i++) {
        ret = drv_i2c_write8(adapter_nr, i2c_addr,
                     reg_addr + i, *(data + i));
        if (ret < 0)
            break;
    }

    return ret;
}

int drv_i2c_read_bytes(int adapter_nr, const uint8_t i2c_addr,
               const uint8_t reg_addr, uint8_t * const data,
               const uint8_t cnt)
{
    uint8_t i;
    int ret = 0;

    for (i = 0; i < cnt; i++) {
        ret = drv_i2c_read8(adapter_nr, i2c_addr,
                    reg_addr + i, data + i);
        if (ret < 0)
            break;
    }

    return ret;
}

int drv_i2c_write(const int adapter_nr, const uint8_t i2c_addr,
          const uint8_t reg_addr, uint8_t * const data,
          const uint8_t data_len)
{
    int fd;
    int ret = 0;
    int loop;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    /* Calculate page spreads */
    loop = (reg_addr & AT24C02_PAGE_MASK) + data_len;
    loop = (loop + AT24C02_PAGE_MASK) / AT24C02_PAGE_SIZE;

    do {
        int i;

        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        for (i = 0; i < loop; i++) {
            int j;
            uint8_t cnt = 5;    /* retry count */
            uint8_t start_addr;
            uint8_t xfer_len;
            uint8_t xfer_cnt;
            uint8_t *xfer_offset;
            uint8_t transfer_data[AT24C02_PAGE_SIZE + 1];

            /* First time with reg_addr shall prevail, and the subsequent page start and end addresses shall prevail */
            if (0 == i) {
                start_addr = reg_addr;
            } else {
                start_addr = (reg_addr + i * AT24C02_PAGE_SIZE);
                start_addr &= ~AT24C02_PAGE_MASK;
            }

            /* Amount of data transferred */
            xfer_cnt = start_addr - reg_addr;

            /* Calculate the amount of data to be transmitted this time */
            xfer_len = AT24C02_PAGE_SIZE -
                (start_addr & AT24C02_PAGE_MASK);

            /* If the last time is less than one page, it shall be transmitted according to the actual situation */
            if ((data_len - xfer_cnt) < AT24C02_PAGE_SIZE)
                xfer_len = data_len - xfer_cnt;

            xfer_offset = data + xfer_cnt;

            /* Fill register address and data to be written */
            transfer_data[0] = start_addr;
            for (j = 1; j <= xfer_len; j++)
                transfer_data[j] = *(xfer_offset + j - 1);

            /* write */
            ret = i2c_try_write(fd, transfer_data,
                        xfer_len + 1, cnt);
            if (ret < 0)
                break;
        }
    } while (0);

    close(fd);

    return ret;
}

int drv_i2c_read(int adapter_nr, const uint8_t i2c_addr,
         const uint8_t reg_addr, uint8_t * const data,
         const uint8_t data_len)
{
    int fd;
    uint8_t addr = reg_addr;
    int ret = 0;

    if (NULL == data)
        return -EINVAL;

    ret = fd = drv_i2c_open(adapter_nr);
    if (ret < 0)
        return ret;

    do {
        if ((ret = ioctl(fd, I2C_SLAVE, i2c_addr)) < 0)
            break;

        if ((ret = write(fd, &addr, 1)) != 1)
            break;

        if ((ret = read(fd, data, data_len)) != data_len)
            ret = -EINVAL;
    } while (0);

    close(fd);

    return ret;
}

int main(void)
{
    int adapter_nr = 1; /* probably dynamically determined */
    int ret;
    uint8_t reg = 0x11; /* Device register to access */
    uint8_t i2c_addr = 0x50;    /* Device address to access */

    {
        uint8_t buf[] = "I Love You, Baby !";

        /* Recover the area to be written */
        system("i2ctransfer -y 1 w255@0x50 0x10 0xff=");
        system("i2ctransfer -y 1 w255@0x50 0x20 0xff=");

        ret = drv_i2c_write_bytes(adapter_nr, i2c_addr,
                      reg, buf, sizeof(buf));
        if (ret < 0) {
            printf("ERROR HANDLING: i2c transaction failed\n");
        }
        usleep(10000);

        system("i2cdump -y 1 0x50 b");
        memset(buf, 0, sizeof(buf));
        ret = drv_i2c_read_bytes(adapter_nr, i2c_addr, reg,
                     buf, sizeof(buf));
        if (ret < 0) {
            printf("ERROR HANDLING: i2c transaction failed\n");
        }
        buf[sizeof(buf) - 1] = '\0';

        printf("reg %d -> %s\n", reg, buf);
    }

    {
        uint8_t buf[] = "I Love You more than I can say!";

        /* Recover the area to be written */
        system("i2ctransfer -y 1 w255@0x50 0x10 0xff=");
        system("i2ctransfer -y 1 w255@0x50 0x20 0xff=");

        ret = drv_i2c_write(adapter_nr, i2c_addr, reg,
                    buf, sizeof(buf));
        if (ret < 0) {
            printf("ERROR HANDLING: i2c transaction failed\n");
        }

        usleep(10000);
        system("i2cdump -y 1 0x50 b");

        memset(buf, 0, sizeof(buf));
        ret = drv_i2c_read(adapter_nr, i2c_addr, reg, buf, sizeof(buf));
        if (ret < 0) {
            printf("ERROR HANDLING: i2c transaction failed\n");
        }
        buf[sizeof(buf) - 1] = '\0';
        printf("reg %d -> %s\n", reg, buf);
    }

    return 0;
}

email: MingruiZhou@outlook.com

Tags: C shell

Posted by BSlepkov on Mon, 30 May 2022 11:11:06 +0530