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 mini project: PICO-PI programmable Lab Switch - 5: use the USB Port as COM
  • 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: 22 Oct 2022 11:43 AM Date Created
  • Views 3933 views
  • Likes 7 likes
  • Comments 7 comments
  • pico_freertos_scpi_switch
  • pico_eurocard
Related
Recommended

mini project: PICO-PI programmable Lab Switch - 5: use the USB Port as COM

Jan Cumps
Jan Cumps
22 Oct 2022

In this series, I design a programmable lab switch based on RP2040, that can turn a set of signals or supplies on or off. Over USB. SCPI compatible.
In this post: enable the USB as communication channel.

image

Differences with UART approach

The design in the previous posts used low level UART0 HAL API. With interrupts to detect incoming traffic. The stdio (easy) flavour of the TinyUML library doesn't support this approach though. That library is also interrupt driven, but they are different types of interrupts, and not interceptable. Shabaz and I have been reviewing options, and he suggested inbound traffic polling, at a 50 ms rate. That's what I'm going for here.

The stdio usb library allows you to talk via printf - and the other typical stdio functions. You don't need to go into the lower level USB API and create your own CDC. It's a convenience layer that will fit many scenarios.

Enable UART0 via USB instead of via the UART TX/RX pins

It's simple. Add these two lines to the CMalke script, and the build will use the stdio_usb library. You'll still talk using a handle called uart0, but the layer will transfer it to the RP2040 USB peripheral.

 pico_enable_stdio_usb(scpi_switch 1)  
 pico_enable_stdio_uart(scpi_switch 0)

In your code, add these 2 includes (will work for both the UART and USB version of the lib: #include <stdio.h> #include "pico/stdlib.h" The main effect is that the stdio is routed to USB with the two lines in the CMake file. They 'll go to the UART pins if they aren't there (or commented out).

In your code, you can use these two defines if you need different implementations for the two choices. In my project, It's not a must - I could use polling for both UART and USB. But that UART with interrupts was half the story I wanted to bring in this mini series (learn FreeRTOS with interrupts, task messaging, ...). So I leave it in. Here's how I distinguish between the two options in C:

/* 
the PURE UART version of the code is compiled if the STDIO_UART lib is used.
this is default for the SDK, except when you put these commands in the CMake script:
# enable usb output, disable uart output
 pico_enable_stdio_usb(scpi_switch 1)
 pico_enable_stdio_uart(scpi_switch 0)
*/

#if LIB_PICO_STDIO_UART

// ...

#endif

/* 
the TINYUSB UART version of the code is compiled if the STDIO_USB lib is used.
this is the case when you put these commands in the CMake script:
# enable usb output, disable uart output
 pico_enable_stdio_usb(scpi_switch 1)
 pico_enable_stdio_uart(scpi_switch 0)
*/

#if LIB_PICO_STDIO_USB

// ...

#endif

The Original UART code, with interrupt mechanism, now wrapped so that it only compiles if the usb library is not selected.:

/* 
the PURE UART version of the code is compiled if the STDIO_UART lib is used.
this is default for the SDK, except when you put these commands in the CMake script:
# enable usb output, disable uart output
 pico_enable_stdio_usb(scpi_switch 1)
 pico_enable_stdio_uart(scpi_switch 0)
*/
#if LIB_PICO_STDIO_UART


#define UART_ID uart0

/* Stores the handle of the task that will be notified when the
 receive is complete. */
volatile TaskHandle_t xTaskToNotify_UART = NULL;


void UART_receive();

void uart_task(void *pvParameters) {

    /* To avoid compiler warnings */
    (void) pvParameters;
    uint32_t ulNotificationValue;
    xTaskToNotify_UART = NULL;
    scpi_instrument_init();


    // TODO semaphore

    while (true) {

        /* Start the receiving from UART. */
        UART_receive();
        /* Wait to be notified that the receive is complete.  Note
         the first parameter is pdTRUE, which has the effect of clearing
         the task's notification value back to 0, making the notification
         value act like a binary (rather than a counting) semaphore.  */
        ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

        if (ulNotificationValue == 1) {
            /* Handle received data */
            while (uart_is_readable(UART_ID)) 
            {
                rxChar = uart_getc(UART_ID);
                if (rxChar == 255) break;
                scpi_instrument_input((const char *)&rxChar, 1);
                
                /* TODO this will be the character handler

                if (telemetry_queue) {
                    // queue needs to be generated}
                    if (xQueueSend(telemetry_queue,
                                   (void *)(&rxChar),
                                   (TickType_t)10) != pdPASS)
                    {
                        // Failed to post the message, even after 10 ticks.
                        // Force an assert
                        configASSERT((volatile void *)NULL);                    }
                }
                */
            }
        }
    }
}

void initUART() {
    // Turn off FIFO's - we want to do this character by character
    uart_set_fifo_enabled(UART_ID, false);
    // Set up a RX interrupt
    // We need to set up the handler first
    // Select correct interrupt for the UART we are using
    int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;

    // TODO set USB handler if needed 
    // irq_has_shared_handler(USBCTRL_IRQ)

    // And set up and enable the interrupt handlers
    irq_set_exclusive_handler(UART_IRQ, UART_Isr);
    irq_set_enabled(UART_IRQ, true);
}

// UART interrupt handler
void UART_Isr() {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    // Now disable the UART to send interrupts
    uart_set_irq_enables(UART_ID, false, false);

    if (xTaskToNotify_UART != NULL) {

        /* Notify the task that the receive is complete. */
        vTaskNotifyGiveFromISR(xTaskToNotify_UART, &xHigherPriorityTaskWoken);
        /* There are no receive in progress, so no tasks to notify. */
        xTaskToNotify_UART = NULL;

        /* If xHigherPriorityTaskWoken is now set to pdTRUE then a
         context switch should be performed to ensure the interrupt
         returns directly to the highest priority task.  The macro used
         for this purpose is dependent on the port in use and may be
         called portEND_SWITCHING_ISR(). */
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

// UART activate a receive with interrupt. Wait for ever for UART_BUFFER_SIZE bytes
void UART_receive() {
    /* At this point xTaskToNotify should be NULL as no receive
     is in progress.  A mutex can be used to guard access to the
     peripheral if necessary. */
    configASSERT(xTaskToNotify_UART == NULL);

    /* Store the handle of the calling task. */
    xTaskToNotify_UART = xTaskGetCurrentTaskHandle();
    // Now enable the UART to send interrupts - RX only
    uart_set_irq_enables(UART_ID, true, false);
}

void UART_write(const char * data, uint32_t len) {
    uart_puts(UART_ID, data);
}
#endif

And the new USB code, with polling. Wrapped so that it only compiles if the usb library is selected.

* 
the TINYUSB UART version of the code is compiled if the STDIO_USB lib is used.
this is the case when you put these commands in the CMake script:
# enable usb output, disable uart output
 pico_enable_stdio_usb(scpi_switch 1)
 pico_enable_stdio_uart(scpi_switch 0)
*/

#if LIB_PICO_STDIO_USB
void uart_task(void *pvParameters) {

    /* To avoid compiler warnings */
    (void) pvParameters;
    scpi_instrument_init();


    // TODO semaphore
    bool bHasChar;
    while (true) {
        vTaskDelay((uint32_t)(50 / portTICK_PERIOD_MS)); // sleep 50 ms
        bHasChar = true;
        while (bHasChar) {
            rxChar = getchar_timeout_us(0); // don't wait for characters
            if (rxChar == 255) {
                bHasChar = false;
            } else {
                scpi_instrument_input((const char *)&rxChar, 1);
            }
        }
    }
}

void initUART() {
    // USB mode does not need additional settings. It's polling vs interrupt
    return;
}

void UART_write(const char * data, uint32_t len) {
    printf(data);
}

#endif

The polling loop: The task will sleep 50 ms, and yields that time to the RTOS and other tasks. Then it'll check (poll) if there's traffic. A NOWAIT BLOCKING read call. The effect is that it 'll read if there's traffic. If no traffic, it'll fail immediately and return. 

That's it. If you apply the CMake settings with the 2 lines in the CMake script, the code will compile, using the second part. You now need to connect a USB cable to the device being programmed / debugged. A COM port will pop up, and you can talk to it, just like to the initial UART scenario.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

Source and binaries attached. Includes your homework from post 4.

scpi_switch_binary_20221022.zip

freertos_scpi_switch_20221022.zip

Show all blog posts

  • Sign in to reply
Parents
  • Jan Cumps
    Jan Cumps over 2 years ago

    If you are using a Pico-PI to drive your relay, and your relay break-out is for an Arduino, the "Relay May Not Click with Confidence". Check this post.

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

    If you are using a Pico-PI to drive your relay, and your relay break-out is for an Arduino, the "Relay May Not Click with Confidence". Check this post.

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
Children
No Data
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