banner
yono

yono

哈喽~欢迎光临
follow
github

24_7_12_C語言入門_SCPI庫API介紹

背景故事#

上大學一直在划水,剛畢業時幾乎啥也不會,是大佬的這個庫拯救了我。

SCPI 是一種字符串解析協議。剛工作時,點完燈開完串口,就讓我實現 SCPI 協議。那時還很菜,花了兩週時間才把這個協議庫開起來。

這個庫中的內容真的特別豐富,也沒有很動態的指針,基本上都是綁定性質的 "靜態" 指針,哪怕 C 語言不怎麼樣的我也可以慢慢看得懂。

在慢慢使用大約兩個月後,其中的大部分內容都瀏覽過,C 語言也正式入門了,

而且後續的應屆生,我也是讓他們先看這個庫。

源碼地址在這裡

j123b567/scpi-parser: Open Source SCPI device library (github.com)

這個代碼庫的優勢#

[!NOTE]

  1. 這是一個純粹的協議解析庫,與硬件並非強關聯,需要關注的 port 也僅僅是數據流入和數據流出。非常迅速就可以使用起來(哪怕是 CodeBlocks 或 DEV-C++ 這樣的學習環境)。
  2. 這是一個能勝任業務功能的庫,SCPI 協議解析所需的所有功能或機制它都可以勝任,學習後的應用場景非常廣,不會白白學習。
  3. 這是一個穩健的庫,我目前還沒有在這個庫中發現致命 bug。舉個反例,最近期望移植和修改的 freemodbus 庫,其中就存在致命 bug 以及大大小小的設計缺陷。
  4. 這是一個相對簡單的庫,比起 rtos 內核、網絡庫、文件系統庫等等,這個庫幾乎可以稱得上非常易懂了,只要花時間,就能從其中學到 面向對象、宏翻譯、宏條件編譯、回調綁定、字符處理 等等 C 語言的常用語法及設計。

應該看什麼#

由於是要寫給 C 語言入門級選手的,哪怕這個庫的結構如此簡單,也依然需要介紹一下。整個庫只有兩個文件夾如下

  • examples 其中是一些示例,也是我們期望將庫用起來首先要看的。

  • libscpi 其中就是所有的庫源碼。在這個文件夾下有test文件夾,是帶有 main 入口的單元測試,實際工程中不必引入libscpi/test文件夾的源碼。

    image-20241205131859389

examples 中建議重點查看如下兩個文件夾

examples/common 下是例程所支持的指令表,以及對應指令的回調,其中可以學習一些庫 API 的用法

examples/test-parser 下是最簡單例程的 main 入口,以及如 SCPI_Write ()、SCPI_Error () 等接口函數的定義,還有少量指令的回調,在指令表中都可以找到綁定。

image-20241205132043166

大致使用流程如下

  1. 使用 SCPI_Init() 函數綁定對象、設備 ID、各種 port 函數
  2. 使用 SCPI_Input() 函數輸入一條所支持的完整指令,庫自動觸發指令回調,自動使用 port 函數發送信息

優秀的庫,使用就是如此簡單。

一些庫 API 介紹#

在官方文檔中實際上也有 API 介紹 ,但是幾乎沒有有效信息,畢竟官方也只有一個人,可參考。

About · SCPI parser (jaybee.cz)

Scpi-Def.c 中聲明了整個 SCPI 處理過程中用到的接口參數 scpi interface

指令參數處理 API#

scpi_bool_t SCPI_ParamErrorOccurred(scpi_t* context);
用於處理函數中,檢測處理函數時是否產生錯誤,若存在錯誤則應立即停止處理函數。


scpi_bool_t SCPI_ParamInt32(
    scpi_t* context,
    int32_t* value,
 scpi_bool_t mandatory);
從 context 取出32位有符號參數,賦值給 value,若 mandatory 為 ture 且沒有參數,則生成-109錯誤(無參數錯誤);若 mandatory 為 false(通常不使用),則參數應為表達式字符串,否則產生-151錯誤(無效字符串錯誤)

scpi_bool_t SCPI_ParamInt64(
    scpi_t* context,
    int64_t* value,
 scpi_bool_t mandatory);
同上述取32位有符號參數邏輯,取出64位有符號參數


與上述兩個函數相同邏輯的還有如下 API 函數


SCPI_ParamUInt32() 取無符號32位數據
SCPI_ParamUInt64()取無符號64位數據
SCPI_ParamDouble()取 double 類型數據
SCPI_ParamFloat()取 float 類型數據
SCPI_ParamBool()取 bool 類型數據

SCPI_ParamChoice(
 scpi_t * context,
 const scpi_choice_def_t * options,
 int32_t * value,
 scpi_bool_t mandatory)
取選項列表中的值 *options 為提取數據的參數 選項的索引賦值給 Value

SCPI_ParamCopyText(
 scpi_t * context,
 char * buffer,
 size_t buffer_len,
 size_t * copy_len,
 scpi_bool_t mandatory)
 提取數據賦給 buffer

SCPI_ParamCharacters(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory)
 提取字符參數 給 value,len 為提取成功的字符長度

SCPI_ParamArbitraryBlock(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory)
 獲取任意塊程序數據 給 value,len 為提取成功的字符

SCPI_ParamNumber(
   scpi_t * context,
   const scpi_choice_def_t * special,
   scpi_number_t * value,
 scpi_bool_t mandatory)
將下個參數解析為數字 or 帶單位數字 or 特定規則的數字賦值給 value。若需按特定規則解析——special 標識了
解析規則:MINimum、MAXimum、DEFaylt、
UP、DOWN 等,詳查 scpi_choice_numbers_def[]定義

產生回傳 API#

經結果生成處理 API,產生的回傳參數會存入接口參數 scpi interface 中,並最終均會調用 SCPI_Write () 函數,目前該函數為:將回傳參數經 uart4 口發出

size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len)
將任意塊數據加上(#1+字節數據長度)頭——更換頭查找 SCPI_ResultArbitraryBlockHeader()函數定義,加
上\r\n 尾,調用 SCPI_Write()函數發出

該函數實際上調用下列兩個函數

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len)
運算數據塊應添加的頭並發送
size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len)
帶錯誤檢測的數據塊發送函數,若 context 的 arbitrary_reminding 參數小於指定長度 len,
則產生一個系統錯誤 SCPI_ERROR_SYSTEM_ERROR

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data)
查找帶有 “的數據,將帶有 “ 的字符串寫入結果(沒什麼用的 API),可能是一個 FUNC

size_t
SCPI_ResultBool(
 scpi_t * context
 scpi_bool_t val)
bool 值寫入結果

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len)
將原始字符串結果寫入輸出,先發出一個回傳分割符“,”,後將字符串發出

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data)
SCPI_ResultCharacters()字符串發送函數等價,但本函數 len 為 sizeof
size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len)
將任意塊數據加上(#1+字節數據長度)頭——更換頭查找 SCPI_ResultArbitraryBlockHeader()函數定義,加
上\r\n 尾,調用 SCPI_Write()函數發出

該函數實際上調用下列兩個函數

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len)
運算數據塊應添加的頭並發送
size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len)
帶錯誤檢測的數據塊發送函數,若 context 的 arbitrary_reminding 參數小於指定長度 len,
則產生一個系統錯誤 SCPI_ERROR_SYSTEM_ERROR

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data)
查找帶有 “的數據,將帶有 “ 的字符串寫入結果(沒什麼用的 API),可能是一個 FUNC

size_t
SCPI_ResultBool(
 scpi_t * context
 scpi_bool_t val)
bool 值寫入結果

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len)
將原始字符串結果寫入輸出,先發出一個回傳分割符“,”,後將字符串發出

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data)
SCPI_ResultCharacters()字符串發送函數等價,但本函數 len 為 sizeof

以下寫入結果的 API 若無 Base 傳參則均以十進制轉換為字符串

size_t
SCPI_ResultDouble(
 scpi_t * context,
 double val)
將一個雙精度值寫入結果,先發出一個結果分割符“,”,後將值發出

與上述函數相同邏輯的還有如下 API 函數
SCPI_ResultFloat(scpi_t * context,float val)寫入 float
SCPI_ResultInt16(scpi_t * context,int16_t val)寫入有符號16位值
SCPI_ResultInt32(scpi_t * context,int32_t val) 
SCPI_ResultInt64(scpi_t * context,int64_t val) 
SCPI_ResultInt8(scpi_t * context,int8_t val) 
SCPI_ResultUInt16(scpi_t * context,uint16_t val) 寫入無符號16位值
SCPI_ResultUInt32(scpi_t * context,uint32_t val)
SCPI_ResultUInt64(scpi_t * context,uint64_t val)
SCPI_ResultUInt8(scpi_t * context,uint8_t val) 
SCPI_ResultUInt16Base(scpi_t * context,uint16_t val,int8_t base) 將無符號 16 位值轉換為 Base 進制,並轉換為字符串寫入結果
SCPI_ResultUInt32Base(scpi_t * context,uint32_t val,int8_t base) 
SCPI_ResultUInt64Base(scpi_t * context,uint64_t val,int8_t base) 
SCPI_ResultUInt8Base(scpi_t * context,uint8_t val,int8_t base)

以數組形式產生回傳 API#

與上一部分產生回傳的 API 類似,本部分 API 產生數組存入接口參數 scpi interface 的結果存儲中,並觸發 SCPI_Write () 函數

size_t 
SCPI_ResultArrayDouble(
    scpi_t * context,
    const double * array,
    size_t count,
    scpi_array_format_t format);
將 array 指向的 count 個 double 元素 轉換為數組 format 選擇系統大小端

SCPI_ResultArrayFloat(scpi_t * context,const Float * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayInt16(scpi_t * context,const int16_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayInt32(scpi_t * context,const int32_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayUInt64(scpi_t * context,const uint64_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayUInt8(scpi_t * context,const uint8_t * array,
size_t count,scpi_array_format_t format);

數據轉字符串的 API#

默認不對接口參數的回傳值進行操作

size_t
SCPI_DoubleToStr(
    double val,
    char * str,
    size_t len)
將雙精度值轉換為字符串,向 str 指向的地址賦值,len 為允許的最長緩存區字節長度

SCPI_FloatToStr(float val,char * str,size_t len)
SCPI_Int32ToStr(int32_t val,char * str,size_t len)
SCPI_Int64ToStr(int64_t val,char * str,size_t len)
SCPI_UInt32ToStrBase(uint32_t val,char * str,size_t len,int8_t base)轉化後的字符串為 base 進制
SCPI_UInt64ToStrBase(uint64_t val,char * str,size_t len,int8_t base)

size_t
SCPI_NumberToStr(
    scpi_t * context,
    const scpi_choice_def_t * special,
    scpi_number_t * value,
    char * str,
    size_t len)
將特殊規則下的數字轉換為帶有單位的字符串,與 SCPI_ParamNumber 是一對反函數

拓展的參數處理 API#

scpi_bool_t
SCPI_ChoiceToName(
    const scpi_choice_def_t * options,
    int32_t tag,
        const char ** text)
options 是一個{字符串,int_t 數據}的表結構,該函數會根據 tag 數據查表並將對應的字符串的地址賦值給 text 指向的地址

scpi_bool_t
SCPI_ParamIsNumber(
    scpi_parameter_t * parameter,
    scpi_bool_t suffixAllowed)
檢查參數是否為數字類型,通常使用 SCPI_Parameter(context, &param, mandatory)取參數後使用

scpi_bool_t
SCPI_ParamIsValid(
    scpi_parameter_t * parameter)
此函數檢查賦值函數時是否有出錯

scpi_bool_t
SCPI_ParamToChoice(
    scpi_t * context,
    scpi_parameter_t * parameter,
    const scpi_choice_def_t * options,
    int32_t * value)
options 是一個{字符串,int_t 數據}的表結構,該函數根據 contexet 中的參數查表,
並將參數轉換為字符串,通常使用 SCPI_Parameter(context, &param, mandatory)取參數後使用
scpi_bool_t
SCPI_ParamToDouble(
    scpi_t* context,
    scpi_parameter_t* parameter,
    double* value)
將參數轉換為 double 值,通常使用 SCPI_Parameter(context, &param, mandatory)取參數後使用


與上述函數有相似邏輯的還有如下函數
SCPI_ParamToFloat(scpi_t* context,scpi_parameter_t* parameter,float* value);
SCPI_ParamToInt32(scpi_t* context,scpi_parameter_t* parameter,int32_t* value);
SCPI_ParamToInt64(scpi_t* context,scpi_parameter_t* parameter,int64_t* value);
SCPI_ParamToUInt32(scpi_t* context,scpi_parameter_t* parameter,uint32_t* value);
SCPI_ParamToUInt64(scpi_t* context,scpi_parameter_t* parameter,uint64_t* value);


scpi_bool_t
SCPI_Parameter(
    scpi_t* context,
    scpi_parameter_t* parameter,
    scpi_bool_t mandatory)
從命令行取一個參數並賦值給 parameter,parameter 中還會存入數據長度、數據類型。

命令處理 API#

int32_t
SCPI_CmdTag(
    scpi_t* context)
返回檢測到的命令標記;看不懂,待測試

scpi_bool_t
SCPI_CommandNumbers(
    scpi_t* context,
    int32_t* numbers,
        size_t len)
在命令列表中,指定允許數字的位置,在處理函數中,可以將這些位置的數字填充給 numbers,len 為數組長度例如{.pattern = "TEST#:NUMbers#", .callback = TEST_Numbers,},
若收到 TEST3:NUMbers2,則可使用 SCPI_CommandNumbers(context,&data[0],2),則 data[0]賦值為3,data[1]賦值為2
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。