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 算法应该可以正常工作。
我使用 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 算法应该可以正常工作。