CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Problems with Button Delay on 16f887

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
feederic



Joined: 08 Apr 2008
Posts: 7

View user's profile Send private message

Problems with Button Delay on 16f887
PostPosted: Tue Apr 08, 2008 8:42 pm     Reply with quote

I'm new to the PIC programming scene and have been teaching myself while programming a school project.

I've been having problems getting a button to react instantly in my code while using the A2D and case statements.

Namely, I've got a A2D converter controlling the speed of 4 scrolling LEDs. I have 4 buttons, one assigned to each LED, and I want the button to pause its corresponding LED if and only the LED is on.

My problem is I need to hold the button down for a half second (sometimes longer) to get it to react. Sometimes it will react 2 seconds later.


Code:


#include <16F887.h>             // header file for the PIC16F887
                                // includes built-in functions and constants
                                // for arguments/returns.
#use delay (clock = 4MHZ)

#use fast_io(A)                 // all other ports default to 'standard_io'
#use fast_io(B)                 // fast_io means the tris bits only change
#use fast_io(C)
#use fast_io(D) 
                             

#FUSES INTRC,NOWDT,NOPUT,NOMCLR,NOPROTECT,NOCPD,NOBROWNOUT,NOIESO,NOFCMEN,NOLVP

long int state_delay = 0;
int state_delay_temp = 0;
int mult = 20;
unsigned char state_led = 0;

struct adc_result
    {
    int8 value;       
    int1 new_flag;     
    } adc_conversion;

void init_io()
{
   
    SET_TRIS_A(0x0F);   // bit 0 = output, 1 = input
    SET_TRIS_B(0x3F);   // bit 0 = output, 1 = input
    SET_TRIS_D(0xF0);
    SET_TRIS_C(0x00);
 
    OUTPUT_E(0x00);
}

void init_adc()
{
    // Set up RA0 as the only analog input, using VDD and VSS
    // as the references
    SETUP_ADC_PORTS(sAN0 | VSS_VDD);

    // Turn on ADC and use Fosc/8 as the conversion clock
    // (Fosc = 4MHz)
    SETUP_ADC(ADC_CLOCK_DIV_8);   

    // Set channel for conversion to AN0 (on RA0)
    SET_ADC_CHANNEL(0);

    // start the first conversion
    READ_ADC(ADC_START_ONLY);
}

#INT_RTCC   // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{
    // channel is always set to AN0.  Otherwise we might change
    // it here
   
    // Get last conversion result.
    adc_conversion.value = READ_ADC(ADC_READ_ONLY);
    adc_conversion.new_flag = 1;    // new value

   delay_ms(state_delay);
   state_led++;
   
    // start next conversion
    READ_ADC(ADC_START_ONLY);
}


void main()
{

    int switch_count = 0; 
    int temp1 = 0;         
    int led_display = 0;   
 
   
    adc_conversion.value = 0;
    adc_conversion.new_flag = 0;

   
    init_io();
    init_adc();
       
       
    SETUP_TIMER_0(RTCC_INTERNAL | RTCC_DIV_256);
    ENABLE_INTERRUPTS(GLOBAL);         
    ENABLE_INTERRUPTS(INT_RTCC);     

    while(1)
    {
        // look for a new conversion result
        if (adc_conversion.new_flag == 1)
        {
            state_delay_temp = adc_conversion.value;
         state_delay = _mul(state_delay_temp, mult);
            adc_conversion.new_flag = 0;       
           
            led_display = 0;
           
         
        }

       
     if (INPUT_STATE(PIN_B0) == 0 && state_led == 0)
        {
            if (switch_count < 4)
            {
                switch_count ++;
                 
                if (switch_count == 4)
                {
                    output_d(1);
                }   
            }
        }
        else
        {
            switch_count = 0;
        }

if (INPUT_STATE(PIN_B1) == 0 && state_led == 1)
        {
            if (switch_count < 4)
            {
                switch_count ++;
                 
                if (switch_count == 4)
                {
                    output_d(2);
                }   
            }
        }
        else
        {
            switch_count = 0;
        }

if (INPUT_STATE(PIN_B2) == 0 && state_led == 2)
        {
            if (switch_count < 4)
            {
                switch_count ++;
                 
                if (switch_count == 4)
                {
                    output_d(4);
                }   
            }
        }
        else
        {
            switch_count = 0;
        }


if (INPUT_STATE(PIN_B3) == 0 && state_led == 3)
        {
            if (switch_count < 4)
            {
                switch_count ++;
                 
                if (switch_count == 4)
                {
                    output_d(8);
                }   
            }
        }
        else
        {
            switch_count = 0;
        }

   switch (state_led)
      {
         case 0:
            led_display = 1;
            if(INPUT_STATE(PIN_D7) == 1)
            {
            state_led =1;
            break;
            }
            OUTPUT_D(1);
            break;
      
         case 1:
            led_display = 2;
            if(INPUT_STATE(PIN_D6) == 1)
            {
            state_led = 2;
            break;
            }
            OUTPUT_D(2);
            break;
         
         case 2:
            led_display = 4;
               if(INPUT_STATE(PIN_D5) == 1)
            {
            state_led = 3;
            break;
            }
            OUTPUT_D(4);
            break;
         
         case 3:
            led_display = 8;
            if(INPUT_STATE(PIN_D4) == 1)
            {
            state_led = 4;
            break;
            }
            OUTPUT_D(8);
            break;
         
         case 4:
            state_led = 0;
            break;
         
         case 5:
            OUTPUT_D(led_display);
            state_led = 5;
            break;

         
            
         

         }

    }

}       




I have a variable state_led that is being incremented in the Timer0 ISR, this increment will cause the next led to light in the case statements.

I would greatly appreciate any help.
Matro
Guest







PostPosted: Wed Apr 09, 2008 1:36 am     Reply with quote

I would have coded that differently.
Do a read_adc() only once per loop (endless loop of the main()).
Use interrupt-on-change feature of the port B to handle button pushes.

Here in your timer interrupt, the read_adc() will slow down the code and the delay_ms() too.
When this interrupt is triggered, main() execution is stopped so if this interrupt continuously "breaks" the main() the reaction delay will be very long.

I think you don't need to acquire very often the new adc_value (once per loop should be sufficient) so place the read in the main() loop.
And (if your buttons are connected to port B) use the interrupt-on-change of this port to handle button changes.

Matro.
Guest








PostPosted: Wed Apr 09, 2008 12:26 pm     Reply with quote

Thank you Matro, I knew the problem was around the a2d conversion but didn't know how to remedy it.

I will try out all of your suggestions immediately!
Guest








PostPosted: Thu Apr 10, 2008 4:53 pm     Reply with quote

I tried the suggested fixes, and have had some success.

The button delays are gone, and the a2d has smoothed out when turning the pot.

The only issue I have right now is being able to generate a interrupt the whole time an LED is lit.

For clarity, I have RB1-4 assigned to LEDs 1-4 (on PortD)

When LED 1 is on and button RB1 is pushed I want to generate the inerrupt.
When LED 2 is on and button RB2 is pushed I want ""
...and so on.

I'm pretty much using this code in the RB interrupt code.

Code:
if (INPUT_STATE(PIN_B2) == 0 && state_led == 2)
        {
            if (switch_count < 4)
            {
                switch_count ++;
                 
                if (switch_count == 4)
                {
                    output_d(4);
                }   
            }
        }
        else
        {
            switch_count = 0;
        }


anyone have any suggestions??
feederic



Joined: 08 Apr 2008
Posts: 7

View user's profile Send private message

PostPosted: Thu Apr 10, 2008 4:59 pm     Reply with quote

That was my post above.

I also did change the button polling for portB to interrupt on change.
feederic



Joined: 08 Apr 2008
Posts: 7

View user's profile Send private message

PostPosted: Wed Apr 16, 2008 8:42 pm     Reply with quote

Hi everyone, I'm still having a few problems I can't work out.

The buttons work fine except when the LED speed is slow. When the delay coming from the A2D produces a large value, the portB interrupts work unless we hold the button down for about a second.

My code thus far:



Code:
#include <16F887.h>             // header file for the PIC16F887
                                // includes built-in functions and constants
                                // for arguments/returns.
#use delay (clock = 4MHZ)

#use fast_io(A)                 // all other ports default to 'standard_io'
#use fast_io(B)                 // fast_io means the tris bits only change
#use fast_io(C)
#use fast_io(D) 
                              // when we explicitly set them.

// FUSES sets the PIC16F887 Configuration Words.  See top of the header file
// 16F887.h for fuse option constants.
#FUSES INTRC,NOWDT,NOPUT,NOMCLR,NOPROTECT,NOCPD,NOBROWNOUT,NOIESO,NOFCMEN,NOLVP

long int state_delay = 0;
int state_delay_temp = 0;
int mult = 20;
unsigned char state_led = 0;
int led_display = 0;
int switch_count1 = 0;
int temp = 0;
struct adc_result
    {
    int8 value;         // ADC built-in functions default to 8 bit result.
    int1 new_flag;      // 1-bit falg to indicate a new (fresh) value
      
 } adc_conversion;

void init_io()
{
    // set up PORTA so RA0 is an input for the ADC, RA1-RA7 are outputs
    SET_TRIS_A(0x0F);   // bit 0 = output, 1 = input
   
    // set up PORTB so RB0 is an input for the switch SW1, RB1-RB7 are outputs
    SET_TRIS_B(0x3F);   // bit 0 = output, 1 = input
   SET_TRIS_D(0xF0);
    // set up remaining ports to be all outputs
    SET_TRIS_C(0x00);
     PORT_B_PULLUPS(0x3f);
    OUTPUT_E(0x00);
}

void init_adc()
{
    // Set up RA0 as the only analog input, using VDD and VSS
    // as the references
    SETUP_ADC_PORTS(sAN0 | VSS_VDD);

    // Turn on ADC and use Fosc/8 as the conversion clock
    // (Fosc = 4MHz)
    SETUP_ADC(ADC_CLOCK_DIV_8);   

    // Set channel for conversion to AN0 (on RA0)
    SET_ADC_CHANNEL(0);

    // start the first conversion
 
}

#INT_RTCC   // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{
    // channel is always set to AN0.  Otherwise we might change
    // it here
   
    // Get last conversion result.
    adc_conversion.value = READ_ADC(ADC_READ_ONLY);
    adc_conversion.new_flag = 1;    // new value

   delay_ms(state_delay);
   state_led++;
   
}

#INT_RB
void button_interrupt_service()
{



   if (INPUT_STATE(PIN_B1) != 1 )
   {
     
      while(1)
      {
      output_d(1);
       if(input_state(pin_b5) == 0)
      {
      break;
      }
      }   
   }
else
{
break;
}

   if (INPUT_STATE(PIN_B2) != 1 )
{
       while(1)
      {
      output_d(2);
       if(input_state(pin_b5) == 0)
      {
      break;
      }
   }   
   }
else
{
break;
}

   if (INPUT_STATE(PIN_B3) != 1 )
{
       while(1)
      {
      output_d(4);
         if(input_state(pin_b5) == 0)
      {
      break;
      }   
 }   
   }
else
{
break;
}
   
   if (INPUT_STATE(PIN_B4) != 1 )
   {
       while(1)
      {
      output_d(8);
         if(input_state(pin_b5) == 0)
      {
      break;
      }   
}   
   }
else
{
break;
}

}

void main()
{
  //  int1 led_bar_right = 0; // default is led bar graph starting on the left (DS0)   
    int switch_count = 0;   // used for debouncing the switch
    int switch_count1 = 0;           // used to compute the number of LED "bars" to display
 //   int temp1 = 0;           temporary working variable
  //  int led_display = 0;    // led display value
   int sound = 0;
      int start =1;
   int temp = 0;   
 // initialize global variables
    adc_conversion.value = 0;
    adc_conversion.new_flag = 0;
   
    // Set up MCU
    init_io();
    init_adc();
        // Timer 0 runs off internal clock with 1:256 prescaler
        // This means it should run over every ((256*256)/(4MHz/4)) = 65.5ms
        // It will be used to start an ADC conversion.
    SETUP_TIMER_0(RTCC_INTERNAL | RTCC_DIV_256);
    ENABLE_INTERRUPTS(GLOBAL);          // enable interrupts in general to be active
    ENABLE_INTERRUPTS(INT_RTCC);        // enable the interrupt for Timer 0
   ENABLE_INTERRUPTS(INT_RB);

    while(1)
    {
   READ_ADC(ADC_START_ONLY);   
 

if( start ==1)
{
        // look for a new conversion result
        if (adc_conversion.new_flag == 1)
        {
            state_delay_temp = adc_conversion.value;
         state_delay = _mul(state_delay_temp, mult);
            adc_conversion.new_flag = 0;        // reset flag
           
          led_display = 0; // clear display variable
           
         
        }

     

   if (INPUT_STATE(PIN_D7) == 1 && INPUT_STATE(PIN_D6) == 1 && INPUT_STATE(PIN_D5) == 1 && INPUT_STATE(PIN_D5) == 1)
        { // switch is low when pressed
            if (switch_count < 4)
            { // increment the count on all consecutive checks where switch is pressed
              // to a max of 3
                switch_count ++;
                // if we've seen 3 consecutive checks where the switch is pressed
                // it's considered a valid switch press.  Reverse the bar graph
                if (switch_count == 4)
                {
                    state_led = 6;
                }   
            }
        }
        else
        { // anytime switch is detected as not pressed, reset the count
            switch_count = 0;
        }
if(INPUT_STATE(PIN_A2) == 1)
{
            
while(led_display ==1)
      {
         output_high(PIN_c6);
           delay_us(500);
           output_low(PIN_c6);
           delay_us(500);
            break;
      }
while(led_display ==2)
      {
         output_high(PIN_c6);
           delay_us(600);
           output_low(PIN_c6);
           delay_us(600);
            break;
      }
while(led_display==4)
      {
         output_high(PIN_c6);
           delay_us(700);
           output_low(PIN_c6);
           delay_us(700);
            break;
      }
while(led_display ==8)
      {
         output_high(PIN_c6);
           delay_us(800);
           output_low(PIN_c6);
           delay_us(800);
            break;
      }
 
}
      


   switch (state_led)
      {
         case 0:
            led_display = 1;
            if(INPUT_STATE(PIN_D7) == 1)
            {
            state_led =1;
            break;
            }
            OUTPUT_D(1);
            break;
      
         case 1:
            led_display = 2;
            if(INPUT_STATE(PIN_D6) == 1)
            {
            state_led = 2;
            break;
            }
            OUTPUT_D(2);
            break;
         
         case 2:
            led_display = 4;
               if(INPUT_STATE(PIN_D5) == 1)
            {
            state_led = 3;
            break;
            }
            OUTPUT_D(4);
            break;
         
         case 3:
            led_display = 8;
            if(INPUT_STATE(PIN_D4) == 1)
            {
            state_led = 4;
            break;
            }
            OUTPUT_D(8);
            break;
         
         case 4:
            state_led = 0;
            break;
         
         case 5:
            OUTPUT_D(led_display);
            state_led = 5;
            break;

         case 6:
            OUTPUT_D(0x0f);
            state_led=4;
         
            
         

         }

   }

   

 }

}       


I'm pretty sure my problem lies here:
Code:
#INT_RTCC   // This function will be called on a Timer0 interrupt
void timer0_interrupt_service()
{
    // channel is always set to AN0.  Otherwise we might change
    // it here
   
    // Get last conversion result.
    adc_conversion.value = READ_ADC(ADC_READ_ONLY);
    adc_conversion.new_flag = 1;    // new value

   [b]delay_ms(state_delay);[/b]
   state_led++;
   
}


When the delay is large I can't generate an interrupt immediately. Is there any way I can keep this delay going while being able to generate an interrupt on portB?

I'm fresh out of ideas right now.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 16, 2008 9:00 pm     Reply with quote

Look at multi-tasking techniques:
http://www.ccsinfo.com/forum/viewtopic.php?t=17189
feederic



Joined: 08 Apr 2008
Posts: 7

View user's profile Send private message

PostPosted: Wed Apr 16, 2008 9:22 pm     Reply with quote

That is an excellent link!

I'll post up progress hopefully by monday.
feederic



Joined: 08 Apr 2008
Posts: 7

View user's profile Send private message

PostPosted: Mon Apr 21, 2008 12:58 am     Reply with quote

Thanks PCM PROGRAMMER and MATRO!

Everything is working as desired! Smile
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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