全网仅此一篇:工业级压力传感器设计及实现(华大半导体HC32L136)

近期基于Metallux ME501/ME505陶瓷压力传感器模组和NSA2862物联网调理芯片设计一款工业级压力传感器,并使用华大半导体HC32L136 MCU实现控制,特将项目心得与体会分享给各位朋友,目前全网仅此一篇,请多多支持~

1、项目概述

本项目传感器基于Metallux ME501/ME505陶瓷压力传感器模组和纳芯微NSA2862物联网调理芯片,微控制器基于华大半导体HC32L136,在保证高性能的前提下,支持待机与休眠唤醒,具备超低的功耗,可连续工作10年而无需更换电池,可大规模应用在智慧城市、工业物联网等领域。

项目的测量、校准、评估和设计这里参照于纳芯微电子传感器解决方案实现,没办法,工业级传感器设计就是烧钱啊。

2、模组简介

2.1、NSA2862物联网专用桥式传感器调理芯片

NSA2862是一颗专门针对物联网应用推出的用于阻式或者电压型传感器,例如阻式压力传感器,热电偶,RTD等传感器的超低功耗信号调理专用芯片。NSA2862集成了24位主信号测量通道和24位辅助温度测量通道,传感器校准逻辑,双路恒流源等电路,支持I2C、SPI或者OWI输出。通过内置的MCU,NSA2862支持对传感器的零点,灵敏度的二阶温度漂移校准以及最高三阶的非线性校准,校准精度可以达到0.1%以内,其校准系数存储于一组EEPROM中,芯片结构如下所示:

参数特性所示所示:

超低待机功耗:<100nA

低温漂内置参考电压

高精度 1X~256X增益可变仪表放大器

24位 ADC用于主信号测量

24位 ADC用于温度测量

支持内置温度传感器和外部温度传感器

双路恒流源输出

1X~8X ADC数字增益

多种 ODR 设置,支持 50/60Hz 工频抑制

基于内置 MCU的通用传感器校准逻辑

EEPROM,可多次编程

支持 SPI和 I2C及OWI接口

封装:TSSOP20

工作温度范围:-40℃~105℃

芯片功能框图如下图所示:

2.2、Metallux ME501/ME505陶瓷压力传感器

 压力传感器一般用于对传感器敏感器件所处气体或液体氛围的压力测量,一般用于反馈给系统主控单元,实现系统精确控制。压力传感器作为传感器中的一大门类,在汽车、工业、家电、消费电子等不同行业均有广泛的应用。常用压力传感器从感测原理来区分,主要包括如下几大类:

硅压阻技术

陶瓷电阻技术

玻璃微熔技术

陶瓷电容技术

硅压阻技术由半导体的压阻特性来实现,半导体材料的压阻特性取决于材料种类、掺杂浓度和晶体的晶向等因素。该技术可以采用半导体工艺实现,具有尺寸小,产量高、成本低、信号输出灵敏度高等优势。不足之处主要体现在介质耐受程度低,温度特性差和长期稳定性较差等方面。常见于中低压量程范围,如5kPa~700kPa。业界也有通过特殊封装工艺提高硅压阻技术的介质耐受程度的方案,如充油、背压等技术,但也会带来成本大幅增加等问题。

陶瓷电阻技术采用厚膜印刷工艺将惠斯通电桥印刷在陶瓷结构的表面,利用压敏电阻效应,实现将介质的压力信号转换为电压信号。陶瓷电阻技术具有成本适中,工艺简单等优势,目前国内有较多厂家提供陶瓷电阻压力传感器芯体。但该技术信号输出灵敏度低,量程一般限定在500kPa~10MPa,且常规中空结构,仅靠膜片承压,抗过载能力差,当待测介质压力过载时,陶瓷电阻传感器会存在膜片破裂,介质泄露的风险。

玻璃微熔技术采用高温烧结工艺,将硅应变计与不锈钢结构结合。硅应变计等效的四个电阻组成惠斯通电桥,当不锈钢膜片的另一侧有介质压力时,不锈钢膜片产生微小形变引起电桥变化,形成正比于压力变化的电压信号。玻璃微熔工艺实现难度较大,成本高。主要优势是介质耐受性好,抗过载能力强,一般适用于高压和超高压量程,如10MPa~200MPa,应用较为受限。

陶瓷电容技术采用固定式陶瓷基座和可动陶瓷膜片结构,可动膜片通过玻璃浆料等方式与基座密封固定在一起。两者之间内侧印刷电极图形,从而形成一个可变电容,当膜片上所承受的介质压力变化时两者之间的电容量随之发生变化,通过调理芯片将该信号进行转换调理后输出给后级使用。陶瓷电容技术具有成本适中,量程范围宽,温度特性好、一致性、长期稳定性好等优势。国际上来看广泛应用于汽车与工业过程控制等领域,分别以美国森萨塔和瑞士E+H为代表。由于电容信号不同于电阻信号,对信号处理电路要求较高,传感器设计时需要将电容和信号调理芯片ASIC协同考虑,国内目前仅苏州纳芯微电子股份有限公司能够同时提供两者整合的完整解决方案。

陶瓷电容作为压力传感器中一种主要技术路线,主要由瓷环、陶瓷膜片和陶瓷盖板三部分组成,具有耐腐蚀、抗冲击、介质兼容性好的优点。Metallux ME501/ME505传感器工作原理是陶瓷电阻技术,其由激光可调PTC电阻进行热补偿,陶瓷的使用确保了整个测量范围内的高线性度,将迟滞效应降到最低,传感器结构如下所示:

传感器技术特性如下表所示:

3、驱动原理

本项目中微控制器华大半导体HC32L136驱动NSA2862传感器调理芯片基于I2C通信协议,I2C 是双线双向的同步串行总线,它利用一根时钟线和一根数据线在连接总线的两个器件之间进行信息的传递,为设备之间数据交换提供了一种简单高效的方法。每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。I2C 标准是一个具有冲突检测机制和仲裁机制的真正意义上的多主机总线,它能在多个主机同时请求控制总线时利用仲裁机制避免数据冲突并保护数据。I2C 总线控制器,能满足 I2C 总线的各种规格并支持所有与 I2C 总线通信的传输模式。

通常标准 I2C 传输协议包含四个部分:起始(S)或重复起始信号(Sr),从机地址及读写位,传输数据,停止信号(P)。I2C 传输协议图如下所示:

当总线处于空闲状态下(SCL 和 SDA 线同时为高),SDA 线上出现由高到低的信号,表明总线上产生了起始信号。当两个起始信号之间没有停止信号时,即产生了重复起始信号。主机采用这种方法与另一个从机或相同的从机以不同传输方向进行通信(例如:从写入设备到从设备读出)而不释放总线。当 SCL 线为高时,SDA 线上出现由低到高的信号,被定义为停止信号。主机向总线发出停止信号结束数据传送。 START和STOP 条件图如下所示:

当起始信号产生后,主机立即传输数据的第一字节:7 位从机地址 + 读写位,读写位控制从机的数据传输方向(0:写;1:读)。被主机寻址的从机会通过在第 9 个 SCL时钟周期将 SDA 置为低电平作为应答。

 数据传输过程中,一个 SCL 时钟脉冲传输一个数据位,且 SDA 线只有在 SCL 为低时才可以改变。I2C 总线上位传输图如下所示:

华大半导体I2C 组件可实现 8 位的双向数据传输,传输速率在标准模式下可达到 100Kbps 而在高速模式下可达 400Kbps,在超高速模式下可达 1Mbps,并且可以在四种模式下工作:主机发送模式、主机接收模式、从机接收模式、从机发送模式。 还有一种特殊模式广播呼叫模式,其操作方式与从机接收模式类似。

华大半导体HC32L136 I2C 控制器支持以下特性:

支持主机发送/接收,从机发送/接收四种工作模式

支持标准(100Kbps) / 快速(400Kbps) / 高速(1Mbps) 三种工作速率

支持 7 位寻址功能

支持噪声过滤功能

支持广播地址

支持中断状态查询功能

I2C功能模块如下图所示:

本项目中采用主机模式实现数据收、发通信,主收、发送模式数据同步图如下图所示(更详细说明可参见用户手册):

4、项目实现

第1步:明确从机地址(压力传感器地址),NSA2862 的 I2C 设备地址如下表所示:

第2步:配置HC32L136 I2C功能模块,这里需要留意NSA2862 的 I2C通讯引脚的电性特性,如下表所示:

I2C GPIO配置代码如下所示: 

///< IO端口配置

void App_PortCfg(void)

{

    stc_gpio_cfg_t stcGpioCfg;

    DDL_ZERO_STRUCT(stcGpioCfg);

    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);  //开启GPIO时钟门控

    stcGpioCfg.enDir = GpioDirOut;                          ///< 端口方向配置->输出   

    stcGpioCfg.enOD = GpioOdEnable;                          ///< 开漏输出

    stcGpioCfg.enPu = GpioPuEnable;                          ///< 端口上拉配置->使能

    stcGpioCfg.enPd = GpioPdDisable;                        ///< 端口下拉配置->禁止

    Gpio_Init(GpioPortB,GpioPin8,&stcGpioCfg);              ///< 端口初始化

    Gpio_Init(GpioPortB,GpioPin9,&stcGpioCfg);

    Gpio_SetAfMode(GpioPortB,GpioPin8,GpioAf1);              ///< 配置PB08为SCL

    Gpio_SetAfMode(GpioPortB,GpioPin9,GpioAf1);              ///< 配置PB09为SDA

}

I2C配置代码如下所示:

///< I2C 模块配置

void App_I2cCfg(void)

{

    stc_i2c_cfg_t stcI2cCfg;

    DDL_ZERO_STRUCT(stcI2cCfg);                            ///< 初始化结构体变量的值为0

    Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c0,TRUE); ///< 开启I2C0时钟门控

    stcI2cCfg.u32Pclk = Sysctrl_GetPClkFreq();            ///< 获取PCLK时钟

    stcI2cCfg.u32Baud = 100000;                            ///< 100K

    stcI2cCfg.enMode = I2cMasterMode;                      ///< 主机模式

    stcI2cCfg.bGc = FALSE;                                ///< 广播地址应答使能关闭

    I2C_Init(M0P_I2C0,&stcI2cCfg);                        ///< 模块初始化

}

第3步:编写接收驱动函数,主机接收状态图如下所示:

接收驱动代码如下所示: 

/**

******************************************************************************

** \brief  主机接收函数

**

** \param u8Addr从机内存地址,pu8Data读数据存放缓存,u32Len读数据长度

**

** \retval 读数据是否成功

**

******************************************************************************/

en_result_t I2C_MasterReadData(M0P_I2C_TypeDef* I2CX,uint8_t u8Addr,uint8_t *pu8Data,uint32_t u32Len)

{

    en_result_t enRet = Error;

    uint8_t u8i=0,u8State;

    I2C_SetFunc(I2CX,I2cStart_En);

    while(1)

    {

        while(0 == I2C_GetIrq(I2CX))

        {}

        u8State = I2C_GetState(I2CX);

        switch(u8State)

        {

            case 0x08:                                ///< 已发送起始条件,将发送SLA+W

                I2C_ClearFunc(I2CX,I2cStart_En);

                I2C_WriteByte(I2CX,I2C_SLAVEADDR);

                break;

            case 0x18:                                ///< 已发送SLA+W,并接收到ACK

                I2C_WriteByte(I2CX,u8Addr);            ///< 发送从机内存地址

                break;

            case 0x28:                                ///< 已发送数据,接收到ACK, 此处是已发送从机内存地址u8Addr并接收到ACK

                I2C_SetFunc(I2CX,I2cStart_En);        ///< 发送重复起始条件

                break;

            case 0x10:                                ///< 已发送重复起始条件

                I2C_ClearFunc(I2CX,I2cStart_En);

                I2C_WriteByte(I2CX,I2C_SLAVEADDR|0x01);///< 发送SLA+R,开始从从机读取数据

                break;

            case 0x40:                                ///< 已发送SLA+R,并接收到ACK

                if(u32Len>1)

                {

                    I2C_SetFunc(I2CX,I2cAck_En);      ///< 使能主机应答功能

                }

                break;

            case 0x50:                                ///< 已接收数据字节,并已返回ACK信号

                pu8Data[u8i++] = I2C_ReadByte(I2CX);

                if(u8i==u32Len-1)

                {

                    I2C_ClearFunc(I2CX,I2cAck_En);    ///< 已接收到倒数第二个字节,关闭ACK应答功能

                }

                break;

            case 0x58:                                ///< 已接收到最后一个数据,NACK已返回

                pu8Data[u8i++] = I2C_ReadByte(I2CX);

                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件

                break;

            case 0x38:                                ///< 在发送地址或数据时,仲裁丢失

                I2C_SetFunc(I2CX,I2cStart_En);        ///< 当总线空闲时发起起始条件

                break;

            case 0x48:                                ///< 发送SLA+R后,收到一个NACK

                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件

                I2C_SetFunc(I2CX,I2cStart_En);        ///< 发送起始条件

                break;

            default:

                I2C_SetFunc(I2CX,I2cStart_En);        ///< 其他错误状态,重新发送起始条件

                break;

        }

        I2C_ClearIrq(I2CX);                            ///< 清除中断状态标志位

        if(u8i==u32Len)                                ///< 数据全部读取完成,跳出while循环

        {

                break;

        }

    }

    enRet = Ok;

    return enRet;

}

第4步:编写发送驱动函数,主机发送状态图如下所示: 

发送驱动代码如下所示:  

/**

******************************************************************************

** \brief  主机发送函数

**

** \param u8Addr从机内存地址,pu8Data写数据,u32Len写数据长度

**

** \retval 写数据是否成功

**

******************************************************************************/

en_result_t I2C_MasterWriteData(M0P_I2C_TypeDef* I2CX,uint8_t u8Addr,uint8_t *pu8Data,uint32_t u32Len)

{

    en_result_t enRet = Error;

    uint8_t u8i=0,u8State;

    I2C_SetFunc(I2CX,I2cStart_En);

    while(1)

    {

        while(0 == I2C_GetIrq(I2CX))

        {;}

        u8State = I2C_GetState(I2CX);

        switch(u8State)

        {

            case 0x08:                              ///< 已发送起始条件

                I2C_ClearFunc(I2CX,I2cStart_En);

                I2C_WriteByte(I2CX,I2C_SLAVEADDR);  ///< 从设备地址发送

                break;

            case 0x18:                              ///< 已发送SLA+W,并接收到ACK

                I2C_WriteByte(I2CX,u8Addr);          ///< 从设备内存地址发送

                break;

            case 0x28:                              ///< 上一次发送数据后接收到ACK

                I2C_WriteByte(I2CX,pu8Data[u8i++]);  ///< 继续发送数据

                break;

            case 0x20:                              ///< 上一次发送SLA+W后,收到NACK

            case 0x38:                              ///< 上一次在SLA+读或写时丢失仲裁

                I2C_SetFunc(I2CX,I2cStart_En);      ///< 当I2C总线空闲时发送起始条件

                break;

            case 0x30:                              ///< 已发送I2Cx_DATA中的数据,收到NACK,将传输一个STOP条件

                I2C_SetFunc(I2CX,I2cStop_En);        ///< 发送停止条件

                break;

            default:

                break;

        }

        if(u8i>u32Len)

        {

            I2C_SetFunc(I2CX,I2cStop_En);            ///< 此顺序不能调换,出停止条件

            I2C_ClearIrq(I2CX);

            break;

        }

        I2C_ClearIrq(I2CX);                          ///< 清除中断状态标志位

    }

    enRet = Ok;

    return enRet;

}

第5步:为验证I2C通信是否成功,向NSA2862空闲寄存器0XCF中执行读数据0X55并读取出来,空闲寄存器地址如下表所示:

验证示例代码如下所示:  

uint8_t u8Senddata[2] = {0x00,0x55};

uint8_t u8Recdata[1]={0x00};

///< 向I2C总线发起开始信号

I2C_SetFunc(M0P_I2C1,I2cStart_En);

///< 写数据

I2C_MasterWriteData(M0P_I2C0,0x30,&u8Senddata[0],1); 

I2C_MasterWriteData(M0P_I2C0,0xCF,&u8Senddata[1],1); 

delay1ms(100);

///< 读数据

I2C_MasterReadData(M0P_I2C0,0xCF,u8Recdata,1);   

///< 串口打印

for(int i=0;i<1;i++)

{

  Uart_SendDataIt(M0P_UART1,u8Recdata[i]); //启动UART1发送数据

  delay1ms(3);

}

使用示波器查看效果,程序运行时钟信号波形如下所示:

程序运行数据信号波形如下所示: 

串口输出效果如下所示: 

说明可以实现正常的I2C数据读写。

第6步:读取压力传感器数值,寄存器地址如下表所示:

读取传感器数值代码如下所示:

uint8_t u8Senddata[2] = {0x00,0x40};

uint8_t u8Recdata[4]={0x00};

///< 向I2C总线发起开始信号

I2C_SetFunc(M0P_I2C1,I2cStart_En);     

///< 写入寄存器启动使RAW_P=0

I2C_MasterWriteData(M0P_I2C0,0x30,&u8Senddata[0],1); 

I2C_MasterWriteData(M0P_I2C0,0xA5,&u8Senddata[1],1); 

delay1ms(100);

///< 读取经过校正的传感器数据

I2C_MasterReadData(M0P_I2C0,0x06,u8Recdata,1); 

I2C_MasterReadData(M0P_I2C0,0x07,u8Recdata+1,1);   

I2C_MasterReadData(M0P_I2C0,0x08,u8Recdata+2,1); 

for(int i=0;i<3;i++)

{

  Uart_SendDataIt(M0P_UART1,u8Recdata[i]); //启动UART1发送数据

  delay1ms(3);

}

使用示波器查看效果,程序运行时钟信号波形如下所示:

程序运行数据信号波形如下所示: 

串口输出效果如下所示: 

数据0X014ADD就是校准后ME501压力数值(2的补码形式)。

你可能感兴趣的:(全网仅此一篇:工业级压力传感器设计及实现(华大半导体HC32L136))