banner
yono

yono

哈喽~欢迎光临
follow
github

Introduction to SCPI Library API for C Language Beginners 24_7_12

Background Story#

I was just coasting through college, and when I graduated, I barely knew anything. It was this library from a senior that saved me.

SCPI is a string parsing protocol. When I first started working, after turning on the lights and opening the serial port, I was tasked with implementing the SCPI protocol. At that time, I was still quite inexperienced and it took me two weeks to get this protocol library up and running.

The content of this library is really rich, and it doesn't have very dynamic pointers; it's mostly bound "static" pointers, which I could gradually understand even though my C language skills were not great.

After using it for about two months, I had browsed through most of its content, and I had officially entered the world of C programming.

Moreover, for the subsequent fresh graduates, I also let them look at this library first.

The source code can be found here

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

Advantages of This Code Library#

[!NOTE]

  1. This is a pure protocol parsing library, not strongly associated with hardware; the only ports to be concerned with are data input and output. It can be used very quickly (even in learning environments like CodeBlocks or DEV-C++).
  2. This is a library capable of handling business functions; it can meet all the functions or mechanisms required for SCPI protocol parsing, and the application scenarios after learning are very broad, so it won't be a waste of learning.
  3. This is a robust library; I have not yet discovered any fatal bugs in this library. As a counterexample, the recently expected porting and modification of the freemodbus library has fatal bugs and various design flaws.
  4. This is a relatively simple library; compared to RTOS kernels, network libraries, file system libraries, etc., this library can be considered very easy to understand. With time, you can learn common C language syntax and design such as object-oriented programming, macro translation, conditional compilation, callback binding, character processing, etc.

What to Look At#

Since this is aimed at beginners in C programming, even though the structure of this library is so simple, it still needs some introduction. The entire library consists of only two folders as follows:

  • examples contains some examples, which are what we expect to look at first to use the library.

  • libscpi contains all the library source code. In this folder, there is a test folder, which includes unit tests with a main entry point; in actual projects, there is no need to include the source code from the libscpi/test folder.

    image-20241205131859389

In examples, it is recommended to focus on the following two folders:

The examples/common folder contains the command table supported by the examples and the corresponding callbacks, where you can learn some usage of the library API.

The examples/test-parser folder contains the main entry of the simplest example, as well as the definitions of interface functions such as SCPI_Write() and SCPI_Error(), along with a small number of command callbacks, which can all be found in the command table.

image-20241205132043166

The general usage process is as follows:

  1. Use the SCPI_Init() function to bind objects, device IDs, and various port functions.
  2. Use the SCPI_Input() function to input a complete command that is supported; the library automatically triggers the command callback and automatically uses the port functions to send information.

An excellent library is this simple to use.

Some Library API Introductions#

There is actually an API introduction in the official documentation, but it has almost no effective information, as there is only one person in the official team, which can be referenced.

About · SCPI parser (jaybee.cz)

In Scpi-Def.c, the interface parameters used in the entire SCPI processing process are declared.

Command Parameter Handling API#

scpi_bool_t SCPI_ParamErrorOccurred(scpi_t* context);
Used in handling functions to detect whether an error occurred during processing; if an error exists, the handling function should be stopped immediately.

scpi_bool_t SCPI_ParamInt32(
    scpi_t* context,
    int32_t* value,
 scpi_bool_t mandatory);
Extracts a 32-bit signed parameter from the context and assigns it to value. If mandatory is true and there is no parameter, it generates error -109 (no parameter error); if mandatory is false (usually not used), the parameter should be an expression string, otherwise it generates error -151 (invalid string error).

scpi_bool_t SCPI_ParamInt64(
    scpi_t* context,
    int64_t* value,
 scpi_bool_t mandatory);
Similar to the above logic for extracting a 32-bit signed parameter, it extracts a 64-bit signed parameter.

The following API functions have similar logic to the above two functions:

SCPI_ParamUInt32() extracts unsigned 32-bit data.
SCPI_ParamUInt64() extracts unsigned 64-bit data.
SCPI_ParamDouble() extracts double type data.
SCPI_ParamFloat() extracts float type data.
SCPI_ParamBool() extracts bool type data.
SCPI_ParamChoice(
 scpi_t * context,
 const scpi_choice_def_t * options,
 int32_t * value,
 scpi_bool_t mandatory);
Extracts a value from the options list; *options is the parameter for extracting data, and the index of the option is assigned to value.

SCPI_ParamCopyText(
 scpi_t * context,
 char * buffer,
 size_t buffer_len,
 size_t * copy_len,
 scpi_bool_t mandatory);
Extracts data and assigns it to buffer.

SCPI_ParamCharacters(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory);
Extracts character parameters and assigns them to value; len is the length of the successfully extracted characters.

SCPI_ParamArbitraryBlock(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory);
Obtains arbitrary block program data and assigns it to value; len is the length of the successfully extracted characters.

SCPI_ParamNumber(
   scpi_t * context,
   const scpi_choice_def_t * special,
   scpi_number_t * value,
 scpi_bool_t mandatory);
Parses the next parameter as a number, or a number with a unit, or a number with specific rules, assigning it to value. If specific rules are needed for parsing—special identifies the parsing rules: MINimum, MAXimum, DEFault, UP, DOWN, etc., see scpi_choice_numbers_def[] definition for details.

Generate Return API#

The parameters generated by the processing API will be stored in the interface parameters scpi interface and will ultimately call the SCPI_Write() function. Currently, this function sends the return parameters via the uart4 port.

size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len);
Adds a header to arbitrary block data (#1 + byte data length)—to change the header, refer to the SCPI_ResultArbitraryBlockHeader() function definition, adds a \r\n tail, and calls the SCPI_Write() function to send it.

This function actually calls the following two functions:

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len);
Calculates the header that should be added to the data block and sends it.
size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len);
Sends the data block with error detection; if the arbitrary_reminding parameter of the context is less than the specified length len, it generates a system error SCPI_ERROR_SYSTEM_ERROR.

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data);
Finds the string with " and writes the string with " into the result (not a very useful API), possibly a FUNC.

size_t
SCPI_ResultBool(
 scpi_t * context,
 scpi_bool_t val);
Writes the bool value into the result.

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len);
Writes the raw string result to output, first sending a return separator ",", then sending the string.

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data);
Equivalent to the SCPI_ResultCharacters() string sending function, but this function's len is sizeof.
size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len);
Adds a header to arbitrary block data (#1 + byte data length)—to change the header, refer to the SCPI_ResultArbitraryBlockHeader() function definition, adds a \r\n tail, and calls the SCPI_Write() function to send it.

This function actually calls the following two functions:

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len);
Calculates the header that should be added to the data block and sends it.
size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len);
Sends the data block with error detection; if the arbitrary_reminding parameter of the context is less than the specified length len, it generates a system error SCPI_ERROR_SYSTEM_ERROR.

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data);
Finds the string with " and writes the string with " into the result (not a very useful API), possibly a FUNC.

size_t
SCPI_ResultBool(
 scpi_t * context,
 scpi_bool_t val);
Writes the bool value into the result.

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len);
Writes the raw string result to output, first sending a return separator ",", then sending the string.

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data);
Equivalent to the SCPI_ResultCharacters() string sending function, but this function's len is sizeof.

The following APIs for writing results will convert to strings in decimal if there are no Base parameters.

size_t
SCPI_ResultDouble(
 scpi_t * context,
 double val);
Writes a double precision value into the result, first sending a result separator ",", then sending the value.

The following API functions have similar logic to the above:
SCPI_ResultFloat(scpi_t * context,float val) writes a float value.
SCPI_ResultInt16(scpi_t * context,int16_t val) writes a signed 16-bit value.
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) writes an unsigned 16-bit value.
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) converts an unsigned 16-bit value to Base and converts it to a string for writing results.
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);

Generate Return API in Array Form#

Similar to the previous section's return-generating APIs, this section's APIs generate arrays to store results in the interface parameters scpi interface and trigger the SCPI_Write() function.

size_t 
SCPI_ResultArrayDouble(
    scpi_t * context,
    const double * array,
    size_t count,
    scpi_array_format_t format);
Converts count double elements pointed to by array into an array; format selects the system's endianness.

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);

Data to String API#

By default, no operations are performed on the return values of interface parameters.

size_t
SCPI_DoubleToStr(
    double val,
    char * str,
    size_t len);
Converts a double precision value to a string, assigning it to the address pointed to by str, with len being the maximum allowed buffer byte length.

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) converts the resulting string to 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);
Converts numbers under special rules to strings with units, which is the inverse function of SCPI_ParamNumber.

Extended Parameter Handling API#

scpi_bool_t
SCPI_ChoiceToName(
    const scpi_choice_def_t * options,
    int32_t tag,
        const char ** text);
Options is a table structure of {string, int_t data}; this function will look up the table based on the tag data and assign the corresponding string's address to the address pointed to by text.

scpi_bool_t
SCPI_ParamIsNumber(
    scpi_parameter_t * parameter,
    scpi_bool_t suffixAllowed);
Checks whether the parameter is of number type; usually used after obtaining the parameter with SCPI_Parameter(context, &param, mandatory).

scpi_bool_t
SCPI_ParamIsValid(
    scpi_parameter_t * parameter);
This function checks whether there was an error during the assignment function.

scpi_bool_t
SCPI_ParamToChoice(
    scpi_t * context,
    scpi_parameter_t * parameter,
    const scpi_choice_def_t * options,
    int32_t * value);
Options is a table structure of {string, int_t data}; this function looks up the parameter in the context and converts it to a string, usually used after obtaining the parameter with SCPI_Parameter(context, &param, mandatory).
scpi_bool_t
SCPI_ParamToDouble(
    scpi_t* context,
    scpi_parameter_t* parameter,
    double* value);
Converts the parameter to a double value, usually used after obtaining the parameter with SCPI_Parameter(context, &param, mandatory).

The following functions have similar logic:
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);
Obtains a parameter from the command line and assigns it to parameter; the parameter will also store data length and data type.

Command Handling API#

int32_t
SCPI_CmdTag(
    scpi_t* context);
Returns the detected command tag; not understood yet, pending testing.

scpi_bool_t
SCPI_CommandNumbers(
    scpi_t* context,
    int32_t* numbers,
        size_t len);
In the command list, specifies the allowed positions for numbers; in the handling function, these positions can be filled with numbers, with len being the array length, for example, {.pattern = "TEST#:NUMbers#", .callback = TEST_Numbers,}
If TEST3:NUMbers2 is received, SCPI_CommandNumbers(context,&data[0],2) can be used, then data[0] will be assigned 3, and data[1] will be assigned 2.
Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.