我正在用 C 语言为微控制器编程,所以我不确定这个问题是否属于这里或电子堆栈交换。如果这里不适合,请告诉我,我会将问题移到那里。
所以我有 C 语言的有限状态机。这个 FSM 负责通过 UART 检索数据。每个状态都会检索一些数据并对其执行一些检查。每个州都有三个可能的方向。如果接收到的数据有效,则进入下一个状态。如果没有,它将重复其状态。如果该状态重复一定次数,它将转为失败状态。为了指示这个方向,状态返回一个代码:
ok
、err
或repeat
。这适用于除最后一个州之外的所有州。在这里,当调用 return repeat
时,我可以看到调试器转到该语句,然后遍历函数的最后一个 }
,然后它转到内存中的随机位置,我只能在代码的反汇编视图中看到该位置作曲家工作室。有时这是 0x0 有时它是 RAM 中的随机内存,我看不到。
什么会导致这种奇怪的行为?我在 Code Composer Studio 中关闭了优化选项。
所有州都有这样的机构:
enum ret_codes fc_state(void) {
uint8_t buf[250];
static uint8_t fail_counter = 0;
const uint8_t max_tries = 5;
int ret = 0;
while (ret == 0) {
ret = UART_read(_uart, buf, sizeof(buf));
}
if (ret == UART_STATUS_ERROR) {
return err;
}
/*
perform tests on buf ...
*/
if (testFailed) {
fail_counter++;
if (fail_counter == max_tries) {
// Write DEN over UART ...
return err;
}
// write NACK over UART ...
return repeat; // I can see the debugger reaching this statement in the fc_state
}
// write ACK over UART ...
return ok;
} // When debugging the debugger goes here after 'return repeat' and to a random location in memory after this
// In the other states it goes back to the main loop described further below after 'return repeat'
FSM 的代码如下所示:
enum state_codes { ipv, fup, fc, fail, succ, invalid };
enum ret_codes { ok, err, repeat };
struct transition {
enum state_codes src_state; // source state
enum ret_codes ret_code; // the code it returns
enum state_codes dst_state; // the state it should transfer to if the previous mentioned code was returned
};
enum ret_codes ipv_state(void);
enum ret_codes fup_state(void);
enum ret_codes fc_state(void);
enum ret_codes fail_state(void);
enum ret_codes succ_state(void);
enum ret_codes (* states[])(void) = {ipv_state, fup_state, fc_state, fail_state, succ_state};
struct transition transition_table[] = {
{ipv, ok, fup}, // when in the ipv state and ok is returned then go to fup state
{ipv, repeat, ipv}, // This repeat works
{ipv, err, fail},
{fup, ok, fc},
{fup, repeat, fup}, // This repeats works as well
{fup, err, fail},
{fc, ok, succ},
{fc, repeat, fc}, // This repeat fails for some reason
{fc, err, fail}
};
enum state_codes lookup_transitions(enum state_codes, enum ret_codes);
这些状态的主循环如下所示:
while (1) {
state_fun = states[current_state];
return_code = state_fun();
if (current_state == fail) {
return 1;
}
if (current_state == succ) {
return 0;
}
current_state = lookup_transitions(current_state, return_code);
if (current_state == invalid) {
return 1;
}
}
编辑:我在下面添加了lookup_transitions表。顺便说一句,FSM 的实现取自 https://stackoverflow.com/a/1371654/5600470。该代码是为 Texas Instruments 的带有 Arm Cortex-M4F CPU 的 CC2652R 编写的。
enum state_codes lookup_transitions(enum state_codes cur_state, enum ret_codes rc) {
uint8_t i;
for (i = 0; i < sizeof(transition_table); i++) {
if (cur_state == transition_table[i].src_state && rc == transition_table[i].ret_code)
return transition_table[i].dst_state;
}
return invalid; // the transition table is not configured correctly
}
这是 Code Composer Studio 反汇编查看器针对 fc_state 显示的内容:
505 enum ret_codes fc_state(void) {
fc_state():
000529a0: B500 push {r14}
000529a2: F1AD0D3C sub.w r13, r13, #0x3c
512 const uint8_t max_tries = 5;
000529a6: 2005 movs r0, #5
000529a8: F88D003B strb.w r0, [r13, #0x3b]
514 int ret = 0;
000529ac: 2000 movs r0, #0
000529ae: 9000 str r0, [r13]
515 while (ret == 0) {
000529b0: 9800 ldr r0, [r13]
000529b2: B948 cbnz r0, #0x529c8
516 ret = UART_read(_uart, &rheader, sizeof(rheader));
$C$L62:
000529b4: 4829 ldr r0, [pc, #0xa4]
000529b6: 6800 ldr r0, [r0]
000529b8: A901 add r1, r13, #4
000529ba: 222B movs r2, #0x2b
000529bc: F003FFC6 bl UART_read
000529c0: 9000 str r0, [r13]
515 while (ret == 0) {
000529c2: 9800 ldr r0, [r13]
000529c4: 2800 cmp r0, #0
000529c6: D0F5 beq $C$L62
518 if (ret == UART_STATUS_ERROR) {
$C$L63:
000529c8: 9800 ldr r0, [r13]
000529ca: F1B03FFF cmp.w r0, #-1
000529ce: D101 bne $C$L64
519 return err;
000529d0: 2001 movs r0, #1
000529d2: E040 b $C$L70
522 ret = 0;
$C$L64:
000529d4: 2000 movs r0, #0
000529d6: 9000 str r0, [r13]
523 while (ret == 0) {
000529d8: 9800 ldr r0, [r13]
000529da: B958 cbnz r0, #0x529f4
524 ret = UART_read(_uart, &last_fup_info, rheader.length);
$C$L65:
000529dc: 481F ldr r0, [pc, #0x7c]
000529de: F8BD202D ldrh.w r2, [r13, #0x2d]
000529e2: 6800 ldr r0, [r0]
000529e4: F10D012F add.w r1, r13, #0x2f
000529e8: F003FFB0 bl UART_read
000529ec: 9000 str r0, [r13]
523 while (ret == 0) {
000529ee: 9800 ldr r0, [r13]
000529f0: 2800 cmp r0, #0
000529f2: D0F3 beq $C$L65
526 if (ret == UART_STATUS_ERROR) {
$C$L66:
000529f4: 9800 ldr r0, [r13]
000529f6: F1B03FFF cmp.w r0, #-1
000529fa: D101 bne $C$L67
527 return err;
000529fc: 2001 movs r0, #1
000529fe: E02A b $C$L70
530 if (
$C$L67:
00052a00: 4817 ldr r0, [pc, #0x5c]
00052a02: F8DD1037 ldr.w r1, [r13, #0x37]
00052a06: 6800 ldr r0, [r0]
00052a08: 4288 cmp r0, r1
00052a0a: D115 bne $C$L68
00052a0c: 4815 ldr r0, [pc, #0x54]
00052a0e: F8DD1037 ldr.w r1, [r13, #0x37]
00052a12: 6800 ldr r0, [r0]
00052a14: 4288 cmp r0, r1
00052a16: D10F bne $C$L68
00052a18: 4813 ldr r0, [pc, #0x4c]
00052a1a: F8DD1033 ldr.w r1, [r13, #0x33]
00052a1e: 6800 ldr r0, [r0]
00052a20: 4288 cmp r0, r1
00052a22: D109 bne $C$L68
00052a24: 4811 ldr r0, [pc, #0x44]
00052a26: F8DD1033 ldr.w r1, [r13, #0x33]
00052a2a: 6800 ldr r0, [r0]
00052a2c: 4288 cmp r0, r1
00052a2e: D103 bne $C$L68
535 UPDATEMNGR_writeAck();
00052a30: F001F834 bl UPDATEMNGR_writeAck
536 return ok;
00052a34: 2000 movs r0, #0
00052a36: E00E b $C$L70
539 fail_counter++;
$C$L68:
00052a38: 490D ldr r1, [pc, #0x34]
00052a3a: 7808 ldrb r0, [r1]
00052a3c: 1C40 adds r0, r0, #1
00052a3e: 7008 strb r0, [r1]
540 if (fail_counter != max_tries) {
00052a40: 480B ldr r0, [pc, #0x2c]
00052a42: 7800 ldrb r0, [r0]
00052a44: 2805 cmp r0, #5
00052a46: D003 beq $C$L69
541 UPDATEMNGR_writeNAck();
00052a48: F001F8BC bl UPDATEMNGR_writeNAck
542 return repeat;
00052a4c: 2002 movs r0, #2
00052a4e: E002 b $C$L70
544 UPDATEMNGR_writeDen();
$C$L69:
00052a50: F001F86E bl UPDATEMNGR_writeDen
545 return err;
00052a54: 2001 movs r0, #1
546 }
$C$L70:
00052a56: B00F add r13, #0x3c
00052a58: BD00 pop {pc}
00052a5a: 46C0 mov r8, r8
fc 和重复枚举都变成相同的整数,因为它们在枚举列表中处于相同的位置。您可以通过将枚举设置为不重叠的不同起始值来解决此问题。这就是第二次重复失败的原因。
这个问题有点过时了,但是lookup_transitions()函数中有一个错误。循环变量可以迭代超出数组边界,并且函数可以从意外的内存位置返回值。这可能是通过无效指针和未定义行为调用函数的原因。我认为修复应该是这样的:
for (i = 0; i < sizeof(transition_table) / sizeof(struct transition); i++)