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の記事