In this post, I add a new SCPI query to the USBTMC and SCPI compliant instrument I developed for the Pico.
At the end of the blog you'll find the VSCode project with source and binaries. |
You'll see that it's very easy to support new SCPI commands, once you have the basics working.
what we have until now: Existing SCPI command supported by the firmware
In the previous post, the project supported one SCPI command:
DIGI:OUTP# 0|1
This command turns a Pico GPIO output high or low.
# represents the channel you want to turn on or off.
Example:
DIGI:OUTP2 1
This command drives the Pico GPIO output on channel 2 high. The channels are an index to an array of GPIO pins. I predefined 3 pins, but you can make an array of all the GPIOs. The firmware is (and because I wrote it: I am ) smart enough to adapt to your array.
// supported pins
uint pins[] = {22, 14, 15};
The example SCPI command above would switch GPIO 15 high.
This is the code that does that:
static scpi_result_t SCPI_DigitalOutput(scpi_t * context) { scpi_bool_t param1; int32_t numbers[1]; // retrieve the output index SCPI_CommandNumbers(context, numbers, 1, 0); if (! ((numbers[0] > -1) && (numbers[0] < pinCount()))) { SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SUFFIX); return SCPI_RES_ERR; } /* read first parameter if present */ if (!SCPI_ParamBool(context, ¶m1, TRUE)) { return SCPI_RES_ERR; } setPinAt(numbers[0], param1 ? true : false); return SCPI_RES_OK; }
and this is how we tell the SCPI parser that the above function will handle the command:
{.pattern = "DIGItal:OUTPut#", .callback = SCPI_DigitalOutput,},
what we add in this post: a new SCPI Query
I'm adding a new command that can query the state of a pin. The first command changed a state and did not give a reply. This one doesn't change the state of any pins, but does return a reply: the current pin state. The query command will look like this:
DIGI:OUTP#?
Alsmost the same as the set command, but without a parameter and with a question mark at the end.
First, let's write a function that returns the pin state. This is Pico specific. If you use another controller, you 'd use the API of that device.
bool isPinAt(uint32_t index) { return gpio_get_out_level(pins[index]); }
Then the handler of the query command:
static scpi_result_t SCPI_DigitalOutputQ(scpi_t * context) { int32_t numbers[1]; // retrieve the output index SCPI_CommandNumbers(context, numbers, 1, 0); if (! ((numbers[0] > -1) && (numbers[0] < pinCount()))) { SCPI_ErrorPush(context, SCPI_ERROR_INVALID_SUFFIX); return SCPI_RES_ERR; } SCPI_ResultBool(context, isPinAt(numbers[0])); return SCPI_RES_OK; }
And as last step, train the SCPI parser:
{.pattern = "DIGItal:OUTPut#?", .callback = SCPI_DigitalOutputQ,},
All the changes are simple. If this is hard to follow, comment on this post.
Test the added command
I used the NI-VISA Interactive Control utility to test the new command. It's a great tool to verify if your instrulment will be supported by the major players.
1: Write Operation (*IDN?) 2: Read Operation PICO-PI,LABSWITCH,0,01.00 3: Write Operation (DIGI:OUTP0 1) 4: Write Operation (DIGI:OUTP0?) 5: Read Operation 1 6: Write Operation (DIGI:OUTP0 0) 7: Write Operation (DIGI:OUTP0?) 8: Read Operation 0
I edited the log, removed additional info that the utility adds, and CRLFs
Success!
The attached project has additional changes.
|
VSCode project, sources and binaries
Here's the VSCode project archive. It contains the binary uf2 file. You can drag this on your Pico to program it without building from source.
Don't forget to add these two variables to your VSCode user environment: