[嵌入式開發模塊]SHT30/20 溫溼度傳感器 驅動模塊

前言

幹活中用到了SHT30,其實是對應的轉串口板子,寫了相應的驅動程序,放出來。

好像SHT20和30幾乎是一樣的,所以不用糾結這個。

這個模塊本身的協議比較簡單,所以模塊的驅動也很小巧。

驅動被設計爲擁有指令器和接收機兩個部分,完全被動方式運行,與具體的平臺解耦。

SHT30簡介

概述

SHT20是由瑞士推出的數字溫溼度傳感器,基於領先世界的CMOSens數字傳感技術,具有極高的可靠性和卓越的長期穩定性。全量程標定,兩線數字接口,可與單片機直接相連,大大縮短研發時間、簡化外圍電路並降低費用。此外,體積微小、響應迅速、低能耗、可浸沒、抗干擾能力強、溫溼一體,兼有露點測量,性價比高,使該產品能夠適於多種場合的應用。

通信協議

(1) 串口通信參數

電平:TTL
波特率:115200
停止位:1
校驗位:0

(2)輸出格式

輸出字符串格式: Temp = XX.XXX Humi = XX.XXX\r\n

(3)控制指令:下面指令需要發送兩次,第一次使模塊由工作模式進入到配置模式,第二次發送纔是設置模塊。
設置回傳速度:

設置報警值(這個驅動沒有實現):

驅動文件

SHT30Driver.h

/*
*******************************************************************************************
*
*                                    SHT30 DRIVER MODULE
*                                       SHT30驅動模塊
*
* File : SHT30Driver.h
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date : 2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : The SHT30 driver is divided into two part, i.e. cmder and recver. The cmder is for
*          sending the SHT30 command. The recver is for resolving data from the SHT30.
********************************************************************************************
*/

#ifndef   SHT30DRIVER_H
#define   SHT30DRIVER_H

/*
******************************************************************************************
*                                    INCLUDE
******************************************************************************************
*/

#include <stdint.h>

/*
******************************************************************************************
*                                    DEBUG CONFIGURATION
******************************************************************************************
*/

// to enable debug messages in this module
// #define SHT30_DEBUG

/*
******************************************************************************************
*                                       TYPE DEFINE
******************************************************************************************
*/

typedef struct SHT30STRUCT_DATA{
  float temp;   // °C
  float humi;   // percent
} SHT30Data;

typedef void (* SHT30FUNC_DATA)(SHT30Data data);

typedef enum {
  SHT30SR_10Hz ,
  SHT30SR_5Hz  ,
  SHT30SR_2Hz  ,
  SHT30SR_1Hz  ,
  SHT30SR_0d5Hz,  // 0.5Hz
  SHT30SR_0d2Hz,  // 0.2Hz
  SHT30SR_0d1Hz,  // 0.1Hz
}SHT30SampleRate;
/*
************************************************************************************
*                                    INTERFACES
************************************************************************************
*/

void SHT30Cmder_Init(void (* outChannel)(uint8_t *buf, uint16_t len));
void SHT30Cmder_Destroy(void);
void SHT30Cmder_setSampleRate(SHT30SampleRate value);

void SHT30Recver_Init(SHT30FUNC_DATA onRecvData);
// Call it to modify the call-back function.
void SHT30Recver_RegisterCallback(SHT30FUNC_DATA onRecvData);
void SHT30Recver_CleanupBuffer(void);
// Feed the receiver every byte received so that receiver can notify user
// the resolved data for each frame.
void SHT30Recver_Feed(uint8_t nextByte);
void SHT30Recver_Destroy(void);

#endif  // of SHT30DRIVER_H

SHT30Recver.c

/*
*******************************************************************************************
*
*                                   SHT30 RECEIVER MODULE
*                                   SHT30驅動模塊 - 接收機
*
* File : SHT30Recver.c
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date : 2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : 
********************************************************************************************
*/

/*
*********************************************************************************************
*                                       INCLUDES
*********************************************************************************************
*/

#include <string.h>
#include <ctype.h>
#include "SHT30Driver.h"
#include "RxMac.h"

#ifndef SHT30_DEBUG
#undef _DEBUG
#endif

#include "DebugMsg.h"

/*
*********************************************************************************************
*                                       LOCAL FUNCTION
*********************************************************************************************
*/

#define ARRAYSIZE(arr)  (sizeof(arr)/ sizeof(arr[0]))
static void _onFlushed(RxMac sender,RxMacPtr buf,uint16_t len,RxState state,
                           RxFlag HorU,RxFlag Ender);

/*
*********************************************************************************************
*                                       LOCAL VARIABLE
*********************************************************************************************
*/

static const char * _str_SHT30Recver = "SHT30Recver";

static SHT30FUNC_DATA _onRecvData;
static const uint8_t _flag_header[] = {(uint8_t)'T', (uint8_t)'e', (uint8_t)'m', (uint8_t)'p', 
                                       (uint8_t)' ', (uint8_t)'=', (uint8_t)' '};
static const uint8_t _flag_ender[] = {(uint8_t)'\r', (uint8_t)'\n'};
static const RXFLAG_STRUCT _flags[] = {
  {_flag_header, sizeof(_flag_header), RXFLAG_OPTION_STRONG_HEADER},
  {_flag_ender , sizeof(_flag_ender) , RXFLAG_OPTION_STRONG_ENDER }
};
static RxMac _mac;
static uint8_t _macBuf[36];

/*
*********************************************************************************************
*                                 INTERFACE IMPLEMENTATION
*********************************************************************************************
*/

void SHT30Recver_Init(SHT30FUNC_DATA onRecvData){
  _onRecvData = onRecvData;
  _mac = RxMac_Create(_flags, ARRAYSIZE(_flags), _macBuf, sizeof(_macBuf) - 1,
                       NULL, NULL, _onFlushed);
  if(_mac == NULL)
    for(;;)
      ;
}

void SHT30Recver_Destroy(){
  _onRecvData = NULL;
  RxMac_Destroy(_mac);
}

void SHT30Recver_RegisterCallback(SHT30FUNC_DATA onRecvData){
  _onRecvData = onRecvData;
}

void SHT30Recver_CleanupBuffer(void){
  RxMac_ResetState(_mac);
}

void SHT30Recver_Feed(uint8_t nextByte){
  RxMac_FeedData(_mac, nextByte);
}

/*
*********************************************************************************************
*                               LOCAL FUNCTION IMPLEMENTATION
*********************************************************************************************
*/
static int _FloatFormatRight(const char *beg, const char *end){
  const char *p;
  if(end - beg <= 0)
    return 0;
  for(p = beg; end - p > 0; p++)
    if(!isdigit(*p) && *p != '-' && *p != '.')
      return 0;
  return 1;
}

static void _onFlushed(RxMac sender,RxMacPtr buf,uint16_t len,RxState state,
                       RxFlag HorU,RxFlag Ender){
  const char *p;
  SHT30Data data;
  if(_onRecvData == NULL || !state.headerFound || !state.enderFound)
    return;
  buf[len] = '\0';
  p = strstr((const char *)buf, "   Humi = ");
  if(p == NULL || !_FloatFormatRight((const char *)&buf[7], p) || 
    !_FloatFormatRight((const char *)&p[10], (const char *)buf + len - 2)){
    _dbg_printf2("%s: got corrupt frame-%s", _str_SHT30Recver, (const char *)buf);
    return;
  }
  
  data.temp  = (float)atof((const char *)&buf[7]);
  data.humi  = (float)atof((const char *)&p[10]);
  _dbg_printf3("%s: got data-temp(%.3f),humi(%.3f)\r\n", _str_SHT30Recver, data.temp, data.humi);
  _onRecvData(data);
}

SHT30Cmder.c

/*
*******************************************************************************************
*
*                                    SHT30 COMMANDER MODULE
*                                    SHT30驅動模塊 - 指令器
*
* File : SHT30Cmder.c
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date : 2020/04/29
* version: V1.0
* History: 2020/04/29 V1.0 the prototype
* Note   : Only implement the set sample rate command for now.
********************************************************************************************
*/

/*
*********************************************************************************************
*                                       INCLUDES
*********************************************************************************************
*/

#include <stdio.h>
#include "SHT30Driver.h"

/*
*********************************************************************************************
*                                       LOCAL VARIABLE
*********************************************************************************************
*/
static void (* _out)(uint8_t *buf, uint16_t len) = NULL;

/*
*********************************************************************************************
*                                 INTERFACE IMPLEMENTATION
*********************************************************************************************
*/

void SHT30Cmder_Init(void (* outChannel)(uint8_t *buf, uint16_t len)){
  _out = outChannel;
}

void SHT30Cmder_Destroy(){
  _out = NULL;
}

void SHT30Cmder_setSampleRate(SHT30SampleRate value){
  uint8_t buf[] = {0xFF, 0xAA, 0x09, 0x00, 0x00};
  buf[3] = (uint8_t)value;
  if(_out != NULL)
    _out(buf, sizeof(buf));
}

依賴

接收器的實現依賴於我寫的通用接收機:
通用接收狀態機模塊

至於DebugMsg.h則是我自己使用的調試信息的模塊:

#ifndef _DEBUG_MSG_H
#define _DEBUG_MSG_H
#include <stdio.h>
#ifdef _DEBUG
  #define _dbg_printf0(format)                   ((void)printf(format))
  #define _dbg_printf1(format,p1)                ((void)printf(format,p1))
  #define _dbg_printf2(format,p1,p2)             ((void)printf(format,p1,p2))
  #define _dbg_printf3(format,p1,p2,p3)          ((void)printf(format,p1,p2,p3))
  #define _dbg_printf4(format,p1,p2,p3,p4)       ((void)printf(format,p1,p2,p3,p4))
  #define _dbg_printf5(format,p1,p2,p3,p4,p5)    ((void)printf(format,p1,p2,p3,p4,p5))
  #define _dbg_printf6(format,p1,p2,p3,p4,p5,p6) ((void)printf(format,p1,p2,p3,p4,p5,p6))
  #define _dbg_printf7(format,p1,p2,p3,p4,p5,p6,p7) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7))
  #define _dbg_printf8(format,p1,p2,p3,p4,p5,p6,p7,p8) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7,p8))
  #define _dbg_printf9(format,p1,p2,p3,p4,p5,p6,p7,p8,p9) \
                ((void)printf(format,p1,p2,p3,p4,p5,p6,p7,p8,p9))
#else
  #define _dbg_printf0(format)
  #define _dbg_printf1(format,p1)
  #define _dbg_printf2(format,p1,p2)
  #define _dbg_printf3(format,p1,p2,p3)
  #define _dbg_printf4(format,p1,p2,p3,p4)
  #define _dbg_printf5(format,p1,p2,p3,p4,p5)
  #define _dbg_printf6(format,p1,p2,p3,p4,p5,p6)
  #define _dbg_printf7(format,p1,p2,p3,p4,p5,p6,p7)
  #define _dbg_printf8(format,p1,p2,p3,p4,p5,p6,p7,p8)
  #define _dbg_printf9(format,p1,p2,p3,p4,p5,p6,p7,p8,p9)
#endif

int dummyprintf(const char *format, ...);

#endif

這樣在項目中同時宏定義 _DEBUGSHT30_DEBUG 時纔會打印調試信息。不需要的話刪除相關語句就好。

想要了解相關技巧的話詳見:
C語言宏配置的各種奇淫技巧

使用示例

使用此模塊基本分幾步:

  1. 初始化模塊
  2. 如需要發送指令則實現並註冊信道給指令器;註冊回調函數給接收器,這樣接收器在發現完整的數據幀時就會通過回調函數進行通知。
  3. 不斷接收數據,並喂(Feed)給接收器;如需要發指令,隨時調用指令器的接口。

以下代碼示例了在上電後不斷讀取數據並進行解析(假設使用標準輸入輸出上的串口和SHT30通信):


#include "SHT30Driver.h"
#include <stdio.h>
#include <stdint.h>
……
// 收到數據的回調函數
static void onDataResolved(SHT30Data data);
// 發送信道
static void sendChannel(uint8_t *buf, uint16_t len);

void main(){
  uint8_t b;
  ……
  SHT30Recver_Init(onDataResolved);
  SHT30Cmder_Init(sendChannel);
  
  // 如果需要調整採樣率的話發送對應指令,注意要先發一次指令使進入配置模式的問題
  // SHT30Cmder_setSampleRate(SHT30SR_1Hz);
  
  for(;;){
    // 不斷得到下一個字節並餵給接收機
    // 接收機會通過回調函數實時傳遞解析出來的值
    b = (uint8_t)getchar();
    SHT30Recver_Feed(b);
  }
}

static void onDataResolved(SHT30Data data){
  // data中就是實時解析到的數據,具體怎麼使用是你的事
}

static void sendChannel(uint8_t *buf, uint16_t len){
  // 往標準輸出輸出這個字節
  while(len-- > 0)
    putchar((char)*buf++);
}

更新歷史

2020/06/07 放出V1.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章