|
|
View previous topic :: View next topic |
Author |
Message |
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
How to use one push button for several functions? |
Posted: Sat Jun 25, 2011 8:03 am |
|
|
What i need is a code that counts how many times a push button is pressed and after each press a different code to be executed.
For example 5 analog inputs are active of the ADC.
Every button push should switch to read another adc input. And when it reaches 5 (five times the button is pressed) to start from the beginning.
I already looked the topic where the interrupt-driven button code is, but i don't have an idea how to modify it for me.
Thank you in advance! |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Jun 25, 2011 1:22 pm |
|
|
I assume you're referring to this button code here:
http://www.ccsinfo.com/forum/viewtopic.php?t=39585&start=1
What you could do, is to combine that program and the outline
of a program shown below. Basically you need to use the interrupt
driven button buffer program, but change the main loop so it looks
like the one shown below. This is probably the easiest way for you
to do it. Or, one of the easy ways.
Code: |
#include <16F877.h>
#fuses XT, NOWDT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232 (baud=9600, xmit=PIN_C6, rcv=PIN_C7)
// Put various sub-routines here.
//==========================================
void main () //A test version of main
{
int8 btn;
int8 adc_result; // Assumes using 8-bit ADC resolution
// Put ADC init code here: set_adc_ports() and setup_adc();
// Also put code here to setup the RTCC and interrupts for it.
// And any other setup code also goes here.
// In this loop below, wait for a button press and then read
// the adc and display it on a terminal window on the PC.
while(1)
{
btn = get_button(); // Wait for button press
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
printf("ch. 0: %x \n\r", adc_result);
.
.
.
// Repeat the above code block 4x more,
// except change the ADC channel in each block.
// Also change the printf to show the correct ADC channel
// for each new block.
// Or, you could make the code block into a routine and
// call it with the ADC channel as the parameter.
// You could use a for() loop to go through the 5 channels.
}
} |
|
|
|
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
|
Posted: Sat Jun 25, 2011 11:15 pm |
|
|
Thank you very much PCM programmer for the advices but there is a problem.
In the main loop where this line is:
Code: | btn = get_button(); |
ADC starts when the buffer is full and not after every button push.
Maybe i should change the get button() function?
Code: | int8 get_button(void)
{
int8 c;
while(!button_ready);
c = button_buffer[next_out];
next_out = (next_out+1) % BUTTON_BUFFER_SIZE;
return(c);
} |
Or this line?
Code: | #define button_ready (next_in!=next_out) |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 26, 2011 2:28 pm |
|
|
It works for me. I did it the way I said in my post. I used the button
buffer interrupt code in the earlier link as the basis of the new program,
and I changed the main() to meet your requirements. See the program
below. I tested it with the version you gave in a previous thread, v4.110.
I'm running this on a PicDem2-Plus board (red board vs.) and pushing
the button on Pin B0, while turning the trimpot on pin AN0. Here's what
I get on the TeraTerm window on my PC. As I turn the trimpot and
push the button, I get successive readings that are increasing as I go
along. That's exactly what's expected. There is no delay for "filling the
buffer". One button push gives one line of output on the screen.
Quote: |
ch. 0: 00
ch. 0: 00
ch. 0: 00
ch. 0: 04
ch. 0: 2a
ch. 0: 40
ch. 0: 6a
ch. 0: 8d
ch. 0: a5
ch. 0: af
ch. 0: af
ch. 0: ff
ch. 0: ff |
Here's the modified main():
Code: |
void main()
{
int8 button;
int8 adc_result; // Assumes using 8-bit ADC resolution
printf("Start \n\r");
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
clear_interrupt(INT_RTCC);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
setup_adc_ports(AN0);
setup_adc(ADC_CLOCK_DIV_8);
while(1)
{
button = get_button(); // Wait for button press
set_adc_channel(0);
delay_us(20);
adc_result = read_adc();
printf("ch. 0: %x \n\r", adc_result);
}
} |
|
|
|
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
|
Posted: Sun Jun 26, 2011 11:06 pm |
|
|
Thanks PCM Programmer but it seems like there is some kind of misunderstanding here.
I want when I push the button for the first time to read the result from AN0 and the reading should be continuous (not only when I'm pushing the button). Second push of the button should switch readings to AN2 and so on until it reaches AN5.
The best will be if all adc channels from AN0 to AN5 are read together and every button push only to switch the readings that are visualized.
For example: I pushed the button once and on the display i can read result from AN0 continuously. In the same time all other ADC channels should be active and sample readings. If you push the button one more time to switch visualization to AN1.
I hope you'll understand me now.
I apologize if my English is the reason for this misunderstanding. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 26, 2011 11:37 pm |
|
|
This revised main() code basically does what you want. Everytime you
press the button it switches to the next higher ADC channel and keeps
reading it (and displaying it) until you press the button again. When it
is reading the last channel, if you press the button, then it goes back to
the first channel.
If you want something more than this, then you should do it yourself.
Quote: |
ch. 0: fe
ch. 0: fe
ch. 0: fe
ch. 0: fe
ch. 1: ef
ch. 1: e6
ch. 1: e2
ch. 1: dd
ch. 1: da
ch. 1: d8
ch. 1: d7
ch. 2: ff
ch. 2: ff
ch. 2: ff
ch. 2: ff
ch. 2: ff
ch. 3: ff
ch. 3: ff
ch. 3: ff
ch. 3: ff
ch. 3: ff
ch. 4: f5
ch. 4: f0
ch. 4: ec
ch. 4: e8
ch. 4: e5
ch. 4: e1
ch. 4: de
ch. 5: bd
ch. 5: ad
ch. 5: a4
ch. 5: 9b
ch. 5: 94
ch. 5: 8d
ch. 5: 88
ch. 0: fe
ch. 0: fe
ch. 0: fe
ch. 0: ff
ch. 0: ff
ch. 0: c6
|
Code: |
void main()
{
int8 adc_result;
int8 adc_ch;
int8 button;
adc_ch = 0;
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
clear_interrupt(INT_RTCC);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
setup_adc_ports(AN0_AN1_AN2_AN3_AN4_AN5);
setup_adc(ADC_CLOCK_DIV_8);
button = get_button(); // Wait for first button press
while(1)
{
set_adc_channel(adc_ch);
delay_us(20);
adc_result = read_adc();
printf("ch. %u: %x \n\r", adc_ch, adc_result);
if(button_ready) // Was a button pressed ?
{
button = get_button(); // If so, then get it
adc_ch++; // Go to next higher ADC channel
if(adc_ch > 5) // Only do channels 0 to 5
adc_ch = 0; // Then start reading ch. 0 again
}
delay_ms(500); // Read ADC every 1/2 second
}
} |
|
|
|
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
|
Posted: Tue Jun 28, 2011 12:20 am |
|
|
Thank you very much PCM Programmer!
I modified the program myself and it works with the exception of power pwm which I added later. Output B0 stays at high level constantly - there are no pwm pulses, only logic "1".
When left only the code needed for the power pwm it works. Maybe something is interfering with it.
This is my main.c:
Pot is connected to AN6.
Code: |
#include <18f4431.h>
#include <PIC18F4431_registers.h>
#device adc=10
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=10000000)
#include <LCD2.c>
#include <button.c>
#include <1wire.c>
#include <ds1820.c>
#define SELECT_BUTTON 1
// Button pins
#define SELECT_BUTTON_PIN PIN_C4
// The preload value below gives an RTCC interrupt rate of
// about 100 Hz (10 ms period) with a 4 MHz crystal.
#define RTCC_PRELOAD (256 - 39)
// GLOBAL VARIABLES
// These are the "Bvar" variables required by Button.c.
// There is one variable for each button.
int8 Bvar_select = 0;
static long int result,lowvolt,highvolt,accel,brake,rpm,adc_accel,period=78,pwm_duty;
// Variables used by button buffer code.
#define BUTTON_BUFFER_SIZE 16
int8 button_buffer[BUTTON_BUFFER_SIZE];
int8 next_in = 0;
int8 next_out = 0;
#define button_ready (next_in!=next_out)
//---------------------------------------------------
// The buttons are read in this Timer0 (RTCC) isr.
#int_rtcc
void rtcc_isr(void)
{
int t;
set_rtcc(RTCC_PRELOAD + get_rtcc());
// Check if the Select button was pressed.
// If so, put it in the buffer.
if(button(SELECT_BUTTON_PIN, 0, 50, 10, Bvar_select, 1))
{
button_buffer[next_in] = SELECT_BUTTON;
t = next_in;
next_in = (next_in+1) % BUTTON_BUFFER_SIZE;
if(next_in == next_out)
next_in = t; // Buffer full !!
}
}
int8 get_button(void)
{
int8 c;
while(!button_ready);
c = button_buffer[next_out];
next_out = (next_out+1) % BUTTON_BUFFER_SIZE;
return(c);
}
void main()
{
float temperature;
int8 ch;
int8 button;
ch = 0;
setup_power_pwm_pins(PWM_COMPLEMENTARY,PWM_OFF,PWM_OFF,PWM_OFF);
setup_power_pwm(PWM_CLOCK_DIV_4|PWM_UP_DOWN|PWM_DEAD_CLOCK_DIV_2,1,0,period,0,1,10);
lcd_init();
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
clear_interrupt(INT_RTCC);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER0);
setup_timer_0(RTCC_INTERNAL);
setup_adc_ports(sAN0 || sAN1 || sAN2 || sAN4 || sAN6 || sAN7);
setup_adc(ADC_CLOCK_INTERNAL);
button = get_button(); // Wait for first button press
while(true)
{
temperature = ds1820_read();
pwm_duty=adc_accel;
set_adc_channel(0);
delay_us(20);
highvolt = read_adc();
set_adc_channel(1);
delay_us(20);
lowvolt = read_adc();
set_adc_channel(2);
delay_us(20);
brake = read_adc();
set_adc_channel(4);
delay_us(20);
accel = read_adc();
set_adc_channel(6);
delay_us(20);
adc_accel = read_adc();
set_adc_channel(7);
delay_us(20);
rpm = read_adc();
set_power_pwm0_duty((pwm_duty/3));
if(ch==0)
{
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc," TEMPERATURE");
lcd_gotoxy(1,2);
if(temperature >= 29.0)
{
lcd_gotoxy(1,2);
printf(lcd_putc,"%3.1fC",temperature);
printf(lcd_putc," Hot!");
delay_ms(500);
lcd_gotoxy(1,2);
printf(lcd_putc,"%3.1fC",temperature);
printf(lcd_putc," ");
delay_ms(500);
}
else if( temperature >= 25 && temperature < 29.0)
{
printf(lcd_putc,"%3.1fC",temperature);
printf(lcd_putc," Normal");
}
else
{
lcd_gotoxy(1,2);
printf(lcd_putc,"%3.1fC",temperature);
printf(lcd_putc," Cold!");
delay_ms(500);
lcd_gotoxy(1,2);
printf(lcd_putc,"%3.1fC",temperature);
printf(lcd_putc," ");
delay_ms(500);
}
}
if(ch==1)
{
if(lowvolt >= 1000)
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," 12V SUPPLY ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%2.1wV",lowvolt*14/100);
printf(lcd_putc," High!");
delay_ms(500);
lcd_gotoxy(1,2);
printf(lcd_putc,"%2.1wV",lowvolt*14/100);
printf(lcd_putc," ");
delay_ms(500);
}
else if( lowvolt >=815 && lowvolt <1000)
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," 12V SUPPLY ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%2.1wV",lowvolt*14/100);
printf(lcd_putc," Normal");
}
else
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," 12V SUPPLY ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%2.1wV",lowvolt*14/100);
printf(lcd_putc," Low!");
delay_ms(500);
lcd_gotoxy(1,2);
printf(lcd_putc,"%2.1wV",lowvolt*14/100);
printf(lcd_putc," ");
delay_ms(500);
}
}
if(ch==2)
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," 200V SUPPLY ");
result=highvolt;
}
if(ch==3)
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," CURRENT ");
if(accel>20)
{
lcd_gotoxy(1,2);
printf(lcd_putc,"%luA",accel);
}
if(brake>20)
{
lcd_gotoxy(1,2);
printf(lcd_putc,"-%luA",brake);
}
}
if(ch==4)
{
lcd_putc('\f');
lcd_gotoxy(1,1);
printf(lcd_putc," RPM ");
lcd_gotoxy(1,2);
printf(lcd_putc,"%lurpm",rpm*2);
result=rpm;
}
if(button_ready) // Was a button pressed ?
{
button = get_button(); // If so, then get it
ch++; // Go to next higher ADC channel
if(ch > 4) // Only do channels 0 to 5
ch = 0; // Then start reading ch. 0 again
}
delay_ms(10);
}
} |
ds1820.c, 1wire.c , button.c are untouched by me.
LCD2.c is the flex lcd 16x2 driver. I changed only the communication pins to suit my hardware configuration. |
|
|
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
|
Posted: Tue Jun 28, 2011 6:47 am |
|
|
It seems like that the ds1820 code is the problem. When this row:
Code: |
temperature = ds1820_read();
|
is present the pwm is very slow. If I remove it everything is ok. Why is that? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Jun 28, 2011 2:39 pm |
|
|
Quote: | temperature = ds1820_read();
is present the pwm is very slow. If I remove it everything is ok. Why is that? |
What do you mean by "very slow" ? Do you mean the duty cycle is
low ? Do you mean the update rate is low ?
That line is loading the 'temperature' variable with something. I noticed
you don't initialize 'temperature'. So it probably comes up equal to 0,
just due to the way PIC ram powers-up from reset. Then I noticed you
have several if-else tests that you do on 'temperature'. Depending on
the value, you inject huge 500 ms delays into your loop. That could
certainly cause your PWM update to appear to be slow.
Other problems:
Quote: |
setup_adc_ports(sAN0 || sAN1 || sAN2 || sAN4 || sAN6 || sAN7); |
You're using the Logical OR operator to combine these terms. That will not
work. Your expression above evaluates to TRUE, which is simply 0x01.
That will only enable sAN0. If you read the ADC section of the 18F4431.h
file, it explains that you should combine these terms with the bitwise OR
operator, which is a single vertical bar: |
Quote: | setup_adc(ADC_CLOCK_INTERNAL);
|
According to the 18F4431 data sheet, you're supposed to use this
divisor with a 10 MHz oscillator: ADC_CLOCK_DIV_4
Quote: |
setup_counters(RTCC_INTERNAL, RTCC_DIV_256);
set_rtcc(RTCC_PRELOAD);
clear_interrupt(INT_RTCC);
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
enable_interrupts(INT_TIMER0);
setup_timer_0(RTCC_INTERNAL);
|
Timer0 and RTCC are two names for the same thing (Timer0).
So the last two lines in bold are not necessary. Also, you are changing
the pre-scaler. In the the top line, you've set the prescaler on the timer
to 256. But in the last line, you've changed it to 1. You need to decide
what you really want to do.
Quote: |
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=10000000)
// The preload value below gives an RTCC interrupt rate of
// about 100 Hz (10 ms period) with a 4 MHz crystal.
#define RTCC_PRELOAD (256 - 39) |
The two items in bold are in conflict with each other. If you want to use
a 10 MHz crystal, you need to change the Timer0 preload value so it will
still generate a 100 Hz interrupt rate.
Just to save time, for a 10 MHz oscillator I suggest using a prescaler of
256, and change the above #define statement to:
Code: |
#define RTCC_PRELOAD (256 - 98)
|
That should give an interrupt rate of approximately 100 Hz. |
|
|
tonkostz
Joined: 07 May 2011 Posts: 40 Location: Bulgaria
|
|
Posted: Wed Jun 29, 2011 8:03 am |
|
|
I did everything you said but it seems like reading of DS1820 in the main loop slows down the update rate of the PWM. When I'm not reading the temp everything is ok. Would you tell me how to deal with that? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Jun 29, 2011 12:52 pm |
|
|
As I said, I believe the problem is because certain temperature values
cause your if-else code to inject 1 second delays into the main loop.
You want to display the "Hot" or "Cold" message on the 2nd line for 500ms
and then clear it. The problem is that your static delays of 500 ms slow
down the whole loop. You need a way to clear the 2nd line after 500ms
without slowing down the main loop.
ckielstra shows how to do this with a Timer interrupt routine and a
global variable in the following thread:
http://www.ccsinfo.com/forum/viewtopic.php?t=45697 |
|
|
|
|
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
|