我正在用 C 為設備撰寫韌體。該軟體允許 PC 通過串行介面 (UART) 與該設備進行通信。韌體包含以下多層:
- 通過 UART 發送和接收資料的通信層。
- 塊層:該層通過通過 UART 向設備寫入資料來啟用/禁用設備上的某些塊。
- API 層:這包含對塊層中的例程的呼叫序列。它用于啟用或禁用設備上的一組塊。
我的問題是錯誤處理,因為在 C 中沒有例外。這是我如何實作我的韌體,我試圖看看是否有更有效和緊湊的方式來構建它,同時仍然有效地處理錯誤。我想避免在每一層檢查下層呼叫的狀態。下面的代碼非常緊湊,實際上,我在塊層中有很長的 send_uart_commands 序列。
// Communication layer
operation_status_t send_uart_command(command_id_t id, command_value_t value)
{
// Send data over UART
// Return success if the operation is successful; otherwise failure
}
// Block layer
operation_status_t enable_block1(void)
{
if (send_uart_command(BLOCK1_COMMAND_1, 10) != operation_success)
return operation_failure;
if (send_uart_command(BLOCK1_COMMAND_2, 20) != operation_success)
return operation_failure;
// A list of sequences
if (send_uart_command(BLOCK1_COMMAND_N, 15) != operation_success)
return operation_failure;
return operation_success;
}
operation_status_t enable_block2(void)
{
if (send_uart_command(BLOCK2_COMMAND_1, 1) != operation_success)
return operation_failure;
if (send_uart_command(BLOCK2_COMMAND_2, 8) != operation_success)
return operation_failure;
return operation_success;
}
// API layer
operation_status_t initialize(void)
{
if (enable_block1() != operation_success)
return operation_failure;
if (enable_block2() != operation_success)
return operation_failure;
// A list of calls to the functions in the block layer
return operation_success;
}
uj5u.com熱心網友回復:
C 中例外處理的許多大問題之一是例外可能像炮彈一樣在所有層中崩潰。因此,當您撰寫一些完全不相關的代碼時,您會突然收到炮彈:“UART 幀錯誤!” 當你甚至沒有接觸 UART 代碼時......
因此,“我想避免在每一層檢查下層呼叫的狀態”是錯誤的前提。相反,你應該這樣做:
- 檢查每一層的錯誤。
- 盡可能靠近錯誤源處理錯誤。
- 只有在錯誤對呼叫者真正有意義的情況下才將錯誤轉發給呼叫者。
- 在此程序中,您可以重命名/更改錯誤型別以適合呼叫者。
例如:“UART 幀錯誤”可能對呼叫 UART 驅動程式的代碼有用,但對更高層應用程式無用。“可能不正確的波特率設定”可能是您應該傳遞的更相關的錯誤描述。盡管在某些情況下,即使在更高層,您也希望獲得詳細的錯誤資訊。
您可能想要這樣做的一個原因是,在頂層有一個集中的錯誤處理程式是常見且通常是好的設計,它可以從代碼中的一個地方做出狀態更改、列印/記錄錯誤等的決定。而不是從各地這樣做。您經常會發現微控制器應用程式的頂層看起來像這樣:
void main (void)
{
/* init & setup code called */
for(;;)
{
kick_watchdog(); // the only place in the program where you do this
result = state_machine[state]();
if(result != OK)
{
state = error_handler(result);
}
}
}
至于您的特定代碼,它看起來很好,并且與我上面寫的任何內容都沒有矛盾。出錯時回傳錯誤代碼總是好的 - 比 更容易混淆goto,甚至更糟:大量嵌套的陳述句和/或帶有錯誤條件標志的回圈。
uj5u.com熱心網友回復:
你的代碼沒問題。實際上,在 C 中避免顯式錯誤檢查是一種不好的做法。但是如果你真的想要它,你可以使用longjmp. 但是你應該非常小心地使用它。
此功能允許跳過堆疊跳過任意數量的嵌套呼叫。
您可以在下面找到一個帶有模擬的示例send_uart_command()。
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
void send_uart_command_impl(const char *cmd, int val) {
static int left = 3;
if (left-- == 0) {
printf("%s(%d): failed\n", cmd, val);
longjmp(env, 1);
}
printf("%s(%d): success\n", cmd, val);
}
#define send_uart_command(name, val) send_uart_command_impl(#name, val)
void enable_block1(void) {
send_uart_command(BLOCK1_COMMAND_1, 10);
send_uart_command(BLOCK1_COMMAND_2, 20);
send_uart_command(BLOCK1_COMMAND_N, 15);
}
void enable_block2(void) {
send_uart_command(BLOCK2_COMMAND_1, 1);
send_uart_command(BLOCK2_COMMAND_2, 8);
}
int initialize(void) {
if (setjmp(env)) return -1;
enable_block1();
enable_block2();
return 0;
}
int main() {
if (initialize() != 0)
puts("initialize failed");
else
puts("initialize success");
}
該程式構造為在第 4 次呼叫 時失敗send_uart_command()。調整變數left以選擇其他呼叫。
程式邏輯非常精簡,它列印了預期的輸出:
BLOCK1_COMMAND_1(10): success
BLOCK1_COMMAND_2(20): success
BLOCK1_COMMAND_N(15): success
BLOCK2_COMMAND_1(1): failed
initialize failed
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/324109.html
