The Analog-To-Digital converter (ADC) transfers data over the I2C bus. The kernel module i2c_dev is used to interact with the physical interface. The currently loaded modules can be checked with the lsmod command. As it can be seen in i2c.c the C commands open(), close(), read(), write() and ioctl() are only used to interface the i2c-driver.

src/i2c.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/** opens the interface
 * \param device interface name e.g. "i2c-0" or "i2c-1"
 * \return file descriptor to the opened stream, or -1 in case of error
 */
int i2c_open(const char *device);


/** sets the slave address for which the following commands 
 * will operate on
 * \param fd file descriptor (of a previous i2c_open command)
 * \param addr i2c address usually in the range 1-127
 * \return 0 on success, -1 on error
 */
int i2c_select(int fd, int addr);


/** writes altogether 3 bytes to the i2c slave.
 * \param fd file descriptor (of a previous i2c_open command)
 * \param addr i2c address usually in the range 1-127
 * \param reg 1 byte of register address
 * \param data 2 bytes of data to be written
 * \return 0 on success, -1 on error
 */
int i2c_write(int fd, int addr, int reg, int data);


/** reads 2 bytes from the \a reg register of the i2c slave
 * \param fd file descriptor (of a previous i2c_open command)
 * \param addr i2c address usually in the range 1-127
 * \param reg 1 byte of register address to read from
 * \return 2 bytes of data read, or -1 on error
 */
int i2c_read(int fd, int addr, int reg);

or here the implementation

src/i2c.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int i2c_open(const char *device) 
{
    int fd;
    if ((fd = open (device, O_RDWR)) < 0){
        logErrDate("%s: Couldn't open %s!"
            "Check whether it exists and if you have permisson!\n",__func__,device);
        return -1;
    }
    return fd;
}

int i2c_select(int fd, int addr)
{
    if (ioctl (fd, I2C_SLAVE, addr) < 0){ // or I2C_SLAVE_FORCE
        logErrDate("%s: Couldn't find device %02x on address!\n",__func__, addr);
        return -1;
    }
    return 0;
}

int i2c_write(int fd, int addr, int reg, int data)
{
    uint8_t buf[3];

    i2c_select(fd, addr);

    buf[0] = reg & 0xFF;
    buf[1] = (data & 0xFF00) >> 8;
    buf[2] = data & 0x00FF;
    if (write(fd, buf, 3) != 3) {
        logErrDate("%s: failed to write to register %02X!\n",__func__,reg);
        return -1;
    }
    return 0;
}

int i2c_read(int fd, int addr, int reg)
{
    uint8_t buf[2];

    i2c_select(fd, addr);

    buf[0] = reg & 0xFF;
    if (write(fd, buf, 1) != 1) {
        logErrDate("%s: failed to write to register %02X!\n",__func__,reg);
        return -1;
    }
    if (read(fd, buf, 2) != 2) {
        logErrDate("%s: failed to read from register %02X!\n",__func__,reg);
        return -1;
    }
    return (int16_t)buf[0]*256 + (uint16_t)buf[1];
}

The main program starts a 2nd thread, this is the datastore thread. While the main thread carries out the high frequency (currently up to 500Hz) sampling, the save-thread will be notified once a minute, and writes the data from the buffer to the disk (already in a compressed binary file format). Thus the main thread has to have high priority, the datastore thread low/normal. The thread will be created and initialized as following:

src/main.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
...
rc=pthread_attr_init(&p_attr);
    PthreadCheck("pthread attribute init check", rc);
rc=pthread_attr_setdetachstate(&p_attr, PTHREAD_CREATE_DETACHED);
    PthreadCheck("pthread attribute setdetachstate check", rc);
rc = pthread_create(&p_thread, &p_attr, thread_sampling, NULL);
    PthreadCheck("pthread create check", rc);


if (setpriority(which, pid, -15) != 0){
    perror("set priority error");
    exit_all(-1);
}

struct sched_param param;
param.sched_priority = 50;
if (sched_setscheduler(0, SCHED_FIFO, &param) != 0) {
    perror("sched setscheduler error");
    exit_all(-1);  
}
...

The datastore thread is located in save.c. The application is started as root! (Due to permission requirement for i2c operations) However, the datastore thread which writes files to the disk, is running as normal user, in this case pi.

src/save.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
...
void *thread_datastore(void * p)
{

    int rc;
    long tpid = (long)syscall(SYS_gettid);
    printf("data-storage thread started with pid: %ld\n",tpid);

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set,SIGALRM);
    rc = pthread_sigmask(SIG_SETMASK, &set, NULL);
        PthreadCheck("pthread_sigmask", rc);

    uid_t uid;
    gid_t gid;
    get_uid_gid(file_user, &uid, &gid);
    setfsuid(uid);
    setfsgid(gid);



    pthread_mutex_lock(&a_mutex);
    while (done==0) {
        pthread_cond_wait(&got_request, &a_mutex);
        pthread_mutex_unlock(&a_mutex);
        rc = write_netcdf();
        if (rc < 0){
            logErrDate("%s: severe error while writing netcdf file\nExit\n",__func__);
            exit(EXIT_FAILURE);
        }

        if (auto_pga && pga_uf){    //write new pga in ini file 
            update_ini_file(ini_name);
            //printf("New PGA written in %s!\n",ini_name);
            pga_uf = 0; //clear update flag
        }
        pthread_mutex_lock(&a_mutex); // ????????

    }
    pthread_mutex_unlock(&a_mutex);
    pthread_exit(NULL);
}
...

Side notes

Some explanation what preemption means: realtime preempt.

High resolution timers without softirq: explained at the bottom of the page



Comments

comments powered by Disqus