CCWO Embedded Space

CCWOの日々の開発を発信するブログ

PIC 温湿度センサー SHT-11

PICでセンシリオンのSHT-11を読んでみました。上位互換のSHT-35など出ているのでなかなか使う人はいないと思いますが、自分メモ用に。

SHT-11はI2Cに似た通信方法を採用しているので、I2Cのモジュールは使えません。なので、ソフトウェアI2Cを書く要領で通信を書いてみました。

はじめは後閑さんだったり、ネットに落ちてるサンプルを使ってみたのですが、ことごとく読めなかったので、結局公式のサンプルをPIC用に書き換えてようやっと使うことができました。

大変だったのは、
・PICのGPIO入出力の切り替え
・wait時間の設定
・オシロで通信を確認しまくるの(ロジアナだと厳密にはプロトコルが違うので読めない)

というわけでメモ用にプログラムを

SHT11.h

/* 
 * File:   SHT11.h
 * Author: ccr
 *
 * Created on October 7, 2017, 6:14 PM
 */

#ifndef SHT11_H
#define	SHT11_H

#include <stdint.h>

// MODE
enum {
    TEMP, HUMI
};

// prototype definitions
void s_trans_start(void); // generates a transmission start
void s_connection_reset(void); // communication reset: DATA-line=1 and at least 9 SCK cycles followed by transstart
uint8_t s_soft_reset(void); // resets the sensor by a softreset
uint8_t s_write_byte(uint8_t value); // writes a byte on the Sensibus and checks the acknowledge
uint8_t s_read_byte(uint8_t ack); // read a byte from the Sensibus and gives an acknowledge in case of "ack=1"
uint8_t s_measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode); // makes a measurement (humidity/tempearture) with checksum


#ifdef	__cplusplus
extern "C" {
#endif




#ifdef	__cplusplus
}
#endif

#endif	/* SHT11_H */

SHT11.c

#include "SHT11.h"
#define FCY 16000000UL   // FOSC/2=32MHz/2=16MHz
#include <libpic30.h>
#include "mcc_generated_files/mcc.h"

// PIN DEFINE
#define SCK         LATBbits.LATB8      // 1: High, 0: Low
#define SDA         LATBbits.LATB9      // 1: High, 0: Low
#define TRIS_SCK    TRISBbits.TRISB8    // 1: Input, 0: Output
#define TRIS_SDA    TRISBbits.TRISB9    // 1: Input, 0: Output
#define vSCK        PORTBbits.RB8       // Read SCK Value
#define vSDA        PORTBbits.RB9       // Read SDA Value

// ACK
#define noACK   0
#define ACK     1

// Register                    adr command r/w
#define STATUS_REG_W 0x06   // 000 0011    0
#define STATUS_REG_R 0x07   // 000 0011    1
#define MEASURE_TEMP 0x03   // 000 0001    1
#define MEASURE_HUMI 0x05   // 000 0010    1
#define RESET        0x1e   // 000 1111    0

void s_trans_start(void) {
    TRIS_SDA = 0;   // SDA output mode
    SDA = 1;
    SCK = 0;        // initial state
    __delay_us(1);
    SCK = 1;
    __delay_us(1);
    SDA = 0;
    __delay_us(1);
    SCK = 0;
    __delay_us(3);
    SCK = 1;
    __delay_us(1);
    SDA = 1;
    __delay_us(1);
    SCK = 0;

    return;
}

void s_connection_reset(void) {
    uint8_t i;
    TRIS_SDA = 0; // SDA output mode
    SDA = 1;
    SCK = 0;
    for (i = 0; i < 9; i++) {
        __delay_us(1);
        SCK = 1;
        __delay_us(1);
        SCK = 0;
    }
    s_trans_start();

    return;
}

uint8_t s_soft_reset(void) {
    uint8_t error = 0;
    s_connection_reset();
    error += s_write_byte(RESET);

    return error;
}

uint8_t s_write_byte(uint8_t value) {
    uint8_t pos;
    uint8_t mask;
    uint8_t error = 0;

    TRIS_SDA = 0;       // SDA output mode
    mask = 0x80;        // set bit position
    for (pos = 0; pos < 8; pos++) {
        SCK = 0;
        SDA = (mask & value) ? 1 : 0; // write to SENSI-BUS
        __delay_us(1);  // observe set up time
        SCK = 1;        // clk for SENSI-BUS
        __delay_us(5);  // pulsewidth approx. 5 us
        SCK = 0;
        __delay_us(1);  // observe hold time
        mask = mask >> 1;
    }
    /* ACK Receive */
    SDA = 1;        // release data line
    TRIS_SDA = 1;   // SDA input mode
    __delay_us(1);  // observe set up time
    SCK = 1;        // clk #9 for ack
    error = vSDA;   // check ack (DATA will be pulled down by SHT11)
    SCK = 0;

    return error;
}

uint8_t s_read_byte(uint8_t ack) {
    uint8_t pos;
    uint8_t mask;
    uint8_t value = 0;

    TRIS_SDA = 1;                   // SDA input mode
    mask = 0x80;
    for (pos = 0; pos < 8; pos++) {
        SCK = 1;                    // clk for SENSI-BUS
        if (vSDA == 1)
            value = value | mask;   // read bit
        SCK = 0;
        mask = mask >> 1;
    }
    TRIS_SDA = 0;       // SDA output mode
    SDA = !ack;         // in case of "ack==1" pull down DATA-Line
    __delay_us(1);
    SCK = 1;            // clk #9 for ack
    __delay_us(5);      // pulswith approx. 5 us
    SCK = 0;
    __delay_us(1);      // observe hold time
    SDA = 1;            // release data line
    TRIS_SDA = 1;       // SDA input mode

    return value;
}

uint8_t s_measure(uint8_t *p_value, uint8_t *p_checksum, uint8_t mode) {
    uint8_t error = 0;

    s_trans_start();        // transmission start
    __delay_us(5);
    switch (mode) {         // send command to sensor
        case TEMP:
        {
            error += s_write_byte(MEASURE_TEMP);
            break;
        }
        case HUMI:
        {
            error += s_write_byte(MEASURE_HUMI);
            break;
        }
    }
    __delay_ms(100);
    while (vSDA){
    };                       // wait until sensor has fished the measurement
    if (vSDA) error += 1;               // or timeout (~2 sec.) is reached
    *(p_value) = s_read_byte(ACK);      // read the first byte (MSB)
    *(p_value + 1) = s_read_byte(ACK);  // read the second byte (LSB)
    *p_checksum = s_read_byte(noACK);   // read checksum

    return error;
}

FCY:16MHzで使えると思います。違うクロックで使用する際は、もしかするとdelayを少し変えないと行けないかもです。MCCの使用を前提にしていますが、MCC依存の関数を使っていないので、MCCを使わないプロジェクトでも大丈夫です。

2017/11/01の記事