【问题标题】:C Atmega328P String to Int Conversion blocks RX InterruptC Atmega328P 字符串到整数转换块 RX 中断
【发布时间】:2016-04-27 10:16:41
【问题描述】:

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

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

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

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

【问题讨论】:

  • 这个字符串,是完整的吗?包括它的空终止符?在尝试任何 atoi() 或 strxxx() 调用之前,通过您使用的任何协议完全传输?
  • 另外,您使用的波特率是多少?对于阻碍中断处理的此类调用,速率必须高于 [extremely high]。
  • 您没有在中断服务程序中执行转换吗? ISR 应该简单地将数据保存在某处,主程序流应该执行转换。
  • 嗯 - 这是一个微不足道的功能,即使在中断处理程序中执行时也是如此。当数据进入时,可能会更好地逐个字符地运行,将累加器乘以 10 并添加“char-0x30”直到出现空值,(或 CR,或 LF,或任何/无论如何协议作品)。
  • 无论如何,除非 OP 提供代码和协议的更多详细信息,否则我是 VTC 的“不清楚”。

标签: c string type-conversion microcontroller interrupt


【解决方案1】:

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

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

如果您需要转换单个字符数,您可以不使用缓冲区(但如果 USART 频率不低于 CPU 频率,则不建议使用)。对于 ASCII 编码的接收字符,每个数字的值都是 0x30..0x39。如果收到用另一个字符集编码的字符,您需要参考他们的表格。

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

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

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

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

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

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

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

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

总之必须使用空闲时间来省电。

【讨论】:

  • 我已经实现了您的parse_num 函数,它运行良好。但是现在我发现了我的问题:如果我停止每个中断工作并使用 UART 轮询,即使在 250kBaud 的速度下它也能很好地工作。但是,如果我激活中断比较匹配 A(我需要它来更新 TLC5940 PWM 定时器的值)并且每 4095 个时钟周期调用一次,它会阻止我的 UART 接收工作。我可以做些什么来停止阻止我的 UART 接收吗?在中断中切换引脚并完成 SPI 传输。
  • @jannik1810,你确定只有 UART 停止吗?当新代码包含一些停止整个 MCU 的错误时,通常行为相同。
  • 我查看了我的比较匹配 A 中断,发现 SPI 传输是一个需要很长时间的过程。我已将其更改为通过 UART 发送标头,并且它会停止 SPI 传输,直到 UART 完成。所以这可行,但传输中也遗漏了一些值。而且我确信 MCU 会继续工作。
  • @jannik1810,看来您已经耗尽了处理器的时间。如果猜对了:尝试优化代码(减少 irq 处理程序处理时间),增加 MCU 时钟,减少 SPI/USART 时钟 - 这些方法中的每一个都会增加处理器时间。
  • 我已经优化了 irq 处理程序(现在它只为 pwm 驱动程序切换一个引脚),然后其余的由程序的主循环处理。我正在检查特殊字符以表示传输结束以启动 spi 传输,仅当不再等待 uart smybols 时。我希望它会起作用,我今天会尝试一下。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-06
    • 1970-01-01
    • 1970-01-01
    • 2015-07-08
    • 1970-01-01
    • 2018-10-01
    • 1970-01-01
    • 2011-04-21
    相关资源
    最近更新 更多