Author Topic: Checking which pin triggered on a port triggered an interrupt on attiny84  (Read 424 times)

0 Members and 1 Guest are viewing this topic.

Offline hydrogen18

  • Contributor
  • Posts: 17
  • Country: us
I have this somewhat silly frequency generator project I've been working on.

The brief description is I built a 5-bit ADC converter using an exponential resistor series, 2N7000 logic level MOSFETs and an attiny84. Instead of generating a sine wave between 0 and 2*pi, I'm generating a wave between 0 and pi.  I added two bits to my ADC (7 total now) that emit square waves that are in phase with the sine wave but 180 degrees out of phase with each other. Basically the idea is I can drive a center tapped transformer and get +/- by using the square waves to toggle which half of my transformer has current in it.

But enough about the analog portion. I'm aware this is a pretty silly way to generate a signal but I've had fun building it so far. What I'm struggling with is the interrupt code in avr-gcc and assembler. I'm trying to use PCINT8, PCINT9, and PCINT10 on the chip. Here is what I have figured out so far

1. Set DDRB to 0x0 (default, but whatever), this is input
2. Set PORTB to 0x7 (bits 0,1,2), this enables pullup on the interrupts
3. Set PCMSK1 to enable PCINT8, PCINT9, PCINT10.
4. Set GIMSK bit 5 to high to enable the pin change interrupt
5. Set an entry in the vector for PCINT1_vect to jump to whatever I want.

I'm using avr-gcc to build this, but most of it is assembler. I have a main that does setup before jumping to my assembly code which does DDS.

Since I need some visual output, I added an LED that is tied via a 2200 ohm resistor to +5 volts on my board. The other end goes to PA6 which is one of my square wave output pins. So a low signal on this enables the LED. Under normal circumstance this guy runs a 50% duty cycle and lets me know that at least something is running. When my ISR runs, I set all the D->A bits to zero. All the MOSFETs are off, so nothing has any current. I then just wrote a C routine that blinks the LED by toggling PA6in PORTA. Since the MOSFETs are off I've got no current in my transformer. This gives me a visual output other than hooking up my scope.

The purpose of each interrupt is as follows

* PCINT8 - Toggle the byte of the DDS phase word that is being changed
* PCINT9 - Increment selected phase word byte
* PCINT10 - Decrement selected phase word

The phase word of my DDS is r24, r25, r26. The interrupt service routine has some additional constraints, such as r24 can't decrement below 1 and r26 can't be incremented to more than 0x7f.

My interrupt service routine runs and does this

Code: [Select]
rcall dds_isr

the dds_isr starts with

Code: [Select]
in r21, 0x18

This should copy PORTA into r21 where I can check it. I bit check it using very simple code such as

ldi 24, 1
and r24, r21
cpi r24, 1
breq skip_cycle ; Bit is high, so skip it
; Asssembler here to change the byte of the DDS phase word

The idea is to AND the value in r21 with a known bitpattern and then check to see if it is still the same value. If it is, the bit is still set, so pullup hasn't happened.

Since I've got my blink routine, I also setup some extra code to do the following

* PCINT8 - 1 blink of LED
* PCINT9 - 2 blink of LED
* PCINT10 - 3 blink of LED

If no bits are found to be pulled low, 4 blinks of the LED is performed. Calling the C - routine involves saving r23-r31, but this is just a bunch of push/pop around the call. Nothing fancy.

Photo of the physical setup

No transformer here, I have a 100 ohm resistor in place of the transformer. So it's just a big voltage divider

Scope shot of output

A stepped (crummy) inverted sine wave since I've got a voltage divider hooked up.

I know my ISR is running and working! But I always get 4 blinks of the LED when I trigger it. The 3 jumpers up in the top left have 10k resistors going to the PORTB pins and the other pin is just ground. So shorting them with a screwdriver triggers it. But I never get anything other than 4 blinks! Is "in r21, 0x18" not sufficient to capture the state of PORTB? Do I just absolutely have to use something with some sort of flip-flops to capture the state of the pins physically? Is there a chip i can use to do this? Any help you guys can provide would be great.

Here are some links to my complete code and the refs for the attiny84

* my main.c -
* my DDS assembler  code -
* spec sheet for attiny84 -


Offline hydrogen18

  • Contributor
  • Posts: 17
  • Country: us
Re: Checking which pin triggered on a port triggered an interrupt on attiny84
« Reply #1 on: December 07, 2017, 05:07:50 pm »
So I had an idea on this, which is keep running "in r21, 0x18" until I get one of the bits in r21 & 0x7 set to low. I tried to write a loop that runs this 32768 times. I'm unsure if it is working. My "dds_isr" now starts with this.

Code: [Select]
  push r19
  push r20
  ldi r19, 0x00
  ldi r20, 0x80

  in r21, 0x18
  andi r21, 0x7
  cpi r21, 0x7
  brne done_reading_input_port
  subi r19, 0x1
  sbci r20, 0x0
  breq done_reading_input_port 
  rjmp read_input_port

  pop r19
  pop r20

The idea is r20:r19 makes up a 16 bit counter. If it counts to zero the loop breaks (to prevent staying in the ISR forever). Doing "andi r21, 0x7" should leave us with only the lowest 3 bits in r21. Executing "cpi r21, 0x7" checks it and if they aren't equal then the "brne" jumps out of the loop.

However, I am still just getting 4 blinks when my ISR runs. I'm out of ideas.

Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo