Thursday, November 21, 2013

STM32F103ZET6 ARM Cortex-M3 Development Board

STM32F103ZET6(development board ) with FSMC,NAND/NOR FLASH 
STM32 development board based on STMicroelectronics (ST) has introduced the highest series of ARM CortexM3 chip configuration as the core component STM32F103ZET6
On-board resources:
* CPU: STM32F103ZET6; (LQFP144-pin, on-chip integrated 512K flash, 64KRAM, 12Bit A / D, D / A; PWM, CAN, USB, SDIO, FSMC and other resources)
* Outside the expanded board 512K SRAM, 2M NOR FLASH (on-board support for the largest 1024k SRAM, 16M of NOR FLASH) to meet the large capacity data acquisition, processing and analysis requirements
 

* Board outside 128M NAND FLASH expand to meet the rich color of the picture storage, data storage form, document management applications and so on
*FSMC control, color touch module configuration TSC2046 controller, support for a SD card (SPI mode) can be used to store pictures, in support of AT45DBxxx of a DATA FLASH (can be used for storage)
 

* All the way CAN communication interface, the drive chip SN65VHD230

* Two-way RS232 interface
 

* All the way RS485 communication interface

* A SD card connector SDIO control

* A I2C memory interface, the standard 24LC02 (EEPROM)

* A SPI memory interface, AT45DB161D or M45PE16V (DATA FLASH)
* All the way adjustment potentiometer ADC input

* Three-way leads to ADC input terminals

* Two-way terminal block PWM output leads

* Two-way leads to DAC output terminals
* A buzzer, the five users LED lights, a power indicator light, a USB communication indicator, four user keys, a system reset button
 * Power Select jumper to support the external 5V power supply, USB power supply or power supply by JLINK
* Board Size: 13CM X 10CM 




Tuesday, November 12, 2013

[peekstm32] the problem was data toggle

The odd thing was that the problem lay with JUST the peek function or host "IN" packets. I played around with the stm32_endp_isr_user_out() routines "IN" packet handling section. Wasnt sure of the state of DTOG_TX so I figured Id force it to 0 instead 1.

Immediately the first peek command from the next run worked and failed there after. After playing around with DTOG_TX forced to 1, toggled I finally deleted it, essentially leaving it floating and that worked

Basically delete this line from stm32_endp_isr_user_out()
        stm32_usb_set_epr_dtog(wdEndP,\
                               1,\
                               STM32_USB_EPR_DTOG_TX_OFFSET);


End Result - arm m-3 minicom:
--------RESET----------- 27uS
--------RESET----------- 28uS
80 06 00 01 00 00 40 00
--------RESET----------- 28uS
00 05 3E 00 00 00 00 00
usb device address = 3E
80 06 00 01 00 00 12 00
80 06 00 02 00 00 09 00
80 06 00 02 00 00 20 00
80 06 00 03 00 00 FF 00
80 06 02 03 09 04 FF 00
80 06 01 03 09 04 FF 00
80 06 03 03 09 04 FF 00
00 09 01 00 00 00 00 00
00 08 ED FE 04 5C 00 40
user req peek   [40005C04]=7021
00 08 ED FE 04 5C 00 40
user req peek   [40005C04]=3061
00 08 ED FE 04 5C 00 40
user req peek   [40005C04]=7021
00 08 ED FE 04 5C 00 40
user req peek   [40005C04]=3061
00 08 ED FE 40 60 00 40
user req peek   [40006040]=00
01 0C ED FE 40 60 00 40
user req poke   [40006040]=B0
00 08 ED FE 40 60 00 40
user req peek   [40006040]=B0



On the host linux pc side:
sudo ./peekstm32.out 0x40005c04
[40005c04] = 7021
sudo ./peekstm32.out 0x40005c04
[40005c04] = 3061
sudo ./peekstm32.out 0x40005c04
[40005c04] = 7021
sudo ./peekstm32.out 0x40005c04
[40005c04] = 3061
sudo ./peekstm32.out 0x40006040
[40006040] = 0
sudo ./peekstm32.out 0x40006040 0xb0
sudo ./peekstm32.out 0x40006040
[40006040] = b0

DONE!!!

Monday, November 11, 2013

[peekstm32] libusb_bulk_transfer() command failed with -7

/usr/include/libusb-1.0/libusb.h lists -1 as
LIBUSB_ERROR_TIMEOUT = -7,

But timeout is set to 1000ms or 1 second in my peekstm32.c program. My primary concern is that I am not seeing any communication on the stm32 side with bulk endpoints. Need to check their configuration settings in the EPRX registers later.

I am seeing this now ... but the problem persists EVEN though the usb sniffer clearly shows data transmission without any errors, with peek and pokes from peekstm32.c


using linux libusb to test communication with the my stm32 firmware

Figured Id put together a small example program to allow me to test out my usb device/firmware by using usb communication (on the linux host) to peek and poke offsets in stm32 space. Culminating in toggling LED1 off and on from the linux host.

With that in mind I installed libusb
apt-cache search libusb
apt-get install libusb-1.0.0 libusb-1.0.0-dev

And wrote peekstm32.c
usage is a follows:
[peekstm32]    options
                        adddress value[optional]


But it seemed to be failing on the libus_claim_device() routine. Thats also when I discovered that not all the configuration data had been transferred.
lsusb -vvv -dc0de: returns

Bus 001 Device 013: ID c0de:feed 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0xc0de
  idProduct          0xfeed
  bcdDevice            1.00
  iManufacturer           1 Igbo Embedded
  iProduct                2 My Device
  iSerial                 3 1234
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              500mA
Device Status:     0x0000
  (Bus Powered)



The Interface and Endpoint descriptors are missing. Also I noticed on the minicom usb communication dump that the linux host send two get configuration descriptor commands. one for 9 bytes the other for 32 so the first succeeds (IN) but the second fails:

80 06 00 02 00 00 09 00
80 06 00 02 00 00 20 00

Need to look at this closely before continuing with the userland program. Found the problem - bug on line 825 of IE_stm32_usb.c: Removed this line

                        if(wdSize > (*ptrTemp))
                                wdSize  = (*ptrTemp);


And  all is well. I actually found that bug by placing tclib_printf() of wdCount inside stm32_dequeue_IN_transfers.

Now
sudo lsusb -dc0de: -vvv returns

Bus 001 Device 017: ID c0de:feed 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0xc0de
  idProduct          0xfeed
  bcdDevice            1.00
  iManufacturer           1 Igbo Embedded
  iProduct                2 My Device
  iSerial                 3 1234
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           32
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0xc0
      Self Powered
    MaxPower              500mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               2
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               2
Device Status:     0x0000
  (Bus Powered)










peekstm32.c

/*
 ***********************************************************************
 *name:         peekstm32
 *author:       Samuel Igwe
 *date:         11/11/2013 A.D
 *description:  stm32 host to usb peek and poke routine. 
 *note:         peekstm32 address               = read address
 *              peekstm32 address  value        = write value to address
 *compiled as:  gcc -o peekstm32.out -lusb-1.0  linux/peekstm32.c
 ***********************************************************************
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>



/*#define constants*/
#define STMUSB_VENDOR_ID                                0xc0de
#define STMUSB_PRODUCT_ID                               0xfeed

#define STMUSB_BULK_ENDP_OUT                            0x1
#define STMUSB_BULK_ENDP_IN                             0x81

#define STMUSB_CMD_PEEK                                 0
#define STMUSB_CMD_POKE                                 1

#define STMUSB_TIMEOUT                                  1000



/*structure*/
struct  STR_PEEKSTM32
        {
        unsigned int                    wdAddress;
        unsigned int                    wdValue;
        unsigned int                    wdMaxPktSize;
        
        struct  libusb_device_handle    *ptrHandle;
        struct  libusb_device           *ptrDevice;

        }strPeekStm32;



void    func_atexit(void);



/*
 ***********************************************************************
 *description:  main routine    
 *inputs:       int     argc    = count
 *              char    *argv[] = pointer to command line parameters
 ***********************************************************************
 */
main(int argc, char *argv[])
{
unsigned int    wdCount, wdTemp;
         char   sbBuffer[64];
/*
 ***********************************************************************
 *check commandline parameters
 ***********************************************************************
 */
memset(&strPeekStm32, 0, sizeof(strPeekStm32));
switch(argc)
        {
        case    2:
        case    3:
                {
                strPeekStm32.wdAddress = \
                (unsigned int)strtol(argv[1], (char **)NULL, 16);
                if(argc == 3)
                        {
                        strPeekStm32.wdValue = \
                        (unsigned int)strtol(argv[2], (char **)NULL, 16);
                        }

                break;
                }
        default:
        case    0:
        case    1:
                {
                printf("[peekstm32]\toptions\n\t\tadddress value[optional]\n");
                return 0;
                }
        }



/*
 ***********************************************************************
 *initialize variables and atexit routine
 ***********************************************************************
 */
if(atexit(func_atexit) != 0)
        {
        printf("[peekstm32]\tatexit() failed\n");
        return 0;
        }       



/*
 ***********************************************************************
 *initialize library
 ***********************************************************************
 */
if((wdTemp = libusb_init(NULL)) != 0)
        {
        printf("[peekstm32]\tlibusb_init() failed with %d\n",\
               wdTemp);
        return 0;
        }

if((strPeekStm32.ptrHandle=libusb_open_device_with_vid_pid(NULL,\
                                   STMUSB_VENDOR_ID,\
                                   STMUSB_PRODUCT_ID)) == NULL)
        {
        printf("[peekstm32]\tlibusb_open_device_vid_pid() failed\n");
        return 0;
        }

if((strPeekStm32.ptrDevice = libusb_get_device(strPeekStm32.ptrHandle)) == NULL)
        {
        printf("[peekstm32]\tlibusb_get_device() failed\n");
        return 0;
        }

if((wdTemp = libusb_kernel_driver_active(strPeekStm32.ptrHandle, 0)) !=0)
        {
        if((wdTemp = libusb_detach_kernel_driver(strPeekStm32.ptrHandle, 0)) !=0)
                {
                printf("[peekstm32]\tlibusb_detach_kernel_driver() failed with %d\n",\
                       wdTemp);
                return 0;
                }
        }
else
        {
        if((wdTemp = libusb_claim_interface(strPeekStm32.ptrHandle, 0)) != 0)
                {
                printf("[peekstm32]\tlibusb_claim_interface() failed with %d\n",\
                       wdTemp);
                return 0;
                }
        }



/*
 ***********************************************************************
 *get usb resources limits
 ***********************************************************************
 */
strPeekStm32.wdMaxPktSize = 0;
for(wdCount=0; wdCount < 16; wdCount++)
        {
        wdTemp = libusb_get_max_packet_size(strPeekStm32.ptrDevice, wdCount);
        if(wdTemp != LIBUSB_ERROR_NOT_FOUND && wdTemp != LIBUSB_ERROR_OTHER)
                strPeekStm32.wdMaxPktSize = wdTemp;
        }


if(strPeekStm32.wdMaxPktSize <8)
        {
        printf("[peekstm32]\tlibusb_get_max_packet_size() failed to return adequate value\n");
        return;
        }



/*
 ***********************************************************************
 *execute OUT transaction:COMMAND format is as follows:
 *      byte    0       = command
 *      byte    1       = length of data in bytes
 *      byte    2       = 0xed
 *      byte    3       = 0xfe
 *      byte    4       = 32bit address
 *      byte    5       = 
 *      byte    6       = 
 *      byte    7       = 
 ***********************************************************************
 */
if(argc == 2)                                   /*IN*/
        {
        sbBuffer[0] = STMUSB_CMD_PEEK;
        sbBuffer[1] = 8;                        /*8*/ 
        }
else
        {
        sbBuffer[0] = STMUSB_CMD_POKE;
        sbBuffer[1] = 12;                       /*8 + 4 byte (dword)value*/
        }
sbBuffer[2] = 0xed;
sbBuffer[3] = 0xfe;
memcpy(sbBuffer+4, &strPeekStm32.wdAddress, sizeof(int));

wdTemp = libusb_bulk_transfer(strPeekStm32.ptrHandle,\
                              STMUSB_BULK_ENDP_OUT,\
                              sbBuffer,\
                              8,\
                              &wdCount,\
                              STMUSB_TIMEOUT);
if(wdTemp != 0)
        {
        printf("[peekstm32]\tlibusb_bulk_transfer() command failed with %d\n",\
               wdTemp);
        return 0;
        }



/*
 ***********************************************************************
 *execute IN or OUT transaction depending on whether PEEK or POKE sought
 ***********************************************************************
 */
if(argc == 2)                                   /*IN*/
        {
        if((wdTemp =libusb_bulk_transfer(strPeekStm32.ptrHandle,\
                                STMUSB_BULK_ENDP_IN,\
                                (char *)&(strPeekStm32.wdValue),\
                                4,\
                                &wdCount,\
                                STMUSB_TIMEOUT)) != 0)
                {
                if(wdCount == 0)
                        {
                        printf("[peekstm32]\tlibusb_bulk_transfer() data failed with %d\n",\
                               wdTemp);
                        return 0;
                        }
                }

        printf("[%x] = %x\n", strPeekStm32.wdAddress,\
                              strPeekStm32.wdValue);
        return 0;
        }
else                                            /*OUT*/
        {
        memcpy(sbBuffer, &strPeekStm32.wdValue, sizeof(int));
        if((wdTemp = libusb_bulk_transfer(strPeekStm32.ptrHandle,\
                                STMUSB_BULK_ENDP_OUT,\
                                (char *)&(strPeekStm32.wdValue),\
                                4,\
                                &wdCount,\
                                STMUSB_TIMEOUT)) != 0)
                {
                printf("[peekstm32]\tlibusb_bulk_transfer() data failed with %d\n",\
                       wdTemp);
                return 0;
                }
        }



/*
 ***********************************************************************
 *atexit
 ***********************************************************************
 */
return 0;
}





/*
 ***********************************************************************
 *description:  at exit routine
 ***********************************************************************
 */
void    func_atexit(void)
{
if(strPeekStm32.ptrHandle != NULL)
        {
        libusb_release_interface(strPeekStm32.ptrHandle, 0);
        libusb_close(strPeekStm32.ptrHandle);
        }

return;
}






Monday, November 4, 2013

USB Enumeration Done

Finally got USB enumeration working on this stm32 cortex-m3 board. Spend the night working through a host of errors  and bugs and reading and re-reading the manual and parts  of Jan Axelsons USB complete book.

The invariant settings code in
stm32_usb_enable_endp
stm32_usb_clean_epr_ctr
stm32_usb_set_epr_dtog
stm32_usb_set_epr_stat

was flawed. I had left out the check against toggling RX and TX status. Later I had to examine the stm32_usb_post_reset_setup() code fearing I had omitted some detail. I did but there were not show stoppers. It was really the little things like

1. CLEARING RX and TX SPURIOUS interrupts on USB RESET in the stm32_usb_post_reset_setup() code. This was preventing me from getting further IN and OUT interrupts.

2. setting the data toggle to 1 for TX before calling stm32_usb_send_packet or stm32_usb_queue_IN_transfers. And later realizing that the hardware will auto toggle it afterwards and I  neednt do anything more than set it to 1 for the initial transfer or start of transfer

3. cancelling queue IN transfers whenever a new OUT/SETUP command comes through.

4. omitting the check for SETUP and just relying on CTR_RX for incoming control commands

5. fixing a bug in stm32_usb_dequeue_IN_transfers that was preventing me from transfer more than just the first data block





I will be using libusb to write the data communication software to talk to this device. I have yet to decide what sort of data I am going to pipe out. Maybe simulate a joystick or take the genesis controller and run it through ... after decoding. Or better yet design something in fpga and interface to this stm32 or the new one I purchased ...

Sunday, November 3, 2013

STAT_TX and STAT_RX are toggle bits

During a debugging session with the TotalPhase USB sniffer I noticed the reoccurring problem of failed IN transfers.


IN transfers werent taking place, worse they were flagged as STALLS. It occurred to me that the STAT_TX and STAT_RX bits might be at fault. STAT_RX really doesnt matter because only 00 = disabled is the only mode that will stall/halt/nak a setup out transfer.

At stm32_usb_init() I had set STAT_TX to NAK (01). Later after calls to stm32_usb_endp_send_packet() I would write VALID(11) to the 2 relevant bits of the EPR0 register representing STAT_TX. The problem is that a value of 1 for either bit indicates a toggle NOT a set. So it wasnt an error in the documentation - which was true to form. But rather an error in my interpretation of it. Sure enough minicom dumps show no print outs from within IN isr.



As a final test and confirmation I decided to stop execution under gdb and write the value 0x11 to STAT_TX and STAT_RX (bits 4+5 and 12+13) of the  EPR0 register. Just to see if its set or toggled. Sure enough my worst fears were confirmed



This led to writing the following routine to encapsulate the process of properly setting STAT_TX and STAT_RX


/*
 **********************************************************************
 *description:  routine to properly set the EP_TYPE bits in the
 *              Endpoint register.
 *input:        unsigned int    wdEndP     = endpoint number
 *              unsigned int    wdEndPType = the following
 *                              STM32_USB_EPR_EP_TYPE_BULK
 *                              STM32_USB_EPR_EP_TYPE_CONTROL
 *                              STM32_USB_EPR_EP_TYPE_ISO
 *                              STM32_USB_EPR_EP_TYPE_INTERRUPT
 *output:       void
 *
 *note:         the CTR_RX and CTR_TX bits are governed by the rules
 *              1 = dont change 0 = clear
 **********************************************************************
 */
void    stm32_usb_enable_endp(unsigned int wdEndP,\
                              unsigned int wdEndPType)
{
unsigned int    wdTemp;

/*retrieve the current register value*/
wdTemp  = *(PTR_STM32_USB_EP0R + wdEndP);

/*invariants - values that will not modify these reg bits*/
wdTemp  &= ~(STM32_USB_EPR_STAT_TX_MASK  | \
             STM32_USB_EPR_STAT_RX_MASK  | \
            (STM32_USB_EPR_DTOG_TX       | STM32_USB_EPR_DTOG_RX));
wdTemp  |=  (STM32_USB_EPR_CTR_TX        | STM32_USB_EPR_CTR_RX);

/*set EndPType and number*/
wdTemp  |= (wdEndPType << STM32_USB_EPR_EP_TYPE_OFFSET);
wdTemp  |= wdEndP;

/*set the current register value*/
*(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP);
}





/*
 **********************************************************************
 *description:  routine to properly set the RX and TX STAT bits in the
 *              Endpoint register.
 *input:        unsigned int    wdEndP  = endpoint number
 *              unsigned int    wdMask  = the following
 *                              STM32_USB_EPR_CTR_RX
 *                              STM32_USB_EPR_CTR_TX
 *output:       void
 *
 *note:         the CTR_RX and CTR_TX bits are governed by the rules
 *              1 = dont change 0 = clear
 **********************************************************************
 */
void    stm32_usb_clear_epr_ctr(unsigned int wdEndP,\
                                unsigned int wdMask)
{
unsigned int    wdTemp;


/*retrieve the current register value*/
wdTemp  = *(PTR_STM32_USB_EP0R + wdEndP);

#ifdef  DONT_DEBUG
tclib_printf("stm32_usb_clear_epr_ctr initial value =%x\n", wdTemp);
#endif
/*invariants - values that will not modify these reg bits*/
wdTemp  &= ~(STM32_USB_EPR_STAT_TX_MASK  | \
             STM32_USB_EPR_STAT_RX_MASK  | \
            (STM32_USB_EPR_DTOG_TX       | STM32_USB_EPR_DTOG_RX));
wdTemp  |=  (STM32_USB_EPR_CTR_TX        | STM32_USB_EPR_CTR_RX);

wdTemp  &= ~wdMask;

#ifdef  DONT_DEBUG
tclib_printf("stm32_usb_clear_epr_ctr final value =%x\n", wdTemp);
#endif
/*set the current register value*/
*(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP);
}





/*
 **********************************************************************
 *description:  routine to properly set or toggle the DTOG_RX | DTOG_TX
 *              bits of the Endpoint register.
 *input:        unsigned int    wdEndP     = endpoint number
 *              unsigned int    wdNewStat  = the following
 *                                      0  = set to 0
 *                                      1  = set to 1
 *                                     -1  = toggle
 *              unsigned int    wdOffset   = DTOG_RX or DTOG_TX STAT
 *                              STM32_USB_EPR_DTOG_RX_OFFSET
 *                              STM32_USB_EPR_DTOG_TX_OFFSET    
 *output:       void
 *                      
 *note:         the DTOG_RX and DTOG_TX bits are governed by the rules
 *              0 = dont change 1 = TOGGLE
 **********************************************************************
 */
void    stm32_usb_set_epr_dtog(unsigned int wdEndP,\
                               int wdNewStat,\
                               unsigned int wdOffset)
{
unsigned int    wdMask, wdTemp;

/*retrieve the current register value*/
wdMask  = *(PTR_STM32_USB_EP0R + wdEndP);
wdTemp  = wdMask;


/*wdMask has STAT position extracted*/
if(wdOffset == STM32_USB_EPR_DTOG_RX_OFFSET)
        wdMask &= STM32_USB_EPR_DTOG_RX; 
else
        wdMask &= STM32_USB_EPR_DTOG_TX; 


/*normalize then compute XOR of value and mask*/
wdMask >>= wdOffset;
if(wdNewStat != -1 && wdMask == wdNewStat)
        return;

if(wdNewStat == -1)
        wdNewStat = 1;

wdMask  ^= wdNewStat;
wdMask <<= wdOffset;

/*mask out non important bits*/
if(wdOffset == STM32_USB_EPR_DTOG_RX_OFFSET)
        wdMask &= STM32_USB_EPR_DTOG_RX; 
else
        wdMask &= STM32_USB_EPR_DTOG_TX; 


/*invariants - values that will not modify these reg bits*/
wdTemp  &= ~(STM32_USB_EPR_STAT_TX_MASK | \
             STM32_USB_EPR_STAT_RX_MASK | \
            (STM32_USB_EPR_DTOG_TX      | STM32_USB_EPR_DTOG_RX));
wdTemp  |=  (STM32_USB_EPR_CTR_TX       | STM32_USB_EPR_CTR_RX);

/*set user settings*/
wdTemp  |= wdMask;

/*update register*/
*(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP);
}





/*
 **********************************************************************
 *description:  routine to properly set the RX and TX STAT bits in the
 *              Endpoint register.
 *input:        unsigned int    wdEndP     = endpoint number
 *              unsigned int    wdNewStat  = the following
 *                              STM32_USB_EPR_STAT_RX_DISABLE
 *                              STM32_USB_EPR_STAT_TX_DISABLE
 *
 *                              STM32_USB_EPR_STAT_RX_STALL
 *                              STM32_USB_EPR_STAT_TX_STALL
 *
 *                              STM32_USB_EPR_STAT_RX_NAK
 *                              STM32_USB_EPR_STAT_TX_NAK
 *
 *                              STM32_USB_EPR_STAT_RX_VALID
 *                              STM32_USB_EPR_STAT_TX_VALID
 *              unsigned int    wdOffset   = RX or TX STAT
 *                              STM32_USB_EPR_STAT_RX_OFFSET
 *                              STM32_USB_EPR_STAT_TX_OFFSET    
 *output:       void
 *                      
 *note:         the RX_STAT and TX_STAT bits are governed by the rules
 *              0 = dont change 1 = TOGGLE
 *              A major error was not taking this into consideration
 **********************************************************************
 */
void    stm32_usb_set_epr_stat(unsigned int wdEndP,\
                               unsigned int wdNewStat,\
                               unsigned int wdOffset)
{
unsigned int    wdMask, wdTemp;

/*retrieve the current register value*/
wdMask  = *(PTR_STM32_USB_EP0R + wdEndP);
wdTemp  = wdMask;


/*wdMask has STAT position extracted*/
if(wdOffset == STM32_USB_EPR_STAT_RX_OFFSET)
        wdMask &= STM32_USB_EPR_STAT_RX_MASK; 
else
        wdMask &= STM32_USB_EPR_STAT_TX_MASK; 


/*normalize then compute XOR of value and mask*/
wdMask >>= wdOffset;
wdMask  ^= wdNewStat;
wdMask <<= wdOffset;

/*mask out non important bits*/
if(wdOffset == STM32_USB_EPR_STAT_RX_OFFSET)
        wdMask &= STM32_USB_EPR_STAT_RX_MASK; 
else
        wdMask &= STM32_USB_EPR_STAT_TX_MASK; 


/*invariants - values that will not modify these reg bits*/
wdTemp  &= ~(STM32_USB_EPR_STAT_TX_MASK | \
             STM32_USB_EPR_STAT_RX_MASK | \
            (STM32_USB_EPR_DTOG_TX      | STM32_USB_EPR_DTOG_RX));
wdTemp  |=  (STM32_USB_EPR_CTR_TX       | STM32_USB_EPR_CTR_RX);

/*set user settings*/
wdTemp  |= wdMask;

/*update register*/
*(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP);
}