cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 Clock, Flash, and USB

mvi
Associate II
Posted on April 16, 2008 at 02:54

STM32 Clock, Flash, and USB

24 REPLIES 24
lanchon
Associate III
Posted on May 17, 2011 at 12:29

> check out the interaction between endp.c and main.c

and also the USART IRQ handler.

mvi
Associate II
Posted on May 17, 2011 at 12:29

Hi Lanchon,

I cannot locate the USART IRQ handler. Which file is it in?

This needs much more work than I anticipated!

-Mad

mvi
Associate II
Posted on May 17, 2011 at 12:29

Hi again Lanchon,

1) Don't the D+ and D- GPIOs need to be set in the initializing, to maybe a push-pull?

2) ''The SysTick calibration value is fixed to 9000 which allows the generation of a time-base of 1ms with the SysTick clock set to 9 MHz (max HCLK/8)'' I got HCLK = 48MHz, so is SysTick time-base = 667us? Apart from generating an interrupt at every time-base, is there any other use for the SysTick?

3) Is there any particular reason that you changed long to int for u32 in your code?

I noticed the Double-buffer for the Bulk endpoint. Can this help in anyway with data races?

When are the ''EP1_IN_Callback'' and ''EP3_OUT_Callback'' functions called?

The lack of documentation on the USB Firmware Library is disappointing.

-Mad

BTW WinMerge is awesome. Thanks!

[ This message was edited by: mvi on 09-04-2008 18:19 ]

lanchon
Associate III
Posted on May 17, 2011 at 12:29

> I cannot locate the USART IRQ handler. Which file is it in?

stm32f10x_it.c or something like that.

> 1) Don't the D+ and D- GPIOs need to be set in the initializing, to maybe a push-pull?

I thought this too, apparently not. either a) the USB lib is configuring the GPIO, or b) the USB transceiver overrides the GPIO. I did a quick search of the lib and didn't find evidence of a). then I did a quick search of the ref manual and didn't find evidence of b) either. if you ask me I believe b) is true: the transceiver probably can't use the normal GPIO I/O structure and so must be connected in parallel to the I/O pad; it'd disable the GPIO to avoid contention. there's a precedent: a similar situation is found in the LSE oscillator, the peripheral overrides the GPIO.

> 2) ''The SysTick calibration value is fixed to 9000 which allows the generation of a time-base of 1ms with the SysTick clock set to 9 MHz (max HCLK/8)'' I got HCLK = 48MHz, so is SysTick time-base = 667us? Apart from generating an interrupt at every time-base, is there any other use for the SysTick?

I'm not familiar with the systick timer. it's an ARM thing, ARM wants it there for some reason. I guess it could be used for profiling tools that work on all cortex cores independent of chip vendor.

> 3) Is there any particular reason that you changed long to int for u32 in your code?

style. I want s32 and int to be compatible types. the fw lib defines s32 and u32 as long ints. in codesourcery GCC both ints and longs are 32 bit types, but they are not assignment compatible and rightly so. (I did that change in cs-stm32-1.0, the original ..._type.h file is also included.)

> I noticed the Double-buffer for the Bulk endpoint. Can this help in anyway with data races?

data races are a problem generated by asynchronous code, not by a peripheral mode. they happen when the two IRQ handlers and main code interact in unintended ways.

> When are the ''EP1_IN_Callback'' and ''EP3_OUT_Callback'' functions called?

these are called from the USB low priority interrupt, when a ''correct transfer'' event or something like that occurs.

> The lack of documentation on the USB Firmware Library is disappointing.

I'm more worried about the quality of the lib itself. fortunately my project doesn't have to involve USB...

your problem is not that hard. check out endp.c in my code to see how to solve the races.

mvi
Associate II
Posted on May 17, 2011 at 12:29

Hello!

/*******************************************************************************

* Function Name : UserToPMABufferCopy

* Description : Copy a buffer from user memory area to packet memory area (PMA)

* Input : - pbUsrBuf: pointer to user memory area.

* - wPMABufAddr: address into PMA.

* - wNBytes: no. of bytes to be copied.

* Output : None.

* Return : None .

*******************************************************************************/

void UserToPMABufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)

{

u32 n = (wNBytes + 1) >> 1; /* n = (wNBytes + 1) / 2 */

u32 i, temp1, temp2;

u16 *pdwVal;

pdwVal = (u16 *)(wPMABufAddr * 2 + PMAAddr);

for (i = n; i != 0; i--)

{

temp1 = (u16) * pbUsrBuf;

pbUsrBuf++;

temp2 = temp1 | (u16) * pbUsrBuf << 8;

*pdwVal++ = temp2;

pdwVal++;

pbUsrBuf++;

}

}

/*******************************************************************************

* Function Name : PMAToUserBufferCopy

* Description : Copy a buffer from user memory area to packet memory area (PMA)

* Input : - pbUsrBuf = pointer to user memory area.

* - wPMABufAddr = address into PMA.

* - wNBytes = no. of bytes to be copied.

* Output : None.

* Return : None.

*******************************************************************************/

void PMAToUserBufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)

{

u32 n = (wNBytes + 1) >> 1;/* /2*/

u32 i;

u32 *pdwVal;

pdwVal = (u32 *)(wPMABufAddr * 2 + PMAAddr);

for (i = n; i != 0; i--)

{

*(u16*)pbUsrBuf++ = *pdwVal++;

pbUsrBuf++;

}

}

From the above functions:

> pdwVal = (u16 *)(wPMABufAddr * 2 + PMAAddr);

> pdwVal = (u32 *)(wPMABufAddr * 2 + PMAAddr);

Any idea why wPMABufAddr is multiplied by 2 before adding to the PMAAddr base address?

-Mad

lanchon
Associate III
Posted on May 17, 2011 at 12:29

this is horrible and buggy code. code like this gives me the creeps and makes me ask, is the hardware of the peripherals designed to the same standards?

> Any idea why wPMABufAddr is multiplied by 2 before adding to the PMAAddr base address?

my guess is that the PMA must be 16 bits wide and must only contain 2 buffered bytes per 32-bit word on bits 0 to 15, check the manual. C has nice constructs called pointers, but the library designer chose to use an integer relative to the start of the PMA to point into the PMA. and chose the integer to mean the number of useful bytes from the start of the PMA; since there are 2 useful bytes per word (per 4 addressable bytes), the number is multiplied by 2. of course this only works if the integer is even, nice.

in UserToPMABufferCopy this code:

u32 temp1, temp2;

temp1 = (u16) * pbUsrBuf;

pbUsrBuf++;

temp2 = temp1 | (u16) * pbUsrBuf << 8;

*pdwVal++ = temp2;

pdwVal++;

pbUsrBuf++;

is just:

*pdwVal++ = *((u16*) pbUsrBuf)++;

pdwVal++;

but awfully written. one could say that the style is to assure that the user buffer will not be read unaligned (is this necessary in the cortex?), but then a similar precaution is not taken in PMAToUserBufferCopy. plus the function is buggy: if wNBytes is odd, an extra byte is read from the user buffer. (this bug will manifest if the buffer is the last thing on a memory area.)

PMAToUserBufferCopy has the same bug, only this time the extra byte in user space is written, triggering havoc. consistency is lacking: when accessing the PMA, 32 bit words are read but 16 bit words are written, why? and look at this horrific code:

*(u16*)pbUsrBuf++ = *pdwVal++;

pbUsrBuf++;

is it *((u16*)(pbUsrBuf++)) or *(((u16*)pbUsrBuf)++)? are maintainers really supposed to know the relative preference of the postincrement and the cast operators? the above segment should be expressed as:

*((u16*) pbUsrBuf)++ = *pdwVal++;

examples like these can't help but give me a bad feeling. on the hardware side I've found design flaws in the few peripherals I've studied, which BTW don't include the USB macrocell. (I didn't report them because ST didn't establish an efficient bug reporting channel so I guess they are not terribly interested. or read it as: ST didn't motivate me enough to write them down.) design flaws are ''ok'' by me, what scares me a bit is the implementation. I'm hoping the short errata is the result of solid implementation, not sloppy testing.

lanchon
Associate III
Posted on May 17, 2011 at 12:29

> PMAToUserBufferCopy has the same bug, only this time the extra byte in user space is written, triggering havoc.

BTW, to avoid this bug (and to provide double buffering) I use an intermediate buffer in ram for reception in the code I posted.

mvi
Associate II
Posted on May 17, 2011 at 12:29

Hello Lanchon and Friends,

I was busy with some Schematic/Layout of a different part of the circuit. Just got back to the USB!

Yet again a dark mystery in the form of memory buffers have brought me to a halt...

From stm32f10x_it.c:

bool lowByte = TRUE;

extern u16 count_in;

extern u32 *txDataBuffer;

/*******************************************************************************

* Function Name : USART1_IRQHandler

* Description : This function handles USART1 global interrupt request.

* Input : None

* Output : None

* Return : None

*******************************************************************************/

void USART1_IRQHandler(void) {

if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {

/* Send the received data to the PC Host*/

u8 tempData;

tempData = (u8) USART_ReceiveData(USART1);

// Carriage rturn - '\r'

if ((tempData == 0x0D) && (count_in > 0)) {

SetEPTxValid(ENDP1);

}

else {

if (lowByte) {

*txDataBuffer = (u16) tempData;

lowByte = FALSE;

}

else {

*txDataBuffer |= (((u16) tempData) << 8);

txDataBuffer++;

lowByte = TRUE;

}

SetEPTxCount(ENDP1, ++count_in);

}

/* Clear the USART1 Receive interrupt */

USART_ClearITPendingBit(USART1, USART_IT_RXNE);

}

}

From usb_endp.c:

u16 count_in = 0;

u32 *txDataBuffer = (u32 *) (ENDP1_TXADDR * 2 + PMAAddr);

/*******************************************************************************

* Function Name : EP1_IN_Callback

* Description :

* Input : None.

* Output : None.

* Return : None.

*******************************************************************************/

void EP1_IN_Callback(void) {

count_in = 0;

txDataBuffer = (u32 *) (ENDP1_TXADDR * 2 + PMAAddr);

}

Now as I understand, the PMA address is 32 bits where as the location is 16 bits. (256 x 16 bits = 512k PMA)

Since the USART IRQ is invoked everytime a byte of data is received, I changed the code around a bit so that the data is added onto the PMA. When a return is sent, the Tx Endpoint is validated to indicate that the USB is ready to send out data. When the data is read out by the USB, the Tx Endpoint is invalidated automatically.

Just for the sake of trying to understand this and getting it to work, I am only transmitting data from the USART to the USB making sure that EP3 is not in action yet.

Test Tx data (Hex):

300D

310D

320D

330D

340D

350D

360D

370D

380D

390D

Received data (ASCII):

0

1

1

3

3

5

5

7

7

9

Any idea where I might be messing things up? Honestly Im not sure how to address the PMA and place data in each 16bit segment...

-Mad

lanchon
Associate III
Posted on May 17, 2011 at 12:29

hi,

-first, with that code you must be using the same preemption priority on both interrupts to make them mutually exclusive or races will ensue. (else make the variables volatile and synchronize the critical sections.)

-why are you calling SetEPTxCount multiple times? call it once before SetEPTxValid.

-the repeat is because you forgot to set lowByte to TRUE in EP1_IN_Callback. remove the variable altogether and change if(lowByte) to if(!(count_in & 1)).

-bug: type in a line longer than the PMA buffer and you're out.

-design bug: what happens if a USART byte is received after SetEPTxValid is called but before the EP1_IN_Callback event?

the last item means the whole idea is flawed: the USB is required to complete a full buffer send in between any two USART chars. don't work further on this idea, abandon it.

say X is the USB transfer size. set a *circular* buffer in ram bigger than X (ideally 2X or more) and flush as big a part of it as possible to USB if...

((at least X bytes are in the buffer OR a newline is in the buffer) AND there is no pending EP1_IN_Callback event)

Check the condition after putting a char in the buffer AND in the EP1_IN_Callback event. You'll have to handle buffer overruns in the USART receive handler somehow. Ideally you'll throttle the source with some kind of flow control. Waiting for USB is not an option: it wouldn't help, and besides it would deadlock since both code segments are mutexed.

philipp2399
Associate II
Posted on May 17, 2011 at 12:29

Hi mvi, Hi lanchon,

I've another comment on the ''Virtual_COM_Port'' example from the USB library ''STM32F10xUSBLib_V1.0'' regarding the Promlem ''...send more than a single character at a time. This results in garbage printed on the pc.''

I started my development based on this example and found something strange in the file ''hw_config.c'', function ''void USART_To_USB_Send_Data(void)''. This function is called by the ''USART1_IRQHandler''.

Everytime a byte is received from USART the function ''USART_To_USB_Send_Data'' sends the whole buffer content to USB by the use of ''UserToPMABufferCopy(buffer_in, ENDP1_TXADDR, count_in)...''.

--> This example works correctly only when the incoming data from USART is slow and the USB can clear its transmit buffer (count_in == 0) before the next byte comes in from USART1_IRQHandler.

To solve the Problem, I would implement ''buffer_in[count_in]'' as a ring buffer.

Philipp