Thursday, November 21, 2013

the sort of bugs that keep you up at night - ripping your hair out

Ok so I figured Id test this $50 investment real quick, before I get back to work I am doing with the Xilinx Xcv3 Spartan board. Figured Id modify the my stm32 firmware/linker scripts for the different I/O ports on this board. Figured Id get it to where I was originally with USB access - didnt think it would take three days ...

I had encountered a serious error right off the bat. Where the code was generating hard faults with fault #3 in execution and systick handler #15 pending and never running (after the first time).

The cortex m3 pushes 8 registers on the stack whenever it takes an exception. So I modified stm32_nvic_fault_handler as follows

asm ("mrs r0, msp");
asm ("ldr   r1, [r0, #24]");

The value returned was something like 0x2000 0887. Checked the map file flash.map and found that was a line of code in the stm32_uart_puts() routine. I tried to disassemble it from within gdb and it balked after address 0x2000 0888.

I used monitor mdw 0x20000888 3 to look at the three words immediately after loading the file "load" and before setting a break point and running to main. I noticed that the value was changing when I got to main ... it made no sense.


I started to wonder why the stack (monitor reg sp) held a value other than the 0x20010000 I wanted it set to. After all the first instruction in main was forcing the stack with

asm ("ldr sp, =(0x20000000 + (64 * 1024) - 4)");

Thats when I noticed that when arm-linux-gnueabi-gdb ./usb_test.out runs it prints out

xPSR: 0x01000000 pc: 0x0800125 msp: 0x20000890

Where was it getting this value? The command
monitor reset halt
Should be placing it in a known state. But where was that. I checked the map file and found nothing that would lead me to believe it was using information I provided. I checked the openocd scripts for odd initialization parameters.

Then I remember that on the arm7 board I had a similar problem because the board would start up running from flash. So when I dumped the value of 0x0 it was 0x2000 0890.

EUREKA!!!

Jtag still allows it to boot from eeprom on reset before it grabs control and the stack value was corrupting my program in ram.

So the solution would have to be the same I did with the arm7 and other cortex-m3. I will wipe out the contents of flash.

I did it with.

monitor flash probe 0
monitor flash protect 0 0 127 off
monitor reset halt
monitor stm32f1x mass_erase 0
monitor flash protect 0 0 127 on 
 

Now arm-linux-gnueabi-gdb ./usb_test.out starts off with

xPSR: 0x01000000 pc: 0xfffffffe msp: 0xfffffffc

No where near my program at ram address 0x2000 0000 (64k of it on this ZET)



No more hard faults and my systick stay-alive-LED blinks once a second. Next Ill tackle uart and usb  ... if I have time













I purchased The Definitive Guide To Arm cortex-m3 this morning as well. It should shed a little more light on some gotchas - not necessarily listed or described well in the arm cortex m3 and the stm32 manuals

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);
}





Friday, November 1, 2013

added IN transfer queing/dequeing routines in IE_stm32_usb.c

void    stm32_usb_cancel_IN_transfers(unsigned int wdEndP);
void    stm32_usb_dequeue_IN_transfers(unsigned int wdEndP);
void    stm32_usb_queue_IN_transfers(unsigned int  wdEndP,\
                                     unsigned char *ptrBuffer,\
                                     unsigned int  wdSize);



/*
 **********************************************************************
 *#define constants for USB enumeration
 **********************************************************************
 */
struct  STR_USB_PKT_XFER{
                        unsigned char   *ptrCtrlBuf;
                        int     wdCtrlSize;

                        unsigned char   *ptrUserBuf;
                        int     wdUserSize;
                        }strUsbPktXfer;
 
 
 
/*
 **********************************************************************
 *description:  tx transfer cancel routine
 *input:        unsigned int    wdEndP     = endpoint number
 *note:         called at post_reset and whenever the need arises
 **********************************************************************
 */
void
stm32_usb_cancel_IN_transfers(unsigned int  wdEndP)
{
if(wdEndP == STMUSB_ENDP_USER_NUM)
        strUsbPktXfer.wdUserSize = -1;
else
        strUsbPktXfer.wdCtrlSize = -1;
}





/*
 **********************************************************************
 *description:  tx transfer dequeue routine
 *input:        unsigned int    wdEndP     = endpoint number
 *note:         be prepared to deal with zero length packets
 **********************************************************************
 */
void
stm32_usb_dequeue_IN_transfers(unsigned int  wdEndP)
{
unsigned char   *ptrBuf, **ptrPtrBuf;
int     *ptrCount, wdCount, wdEndPSize;


if(wdEndP == STMUSB_ENDP_USER_NUM)
        {
        ptrBuf    = strUsbPktXfer.ptrUserBuf;
        ptrPtrBuf = (unsigned char **)&strUsbPktXfer.ptrUserBuf;
        ptrCount  = &strUsbPktXfer.wdUserSize;

        wdCount   = *ptrCount;
        wdEndPSize= STMUSB_ENDP_USER_SIZE;
        }
else
        {
        ptrBuf    = (unsigned char *)strUsbPktXfer.ptrCtrlBuf;
        ptrPtrBuf = (unsigned char **)&strUsbPktXfer.ptrCtrlBuf;
        ptrCount  = &strUsbPktXfer.wdCtrlSize;

        wdCount   = *ptrCount;
        wdEndPSize= STMUSB_ENDP_CTRL_SIZE;
        }
                

/*check if xfer is >= 0, return if its -1*/
if(wdCount < 0)                         /*check xfer == -1 or xfer >=0 */
        return;
else
        {
        if(wdCount == 0)                /*should we generate a zero length packet*/
                {
                *ptrCount = -1;         /*end future xfers*/
                stm32_usb_endp_send_packet(wdEndP,\
                                           ptrBuf,\
                                           wdEndPSize,\
                                           0);
                }
        else
                {
                if(wdCount >= wdEndPSize)
                        wdCount = wdEndPSize;

                stm32_usb_endp_send_packet(wdEndP,\
                                           ptrBuf,\
                                           wdEndPSize,\
                                           wdCount);

                /*update variables*/
                *(ptrPtrBuf) = (unsigned char *)(ptrBuf + wdCount);
                *(ptrCount) -= wdCount;


                /*
                 *******************************************************
                 *prep for a zlp on the next interrupt if 
                 *this is the last transfer (wdCount == 0) and
                 *the transfer size matches the endpoint buffer size
                 *******************************************************
                 */
                if(wdCount == wdEndPSize &&\
                   (*ptrCount) == 0)
                        ;               /*do nothing here*/
                else
                        (*ptrCount) = -1;
                }
        }
}       





/*
 **********************************************************************
 *description:  tx transfer queue routine
 *input:        unsigned int    wdEndP     = endpoint number
 *              unsigned char   *ptrBuffer = pointer to buffer
 *              unsigned int    wdSize     = buffer size in bytes
 *notes:        store pointers to the global record. stores count of 
 *              records
 **********************************************************************
 */
void
stm32_usb_queue_IN_transfers(unsigned int  wdEndP,\
                             unsigned char *ptrBuffer,\
                             unsigned int  wdSize)
{
if(wdEndP == STMUSB_ENDP_USER_NUM)
        {
        strUsbPktXfer.ptrUserBuf = ptrBuffer;
        strUsbPktXfer.wdUserSize = wdSize;
        }
else
        {
        strUsbPktXfer.ptrCtrlBuf = ptrBuffer;
        strUsbPktXfer.wdCtrlSize = wdSize;
        }
}




Friday, October 4, 2013

modification to using external pin to control the USB D+ line/pin

So I decided to abandon using the jumper switch. It suffers from mechanical bounce and that is a major problem going forward debugging wize. Cant have the usb host mis-interpreting connects and disconnects. Besides I wanted a purely software solution since that will allow me to debug this remotely without having to stand in front of the board pushing and holding the pushbutton switch-hack I used previous to this.

My solution?
The realization that the external pin ALONE does not produce enough current to drive the USB D+ pin high. So my work around was to take a 3 position jumper and tie all three leads together - and plug it into J6 effectively driving D+ to vcc by DEFAULT.

Then connect one of the jumper positions to pin PA0. Now when the software loads it calls stm32_usb_disconnect_pullup() which sets PA0 as an output and pulls it down to ground (0). then later when the user presses "c" on the terminal I call stm32_usb_connect_pullup() which sets PA0 as in input and floating - which drives D+ back up to VCC. This results seen on my Total Phase USB Sniffer is as I expected!!!

Clever?
Yeah ... thats me


/*
 **********************************************************************
 *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
 *implemented:  using three position jumper pin with all three wire
 *              wrapped linked - and a jumper from PAx to one of the
 *              positions
 **********************************************************************
 */
void    stm32_usb_connect_pullup(unsigned int wdPin)
{
volatile unsigned int wdTemp, wdMask;


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

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





/*
 **********************************************************************
 *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_disconnect_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 << 2));
wdMask  = ((STM32_GPIO_CNF_OUT_GEN_PUSH_PULL << 2) | \
            STM32_GPIO_MODE_OUTPUT_2MHZ) << (wdPin << 2);

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

Thursday, October 3, 2013

bug fixes 10-02-13

1. the size of the receiver packet buffer must be packet size + 2 bytes to account for the 2 bytes of crc the usb controller slave will read. when there is no space for it it will send a STALL to the host


/*
 **********************************************************************
 *description:  usb routine to enable a particular endpoint
 *
 *inputs:       unsigned int    wdEndPNum = endpoint number
 *              unsigned int    wdEndPType= endpoint type
 *                              STM32_USB_EPR_EP_TYPE_BULK
 *                              STM32_USB_EPR_EP_TYPE_CONTROL
 *                              STM32_USB_EPR_EP_TYPE_ISO
 *                              STM32_USB_EPR_EP_TYPE_INTERRUPT
 *              unsigned int    wdEndPSize= 8,16,32,64 etc
 *output:       wdLclPktAddr    = usb local packet address
 *NOTE:         packet descriptor size must be pktsize+2 to account for
 *              the two byte crc the host writes
 **********************************************************************
 */
int     stm32_usb_endp_enable(unsigned int wdEndPNum,\
                              unsigned int wdEndPType,\
                              unsigned int wdEndPSize,\
                              unsigned int *ptrDescAddr,\
                              unsigned int wdLclPktAddr)
{
volatile unsigned int   wdTemp;


/*set tx buffer addr*/
(*ptrDescAddr++) =  wdLclPktAddr;
wdLclPktAddr    +=  wdEndPSize;


/*set tx count*/
(*ptrDescAddr++) =  wdEndPSize;


/*adjust wdEndPSize to account for extra 2 bytes of crc*/
/*set rx buffer addr*/
wdEndPSize      +=  2;
(*ptrDescAddr++) =  wdLclPktAddr;
wdLclPktAddr    +=  wdEndPSize;


if(wdEndPSize >= 64)
        {
        wdTemp  = STM32_USB_COUNT_RX_BL_SIZE_32BYTE;
        wdTemp |= ((wdEndPSize >> 6) << STM32_USB_COUNT_RX_NUM_BLOCK_OFFSET);
        }
else
        {
        wdTemp  = STM32_USB_COUNT_RX_BL_SIZE_2BYTE;
        wdTemp |= ((wdEndPSize >> 1) << STM32_USB_COUNT_RX_NUM_BLOCK_OFFSET);
        }


/*set rx count*/
(*ptrDescAddr++) = wdTemp;


/*set rx and tx status bits*/
wdTemp  = (STM32_USB_EPR_STAT_RX_VALID << STM32_USB_EPR_STAT_RX_OFFSET);
wdTemp |= (wdEndPType << STM32_USB_EPR_EP_TYPE_OFFSET);
wdTemp |= (STM32_USB_EPR_STAT_TX_NAK   << STM32_USB_EPR_STAT_TX_OFFSET);
wdTemp |= wdEndPNum;

/*these bits be written as 1 to not clear*/
wdTemp |= (STM32_USB_EPR_CTR_RX|STM32_USB_EPR_CTR_TX);
*(PTR_STM32_USB_EP0R + wdEndPNum) = wdTemp;


return wdLclPktAddr;
} 
 
 
 
2. the access of the packet buffer in stm32_usb_get/send_packet() must be made as words even though only the two bytes are relevant


 
/*
 **********************************************************************
 *description:  usb endpoint send packet routine
 *
 *inputs:       unsigned int    wdEndPNum = endpoint number
 *              unsigned int    wdEndPSize= 8,16,32,64 etc
 *                      char    *ptrBuffer= pointer to buffer
 *              unsigned int    wdBytelen = byte length
 *output:       0 on success -1 on error (timeout)
 **********************************************************************
 */
int    stm32_usb_endp_send_packet(unsigned int  wdEndPNum,\
                                  unsigned char *ptrBuffer,\
                                  unsigned int  wdEndPSize,\
                                  unsigned int  wdBytelen)
{
volatile unsigned int   wdTemp, *ptrTemp, wdCount, *ptrDst;
volatile unsigned int   wdFlag=FALSE, wdZlp=FALSE;
unsigned short int  *ptrSrc;


ptrSrc = (unsigned short int *)ptrBuffer;
while(wdBytelen != 0)
        {
        if(wdBytelen > wdEndPSize)
                wdCount = wdEndPSize;
        else
                wdCount = wdBytelen;


        /*
         **************************************************************
         *copy the data into the buffer - retrieve address
         *update transmission count 
         *now enable transmission control reg - retrieve value
         *prepare for transmission - set status bits
         **************************************************************
         */
        ptrTemp  = (volatile unsigned int *)PTR_STM32_USB_EPRAM;
        ptrTemp += (4 * wdEndPNum);

        wdTemp   = (*ptrTemp);
        wdTemp   = (PTR_STM32_USB_EPRAM + (wdTemp << 1));

        ptrTemp += 1;                   /*tx count descriptor*/
        (*ptrTemp) = wdCount;
        

        /*
         **************************************************************
         *adjust wdBytelen before starting data xfer
         **************************************************************
         */
        wdBytelen -= wdCount;
        if(wdBytelen == 0 && (wdCount == wdEndPSize))
                wdZlp = TRUE;
        else
                wdZlp = FALSE;

        ptrDst = (unsigned int *)wdTemp;
        while(wdCount > 0)
                {
                *(ptrDst++)     = (int)(*(ptrSrc++));
                if(wdCount >= 2)
                        wdCount -= 2;
                else
                        wdCount -= 1;
                }


        ptrTemp  = PTR_STM32_USB_EP0R;
        ptrTemp += wdEndPNum;
        wdTemp   = (*ptrTemp);

        /*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);
        /*user settings*/
        wdTemp  |=  (STM32_USB_EPR_STAT_TX_VALID << \
                     STM32_USB_EPR_STAT_TX_OFFSET);
        (*ptrTemp) = wdTemp;

 
        /*
         **************************************************************
         *continue until done (do it for 1ms then bail on error)
         *wait for CTR_TX to be set
         *clear it and restart
         **************************************************************
         */
        wdCount  =  10;
        do
                {
                stm32_timer_uS_delay(100);
                wdCount--;

                wdTemp   = (*PTR_STM32_USB_ISTR);
                if((wdTemp & STM32_USB_ISTR_RESET) == STM32_USB_ISTR_RESET)
                        {
                        wdFlag = TRUE;
                        break;
                        }

                wdTemp   = (*ptrTemp);
                #ifdef  IGNORE_FOR_NOW
                if((wdTemp & STM32_USB_EPR_CTR_RX) == STM32_USB_EPR_CTR_RX)
                        {
                        wdFlag = TRUE;
                        break;
                        }
                #endif

                }while(((wdTemp & STM32_USB_EPR_CTR_TX) == 0) &&\
                        (wdCount > 0));
        

        /*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);
        /*user settings*/
        wdTemp  &= ~(STM32_USB_EPR_CTR_TX);
        (*ptrTemp) = wdTemp;
        }


/*
 **********************************************************************
 *should we send a zlp ?
 **********************************************************************
 */
if(wdZlp == TRUE)
        {
        ptrTemp    = (volatile unsigned int *)PTR_STM32_USB_EPRAM;
        ptrTemp   += (4 * wdEndPNum);
        ptrTemp   += 1;                 /*tx count descriptor*/
        (*ptrTemp) = 0;
        
        ptrTemp  = PTR_STM32_USB_EP0R;
        ptrTemp += wdEndPNum;
        wdTemp   = (*ptrTemp);

        /*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);
        /*user settings*/
        wdTemp  |=  (STM32_USB_EPR_STAT_TX_VALID << \
                     STM32_USB_EPR_STAT_TX_OFFSET);
        (*ptrTemp) = wdTemp;
        }

return 0;
}





/*
 **********************************************************************
 *description:  usb endpoint get packet routine
 *
 *inputs:       unsigned int    wdEndPNum = endpoint number
 *                      char    *ptrBuffer= pointer to buffer
 *              unsigned int    wdBytelen = byte length
 *output:       int                       = size of data posted
 **********************************************************************
 */
int     stm32_usb_endp_get_packet(unsigned int  wdEndPNum,\
                                  unsigned char *ptrBuffer,\
                                  unsigned int  wdBytelen)
{
volatile unsigned int   wdTemp, *ptrTemp, wdCount, wdSize ,*ptrSrc;
unsigned short    int   *ptrDst;


/*
 **********************************************************************
 *get byte size
 *copy over data
 **********************************************************************
 */
ptrTemp  = (volatile unsigned int *)PTR_STM32_USB_EPRAM;
ptrTemp += (4 * wdEndPNum);
ptrTemp += 2;

wdTemp   = (*ptrTemp);
wdTemp   = (PTR_STM32_USB_EPRAM + (wdTemp << 1));
wdCount  = *(ptrTemp +1);
wdCount &= STM32_USB_COUNT_RX_MASK;
wdSize   = wdCount;

if(wdCount > wdBytelen)
        wdCount = wdBytelen;

ptrSrc = (unsigned int *)wdTemp;
ptrDst = (unsigned short int *)ptrBuffer;
while(wdCount > 0)
        {
        *(ptrDst++)      = (short int)(*(ptrSrc++));
        if(wdCount >= 2)
                wdCount -= 2;
        else
                wdCount -= 1;
        }


/*
 **********************************************************************
 *prepare for new transmission at a later date
 *set valid bits
 **********************************************************************
 */
ptrTemp  = PTR_STM32_USB_EP0R;
ptrTemp += wdEndPNum;
wdTemp   = (*ptrTemp);


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

return wdSize;
}




 
 
 

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);
}















Wednesday, August 28, 2013

usb_test.c

/*
 **********************************************************************
 *name:         stmusb_test.c
 *author:       Samuel Igwe
 *date:         08/12/2013
 *description:  stm32 usb test code.
 **********************************************************************
 */
#include "IE_stm32.h"
#include "IE_tclib.h"
/*#include "usb_test.h"*/





/*
 **********************************************************************
 *mappings
 *PA0           = external wire jumpered to usb d+
 **********************************************************************
 */
#define USB_TEST_GPIO_PIN_USB                           STM32_GPIO_PIN_USB
#define USB_TEST_GPIO_PIN_DELAY                         5000
#define USB_TEST_GPIO_PIN_CONNECT_CHAR                  'c'
#define USB_TEST_GPIO_PIN_DISCONNECT_CHAR               'd'



/*
 **********************************************************************
 *macros
 **********************************************************************
 */
void    usb_test_user_connect(void);
#define STMUSB_SHOW_TIME(ptrString)                     \
        {                                               \
        stmlib_printf("%x\t", stm32_get_seconds);\
        stmlib_printf("%s\n", ptrString);               \
        }



/*
 **********************************************************************
 *description:  main() function
 *              this is where it all happens
 **********************************************************************
 */
int main()
{



/*
 **********************************************************************
 *stm32_init()     initialization ... must be called first
 *stm32_usb_init() initialization
 **********************************************************************
 */
stm32_init();
stm32_usb_init();
stm32_systick_hook_services(usb_test_user_connect);
stm32_usb_disconnect_pullup(USB_TEST_GPIO_PIN_USB);
stm32_timer_uS_delay(250000);
stm32_usb_connect_pullup(USB_TEST_GPIO_PIN_USB);


/*
 **********************************************************************
 *now loop forever
 **********************************************************************
 */
while(1)
        ;

return 0;
}





/*
 **********************************************************************
 *description:  used to check for rx value 'c'|'C' which is a trigger
 *              to call stm32_usb_connect_pullup()
 *inputs:       none
 **********************************************************************
 */
void    usb_test_user_connect(void)
{
volatile unsigned int wdTemp;



/*
 **********************************************************************
 *check the uart rx read status
 *retrieve the data
 **********************************************************************
 */
wdTemp  = (*PTR_STM32_UART1_SR);
if((wdTemp & STM32_UART_SR_RXNE) != STM32_UART_SR_RXNE)
        return;


wdTemp  = (*PTR_STM32_UART1_DR);
wdTemp &= 0x1ff;
switch(wdTemp)
        {
        case    USB_TEST_GPIO_PIN_CONNECT_CHAR:
                {
                tclib_printf("-------user-usb-connect----request------\n", 0);
                stm32_usb_connect_pullup(USB_TEST_GPIO_PIN_USB);
                break;
                }
        case    USB_TEST_GPIO_PIN_DISCONNECT_CHAR:
                {
                tclib_printf("-------user-usb-disconnect-request------\n", 0);
                stm32_usb_disconnect_pullup(USB_TEST_GPIO_PIN_USB);
                break;
                }
        default:
                break;
        }
}






IE_stm32_usbdev.h

/*
 **********************************************************************
 *name:         IE_stm32_usbdev.h
 *author:       Samuel Igwe
 *date:         08/27/2013
 *description:  IE_stm32_usbdev usb device header
 **********************************************************************
 */
#ifndef IE_STM32_USB_DEV
#define IE_STM32_USB_DEV



/*
 **********************************************************************
 *usb control and user endpoint (interrupt)
 **********************************************************************
 */
#define STMUSB_ENDP_CTRL_NUM                            0
#define STMUSB_ENDP_CTRL_SIZE                           8
#define STMUSB_ENDP_CTRL_MAX_SIZE                       64

#define STMUSB_ENDP_USER_NUM                            1
#define STMUSB_ENDP_USER_SIZE                           8
#define STMUSB_ENDP_INT_INTERVAL                        2



/*
 **********************************************************************
 *descriptor type
 **********************************************************************
 */
#define USBDEV_DESCR_TYPE_DEVICE                        1
#define USBDEV_DESCR_TYPE_CONFIGURATION                 2
#define USBDEV_DESCR_TYPE_STRING                        3
#define USBDEV_DESCR_TYPE_INTERFACE                     4
#define USBDEV_DESCR_TYPE_ENDPOINT                      5
#define USBDEV_DESCR_TYPE_DEV_QUALIFIER                 6
#define USBDEV_DESCR_TYPE_OTHER_SPEED_CONFIG            7
#define USBDEV_DESCR_TYPE_INTERFACE_POWER               8
#define USBDEV_DESCR_TYPE_OTG                           9
#define USBDEV_DESCR_TYPE_DEBUG                         10
#define USBDEV_DESCR_TYPE_INTERFACE_ASSOC               11



/*
 **********************************************************************
 *usb class code
 **********************************************************************
 */
#define USBDEV_CLASS_CODE_AUDIO                         0x01
#define USBDEV_CLASS_CODE_COMM                          0x02
#define USBDEV_CLASS_CODE_HID                           0x03
#define USBDEV_CLASS_CODE_PHYSICAL                      0x05
#define USBDEV_CLASS_CODE_IMAGE                         0x06
#define USBDEV_CLASS_CODE_PRINTER                       0x07
#define USBDEV_CLASS_CODE_STORAGE                       0x08
#define USBDEV_CLASS_CODE_HUB                           0x09
#define USBDEV_CLASS_CODE_DATA                          0x0a
#define USBDEV_CLASS_CODE_CARD                          0x0b
#define USBDEV_CLASS_CODE_SECURITY                      0x0d
#define USBDEV_CLASS_CODE_VIDEO                         0x0e
#define USBDEV_CLASS_CODE_PERSONAL                      0x0f
#define USBDEV_CLASS_CODE_AUDIO_VIDEO                   0x10
#define USBDEV_CLASS_CODE_DIAGNOSTIC                    0xdc
#define USBDEV_CLASS_CODE_WIRELESS                      0xe0
#define USBDEV_CLASS_CODE_APPLICATION                   0xfe
#define USBDEV_CLASS_CODE_VENDOR_SPECIFIC               0xff



/*
 **********************************************************************
 *usb requests
 **********************************************************************
 */
#define USBDEV_REQ_GET_STATUS                           0
#define USBDEV_REQ_CLEAR_FEATURE                        1
#define USBDEV_REQ_SET_FEATURE                          3
#define USBDEV_REQ_SET_ADDRESS                          5
#define USBDEV_REQ_GET_DESCRIPTOR                       6
#define USBDEV_REQ_SET_DESCRIPTOR                       7
#define USBDEV_REQ_GET_CONFIGURATION                    8
#define USBDEV_REQ_SET_CONFIGURATION                    9
#define USBDEV_REQ_GET_INTERFACE                        10
#define USBDEV_REQ_SET_INTERFACE                        11
#define USBDEV_REQ_SYNTH_FRAME                          12



/*
 **********************************************************************
 *transfer type request related #defines
 **********************************************************************
 */
#define USBDEV_CONFIG_DESC_ATTR_BUS                     (1 << 7)
#define USBDEV_CONFIG_DESC_ATTR_SELF                    (1 << 6)
#define USBDEV_CONFIG_DESC_ATTR_REMOTE_WAKE             (1 << 5)

#define USBDEV_ENDP_DESC_ADDR_DIR_IN                    (1 << 7)
#define USBDEV_ENDP_DESC_ADDR_DIR_OUT                   (0 << 7)

#define USBDEV_ENDP_DESC_ATTR_CTRL                      0
#define USBDEV_ENDP_DESC_ATTR_ISOCHRONOUS               1
#define USBDEV_ENDP_DESC_ATTR_BULK                      2
#define USBDEV_ENDP_DESC_ATTR_INTERRRUPT                3



/*
 **********************************************************************
 *8 byte setup packet offsets
 **********************************************************************
 */
#define USBDEV_SETUP_REQTYPE_OFFSET                     0
        #define USBDEV_SETUP_REQTYPE_DIR_H_TO_D         (0 << 7)
        #define USBDEV_SETUP_REQTYPE_DIR_D_TO_H         (1 << 7)
        #define USBDEV_SETUP_REQTYPE_DIR_MASK           (1 << 7)

        #define USBDEV_SETUP_REQTYPE_TYPE_STANDARD      (0 << 5)        
        #define USBDEV_SETUP_REQTYPE_TYPE_CLASS         (1 << 5)        
        #define USBDEV_SETUP_REQTYPE_TYPE_VENDOR        (2 << 5)        
        #define USBDEV_SETUP_REQTYPE_TYPE_RESERVED      (3 << 5)        
        #define USBDEV_SETUP_REQTYPE_TYPE_MASK          (3 << 5)        

        #define USBDEV_SETUP_REQTYPE_DEST_DEVICE        0
        #define USBDEV_SETUP_REQTYPE_DEST_INTERFACE     1
        #define USBDEV_SETUP_REQTYPE_DEST_ENDPOINT      2
        #define USBDEV_SETUP_REQTYPE_DEST_OTHER         3
        #define USBDEV_SETUP_REQTYPE_DEST_MASK          0x1f

#define USBDEV_SETUP_REQUEST_OFFSET                     1
#define USBDEV_SETUP_VALUE_OFFSET                       2
#define USBDEV_SETUP_INDEX_OFFSET                       4
#define USBDEV_SETUP_LENGTH_OFFSET                      6



/*
 **********************************************************************
 *string descriptors
 *offset 1 = manufacturers string
 *offset 2 = product string 
 *offset 3 = serial number
 *
 *swStrDescr below is an array of 4 of: pointer
 **********************************************************************
 **********************************************************************
 *string descriptor is unicode (16 bits)
 *string descriptor is 2*strlen(ptrDescStr)+2 bytes
 **********************************************************************
 */
static  unsigned        char    ptrStr0[]={
        (4),                                    /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_STRING,               /*string descriptor type*/
        0x9,                                    /*english code*/
        0x4};                                   /*english subcode*/

static  unsigned        char    ptrStr1[]={
        ((13 << 1)+2),                          /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_STRING,               /*string descriptor type*/
        'I',0,'g',0,'b',0,'o',0,' ',0,\
        'E',0,'m',0,'b',0,'e',0,'d',0,'d',0,'e',0,'d',0};

static  unsigned        char    ptrStr2[]={
        ((9 << 1)+2),                           /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_STRING,               /*string descriptor type*/
        'M',0,'y',0,' ',0,\
        'D',0,'e',0,'v',0,'i',0,'c',0,'e',0}; 

static  unsigned        char    ptrStr3[]={
        ((4 << 1)+2),                           /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_STRING,               /*string descriptor type*/
        '1',0,'2',0,'3',0,'4',0};

static  unsigned int    swUsbDevDescr[]={(unsigned int)ptrStr0,\
                                         (unsigned int)ptrStr1,\
                                         (unsigned int)ptrStr2,\
                                         (unsigned int)ptrStr3};



/*
 **********************************************************************
 *device descriptor
 **********************************************************************
 */
static  const   char    sbDevDescr[]={
        18,                                     /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_DEVICE,               /*device descriptor type*/
        0x10,                                   /*bcd usb version 1.1 = word*/
        0x01,
        0,                                      /*device class*/
        0,                                      /*device subclass*/
        0,                                      /*device protocol*/
        STMUSB_ENDP_CTRL_SIZE,
                                                /*max packet size*/
        0xde,                                   /*vendor id = word*/
        0xc0,

        0xed,                                   /*device id = word*/
        0xfe,

        0x0,                                    /*bcd device = word*/
        0x1,

        1,                                      /*manufacturer descriptor string*/
        2,                                      /*product      descriptor string*/
        3,                                      /*serial       descriptor string*/
        1};                                     /*number of configurations*/


/*
 **********************************************************************
 *configuration descriptor
 **********************************************************************
 */
static  const   char    sbConfDescr[]={
        9,                                      /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_CONFIGURATION,        /*configuration descriptor type*/
        (9+9+7+7),                              /*total lenght = word*/
        0,

        1,                                      /*number of interfaces*/
        1,                                      /*configuration value id*/
        0,                                      /*configuration descriptor string*/
        (USBDEV_CONFIG_DESC_ATTR_SELF | \
         USBDEV_CONFIG_DESC_ATTR_BUS),          /*bus attributes*/
        250,


        /*
         **************************************************************
         *interface descriptor
         **************************************************************
         */
        9,                                      /*byte length of descriptor*/
        USBDEV_DESCR_TYPE_INTERFACE,            /*interface descriptor type*/
        0,                                      /*interface number - zero based*/
        0,                                      /*alternate setting*/
        2,                                      /*number of endpoints*/
        USBDEV_CLASS_CODE_HID,                  /*human interface device*/

        0,                                      /*interface subclass*/
        0,                                      /*interface protocol*/
        0,                                      /*interface descriptor string*/


        /*
         **************************************************************
         *endpoint descriptor - input
         **************************************************************
         */
        7,                                      /*byte lenght of descriptor*/
        USBDEV_DESCR_TYPE_ENDPOINT,             /*endpoint descriptor type*/
        (USBDEV_ENDP_DESC_ADDR_DIR_IN |\
         STMUSB_ENDP_USER_NUM),                 /*endpoint in*/
        USBDEV_ENDP_DESC_ATTR_INTERRRUPT,       /*attributes*/
        STMUSB_ENDP_USER_SIZE,                  /*packet length = word*/
        0,
        STMUSB_ENDP_INT_INTERVAL,               /*interrupt interval*/


        /*
         **************************************************************
         *endpoint descriptor - output
         **************************************************************
         */
        7,                                      /*byte lenght of descriptor*/
        USBDEV_DESCR_TYPE_ENDPOINT,             /*endpoint descriptor type*/
        (USBDEV_ENDP_DESC_ADDR_DIR_OUT |\
         STMUSB_ENDP_USER_NUM),                 /*endpoint in*/
        USBDEV_ENDP_DESC_ATTR_INTERRRUPT,       /*attributes*/
        STMUSB_ENDP_USER_SIZE,                  /*packet length = word*/
        0,
        STMUSB_ENDP_INT_INTERVAL};              /*interrupt interval*/


#endif