In this post, I attempt to understand the flows that the USBTMC class driver follows for:
- standard SCPI query / response
- standard SCPI command only
- *STB? status byte requests
- service requests
- triggers
For each scenario, I'll try to figure out how the logic flows, what flags are set or cleared, payloads, etc...
SCPI Query: *IDN?
main.c tud_task()
usbd.c tud_task_ext()
case DCD_EVENT_XFER_COMPLETE
if ( 0 == epnum ) -> else
usbtmc_device.c usbtmcd_xfer_cb()
case STATE_IDLE
case USBTMC_MSGID_DEV_DEP_MSG_OUT:
usbtmc_device.c handle_devMsgOutStart()
usbtmc_device.c handle_devMsgOut()
usbtmc_app.c tud_usbtmc_msg_data_cb()
if(transfer_complete && (len >=1)
query_received = true;
queryState = 1
scpi_instrument_input();
Reply
main.c usbtmc_app_task_iter()
usbtmc_app.c usbtmc_app_task_iter()
case querystate 1-> 2 case querystate 2:
querystate 3:
set MAV 0x10
set SRQ 0x40
case querystate 3:
// here I reply. I (or you :)) need to check what happens if the SCPI engine didn't generate acommand.
// that happens if the SCPI was a command, not a query.
// will it then do the right thing?
After a reply is complete, the usb_tmc.c state machine calls tud_usbtmc_msgBulkIn_complete_cb()
start side note 1
One thing I found, is that when I send a command (does not reply) instead of a query (replies), tud_usbtmc_msgBulkIn_complete_cb() is not called and MAV isn't reset....
This call originates from the usbtmc_device.c. A file that I do not have control over.
I am taking the "executive" decision" to clear that flag myself if the SCPI didn't return a value (because it got a command instead of a query). This is how I rewrote case 3 (transmit). Check the else clause at the end:
case 3: // time to transmit;
if(/* TODO check if I can just ignore this*/ bulkInStarted && (buffer_tx_ix == 0)) {
if(reply_len) // if this was a SCPI query, there will be a reply.
{
tud_usbtmc_transmit_dev_msg_data(reply, tu_min32(reply_len,msgReqLen),true,false);
queryState = 0;
bulkInStarted = 0;
reply_len = 0;
}
else
{
buffer_tx_ix = tu_min32(buffer_len,msgReqLen);
tud_usbtmc_transmit_dev_msg_data(buffer, buffer_tx_ix, buffer_tx_ix == buffer_len, false);
}
// MAV is cleared in the transfer complete callback.
}
else { // this is not thread safe. When you port this to a multi-threaded device, take care to put guard rails
// jc 20231004: when there is no reply from the scpi engine (the input was a command, not a query)
// mark as if a reply was sent
// MAV is cleared here
if(! reply_len) {
// if this was a SCPI query, reply_len woulkd be > 0
// and tud_usbtmc_msgBulkIn_complete_cb() would clear all of this.
uint8_t status = getSTB();
status &= (uint8_t) ~(IEEE4882_STB_MAV); // clear MAV
setSTB(status);
queryState = 0;
bulkInStarted = 0;
buffer_tx_ix = 0;
query_received = false;
}
}
This code should only be called when a SCPI string is sent that does not generate a reply (a command instead of a query)
end side note 1
start side note 2
or should I not set the MAV bit? if there's no reply? In the end, it's the Message Available Bit
end side note 2
todo
Top Comments
-
Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
ggabe
in reply to Jan Cumps
-
Cancel
-
Vote Up
+1
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
Jan Cumps
in reply to ggabe
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Comment-
Jan Cumps
in reply to ggabe
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Children