Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • isrParameters - Since an APP_PRINT() statement cannot be used from within an interrupt service routine, this structure is used to collect the calling parameters of the ISR for later processing.

    Code Block
    languagec
    typedef struct isrParameters {
        int appID;
        uint16_t pins;
    } isrParameters;
    • appID - The application ID passed to the ISR callback

    • pins - The pin(s) prompting the ISR invocation.

  • applicationContext - A rather trivial example of application context, but never the less ALL the application state is stored in a single structure which is shared between callbacks.

    Code Block
    languagec
    typedef struct applicationContext {
        // ISR call ring-buffer
        volatile size_t isrCount;
        volatile bool isrOverflow;
        isrParameters isrParams[ISR_MAX_CALL_RETENTION];
    
        // Application status
        bool templateRegistered;  // Only `true` once we've successfully registered the template
        bool done;
    } applicationContext;
    • isrCount - The number of times the ISR was invoked.

    • isrOverflow - Indicates whether the ISR was invoked more than ISR_MAX_CALL_RETENTION times.

    • isrParams - An array dedicated to recording the calling parameters of each ISR invocation.

    • templateRegistered - Provides a mechanism for the polling callback to only attempt to register a template once.

    • done - Provides a mechanism for the polling callback to indicate when a note has been submitted to the Notecard, so it will know when to deactivate itself.

...

  • bool diagActivate(int appID, void *appContext)

    Code Block
    languagec
    // Load Application Context
    applicationContext *ctx = appContext;

    This line casts the context, so it can be used in subsequent operations.

    Code Block
    languagec
    APP_PRINTF("diag: Entered application callback function: diagActivate\r\n\tappId: %d\r\n", appID);

    This line logs the entry into the function along with the calling parameters.

    Code Block
    languagec
    ctx->done = false;

    The function resets the done state, so the application will progress until it has generated a Note or has failed during execution.

    Code Block
    languagec
    // Success
    return true;

    The function returns true, so the application will progress to the polling callback

  • void diagISR(int appID, uint16_t pins, void *appContext);

    Code Block
    languagec
    // Load Application Context
    applicationContext *ctx = appContext;

    This line casts the context, so it can be used in subsequent operations.

    Code Block
    languagec
    /*
     * This callback function is executed directly from the ISR.
     * Only perform ISR sensitive operations and exit quickly.
     */
    ctx->isrParams[ctx->isrCount].appID = appID;
    ctx->isrParams[ctx->isrCount].pins = pins;
    ctx->isrCount++;
    ctx->isrCount = (ISR_COUNTER_MASK & ctx->isrCount);
    ctx->isrOverflow = (ctx->isrOverflow || !ctx->isrCount);

    The function executes inside an actual ISR, so there is no time to perform expensive operations (like logging). Therefore, it collects the calling parameters and updates metadata. This is necessary to track how many times the interrupt has been called, so it may log the details outside the ISR.

    Code Block
    languagec
    if ((pins & BUTTON1_Pin) && !schedIsActive(appID)) {
        schedActivateNowFromISR(appID, true, STATE_DIAG_ISR_XFER);
    }

    These lines filter to ensure the ISR only responds to the PAIR button (BUTTON1_Pin), and checks with the scheduler that the application is not already in an active state. If these conditions are met, then it will request the scheduler to schedule the application for immediate activation instead of respecting the activation period.

    The logs generated by the ISR look like this:

    Code Block
    languagenone
    diag: ISR callback function called  <5> times.
    diag: call 0:	appId: 3	pins: 128
    diag: call 1:	appId: 3	pins: 128
    diag: call 2:	appId: 3	pins: 8192
    diag: call 3:	appId: 3	pins: 128
    diag: call 4:	appId: 3	pins: 128

    Since the parameter collection is performed outside the filter, the ISR callback is invoked multiple times. However, an ISR is only significant to this application when pin equals 8192 (which is 0x2000 or BUTTON1_Pin). As you can see, there are other invocations where pin equals 128. Those invocations are related to the PIR application interrupts, and are ignored by this application.

  • void diagPoll(int appID, int state, void *appContext);

    Code Block
    languagec
    // Load Application Context
    applicationContext *ctx = appContext;

    This line casts the context, so it can be used in subsequent operations.

    Code Block
    languagec
    APP_PRINTF("diag: Entered application callback function: diagPoll\r\n\tappId: %d\tstate: %s\r\n", appID, diagStateName(state));

    This line logs the entry into the function along with the calling parameters.

    Code Block
    languagec
    case STATE_ACTIVATED:
        if (!ctx->templateRegistered) {
            registerNotefileTemplate();
            schedSetCompletionState(appID, STATE_DIAG_CHECK, STATE_DIAG_ABORT);
        } else {
            schedSetState(appID, STATE_DIAG_CHECK, "diag: process diagnostics");
        }
        break;

    This case handles the newly activated state. It will first check to see if a template has been registered in a previous invocation of the polling callback.
    If a template has not been registered, then it will attempt to do so. If the request is delivered successfully, then the state will be changed to the next state in the progression, to process the diagnostic values. Otherwise, if the message fails to be delivered, then the state will be set to the abort state.
    If a template has already been registered on a previous invocation, then the state will be changed to the next state, to process diagnostic values.

    Code Block
    languagec
    case STATE_DIAG_ISR_XFER:
        APP_PRINTF("diag: Transfered from application ISR callback function.\r\n");
        APP_PRINTF("diag: ISR callback function called %s <%d> times.\r\n", (ctx->isrOverflow ? "more than" : ""), (ctx->isrOverflow ? ISR_MAX_CALL_RETENTION : ctx->isrCount));
        if (ctx->isrOverflow) { ctx->isrCount = ISR_MAX_CALL_RETENTION; }
        for (size_t i = 0 ; i < ctx->isrCount ; ++i) {
            APP_PRINTF("diag: call %d:\tappId: %d\tpins: %d\r\n", i, ctx->isrParams[i].appID, ctx->isrParams[i].pins);
        }
        resetIsrValues(ctx);
        // fall through and report diagnostics

    This case handles a transfer from the interrupt routine. It will print the invocation, along with the parameters, of of each recorded ISR, up to ISR_MAX_CALL_RETENTION times. Once it has exhausted it’s cache, it will reset all values related to the ISR. Instead of exiting, it will fall through into the next case…

    Code Block
    languagec
    case STATE_DIAG_CHECK:
        if (ctx->done) {
            schedSetState(appID, STATE_DEACTIVATED, "diag: completed successfully");
            break;
        }
        schedSetCompletionState(appID, STATE_DIAG_CHECK, STATE_DIAG_ABORT);
        addNote(true);
        break;

    This case handles the main objective of the application, the collection of diagnostics and sending a Note to the Notehub. It will check for the done flag, which is only set once a Note has been successfully received by the Notecard. If done is set, then the application will log a success message and request to be deactivated. If the done flag has not been set, then schedSetCompletionState is called to instruct the scheduler what to do when the request to add a Note either succeeds or fails. As you can see above, if the request is successful, then it will return to this state, but the done flag will be true, and the app will request deactivation. However, if it were to fail, then the application to move on to the abort state. Finally, the request to add a Note is made.

    Code Block
    languagec
    case STATE_DIAG_ABORT:
        schedSetState(appID, STATE_DEACTIVATED, "diag: aborted due to failure!");
        break;

    This case is reserved for fail cases encountered during the application processing. It request for the application to be deactivated and generates a log indicating the failure.

  • void diagResponse(int appID, J *rsp, void *appContext);

...

  • static void addNote(bool immediate);

  • static const char * diagStateName (int state);

    Code Block
    languagec
    switch (state) {
    case STATE_DIAG_ABORT:
        return "STATE_DIAG_ABORT";
    case STATE_DIAG_CHECK:
        return "STATE_DIAG_CHECK";
    case STATE_DIAG_ISR_XFER:
        return "STATE_DIAG_ISR_XFER";
    default:
    {
        static char undefined_state[20];
        schedStateName(state, undefined_state, sizeof(undefined_state));
        return undefined_state;
    }
    }

    The function translates the int value of an application specific state into a const char * description of the state. In the default case, where a state is unrecognized, it will defer to the analogous system API, which will either transcribe the state or simply print the integer's string representation.

  • static bool registerNotefileTemplate();


  • static inline void resetIsrValues(applicationContext *ctx);

    Code Block
    languagec
    APP_PRINTF("diag: resetting ISR values\r\n");

    This line logs the entry into the function.

    Code Block
    languagec
    ctx->isrCount = 0;
    ctx->isrOverflow = false;
    for (size_t i = 0 ; i < ISR_MAX_CALL_RETENTION ; ++i) {
        ctx->isrParams[i].appID = 0;
        ctx->isrParams[i].pins = 0;
    }

    These lines will reset all metadata and parameters related to the ISR.

...