lm91000——stm32 -气体传感器(氧气 硫化氢,氨气)
最近我做了一下气体传感器,遇到了一些问题,摸索前进。最后还是有些收获。总结分享一下。
第一部分 总体部分电路
LMP91000 传感器AFE 系统:用于低功耗化学感测应用的可配置AFE 稳压器
官方原理图
第二部分 芯片和程序
LMP91000 是一款可编程模拟前端 (AFE),适用于微功耗电化学感测应用。 它可提供传感器与微控制器之间的完整信号路径解决方案,此方案能够生成与电池电流成比例的输出电压。 LMP91000 的可编程性使它能够通过一个与多个离散解决方案相对的单一设计支持多种电化学传感器,例如,3 导线有毒气体传感器和 2 导线原电池型传感器。 LMP91000 支持 0.5nA/ppm 至 9500nA/ppm 范围内的气体灵敏度。 它还可实现 5µA 至 750µA 满量程电流范围的简单转换
这个芯片很强大,可以适配很多气体传感器。
根据不同的产期配置不同的跨阻放大器的电阻,实现放大电流
1.1 Fundamental Blocks of LMP91000
Transimpedance Amplifier — TIA provides an output voltage that is proportional to the cell current. TIA
provides seven programmable internal-gain resistors and allows the external-gain resistor to
connect to the LMP91000.
(Vref_div – Vout) / (RTIA) = Iwe (1)
Vout = (Vref_div) – (RTIA × Iwe) (2)
Input — The LMP91000 provides a 3-electrode solution — counter electrode (CE), reference electrode
(RE), working electrode (WE) (see Figure 4), as well as a 2-electrode solution — short the CE and
RE (see Figure 5).
Variable Bias — Variable bias provides the amount of bias voltage required by a biased gas sensor
between RE and WE. This bias voltage can be programmed to be 1% to 24% of the supply, or it
can be VREF. The bias can also be negative or positive depending on the type of sensing element.
Vref Divider — This is the voltage at the noninverting pin at TIA. This voltage can be programmed to be
either 20%, 50%, or 67% of the supply, or it can be VREF. The Vref divider provides the best use of
the full-scale input range of the analog-to-digital converter (ADC) and sufficient headroom for the
CE of the sensor to swing in case of sudden changes in the gas concentration.
• How to select the appropriate Vref divider:
– If the current at pin WE (Iwe) is flowing into the TIA, then the Vref divider should be set to 67%
of Vref.
– If Iwe is flowing out of the TIA, then the Vref divider should be set to 20% of Vref.
• Assume Vref_divider is set to 20% of Vref.
• Assume variable bias is set to 2% of Vref.
• Assume Vref = 4.1 V.
The Vref divider in that case would be 0.82 V. The noninverting input to A1 is 0.902 V,
which is 22% of Vref.
Control Amplifier A1 — A1 is a differential amplifier used to compare the potential between WE and RE.
The error signal is amplified and applied to the CE. Changes in the impedance between the WE
and RE cause a change in the voltage applied to CE in order to maintain the constant voltage
between WE and RE.
SNOA922–August 2013 Gas Sensor Platform Reference Design 3
Submit Documentation Feedback
Copyright © 2013, Texas Instruments Incorporated
Introduction www.ti.com
Temperature Sensor — An on-board temperature sensor provides a ±3˚C accuracy. The sensor can be
used by an external microcontroller to correct for performance over temperature.
Serial Interface — Calibration and programming is done through the I2C digital interface. The I2C
interface enables calibration and state-of-health monitoring. As mentioned before, health
monitoring is very important because chemical cells can degrade over time.
配置的过程通过IIc接口来实现,TI官网有个软件WEBENCH Design Environments ,里面可以帮我们配置lmp91000的寄存器参数,很方便。
配置好寄存器
#define I2C_RD 1 /* 读控制bit */
#define LMP_WRITE_ADDE 0x90 //LMP??+???
#define LMP_READ_ADDE 0x91 //LMP??+???
#define LMP_REG1_ADDE 0x00 // LMP???1??0x00
#define LMP_REG2_ADDE 0x01 // LMP???2??0x01
#define LMP_REG3_ADDE 0x10 //LMP???3??0x10
#define LMP_REG4_ADDE 0x11 //LMP???4??0x11
#define LMP_REG5_ADDE 0x12 //LMP???5??0x12
/* 氨气 */
//#define LMP_REG2_UNLOCK 0x00 //LMP???2????0x00
//#define LMP_REG2_LOCK 0x01 //LMP???2????0x01
//#define LMP_REG3_DATA 0x1c //LMP???3????0x0E
//#define LMP_REG4_DATA 0x90 //LMP???4????0xB0
//#define LMP_REG5_DATA 0x03 //LMP???5????0x07/03
/*氧气*/
#define LMP_REG2_UNLOCK 0x00 //LMP???2????0x00
#define LMP_REG2_LOCK 0x01 //LMP???2????0x01
#define LMP_REG3_DATA 0x08 //LMP???3????0x0E
#define LMP_REG4_DATA 0xc0 //LMP???4????0xB0
#define LMP_REG5_DATA 0x07 //LMP???5????0x07/03
/*************************************IIC??????*******************************************/
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define GPIO_PORT_I2C GPIOB /* GPIO端口 */
#define RCC_I2C_PORT RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define I2C_SCL_PIN GPIO_Pin_7 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_Pin_6 /* 连接到SDA数据线的GPIO */
#define I2C_EN_PIN GPIO_Pin_8 /* 连接到EN使能线的GPIO */
/* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
#if 0 /* 条件编译: 1 选择GPIO的库函数实现IO读写 */
#define I2C_SCL_1() GPIO_SetBits(GPIO_PORT_I2C, I2C_SCL_PIN) /* SCL = 1 */
#define I2C_SCL_0() GPIO_ResetBits(GPIO_PORT_I2C, I2C_SCL_PIN) /* SCL = 0 */
#define I2C_SDA_1() GPIO_SetBits(GPIO_PORT_I2C, I2C_SDA_PIN) /* SDA = 1 */
#define I2C_SDA_0() GPIO_ResetBits(GPIO_PORT_I2C, I2C_SDA_PIN) /* SDA = 0 */
#define I2C_EN_1() GPIO_SetBits(GPIO_PORT_I2C, I2C_EN_PIN) /* EN = 1 */
#define I2C_EN_0() GPIO_ResetBits(GPIO_PORT_I2C, I2C_EN_PIN) /* EN = 0 */
#define I2C_SDA_READ() GPIO_ReadInputDataBit(GPIO_PORT_I2C, I2C_SDA_PIN) /* 读SDA口线状态 */
#else /* 这个分支选择直接寄存器操作实现IO读写 */
/* 注意:如下写法,在IAR最高级别优化时,会被编译器错误优化 */
#define I2C_SCL_1() GPIO_PORT_I2C->BSRR = I2C_SCL_PIN /* SCL = 1 */
#define I2C_SCL_0() GPIO_PORT_I2C->BRR = I2C_SCL_PIN /* SCL = 0 */
#define I2C_SDA_1() GPIO_PORT_I2C->BSRR = I2C_SDA_PIN /* SDA = 1 */
#define I2C_SDA_0() GPIO_PORT_I2C->BRR = I2C_SDA_PIN /* SDA = 0 */
#define I2C_EN_1() GPIO_PORT_I2C->BSRR =I2C_EN_PIN /* EN = 1 */
#define I2C_EN_0() GPIO_PORT_I2C->BRR = I2C_EN_PIN /* EN = 0 */
#define I2C_SDA_READ() ((GPIO_PORT_I2C->IDR & I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#endif
static void i2c_CfgGpio(void);
/*
*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线位延迟,最快400KHz
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
uint8_t i;
/*
下面的时间是通过安富莱AX-Pro逻辑分析仪测试得到的。
CPU主频72MHz时,在内部Flash运行, MDK工程不优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
IAR工程编译效率高,不能设置为7
*/
for (i = 0; i < 10; i++);
}
/*
***********************************************************************************************
** ????:LMP_Init_Func
** ????:?
** ????:?
** ????:??IIC???ADS???????
************************************************************************************************/
void LMP_IIC_Init_Func(void)
{ i2c_CfgGpio();
i2c_Start();//LMP_IIC_Start();
i2c_SendByte(LMP_WRITE_ADDE);/* LMP_IIC_Send_Byte(LMP_WRITE_ADDE); *//* LMP91000+写操作 */
i2c_WaitAck();//LMP_IIC_Wait_Ack();
i2c_SendByte(LMP_REG2_ADDE); //锁定寄存器 地址01
i2c_WaitAck();
i2c_SendByte(LMP_REG2_UNLOCK); //解锁 命令 0x00
i2c_WaitAck();
i2c_Stop();
i2c_Delay();i2c_Delay();//delay_us(8);
i2c_Start();
i2c_SendByte(LMP_WRITE_ADDE); //LMP91000+写操作
i2c_WaitAck();
i2c_SendByte(LMP_REG3_ADDE); //TIA寄存器 地址 10
i2c_WaitAck();
i2c_SendByte(LMP_REG3_DATA); // 电阻配置 命令 0x0F
i2c_WaitAck();
i2c_Stop();
i2c_Delay();i2c_Delay();
i2c_Start();
i2c_SendByte(LMP_WRITE_ADDE); //LMP91000+写操作
i2c_WaitAck();
i2c_SendByte(LMP_REG4_ADDE); //REFCN寄存器 地址 11
i2c_WaitAck();
i2c_SendByte(LMP_REG4_DATA); // 参考源配置命令 0xb0
i2c_WaitAck();
i2c_Stop();
i2c_Delay();i2c_Delay();
i2c_Start();
i2c_SendByte(LMP_WRITE_ADDE); //LMP91000+写操作
i2c_WaitAck();
i2c_SendByte(LMP_REG5_ADDE); //MODECN 寄存器 地址 1
i2c_WaitAck();
i2c_SendByte(LMP_REG5_DATA); //模式寄存器 ox07
i2c_WaitAck();
i2c_Stop();
i2c_Delay();i2c_Delay();
i2c_Start();
i2c_SendByte(LMP_WRITE_ADDE); //??LMP91000??
i2c_WaitAck();
i2c_SendByte(LMP_REG2_ADDE); //锁定寄存器 地址01
i2c_WaitAck();
i2c_SendByte(LMP_REG2_LOCK); // 锁定
i2c_WaitAck();
i2c_Stop();
i2c_Delay();i2c_Delay();i2c_Delay();
}
/*********************************************************************************************************
* 函 数 名: i2c_Delay
* 功能说明: I2C总线位延迟,最快400KHz
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay1(void)
{
uint8_t i;
/*
下面的时间是通过安富莱AX-Pro逻辑分析仪测试得到的。
CPU主频72MHz时,在内部Flash运行, MDK工程不优化
循环次数为10时,SCL频率 = 205KHz
循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us
循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us
IAR工程编译效率高,不能设置为7
*/
for (i = 0; i < 7; i++);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{
I2C_EN_0();
i2c_Delay();
i2c_Delay();
i2c_Delay();
i2c_Delay();
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_0();
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
I2C_SCL_1();
i2c_Delay();
I2C_SDA_1();
i2c_Delay();//
I2C_EN_1();//
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参:_ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
I2C_SCL_0();//
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
i2c_Delay1();
I2C_SCL_1();
i2c_Delay1();
I2C_SCL_0();
if (i == 7)
{
I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay1();
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参:无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();
i2c_Delay();
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
i2c_Delay();
}
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参:无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay1();
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
i2c_Delay1();
if (I2C_SDA_READ()) /* CPU读取SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
I2C_SCL_0();
//i2c_Delay();
return re;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{
I2C_SCL_0();
I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{
I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CfgGpio
* 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void i2c_CfgGpio(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出 */
GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
// PB8--REF_EN(LMP91000
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_CheckDevice
* 功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
* 形 参:_Address:设备的I2C总线地址
* 返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
LMP_IIC_Init_Func();
i2c_CfgGpio(); /* 配置GPIO */
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
i2c_SendByte(_Address | I2C_WR);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}
第三部分 传感器
气体传感器分为两极很三级。图中的封装要注意。画电路封装是不能按图3的位置画。要镜像一下,(这个问题坑人啊)
我只在官方的参考设计中发现的。
我遇到过坑在LMP91000的参考手册中他的图就没有镜像,我认为他可能画错了
注意