|
|
View previous topic :: View next topic |
Author |
Message |
feederic
Joined: 08 Apr 2008 Posts: 7
|
Problems with Button Delay on 16f887 |
Posted: Tue Apr 08, 2008 8:42 pm |
|
|
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
|
|
Posted: Wed Apr 09, 2008 1:36 am |
|
|
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
|
|
Posted: Wed Apr 09, 2008 12:26 pm |
|
|
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
|
|
Posted: Thu Apr 10, 2008 4:53 pm |
|
|
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
|
|
Posted: Thu Apr 10, 2008 4:59 pm |
|
|
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
|
|
Posted: Wed Apr 16, 2008 8:42 pm |
|
|
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
|
|
|
feederic
Joined: 08 Apr 2008 Posts: 7
|
|
Posted: Wed Apr 16, 2008 9:22 pm |
|
|
That is an excellent link!
I'll post up progress hopefully by monday. |
|
|
feederic
Joined: 08 Apr 2008 Posts: 7
|
|
Posted: Mon Apr 21, 2008 12:58 am |
|
|
Thanks PCM PROGRAMMER and MATRO!
Everything is working as desired! |
|
|
|
|
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
|