PSOC5LP and USB — Update

I received a comment that the USB port on the CY8CKit-059 is causing a timeout. This is from a Thesis. I looked at the student’s code, and it is slightly different from my implementation, so I suspect something is missing. I borrowed the code from the automatically generated API and fixed some issues with it, and released my changes GPL, with a proviso that previous licenses are not overridden.

I have not changed previously posted code for USB. One of the readers of the blog who had issues traced their problems back to a bad board. There is a FreeRTOS on github under wmaxfield which is specific to the 059 board and good to use for a testbed.

During my work with the PSOC and USB, I was forced to document some of my issues for drivers within a file so I could easily recreate the environment on any new machines without having to do rediscovery. I hid it from myself in a file named Configuration.h. Nowhere close to USBSerial.c. Go figure.

Update: The instructions following apply to Windows 7 and early versions of Windows 10. With current updated version of Windows 10, and Windows 11, I have found the default USBUART settings for the USBUART component will operate without driver installs or any additional work. The USBUART typically shows up as COM3 in my experience.

Here is the text of Instructions to Myself (ITM) for the driver for a USB PSOC5 incarnation for windows 7 and Windows 10:

//--------------------------------------------------------------------------
// The PSOC CY8CKit-059 is an (up to) 80mzh processor with built in USB.
// It also has a USB finger stub which can be broken off. 
//--Finger STUB:
//  *IF YOU KEEP IT ATTACHED* you can communicate over the finger stub with an
//  additional serial port.  With that port, you can use the UART on it with 
//  TeraTerm or Putty, or a Linux or Mac machine (using "screen" or some 
//other app).  It must be in Kitprog mode. CMSIS-DAP won't work for kitprog, 
//but will for kitprog2 updated to kitprog3.
//  Under Windows, the signed driver for the stub will automatically install
//  when it is plugged in.  In Linux or Mac, it will show up in 
//     /dev/tty (ls /dev/tty.*)
//--MICRO-USB: 
//-----
//  Macintosh 
//  The USB UART (micro-USB) connector on the CY8C-Kit059 is under 
//          /dev/tty.usbmodemxxxxx
//  or      /dev/cu.usbmodemxxxxx as a tty device.  
//-----    
//  Linux:
//  The USB UART (micro-USB) connector on the CY8C-Kit059 is usually under 
//      /dev/ttyACM0. 
//  It can be under ttyACM1, ttyACM2,...ttyACM9.
//  To make world usable under Linux, then
//  create the following file as root: /etc/udev/rules.d/50-local.rules
//  using "sudo nano" to write the file,
//  with the following 2 lines
//
//   KERNEL=="ttyUSB[0-9]*",MODE="0666"
//   KERNEL=="ttyACM[0-9]*",MODE="0666"
//
//  this will make the plugin of the /dev/ttyACMx world usable without
//  running as root. Easiest is to reboot the linux machine to make this 
//  work.
//  You can restart the udev services rather than reboot.    
//-----
// Windows
//  The USB UART (micro-USB) driver is automatically created by PSOC Creator, 
//  and can be installed
//  under Windows 7 (unsigned driver) or under Windows 10 (signed).  See
//  the following information:  
//  https://socmaker.com/?p=168 for a discussion of Windows 7 Driver,
//--    
//  or
//     Windows 10 driver info: (cypress page)
//      https://community.cypress.com/message/57726#57726
//      (infineon page:)
//      https://community.cypress.com/t5/PSoC-5-3-1-MCU/PSoC5LP-USBUART-signed-driver/m-p/129820#57726
//    
//   The knowledge base article is here: (cypress page)
//     ( https://community.cypress.com/docs/DOC-10894 )
//      (infineon page)
//      https://community.cypress.com/t5/Knowledge-Base-Articles/Binding-a-USB-Serial-Device-to-a-Microsoft-CDC-Driver-KBA91366/ta-p/250126
//--
//  or
//
//    Windows 7 driver info:
//  After programming the PSoC, plug in the USBUART using a USB cable. 
//  You can interrupt the install, but that takes as long as waiting for the 
//  installer to give up talking to Windows Update Server.  Select the local 
//  file location option, and browse to the driver file in the PSOC directory 
//  in your project. It will be at:  
//     PROJECTNAME.cysdn\Generated_Source\PSOC5, 
//  assuming your project is named “PROJECTNAME.” Install that unsigned 
//  driver. 
//  You may have to Google allowing unsigned drivers for Windows 7. 
//  It should be functional after that. 
//    If not, try rebooting your VM if running under Mac or Linux or 
//    reboot your Windows machine if running natively.

Here is the FreeRTOS style code for using the USB port. Notice the “lineChanged” section to detect plug/unplugs:

/*
  * SOCino 3d printer firmware, FreeRTOS version
 *
 * Copyright 2020, Wade Maxfield
 * Written by Wade Maxfield
 *
 * Commercial license Available.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
    This license does not override previous licenses

    THIS TASK MONITORS AND KEEPS THE USB SERIAL PORT IN OPERATION, THROUGH
     PLUGS AND UNPLUGS.  IT ALSO PROVIDES USB SERIAL PORT UTILITY ROUTINES
    
 */
#include "main.h"
#include "Debug/TerminalDebug.h"
#include "Messages/MessageRouter.h"

#define USBFS_DEVICE (0)  // not sure why needed, is probably in future Cypress plans

#define USBSerialSTACK_SIZE		configMINIMAL_STACK_SIZE
const TickType_t xDelay = pdMS_TO_TICKS(1);
const TickType_t mDelay = pdMS_TO_TICKS(5000) ;

// these variables are modified at runtime to allow input from
// UART or from USB Serial.
// If this is to be standalone you MUST setup the Q here.
QueueHandle_t *USBMessageQPtr; // in the USB receive task 

SerialMessage *USBSerialRxBuffer[NUMBER_OF_MSG_BUFFERS+1];

int16_t currentBuffer;
    
/* The task that is created three times. */
static portTASK_FUNCTION_PROTO( vUSBSerialTask, pvParameters );
SemaphoreHandle_t USBMutex;
static int16_t qError;


/*-----------------------------------------------------------*/
// allow multiple task access through semaphores.
// Blocks until sends or a failure occurs.
// Fails silently
/*-----------------------------------------------------------*/
void USBport_putString( char *msg)
{
    int16 hold=0;
    if(0 == USBUART_GetConfiguration()) 
        return;
    // the following has to be checked more than once,
    // since at ~80mhz bus clock the USB can report
    // no activity between calls.
    // if 20 milliseconds have passed, the USB is 
    // probably unplugged.
    while (0==USBUART_CheckActivity()){
        if (++hold>20){
            xSemaphoreGive(USBMutex);
            return;
        }
        vTaskDelay(xDelay);// wait 1 millisecond
    }
    hold=0;  
  
    xSemaphoreTake(USBMutex,portMAX_DELAY);
    while(0 == USBUART_CDCIsReady()){
        // if an error in the uart, skip.
        if (++hold>10){
            xSemaphoreGive(USBMutex);
            return;
        }
        vTaskDelay(xDelay);
    }

    if (USBUART_CDCIsReady())
        USBUART_PutString(msg);
        
    xSemaphoreGive(USBMutex);
}

/*-----------------------------------------------------------*/
// create the task and the serialization semaphore
/*-----------------------------------------------------------*/
void vStartUSBSerialTask( UBaseType_t uxPriority )
{
    
    /*Setup the mutex to control port access*/
    USBMutex = xSemaphoreCreateMutex();
	/* Spawn the task. */
	xTaskCreate( vUSBSerialTask, "USBSerial", USBSerialSTACK_SIZE, NULL, uxPriority, ( TaskHandle_t * ) &TaskMonitorArray[USB_SERIAL_TASK].taskHandle);

}
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
static portTASK_FUNCTION( vUSBSerialTask, pvParameters )
{
    uint16_t rxCount;
    uint16_t myTaskMsgBufferNumber=0;
    TaskMonitor_t *TaskMonitorPtr = &TaskMonitorArray[USB_SERIAL_TASK];
    static uint16_t firstTime=pdFALSE;
    
   	/* The parameters are not used. */
	( void ) pvParameters;
    
    int index;
    
    for (index=0; index < NUMBER_OF_MSG_BUFFERS; index++) {
        if (!USBSerialRxBuffer[index]) // did someone else handle?
            USBSerialRxBuffer[index]=(SerialMessage *) pvPortMalloc( sizeof(SerialMessage));   
    }

    DebugPrintString("USBSerial Task Started..." CRLF);
    // allow queues to be set up, etc.
    vTaskDelay(pdMS_TO_TICKS(50));
        
// This code provides a message router between uart and usb if needed
//    HandleMessageRoutingSetup();// sets up USBMessageQPtr


  //!!!!!!! WARNING  !!!!!!!!!!!!
  // set up the Queue pointer properly here if you do not have
  // an external message router.  In original code, this was
  // already done in HandleMessageRoutingSetup();
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    /* Start the USB_UART */
    /* Start USBFS operation with 5-V operation. */
    // This, in theory, should be in HardwareInit.c
    USBUART_Start(USBFS_DEVICE, USBUART_5V_OPERATION);
    firstTime = pdFALSE;
   
    
	for(;;)
	{
        TaskMonitorPtr->runCounter++;
        
        /* Host can send double SET_INTERFACE request. */
        if (0u != USBUART_IsConfigurationChanged())
        {
            /* Initialize IN endpoints when device is configured. */
            if (0u != USBUART_GetConfiguration())
            {
                /* Enumeration is done, enable OUT endpoint to receive data 
                 * from host. */
                USBUART_CDC_Init();
            }
        }
        
        if(0 != USBUART_GetConfiguration()){  
            if (USBUART_IsLineChanged())
                firstTime = pdTRUE;
            
            if (firstTime){
                // every time a connect or change of baud, etc. occurs
                // print the signon string
                firstTime = pdFALSE;
                USBport_putString(START_STRING CRLF);
            }
            /* Check for input data from host. */
            if (0u != USBUART_DataIsReady()){
                SerialMessage *ptr = USBSerialRxBuffer[myTaskMsgBufferNumber];
                /* Read received data and re-enable OUT endpoint. */
                rxCount = USBUART_GetAll(ptr->msg);
                // now post these bytes to the Debug Terminal Task
                ptr->ucMessageID = myTaskMsgBufferNumber++;
                ptr->size=rxCount;
                ptr->msg[rxCount]=0;// terminate string just in case
                
                
                // if Message queue not assigned, don't use
                if (USBMessageQPtr){// see MessageRouter.c
                    //------------------------------------------------------
                    // message will either post to GCodeCommandReceiverMessageQ (in GCodeHandlerTask.c)
                    // or TerminalSerialMessageQ (in TerminalDebug.c)
                    // General Message Flow:
                    // GCodeCommandReceiverMessageQ->GCodeHoldOffMessageQ->gMotorCommandQueue
                    //------------------------------------------------------
                    if( QSend( *USBMessageQPtr,&ptr, pdMS_TO_TICKS(50),TaskMonitorPtr ) != pdPASS )
                		{
                			qError ++;
                            //try one more time
                            QSend( *USBMessageQPtr,&ptr, pdMS_TO_TICKS(50),TaskMonitorPtr);
                		}
                    if (myTaskMsgBufferNumber >= NUMBER_OF_MSG_BUFFERS)
                        myTaskMsgBufferNumber=0; // reset the ring
                }
                /* Get and process inputs here */
                //vTaskDelay(mDelay);
            } else {
                // let someone else run, wait 50 milliseconds
                vTaskDelay(pdMS_TO_TICKS(50));
            }

        } else {
            // let someone else run, wait 50 milliseconds to see if event occurs
            vTaskDelay(pdMS_TO_TICKS(50));
        }


    } /*lint !e715 !e818 !e830 Function definition must be standard for task creation. */

}

// end of file

Fini

Enjoy!

Add a Comment

Your email address will not be published. Required fields are marked *