diff --git a/Makefile b/Makefile index 9214aa6..1f276e4 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,9 @@ CFLAGS += -Wredundant-decls -Wfloat-equal -Wsign-compare CFLAGS += -fno-common -ffunction-sections -fdata-sections -Wunused-function CFLAGS += -I$(INCL_DIR) +CFLAGS += -DSCPI_FINE_ERRORS +CFLAGS += -DSCPI_WEIRD_ERRORS + ############################################################################### all: lib diff --git a/example/Makefile b/example/Makefile index 9eb068d..cbc1ac5 100644 --- a/example/Makefile +++ b/example/Makefile @@ -11,10 +11,12 @@ CFLAGS += -Wall -Wextra -Wshadow CFLAGS += -Wwrite-strings -Wold-style-definition -Winline CFLAGS += -Wredundant-decls -Wfloat-equal -Wsign-compare -Wunused-function +CFLAGS += -DSCPI_FINE_ERRORS + CC = gcc %.o: %.c - + all: example.elf diff --git a/example/example.c b/example/example.c index 60f8af5..1c74e4f 100644 --- a/example/example.c +++ b/example/example.c @@ -30,10 +30,12 @@ int main(void) send_cmd("SYST:ERR:COUNT?\n"); send_cmd("SYST:ERR:NEXT?\n"); + send_cmd("ERROR_FALLBACK\n"); // test fallback to closest related error + // test chardata send_cmd("CHARD FOOBAR123_MOO_abcdef_HELLO, 12\n"); - - + + send_cmd("SYST:ERR:ALL?\n"); } @@ -56,20 +58,25 @@ void scpi_send_byte_impl(uint8_t b) } -const char *scpi_device_identifier(void) +const char *scpi_user_IDN(void) +{ + return "MightyPork,Test SCPI device,0,0.1"; +} + + +/** Error callback */ +void scpi_user_error(int16_t errno, const char * msg) { - return "FEL CVUT,DDS1,0,0.1"; + printf("### ERROR ADDED: %d, %s ###\n", errno, msg); } -void scpi_service_request_impl(void) +/** Service request impl */ +void scpi_user_SRQ(void) { // NOTE: Actual instrument should send SRQ event somehow - printf("[SRQ] - error: "); - char buf[256]; - scpi_read_error_noremove(buf); // show error message (for debug) - printf("%s\n", buf); + printf("[Service Request]\n"); } @@ -115,6 +122,16 @@ void cmd_CHARD_cb(const SCPI_argval_t *args) } +void cmd_ERROR_FALLBACK_cb(const SCPI_argval_t *args) +{ + (void) args; + + printf("Testing the error fallback feature...\n"); + + scpi_add_error(-427, NULL); +} + + // Command definition (mandatory commands are built-in) const SCPI_command_t scpi_commands[] = { { @@ -138,6 +155,10 @@ const SCPI_command_t scpi_commands[] = { .levels = {"USeRERRor"}, .callback = cmd_USRERR_cb }, + { + .levels = {"ERROR_FALLBACK"}, + .callback = cmd_ERROR_FALLBACK_cb + }, { .levels = {"CHARData"}, .params = {SCPI_DT_CHARDATA, SCPI_DT_INT}, diff --git a/include/scpi_builtins.h b/include/scpi_builtins.h index d61844a..57054ab 100644 --- a/include/scpi_builtins.h +++ b/include/scpi_builtins.h @@ -2,17 +2,17 @@ #include #include -/** *CLS command callback - clear non-SCPI device state */ +/** Optional *CLS command callback - clear non-SCPI device state */ extern __attribute__((weak)) void scpi_user_CLS(void); -/** *RST command callback - reset non-SCPI device state */ +/** Optional *RST command callback - reset non-SCPI device state */ extern __attribute__((weak)) void scpi_user_RST(void); -/** *TST? command callback - perform self test and send response back. */ -extern __attribute__((weak)) void scpi_user_TSTq(void); +/** Optional *TST? command callback - perform self test and send response back. */ +extern __attribute__((weak)) void scpi_user_TST(void); -/** Get device *IDN? string. */ -extern const char *scpi_device_identifier(void); +/** MANDATORY callback to get the device *IDN? string. */ +extern const char *scpi_user_IDN(void); // Provides: // const SCPI_command_t scpi_commands_builtin[]; diff --git a/include/scpi_errors.h b/include/scpi_errors.h index c2914f9..33db046 100644 --- a/include/scpi_errors.h +++ b/include/scpi_errors.h @@ -10,6 +10,12 @@ typedef struct { /** User error definitions */ extern const SCPI_error_desc scpi_user_errors[]; + +/** Callback when error is added to the queue */ +extern __attribute__((weak)) +void scpi_user_error(int16_t errno, const char * msg); + + // SCPI error constants enum { E_NO_ERROR = 0, @@ -137,17 +143,18 @@ enum { }; -/** Get SCPI error message (alone) */ -const char *scpi_error_message(int16_t errno); - /** - * Get SCPI error string for reporting: + * Get SCPI error string: * ,"[; ]" * - * extra can be NULL to skip the optional part. + * @param buffer Buffer for storing the final string. Make sure it's big enough. + * @param errno Error number + * @param extra Extra information, appended after the generic message. Can be NULL. + * + * @returns actual error code. Code may be coerced to closest defined code (categories: tens, hundreds) */ -void scpi_error_string(char *buffer, int16_t errno, const char *extra); +int16_t scpi_error_string(char *buffer, int16_t errno, const char *extra); /** Add error to the error queue */ diff --git a/include/scpi_parser.h b/include/scpi_parser.h index a8af4a6..785691c 100644 --- a/include/scpi_parser.h +++ b/include/scpi_parser.h @@ -61,10 +61,10 @@ typedef struct { // ---------------- USER CONFIG ---------------- -// Zero terminated command struct array - must be defined. +/** Zero terminated command struct array - must be defined. */ extern const SCPI_command_t scpi_commands[]; -// provided by scpi_builtins.h +/** Built-in SCPI commands, provided by scpi_builtins.h */ extern const SCPI_command_t scpi_commands_builtin[]; /** Send a byte to master (may be buffered) */ diff --git a/include/scpi_regs.h b/include/scpi_regs.h index 65657e1..8da779e 100644 --- a/include/scpi_regs.h +++ b/include/scpi_regs.h @@ -99,6 +99,16 @@ extern SCPI_REG_STB_t SCPI_REG_STB; extern SCPI_REG_STB_t SCPI_REG_SRE; // SRE +/** Update the status registers (perform propagation) */ void scpi_status_update(void); -extern __attribute__((weak)) void scpi_service_request_impl(void); + +/** + * Service Request callback. + * User may choose to implement it (eg. send request to master), + * or leave unimplemented. + * + * SRQ is issued when an event enabled in the status registers (namely SRE) occurs. + * See the SCPI spec for details. + */ +extern __attribute__((weak)) void scpi_user_SRQ(void); diff --git a/scpi.pro b/scpi.pro index 754cd68..c975910 100644 --- a/scpi.pro +++ b/scpi.pro @@ -23,7 +23,8 @@ DISTFILES += \ .gitignore \ LICENSE \ README.md \ - Makefile + Makefile \ + example/Makefile HEADERS += \ source/scpi_parser.h \ diff --git a/source/scpi_builtins.c b/source/scpi_builtins.c index 794186f..08b54a1 100644 --- a/source/scpi_builtins.c +++ b/source/scpi_builtins.c @@ -46,8 +46,8 @@ static void builtin_TSTq(const SCPI_argval_t *args) { (void)args; - if (scpi_user_TSTq) { - scpi_user_TSTq(); + if (scpi_user_TST) { + scpi_user_TST(); } } @@ -56,7 +56,7 @@ static void builtin_IDNq(const SCPI_argval_t *args) { (void)args; - scpi_send_string(scpi_device_identifier()); + scpi_send_string(scpi_user_IDN()); } diff --git a/source/scpi_errors.c b/source/scpi_errors.c index c399cbb..c0ab049 100644 --- a/source/scpi_errors.c +++ b/source/scpi_errors.c @@ -13,7 +13,7 @@ // --- queue impl --- -static struct { +static struct ErrorQueueStruct { char queue[ERR_QUEUE_LEN][MAX_ERROR_LEN + 1]; int8_t r_pos; int8_t w_pos; @@ -35,7 +35,13 @@ void scpi_add_error(int16_t errno, const char *extra) } } - scpi_error_string(erq.queue[erq.w_pos], errno, extra); + // get string & coerce errno to valid value + errno = scpi_error_string(erq.queue[erq.w_pos], errno, extra); + + // run optional user error callback + if (scpi_user_error) { + scpi_user_error(errno, erq.queue[erq.w_pos]); + } erq.w_pos++; erq.count++; @@ -106,8 +112,55 @@ uint8_t scpi_error_count(void) // ---- table ---- -static const SCPI_error_desc error_table[] = { +static const SCPI_error_desc no_error_desc = {0, "No error"}; + +static const SCPI_error_desc scpi_std_errors[] = { { -100, "Command error"}, + { -110, "Command header error"}, + { -120, "Numeric data error"}, + { -130, "Suffix error"}, + { -140, "Character data error"}, + { -150, "String data error"}, + { -160, "Block data error"}, + { -170, "Expression error"}, + { -180, "Macro error"}, + { -200, "Execution error"}, + + { -210, "Trigger error"}, + { -220, "Parameter error"}, + { -230, "Data corrupt or stale"}, + { -240, "Hardware error"}, + { -250, "Mass storage error"}, + { -260, "Expression error"}, + { -270, "Macro error"}, + { -280, "Program error"}, + { -290, "Memory use error"}, + + { -300, "Device-specific error"}, + { -310, "System error"}, + { -320, "Storage fault"}, + { -330, "Self-test failed"}, + { -340, "Calibration failed"}, + { -350, "Queue overflow"}, + { -360, "Communication error"}, + + { -400, "Query error"}, + + // Error codes that don't make much sense +#ifdef SCPI_WEIRD_ERRORS + { -410, "Query INTERRUPTED"}, + { -420, "Query UNTERMINATED"}, + { -430, "Query DEADLOCKED"}, + { -440, "Query UNTERMINATED after indefinite response"}, + { -500, "Power on"}, + { -600, "User request"}, + { -700, "Request control"}, + { -800, "Operation complete"}, +#endif + + // Fine error codes. + // Turn off to save space +#ifdef SCPI_FINE_ERRORS { -101, "Invalid character"}, { -102, "Syntax error"}, { -103, "Invalid separator"}, @@ -115,62 +168,62 @@ static const SCPI_error_desc error_table[] = { { -105, "GET not allowed"}, { -108, "Parameter not allowed"}, { -109, "Missing parameter"}, - { -110, "Command header error"}, + { -111, "Header separator error"}, { -112, "Program mnemonic too long"}, { -113, "Undefined header"}, { -114, "Header suffix out of range"}, { -115, "Unexpected number of parameters"}, - { -120, "Numeric data error"}, + { -121, "Invalid character in number"}, { -123, "Exponent too large"}, { -124, "Too many digits"}, { -128, "Numeric data not allowed"}, - { -130, "Suffix error"}, + { -131, "Invalid suffix"}, { -134, "Suffix too long"}, { -138, "Suffix not allowed"}, - { -140, "Character data error"}, + { -141, "Invalid character data"}, { -144, "Character data too long"}, { -148, "Character data not allowed"}, - { -150, "String data error"}, + { -151, "Invalid string data"}, { -158, "String data not allowed"}, - { -160, "Block data error"}, + { -161, "Invalid block data"}, { -168, "Block data not allowed"}, - { -170, "Expression error"}, + { -171, "Invalid expression"}, { -178, "Expression data not allowed"}, - { -180, "Macro error"}, + { -181, "Invalid outside macro definition"}, { -183, "Invalid inside macro definition"}, { -184, "Macro parameter error"}, - { -200, "Execution error"}, + { -201, "Invalid while in local"}, { -202, "Settings lost due to rtl"}, { -203, "Command protected"}, - { -210, "Trigger error"}, + { -211, "Trigger ignored"}, { -212, "Arm ignored"}, { -213, "Init ignored"}, { -214, "Trigger deadlock"}, { -215, "Arm deadlock"}, - { -220, "Parameter error"}, + { -221, "Settings conflict"}, { -222, "Data out of range"}, { -223, "Too much data"}, { -224, "Illegal parameter value"}, { -225, "Out of memory"}, { -226, "Lists not same length"}, - { -230, "Data corrupt or stale"}, + { -231, "Data questionable"}, { -232, "Invalid format"}, { -233, "Invalid version"}, - { -240, "Hardware error"}, + { -241, "Hardware missing"}, - { -250, "Mass storage error"}, + { -251, "Missing mass storage"}, { -252, "Missing media"}, { -253, "Corrupt media"}, @@ -179,9 +232,9 @@ static const SCPI_error_desc error_table[] = { { -256, "File name not found"}, { -257, "File name error"}, { -258, "Media protected"}, - { -260, "Expression error"}, + { -261, "Math error in expression"}, - { -270, "Macro error"}, + { -271, "Macro syntax error"}, { -272, "Macro execution error"}, { -273, "Illegal macro label"}, @@ -190,105 +243,113 @@ static const SCPI_error_desc error_table[] = { { -276, "Macro recursion error"}, { -277, "Macro redefinition not allowed"}, { -278, "Macro header not found"}, - { -280, "Program error"}, + { -281, "Cannot create program"}, { -282, "Illegal program name"}, { -283, "Illegal variable name"}, { -284, "Program currently running"}, { -285, "Program syntax error"}, { -286, "Program runtime error"}, - { -290, "Memory use error"}, + { -291, "Out of memory"}, { -292, "Referenced name does not exist"}, { -293, "Referenced name already exists"}, { -294, "Incompatible type"}, - { -300, "Device-specific error"}, - { -310, "System error"}, + { -311, "Memory error"}, { -312, "PUD memory lost"}, { -313, "Calibration memory lost"}, { -314, "Save/recall memory lost"}, { -315, "Configuration memory lost"}, - { -320, "Storage fault"}, + { -321, "Out of memory"}, - { -330, "Self-test failed"}, - { -340, "Calibration failed"}, - { -350, "Queue overflow"}, - { -360, "Communication error"}, { -361, "Parity error in program message"}, { -362, "Framing error in program message"}, { -363, "Input buffer overrun"}, { -365, "Time out error"}, - { -400, "Query error"}, - { -410, "Query INTERRUPTED"}, - { -420, "Query UNTERMINATED"}, - { -430, "Query DEADLOCKED"}, - { -440, "Query UNTERMINATED after indefinite response"}, - { -500, "Power on"}, - { -600, "User request"}, - { -700, "Request control"}, - { -800, "Operation complete"}, +#endif + {0} // end mark }; -const char * scpi_error_message(int16_t errno) +static const SCPI_error_desc * find_error_desc(const SCPI_error_desc *table, int16_t errno) { + for (int i = 0; (i == 0 || table[i].errno != 0); i++) { + if (table[i].errno == errno) { + return &table[i]; + } + } + + return NULL; +} + + +static const SCPI_error_desc * resolve_error_desc(int16_t errno) +{ + const SCPI_error_desc *desc; + if (errno == 0) { // ok state - return "No error"; + return &no_error_desc; } else if (errno < 0) { // standard errors - for (int i = 0; (i == 0 || error_table[i].errno != 0); i++) { - if (error_table[i].errno == errno) { - return error_table[i].msg; - } - } + + desc = find_error_desc(scpi_std_errors, errno); + if (desc != NULL) return desc; + + // not found in table, use group-common error + errno += -errno % 10; // round to ten + + desc = find_error_desc(scpi_std_errors, errno); + if (desc != NULL) return desc; + + errno += -errno % 100; // round to hundred + + desc = find_error_desc(scpi_std_errors, errno); + if (desc != NULL) return desc; } else { // user error - for (int i = 0; scpi_user_errors[i].errno != 0; i++) { - if (scpi_user_errors[i].errno == errno) { - return scpi_user_errors[i].msg; - } - } + + desc = find_error_desc(scpi_user_errors, errno); + if (desc != NULL) return desc; } - return "Unknown error"; + return NULL; } -void scpi_error_string(char *buffer, int16_t errno, const char *extra) +/** + * Get error string. + * + * @param buffer Buffer for storing the final string. Make sure it's big enough. + * @param errno Error number + * @param extra Extra information, appended after the generic message. + * + * @returns actual error code. Code may be coerced to closest defined code (categories: tens, hundreds) + */ +int16_t scpi_error_string(char *buffer, int16_t errno, const char *extra) { - const char *msg = scpi_error_message(errno); + const SCPI_error_desc *desc = resolve_error_desc(errno); + const char *msg; - // len - total length, offs - current segment length - int len, offs; - - len = offs = sprintf(buffer, "%d,\"", errno); // ," - buffer += offs; - - offs = sprintf(buffer, "%s", msg); // Error message - len += offs; - buffer += offs; - - if (extra != NULL) { - // extra info - offs = sprintf(buffer, "; "); // ; - len += offs; - buffer += offs; - - // copy in the extra string - int ll = 250 - len - 2; - int xlen = strlen(extra); - if (ll > xlen) ll = xlen; - strncpy(buffer, extra, ll); // Extra msg + if (desc != NULL) { + errno = desc->errno; + msg = desc->msg; + } else { + // bad error code + msg = "Unknown error"; + } - len += ll; - buffer += ll; + // Print. + if (extra == NULL) { + sprintf(buffer, "%d,\"%s\"", errno, msg); + } else { + sprintf(buffer, "%d,\"%s; %s\"", errno, msg, extra); } - sprintf(buffer, "\""); // " + return errno; } diff --git a/source/scpi_regs.c b/source/scpi_regs.c index e4cc456..9fa6250 100644 --- a/source/scpi_regs.c +++ b/source/scpi_regs.c @@ -35,8 +35,8 @@ void scpi_status_update(void) // Run service request callback if (SCPI_REG_STB.RQS) { - if (scpi_service_request_impl) { - scpi_service_request_impl(); + if (scpi_user_SRQ) { + scpi_user_SRQ(); } } }