Inconsistent Messaging Behavior (LUFA Serial Comms)

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have the joy of debugging a C program on my Atmega16u2 using only serial comm messages over USB. I'm noticing weird behavior; the debug strings i'm trying to send in the

if(firstRun){
    ...
}

section of my code never show up in my serial comm. But, the message "Hi from else" in the else block DOES in fact show up in the serial terminal.

 

The obvious thought would be that firstRun is never true, but I initialize this boolean as true and I don't change it until the logic inside my if statement above has run. Why am I not seeing my messages "Before calc call" and "After calc call"? 

int baseAcceleration = 0;			//running total average acceleration
int instantBaseAcceleration = 0;
int maxAcceleration = 255;
int aggressiveStrumThreshold = 0;

bool firstRun = true;
bool cap1188Selected = false;
bool fxls8471Selected = false;

static FILE USBSerialStream;

char* convertIntToString(uint8_t integerValue, char* str){
	utoa(integerValue, str, 10);
	return str;
}

char* concat(char *s1, char *s2)
{
	char *result = malloc(strlen(s1)+strlen(s2)+1);//+1 for the zero-terminator
	//in real code you would check for errors in malloc here
	strcpy(result, s1);
	strcat(result, s2);
	return result;
}

/************************************************************************/
/* Configure Port B SPI hardware registers                              */
/************************************************************************/
void SetupSPIHardware(void)
{
	//toastMsg("Hello from SetupSPIHardware \n");
	/* Set MOSI and SCK output, all others input */
	DDRB = (1 << DDB2)|(1 << DDB1);
	/* Enable SPI, Master, set clock rate fck/16 */
	SPCR = (1 << SPE)|(1 << MSTR)|(1 << SPR0);

	//CDC_Device_SendString(&VirtualSerial_CDC_Interface, "SPI done");
	//strcat(outputString, "B_");
}

void writeSPI(char cData)
{
	/* Start transmission */
	SPDR = cData;
	/* Wait for transmission complete */
	while(!(SPSR & (1<<SPIF)));
}

char readSPI(char cData)
{
	/* Start transmission */
	SPDR = cData;

	/* Wait for transmission complete */
	while(!(SPSR & (1<<SPIF)));

	CDC_Device_SendString(&VirtualSerial_CDC_Interface, "spi read");

	/* Return data in SPDR (it was replaced by read data)*/
	return SPDR;
}

void selectFXLS8471(void){
	PORTD |= (1 << PD4);	//set Touch SPI SS to 1
	PORTC &= ~(1 << PC6);	//set Accel SPI SS to 0 (selected)

	fxls8471Selected = true;
	cap1188Selected = false;
}

void deselectFXLS8471(void){
	PORTC |= (1 << PC6);	//set Accel SPI SS to 1
	fxls8471Selected = false;
}

void SetupFXLS8471Hardware(void){
	selectFXLS8471();

	//FIFO setup
	writeSPI(0x89);		//write to FIFO setup reg
	writeSPI(0x00);
	writeSPI(0x40);		//Enable circular FIFO buffer of motion data (sets F_MODE to 01)
	deselectFXLS8471();	//ends SPI write to current address

	//Data Precision setup
	selectFXLS8471();
	writeSPI(0xAA);		//write to Ctrl Reg 1
	writeSPI(0x00);
	writeSPI(0x00);		//set to standby mode so this reg can be edited
	deselectFXLS8471();
	selectFXLS8471();
	writeSPI(0xAA);
	writeSPI(0x00);
	writeSPI(0x06);		//set to 1.25ms data rate, low-noise, fast-read (8-bit precision) and active mode
	deselectFXLS8471();

}

int calcBaselineAcceleration(void){
	CDC_Device_SendString(&VirtualSerial_CDC_Interface, " Hello from calcBaselineAcceleration "); //test:working msg system
	selectFXLS8471();

	writeSPI(0x00); //get number of samples
	readSPI(0x00);
	char fStatus = readSPI(0x00);
	char mask = 0x3F;
	char numSamples = fStatus & mask;

	int runningSum = 0;
	int baselineAccel = 0;

	if (numSamples > 0){
		writeSPI(0x01);							//Begin reading from head of FIFO buffer
		readSPI(0x00);
		for(int i = 0; i < numSamples; i++){
			readSPI(0x00);						//X Axis Data
			readSPI(0x00);						//Y Axis Data
			runningSum += readSPI(0x00);		//Z Axis Data
		}
	}
	return baselineAccel = runningSum/numSamples;
}

/** Main program entry point. This routine contains the overall program flow, including initial
 *  setup of all components and the main program loop.
 */
int main(void){
	//configure CPU clock
	CLKSEL0 |= (1<<CLKS);	//select external clock
	CLKPR = 0x80;			//enable clock speed edits
	CLKPR |= (1<<CLKPS0);	//divide clock speed by 2: 16MHz/2 = 8MHz (for SPI bus constraint)

	//Set SPI SS pins as outputs
	PORTD |= (1 << PD4);  //pre-set SS lines high to avoid multi-selected slaves scenario
	PORTC |= (1 << PC6);
	DDRD = 0x10;	//set PORTD pin 4 (PD4) to output for CAP1188 SS
	DDRC = 0x40;	//set PORTC pin 6 (PC6) to output for FXLS8471 SS

	//Configure the Atmega's USB hardware
	SetupUSBHardware();

	//Configure the Atmega's SPI hardware
	SetupSPIHardware();

	//Configure the FXLS8471 Accelerometer
	SetupFXLS8471Hardware();

	GlobalInterruptEnable();

	while (1) {
		//TODO: Why aren't firstrun messages showing?
		if (firstRun){
			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Before calc call"); //test:working msg system

			baseAcceleration = calcBaselineAcceleration();

			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "After calc call"); //test:working msg system

			firstRun = false;
		}
		else{
			baseAcceleration = (instantBaseAcceleration + baseAcceleration)/2;	//update average acceleration
			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Hi from else"); //test:working msg system
		}

		CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
		CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
		USB_USBTask();
	}
}

...

 

 

Last Edited: Tue. Nov 7, 2017 - 09:53 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not familiar with LUFA details, but are you not sending your initial messages too early, i.e. before the USB connection with whatever host has been established?

Perhaps a potential solution: http://www.avrfreaks.net/comment/861806#comment-861806

 

Edit: link

Last Edited: Wed. Nov 8, 2017 - 11:20 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ccrause wrote:
but are you not sending your initial messages too early, i.e. before the USB connection with whatever host has been established?
That'd be my guess too. Things in LUFA don't really happen until the call to USB_USBTask() so maybe pepper some more calls to that around the "early" code? Maybe the same too for:

CDC_Device_USBTask(&VirtualSerial_CDC_Interface);

As a fall back can you not make use of the hardware UART in the 16U2 and a USB-TTL cable attached to its TX/RX? Obviously much simpler to get setup "early" in the code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The High Septon wrote:
The obvious thought would be that firstRun is never true, but I initialize this boolean as true and I don't change it until the logic inside my if statement above has run

To remove any doubts of if the if-statement is "taken" or not: If you have one digital I/O free then hook up a LED to it. Light it up as the first thing in that if-statement:

 

        if (firstRun){
            // Light up LED here

            CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Before calc call"); //test:working msg system

As you yourself seem to conclude, it is unlikely that firstRun is not true at that point, and that therefore execution should go into that "leg" of the if-statement.

 

OTOH, it can not be totally ruled out that firstRun is false at that point. A rogue pointer, a buffer overrun, or something else, might corrupt the value before execution gets to that if-statement.

 

A lot of the time debugging is about making sure (rather than guessing or assuming) what is at hand. Hving made sure, the problem space can often be divided into one part that needs further investigation and another that does not. The fine art of debugging is not being intuitive about what is at fault, but being intuitive about how to divide the problem space, what hypotheses to form and where to put the tests.

 

Making sure that firstRun is true on the first run seems like a very good division of the problem space. If it turns out to be  true on first run, then the problem lies somewhere with the USB stuff. E.g. perhaps transmitting before things have been properly set up and stabilized, or transmitting the second and following runs without the first run transmission having been properly "pumped out of the local USBstack".

 

LUFA relies on several (pseudo-)parallel threads of execution, sort of a non-preemptive RTOS. Unless you kick all processes in a timely manner they can start to behave bad..

 

If everything else fails then a small test program, setting up USB and then just doing

 

CDC_Device_SendString(&VirtualSerial_CDC_Interface, "First string sent");
CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Second string sent");

is the obvious minimal test to see if LUFA can cope with two such transmissions without "pumping the stack". If it does, perhaps also see how a few more transmissions behaves.

 

Minimal tests are almost always nice. They are simple to do. They do not involve anything but the thing you want to test. (Remember, it's about making sure.)

 

HTH!

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lots of good suggestions. So first things first; the if(firstRun) inner code does run. I confirmed this with a simple strcat() test and appended a character onto the output string that's printed the else statement, and sure enough it prints with the unique firstRun character added on, so we can rule out the firstRun code not running.

 

My intuition was that the timing could be out of sequence for the earlier USB message prints, but I initialize the USB hardware very early in my main routine (See the call SetupUSBHardware() right after I configure the chip clock and SPI hardware at the top of main() ). I don't believe there's anything more than this that needs to be done for initialization. Am I wrong? 

 

It seems very odd that if it is a timing issue, the timing is only incorrect in my firstRun logic but works afterwards. Another odd behavior is that messages only work in the else block and afterwards in the while loop; messages I put in other places (in main() after SetupUSBHardware(), Or even right before the while loop) don't get printed, but a message at the top of the while loop does. So it seems to me that there has to be something funky going on in the background with the LUFA threading.

 

For example:

void SetupUSBHardware(void)
{
#if (ARCH == ARCH_AVR8)
	/* Disable watchdog if enabled by bootloader/fuses */
	MCUSR &= ~(1 << WDRF);
	wdt_disable();

	/* Disable clock division */
	clock_prescale_set(clock_div_1);
#elif (ARCH == ARCH_XMEGA)
	/* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */
	XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU);
	XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL);

	/* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */
	XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ);
	XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB);

	PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
#endif

	/* Hardware Initialization */
	USB_Init();
	
	/* Create a regular character stream for the interface so that it can be used with the stdio.h functions */
	CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, &USBSerialStream);
}

int main(void){
	...
	//Configure the Atmega's USB hardware
	SetupUSBHardware();
	CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Won't print");

	//Configure the Atmega's SPI hardware
	SetupSPIHardware();

	//Configure the FXLS8471 Accelerometer
	SetupFXLS8471Hardware();

	GlobalInterruptEnable();
	CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Also Won't print");

	while (1) {
	
		CDC_Device_SendString(&VirtualSerial_CDC_Interface, "This prints");
		if (firstRun){
			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Won't print either"); //test:working msg system

			baseAcceleration = calcBaselineAcceleration();

			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "Still won't print"); //test:working msg system

			firstRun = false;
		}
		else{
			baseAcceleration = (instantBaseAcceleration + baseAcceleration)/2;	//update average acceleration
			CDC_Device_SendString(&VirtualSerial_CDC_Interface, "This also prints"); //test:working msg system
		}

		CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
		CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
		USB_USBTask();
	}
}

Its almost as if the USB hardware isn't ready during the first few CPU scan cycles, and then starts printing messages as soon as it is during while() execution.

 

Any thoughts? 

 

Last Edited: Wed. Nov 8, 2017 - 05:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The High Septon wrote:
Any thoughts?
Ask Dean.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What happens if you simply remove (or comment out) the call

 

			baseAcceleration = calcBaselineAcceleration();

?

 

Will the two surrounding sends work?

 

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

They don't work, even after commenting that line out.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just made a simple test and may have found another interesting behavior.

 

I made a simple incrementer in my else statement, looks like this:

int incr = 0;

while(1){
    ...
    else{
	    char str[12];
	    fputs("(", &USBSerialStream);
	    fputs(convertIntToString(incr, str), &USBSerialStream);
	    fputs(")", &USBSerialStream);
	    incr++;
    }
    ...
}

I did this so I could see what the first value of incr read on my serial terminal is. If USB messages are being sent synchronously I would expect the first msg to read (0), and the next would read (1), etc. But, instead I see a random number as the first value coming across the serial port. A sample output:

(182)9)(190)(191)(192)(193)(194)(195)(196)(197)(198)(199)(200)(201)(202)(203)
(204)(205)(206)(207)(208)(209)(210)(211)(212)(213)(214)(215)(216)(217)(218)(219
)(220)(221)(222)(223)(224)(225)(226)(227)(228)(229)(230)(231)(232)(233)(234)(235)
(236)(237)(238)(239)(240)(241)(242)(243)(244)(245)(246)(247)(248)(249)(250)(251)
(252)(253)(254)(255)(0)(1)(2)(3)(4)

Maybe this asynchronous start point for serial port "listening" is expected behavior? If not, it seems to be a strong indicator of the USB hardware not being ready for messaging until many cycles after the main routine begins.

 

Edit: Interestingly as well; if I change the else block to stop reporting after it increments to 255 I don't see anything in my terminal at all! Lending itself strongly to the theory that the USB hardware/firmware isn't finished initializing before a large number of clock cycles have already elapsed and the program is on its nth iteration through the while() loop.

else{
	if (incr <=255){
		char str[12]; 
		fputs("(", &USBSerialStream);
		fputs(convertIntToString(incr, str), &USBSerialStream);
		fputs(")", &USBSerialStream);
		incr++;
	}
}

 

 

Last Edited: Wed. Nov 8, 2017 - 05:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I know I've advocated against wild guesses above, so I'll disguise this into something that looks like very wise general advice... ;-)  :

 

Don't rule out that the problem is on the host side.

 

So, on to your theory of the loong time to initialize: That should be easily testable, with a multi-second delay right before the first transmit.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Adding a 3.75s delay before hitting the firstRun logic seemed to do the trick, I actually saw the messages inside if(firstRun) get printed!

 

It looks like its a timing issue afterall. So, my question is, where is the appropriate place to block while waiting for USB initializations to finish? I have messages i'd like to add to debug main() before I enter the while loop, so I'd imagine the bottom of SetupUSBHardware() is the correct place to add a delay. No?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

At this point I'd go with 'clawson's advice: Ask Dean Camera, the author of LUFA. He's not been active here for quite some time now, but you might stand a better chance asking at e.g. the LUFA mailing list.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah ive already messaged and emailed him. Just continuing the convo here since someone may weigh in/benefit.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I has been a while since I have done a LUFA project, but don't you need something like:

	while (USB_DeviceState != DEVICE_STATE_Configured) {} // Device must be connected and configured to continue

before trying to use the USB?

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good catch, Jim!

 

IIRC (and I was just over in the LUFA files to double-check and I seem to recall correctly) this is handled in the LUFA layers. At least if you go for a "class driver" project (none of the class driver demos do that check in the "application layer"). Only in the "low-level" demos is that check present in the application code.

 

Yes, if communication is attempted before enumeration etc is done then things will go sour.

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Wed. Nov 8, 2017 - 10:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
while (USB_DeviceState != DEVICE_STATE_Configured) {} // Device must be connected and configured to continue

Is there some semantic involved with the placement of this line? Everywhere i've tried putting it (in the body of main(), at the end of SetupUSBHardware()) has caused my chip to be an unrecognizable USB device after programming.

Last Edited: Wed. Nov 8, 2017 - 11:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you look in the LUFA sources it seems to be placed at the beginning of USB_USBtask(), which simply returns if the state isn't DEVICE_STATE_Configured. E.g. in the CDCClassDevice driver:

 

void CDC_Device_USBTask(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo)
{
	if ((USB_DeviceState != DEVICE_STATE_Configured) || !(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS))
	  return;

    .
    .
    .
    

or in the low-level demo VirtualSerial (in <LUFA-root>/Demos/Device/LowLevel/VirtualSerial/VirtualSerial.c)

void CDC_Task(void)
{
	char*       ReportString    = NULL;
	uint8_t     JoyStatus_LCL   = Joystick_GetStatus();
	static bool ActionSent      = false;

	/* Device must be connected and configured for the task to run */
	if (USB_DeviceState != DEVICE_STATE_Configured)
	  return;
    
    .
    .
    .

 

My dabblings with LUFA has been using driver-type projects (not low-level) and, as I said above, it seems this thing is handled by the driver layer (not my application code) in that scenario. I cant recall ever coding that check myself (although my memory might fail me).

 

Anyway, a study of some demos and example projects should hopefully reveal more about this.

 

BTW, if you get an answer from Dean re this we'd all appreciate it if you relayed it to here!

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Argh, everytime I think i've found consistency it fleets away again.

 

I don't understand why the message in calcBaselineAcceleration() isn't displayed. If I comment out the call to this method, then I see "Before calc call" and "After calc call" in my terminal. But, as soon as I uncomment the call to calcBaselineAcceleration(), nothing is displayed over the serial port.

 

Wtf.

 

int calcBaselineAcceleration(void){
	fputs("During calc call ", &USBSerialStream); //never shows up
	int testInt = 4;
	return testInt;
}

main(){
...
	while (1) {

		while (delayVal!=0){
			_delay_ms(15);
			delayVal--;
		} 

	if (firstRun){
		fputs("Before calc call ", &USBSerialStream);   //never shows up
		baseAcceleration = calcBaselineAcceleration();
		fputs("After calc call ", &USBSerialStream);	//never shows up
		firstRun = false;
	}
	...

	}
}
		

 

Last Edited: Wed. Nov 8, 2017 - 11:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:

 

My dabblings with LUFA has been using driver-type projects (not low-level) and, as I said above, it seems this thing is handled by the driver layer (not my application code) in that scenario. I cant recall ever coding that check myself (although my memory might fail me).

 

 

I don't recall coding this myself in any other LUFA project either, so i'm surprised that its necessary here. I call USB_USBTask() at the end of my while() loop, so at best maybe I could put it at the beginning of my while loop to avoid missing firstRun messages? Coupled with the other issues I mention above about more buggy missing of messages this may not even be the biggest concern for a root cause.

 

My entire application is just an altered version of the VirtualSerial.c program in Demos/Devices/ClassDriver/VirtualSerial, so I'm pretty surprised that I'm having so much grief working off of a verified example. 

Last Edited: Thu. Nov 9, 2017 - 12:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ccrause wrote:

Perhaps a potential solution: http://www.avrfreaks.net/comment/861806#comment-861806

The High Septon wrote:

My entire application is just an altered version of the VirtualSerial.c program in Demos/Devices/ClassDriver/VirtualSerial, so I'm pretty surprised that I'm having so much grief working off of a verified example. 

Have you confirmed that the solution in comment 861806 (note the title - LUFA USB CDC - Detect when host application connects) doesn't help you?  It would be pointless transmitting data before the host has registered and initialized the USB connection.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ccrause wrote:

ccrause wrote:

Perhaps a potential solution: http://www.avrfreaks.net/comment/861806#comment-861806

Have you confirmed that the solution in comment 861806 (note the title - LUFA USB CDC - Detect when host application connects) doesn't help you?  It would be pointless transmitting data before the host has registered and initialized the USB connection.

 

My VirtualSerial.c program already included a USB connection/disconnection callback, its standard in the demo projects. The example in your link showcases a modification to this callback to turn LEDs on/off based on the connection state. I don't have this sample board so these LEDs don't exist in my application; I don't think this solution is doing anything special that solves my issue. 

 

What's especially curious is that my 3.75s delay loop allows my "Before calc call" message to post to the terminal, but as soon as I uncomment calcBaselineAcceleration() no messages at all post, as if my delay is no longer sufficient to let the USB hardware finish prepping. (Look at post #18 in this thread).

 

For reference, here's my callback i've been using. If i'm not mistaken, this method is supposed be handling the issue of blocking during connection attempts.

/** CDC class driver callback function the processing of changes to the virtual
*  control lines sent from the host..
*
*  \param[in] CDCInterfaceInfo  Pointer to the CDC class interface configuration structure being referenced
*/
void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo)
{
	/* You can get changes to the virtual CDC lines in this callback; a common
	use-case is to use the Data Terminal Ready (DTR) flag to enable and
	disable CDC communications in your application when set to avoid the
	application blocking while waiting for a host to become ready and read
	in the pending data from the USB endpoints.
	*/
	bool HostReady = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey, my account still works! Looks like the awful site implementation hasn't gotten better over the last year -- I had to log in twice to access my PMs.

 

The comments about waiting on the host to become ready before sending data are on the money - with CDC, there's nothing in the USB class that says that an application on the PC is actually listening to you, as the OS will happily configure your device as soon as it's plugged in. Since the LUFA stream APIs can't read the PC's mind, they will try to buffer up data into the endpoint bank(s) until there's no space left, and then sit around until the stream timeout period waiting for the host to accept it before it just gives up. The best way around this is to hold off trying to send data until the virtual DTR line goes high, which you would then trigger from your terminal application (see the post here someone else already linked) to initiate the comms transfer.

 

The other way around this would be to bypass the stream APIs and use the endpoint byte APIs directly so you can manually fill the bank and asynchronously wait until the host accepts the packet.

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh dear, look what the cat dragged in... ;-)  Hello there Dean, and how nice to see you stopping by!

 

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey Dean!

 

I've tested just about every configuration I can imagine using a DTR line check and I still don't see messages in my terminal. For example, my code below blocks the main while loop until HostReady (from the pre-built CDC callback function) but I don't see a single message on my Host with this code.

 

bool HostReady = false;

int main(void){

	//configure CPU clock
	CLKSEL0 |= (1<<CLKS);	//select external clock
	CLKPR = 0x80;			//enable clock speed edits
	CLKPR |= (1<<CLKPS0);	//divide clock speed by 2: 16MHz/2 = 8MHz (for SPI bus constraint)

	//Configure the Atmega's USB hardware
	SetupUSBHardware();
	fputs("Clock set \n", &USBSerialStream); 

	...

	while (1) {

		//test: Wait for DTR line to go high (host is ready)
		while(!HostReady){
			//block until host is ready for usb comms from device
		}

		//If first run of while loop, calculate the baseline acceleration (in Z axis) seen by FXLS accelerometer
		if (firstRun){
			fputs("Before calc call ", &USBSerialStream);
			baseAcceleration = calcBaselineAcceleration();	
			fputs("After calc call ", &USBSerialStream);
			firstRun = false;
		}
		//Otherwise, just update the baseline acceleration (Z axis) with recent values
		else{
			baseAcceleration = (instantBaseAcceleration + baseAcceleration)/2;	//update average acceleration
		}

		fputs("Hello from while loop ", &USBSerialStream);	

		CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
		CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
		USB_USBTask();
	}
}

void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t *const CDCInterfaceInfo)
{
	HostReady = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) != 0;
}

 

Some additional odd behavior; if I place my HostReady blocking while statement too early in my main function (at the end of SetupUSBHardware() for example) my device fails to be recognized by my host at all and shows up as an unrecognized USB device. What am I doing incorrectly?

 

 

Last Edited: Wed. Nov 15, 2017 - 07:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to pump the LUFA internals unconditionally, don't spin-loop on "HostReady" without calling the periodic LUFA management tasks. Try only calling functions like fputs() that operate on the stream inside your HostReady conditional -- if the virtual DTR line is high, you skip writing to the stream and just call the LUFA management functions so that the stack can enumerate the device with the host and manage the CDC class.

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think my brain just exploded!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

abcminiuser wrote:

You need to pump the LUFA internals unconditionally, don't spin-loop on "HostReady" without calling the periodic LUFA management tasks. Try only calling functions like fputs() that operate on the stream inside your HostReady conditional -- if the virtual DTR line is high, you skip writing to the stream and just call the LUFA management functions so that the stack can enumerate the device with the host and manage the CDC class.

I think I understand but I'm not sure what the implementation would actually look like. Can you provide a code sample?