Pin Change Interrupts, Again

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

Hi Guys,

Not  much information about Pin Change Interrupts out there!

I have read lots of threads here about this.

Certainly there must be a good tutorial somewhere.

Just looking for some friendly advice here.

 

I am trying to interface a large keypad (8 rows, 6 columns) to an atmega328p. 
I cannot modify the hardware in any way so the only option is to adapt the code to work 
on my board. 

I now have this working with polling method.

I want to save power so I thought of 
using pin change interrupts for the 8 inputs (rows) and letting the processor sleep.

There is very little information about this anywhere especially in assembler.
(Assembler is all I know)

 

I took some info from avr240 but there is more than one interrupt in my case.
(as mentioned I cannot modify the board in any way even adding 8 diodes to the inputs as in avr240)

The 8 rows are connected to PB0 - PB5 (PCINT 0 - 5] and PC0, PC1 (PCINT 8 - 9)
The 6 columns are connected to PC2 - PC5 and PD2, PD3. These pins are outputs
and do not have any interrupts enabled.

Everything is enabled correctly according to the datasheet.

Here is what I have done so far:

;Enable Pin Change interrupts on PB0-PB5 (PCINT[5:0])
    ldi    temp,(1<<PCINT5)|(1<<PCINT4)|(1<<PCINT3)|(1<<PCINT2)|(1<<PCINT1)|(1<<PCINT0)
    sts    PCMSK0,temp        ;

;Enable Pin Change interrupts on PC0-PC1 (PCINT[9:8])
    ldi    temp,(1<<PCINT8)|(1<<PCINT9)
    sts    PCMSK1,temp        ;

;enable the interrupt vectors
    lds    temp,PCICR
    sbr    temp,(1 << PCIE1)|(1 << PCIE0)
    sts    PCICR,temp

Here is the port initialization:

.equ    col1 = 2    ;PC2
.equ    col2 = 3    ;PC3
.equ    col3 = 4    ;PC4
.equ    col4 = 5    ;PC5
;PORTD
.equ    col5 = 2    ;PD2
.equ    col6 = 3    ;PD3
;
;PORTB
.equ    row1 = 0    ;PB0
.equ    row2 = 1    ;PB1
.equ    row3 = 2    ;PB2
.equ    row4 = 3    ;PB3
.equ    row5 = 4    ;PB4
.equ    row6 = 5    ;PB5
;PORTC
.equ    row7 = 0    ;PC0
.equ    row8 = 1    ;PC1
;
;set up the column pins
    in    temp,DDRC
    sbr    temp,(1<<col1)|(1<<col2)|(1<<col3)|(1<<col4) ;outputs (columns 1 - 4)
    out    DDRC,temp

    in    temp,DDRD
    sbr    temp,(1<<col5)|(1<<col6) ;PD2 & PD3 = output (Columns 5 & 6)
    out    DDRD,temp

;start out with outputs (columns 1 - 4) low
    in    temp,PORTC
    cbr    temp,(1<<col1)|(1<<col2)|(1<<col3)|(1<<col4)
    out    PORTC,temp

;start out with outputs (columns 5 & 6) low
    in    temp,PORTD
    cbr    temp,(1<<col5)|(1<<col6)
    out    PORTD,temp

;set up the row pins
    in    temp,DDRB        ;inputs (rows 1 - 6, PB0 - PB5)
    cbr    temp,(1<<row1)|(1<<row2)|(1<<row3)|(1<<row4)|(1<<row5)|(1<<row6)
    out    DDRB,temp

    in    temp,DDRC     ;inputs (rows 7 (PC0) and 8 (PC1))
    cbr    temp,(1<<row8)|(1<<row7)
    out    DDRC,temp

;enable internal pull-ups on rows 1 - 6
    in    temp,PORTB
    sbr    temp,(1<<row1)|(1<<row2)|(1<<row3)|(1<<row4)|(1<<row5)|(1<<row6)
    out    PORTB,temp

    in    temp,PORTC
    sbr    temp,(1<<row8)|(1<<row7) ;enable internal pull-ups rows 7 & 8
    out    PORTC,temp
    ret

I am just looking for advice on how to write both pin change ISR's.
Since the columns are all low from the start, this should trigger a 
Pin change interrupt when one key is pressed.

 

I am also confused as to how to perform the debouncing when the bouncing might cause multiple interrupts
because you can't configure the Pin Change interrupt for a low level only. Both levels will cause an interrupt.

I also read that it is wise to keep the ISR's very short (no long debouncing delays).
Wasting CPU time is irrelevant in this case as the program only waits for a key to be pressed,
executes the command and then goes back to sleep.

 

Here are my thoughts:

The ISR must check every single pin in every single row to see which pin is low.

As per avr240, In the ISR set all columns high asap.

Wouldn't this affect the pin read?

After the debouncing is complete,
Enable column 1 (low)
Then start to check every single pin for a low in all of the rows, 1 - 8!
now disable column 1 (high) and enable column 2(low)
Then start to check every single pin for a low in all of the rows, 1 - 8!
repeat for all columns and rows.

 

When the ISR finds a low, set a flag to indicate it's valid.
If the ISR finds a pin change that is not low, clear the flag.

 

Here are my questions:
1. Debouncing methods?

2. How can I EASILY check if the pin is low or high?
I don't want to have many sbic instructions in my code when there are so many pins to check!

I read somewhere to save the last port read and compare it to the current port read
but I am a bit confused as how to do this.

 

Also because the inputs are not all on one port, and they are separated into a different ISR, 
it makes things that much more difficult because you can't just read PINB and compare 
it to the previous read.(it can be done separately in each ISR but I am looking for a better, more efficient way)

 

I am not looking for complete code, just some examples (asm only please) or advice would be greatly appreciated.

Thanks in advance

Last Edited: Fri. Nov 1, 2019 - 09:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Use the pcints to wake you from sleep, disable pcints, then just use your polling method. If you don't detect keypresses within a given time, sleep.

 

 

Last Edited: Fri. Nov 1, 2019 - 10:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

Use the pcints to wake you from sleep, disable pcints, then just use your polling method. If you don't detect keypresses within a given time, sleep.

 

Thanks that is a good idea.

 

So i would just set a in the ISR, then exit back to the mainloop and use the polling method.

there will be two ISR's so I would need two bits (flags) set.

 

So the debouncing routine would be placed outside the ISR, correct?

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

The pcints are just for waking you up. You don't care which pin caused the pcint.

Debouncing etc are all handled in your polling code.

 

ensure you clear the pcint interrupt flags before you re-enable the pcints and go back to sleep

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

sounds good, thanks for the tip