电子产业一站式赋能平台

PCB联盟网

搜索
查看: 31|回复: 0
收起左侧

通用SCPI协议-单片机控制各种仪器仪表

[复制链接]

666

主题

666

帖子

8835

积分

高级会员

Rank: 5Rank: 5

积分
8835
发表于 2025-5-5 11:45:00 | 显示全部楼层 |阅读模式
关注+星标公众,不错过精彩内容来源 | 嵌入式情报局
实验室里的示波器、万用表、电源是如何被计算机精确控制的?答案藏在一种名为SCPI的“普通话”中。
本文不仅揭秘SCPI协议,还教你用STM32单片机通过串口控制万用表,并手写代码解析数据——无需复杂库函数,嵌入式小白也能轻松上手!
一、什么是SCPI协议?仪器的“普通话”1. 为什么需要SCPI?SCPI(Standard Commands for Programmable Instruments)是一种标准化仪器控制语言,解决了不同品牌设备之间的“语言障碍”。
?跨厂商兼容:Keysight、Tektronix、Fluke等品牌设备均可通过相同语法控制。
?层级化命令:类似文件路径的树状结构(如:MEAS:VOLT:DC?查询直流电压)。
?简单高效:通过ASCII字符串通信,无需专用驱动。
2. SCPI vs. 其他协议协议特点适用场景SCPI标准化高、通用性强实验室仪器、测试设备Modbus轻量级、适合工业环境PLC、传感器LXI基于以太网、支持Web控制高端仪器、分布式系统二、台式万用表的通用SCPI指令(以Keysight 34401A为例)1. 基础配置指令?选择测量功能
:CONF:VOLT:DC      ; 直流电压测量
:CONF:CURR:AC      ; 交流电流测量
:CONF:FRES         ; 4线电阻测量(高精度)
?量程与分辨率设置
:VOLT:DC:RANG 10    ; 手动量程10V(若超量程自动切换)
:VOLT:DC:RES 0.001  ; 分辨率设为1mV
2. 触发与数据读取?单次触发
:TRIG:SOUR IMM      ; 触发源设为“立即触发”
:READ?              ; 返回当前电压(如“+5.325E+0”)
?连续采样
:SAMP:COUN 100      ; 采样100次
:INIT               ; 启动采样
:FETC?              ; 读取数据(逗号分隔字符串)
3. 错误查询:SYST:ERR?           ; 返回错误信息(如“0, No error”)
三、实战:STM32单片机控制万用表(含自定义数据解析)1. 硬件连接?STM32:USART1(TX-PA9,RX-PA10)
?万用表:RS-232串口(波特率9600,8N1)
?电平转换:MAX3232芯片(TTL转RS-232)
2. 代码实现(HAL库)步骤1:初始化串口UART_HandleTypeDef huart1;
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
HAL_UART_Init(&huart1);
步骤2:发送SCPI命令// 设置直流电压测量
char cmd[] = ":CONF:VOLT:DC\r
";  // SCPI命令需以\r
结尾
HAL_UART_Transmit(&huart1, (uint8_t*)cmd, strlen(cmd), 1000);
步骤3:接收数据并解析(自定义函数替代atof)// 自定义ASCII转浮点函数(支持科学计数法)
float string_to_float(const char *str) {
    float result = 0.0, fraction = 0.1;
    int exponent = 0, sign = 1, exp_sign = 1;
    char *p = (char*)str;
    // 处理符号位
    if (*p == '-') { sign = -1; p++; }
    elseif (*p == '+') { p++; }
    // 解析整数和小数部分
    while (*p >= '0' && *p '9') { result = result * 10 + (*p - '0'); p++; }
    if (*p == '.') {
        p++;
        while (*p >= '0' && *p '9') {
            result += (*p - '0') * fraction;
            fraction *= 0.1;
            p++;
        }
    }
    // 处理指数部分(E/e)
    if (*p == 'E' || *p == 'e') {
        p++;
        if (*p == '-') { exp_sign = -1; p++; }
        elseif (*p == '+') { p++; }
        while (*p >= '0' && *p '9') { exponent = exponent * 10 + (*p - '0'); p++; }
        exponent *= exp_sign;
    }
    return sign * result * powf(10, exponent);
}
// 接收数据并转换
uint8_t rx_buf[32];
HAL_UART_Receive(&huart1, rx_buf, sizeof(rx_buf), 1000);
float voltage = string_to_float((char*)rx_buf);
printf("电压值: %.3f V", voltage);
步骤4:错误处理// 查询错误信息
char err_cmd[] = ":SYST:ERR?\r
";
HAL_UART_Transmit(&huart1, (uint8_t*)err_cmd, strlen(err_cmd), 1000);
HAL_UART_Receive(&huart1, rx_buf, sizeof(rx_buf), 1000);
printf("错误信息: %s", rx_buf);
四、为什么不用标准库函数?自定义函数的优势特性自定义string_to_float标准库atof代码体积~200字节(无依赖)依赖完整库文件执行速度更快(无冗余检查)较慢(支持复杂错误处理)内存占用极低(无动态内存)可能依赖系统内存管理适用场景:嵌入式设备、资源受限的单片机项目。
更多学习参考如下资料:
? 《IEEE 488.2标准协议》
? GitHub开源库:PyVISA、SCPI-parser
------------ END ------------

wvmbr0nzynt6404450605.gif

wvmbr0nzynt6404450605.gif

●专栏《嵌入式工具●专栏《嵌入式开发》●专栏《Keil教程》●嵌入式专栏精选教程
关注公众号回复“加群”按规则加入技术交流群,回复“1024”查看更多内容。
点击“阅读原文”查看更多分享。
回复

使用道具 举报

发表回复

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

本版积分规则

关闭

站长推荐上一条 /1 下一条


联系客服 关注微信 下载APP 返回顶部 返回列表