Need help with PSoC 1 USB HID Wake-up

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Pauven
Level 2
Level 2
10 sign-ins 10 replies posted 5 replies posted

I have successfully created a joystick button HID device using a PSoC 1.  It actually emulates 2 joysticks for a total of 56 buttons, which I use to interface with a pinball machine to read switch inputs from the playfield.  I'm using this connected via USB to a Windows PC.

It has one problem - I have to toggle an input before Windows reads any of the button values.  For example, in a normal pinball machine the balls are already pressing down on certain switches when you turn on the machine, plus some switches are normally closed and they open on triggering.  So at power on, out of 56 joystick buttons there might already be 5-10 buttons that are being pressed.

The problem is evident in the Windows configure USB gamepads tool, which let's you observe the current button states.  When I first open it up, it shows no buttons as being pressed.  I press any button at all, and this somehow wakes up the readout, and now I see all the various buttons registering presses.  But it gets worse - after a while of no use the button readouts seem to go to sleep again.  This is a problem in any typical robotic type implementation, where you need to read button presses at startup before you start making movements, otherwise something could be damaged.

I've seen a similar sleepy behavior in consumer programmable HID devices.  In fact, this was one of the reasons I decided to create my own HID solution, as a commercial developer said they were too busy to fix this issue.

I'm at a loss as to how to fix this.  Originally in my code I only sent the button readout if something changed.  I modified this to send a button state every 100 cycles regardless, thinking this would solve the issue, but it had no effect.

My actual code is longer to address all 56 buttons.  The shortened version below shows the exact same logic for the first 8 buttons - the omitted code is redundant.

What I'm most confused about is how triggering a GPIO input somehow wakes this up.  I don't see how that does anything that my Counter1 variable isn't also doing by forcing a data transmission.  Is my code getting hung-up on one of those while statements?  Does triggering a input somehow force an interrupt that resets my code execution?

I've looked through the USBFS datasheet, and a few things piqued my interest. USBFS_Force talks about remote wake-up functionality.  USBFS_UpdateHIDTimer talks about resetting an expired HID timer.  And the USB_TOGGLE parameter option on the USBFS_LoadInEP sounds like it could prevent packet loss.  But I fear I'm grasping at straws.

Any help would be greatly appreciated!!!

Paul

 

//----------------------------------------------------------------------------
// C main line
//----------------------------------------------------------------------------

#include <m8c.h>        // part specific constants and macros
#include "PSoCAPI.h"    // PSoC API definitions for all User Modules

BYTE GP1Buttons[1];

BYTE Data1ChangeFlag;
int Counter1 = 0;

struct { BYTE Report_ID; BYTE Buttons[1]; } GamePad1Report;

#define GAMEPAD1_RPT_LENGTH (sizeof(GamePad1Report))

void main(void)
{   OSC_CR0 |= 0x20;  //Sets the No Buzz bit in the OSC_CR0 register to keep the bandgap enabled during sleep, improving resume from sleep/wake-up performance
	
	// Enable global interrupts
    M8C_EnableGInt;
	
	GamePad1Report.Report_ID = 1;

	// Start USB and wait till enumeration complete	
	USBFS_Start(0, USB_5V_OPERATION);
	while(!USBFS_bGetConfiguration());
   
	USBFS_LoadInEP(1, (char*)&GamePad1Report, GAMEPAD1_RPT_LENGTH, USB_NO_TOGGLE); 
	
	// Enable Pull down resistors on All Ports
	PRT0DR = 0;
	PRT1DR = 0;
	PRT2DR = 0;
	PRT3DR = 0;
	PRT4DR = 0;
	PRT5DR = 0;
	PRT7DR = 0;
   
   while(1)
   {  ++Counter1;
	
	 /*BitShift the PSoC Port Bits to map them to Chameleon IO Gamepad Button Groups*/

	//	Buttons[0] = PRT4DR & 0XAA | ((PRT3DR & 0XAA) << 1);
		GP1Buttons[0] = (
		((PRT4DR & 0X80) >> 7) | // 10000000 > 00000001
		((PRT4DR & 0X20) >> 4) | // 00100000 > 00000010
		((PRT4DR & 0X08) >> 1) | // 00001000 > 00000100
		((PRT4DR & 0X02) << 2) | // 00000010 > 00001000
		((PRT3DR & 0X80) >> 3) | // 10000000 > 00010000
		((PRT3DR & 0X20)     ) | // 00100000 > 00100000
		((PRT3DR & 0X08) << 3) | // 00001000 > 01000000
		((PRT3DR & 0X02) << 6)); // 00000010 > 10000000

	//Check if any buttons have changed
      if(   (GP1Buttons[0] != GamePad1Report.Buttons[0])
		 || (GP1Buttons[1] != GamePad1Report.Buttons[1])
         || (GP1Buttons[2] != GamePad1Report.Buttons[2])
         || (GP1Buttons[3] != GamePad1Report.Buttons[3]) ) Data1ChangeFlag = 1;
	
      if((Data1ChangeFlag) || (Counter1 > 99)) {
         Data1ChangeFlag = 0;
		 Counter1 = 0;
		 GamePad1Report.Buttons[0] = GP1Buttons[0];

         while(!USBFS_bGetEPAckState(1));
         USBFS_LoadInEP(1, (char*)&GamePad1Report, GAMEPAD1_RPT_LENGTH, USB_TOGGLE); 
      }
	
   }
}
   
   

 

0 Likes
1 Solution

Hi Sampath,

Programming the PSoC 1 has been a little bit like trying to solve a puzzle.  The results have been very inconsistent and confusing.

I enabled the Watchdog, and did observe what appeared to be some Watchdog initiated resets, as the device would disappear and reappear from Windows.  This likely means my code was unexpectedly getting caught in some of the while loops.

The odd part is that the reset didn't occur until the PSoC had been non-responsive for about 30 seconds.  With a 1Hz Sleep_Timer, based upon the documentation I would expect the Watchdog to kick in between 1.5 and 4.5 seconds, quite a bit sooner.  I've never used the Watchdog feature before, so perhaps this extra latency is normal?

I did try increasing the Sleep_Timer to 64Hz, but then the PSoC didn't even connect to Windows, as if it simply did not exist.  Reverting to a 1Hz Sleep_Timer solved that.

When I tested with a 10K max loop counter, behavior was abysmal. Not only did I get the Watchdog resets, but general latency seemed horrible - much worse than not even having the loop counter at all.  Very odd.

But as I wrote above to odyssey1, I dropped the loop counter to 50, trying to achieve a 10ms / 100Hz HID update frequency, and somehow that solved this puzzle.

Not only did the Watchdog issues go away, latency is now great, and I no longer have the caught in a loop sleep issues that first prompted me to post this help request.  Even after several days of being idle, every time I check the button states they read out perfectly.

So I guess we can mark this one closed.  I think the primary fix was simply setting the loop counter to 50, aproximating the 100Hz update frequency.  I'm not sure if the Sleep ISR is doing anything now, as I think I'm avoiding sleep with the new loop counter.  And having the Watchdog enabled seems to make this solution robust over the long term.

View solution in original post

0 Likes
7 Replies
SampathS_11
Moderator
Moderator
Moderator
250 sign-ins 250 solutions authored 5 questions asked

Hello @Pauven ,

I can understand the description of the issue you have provided. 

Inside the code block of 

if((Data1ChangeFlag) || (Counter1 > 99)) {

can you toggle an LED connected to a GPIO to indicate Data1ChangeFlag? You can remove the condition on Counter1.

Also, do kindly elaborate the comment // Enable Pull down resistors on All Ports In what mode are the GPIOs? It would be helpful if you can attach the minimum project for us to review.

Best regards,

Sampath Selvaraj

0 Likes
odissey1
Level 9
Level 9
First comment on KBA 1000 replies posted 750 replies posted

Pauven,

Please note that the code in the loop is likely to  execute faster than 1us, so 100 Counts takes only 0.1ms, while USBFS packets can be sent at about 1 ms intervals only. So USBFS is flooded with requests. Try to increase the Counter Maximum from 100 to 1000 or 10000 to see if there is any effect. 

0 Likes
Pauven
Level 2
Level 2
10 sign-ins 10 replies posted 5 replies posted

Hi Sampath & odissey1, I appreciate the input.

On the PSoC 1 chip I am using, I have configured all 56 GPIO as Pull Down, and none are left over for controlling LED's.  I think that comment about enabling Pull Down on all ports was leftover from the original HID sample code I found for developing this project.  Additionally, the PSoC is already installed on the PCB with static charge dissipation components on all 56 GPIO, and I'm doubtful I could reconfigure one as an LED output in this type of circuit. 

I'm also not sure how toggling an LED on a GPIO would do anything. Perhaps you are suggesting that I trigger an input on one GPIO by toggling an output on another GPIO. While that would likely work to keep the PSoC awake, that seems expensive, as I would have to sacrifice 4 GPIO (two per logical HID Joystick controller), reducing my total button count from 56 down to 52. Pinball machines are incredibly complicated, and for some designs I would need every last input - I'm already worried that 56 may not be enough for some playfields. I would prefer extra code versus loosing GPIO.

Progress update. I've been developing code on 2 separate PC's, and didn't realize a code copy/paste mistake on the 2nd PC that I used to program the PSoC until after I posted my help request. I had completely forgotten to increase the counters on each loop, doh! I fixed that, plus made one other change. I had come to the conclusion that the PSoC was likely going asleep, so I added in a Sleep_ISR interrupt handler, and have the sleep timer set to 1Hz. Combined, both of these changes seemed to completely fix the issue - or so I thought.

I later connected my PSoC PCB to a different PC, and some of the problems came back, but were seemingly worse. I was finding the joystick buttons non-responsive at times, and had to power cycle the PSoC PCB to fix. I even had this occur randomly during active use, when it seems unlikely for the PSoC to go to sleep.

odissey1, thanks for the valuable tip! A while back I had done some latency testing, measuring round-trip from PSoC 1 input (the joystick emulator above) through a host PC and back to another PSoC1 handling PWM/PFM output. I had measured latency in the sub 10ms range, and I was mistakenly applying this knowledge to the loop above, thinking 100 loops would equate to around 1-5 seconds. It wasn't until you posted that I realized my blunder. Of course the loop runs much faster than my measured round-trip latency.

I had measured my highly optimized PWM/PFM output code at just under 12KHz, so every 12 loops would be 0.1ms. My joystick code is quite a bit simpler, so I would expect it to run even faster, but probably not quite 8 times faster. I can't think of a good way to measure without changing one of the inputs into an output to scope - do you know of any other way to measure loop performance?

In light of the new issues I'm experiencing, I'm wondering if flooding is the root cause. I've followed your advice, and I've cranked up the counter maximum from 100 to 10,000. Best guess is that results in an interval somewhere betwen 1 to 10 seconds.

I'm also adding a watchdog. If flooding isn't the issue, then my next best guess is that the code is getting caught in one of the while statements in the loop. I have those "while(!USBFS_bGetEPAckState(1));" statements in 3 places, right before sending USBFS_LoadInEP requests. Perhaps communications are getting out of sync, maybe due to flooding as you suggested, and then the code gets stuck in the while loop. That could explain why the only fix is to power cycle.

Side note: If I'm understanding the while loops above, wouldn't waiting on USBFS_bGetEPAckState(1) prevent flooding?

I'll be testing the watchdog and the 10k loop counter in the coming days. I'm hopeful the watchdog may resolve the last of the issues.

0 Likes

Pauven,

>>do you know of any other way to measure loop performance?

- I am not familiar with PSoC1. My guess that there should be some SysTick timer available. May I ask, why PSoC1 was selected for the design? 

0 Likes

I was able to hack my way to an approximate loop timer.  I disabled the switch input as a trigger to send updates to the PC, and instead only sent HID updates once every 10k loops.  Then on the PC I monitored to see approximately how long it took to receive button press updates.  

The max latency I observed was about 2 seconds, or about 5KHz loop frequency.  I was surprised by this, as I expected this loop to be at least as fast if not faster than my PWM/PFM loop, which runs at nearly 12KHz.  It's still plenty fast, though, so not an issue.

Based upon this, I've set the max counter to 50, half of the original 100 loops I tried initially.  If I did my napkin math right, this puts the regular HID updates over USB in the neighborhood of 10ms / 100Hz updates.   

Apparently USB flooding wasn't an issue after all.  So far 50 loop counter is working great.  I still have button press change detection running, so when there's an input change it should update the PC on the same loop for best possible latency.

Why did I choose the PSoC 1?  Well, I chose it back in 2014, and back then it seemed like a good choice for a value oriented solution to get to 56 discrete inputs/outputs.  In hindsight, it obviously wasn't the best possible option, but it got the job done.  Creating a pinball controller was my very first complex IC PCB design, and I have 3 PSOC 1's and a USB hub on a small PCB that allows me to easily wire up a custom pinball machine, with 56 inputs and 112 PWM/PFM outputs.

Unfortunately, as an amateur designer, the road to get here took longer than I expected.  The PSoC 1 I'm using is now out of production, and I only have enough on hand to make 10 more controllers, so any type of volume production ambitions I may have had are now moot.  I'll have to redesign the controller using different chips if I want to continue this project beyond a few more machines.

I am open to suggestions for chips.  So far I really like what the PSoC 1 has accomplished as a gamepad HID device, with 56 discrete button inputs using all GPIO.  Whatever replaces that part needs to have at least 56 GPIO, and preferably more if available (64 would be perfect).  Cheaper is better.  The PSoC 1 is technically 5x faster than needed for this purpose, as it easily maxes out the 1KHz HID update limit.  On a certain level, the PSoC 1 is way too sophisticated for this role, but I'm not aware of any stock off-the-shelf chips that provide the unique requirements I have.

For the outputs, I'll likely transition to a true LED lighting controller.  I've already picked out a potential candidate, and will be testing it near future.  It makes a lot more sense to use a dumb, dedicated PWM output controller versus what I did with the PSoC 1, but I didn't even know these things existed 8 years ago.  I will need to interface USB to SPI to control these lighting controllers, but I'm sure dedicated chips exist for this purpose and I wouldn't need anything near so complex as a PSoC, though a low end PSoC remains an option if better solutions don't exist.

I do have 1 more IO controller to design, a cabinet controller.  It needs to receive up to 16 inputs and 8 on/off outputs.  I was originally planning to use a lower end PSoC 1 with around 24 GPIO for this, but it would be foolish to use the PSoC 1 now.  I'd like to use a single chip for all functions, so a programmable PSoC might still be one of the better options for this purpose.  Maybe something in the PSoC 5 family?  I'd also like to include a simple USB hub for expandability, but I don't think any PSoC's have an integrated hub, do they?  The requirements are pretty basic, though, and I've contemplated using a Raspberry Pi Pico and a custom breakout board to be the cabinet controller.

I've also learned a lot about FCC certification requirements in the past few years, and as an amateur hobbyist this seems like something best avoided if possible.  Using Pi's/Espy's or other pre-made controller boards combined with my own custom break-out boards seems a wiser path forward for someone in my position.

0 Likes
SampathS_11
Moderator
Moderator
Moderator
250 sign-ins 250 solutions authored 5 questions asked

Hello @Pauven ,

If our issue has not yet been resolved, would you be able to get us a WireShark trace?

Best regards,

Sampath

0 Likes

Hi Sampath,

Programming the PSoC 1 has been a little bit like trying to solve a puzzle.  The results have been very inconsistent and confusing.

I enabled the Watchdog, and did observe what appeared to be some Watchdog initiated resets, as the device would disappear and reappear from Windows.  This likely means my code was unexpectedly getting caught in some of the while loops.

The odd part is that the reset didn't occur until the PSoC had been non-responsive for about 30 seconds.  With a 1Hz Sleep_Timer, based upon the documentation I would expect the Watchdog to kick in between 1.5 and 4.5 seconds, quite a bit sooner.  I've never used the Watchdog feature before, so perhaps this extra latency is normal?

I did try increasing the Sleep_Timer to 64Hz, but then the PSoC didn't even connect to Windows, as if it simply did not exist.  Reverting to a 1Hz Sleep_Timer solved that.

When I tested with a 10K max loop counter, behavior was abysmal. Not only did I get the Watchdog resets, but general latency seemed horrible - much worse than not even having the loop counter at all.  Very odd.

But as I wrote above to odyssey1, I dropped the loop counter to 50, trying to achieve a 10ms / 100Hz HID update frequency, and somehow that solved this puzzle.

Not only did the Watchdog issues go away, latency is now great, and I no longer have the caught in a loop sleep issues that first prompted me to post this help request.  Even after several days of being idle, every time I check the button states they read out perfectly.

So I guess we can mark this one closed.  I think the primary fix was simply setting the loop counter to 50, aproximating the 100Hz update frequency.  I'm not sure if the Sleep ISR is doing anything now, as I think I'm avoiding sleep with the new loop counter.  And having the Watchdog enabled seems to make this solution robust over the long term.

0 Likes