因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
* 蒋晓岗
* 2020.03.06
*/
#include
#include
#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);
}