PSOC5LP and USB
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 change 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.
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!