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); }
No comments:
Post a Comment