Maker Pro
Maker Pro

need help with simple 3v to 34v transistor relay/switch

GSM modem idle current 25 mA? That's a reasonable figure.

Yes, a 2000 mAh battery should give about 70 hours at that current.

Have you confirmed that the GSM modem will work properly at 3.7V? The specification is very clear that its input range is 4.5~5.5V. If you need to add a boost converter, expect an increase of about 50% in the current, and a reduction in operating time down to about 50 hours.

Yeah, the gsm has been working great for the last few days and it idled over night and in to the next day even when i didnt have the mosfet cutting off the booster.

I measured all of the current loads for each part:

Gsm:
26 ma, idle
60 ma, incoming call (RI)
90 ma, connected to a call

Pic: 3ma

Booster:
Idle 58ma (not ringing bell)
Ring:120

Everything is working now so I am back on the USART to capture the RING and NO CARRIER strings instead of soldering that wire to the RI pin on the GSM.

Reading up on how to do interrupts now... I think that is the best way to monitor the USART for character streams so in can all happen outside of my main loop.

I figured I'd create a global array and have an interrupt watch RCIF and store incoming characters into the global array. Then my main loop can look at that array for "RING" and "NO CARRIER".

On a side note, I am a little concerned now that the GSM module will be obsolete soon and not supported by cell providers becuase it is not 3G.. That's a whole other issue to deal with though.. Not going to worry about it right now but I got a little hassle with ATT today when trying to get another line added to my plan. One guy said he can't do it after I gave him the IMEI number because it wasn't 3G.. Then I called back and another guy hooked me right up no questions so I don't know what that's all about. Maybe he was just trying to talk me into buying a new phone..

Let me know if you ever run across one of these! http://www.britishtelephones.com/atm/l11576.htm
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
OK. Yes, interrupts are a good way to do what you want. In this application you can probably get away without them. Personally I would do the decoding in the function that receives the characters. You don't even need a buffer if you match the string as it's received. The interrupt handler would just set a flag that would be detected and then reset by the mainline. I'll write up some pseudocode later.
 
OK. Yes, interrupts are a good way to do what you want. In this application you can probably get away without them. Personally I would do the decoding in the function that receives the characters. You don't even need a buffer if you match the string as it's received. The interrupt handler would just set a flag that would be detected and then reset by the mainline. I'll write up some pseudocode later.

Ya, the problem is I'm not reading from the USART at all.. I'm just writing AT commands to the gsm..
My loops are monitoring inputs and then falling into various other loops. I ran into a lot of trouble trying to deal with the USART because of all of the various loops we could be in depending on if the RI input is active, or the HOOK is active, or the DIAL is active. I had my serial reader sitting in the main loop, like so:

if(RCIF){ //we received a character from the USART, check if it is a RING indicator
//WriteString("got byte");
ReadString(buff);
//sprintf(print_buff, "Received: \"%s\"\n", buff);
//WriteString(print_buff);
if(!strcmp(buff, "RING")){
RI=true;
} else {
if(!strcmp(buff, "NO CARRIER")){
RI=false;
}
}
}


// Read string and save to buffer until ENTER key or buffer lenght
void ReadString(unsigned char* buff) {

unsigned char tmp, index;

tmp=index=0;

while(1) {
tmp = ReadByte();

if(tmp==0x0D) {
tmp=ReadByte();// eat the carriage return byte
buff[index]='\0';
break;
}else {
if(index<BUFFER_SIZE) {
buff[index]=tmp;
index++;
}
}
}
}

But it kept hanging in the ReadString routine after I received the RING string.
Like something got out of sync and it hung up waiting for bytes and never got the '\0' and then that halted the whole program of course..
I think the hanging was because when you pick up the HOOK while its ringing, I write out the ATA command to tell the GSM to answer the call and then the GSM sends back a NO CARRIER and something clashes there and I get stuck in the read loop.

I guess I could try it again, this was a couple days ago. It might have something to do with the debug strings I was writing out. I cant remember if I commented all of them out or not I just gave up trying
and thought it would be better to put the string reading code in its own thread/timer/interrupt.


so it will be cleaner if the USART reader was running in a thread by itself. But yes, maybe I don't need an interrupt. I'm thinking I can make a timer instead. I haven't tried coding up timers or interrupts yet.. I'm pretty much a PIC rooky. This is my first time so I'm figuring things out as I go..
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
Yes, you have to structure the code so that independent tasks operate as separate threads. This doesn't mean that you need a full RTOS (real-time operating system), with pre-emptive multi-tasking, separate stacks, and so on. That kind of behaviour can be implemented without any of those things, by restructuring the design of the code. Although this isn't as pretty as using threads and coroutines, it works fine. See post #49 in this thread for my suggested firmware architecture.

You don't need to use a timer for the serial receiver. You wouldn't gain anything from trying. Unless you use the timer as a periodic interrupt, which wouldn't help with serial receive. I would use the timer for timing the signal generation functions - generating the tones using output compare, and timing the transitions on the bell driver output.

It could be useful if you wrote up the following documentation:

1. A list of all I/O, with pin numbers, register numbers, data direction settings and special functions;

2. A list of tasks, written from the point of view of a description of behaviour.

From what I know so far, you have the following I/O:

Serial received data from GSM - input into PIC - (special function: USART input)
Serial transmitted data to GSM - output from PIC - (special function: USART transmit)
Bell supply enable - output from PIC; active low (no special function)
Bell current path enable - output from PIC; active high (no special function)
Speaker output - output from PIC; special function: timer output compare

Then the functional blocks need to be:

Receive characters from USART (from GSM modem); keep track of current string and position to match recognised messages; set flag when complete recognised string is recelved.

Update timer output compare register for next transition on speaker output, if a beep is in progress. Count down output compare matches and when beep period has finished, disable the beep flag.

Poll timer and update the bell control outputs. Maintain a countdown timer that's reset when the RING_detected flag is set; immediately enable the DC-DC converter, and periodically drive the bell current path enable device to make the bell ring; after a set number of current pulses, disable the DC-DC converter.

Those functions should be implemented as separate functions, which operate independently apart from specifically defined flags that are used for inter-process communication. Each function needs to keep track of its context, and must yield (return, in this case) within a short time of being called; delays cannot be implemented inside the code itself, because that would lock out other code during the delay. Delays must be implemented using the timer.

Those descriptions are pretty simplified. I'll write up some pseudocode so you can see what I'm thinking.
 
Close but I no longer need to drive the speaker form the PIC. I am using the GSM's build in tone generator for that now.

Serial received data from GSM - input into PIC - (special function: USART input)
Serial transmitted data to GSM - output from PIC - (special function: USART transmit)
Bell supply enable - output from PIC; active low (no special function)
Bell current path enable - output from PIC; active high (no special function)
Handset hook, lift off hook to place calls; active low
Dial Complete, resting position/dialing; active low
Rotary, detects pulses from dial; active low
status: LED various blinks indicating initializing gsm etc..
gsm power up - starts up gsm module.

I also plan to do a lot more receiving data from the USART
I plan on receiving the incoming call number (caller ID) so I can add the ability to return missed calls. And the ability to send and receive text messages. I'm thinking of adding a help menu that it will text you back and giving it the ability to program preset numbers in its address book by sending it text messages.

Going to study what youv got here. And yea my architecture is pretty much a hack job so far.. Your design is much better.
 

KrisBlueNZ

Sadly passed away in 2015
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?
 
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?

Thats clean..looks good, I'll try it.
And it's in the lines of what I was thinking.. and having a global buffer. Other than I was thinking of putting it in a timer so it would just call itself every second or so and keep it out of my main loop (because its not setup well for multitasking).. The trouble with my main loop right now is there is a separate loop within the main loop for each input condition.

So when the handset is picked up for example, the main loop catches it and calls "makePhoneCall()" and then inside of that function is a while(HOOK == OFFHOOK); //wait for user to hang up the phone.
So that type of structure would bring the USART routine to a hault..

I need to figure out what was making my ReadString() hang.
Here's some comments in my current code showing where it is hanging:

//main loop
while(1) {
if(HOOK == OFF_HOOK){ //user picked up phone

//when GSM sends us "RING", we startup the bell..
//user picks up HOOK and we fall in here, routines in here send ATA comand to GSM to answer the call,wait for HOOK == ON_HOOK then send ATH to GSM to disconnect the call and fall out of this if statement.
//at this same time, "NO CARRIER" is sent back from the GSM
}

while(HOOK == ON_HOOK){ //while the phone is hung up...

if(RCIF){ //got a character from the USART, check if it is a RING indicator
ReadString(buff); //we are hanging here after HOOK == OFF HOOK (call is answered with a write string to GSM, I think that is effecting the read string)
if(!strcmp(buff, "RING")){ //this works well...
RI=true; //this flag is used to call RingBell(); routine
} else {
if(!strcmp(buff, "NO CARRIER")){ //after HOOK is picked up, AT commands sent to gsm to answer the call, things go bad here and we never receive the "NO CARRIER String"
RI=false;
}
}
}

I think in order to get your code to work I need to rewrite how my even loop works. because any time during my while loops, characters can be coming in from the GSM.. I'm not sure how the USART handles incoming characters that never get read. Do they get tossed into the bit bucket or do they get buffered up waiting to be read?
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
Other than I was thinking of putting it in a timer so it would just call itself every second or so and keep it out of my main loop (because its not setup well for multitasking).
If you're talking about using a timer interrupt, then I advise against it. Adding interrupts just complicates things, unless there's a real reason to use them. You may find that you need to use the USART received data interrupt, if the data rate is 115200 bps. That data rate corresponds to a maximum received character rate of 11,520 characters per second, or one character every 86.8 microseconds. That shouldn't be a problem with the polled receive approach I've described, as long as no other code does anything time-consuming.
The trouble with my main loop right now is there is a separate loop within the main loop for each input condition.
That is a problem, and it's one that you should fix by changing that behaviour. Each firmware function should be structured like the USART receive handler I wrote up. Generally this means no loops whatsoever, and context information retained in variables.

This will probably need to include one or more global variables to keep track of various context information. You will need to define this based on the functions that the unit needs to perform, and which functions can be concurrent and which are mutually exclusive. You need to do a lot of thinking about this. But you can make a start on each part of the firmware as long as you keep to the co-operative multi-tasking structure I've suggested.

I haven't gone through your code and rewritten it because I think it's best for you to do that.
 
If you're talking about using a timer interrupt, then I advise against it. Adding interrupts just complicates things, unless there's a real reason to use them. You may find that you need to use the USART received data interrupt, if the data rate is 115200 bps. That data rate corresponds to a maximum received character rate of 11,520 characters per second, or one character every 86.8 microseconds. That shouldn't be a problem with the polled receive approach I've described, as long as no other code does anything time-consuming.
That is a problem, and it's one that you should fix by changing that behaviour. Each firmware function should be structured like the USART receive handler I wrote up. Generally this means no loops whatsoever, and context information retained in variables.

This will probably need to include one or more global variables to keep track of various context information. You will need to define this based on the functions that the unit needs to perform, and which functions can be concurrent and which are mutually exclusive. You need to do a lot of thinking about this. But you can make a start on each part of the firmware as long as you keep to the co-operative multi-tasking structure I've suggested.

I haven't gone through your code and rewritten it because I think it's best for you to do that.

Agreed, I'll be jumping back into this shortly. I think I might work on finishing the schematic first and get a board built. The only circuit I have at the moment is jumpers on a breadboard.
 
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.

You are right about this with your design. Currently my code is written to pulse the ringer every 3 seconds while the RI pin is high. And my usart version only looks for the first ring string, sets the RI var high and then waits for no carrier. I like the idea of just calling the ring routine each time the usart receives a RING string.

It's too bad that the 16f628 does not have a built in debugger like the 16f88 does.
I spent a few hours today porting the code to the 16f88 so I can use the source level debugger. Unfortuately the TX and RX pins are different so I can't use the serial I/O on my development board.

I've also realized that if I plan to add the address book programming via SMS the code is going to be too large for the 16f628. there is only 2k available. So I might migrate over to the 16f88 for the 16f648.
 
Hey Kris,

Before I create a new post I wanted to run this by you and see if you may know what the problem might be. Ive been working on porting my code to a 16f88 and I am running into problems with the outputs. some will stay active high and others will not:

RA0/AN0
RA1/AN1
RB6/AN5

If I run in the debugger the outputs stay high but when I run stan alone the stay low.

I thought MCLR was the problem but I am pulling that pin high with a 10k resistor and still no luck. I have also set MCLRE to both on and off and no luck.

__CONFIG(WDTE_OFF & MCLRE_ON & CP_OFF & BOREN_OFF & FOSC_INTOSCCLK & PWRTE_OFF & LVP_OFF);


uint8_t main(void)
{
OSCCON = 1100000; //4mhz

OPTION_REG = 0x80; //pull-ups are disabled
INTCON = 0x0; // Disable interupt
ANSEL = 0x00; //disabe analog
ADCON0 = 0x00; //disabe analog
ADCON1 = 0x00; //disabe analog
CMCON = 0x07; // Comparators OFF

//outputs
TRISB6 = 0;
TRISA3 = 0;
TRISA2 = 0;
TRISA1 = 0;
TRISA0 = 0;

PORTA &= ~0xF; // Clear all outputs

PORTBbits.RB6= 1; //flashes but wont stay active
PORTBbits.RB7 = 1;//same
PORTAbits.RA0 = 1//same
PORTAbits.RA1 = 1; //same

PORTAbits.RA2 = //works as expected, stays active
PORTAbits.RA3 //works as expected, stays active

while(1);
}

I have LED's connected to each output. They stay on in debug and stay off when running normally.

Any ideas?
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
Sorry, I can't suggest any explanation for that problem. I've never used PICs myself; I prefer AVRs for that sort of application. But CocaCola and other users have a lot of experience with PICs. Perhaps they can suggest something.
 
I solved it. I had a GND problem on my breadboard.
So now I've got everything running on a 16f88 and can now use the source level debugger. Much better.

I figured out what the problem is with my usart code never receiving the "NO CARRIER" string to stop the ringer. The problem is when the ringer function is pulsaating the ring output on and off making the bell ring and characters come in on the us art, the code that checks in the main loop for RCIF is not being called so we never see in the incoming characters. I thought that once the bell ringer code falls out of its ring loop the mainloop would then see the RCIF being high but it never does. It's as iff you have to receive the character the moment it comes in.


Heres an example of my test code that explains it better:

Code:
while(HOOK == ON_HOOK){ //while the phone is hung up... main loop..
	
	if(RCIF){ //we received a character from the USART, check if it is a RING indicator
		if(RCREG==0x0D) {
			buff[index]='\0';
			index=0;
			sprintf(print_buff, "Received: \"%s\"\n", buff);
			WriteString(print_buff); //print the string out the for debugging
		} else {
			//store the received character
			if(RCREG != 0x0A && RCREG != 0XE1){
				buff[index] = RCREG;
				index++;
			}
		}
		if(!strcmp(buff, "RING")){
			//we receive the "RING" string, set the "ringing" flag
			RI=true;
		} else {
			//we never get here 
			if(!strcmp(buff, "NO CARRIER")){
				RI=false;
			}
		}
	}
	
	if(RI){
		ringBell(); //rings the bell for about 2 seconds... 
			       //If "NO CARRIER" comes in from USART during this period, 
			       //we never get RCIF flag and therefore never receive the string and set RI to false
	}
}
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
I've explained how to write functions that multi-task co-operatively. Re-read my example code and see how it is designed: as a self-contained function that can be (and must be) called repeatedly inside any loop in the mainline.

Personally I would use this structure for EVERY parallel operation that needs to be implemented, with flags and variables for inter-process communication, and a mainline loop that just calls each function, one after the other, then jumps back to the start. Each function should perform a defined part of the overall operation of the firmware.

None of these functions may include a wait loop. If the function needs to wait for something, it must set a flag or a state variable to indicate this, and return, so the other code can run as well. This is the basic guiding principle of co-operative multi-tasking, and it can work very nicely.

Co-operative multi-tasking is normally implemented using a defer() function and stack switching, and this makes the code a lot easier to write, and to follow, but it's not really practical on devices with a hardware stack of limited size, like some PICs. (I haven't checked to see whether your one has this limitation or not.)

BTW you also need to deal with line feeds (ASCII code 0x0A) from the GSM modem; it will send one after every carriage return (0x0D).
 
I follow you, In this case I would have to set a ringing flag after setting the RING_OUT output high and return. then in the mainloop, cycle "delay" amount of times, come back in and set RING_OUT low. I was just thinking instead of having to rewrite all of this code to work this way a timer that handles the usart would be easier.. A usart server basically that runs in the background preemptively.

Code:
void ringBell(int count)
{
    int j;
    int i;
    for(j=0;j<count;j++){
        for(i=0;i<30;i++){

            if(HOOK == OFF_HOOK) //if user picks up phone, bail out of ring loop
                return;

            RING_OUT = 1;
            RingDelay(400);
            RING_OUT = 0;
            RingDelay(1300);
        }
        RingDelay(75000); //3 secs
    }
}

I am already handling the linefeeds and carriage returns fomr the GSM it is reading the RING messages without any issues.
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
I basically understand where you are going with that but I am stuck on sorting out how I can avoid being stuck in some routines that must loop like in this case the ringBell routine.
MUST loop? No. That's the problem. You can't write the code like that.

Instead of writing delay(400), imagine how you would write code that can be called at any time and will test whether the delay in progress has expired.

First of all, you will need a source of timing information that can be polled. I would suggest using Timer 1 as a 16-bit counter, clocked at a frequency somewhere in the range 100 kHz to 1 MHz. When you start your delay, you read the timer register and store it in a variable. Then, every time you want to tell whether the timeout has expired, you read the timer, subtract the value in the variable, and compare the result to a number that's equal to the number of timer clocks in the period you're waiting for.

That algorithm, using 16-bit values, automatically takes care of the possibility that the timer register wraps around part way through the period you are measuring.

The timer can be used to measure multiple periods independently and concurrently. Each period just needs a separate variable to record the value that was in the timer register at the start of the measurement period.

Each thread of code needs some way to "remember what it's doing". In the example I wrote out, this is done through several variables; mainly the match pointer. The behaviour of that code on a call to the function is determined by whether a new character has been received, what that character is, and what those variables are. You need to think in those terms when you're writing your code.

In the case of delays, like the code you posted, it gets a little bit messy, unless you can implement a proper co-operative multi-tasking arrangement using a defer() function. You can still do it, though. The usual way is either through a code pointer, which the code sets to the desired entry point for the next invocation before it returns, or a state variable, which has a similar effect. Look up State Machine on Wikipedia or Google to get an idea of how it's done.
 
Sorry.. I just edited my previous post.. I know I can work around looping in routines..
Right, sounds good. either way I need to figure out how to get a Timer working.
Is there something bad about putting the usart manager in a timer and let it be its own thread?
 
Last edited:

KrisBlueNZ

Sadly passed away in 2015
You need to avoid delays as well. The code needs to be structured like my example, not like the code you posted.

Using interrupts is sometimes essential, but in your case I don't think it will be. Using interrupts can introduce problems with resource ownership - an interrupt handler can change the value in a variable without the knowledge of the main code. You need to take extra precautions to avoid problems of that type. So I would avoid interrupts if you can.

Interrupt handlers also need to be written as state machines, or using some other model that simulates a state machine. So using interrupts would not make your code simpler.

Have a good read-up on co-operative multi-tasking and state machines.
 
You need to avoid delays as well. The code needs to be structured like my example, not like the code you posted.

Using interrupts is sometimes essential, but in your case I don't think it will be. Using interrupts can introduce problems with resource ownership - an interrupt handler can change the value in a variable without the knowledge of the main code. You need to take extra precautions to avoid problems of that type. So I would avoid interrupts if you can.

Interrupt handlers also need to be written as state machines, or using some other model that simulates a state machine. So using interrupts would not make your code simpler.

Have a good read-up on co-operative multi-tasking and state machines.

OK I will, so during a call to delay() will the timer (and everything else) stop?

Back in the cooperate multitasking days of the older mac and windows operating systems, you were able to add Event Manager calls like WaitNextEvent() that would work around this problem I have in my ringBell() routine for example.
 

KrisBlueNZ

Sadly passed away in 2015
OK I will, so during a call to delay() will the timer (and everything else) stop?
That question doesn't make sense. Here's what your code needs to do to implement a delay in a thread:
Read the timer 1 register and store it in a variable - e.g. thisthread_starttime;
Set the state number appropriately so the next time this thread gets execution, it will start at the following label;
Return.
Label:
Read the timer 1 register;
Subtract the thisthread_starttime variable;
// This value is equal to the number of timer 1 clocks since the start of the delay
Compare to the number of timer 1 clocks in the desired delay time;
If the elapsed clocks is less, return without changing the state, so the next time this thread is called, it once again looks to see whether the time has elasped or not.
If the desired number of clocks has elapsed, do the next thing(s) that the thread needs to do;

If the thread needs to do anything else that takes a significant amount of time, more states and labels are required, so that the thread function can return, and the next time it's called, it will resume at the appropriate label.

This timing method is not suitable if a very short latency is required. The time period expiry will be detected some time after it expires; the delay could be significant, depending on how much time the other functions in the main loop take; this is why they mustn't use any delay loops, or do anything that takes significant time. Even a call to strcmp() could take a little while, and this must be considered in the overall design. That's why, in my code example, I did the comparison as the characters are received, instead of a big loop (strcmp() is a loop) when the whole string has been received.

If consecutive delays are required, you should calculate the delay period start by adding the previous delay cycles to the previous start time, instead of sampling the timer. That way, errors due to delays in detecting the timer expiry will not accumulate.

Labels in C code are considered bad practice. The various states can be implemented as sections of a switch() statement instead of a long block of code with labels. There are other ways to do co-operative multi-tasking; the defer() function method is a nice one but may not be workable as I explained earlier.

Back in the cooperate multitasking days of the older mac and windows operating systems, you were able to add Event Manager calls like WaitNextEvent() that would work around this problem I have in my ringBell() routine for example.
Right. If you used an RTOS you would have features like that as well. But you can manage without them. It's just a matter of organising your code in a way that allows multiple threads to operate simultaneously and with minimal mutual interference.
 
Last edited:
Top