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);
}

 

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