Friday, September 27, 2013

sync errors (with usb sniffer) resolved to j6 jumper switch

After discovering that FRES bit of the USB Control Register isnt useful as a software disconnect/connect. I tied one of the header pins to PA12 - USB D+ and used the user 'c' and 'd' values on the uart to connect (set as output and pullup) and disconnect(set as input floating) ...

Unfortunately other that RESET, no USB transactions were taking place. My usb sniffer showed sync errors.

Last night I decided to test a theory - which is that the internal pullup/pulldown wasnt driving the pin/line hard enough. I decided to remove the header wire between PA0 and PA12 and just insert the jumper on J6 after initialization. Immediately communication started flowing between the host and my board as seen on my beagle USB sniffer.

The jumper pulls PA12 to vcc (3.3v).

I even tested using PA12 to do the work PA0 was meant to do. To no avail. Finally I decided I needed a momentary switch plugged into J6 - for now while I am debugging my firmware. Normal operation would have firmware running shortly after usb power plugin

I salvaged this switch from the front panel of an old PC I had laying around and it serves its purpose well. Here is the output from the TotalPhase Beagle USB sniffer and a picture of the board and push button switch.






Wednesday, September 4, 2013

usb irq to systick irq preemption issue resolved

Ok so this bug was pretty interesting. Basically upon branching into the USB isr I call tclib_printf() to print a message to the serial port. tclib_printf would stall trying to retrieve a mutex lock held by the SysTick isr.  A dump of the core registers indicated the USB interrupt was executing and SysTick was pending but it would never hand over. Deadlock.

Now I know that the Cortex-m3 core NVIC controller implements priority based preemption and I felt that leaving the default settings in place and since SysTick was at a lower position in the vector table than Usb it would automatically pre-empt.

Thats not the case. Some googling and a reread of the Cortex-m3 r1p1 manual cleared things up.

Basically only the first three interrupts (fault, nmi, reset) are automatically locked in with a priority that is non configurable. The other positions in the exception table (position <= 15) and external interrupts of peripherals(position >16) are configurable. And here the kicker:

In order for an Irq to preempt the currently executing Irq, its priority must be greater (smaller number) than that of the currently executing Irq. Meaning if they have identical priorities (as they did because I never bothered to change the default power up settings) then Systick will never preempt Usb.

Once I discovered this - I made a quick change to verify before putting in a permanent fix in both stm32_nvic_init() and stm32_nvic_install_isr(). Now I can see usb interaction while the SysTick handler' keep-alive-LED-blink continues to run.

before
/*set the priority low (high number) so as not to conflict with systick*/
wdByteIdx = (wdIrqNum / 4);
ptrTemp   = PTR_STM32_NVIC_INTP_BASE;
ptrTemp  += wdByteIdx;
(*ptrTemp)= 0xffffffff;


after
/*set the priority low (high number) so as not to conflict with systick*/
wdByteIdx = (wdIrqNum / 4);
wdBitIdx  = (wdIrqNum % 4);

wdTemp    = (wdIrqNum >> 4);            /*build prio from irqnum*/
if(wdTemp == 0)
        wdTemp++;

ptrTemp   = PTR_STM32_NVIC_INTP_BASE;
ptrTemp  += wdByteIdx;
(*ptrTemp)= ((wdTemp << 4) << (wdBitIdx << 8)); /*bits [7:4] of prio*/


Now to start debugging USB communication. I may need to fish out my USB sniffer.

+-++-++-++-+ +-++-++-++-++-++-++-++-+ +-++-++-++-++-++-++-++-++-+
|I||g||b||o| |E||m||b||e||d||d||e||d| |C||o||r||t||e||x||-||m||3|
+-++-++-++-+ +-++-++-++-++-++-++-++-+ +-++-++-++-++-++-++-++-++-+
-------user-usb-connect-request---------
--------RESET-----------
-------user-usb-disconnect-request------
-------user-usb-connect-request---------
--------RESET-----------


rombios@lenovo:~/FOR_NBOX$ dmesg
[59112.279114] usb 1-3.4: new full speed USB device number 60 using ehci_hcd
[59112.352119] usb 1-3.4: device descriptor read/64, error -32
[59112.526134] usb 1-3.4: device descriptor read/64, error -32
[59112.700189] usb 1-3.4: new full speed USB device number 61 using ehci_hcd
[59112.774164] usb 1-3.4: device descriptor read/64, error -32
[59112.949177] usb 1-3.4: device descriptor read/64, error -32
[59113.123905] usb 1-3.4: new full speed USB device number 62 using ehci_hcd
[59113.526020] usb 1-3.4: device not accepting address 62, error -32
[59113.599118] usb 1-3.4: new full speed USB device number 63 using ehci_hcd
[59114.001029] usb 1-3.4: device not accepting address 63, error -32
[59114.001150] hub 1-3:1.0: unable to enumerate USB device on port 4


Tuesday, September 3, 2013

using google drive to store file for upload

https://docs.google.com/file/d/0Bw6D8nqb-AYzZFJ4dl90TVdzeEU/edit?usp=sharing

click on google drive link above ... create folder ... upload file ... click on file name ... select share ... change from private to anyone with a link ... take url link to downloads

file above is a snapshot of my stm32 build directory as of 0903 and its password protected

https://docs.google.com/file/d/0Bw6D8nqb-AYzZFJ4dl90TVdzeEU/edit?usp=sharing

unsupported timers and usb connect

Discovered that timers 5 ,6  and 7 arent supported on low density stm32 devices like the one I have - stm32f103. So now I am using timer 2 for implementing uS and mS Delays. The stm32_timer_uS_delay() routine is posted below.





/*
 **********************************************************************
 *description:  delay wdUs ticks
 *input:        unsigned int wdUs = number of uS ticks to delay for
 *note:         use timer2. its tied to a 36Mhz reference (PCLK1)
 **********************************************************************
 */
void    stm32_timer_uS_delay(unsigned int wdUs)
{
volatile unsigned int wdTemp, wdPr;

if(wdUs > 1000000)
        wdUs = 1000000;


/*adjust preload counter if greater than 1000*/
if(wdUs > 1000)
        {
        wdUs /= 1000;
        wdPr  = 36000;
        }
else
        wdPr  = 36;



/*
 **********************************************************************
 *disable timer
 *clear status
 *clear interrupt enable reg
 *clear control reg 2
 *then
 *setup prescaler value
 *setup autoreload (for count up)
 *clear the status register' UIF bit
 *then set control reg 1 OPM + CEN
 *finally
 *loop on status register checking UIF
 **********************************************************************
 */
(*PTR_STM32_TIM2_CR1) = 0;
(*PTR_STM32_TIM2_SR)  = 0;
(*PTR_STM32_TIM2_DIER)= 0;
(*PTR_STM32_TIM2_CR2) = 0;

(*PTR_STM32_TIM2_PSC) = wdPr;
(*PTR_STM32_TIM2_ARR) = wdUs;
(*PTR_STM32_TIM2_SR)  = STM32_TIM_SR_UIF;

wdTemp  = STM32_TIM_CR1_OPM;
wdTemp |= STM32_TIM_CR1_CEN;
(*PTR_STM32_TIM2_CR1) = wdTemp;

do
        {
        wdTemp  = (*PTR_STM32_TIM2_SR);
        wdTemp &= STM32_TIM_SR_UIF;
        }while(wdTemp != STM32_TIM_SR_UIF);
}






So the issue of bus enumeration (beginning with RESET) became an issue this weekend. On the stm32f103 board D+ is tied to vcc via jumper J6. This is optional. I dont like this setup because during debugging I like the freedom to connect and disconnect the usb device. This necessitates pulling D+ high to connect and pulling it low later to disconnect.

I wanted a means to do this in software and the stm32 core doesnt appear to have the software CONNECT feature I found on the nxp lpc2378. So I decided to tack a jumper wire between PA0 (user headers) and D+. This allows me to setup PortA and pin 0 as an output and drive it high when I am ready to initiate the host to usb enumeration process.

Clever right?
I thought so too ... routine for me

I wrote stm32_usb_connect_pullup() and stm32_usb_disconnect_pullup() to realize this. They are meanth to be called in usb_test.c/main() after stm32_init() and stm32_usb_init()




/*
 **********************************************************************
 *description:    I am using PAX (external wire to Dplus pin on J6 facing
 *        the USB connector) to pull up the line to trigger the
 *        USB HOST enumeration.
 *        setup PAX to generic output. pull it high
 *input:    unsigned int wdPin = PAx pin number to use. it must be
 *        portA block pins ONLY
 **********************************************************************
 */
void    stm32_usb_connect_pullup(unsigned int wdPin)
{
volatile unsigned int wdTemp, wdMask;


(*PTR_STM32_GPIO_A_BSRR) = ((1 << wdPin) << 16);

wdTemp  = (*PTR_STM32_GPIO_A_CRL);
wdTemp &= ~(0x0f << wdPin);
wdMask  = ((STM32_GPIO_CNF_OUT_GEN_PUSH_PULL << 2) | \
            STM32_GPIO_MODE_OUTPUT_10MHZ) << (wdPin << 2);

(*PTR_STM32_GPIO_A_CRL)  = (wdMask | wdTemp);

(*PTR_STM32_GPIO_A_BSRR) = (1 << wdPin);
}





/*
 **********************************************************************
 *description:    setup particular portA pin to an input, so its no
 *        longer driving the pin
 *input:    unsigned int wdPin = PAx pin number to use. it must be
 *        portA block pins ONLY
 *note:        this should be called inside the usb isr on RESET flag
 **********************************************************************
 */
void    stm32_usb_disconnect_pullup(unsigned int wdPin)
{
volatile unsigned int wdTemp, wdMask;


wdTemp  = (*PTR_STM32_GPIO_A_CRL);
wdTemp &= ~(0x0f << wdPin);
wdMask  = ((STM32_GPIO_CNF_IN_FLOATING << 2) | \
            STM32_GPIO_MODE_INPUT_MODE) << (wdPin << 2);

(*PTR_STM32_GPIO_A_CRL)  = (wdMask | wdTemp);
}