Needs help with a problem in using TIMER1 and PCINT2 of ATmega328P

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


I'm writing a code that:

1) generates interrupt on Pin Change on PORTD (PCINT2).

2) In the ISR of PCINT2: the program clocks, scales and resets the 16-bit timer TIMER1 to use for debouncing the pressed key.

The clock is an external source (16MHz). The problem is most likely not in timing. Finally, it disables the PCINT2 so it doesn't interrupt again over the key bouncing period.

3)In the ISR of TIMER1: the program increments an overflow counter, checks for some overflows, if yes, it executes some code and then disables, resets, and un-clocks the TIMER1.

the code executes in the timer ISR is a keyboard scanning of 10 keys (3*3 matrix) followed by updating of the outputs on PORTB if some conditions are met.

When I simulate the code and the circuit using Proteus (attached Image of the layout), the code doesn't work as expected and generates the same response (PB0 becomes HIGH) no matter what key is pressed!  

I've tried many things but in vain! I think the problem lies somewhere around the way I'm using the interrupts. Otherwise, it could be that I messed up some if's statements. 

I need your advice and help. The code is attached below. Thanks very much in advance for your time and interest.  

 

#include<avr/interrupt.h>
#include <avr/io.h>
volatile uint8_t OverflowCount;
volatile uint8_t PositiveEdgeFlag;
volatile uint8_t OutputUpdatingDone;
int main(void)
{
	PositiveEdgeFlag = 0;
	OutputUpdatingDone = 0;
	MCUCR |= 0x10;         // 0x10 = 00010000 Disabling Pull-ups resistors.
	DDRD = (DDRD & 0x1F) | 0x1C; // 0x1F = 00011111, pins: 7,6,5 are inputs, 2,3,4 outs initially.
	PORTD = (PORTD & 0x1F) | 0x1C; //Initially all inputsD are LOWs, outsD highs.
	DDRB |= 0x1F;          // Masking to perserve pins 13,14,15. (ones mark outputs).
	PORTB &= 0xE0;         // 0xE0 = 11100000 OutputsB initially are LOWS. (NO pushbutton pressed).
	SREG |= 1u << 7;   // Enabling Global Interrupt.
	PCICR  |= 0x4;    // Enabling the portD interrupt.
	PCMSK2 = (PCMSK2 | 0xE0) & 0xFC; //Which pins? 0xE0 = B11100000; 0xFC = 11111100 Setting RX, TX to 0;
	PCIFR |= 0x4;    // Ensuring no portD interrupt initially (Reseting Flag)
	TCCR1A &= 0xFC;  //to override the default arduino settings for timer and select the normal mode.
	TCCR1B &= 0xE7;
	while (1)
	{
	}
}

ISR(PCINT2_vect)
{ // the program enters Pin Change ISR 3 times:
	/*1st: once key is pressed.
	2nd: after updating the output and return to the main loop(since inputs go from low to high again, still pressed).
	3rd: once the key is released.
	*/
	if ((PositiveEdgeFlag == 0) && (OutputUpdatingDone == 0)) { //1st INT..
		PositiveEdgeFlag = 1;
		OverflowCount = 0;
		TIMSK1 |= 0x1;   // Enabling Timer Interrupt.
		TCCR1B |= 0x1;   // Timer Clocking: (1/(16 MHz)) second/tick * 2^16 ticks to overflow =~ 4 ms.
		/*
		Detection complete and the timer Enabled, scaled for debouncing, and reset.
		Disabling the PINs interrupt for now frees the CPU from over-interrupting needlessly
		and must be closed so the Positive edge detection can work properly.
		*/
		PCMSK2 ^= 0xE0;
		TCNT1 = 0;    //Initial timer value..
	}

	else if ((PositiveEdgeFlag == 1) && (OutputUpdatingDone)) {//2nd INT..
		PositiveEdgeFlag = 0;
	}
	else if ((PositiveEdgeFlag == 0) && (OutputUpdatingDone)) {//3rd INT..
		//Release_debouncing then OutputUpdatingDone=0.
		OverflowCount = 0;
		TIMSK1 |= 0x1;   // Enabling Timer Interrupt.
		TCCR1B |= 0x1;   // Timer Clocking: (1/(16 MHz)) second/clock * 2^16 clocks to overflow =~ 4 ms.
		/*
		Detection complete and the timer Enabled, scaled for debouncing, and reset.
		Disabling the PINs interrupt for now frees the CPU from over-interrupting needlessly.
		*/
		PCMSK2 ^= 0xE0;
		TCNT1 = 0;    //Initial timer value..
		OutputUpdatingDone = 0;
	}
}
ISR(TIMER1_OVF_vect)
{
	/* ACTIVATED FROM THE PIN CHANGE INTERRUPT.
	The Keyboard scanning starts after Positive is detected and the bouncing has finished.
	The scan method is:
	1) toggle the DDRD of input pin driven high (7 or 6 or 5) to output sending high.
	2) toggle the DDRD of pins (4, 3, 2) to inputs.
	3) Read the PORTD of pins (4, 3, 2) respectively.
	4) the following conditions yield the pressed key:
	7+4 = 1, 7+3=2, 7+2=3
	6+4 = 4, 6+3=5, 6+2=6
	5+4 = 7, 5+3=8, 5+2=9
	if all false -> Key 10 pressed.
	5) if the respective keys is pressed check the output PORTB and realize the logic equations:
	for Transformer-1: (1,2= T1on,T1off) maps to ( G1sw`.(G2sw`+BTsw`), zero)
	for Transformer-2: (3,4 T2on,T2off)  maps to ( G2sw`.(G1sw`+BTsw`), zero)
	for Generator-1: (5,6 G1on,G1off)    maps to ( T1sw`.(G2sw`.T2sw` + BTsw`), zero)
	for Generator-2: (7,8 G2on,G2off)    maps to ( T2sw`.(G1sw`.T1sw` + BTsw`), zero)
	for Bus Tie-1: (9,10 B.Ton,B.Toff)   maps to ( G1sw`.G2sw`+ T1sw`.T2sw`.(G1sw`+G2sw`), zero)
	6) Toggle back the DDRD of pins (7 or 6 or 5) to inputs and DDRD of (4,3,2) to outputs.
	7) set the PORTD of pins (4,3,2) to high again.
	finally the timer is stopped, reset and Disabled.
	The PIN Change is Re-enabled and its flag is cleared
	to ensure no-false interrupts because of manipulating pins above.
	By debouncing of the key release done in 3st PINCT INT, The code is reset and ready for the next key press.
	*/
	if ((PositiveEdgeFlag == 1) && (OverflowCount == 6)) {
		// 3 Overflows = ~12ms for clearing the bouncing.
		uint8_t portd = PORTD & 0xE0;
		uint8_t portb = PORTB & 0x1F;
		uint8_t T1sw = ((~portb) & (1u << 0));
		uint8_t T2sw = ((~portb) & (1u << 1));
		uint8_t G1sw = ((~portb) & (1u << 2));
		uint8_t G2sw = ((~portb) & (1u << 3));
		uint8_t BTsw = ((~portb) & (1u << 4));
		if (portd | (1u << 7)) {
			DDRD &= 0xE3;
			DDRD |= (1u << 7);
			PORTD |= (1u << 7);
			portd = PORTD & 0x1C;
			if (portd | (1u << 4)) {
				//Key_1: T1on
				if ( G1sw && (G2sw || BTsw) ) {
					portb |= 1u;
				}
			}
			else if (portd | (1u << 3)) {
				//Key_2: T1off
				portb  &= ~(1u);
			}
			else if (portd | (1u << 2)) {
				//Key_3: T2on
				if ( G2sw && (G1sw || BTsw) ) {
					portb |= (1u << 1);
				}
			}
		}
		else if (portd | (1u << 6)) {
			DDRD &= 0xE3;
			DDRD |= (1u << 6);
			PORTD |= (1u << 6);
			portd = PORTD & 0x1C;
			if ((portd | (1u << 4))) {
				//Key_4: T2off
				portb  &= ~(1u << 1);
			}
			else if (portd | (1u << 3)) {
				//Key_5: G1on
				if ( T1sw && ((G2sw && T2sw) || BTsw) ) {
					portb |= (1u << 2);
				}
			}
			else if (portd | (1u << 2)) {
				//Key_6: G1off
				portb  &= ~(1u << 2);
			}
		}
		else if (portd | (1u << 5)) {
			DDRD &= 0xE3;
			DDRD |= (1u << 5);
			PORTD |= (1u << 5);
			portd = PORTD & 0x1C;
			if (portd | (1u << 4)) {
				//Key_7: G2on
				if ( T2sw && ((G1sw && T1sw) || BTsw)  ) {
					portb |= (1u << 3);
				}
			}
			else if (portd | (1u << 3)) {
				//Key_8: G2off
				portb  &= ~(1u << 3);
			}
			else if (portd | (1u << 2)) {
				//Key_9: BTon
				if ((G1sw && G2sw) || ((T1sw && T2sw) && (G1sw || G2sw))) {
					portb |= (1u << 4);
				}
			}
		}
		else {
			//Key_10: BToff
			portb  &= ~(1u << 4);
		}

		TCCR1B &= 0xF8;// B11111000 = Stopping(Un-clocking) the Timer.
		TIMSK1 ^= 0x1; // Disabling Timer Interrupt.
		TCNT1 = 0;     // Resetting for future use elsewhere in the program.
		DDRD = (DDRD & 0x1F) | 0x1C; //0x1F = 00011111, pins: 7,6,5 are inputs, 2,3,4 outputs.
		PORTD = (PORTD & 0x1F) | 0x1C; //all inputsD are LOWs, outsD highs.
		PCMSK2 |= 0xE0;// Re-enabling the PCINT (7,6,5) change interrupt.
		PCIFR |= 0x4; // Clearing the PCINT flag to ensure no-false interrupts because of manipulating pins above.
		PORTB = (PORTB & 0xE0) | portb; // update the output if pressed.
		OutputUpdatingDone = 1;
	}
	else if ((PositiveEdgeFlag == 0) && (OverflowCount == 6) ) { //Release Debouncing:
		TCCR1B &= 0xF8;// B11111000 = Stopping(Un-clocking) the Timer.
		TIMSK1 ^= 0x1; // Disabling Timer Interrupt.
		TCNT1 = 0;     // Resetting for future use elsewhere in the program.
		PCMSK2 |= 0xE0;// Re-enabling the PCINT (7,6,5) change interrupt.
		PCIFR |= 0x4; // Clearing the PCINT flag.
	}
	else {
		OverflowCount += 1;
	}
}

The circuit schematic:  

Thanks again for anyone trying to help. 

A single byte sometimes makes a difference.

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

Buttons in an interrupt? Recipe for disaster.

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

Firstly, don’t use the pcints. Just run the timer constantly to generate, say a 10ms interrupt. Use the timer CTC mode.

Your long string of else-ifs and obscure bit manipulation only serves to confuse us. Write your code in a way so that it is simple.

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

Thanks very much to you all. I've fixed it. It turned out to be I messed up with if's statements (ORing with an ever true value of 1) and I was reading the register PORTxn for inputs that change EXTERNALLY by the connected circuit which caused bugs when used to trigger edge-based (-ve or +ve doesn't matter here) interrupts because the of the synchronizations issue (short solution: Read from PINxn registers). For more about synchronization, read the "configuring pins" section on ATmega328P datasheet. 

 

A single byte sometimes makes a difference.

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

Great! Mark it as solution, please.

 

Roga.