[传感器] 基于NSA2860的压力传感器校准工具设计

[复制链接]
12|1
曲鹏 发表于 2026-2-24 15:49 | 显示全部楼层 |阅读模式
#申请原创#
大家有没有为NSA2860压力传感器变送芯片资料少、协议复杂,不能解锁等问题而发愁?
本帖就参考NSA2860 DatasheetNSA2860单颗校准系统软件,利用逻辑分析仪进行检测其通信协议,抽丝剥茧,分析其通信协议。
1NSA2860这颗芯片介绍
它是一款高度集成的传感器信号调理与变送专用芯片,将完整的模拟信号链、数字校准和多种输出接口集成于单芯片。
三大核心功能:
采集:集成双 24 ADCPGA 和恒流/压源,可高精度采集传感器(压力、RTD、热电偶等)和温度信号。
补偿:内置 MCU 和校准算法,支持对传感器进行二阶温漂和三阶非线性补偿,精度达 0.1%
输出:提供 4~20mA0~5/10VPWM/PDM​ 等多种变送输出,并集成 16 DAC
核心优势:
简化设计:集成 JFET 控制器,可直接从工业总线取电,支持两线制方案。
灵活配置:支持 OWI(单总线)、SPII2C​ 通信,特别是 OWI 协议,无需额外接线即可在输出线上完成配置和校准。
高集成度:大幅减少外围元件,降低成本与体积。
主要应用:
压力/温度变送器、工业过程控制传感器模块等。
2、通信接口和协议
NSA2860支持三种通信协议,其中OWI(单线接口)是其最具特色的专有协议,主要应用于两线制(4-20mA)变送器场景,无需额外通信线即可完成校准。
本帖就用的是OWI通信接口。
部分电路图如下
通过S1_CMD控制传感器通电,电源为DC24V,通过S1_DATA控制直流载波信号,给传感器发送数据,通过三个精密电阻并联采样输出电流变化,再经过放大(LM321)、比较(LM393)后通过三极管Q5电平转换后送到单片机的R1_DATA接口。
校准主要涉及到读写ID、初始化芯片、DAC校准、采集数据、烧录、EE锁定和解锁等操作。
下面分别介绍:
ID
用逻辑分析仪分别监控发送和接收端的电平,如图所示
图中数据高电平760us为逻辑1,高电平252us为逻辑0
分析进入OWI模式:系统reset后有18.418ms的低电平,被识别为起始信号,然后发送0xB5A6C9进入OWI模式,其他协议解析如下。
ID
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
初始化
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
(数据太大,请参见附件)
DAC校准
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
数据采集
分别采集41220mA的数据,用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
(数据太大,请参见附件)
烧录
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
EE锁定
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
EE解锁
用逻辑分析仪分别监控发送和接收端的电平,如图所示
协议解析如下:
3、软件设计
根据以上监测到的协议,编写NSA2860驱动程序,关键代码如下:
u16 delay_start = 290, delay_stop = 840;
u16 delay_long = 72, delay_shot = 220;
void NSA_send_start(u8 NSA_num)   //起始信号
{
    switch(NSA_num)
    {
    case 0:
        Send_data1 = 0;
        delay_us(delay_start);
        Send_data1 = 1;
        break;
    case 1:
        Send_data2 = 0;
        delay_us(delay_start);
        Send_data2 = 1;
        break;
    case 2:
        Send_data3 = 0;
        delay_us(delay_start);
        Send_data3 = 1;
        break;
    }
}
void NSA_send_stop(u8 NSA_num)   //结束信号
{
    switch(NSA_num)
    {
    case 0:
        Send_data1 = 1;
        break;
    case 1:
        Send_data2 = 1;
        break;
    case 2:
        Send_data3 = 1;
        break;
    }
    delay_us(delay_stop);
}
void NSA_send_0(u8 NSA_num)       //发送0
{
    switch(NSA_num)
    {
    case 0:
        Send_data1 = 1;
        delay_us(delay_long);
        Send_data1 = 0;
        delay_us(delay_shot);
        Send_data1 = 1;
        break;
    case 1:
        Send_data2 = 1;
        delay_us(delay_long);
        Send_data2 = 0;
        delay_us(delay_shot);
        Send_data2 = 1;
        break;
    case 2:
        Send_data3 = 1;
        delay_us(delay_long);
        Send_data3 = 0;
        delay_us(delay_shot);
        Send_data3 = 1;
        break;
    }
}
void NSA_send_1(u8 NSA_num)       //发送1
{
    switch(NSA_num)
    {
    case 0:
        Send_data1 = 1;
        delay_us(delay_shot);
        Send_data1 = 0;
        delay_us(delay_long);
        Send_data1 = 1;
        break;
    case 1:
        Send_data2 = 1;
        delay_us(delay_shot);
        Send_data2 = 0;
        delay_us(delay_long);
        Send_data2 = 1;
        break;
    case 2:
        Send_data3 = 1;
        delay_us(delay_shot);
        Send_data3 = 0;
        delay_us(delay_long);
        Send_data3 = 1;
        break;
    }
}
u8 NSA_rec_real_data_sinal(u8 NSA_num, u8 send_num)
{
    u8 data_check1 = 0, data_check2 = 0, i = 0;
    switch(NSA_num)
    {
    case 0:
        for(i = 0; i < send_num; i++)
        {
            data_check1 = (((exti2_rec_data >> 3) & 1) + ((exti2_rec_data >> 5) & 1) + ((exti2_rec_data >> 7) & 1) + ((exti2_rec_data >> 9) & 1)) % 2;
            data_check2 = (((exti2_rec_data >> 2) & 1) + ((exti2_rec_data >> 4) & 1) + ((exti2_rec_data >> 6) & 1) + ((exti2_rec_data >> 8) & 1)) % 2;
            if((((exti2_rec_data >> 1) & 1) == data_check1) && (((exti2_rec_data >> 0) & 1) == data_check2))
                NSA_read_sinal[send_num - i - 1] = (u8)(exti2_rec_data >> 2);
            else return 0xFF;
            exti2_rec_data = exti2_rec_data >> 10;
        }
        break;
    case 1:
        for(i = 0; i < send_num; i++)
        {
            data_check1 = (((exti0_rec_data >> 3) & 1) + ((exti0_rec_data >> 5) & 1) + ((exti0_rec_data >> 7) & 1) + ((exti0_rec_data >> 9) & 1)) % 2;
            data_check2 = (((exti0_rec_data >> 2) & 1) + ((exti0_rec_data >> 4) & 1) + ((exti0_rec_data >> 6) & 1) + ((exti0_rec_data >> 8) & 1)) % 2;
            if((((exti0_rec_data >> 1) & 1) == data_check1) && (((exti0_rec_data >> 0) & 1) == data_check2))
                NSA_read_sinal[send_num - i - 1] = (u8)(exti0_rec_data >> 2);
            else return 0xFF;
            exti0_rec_data = exti0_rec_data >> 10;
        }
        break;
    case 2:
        for(i = 0; i < send_num; i++)
        {
            data_check1 = (((exti1_rec_data >> 3) & 1) + ((exti1_rec_data >> 5) & 1) + ((exti1_rec_data >> 7) & 1) + ((exti1_rec_data >> 9) & 1)) % 2;
            data_check2 = (((exti1_rec_data >> 2) & 1) + ((exti1_rec_data >> 4) & 1) + ((exti1_rec_data >> 6) & 1) + ((exti1_rec_data >> 8) & 1)) % 2;
            if((((exti1_rec_data >> 1) & 1) == data_check1) && (((exti1_rec_data >> 0) & 1) == data_check2))
                NSA_read_sinal[send_num - i - 1] = (u8)(exti1_rec_data >> 2);
            else return 0xFF;
            exti1_rec_data = exti1_rec_data >> 10;
        }
        break;
    }
    return 0;
}
u8 NSA_send_rec_data(u8 NSA_num, u8 add, u8 R1_W0, u8 len, u8* data)  //发送接收数据,add地址,read_write读或写数据,len读写长度,data写数据
{
    s8 i = 0, j = 0;
    switch(NSA_num)
    {
    case 0:
        EXTI->IMR &= ~(EXTI_Line2); //关闭外部中断
        exti2_rec_data = 0;
        break;
    case 1:
        EXTI->IMR &= ~(EXTI_Line0); //关闭外部中断
        exti0_rec_data = 0;
        break;
    case 2:
        EXTI->IMR &= ~(EXTI_Line1); //关闭外部中断
        exti1_rec_data = 0;
        break;
    }
    NSA_send_start(NSA_num);        //上一个函数结束到这个需要842us
    for(i = 7; i >= 0; i--)  //地址
    {
        if((add >> i) & 0x01)
            NSA_send_1(NSA_num);
        else
            NSA_send_0(NSA_num);
    }
    if((len <= 4) && (len >= 1))  //长度
    {
        len = len - 1;
        for(i = 1; i >= 0; i--)
        {
            if((len >> i) & 0x01)
                NSA_send_1(NSA_num);
            else
                NSA_send_0(NSA_num);
        }
    }
    else return 0xFD;
    if(R1_W0 == 0) //读写数据,01
    {
        NSA_send_0(NSA_num);
        for(j = 0; j <= len; j++)
        {
            for(i = 7; i >= 0; i--)  //地址
            {
                if((data[j] >> i) & 0x01)
                    NSA_send_1(NSA_num);
                else
                    NSA_send_0(NSA_num);
            }
        }
    }
    else
    {
        NSA_send_1(NSA_num);
        switch(NSA_num)
        {
        case 0:
            tim2_num_ins_2 = 0;
            EXTI->IMR |= EXTI_Line2;    //开启外部中断
            break;
        case 1:
            tim2_num_ins_0 = 0;
            EXTI->IMR |= EXTI_Line0;    //开启外部中断
            break;
        case 2:
            tim2_num_ins_1 = 0;
            EXTI->IMR |= EXTI_Line1;    //开启外部中断
            break;
        }
    }
    NSA_send_stop(NSA_num);
    switch(NSA_num)
    {
    case 0:
        flag_rec_end_2 = 0;
        if(R1_W0 == 1)
        {
            while(flag_rec_end_2 < 100) delay_us(10);
            delay_ms(2);
            return NSA_rec_real_data_sinal(NSA_num, len + 1);
        }
        else
        {
            while(flag_rec_end_2 < 100) delay_us(10);
            delay_ms(1);
        }
        break;
    case 1:
        flag_rec_end_0 = 0;
        if(R1_W0 == 1)
        {
            while(flag_rec_end_0 < 100) delay_us(10);
            delay_ms(2);
            return NSA_rec_real_data_sinal(NSA_num, len + 1);
        }
        else
        {
            while(flag_rec_end_0 < 100) delay_us(10);
            delay_ms(1);
        }
        break;
    case 2:
        flag_rec_end_1 = 0;
        if(R1_W0 == 1)
        {
            while(flag_rec_end_1 < 100) delay_us(10);
            delay_ms(2);
            return NSA_rec_real_data_sinal(NSA_num, len + 1);
        }
        else
        {
            while(flag_rec_end_1 < 100) delay_us(10);
            delay_ms(1);
        }
        break;
    }
    return 0;
}
void NSA_send_OWI(u8 NSA_num) //发送OWI命令0xB5A6C9
{
    s8 i = 0;
    u32 data_owi = 0xB5A6C9;
    NSA_send_start(NSA_num);
    for(i = 23; i >= 0; i--)  //地址
    {
        if((data_owi >> i) & 0x01)
            NSA_send_1(NSA_num);
        else
            NSA_send_0(NSA_num);
    }
    NSA_send_stop(NSA_num);
}
void NSA_reboot(u8 NSA_num)
{
    switch(NSA_num)
    {
    case 0:
        Send_cmd1 = 0;
        delay_ms(18);
        Send_data1 = 0;
        delay_ms(192); //重启NSA2860
        Send_cmd1 = 1;
        delay_ms(18);
        Send_data1 = 1;
        delay_ms(48);
        break;
    case 1:
        Send_cmd2 = 0;
        delay_ms(18);
        Send_data2 = 0;
        delay_ms(192); //重启NSA2860
        Send_cmd2 = 1;
        delay_ms(18);
        Send_data2 = 1;
        delay_ms(48);
        break;
    case 2:
        Send_cmd3 = 0;
        delay_ms(18);
        Send_data3 = 0;
        delay_ms(192); //重启NSA2860
        Send_cmd3 = 1;
        delay_ms(18);
        Send_data3 = 1;
        delay_ms(48);
        break;
    }
}
4、界面设计
软件界面设计如下
实际应用界面如下
通过激活通信、DAC校准、数据采集、计算烧录后,就完成了压力传感器的校准工作,校准完还可以对当前压力和压力源进行对比,如图所示。
如需具体实现,可参考本人分享的源码(NSA2860的压力传感器校准工装和算法
https://dl.21ic.com/download/ic-867282.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
 楼主| 曲鹏 发表于 2026-2-24 16:08 | 显示全部楼层
文中涉及到的数据手册和通信协议

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

7

主题

20

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部
0