18F4520 Interrupts, RTOS advice

Status
Not open for further replies.

GrandAlf

Advanced Member level 2
Joined
Mar 9, 2002
Messages
520
Helped
47
Reputation
92
Reaction score
6
Trophy points
1,298
Location
UK
Activity points
4,730
18F4520 4Mhz Internal Osc, 300 Baud TX only, watchdog used.

I have an application that need to look for a button low press on A5 about every 100ms. As this port/pin cannot use ext interrupts like port B, I have to find another method. I had thought about using a timer to run a check periodically, but am not sure which one to use as I do not want to interfere with watchdog or serial out, not sure how to impliment it either. I had thought of using CCS built in RTOS, but as the called function runs for about 5 seconds I understand that schedule time would have to be longer than the function time, this of course means that scanning for a button press ever 5+ seconds would be next to useless. Not used this RTOS before, so what I was thinking was that if I ran the task every 100ms and called the function, checked for bounce and if invalid, return. If it was a valid press, could I then stop the task from within the function and resume it when finished, thus leaving the main program running without task switching, or could it only be stopped from main.

It would be impractical to use polling in this application, even though it would not be a major issue if the main function paused for the duration of the called function. Any advice or alternative suggestions would be most welcome.

Added after 3 hours 27 minutes:

I think I may have misunderstood the CCS implementation. Assuming I have a function that is little more than a delay, and that I use an Rtos like Keil, I would expect to be able to switch to it, say every 20ms and it would carry on its timing where it left off last time until completion. Obviously there would be some variation in timing due to switching. As I understand it in CCS, you do not get control back until the function is complete. This seems no more useful than using an interrupt. Surely I must have this wrong, the explanations are really not clear at all. Can anyone confirm or deny?
 

You could use a timer set to interrupt every 100mS to check the button.
Just preload the timer so it interrupts at the interval you need. Use mplab sim to check the interrupt frequency.

The watchdog uses it's own rc timer and the serial it's own baud rate genetrator.
 

Thanks for the info. Have decided that interrupt would be a better solution, so am re-designing board to use B0. Your advice much appreciated.
 

Could I ask a further question?

At the moment I am using B0 as a button input, this jumps to an isr that sets a global flag variable to true. This is then checked in main, and if true, toggles a port pin on, then uses a standard delay function for 4 seconds, after which it resets the port pin and clears the flag variable. This works fine, but of course the problem is that while the delay is in operation, nothing else is going on. It would be better to have this delay running in the background while main carries on with its own thing. The obvious answer would be to use one of the timers, but I am not sure if this can be implimented from within an existing isr. Any advice on how to achieve this would be most welcome.
 

Does this RTOS support software timers?
 

Not really sure about this, not very well documented. Have tried to use the built in rtos, but has been difficult to get a result with it, appears to really screw up with delays. Thought it may be a better option just to use timers if possible.
 

I think you need an rtos to do so.
Or you will have to use another timer thats enabled for the delay you want and its ISR does the flag clearing and stoping the timer when the btn interrupt occurs and the flag
 

Yes I could probably use one of the other timers and ignore the interrupt unless the B0 int flag was set. Will have to think futher along those lines. Many thanks.
 


You could use Timer 1 to increment a global counter via an interrupt.
Pre-load timer one for a period, such as 10mS by using mplab sim, the stop watch and a breakpoint in the interrupt to check the pre-load value.
You could clear the timer to start it and poll it to find the timeout period.
Something like this, using mcc18 compiler.

Code:
unsigned int timer_count;  /* Global timer */

/*--- set up timer 1 for led timing ---*/

void setTMR1(void)
  {
  IPR1bits.TMR1IP = 0;    /* Set low priority */
  PIR1bits.TMR1IF = 0;    /* Clear Interrupt flag */
  PIE1bits.TMR1IE = 1;    /* Timer 1 overflow interrupt enable */
  TMR1H = 0;              /* clear timer */
  TMR1L = 0;
  T1CON = 0b00110001;     /* Timer 1 ON with prescale 1:8 */
  }  

/*--- Interrupt ---*/

#pragma interrupt ISR 
void ISR (void) 
  { 
  if(PIR1bits.TMR1IF)
    {
    timer_count++;

    T1CONbits.TMR1ON = 0;
    TMR1H = 0xf0; /* Pre-load timer */
    TMR1L = 0;
    T1CONbits.TMR1ON = 1;    
    PIR1bits.TMR1IF = 0;
    }
  }
 

I have pretty much given up with CCS rtos, too many problems. Seems like hardware timers would be the way to go. I had initially thought that I could start a hardware timer from the B0 isr and set the port pin, then clearing it when the time counted down. I assume that I would need a separate timer isr to clear the port pin again. The question really was can I start the time from within the B0 isr, or can it only be done in main. I am not a work at the boment so unable to experiment. Again many thanks for your continued help.

Added after 2 minutes:

Also thanks btbass. Dont use mplab, but proteus. Get the idea though.
 

Thats good news. So does this mean I can setup say timer 2 that increments a global variable every so often and when it reaches a value reset the port pin and disable the timer. As so

In main - setup timer increment say every 100ms and enable global interrupts.

in B0 isr Set port pin, clear variable, enable timer interrupt.

In main, check for correct variable value, if = clear port pint and disable time interrupt until next time B0 interrupt is called.

Added after 4 hours 50 minutes:

It worked as my last post. Thanks for the help guys.
 

Thats nice to hear. Can you share the code?
 

Hope this is useful

// Use as needed
#include <18F4520.h>
#FUSES WDT8192 //Watch Dog Timer uses 1:8192 Postscale
#FUSES INTRC_IO //Internal RC Osc
#FUSES NOPROTECT //Code not protected from reading
#FUSES BROWNOUT //Reset when brownout detected
#FUSES BORV27 //Brownout reset at 2.7V
#FUSES PUT //Power Up Timer
#FUSES NOCPD //No EE protection
#FUSES STVREN //Stack full/underflow will cause reset
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOWRT //Program memory not write protected
#FUSES NOWRTD //Data EEPROM not write protected
//#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOPBADEN //PORTB pins are configured as digital input channels on RESET
#FUSES NOWRTC //configuration not registers write protected
#FUSES NOWRTB //Boot block not write protected
#FUSES NOEBTR //Memory not protected from table reads
#FUSES NOEBTRB //Boot block not protected from table reads
#FUSES NOCPB //No Boot Block code protection
#FUSES MCLR //Master Clear pin enabled
#FUSES NOXINST //Extended set extension and Indexed Addressing mode disabled (Legacy mode)

#define Keep PIN_A3

#use delay(clock=4000000,RESTART_WDT)
#use rs232(baud=300,parity=N,xmit=PIN_C6,rcv=PIN_C7,enable=PIN_C5,bits=8)

//Global Variables
unsigned int16 Ticker = 500; // Value Higher than ever used
unsigned int16 LockValue;


// In Main. Use as needed
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_psp(PSP_DISABLED);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16, 249, 15); //Every few milliseconds
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
setup_oscillator(OSC_4MHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);
setup_wdt(WDT_ON);

enable_interrupts(INT_EXT);
ext_int_edge( H_TO_L );
enable_interrupts(global);
LockValue = (read_eeprom_16(30)*20); // Lock Time + Correction to seconds
// Assign what you need to Lockvalue ignore readeeprom


//B0 Interrupt
#int_EXT
void Open (void) // PTE Exit
{
Ticker = LockValue; // Lock Time
output_low(Keep); // Open
enable_interrupts(INT_TIMER2);
}


//Ticker Interrupt
#INT_TIMER2
void Ticker_Counter(void)
{
if (Ticker) --Ticker;

if (Ticker == 0) // This way shown for clarity
{
output_high(Keep); // OFF
Ticker = 500;
clear_interrupt(INT_TIMER2);
disable_interrupts(INT_TIMER2);
}
 

Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…