JHM3000體溫傳感器驅動

因2020疫情影響,國內突然就冒出來一堆做體溫計的廠商。。。

硬件平臺:nRF52832 + JHM3000

JHM3000是個單線通信的體溫傳感器芯片,線性度較好,校準後精度可達0.1度。

主要問題是通信是單IO輸出,芯片上電後,IO就會不停地輸出脈衝信號,需要MCU計算脈寬來解碼數據。

每個採樣週期爲:2.8ms間隔 + 6ms數據傳輸

相鄰兩個高脈寬之比決定數據類型,START=3:3, BIT0=5:1, BIT1=1:5
數據樣本:91,3,3,1,6,1,5,5,1,1,5,5,1,1,5,1,5,6,1,1,5,6,1,1,5,5,2,1,5,5,1,91,3,4,...
每次採樣共1個START+14個BIT數據:S+BBBBBBBBBBBBB

解碼數據的時候,需要先找到START數據,然後纔開始解析採樣數據。

下面是主要源碼:

/*
* JHM3000 體溫傳感器驅動程序
* 每次採樣2.8ms + 數據傳輸6ms
* 單線IO輸出,相鄰兩個高脈寬之比決定數據,START=3:3, BIT0=5:1, BIT1=1:5
* 數據樣本:61,4,3,1,5,1,6,5,1,1,6,5,1,1,5,1,5,1,5,6,1,1,5,6,1,5,1,1,6,1,5,
*           91,3,3,1,6,1,5,5,1,1,5,5,1,1,5,1,5,6,1,1,5,6,1,1,5,5,2,1,5,5,1,
*           91,3,4,...
* 每次採樣共1個START+14個BIT數據:S+BBBBBBBBBBBBB
* 蔣曉崗<[email protected]>
* 2020.03.06
*/
#include <stdint.h>
#include <stdbool.h>
#include "log.h"
#include "bsp.h"
#include "sdk.h"

//判斷數據類型,錯誤返回0, 成功返回1,S位bit=-1,BIT10,BIT1返回1
static int decode_one_bit(uint32_t t1, uint32_t t2, int *bit)
{
	uint32_t diff;
	if((t1 + t2) > 10)
	{
		LOG("t1=%d, t2=%d\r\n", t1, t2);
		return 0;
	}
	if(t1 > t2)
	{
		diff = t1 - t2;
		if(diff > t2)
		{
			*bit = 0;
			return 1;
		}
	}
	if(t2 > t1)
	{
		diff = t2 - t1;
		if(diff > t1)
		{
			*bit = 1;
			return 1;
		}
	}
	*bit = -1;
	return 1;
}

//等待管腳電平跳變
static int wait_pin_state(int state, uint32_t timeout)
{
	uint32_t diff;
	uint32_t tick;
	diff = 0;
	tick = app_timer_cnt_get();
	timeout = timeout << 5; //1毫秒約等於32個時鐘
	while(diff < timeout)
	{
		if(nrf_gpio_pin_read(HM3000_PIN_DATA) == state)
		{
			return 1;
		}
		else
		{
			app_timer_cnt_diff_compute(app_timer_cnt_get(), tick, &diff);
		}
	}
	return 0;
}

//計算高電平時長
static int calc_pulse_width(uint32_t *pw)
{
	int ret;
	uint32_t diff;
	uint32_t tick;
	ret = wait_pin_state(1, 5);
	if(!ret)
	{
		return 0;
	}
	tick = app_timer_cnt_get();
	ret = wait_pin_state(0, 5);
	if(!ret)
	{
		return 0;
	}
	app_timer_cnt_diff_compute(app_timer_cnt_get(), tick, &diff);
	*pw = diff;
	return 1;
}

//等待高電平結束
static int wait_for_idle(void)
{
	return wait_pin_state(0, 5);
}

//等待ADC採樣開始
//ADC採樣有2.5ms高電平
static int wait_for_adc(void)
{
	int i;
	int ret;
	uint32_t pw;
	for(i=0; i<32; i++)
	{
		ret = calc_pulse_width(&pw);
		if(!ret)
		{
			return 0;
		}
		if(pw > 30)
		{
			return 1;
		}
	}
	return 0;
}

//接收一位數據
static int recv_one_bit(int *bit)
{
	int ret;
	uint32_t t1;
	uint32_t t2;
	ret = calc_pulse_width(&t1);
	if(!ret)
	{
		return 0;
	}
	ret = calc_pulse_width(&t2);
	if(!ret)
	{
		return 0;
	}
	return decode_one_bit(t1, t2, bit);
}

//接收採樣起始信號
static int recv_start_bit(void)
{
	int ret;
	int bit;
	ret = recv_one_bit(&bit);
	if(!ret)
	{
		return 0;
	}
	return bit == -1;
}

//接收採樣數據
//數據=1位符號+13位數據
static int recv_adc_data(int *data)
{
	int i;
	int bit;
	int ret;
	int16_t tmp;
	tmp = 0;
	for(i=0; i<14; i++)
	{
		ret = recv_one_bit(&bit);
		if(!ret)
		{
			LOG("drv_hm3000: recv bit%d failed!\r\n", i);
			return 0;
		}
		if(bit < 0)
		{
			LOG("drv_hm3000: recv bit%d invalid!\r\n", i);
			return 0;
		}
		if(bit)
		{
			tmp |= 1 << (15 - i);
		}
	}
	//LOG("drv_hm3000: raw=%d\r\n", tmp);
	*data = tmp >> 2;
	return 1;
}

//採樣數據
static int sample_data(int *data)
{
	int ret;
	ret = wait_for_idle();
	if(!ret)
	{
		LOG("drv_hm3000: wait idle timeout!\r\n");
		return 0;
	}
	ret = wait_for_adc();
	if(!ret)
	{
		LOG("drv_hm3000: wait adc timeout!\r\n");
		return 0;
	}
	ret = recv_start_bit();
	if(!ret)
	{
		LOG("drv_hm3000: recv start failed!\r\n");
		return 0;
	}
	ret = recv_adc_data(data);
	if(!ret)
	{
		LOG("drv_hm3000: recv data failed!\r\n");
		return 0;
	}
	return ret;
}

#if 0
//調試數據
static void dump_data(void)
{
	int i;
	static uint32_t time[100];
	for(i=0; i<64; i++)
	{
		calc_pulse_width(&time[i]);
	}
	for(i=0; i<64; i++)
	{
		LOG("%d,", time[i]);
	}
	LOG("\r\n");
}
#endif

//讀取採樣數據
int drv_hm3000_read(int *data)
{
	//dump_data();
	//return 0;
	
	int ret;
	//uint8_t sr;
	//sd_nvic_critical_region_enter(&sr);
	ret = sample_data(data);
	//sd_nvic_critical_region_exit(sr);
	return ret;
}

//打開芯片電源
void drv_hm3000_enable(void)
{
	nrf_gpio_pin_write(HM3000_PIN_PWR, 1);
}

//關閉芯片電源
void drv_hm3000_disable(void)
{
	nrf_gpio_pin_write(HM3000_PIN_PWR, 0);
}

//初始化驅動
void drv_hm3000_init(void)
{
	nrf_gpio_cfg_output(HM3000_PIN_PWR);
    nrf_gpio_cfg_input(HM3000_PIN_DATA, NRF_GPIO_PIN_NOPULL);
	nrf_gpio_pin_write(HM3000_PIN_PWR, 0);
}

 

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