2
\$\begingroup\$

I am using the STM32 Nucleo F401-RE and HAL libraries. I would like to send value through UART to the PC console every 1µs interruption. Actually I want every 1 µs interruption to increment a counter so it will act like a timestamp,then send this value every 1µs,so when there is interruption every 1 µs -> Incrementation of a counter (++count) and send the value of the counter.

My code that doesn't work like I want it to be :

My Clock : 84Mhz

I Used MicroElektronika timer calculator to calculate for 1µs interruption the period and Prescaler. Timer configuration :

__TIM4_CLK_ENABLE()
;
TIM_Handle.Init.Prescaler = 0;
TIM_Handle.Init.ClockDivision = 0;
TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
TIM_Handle.Init.Period = 83;
TIM_Handle.Init.RepetitionCounter = 0;
TIM_Handle.Instance = TIM4;   //Same timer whose clocks we enabled
HAL_TIM_Base_Init(&TIM_Handle);     // Init timer
HAL_TIM_Base_Start_IT(&TIM_Handle); // start timer interrupts
HAL_NVIC_SetPriority(TIM4_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM4_IRQn);

And the TIM4 IRQ handler :

uint32_t count=0;

void TIM4_IRQHandler(void) {
if (__HAL_TIM_GET_FLAG(&TIM_Handle, TIM_FLAG_UPDATE) != RESET) 
        {
    if (__HAL_TIM_GET_ITSTATUS(&TIM_Handle, TIM_IT_UPDATE) != RESET) {

        __HAL_TIM_CLEAR_FLAG(&TIM_Handle, TIM_FLAG_UPDATE);
        ++count;
        printf("%ld\r\n,count");

    }
}

}

\$\endgroup\$
  • 5
    \$\begingroup\$ UARTs don't generally work that fast. In order to send a 32-bit quantity every 1 us, your UART would have to be running at a minimum rate of 40 Mbit/sec! \$\endgroup\$ – Dave Tweed Apr 25 '16 at 12:25
  • \$\begingroup\$ So what is the best rate ? can I at least every 10µs? \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 12:41
  • \$\begingroup\$ The best rate is partly constrained by the data receiver. I guess that 4Mbits might be sustainable, but that is just a guess. At that rate, a 5 byte, fixed length, protocol would take 10µs to complete the transfer. So 10µs sounds like a plausible target. I've added some mre detail in my answer. \$\endgroup\$ – gbulmer Apr 25 '16 at 13:01
  • \$\begingroup\$ I need for my application to be at least 10µs resolution for my clock,that is why i need a timer with interrutpino 10µs. \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 13:26
  • 1
    \$\begingroup\$ Please explain the problem you are trying to solve, including the receiver, and we may be able to help. Currently it seems to be far from feasible with the hardware you are using. Start a new question for that. You can include a link back to this question in your new question. \$\endgroup\$ – gbulmer Apr 25 '16 at 13:50
2
\$\begingroup\$

The line printf("%ld\r\n,count"); will always send "\r\n,count", which is 8 bytes, and likely not what you want.

You probably meant to write :

printf("%ld\r\n",count);

However, as Dave Tweed explained, it is unlikely the UART will run fast enough.

After the first millisecond, the count will be 4 digits, so that printf will try to print at least 6 bytes, which would require 60Mbit/second.

Worse, TIM4_IRQHandler(void) will be blocked waiting for the printf to complete, so all lower priority interrupts will be blocked. The Nucleo F401-RE will be blocked inside the IRQ handler.

Turbo J points out, even if the UART could handle the data rate, the code of the printf will consume more instruction cycles than available before the next interrupt happens.

BTW - What is receiving the characters from the UART? What data rate is it capable of receiving? A lot of ordinary USB to UART converters will struggle to get much above 1MByte/second.

Either a fixed length format, or a format which only uses one bit to signal end of 'packet' would be more efficient. A god choice also depends on the receivers capabilities. Write directly to the UART buffer, avoid using printf. It won't be doing anything useful to a binary protocol (unless it internally used DMA).

Possible approaches: 4 byte fixed 'packet'.

Edit3:
Assuming a sendByte(byte) function is available to stuff bytes directly int the UART buffer:

sendbyte((count>>24)&0xff);
sendbyte((count>>16)&0xff);
sendbyte((count>>8)&0xff);
sendbyte((count)&0xff);

Would send the 4 bytes of the count. However the receiver might lose synchronisation. This wouldn't be a big problem for the first few seconds as the top byte would always be zero. Further, the receiver could always see which byte is changing most quickly. However, if losing bytes is a problem, this might not be enough.

To help avoid losing synchronisation, reserve the top bit of each byte to mark the start or end of the packet. So using a maximum 28bit count, and setting the top bit of the first byte of the 4 byte packet to a '1' and the top bit of the following three bytes to '0'.

Use printable characters, for simplicity use 6 bits, with the top bit of the first byte set (so it is above ASCII) but the rest using ASCII. This would lose another bit from each byte, so you might want to transmit 30 bits in a 5 byte packet. These messages would be printable, which might help you debug it.

If you kept the data rate to a rate the receiver could handle (maybe 4Mbit/s or less?), and sent slowly enough to give a reasonable amount of processing time to the rest of the application, it should be okay.

That would suggest a byte every 2µseconds, and for the 5 byte protocol, a timestamp every 10µs.

You could use DMA to send the bytes. The timer IRQ would set up the bytes for transfer, initialise the DMA, and it would all happen in the background. However, this is much more complex to set up, so get the transfer working before attempting to use DMA.

Edit:
At a baudrate of 115200, that is only 14,400 bytes/second.
A count of 1,000,000,000 will take 30 bits, or at least 4 bytes.
So the maximum number of counts that could be transferred is 3,600/second, i.e. a timestamp each 277µs.

I strongly recommend you explain the actual problem you are trying to solve, maybe in a new question, as it currently seems orders of magnitude away from feasible.

Edit2: I've failed to find them, but I believe FTDI published some benchmark numbers for their USB-to-UART using 12Mbits USB transfers a few years ago. I believe, under benchmark conditions (which they designed) they got just under 700kBytes/second (abut 670kB/s, I think). Without more detail, I would regard this as unachievable in general situations, and an absolute maximum.

There is a somewhat interesting set of USB 'read' benchmarks by PJRC comparing various USB-equiped microcontrollers. The results for Maple (an STM32F103) was 234,114 Bytes/second directly over 12Mbit/s USB. PJRC's Teensy 3.0 managed 683,689 under the same constraints. Paul Stoffregen seems to be very talented, so I would use that as very good throughput for read. It also is close enough to my recollection of the FTDI benchmark (I thought 670k) that I would reduce that by 20%, say 500kB/s, and use that as my maximum.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ s/mostly/always The printf() alone is longer than the availabe clock cycles left. \$\endgroup\$ – Turbo J Apr 25 '16 at 12:38
  • \$\begingroup\$ Yeah i meant to write that , so So what is the best rate ? can I at least every 10µs send? or maybe in a binary format ,it will be faster? or Uart_transmit_IT? \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 12:41
  • \$\begingroup\$ The Baudrate is 115200.As for what you suggested, How can I achieve it ? It is a bif confusing for me . actually the length of my value could be like 1000000000 µs because it will act like a clock for timestamping. \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 13:03
  • \$\begingroup\$ Yeah so a Timestamp each 277µs,how to achieve it for each 10µs? can you show me actual code? it is easier for me to understand :) \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 13:19
  • \$\begingroup\$ It is not feasible to send a timestamp, of any form, faster than the baudrate/8. So if the baudrate is 115200, the best possible is 14,400 bytes/second. You'll need a much higher baud rate to send a timestamp every 10us. \$\endgroup\$ – gbulmer Apr 25 '16 at 13:50
1
\$\begingroup\$

This is in my humble opinion an unrealistic application.

First of all; printf is slow. Like really slow. Your CPU runs 84MHz and you want to interrupt it at 1MHz. That is already close to the maximum interrupt rate just from context-switching overhead.

Additionally if you could devote all 84 CPU cycles to printing, purely printf() is too slow. If the counter value creeps up to 8, 9 or 10 digits, the CPU only has ~10 cycles to process 1 character. I don't think it's that efficient, or magic-like.

So that suggests a binary format. In that case, you can print the raw data bytes. But even then 1MHz output rate is very high. If you were to just print 4-byte raw data (no header, no sync, no CRC - nothing extra!) at 1MHz, you have a data rate of 4MByte/s or 32Mbit/s. The UART can only go up to 10.5Mbit/s, SPI up to 42Mbit/s.

At 10us interval, the UART should be capable of reaching that speed; but it will require 3.2Mbit/s or a Baud rate quite a bit above that (like 4 or 6Mbaud). What receiver are you going to connect (& process) to that? E.g. most USB serial cables will reach 1-3MBaud max (while taking into account that the serial converters can't operate on any arbitrary baud rate at this level).

Additionally you may want to consider that most Uart tx functions are blocking, so the CPU will spend almost all of it's cycles waiting for hardware to complete, or load bytes into the peripheral. That means you may want to look at DMA, and figure out some way how you can packet-ize data properly in case the receiving end loses sync.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I wanted to try to use Hal_Uart_Transmit but it doesn't work for me and the official example of ST is redirecting it to Printf. my baudrate is 115200 ,as for binary values ,how to achieve it ? \$\endgroup\$ – Youssef Kamoun Apr 25 '16 at 13:06
0
\$\begingroup\$

You have not contradicted anyone who suggested that you are using a USB-serial interface, and in fact that is what is present on the Nulceo board you mention. A typical USB-serial converter is going to have sampling jitter on the order of a millisecond in when it reports received data to the host, so completely independent of the insufficient serial baud rate this whole approach is unable to yield the results you desire.

If you need tighter timing than that, you will probably need a hardware strobe more directly interfaced to the host CPU (not easy in a modern PC, more reasonable in some ARM-based embedded board), and then use preceding or following serial messages to indicate the timestamp that is being strobed. It will probably be easiest if you make the strobe interval much less frequent so that you have time to report the count in between, and run a local clock across the gaps. But even doing that won't get you better than millisecond accuracy until you stop depending on full-speed USB.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.