3

First, some background on what I'm working on.

I have a custom board with ATmega324A that I designed as part of a project I'm working on. Originally, software for the uC was written for GCC in Atmel Studio. Now I need to port it from GCC to the IAR compiler. I'm using IAR Workbench with AVR toolchain.

The board uses UART to communicate with a Bluetooth module embedded on the board (irrelevant here). There is a button on the board which is HW debounced with a LPF and a Schmitt-Trigger so the ISR triggering signal is clean (checked over oscilloscope).

Triggering signal is connected to PB2 (INT2) of ATmega324A.

What is the issue?

I have a global variable (set as volatile) that is being changed by the ISR from 0 -> 1 everytime the input changes state (0 -> 1 or 1 -> 0). I have an if statement in main() that checks the value of the variable and does something if its set to 1.

This works fine in GCC and always has... for me at least.

However, something strange seems to be happening when the code is compiled with IAR compiler. The global variable is set to 1 inside the ISR but never changes value inside main().

Ok, let me add the code so this will be more clear.

Setup code for ports and interrupts:

#include <ioavr.h>

void Sys_Init(void)
{
  
  DDRC |= (1<<DDRC5) | (1<<DDRC6) | (1<<DDRC7); // LED pins set as output
  DDRB &= ~(1<<DDRB2); // pin set as input (interrupt pin)
  
  EICRA |= (1<<ISC20); // interrupt triggered on both edges
  EIMSK |= (1<<INT2); // enable interrupt on INT2
  
}

Code:

#include <ioavr.h>
#include <intrinsics.h>
#include "init.h"
#include "bluetooth.h"
#include "uart.h"

volatile unsigned char edge_level = 0;
volatile unsigned char btn_trig = 0;

int main(void)
{
  __disable_interrupt();
  Sys_Init(); // initialize ports etc.
  USART0_Init(7); // sets UBRR value for 115200 baud rate
  __enable_interrupt();
  
  while (1) 
  {
    
    if(btn_trig == 1) {
      btn_trig = 0;
      PORTC ^= (1 << PORTC6);
    }
    
  }
  
  while(1); // makes sure that main is never exited
  
}

#pragma vector=INT2_vect
__interrupt void INT2_interrupt(void) // interrupt routine triggered by button press
{
  edge_level = PINB & 0x04;
  btn_trig = 1;
}

So, for some reason, this isn't working in IAR but works fine in GCC. However, if I move:

  if(btn_trig == 1) {
    btn_trig = 0;
    PORTC ^= (1 << PORTC6);
  }

... inside the ISR, the program works fine and the LED turns ON or OFF.

#pragma vector=INT2_vect
__interrupt void INT2_interrupt(void) // interrupt routine triggered by button press
{
  edge_level = PINB & 0x04;
  btn_trig = 1;
  
  if(btn_trig == 1) {
    btn_trig = 0;
    PORTC ^= (1 << PORTC6);
  }
  
}

Looks like the btn_trig is changed inside the ISR but not outside of it... What is going on here?

Thank you for your time!

0xd4v3
  • 99
  • 7
  • What's the purpose of `edge_level` ? – Eugene Sh. Nov 16 '20 at 15:40
  • This variable is used for detecting whether the button has been pressed or released. Its implemented in the GCC version of the project... I stripped the code down as much as possible to narrow down the problem in IAR. For now, it plays no role... its just there until I figure out what's going on. – 0xd4v3 Nov 16 '20 at 15:45
  • So the stripped code is reproducing the same problem, I hope? – Eugene Sh. Nov 16 '20 at 15:47
  • Yes, the stripped code is producing the same problem. The code I showed above is currently running on the uC. – 0xd4v3 Nov 16 '20 at 15:48
  • 4
    Well, there could be a compiler bug not respecting the `volatile` qualifier. You can try looking at the assembly code. BTW, is the interrupt cleared automatically on handling? – Eugene Sh. Nov 16 '20 at 15:49
  • Just a guess. Can you try with a __monitor function that simply returns the value of btn_trig ? That makes the condition inside if() atomic. – Mitu Raj Nov 16 '20 at 16:27
  • @EugeneSh. IAR apparently doesn't produce an asm file. Yes, the flags are cleared automatically. Quote from datasheet " The flag is cleared when the interrupt routine is executed." – 0xd4v3 Nov 16 '20 at 16:44
  • @MituRaj I'll give it a try. Edit: created a __monitor function that returns the btn_trig value and called it inside the if statement... no difference, doesn't work. – 0xd4v3 Nov 16 '20 at 16:45
  • 1
    What are you doing to de-bounce your button? Are you sure the ISR is clearing its causing condition? Consider blipping a GPIO in both the ISR and main and watching their relative timing with a scope. – Chris Stratton Nov 16 '20 at 16:50
  • You definitely can ask the compiler to emit assembly code. Check if suggestions here help: https://www.avrfreaks.net/forum/generate-listfile-iar-embedded-workbench – Eugene Sh. Nov 16 '20 at 16:53
  • I'll double-down on the switch debouncing. What are you doing in this regard? A mechanical switch, connected directly to an external interrupt, can cause some serious havoc. This can lead to very unpredictable (sometimes fatal) behavior. – Chris Knudsen Nov 16 '20 at 16:56
  • @everyone in regards to debouncing... Debouncing is described in OP. Triggering signal is clean. – 0xd4v3 Nov 16 '20 at 17:05
  • @EugeneSh. thanks, i'll try it. – 0xd4v3 Nov 16 '20 at 17:06
  • 2
    Consider trying different optimisation levels in the failing compiler : it may help pin the "volatile" misbehaviour down further. –  Nov 16 '20 at 18:26
  • 1
    Hmm, something really strange was happening. Setting optimization level to "high" and then back to "none" solved the issue... It was set to "none" as default. – 0xd4v3 Nov 16 '20 at 21:16
  • 3
    ok, this does not sound good. The program is supposed to work regardless of optimization levels, unless it has bugs or the compiler has bugs. If the former - you really want to fix them. If the latter - then you probably don't want to use this compiler. Given the program is here, and seem to be bug-free, I would suspect the compiler. One thing you can do - is to try and reproduce it by starting a new project with initial settings and copying the same code into it. – Eugene Sh. Nov 16 '20 at 21:52
  • 1
    How do you know it isn't change? Did you watch it in the debugger? Breakpoint inside main()? Or are you just watching the leds? Anyway, post the disassembly next, then we can tell if it's a compiler bug or not. – Lundin Nov 17 '20 at 07:38
  • Seems like you have figured out what was wrong. You can answer your own question here then. – Mitu Raj Nov 17 '20 at 13:18
  • I'll answer it as soon as I find out what is actually wrong. It does work now but I want to know why it hasn't before. :) – 0xd4v3 Nov 19 '20 at 20:29

0 Answers0