Here's my suggested design for the USART receive function.
I've realised that you probably don't need to match NO CARRIER. All you need to do is generate a burst of activity on the bell control outputs in response to every RING string. But I'll leave the code in there.
Code:
static int UR_flags;
void UR_fn(int doinit) {
// This function is called once at program startup with doinit = 1 to initialise its variables and the USART hardware.
// Then, it is called regularly from the main loop, with doinit = 0, to perform its USART receive function.
// It takes care of all USART received character processing.
// It sets ring_flag or no_carrier_flag when a complete matching string is received.
// It must return within a short time - it cannot include delays.
#define FLAG_RING (1 << 0)
#define FLAG_NOCARRIER (1 << 1)
//... more FLAG_ bits
static char UR_matchstr1 = "RING";
static char UR_matchstr2 = "NO CARRIER"
static char * UR_matchstrings[] = {UR_matchstr1, UR_matchstr2};
#define N_MATCHSTRINGS (sizeof(UR_matchstrings) / sizeof(UR_matchstrings[0]))
static char * UR_matchptr;
static int UR_pending_flags;
auto char c;
auto int n;
if (doinit) {
//** Do USART initialisation here **//
UR_matchptr = NULL; // No match string selected
UR_flags = 0;
}//doinit
// Normal logic if not initialising
if (RCIF == 0)
return; // No character received - nothing to do
c = RCREG; // Read character (this clears RCIF)
if (c == 0x0A || c = '\0')
return; // Ignore line feeds and nuls
if (c == 0x0D) {
if (UR_matchptr != NULL && *UR_matchptr == '\0') // Fully matched string
UR_flags |= UR_pending_flags; // Set appropriate flag
UR_matchptr = NULL;
UR_pending_flags = 0; // Ready for next string
return;
}
// Received character is not a CR and not a LF
#if DEBUGCODE
c = toupper(c); // If you want to test this receive code with a terminal, you can type in lower case
#endif
if (UR_matchptr != NULL) { // If already in the process of matching a string
if (c = *UR_matchptr)
++UR_matchptr; // Correct match - advance match pointer
// Note - this code doesn't properly handle a partial match. When there's a
// mismatch part way through the string, this code should probably throw out
// characters until the next carriage return. At present, it just neglects to
// advance the match pointer; when the carriage return is actually received,
// variables will be correctly set by the 0x0D handler. So any bogus characters
// in the received string will effectively be ignored.
return; // Received another character
}
// At this point, UR_matchptr is NULL; this means that no string match is
// currently in progress. Try to match the received character against the
// first character of each match string.
//
// Assumption: The first character of each match string is different.
//
for (n = 0; n < N_MATCHSTRINGS; ++n) { // For each possible match string
if (c == UR_matchstrings[n][0] { // Match against first character
UR_matchptr = UR_matchstrings[n] + 1; // Point to next character in match string
UR_pending_flags = 1 << n; // Set up the bitmask for setting appropriate on match
break;
}
}//for
return;
}//UR_fn
That function contains all relevant variables except for UR_flags, which is global so that other parts of the code can use it. In response to a complete matched string, the function sets bits in UR_flags; these flags must be cleared by code elsewhere in the program.
The function must be called once at startup with its doinit parameter set to 1, and subsequently, it must be called from the mainline loop as part of a co-operative multi-tasking setup.
That code is mostly complete. You can see that it doesn't use a buffer; it just keeps track of the match position using UR_matchptr. UR_pending_flags contains a 1-bit in one position, according to the number of the string that is matched; it is copied into UR_flags when the complete string is matched (when the 0x0D carriage return character is received).
As mentioned in the comment, the code doesn't properly handle all possibilities. As soon as a character is received that doesn't match the character at the current position in the match string, or that doesn't match the first character of any match string, the code should go into a state (indicated by a flag, probably) that causes it to throw out characters until a 0x0D is received, then start again.
That change wouldn't be hard to implement. I'll leave it as "an exercise for the reader"
So that code implements a self-contained USART receiver. It always completes and returns within a short time. It keeps track of its own variables, and only communicates with other parts of the code via the UR_flags variable; it only sets bits in this variable. There's no need to disable interrupts or take any other precautions related to multi-tasking, because that variable isn't volatile - it can only change when that function is called. This is a pretty tidy approach.
The other functions can be done using a similar approach.
There may be some mismatched parentheses etc in that code. I haven't tried to compile it.
What do you think?