Sıcaklık Değerinin Yazılımsal Olarak Elde Edilmesi

Bu bölümde, daha önce Linux Altında I2C İşlemleri konusunda bahsettiğimiz yöntemlere ilişkin birer örnek vereceğiz.

Örnek kodları derlerken, i2c-tools içinden çıkan i2c-dev.h başlık dosyasına ihtiyacımız olacak, bu sebeple derleme işleminde bu başlık dosyasının bulunduğu dizini de derleyiciye göstermeliyiz.

Her 3 örnekte de MCP9808 adresi, sıcaklık değerini okuyacağımız yazmaç adresi ve sıcaklık çözünürlük değeri aşağıdaki sembolik sabitlerle gösterilmiştir.

#define MCP9808_ADDR    0x18    // MCP9808 I2C adresi
#define TEMP_REG_ADDR   0x05    // Ortam sıcaklık yazmaç adresi
#define RESOLUTION      0.0625  // Sıcaklık çözünürlüğü

3 örneğimizde yalnızca sıcaklık değerinin okunma kısımları farklıdır. Sıcaklık yazmacındaki değer elde edildikten sonraki yorumlama kısmı ise değişmemektedir. Daha önce i2cget ile elde ettiğimiz değeri inceleyerek sıcaklık değerine ulaşmıştık. Benzer işlemi burada C kodu ile yapacağız.

Her 3 uygulamada da temelde yapılan işlemler, MCP9808 ile iletişim kurmak, sonrasında sıcaklık yazmacının adresini göndermek ve periyodik olarak sıcaklık yazmacının değerini okuyarak yorumlamak şeklindedir.

İlk olarak sıcaklık yazmaç değerinin t isimli int bir değişkene çektiğimizi varsayalım. Bu aşamadan sonraki işlemler aşağıdaki gibi olacaktır.

int t = SICAKLIK_YAZMAÇ_DEĞERİ;
double temp;
temp = t & 0x0FFF;
temp *= RESOLUTION;
if (t & 0x1000) {
    temp -= 256;
}
printf("Sıcaklık: %.2f C\n", temp);

Bu kod üzerinden tekrar eski bilgilerimizi tekrarlayalım. Sıcaklık yazmacındaki yüksek anlamlı 4 bit'in sıcaklığın mutlak değeriyle alakalı olmadığını hatırlayın. 15., 14. ve 13. bitler alert bitleri, 12. bit ise işaret bitidir. Bu sebeple başlangıçta bu bitleri göz ardı edebiliriz. Aşağıdaki kod bu duruma ilişkindir.

temp = t & 0x0FFF;

Bu değer daha sonra sıcaklık ölçüm çözünürlüğüyle çarpılarak, mutlak sıcaklık değerine erişilir.

temp *= RESOLUTION;

Bu aşamadan sonra sıcaklığın polaritesi için işaret bitine (12. bit) bakılmalıdır. 0x1000 ile bitsel and işlemi sonucu 1 çıkıyorsa sayı negatif demektir. Sıcaklığın sayının ikiye tümleyeni şeklinde tutulduğunu daha önce söylemiştik. Bu sebeple bu durumda sayıdan 256 değeri çıkarılmaktadır.

256 sayısının nereden geldiğini daha iyi anlamak için aşağıdaki nota bakabilirsiniz.

Not: Bir sayının 2'ye tümleyeni alınırken, ilk önce 1'e tümleyenin alındığını daha sonra elde edilen sonucun 1 ile toplandığını hatırlayınız. 1'e tümleme işlemi için sayının 1 olan bitleri 0, 0 olanlar ise 1 yapılmalıdır.

Bir byte sınırlarındaki bir sayı için konuşacak olursak. Sayının bitlerinin ters çevrilmesi neticesinde elde edilen sayı, sayının kendisinin 255 sayısı ile arasındaki farkı vermektedir. Çünkü sayının kendisiyle bitlerinin ters çevrilmiş halinin toplamı 255 sayısını verecektir.

Sonrasındaki 1 ile toplama işlemi ise sayının 255 ile arasındaki farktan 1 fazlasının elde edilmesine neden olacaktır. Aynı işlem sayının 256 sayısı ile arasındaki fark ile de elde edilebilir.

if (t & 0x1000) {
    temp -= 256;
}

Bu aşamadan sonra temp değişkeni Celsius derece cinsinden sıcaklık değerini göstermektedir.

Örnek kodları sırasıyla i2c_1.c, i2c_2.c ve i2c_3.c adlarıyla saklayıp, çapraz derleyicinize i2c-dev.h dosyasının yerini göstererek derleyebilirsiniz. Bizim sistemimiz için geçerli derleme aşağıdaki gibidir.

$ arm-none-linux-gnueabi-gcc -o i2c_1 i2c_1.c -I i2c-tools-3.1.2/include

Her 3 örnekte de sıcaklık 1 saniye aralıklarla okunarak konsola basılmaktadır.

Yazma ve Okuma İşlemlerinde read, write Sistem Çağrılarının Kullanılması

Örnek kodda, I2C üzerinden okuma işlemleri read/write sistem çağrılarıyla yapılmaktadır.

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

#define MCP9808_ADDR    0x18    // MCP9808 I2C adresi
#define TEMP_REG_ADDR   0x05    // Ortam sıcaklık yazmaç adresi
#define RESOLUTION      0.0625  // Sıcaklık çözünürlüğü

void main()
{
    int i2c_fd;
    char *bus = "/dev/i2c-2";
    long funcs;
    int rc;

    if ((i2c_fd = open(bus, O_RDWR)) < 0)
    {
        printf("Failed to open the bus. \n");
        exit(1);
    }

    // İletişim kurulacak aygıtın adreslenmesi
    ioctl(i2c_fd, I2C_SLAVE, MCP9808_ADDR);

    // İçeriği okunacak yazmacın adreslenmesi
    char reg[1] = {TEMP_REG_ADDR};
    write(i2c_fd, reg, 1);
    char data[2] = {0};

    do {
        // Sıcaklık yazmaç değerinin okunması
        if(read(i2c_fd, data, 2) != 2) {
            printf("read error \n");
        }
        else {
            double temp;
            int t = (data[0] << 8) + data[1];
            temp = t & 0x0FFF;
            temp *= RESOLUTION;
            if (t & 0x1000) {
                temp -= 256;
            }
            printf("Sıcaklık: %.2f C\n", temp);
        }
        sleep(1);
    } while(1);
}

Yazma ve Okuma İşlemlerinde ioctl Çağrılarının Kullanılması

Örnek kodda, I2C üzerinden okuma işlemleri ioctl çağrısı ile yapılmıştır. Bu yöntemde okuma ve yazma işlemleri, hattın kullanımını bırakmaksızın, combined bir şekilde yapılabilmektedir.

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

#define MCP9808_ADDR    0x18
#define TEMP_REG_ADDR   0x05
#define RESOLUTION      0.0625

int i2c_fd;

int i2c_read16(int addr, int reg) {
    struct i2c_rdwr_ioctl_data msgset;
    struct i2c_msg iomsgs[2];
    char buf[1], rbuf[2];
    int rc;

    buf[0] = (char) reg;

    iomsgs[0].addr = iomsgs[1].addr = (unsigned) addr;
    iomsgs[0].flags = 0;        /* Yazma */
    iomsgs[0].buf = buf;
    iomsgs[0].len = 1;

    iomsgs[1].flags = I2C_M_RD; /* Okuma */
    iomsgs[1].buf = rbuf;
    iomsgs[1].len = 2;

    msgset.msgs = iomsgs;
    msgset.nmsgs = 2;

    if ( (rc = ioctl(i2c_fd, I2C_RDWR, &msgset)) < 0 )
        return -1;
    return (rbuf[0] << 8) | rbuf[1];
}

void main()
{
    char *bus = "/dev/i2c-2";
    double temp;
    int t;
    if ((i2c_fd = open(bus, O_RDWR)) < 0)
    {
        printf("Failed to open the bus. \n");
        exit(1);
    }
    do {
        t = i2c_read16(MCP9808_ADDR, TEMP_REG_ADDR);
        if (t < 0) {
            printf("read error \n");
             continue;
        }
        temp = t & 0x0FFF;
        temp *= RESOLUTION;
        if (t & 0x1000) {
            temp -= 256;
        }
        printf("Sıcaklık: %.2f C\n", temp);
        sleep(1);
    } while (1);
}

Yazma ve Okuma İşlemlerinde i2c-dev.h Fonksiyonlarının Kullanılması

Bu yöntemde, i2c-tools fonksiyonlarını kullandık. Gerekli fonksiyonlar i2c-dev.h başlık dosyasında inline olarak tanımlıdır.

Örnekteki bir noktaya dikkatinizi çekmek istiyoruz. i2c_smbus_read_word_data ile okunan değerin yüksek ve düşük anlamlı byte'ları swap edilmiştir.

rt = i2c_smbus_read_word_data(i2c_fd, TEMP_REG_ADDR);
t = rt << 8 | rt >> 8;

Daha önce benzer işlemi i2cget ile elde ettiğimiz değer için de yaptığımızı hatırlayınız. i2c_smbus_read_word_data, ilk okuduğu değeri düşük anlamlı olarak ifade etmektedir. Fakat MCP9808 üzerinden ilk olarak yüksek anlamlı byte gönderilmektedir. Bu sebeple, sıcaklık değeri yorunlanmadan önce, bir swap işlemi yapılmaktadır.

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

#define MCP9808_ADDR    0x18
#define TEMP_REG_ADDR   0x05
#define RESOLUTION      0.0625

void main()
{
    int i2c_fd;
    char *bus = "/dev/i2c-2";
    double temp;
    int t;
    int rt;

    if ((i2c_fd = open(bus, O_RDWR)) < 0)
    {
        printf("Failed to open the bus. \n");
        exit(1);
    }

    ioctl(i2c_fd, I2C_SLAVE, MCP9808_ADDR);

    do {
        rt = i2c_smbus_read_word_data(i2c_fd, TEMP_REG_ADDR);
        t = rt << 8 | rt >> 8;
        temp = t & 0x0FFF;
        temp *= RESOLUTION;
        if (t & 0x1000){
            temp -= 256;
        }
        printf("Sıcaklık: %.2f C\n", temp);
        sleep(1);
    } while(1);
}

results matching ""

    No results matching ""