C Atmega328P 字符串到 Int 转换块 RX 中断

C Atmega328P String to Int Conversion blocks RX Interrupt

我使用 Atmega328P µC 通过 UART 获取字符串并将其转换为 int

我曾尝试使用 atoi() 函数或 sscanf() 来转换它,但它们需要很长时间才能转换,因此它们会阻止中断。

如果我停止转换并仅通过 UART 接收所有符号,但如果我在接收后转换,则会丢失一些传输字符。

有什么方法可以加速转换以停止阻塞 RX 中断吗?

如果您需要将多字符数字转换为整数,您需要使用缓冲区。循环缓冲区使用示例:

#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
    }
}

如果需要转换单个字符数字可以不使用buffer(但如果USART频率不小于CPU频率则不推荐)。对于 ASCII 编码的接收字符,每个数字的值为 0x30..0x39。如果收到使用其他字符集编码的字符,您需要参考他们的表格。

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

编辑 1 [由于来自 OP 的新信息] 用于将十进制数从字符串转换为整数 我用这个函数:

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

它跳过非数字字符并转换第一个创建的数字,但不 return 解析缓冲区的最终位置。您可以根据需要进行修改。

编辑 2[由于与 OP 聊天]关于处理器时间管理的好方法:

下面的图片(来自这个answer)说明了程序中的正常处理器时序:

因此,中断处理程序时间越短 - 其他处理程序成功的可能性越大,执行主进程的时间就越多。

通过增加MCU时钟减少运行时间码并增加空闲时间

通过减少外围时钟减少了外围中断的频率并增加了空闲时间。

总结闲置时间一定要用来省电

是的,该函数在主循环中运行,并从中断例程中填充的缓冲区中复制字符。在检测到字符串中的 \n 后,它使用 sscanf 或 char by char 将字符串转换为值。我首先使用了一个非常慢的波特率,然后是一个非常快的波特率。还有两个中断例程(用于 100us 的时序和一个用于 pwm 驱动程序)。我试图将这些例程的代码取出来加速它们并且它成功了。现在中断例程获取 uart 传输的每个字符。现在,由于性能问题,我正在实施一种算法,无需使用 sscanf 即可将字符串转换为值。使用带 -0x30 的 imbearr 算法应该可以正常工作。