0

i've used an Atmega328P µC to get a string over UART and convert it to an int.

I've tried to use atoi() function or sscanf() to convert it but they are taking to long to convert so that they are blocking the interrupt.

If I stop converting and just receiving over UART all symbols are transmitted but if I convert after receiving some characters of transmission are missed.

Is there any way to speed up conversion to stop blocking RX interrupt?

LPs
  • 16,045
  • 8
  • 30
  • 61
  • This string, it is complete yes? Including its null-terminator? Fully transferred by whatever protocol you use, before attempting any atoi() or strxxx() calls? – Martin James Apr 27 '16 at 10:21
  • Also, what baud rate are you using? For such calls to impede interrupt-handling, the rate must be higher than [extremely high]. – Martin James Apr 27 '16 at 10:28
  • .... especially with a 16 byte hardware FIFO. – Martin James Apr 27 '16 at 10:30
  • 2
    You're not performing the conversion in the interrupt service routine by any chance ? The ISR should simply save the data somewhere and the main program flow should perform the conversion. – Unimportant Apr 27 '16 at 10:33
  • 1
    Meh - it's a trivial function, even when performed in an interrupt-handler. It would probably be even better run char-by-char as the data comes in, multipying an accumulator by 10 and adding the 'char-0x30' until a null comes in, (or CR, or LF, or whatever/however the protocol works). – Martin James Apr 27 '16 at 10:50
  • 1
    Anyway, I'm VTC as 'unclear' unless OP provides more details of the code and protocol. – Martin James Apr 27 '16 at 10:52

2 Answers2

0

If you need to convert multicharacter numbers to integer you need to use buffer. There example of cycle buffer uses:

#define BUF_LEN    128
#define BUF_MSK    (BUF_LEN-1)

uint8_t buf[BUF_LEN], b_in = 0, b_out = 0;

void usart_irq_handler(void)
{
    buf[b_in&BUF_MSK] = USART_DATA_REG;
    b_in++;
}

int main(void)
{
    uint8_t tmp;
    while (b_in!=b_out) {
        tmp = buf[b_out&BUF_MSK];
        b_out++;
        // parse here
    }
}

If you need to convert single character numbers you may not use buffer (but if USART frequency not much less than CPU frequency it's not recommended). For ASCII encoded received characters each number will have values 0x30..0x39. If received characters encoded with another charset you need refer to their tables.

uint8_t num  = USART_DATA_REG - 0x30;
if (num >= 0 && num <= 9) {
    // is number
}

EDIT 1 [due to new information from OP] For convert decimal number from string to integer I use this function:

uint32_t parse_num(uint8_t * ptr) 
{
    uint32_t res = (uint32_t)0x0;
    uint8_t chr = 0x0, pchr = 0xA;
    while (*ptr != (uint8_t)0x0) {
        chr = (uint8_t)(*ptr - (uint8_t)0x30);
        if (chr < 0xA) {
            res *= (uint32_t) 0xA;
            res += (uint32_t) chr;
        } else {
            if (pchr < 0xA) break;
        }
        pchr = chr;
        ptr++;
    }
    return res;
}

It skip non-numbers chars and convert first founded number, but doesn't return final position of parse buffer. You can modify it as you need.

EDIT 2 [due to chat with OP] about good approach to processor time management:

Pictures below (from this answer) illustrate normal processor timing in program:

programm flows priorities

programm time

So, the smaller the interrupt handler time - the more likely success of the other handlers, and more time to perform main process.

By increasing of MCU clock is reduced run time code and increases idle time.

By decreasing of periferal clock is reduced frequency of interrupts by perifery and increases idle time.

In conclusion idle time must be used for power save.

Community
  • 1
  • 1
imbearr
  • 999
  • 7
  • 22
  • I've implemented your `parse_num` function at it works great. But now I found my problem: If I stop every interrupt from working and use polling for UART it works great even at a speed of 250kBaud. But if I activate the interrupt Compare Match A (I need it for updating values at a TLC5940 PWM timer) and it is called every 4095 clock cycles it stops my UART receiving from working. Is there something I can do to stop blocking my UART receiving? In the interrupt a pin is toggled and a SPI-Transmission is fulfilled. –  May 03 '16 at 13:59
  • @jannik1810, are you shure that only UART stops? Usually same behaviour when new code content some errors that stop whole MCU. – imbearr May 04 '16 at 08:14
  • I've looked into my Compare Match A interrupt and found out that the SPI transmission is the process which takes a long time. I've changed it to send a header via UART and that it stops the SPI transmission until UART is finished. So this works but some values are also missed in the transmission. And i'm sure that the MCU keeps working. –  May 04 '16 at 20:16
  • @jannik1810, it seems you have exhausted processor's time. If it's right guess: try to optimize code(decrease irq handlers process time), increase MCU clock, decrease SPI/USART clock - each of these methods increase processor time. – imbearr May 08 '16 at 19:03
  • i've optimized the irq handler (now it only toggles a pin for the pwm Driver) and then the rest is handled by the main Loop of the program. I'm checking for Special characters to symbolize the end of Transmission to start spi Transmission only if no more uart smybols are awaited. I hope it will work I will try it today. –  May 10 '16 at 12:49
0

Yes, the function runs in main loop and copies the chars out of buffer which is filled in the interrupt routine. After detecting the \n in string, it converts the string into values using sscanf or char by char. I've first used a very slow baud rate and then a very fast one. There are also two more interrupt routines (for timing of 100us and one for a pwm driver). I've tried to take out code of these routines to speed them up and it was successful. Now the interrupt routine gets every character of the uart transmission. Now I am implementing an algorithm to convert the strings to values without using sscanf because of performance issues. Using the algorithm of imbearr with -0x30 should work properly.