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