1

I am messing around with an ATmega328P hooked on a Raspberry Pi, communicating over I2C. The ATmega is the slave (details at the end). While testing the basics of the concept, I faced an interesting problem.

This is the code on the ATmega:

#include <Wire.h>

#define SLAVE_ADRESS 0x1A
#define LED 6

int count = 10;
void setup()
{
    pinMode(LED, OUTPUT);
    /*for(int i=1; i<=3; i++)
    {
      digitalWrite(LED, HIGH);
      delay(100);
      digitalWrite(LED, LOW);
      delay(100);
    }*/
    Wire.begin(SLAVE_ADRESS);
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
}

void loop()
{
  delay(10);
}

void receiveEvent(int received_bytes)
{
  while(Wire.available())
  {
    unsigned char i = Wire.read();
    if(1)
    {
      int j = 5;
      while(j>0)
      {
        digitalWrite(LED, HIGH);
        delay(300);
        digitalWrite(LED, LOW);
        delay(300);
        j-=1;
      }
    }
    delay(1);
  }
}

void requestEvent()
{
  Wire.write(count);
}

What you would expect it to do is blink the LED connected to pin 6 five times when the Pi sends a byte to the ATmega slave. (Things like if(1) are there because the code was much more specific originally.) The strange thing is that this is not the case:

The LED only blinks 1 time.

I honestly have no idea why. This "stdio-fitted" version of the problematic loop ran on the RPi as expected, prints 5*2 times:

#include <stdio.h>

int main()
{
  int j = 5;
  while(j>0)
  {
    printf("LED, HIGH\n");
    printf("LED, LOW\n");
    j-=1;
  }
  return 0;
}

Circumstances:

  • I compile the code in the Arduino IDE on the Pi, then upload it using AVRDUDE to the ATmega with the Pi's GPIOs. No error messages over the process.
  • The ATmega gets 3.3 V supply voltage from the Pi, and operates on 16.000 MHz. Only the LED and the Pi is connected to it.
  • The (Python) code I use on the Pi to write and read a byte is the following. Reading works well, and I know by tests that the ATmega actually receives the correct byte:
import pigpio
import time

pi = pigpio.pi()
atmega = pi.i2c_open(1, 0x1A)

for i in range(6):
    a = pi.i2c_read_byte(atmega)
    print a

pi.i2c_write_byte(atmega, 0x05)
time.sleep(1)
1
  • Side note. Better use for(;;) for controlling loops. Just one clear instruction vs three unrelated ones. Example: for (int j=5; j > 0; j--) do the same, it's shorter and express your intention clearly to any other programmer.
    – user31481
    Sep 9 '17 at 8:17
0
void receiveEvent(int received_bytes)
{
  while(Wire.available())
  {
    unsigned char i = Wire.read();
    if(1)
    {
      int j = 5;
      while(j>0)
      {
        digitalWrite(LED, HIGH);
        delay(300);
        digitalWrite(LED, LOW);
        delay(300);
        j-=1;
      }
    }
    delay(1);
  }
}

receiveEvent is an ISR (interrupt service routine). Don't do delay in an ISR. That will hang indefinitely. Thus the LED only blinks once. You could do delayMicroseconds for testing. In general an ISR should be short.


3
  • Thank you! So I should move as much as possible to the main loop from an ISR, or only avoid timed functions?
    – Neinstein
    Feb 20 '16 at 21:47
  • It is valid to receive the data in the ISR (make the global variable (buffer) volatile). After that, set a flag (also volatile) and do the rest in the main loop.
    – Nick Gammon
    Feb 20 '16 at 22:40
  • I should move as much as possible to the main loop from an ISR - yes you should.
    – Nick Gammon
    Feb 21 '16 at 8:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.