|
|
View previous topic :: View next topic |
Author |
Message |
sbayeta
Joined: 27 Oct 2003 Posts: 6
|
Erratic behavior. Unexpected reset. |
Posted: Sat Jan 17, 2004 7:14 pm |
|
|
Hi,
I ported my program from assembler to CCS and I'm experiencing some problems trying to make it run.
The program is the firmware of a PBX system.
Users of the PBX need to dial a PIN (Personal Identification Number) in order to use the lines.
The program is written as a state machine. The possible states are:
Code: |
#define IDLE 1 //Idle state (phone is on-hook, no ring signal)
#define TONE 2 //Internal tone
#define LOGN 3 //Logging in
#define HUWT 4 //Waiting for hang-up
#define PUWT 5 //Waiting for pick-up
#define SSEL 6 //Start of selection
#define CSEL 7 //Completion of selection
#define ANWT 8 //Waiting for answer
#define OUTC 9 //Outgoing call
#define RING 10 //Ringing
#define INCC 11 //Incomming call
#define BUSY 12 //busy tone
#define COMM 13 //Communicating with PC
|
The main function is:
Code: |
void main() {
#bit tris_RLY = 0x85.0 //relay tris (PORTA<0>)
#bit tris_HOOK = 0x86.5 //hook tris (PORTB<5>)
#bit tris_RING = 0x86.6 //ring tris (PORTB<6>)
#bit tris_PWR = 0x86.7 //power tris (PORTB<7>)
#byte adcon1 = 0x9F //A/D control
int state = IDLE; //current state of the system
// initializations
enable_interrupts(GLOBAL); // global interrupts enabled
init_I2C(); // I2C initialization
init_PWM(); // PWM initialization
init_timer(); // timer initialization
init_DTMF(); // DTMF initialization
init_clock(); // clock initialization
tris_RLY = 0; // relay pin as output
tris_HOOK = 1; // hook pin as input
tris_RING = 1; // ring pin as input
tris_PWR = 1; // power pin as input
adcon1 = 0x18; // PORTA pins as inputs
#ifdef DEBUG_MAIN
printf("\n\rQOK");
#endif
while(1) {
if(state == IDLE) state = sIDLE();
if(state == TONE) state = sTONE();
if(state == LOGN) state = sLOGN();
if(state == HUWT) state = sHUWT();
if(state == PUWT) state = sPUWT();
if(state == SSEL) state = sSSEL();
if(state == CSEL) state = sCSEL();
if(state == ANWT) state = sANWT();
if(state == OUTC) state = sOUTC();
if(state == RING) state = sRING();
if(state == INCC) state = sINCC();
if(state == BUSY) state = sBUSY();
if(state == COMM) state = sCOMM();
}
}
|
And here are some of the states (only four of them):
Code: |
int sIDLE() {
#ifdef DEBUG_STATE
printf("\n\rIDLE");
#endif
//state initialization
relay_on = 0;
stop_PWM();
while(1) {
update_clock();
if(!on_hook) return TONE; //if a user picks up the phone, jump to TONE state
if(!ring_off) return RING; //if ring signal is detected, jump to RING state
if(kbhit()) return COMM; //if a character is received, jump to COMM state
}
}
int sTONE() {
#ifdef DEBUG_STATE
printf("\n\rTONE");
#endif
//state initalizations
start_timer(1500); //set the timer to 15 seconds
start_PWM(190); //generate a continuous tone
while(1) {
update_clock();
if(on_hook) return IDLE; //if the user hungs up, jump back to the IDLE state
if(DTMF_pressed()) { //if the user presses a DTMF key, jump to LOGN state
user = (read_DTMF() - 1) << 4;
return LOGN;
}
if(timer_expired()) return BUSY; //if the timer expires, jump to BUSY state
}
}
int sLOGN() { //Login
int keys[4]; //keys entered by the user
int nkeys = 0; //number of keys actually entered
int PIN[4]; //user's Personal Identification Number
#ifdef DEBUG_STATE
printf("\n\rLOGN");
#endif
//state initializations
start_timer(1000); //set the timer to 10 seconds
stop_PWM(); //mute the tone
while(1) {
update_clock();
if(on_hook) return IDLE; //if the user hungs up, jump back to the IDLE state
if(timer_expired()) return BUSY; //if the timer expires, jump to BUSY state
if(DTMF_pressed()) { //wait for 4 keys from the user
keys[nkeys] = read_DTMF();
nkeys ++;
if(nkeys == 4) {
read_ext_EEPROM(user + 12, PIN, 4); //retrieve the user's PIN from the external EEPROM
if(keys[0] == PIN[0] && keys[1] == PIN[1] && keys[2] == PIN[2] && keys[3] == PIN[3]) {
beep(200); //let the user know the PIN is OK
return HUWT; //jump to HUWT
}
else return BUSY; //give busy tone (the PIN is not valid)
}
}
}
}
int sBUSY() { //busy tone
short is_on = 1;
#ifdef DEBUG_STATE
printf("\n\rBUSY");
#endif
//state initializations
start_timer(50);
start_PWM(190);
while(1) {
update_clock();
if(on_hook) return IDLE;
if(timer_expired()) {
if(is_on) {
stop_PWM();
is_on = 0;
} else {
start_PWM(190);
is_on = 1;
}
start_timer(50);
}
}
}
|
The program starts in IDLE state: that means nobody is using a phone and there is no ring signal. When a user picks up the phone, the system jumps to TONE state, presenting the user a dial tone so he can dial his PIN. After the PIN has been entered (1 key for user number and 4 keys for PIN) the program verifies the correctness of the keys entered and then jumps either to BUSY tone state or the "waitng for hang-up" state.
The problem arrises during the login state (sLOGN), where erratic bahavior occurs. Depending on the speed with which I dial my PIN, the program may or may not arrive to the next state before a reset occurs.
As you may see, the main function as well as the states functions are really simple, so I don't have a clue on what is causing the erratic behavior.
Below are the rest of the functions called in main and the states involved. Note that among this functions is the timer0 interrupt handler.
I used version 3.150 to compile this code.
I will really apreciate the help from anyone who has the time to look at this code.
Let me know if you need any other info. Thanks in advance !
Code: |
External EEPROM interface functions:
void init_I2C() {
#bit tris_SCL = 0x87.3 //SCL tris (TRISC,3)
#bit tris_SDA = 0x87.4 //SDA tris (TRISC,4)
#byte sspcon = 0x14 //SSP control register
#byte sspadd = 0x93 //SSP baud generator
#byte sspstat = 0x94 //SSP state
tris_SCL = 1; //SCL pin as input
tris_SDA = 1; //SDA pin as input
sspcon = 0x28; //SSP enabled as master
sspadd = 0x09; //bus speed = 100KHz
sspstat = 0x00; //slew rate control disabled
}
void read_ext_EEPROM(long address, int *data, int size) {
#bit sspif = 0x0C.3 //SSP interrutp flag
#bit sen = 0x91.0 //start bit enable
#bit rsen = 0x91.1 //restart bit enable
#bit pen = 0x91.2 //stop bit enable
#bit rcen = 0x91.3 //receive bit enable
#bit acken = 0x91.4 //ack bit enable
#bit ackdt = 0x91.5 //ack data bit
#bit ackstat = 0x91.6 //ack bit state
#byte sspbuff = 0x13 //SSP send buffer
int i = 0; //index for data reception
sen = 1; //send start bit
while(sen){}
sspif = 0; //clear interrupt flag
sspbuff = 0xA0; //send control byte
while(!sspif){} //
while(ackstat){} //wait for ack from slave
sspif = 0; //clear interrupt flag
sspbuff = (int)(address >> 8); //send address high byte
while(!sspif){}
while(ackstat){} //wait for ack from slave
sspif = 0; //clear interrupt flag
sspbuff = (int)(address & 0xFF); //send address low byte
while(!sspif){}
while(ackstat){} //wait for ack from slave
rsen = 1; //send restart bit
while(rsen){}
sspif = 0; //clear interrupt flag
sspbuff = 0xA1; //send control byte
while(!sspif){} //
while(ackstat){} //wait for ack from slave
while(i < size) {
rcen = 1; //enable receive mode
while(rcen){} //wait for data to arrive
*(data + i) = sspbuff; //save the data
i ++; //increment the index
if(i < size) ackdt = 0; //if there are bytes left, send an ack
else ackdt = 1; //else send a nack
acken = 1;
while(acken){}
}
pen = 1; //send stop bit
while(pen){}
}
PWM functions:
void init_PWM() {
#bit tris_CCP = 0x87.2 //CPP tris (TRISC,2)
#byte t2con = 0x12 //timer2 control
#byte ccpr11 = 0x15 //CCP1 register 1
tris_CCP = 0; //PWM pin as output
t2con = 6; //set prescaler to audible frequencies
ccpr11 = 16; //duty cycle = 50%
}
void start_PWM(int freq) {
#byte ccp1con = 0x17 //CPP control
#byte pr2 = 0x92 //PWM frequency
pr2 = freq; //set the frequency
ccp1con = 0xC; //turn the module on
}
void stop_PWM() {
#byte ccp1con = 0x17 //CPP control
ccp1con = 0x00; //turn the module off
}
Timer functions:
void init_timer() {
setup_counters(RTCC_INTERNAL, RTCC_DIV_128);
}
void start_timer(long time) {
while(timer_value != time) timer_value = time;
set_rtcc(178);
enable_interrupts(INT_RTCC);
}
short timer_expired() {
if(timer_value == 0) return 1;
else return 0;
}
#INT_RTCC //timer0 interrupt
void timer0_handler() {
if(timer_value > 0) {
timer_value --;
set_rtcc(178); //overflow every 1/100s
} else disable_interrupts(INT_RTCC);
}
DTMF functions:
void init_DTMF() {
#bit tris_dtmf0 = 0x86.0
#bit tris_dtmf1 = 0x86.1
#bit tris_dtmf2 = 0x86.2
#bit tris_dtmf3 = 0x86.3
#bit tris_dtmfdet = 0x86.4
//all DTMF pins as inputs (PORTB<0:4>)
tris_dtmf0 = 1;
tris_dtmf1 = 1;
tris_dtmf2 = 1;
tris_dtmf3 = 1;
tris_dtmfdet = 1;
}
short DTMF_pressed() {
#bit DTMF = 0x06.4 //DTMF signal actual state
static short prev_DTMF; //DTMF signal previous state
//returns 1 on the negative edge
if(prev_DTMF && !DTMF) {
prev_DTMF = DTMF;
return 1;
}
else {
prev_DTMF = DTMF;
return 0;
}
}
int read_DTMF() {
#byte PORTB = 0x06
return PORTB & 0x0F;
}
Real Time Clock functions:
void init_clock() {
#byte t1con = 0x10 //timer1 control
t1con = 0x0F; //timer1 enabled, ext oscilator, async, prescaler=1, on
}
void update_clock() {
#bit timer1flag = 0x0C.0 //timer1 flag
#byte tmr1h = 0x0F //timer1 high byte
if(!timer1flag) return; //return if a second has not passed yet
#ifdef DEBUG_TIC
printf("\n\rTIC");
#endif
timer1flag = 0; //clear timer1 interrupt flag
tmr1h = 0x80; //overflow every second
time ++; //update the time
// hour of day
hour_seconds ++;
if(hour_seconds == 3600) {
hour_seconds = 0;
hours ++;
if(hours == 24) {
day_of_week ++;
if(day_of_week == 7) day_of_week = 0;
period_day ++;
if(period_day == period_duration) {
//renew_credits();
period_day = 0;
}
}
if((hours > 7 && hours < 20 && day_of_week > 0 && day_of_week < 6) || (hours > 7 && hours < 13 && day_of_week == 6))
low_cost = 0; // 2 minutes per credit
else low_cost = 1; // 4 minutes per credit
}
}
|
|
|
|
Haplo
Joined: 06 Sep 2003 Posts: 659 Location: Sydney, Australia
|
|
Posted: Sun Jan 18, 2004 12:55 am |
|
|
I took a very fast look at your code and the first thing that came to my mind was the watchdog timer. Do you have NOWDT in your fuses settings?
You are not kicking the watchdog anywhere in the code. Maybe the PIC is resetting all the time but you can observe it only during sLOGN state? (If the code resets during IDLE or TONE state it will automatically go back to that state so everything will look 'normal'.) |
|
|
sbayeta
Joined: 27 Oct 2003 Posts: 6
|
|
Posted: Sun Jan 18, 2004 7:20 am |
|
|
Thanks for your reply, but no, it's not the wdt. I'm using the configuration word 3D79, which I also used in the assembler version of this program.
The assembler version works great, although it's really hard to maintain and improve.
Any other thoughts ? |
|
|
Ttelmah Guest
|
|
Posted: Sun Jan 18, 2004 10:19 am |
|
|
sbayeta wrote: | Thanks for your reply, but no, it's not the wdt. I'm using the configuration word 3D79, which I also used in the assembler version of this program.
The assembler version works great, although it's really hard to maintain and improve.
Any other thoughts ? |
You do not show how the code every accesses the code for the states such as sLOGN. The main code will never go to this code, so the implication, is that these other states are probably reached using a process such as one of the interrupts.
If so, then the problem could well tie up with the compilers interrupt protection.
The next comment, is that you are accessing 'TRIS' directly, but there is no mention of #USE FAST_IO in the code posted. Unless this is done, the compiler will itself override TRIS settings, which might cause problems.
Finally, if the other states are accessed through interrupt code, then the state variable itself will need to be global. You have the variable declared in the main, and the functions you show return the new 'state', but there is no mention of how the return value gets transferred to the state variable.
Best Wishes |
|
|
sbayeta
Joined: 27 Oct 2003 Posts: 6
|
|
Posted: Sun Jan 18, 2004 4:25 pm |
|
|
Ttelmah wrote: | You do not show how the code every accesses the code for the states such as sLOGN. The main code will never go to this code, so the implication, is that these other states are probably reached using a process such as one of the interrupts.
If so, then the problem could well tie up with the compilers interrupt protection. |
I'm not shure I get what you're saying. All the states are only reached from another state. I didn't post all the cade just for brevity's sake (since the error appears before a user can reach the states not shown).
As for the interrupts, the only interrupt enabled is timer0's. The routines involved in enabling, disabling and handling that interrupt are the 'timer' functions I posted near the end of my code. Anyway, I don't know what "compilers interrupt protection" means. I'd preciate if you coul clarify this.
Ttelmah wrote: | The next comment, is that you are accessing 'TRIS' directly, but there is no mention of #USE FAST_IO in the code posted. Unless this is done, the compiler will itself override TRIS settings, which might cause problems. |
OK, I didn't know that (I'm new to CCS). I will try using "#USE FAST_IO" an see what happens. Anyhow, if this is the problem, shouldn't it be consistent every time ?
Ttelmah wrote: | Finally, if the other states are accessed through interrupt code, then the state variable itself will need to be global. You have the variable declared in the main, and the functions you show return the new 'state', but there is no mention of how the return value gets transferred to the state variable. |
Again here, the only interrupt used is timer0's. I't used for non-blocking delays in the code, so I don't think that may be causing problems. I'll check that out anyway to make shure though.
Thanks a lot for your reply, any other help will be really apreciated.
Thanks again. |
|
|
sbayeta
Joined: 27 Oct 2003 Posts: 6
|
|
Posted: Mon Jan 19, 2004 8:34 am |
|
|
Well, I talked to a friend at work, and he told he has a similar problem with a program compiled with CCS.
He's program has also a while(1) block in the main function (without breaks) that is being exited sporadically.
As soon as I get the program I'll post it here.
Thanks in advance for any help. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
sbayeta
Joined: 27 Oct 2003 Posts: 6
|
|
Posted: Mon Jan 19, 2004 12:50 pm |
|
|
Thanks a lot for that reply. This friend of mine at work is using 3.182 version. I will try that and see what happens.
Thanks again. |
|
|
Haplo
Joined: 06 Sep 2003 Posts: 659 Location: Sydney, Australia
|
|
Posted: Mon Jan 19, 2004 6:01 pm |
|
|
I remember now, version 3.150 was probably the worst one in the 3.xxx series! I remember I was working on a project and everything was fine. Then I installed 3.150 on the day it was released and everything stopped working. I checked the .LST file and found out some of the bank select code were wrong or completely missing. That caused the PIC to go on a ride to never never land on a one-way ticket during function calls.
There is a high chance that version 3.182 will solve your problem. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|