element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Test & Tools
  • Technologies
  • More
Test & Tools
Blog understand TinyUSB USBTMC protocols
  • Blog
  • Forum
  • Documents
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Test & Tools to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 4 Oct 2023 4:14 PM Date Created
  • Views 3852 views
  • Likes 10 likes
  • Comments 25 comments
  • pico_usbtmc_scpi
  • USBTMC
  • Pico SCPI labTool
  • labview
  • scpi
Related
Recommended

understand TinyUSB USBTMC protocols

Jan Cumps
Jan Cumps
4 Oct 2023

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

  • Sign in to reply

Top Comments

  • ggabe
    ggabe over 2 years ago in reply to Jan Cumps +1
    The SRQ bit is only set after a certain delay (125ms), for strictly testing purposes with the included python test client. I rebuilt the test example, and the python test cases passing pedantically.…
  • ggabe
    ggabe over 2 years ago in reply to Jan Cumps +1
    The tinyusb build puzzles me. I wanted to add to following function to usbtmc_device.c (and I did), but the usbtmc_app.c breaks at linking time. I don't know what else to say in the tinyusb codebase, neither…
  • ggabe
    ggabe over 2 years ago in reply to ggabe +1
    Some progress: creates a proper Visa handled SRQ, when invoked from tud_usbtmc_msg_trigger_cb, no errors in IO Trace: index 0cf0743a7..e94e7c062 100644 --- a/src/class/usbtmc/usbtmc_device.c +++ b/src…
Parents
  • Jan Cumps
    Jan Cumps over 2 years ago

     ggabe , the TinyUSB example sets SRQ bit (0x40) in the STB when it receives a SCPI command. https://github.com/hathach/tinyusb/blob/b394ae1786911a89ea3437d7090cda477ca7df6c/examples/device/usbtmc/src/usbtmc_app.c#L219


    I ported this to the firmware

    Are you aware that this is normal? I start to expect that this is a test case, not default behaviour ...

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ggabe
    ggabe over 2 years ago in reply to Jan Cumps

    The tinyusb build puzzles me. I wanted to add to following function to usbtmc_device.c (and I did), but the usbtmc_app.c breaks at linking time. I don't know what else to say in the tinyusb codebase, neither the other app functions, like tud_usbtmc_start_bus_read(void); declares anything special to get included in the library.

    bool tud_usbtmc_send_srq(void); 

     

    usbtmc_app.c:(.text.tud_usbtmc_msg_trigger_cb+0xc): undefined reference to `tud_usbtmc_send_srq'

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to ggabe

    ... side thought. All the TinyUSB code is called from our main loop. 

    A first attempt may be to try and fit the *SRQ code in there.
    Either by adding a call before or after the tud_task()  

    or by creating our own wrapper or replacement for tud_task() , with the specific code...

    It may allow us to test things out without changing the lib. Once we know what base changes are needed, we could think about pull requests to tinyusb ...

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ggabe
    ggabe over 2 years ago in reply to ggabe
    broken linking mystery solved: needs TU_ATTR_WEAK
    TU_ATTR_WEAK bool tud_usbtmc_send_srq(void); 
    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to ggabe

    if the TU functions are weak in the library, we should be able to overwrite them,

    by providing a non-weak (for the rest identical signature) replacement implementation in our project sources.

    In that case, the linker will use our implementation, not the Lib one...

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ggabe
    ggabe over 2 years ago in reply to ggabe

    Some progress: creates a proper Visa handled SRQ, when invoked from tud_usbtmc_msg_trigger_cb, no errors in IO Trace:

    index 0cf0743a7..e94e7c062 100644
    --- a/src/class/usbtmc/usbtmc_device.c
    +++ b/src/class/usbtmc/usbtmc_device.c
    @@ -880,4 +880,42 @@ bool usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request
       }
     }
     
    +#if (CFG_TUD_USBTMC_ENABLE_488)
    +bool tud_usbtmc_send_srq(void) 
    +  {
    +    usbtmc_read_stb_rsp_488_t rsp;
    +    
    +    rsp.bTag = 0x01; //Indicates SRQ
    +    if(usbtmc_state.ep_int_in != 0)
    +    {
    +      rsp.statusByte = 0x00; // Use interrupt endpoint, instead. Must be 0x00 (USB488v1.0 4.3.1.2)
    +      if(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in))
    +      {
    +        rsp.USBTMC_status = USB488_STATUS_INTERRUPT_IN_BUSY;
    +      }
    +      else
    +      {
    +        rsp.USBTMC_status = USBTMC_STATUS_SUCCESS;
    +        usbtmc_read_stb_interrupt_488_t intMsg =
    +        {
    +          .bNotify1 = {
    +              .one = 1,
    +              .bTag = 0x01 //Indicates SRQ
    +          },
    +          .StatusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status))
    +        };
    +        // Must be queued before control request response sent (USB488v1.0 4.3.1.2)
    +        usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, (void*)&intMsg, sizeof(intMsg));
    +      }
    +    }
    +    else
    +    {
    +      rsp.statusByte = tud_usbtmc_get_stb_cb(&(rsp.USBTMC_status));
    +    }
    +    //TU_VERIFY(tud_control_xfer(usbtmc_state.rhport, request, (void*)&rsp, sizeof(rsp)));
    +    return true;
    +  }
    +#endif
    +
     #endif /* CFG_TUD_TSMC */
    +
    diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
    index c1298ddb8..fb3fcd873 100644
    --- a/src/class/usbtmc/usbtmc_device.h
    +++ b/src/class/usbtmc/usbtmc_device.h
    @@ -95,6 +95,9 @@ bool tud_usbtmc_transmit_dev_msg_data(
     
     bool tud_usbtmc_start_bus_read(void);
     
    +#if (CFG_TUD_USBTMC_ENABLE_488)
    +bool tud_usbtmc_send_srq(void); 
    +#endif
     
     /* "callbacks" from USB device core */
     
    @@ -104,6 +107,7 @@ bool     usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
     bool     usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
     void     usbtmcd_init_cb(void);
     
    +
     /************************************************************
      * USBTMC Descriptor Templates
      *************************************************************/
    

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • ggabe
    ggabe over 2 years ago in reply to ggabe

    Good news: SRQ callbacks work with PyVisa!

    Beside the diff above to TinyUSB, this update makes it complete, in 

    /source/usb/usbtmc_app.c

    void setControlReply () {
      tud_usbtmc_send_srq();
    }
    
     

    In PyVisa, when the event handler fires, it only needs to acknowledge with a *CLS
    Fairly robust, not seen any crashes or contentions (none is expected, per the single threaded polling based approach to events on the Pico side of the house)

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Comment
  • ggabe
    ggabe over 2 years ago in reply to ggabe

    Good news: SRQ callbacks work with PyVisa!

    Beside the diff above to TinyUSB, this update makes it complete, in 

    /source/usb/usbtmc_app.c

    void setControlReply () {
      tud_usbtmc_send_srq();
    }
    
     

    In PyVisa, when the event handler fires, it only needs to acknowledge with a *CLS
    Fairly robust, not seen any crashes or contentions (none is expected, per the single threaded polling based approach to events on the Pico side of the house)

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
  • Jan Cumps
    Jan Cumps over 2 years ago in reply to ggabe

    Do we have to put tud_usbtmc_send_srq() in the tinyusb sources?

    Maybe if we put it in our sources, and declare:

    extern ... usbtmc_state;

    We can leave the lib sources alone.

    Then suggest a pull request for the tinylib, and remove our code once it becomes part of a picosdk ....

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube