Pushbutton reaction time.

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

I have a question for an assignment for school.

 

I do not want the solution but a clear explanation of what i do wrong and a push in the right direction to learn how to code microcontrollers. 

 

When the green LED lights up after ?? time you push button SW2 and then it needs to show a reaction time on the most left seven-segment display.

The reaction time is in tenths of a second.

The timer needs to count every 50ms.

If the reaction time takes to long >1 sec it needs to show - (segment G on the display).

 

The program has a delay function, a display function and an interrupt routine.

 

I have a ATmega328P with a display shield.

The display shield contains a 4 x segment display, two pushbuttons and a RGB LED.   

 

The code i have till now is the following:

#define	F_CPU 16000000UL

#include <avr/io.h>
#include <avr/interrupt.h>

#define SEGMENT_ON	PORTD |= (1<<5)  // macro red LED on
#define LED_OFF 	PORTB &= ~(1<<1) // macro red LED OFF
#define LED_TOGGEL	PINB  |= (1<<1)  // macro toggel LED

void debounce(uint16_t MAXIMUM)
{
	uint8_t input;
	uint16_t integrator;
	uint8_t output;
	const uint16_t MAXIMUM=30;

	input = PINB & (1<<PINB5); // SW2 high

	if (input ==0)
	{
		if (integrator >0)
		{
			integrator--;
		}
	}
	else if (integrator > MAXIMUM)
	{
		integrator++;
	}
	if (integrator==0)
	{
		output = 0;
	}
	else if (integrator==MAXIMUM)
	{
		output=1;
	}
	if ((old_output==0)&&(output==1))
	{
		count = count + 1;
	}
	if (count >=2)
	{
		//Program ???
	}
}*/
void delay(uint16_t ms)
{
	volatile uint16_t i;
	volatile uint16_t c;
	for (c=0 ; c < ms ; c++)
	{
		for (i=0;i<1000;i++)
		{

		}
	}
}

void display0(uint8_t nr)
{
/*
This is the build off the 7 segments
       A          PB0
     F   B    PD3     PD7
       G          PD5
     E   C    PD4     PD6
       D          PD2
*/
	if (nr==0)
	{
		PORTB=PORTB|(1<<0);                             //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<4)|(1<<3)|(1<<2); //segment B, C, E, F & D
	}
	if (nr==1)
	{
		PORTD=PORTD|(1<<7)|(1<<6);  //segment B & C
	}
	if (nr==2)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<5)|(1<<4)|(1<<2);  //segment B, G, E en D
	}
	if (nr==3)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<2);  //segment B, C, G en D
	}
	if (nr==4)
	{
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<3);  //segment B, C, G en F
	}
	if (nr==5)
	{
		PORTB=PORTB|(1<<0);                       //segment A
		PORTD=PORTD|(1<<6)|(1<<5)|(1<<3)|(1<<2);  //segment C, G, F en D
	}
	if (nr==6)
	{
		PORTB=PORTB|(1<<0);                              //segment A
		PORTD=PORTD|(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2);  //segment C, G, E, F en D
	}
	if (nr==7)
	{
		PORTB=PORTB|(1<<0);        //segment A
		PORTD=PORTD|(1<<7)|(1<<6); //segment B, C
	}
	if (nr==8)
	{
		PORTB=PORTB|(1<<0);                                    //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2); //segment B, C, G, E, F & D
	}
	if (nr==9)
	{
		PORTB=PORTB|(1<<0);                             //segment A
		PORTD=PORTD|(1<<7)|(1<<6)|(1<<5)|(1<<3)|(1<<2); //segment B, C, G, F & D
	}
}

ISR (PCINT0_vect)
{
	if (PINB & (1<<PINB5)) // SW2 high
	{
		display0(8);
		LED_OFF;
	}
	else
	{
		SEGMENT_ON;  //
	}
}
 int main(void)
 {
	 int8_t  x;
	 DDRB=0b00000101;  // set segment A and green led
	 DDRC=0b00001000;  // set display 4
	 DDRD=0b11111100;  // set segment B to G
	 PORTC = 0b00001000;

	 PCMSK0 |= (1<<PCINT5);  // interrupt buton 2 (PB5)
	 PCICR  |= (1<<PCIE0);   // Register
	 sei();                 // interrupt enable

	 while (1)
	 {

		delay(2500);
		PORTB |= (1<<2); // Green LED on

	 }
 }

The green LED lights up after 2500ms and when i push SW2 the G segment lights up.

I know that i need to count the 50ms but where do i put the code for that?

I put as much comments in as possible.

 

Thanks for your help

Stef.

Last Edited: Mon. Oct 2, 2017 - 08:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would sugest the best thing to do it to setup a timer to generate an interrupt at a suitable frequency, & use that to increment a counter.

 

you now simp;y need to store the value of the counter at the start (you could set it to 0, but it is easiest to just leave it running IMHO).

 

then make another note of the count when the pin interrupt triggers the time taken is simply finish count - start count.

 

for this type of timing application I would not so much debouce the button but instead ensure I ignore any subsequent triggers, because you do want the timing to be from the 1st instance the switch closes,

you could even simply disable the pin change irq until the system has been reset for the next go.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IPguru wrote:
I would sugest the best thing to do it to setup a timer to generate an interrupt at a suitable frequency, & use that to increment a counter.   you now simp;y need to store the value of the counter at the start (you could set it to 0, but it is easiest to just leave it running IMHO).   then make another note of the count when the pin interrupt triggers the time taken is simply finish count - start count.   for this type of timing application I would not so much debouce the button but instead ensure I ignore any subsequent triggers, because you do want the timing to be from the 1st instance the switch closes, you could even simply disable the pin change irq until the system has been reset for the next go.

 

Thanks for the suggestion but i need to use the delay in this program.

 

But i will make a mental not of it for future programes where the delay function is not a requirement. 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In the first place, why do you think your delay function is actually counting milliseconds?

 

Secondly, if you must use a delay function, then after you turn on the LED you need to enter a loop, counting 50ms delay periods.  This count should be in a global volatile variable, and you would then use that count value in your ISR.  I've left out some details for you to figure out, but that's the essence.

 

EDIT: And what is this about?

for (tijd=0; tijd<=0; tijd++)

 

Last Edited: Sun. Oct 1, 2017 - 03:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the suggestion but i need to use the delay in this program.

Do you have to use your own delay routine or can you use the included with libc?

 

#define F_CPU   16000000UL
#include <util/delay.h>
.
.
.
.
{
    .
    .
    .
    _delay_ms(1234);
    .
    .
    .
}

 

Greg Muth

Portland, OR, US

Xplained Boards mostly

Atmel Studio 7.0 on Windows 10

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@kk6gm said: 

EDIT: And what is this about?

for (tijd=0; tijd<=0; tijd++)

Hmm, an obscure way of counting from 0 to 127..?  For an int8_t, 127 + 1 = (-1).

 

Greg Muth

Portland, OR, US

Xplained Boards mostly

Atmel Studio 7.0 on Windows 10

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greg_Muth wrote:
For an int8_t, 127 + 1 = (-1).
Nope, more probably -128. But actually signed overflows are undefined in C and C++.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greg_Muth wrote:
Do you have to use your own delay routine or can you use the included with libc?

 

Yes i need to use my own delay routine.

 

Greg_Muth wrote:
EDIT: And what is this about?

for (tijd=0; tijd<=0; tijd++)

 

This was something i tried and did not work and forgot to delete it before posting.

Now i deleted it from the post.

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void debounce(uint16_t MAXIMUM)
{
	...
	uint16_t integrator;

integrator is a an automatic stack frame variable. C gives no guarantees about its initial value yet the code does things like:

		if (integrator >0)
		{
			integrator--;
		}

without it ever being set? Also this:

	else if (integrator > MAXIMUM)
	{
		integrator++;
	}

Is it me? Doesn't that test appear to be the "wrong way up"? Surely you test that it has not reached MAXIMUM before you increment it. As it stands it has to have exceeded MAXIMUM before it could ever be incremented.

 

Luckily for you I see nothing in that code that ever called debounce(). By the way that's a kind of odd name. Usually "debounce()" means something along the lines of "wait a while and see what happens". In what sense (apart from a few microseconds) is debounce() temporal?

 

Oh and for delays you will get more mileage (well certainly a more accurate idea of how long) if you explore avr-gcc's <util/delay.h>. As it stands:

void delay(uint16_t ms)
{
	volatile uint16_t i;
	volatile uint16_t c;
	for (c=0 ; c < ms ; c++)
	{
		for (i=0;i<1000;i++)
		{

		}
	}
}

has a parameter called "ms" which perhaps suggests a hope this may be specifying some number of milliseconds? You would be VERY lucky if the generated opcode sequence for that inner loop happens to be exactly one millisecond? Counting a for loop to 1,000, even if F_CPU were 1MHz does not delay 1ms. At 16MHz I guess you *might* be in the ballpark if it really does take 16 cycles for one loop iteration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope, more probably -128.

I realized that after I hit the post button.  I tried to change it, but was not allowed.  Apparently the forum software decided I was a spammer and kept asking me to wait a number of seconds that increased exponentially with every attempt. 

Greg Muth

Portland, OR, US

Xplained Boards mostly

Atmel Studio 7.0 on Windows 10

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
C gives no guarantees about its initial value yet the code does things like:

		if (integrator >0)
		{
			integrator--;
		}

without it ever being set? 

Surely, that should get an "uninitialised variable" warning?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IPguru wrote:

I would sugest the best thing to do it to setup a timer to generate an interrupt at a suitable frequency, & use that to increment a counter.

 

you now simp;y need to store the value of the counter at the start (you could set it to 0, but it is easiest to just leave it running IMHO).

 

then make another note of the count when the pin interrupt triggers the time taken is simply finish count - start count.

 

for this type of timing application I would not so much debouce the button but instead ensure I ignore any subsequent triggers, because you do want the timing to be from the 1st instance the switch closes,

you could even simply disable the pin change irq until the system has been reset for the next go.

 

 

Thanks for your suggestion IPguru.

I have deleted the debouncing.

In the ISR (PCINT0_vect) i put the button and the display as well as the  (count 1/)2 for the millisecond readout.

In the main (void) i will put the counter for every 50ms.

 

I`m going to program try that tomorrow.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For extra credit, allow the resolution and display to be changed to 10ms with a #define or two, or even better, with a switch, and demo that one too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

S_Tukker:

 

It is good first to write a "program plan" in a human language and then translate into C.

 

Example:


// Reaction time
// Button on PB5, Led on PB1

***pseudocode***

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h> 

uint8_t counter;
volatile uint8_t button_pressed; // variable used both in main() and ISR() must be "volatile"

main() 
{
   init ports ( Button input with pull-up, Led output, segments output )   
   enable PCINT5 interrupt
   enable global interrupts 

   while(1)
   {   
      wait 2500 ms
      clear PCIE flag   // by writing "1" to PCIFR.PCIF0
      clear counter
      switch Led on

      while(2)
      {
         wait 50 ms
         counter++
         
         if(button_pressed)
         {
            switch Led off
            clear button_pressed;
            get out from while(2)  // break;
         }
      }
 
      if counter < 20          // if time < 1 sec
         display (counter / 2)
      else
         display segment "g"
   }
}


ISR (PCINT0_vect)
{
	button_pressed = 1;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Visovian,

 

Thanks for your for the hint.

This will help me further with the programming of microcontrollers.