Go Down

Topic: SPI_CLOCK_DIV and internal oscillator (Read 4974 times) previous topic - next topic

lemming

Apr 29, 2012, 02:17 am Last Edit: Apr 29, 2012, 02:24 am by lemming Reason: 1
I am running a 328 using the internal oscillator at 8 Mhz dividing down to 1 Mhz as I am using a 3.3v power source. However the system hangs after a while (I have a 'hearbeat' LED connected, which stops flashing).

I know that to use serial (UART) communications with this setup, I have to use a slow baud rate due to the inaccuracy of the internal clock.
(I am using 1200 baud to play it safe. Does anyone know the maximum safe baud rate for the above settings?)

I am using an RFM22B transceiver module with the Atmega which uses SPI.
Does the inaccuracy of the internal clock affect SPI reliable rate like the UART rate?
What SPI_CLOCK_DIV setting should I use to ensure reliable SPI communication at the effective 1 Mhz Atmega328 clock rate?

nickgammon

Can you show your sketch? Sounds more like a programming issue.

The clock speed /accuracy should not affect SPI at all. Unlike serial comms, the clock is explicit - that is, you actually send the clock pulse.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

lemming

Thanks Nick.

The sketch relays packets from other RFM22B nodes on a network. It uses Mike McCauleys library http://www.open.com.au/mikem/arduino/RF22/. Sketch as follows:

Code: [Select]

#include <RF22Datagram.h>
#include <RF22.h>
#include <SPI.h>

#define BUFFERLENGTH 8

char SoftwareVersion[] = "Centre version 1.0";
uint8_t PumpAddress = 1;
uint8_t RelayAddress = 2;
uint8_t CentreAddress = 3;
uint8_t BoomAddress = 4;
uint8_t SSpin = 10;
uint8_t Intpin = 0;
uint8_t ErrorCount = 0;

long interval = 5000;
long previousMillis = 0;

uint8_t RfBuffer[BUFFERLENGTH];
RF22Datagram rf22(RelayAddress, SSpin, Intpin);

void setup()

  pinMode(8, OUTPUT);
  digitalWrite(8, HIGH);
  delay(3000);
  digitalWrite(8, LOW);
  Serial.begin(1200);
  Serial.println(F("Setup"));
  SPI.setClockDivider(SPI_CLOCK_DIV4);
  InitialiseRF(); 
}

void loop()
{
  rf22.waitAvailableTimeout(2000);
  uint8_t len = sizeof(RfBuffer);
  uint8_t from;
  uint8_t to;
  if (rf22.recvfrom(RfBuffer, &len, &from, &to))
  {
    Serial.print(F("got request from: "));
    Serial.println(from, HEX);
    Serial.print(F("  Type: "));
    Serial.print((char)RfBuffer[1]);
    Serial.print(F("  "));
    Serial.print(F("Dest: "));
    Serial.print(RfBuffer[2]);
    Serial.print(F("  "));
    Serial.print(F("Payload: "));
    Serial.println(RfBuffer[3]);
    test_rssi();
    digitalWrite(8, HIGH);
    delay(150);   
    digitalWrite(8, LOW);
    if(to == RelayAddress)
    {
      if(RfBuffer[2] == CentreAddress)
      {
        rf22.sendto(RfBuffer, sizeof(RfBuffer), CentreAddress);
        rf22.waitPacketSent();
        Serial.print(F("Relayed1 from "));
        Serial.print(from, DEC);
        Serial.println(F(" to Centre"));         
      }
      else if(RfBuffer[2] == BoomAddress)
      {
        rf22.sendto(RfBuffer, sizeof(RfBuffer), CentreAddress);
        rf22.waitPacketSent();
        Serial.print(F("Relayed2 from "));
        Serial.print(from, DEC);
        Serial.println(F(" to Boom"));       
      }
      else if(RfBuffer[2] == PumpAddress)
      {
        rf22.sendto(RfBuffer, sizeof(RfBuffer), PumpAddress);
        rf22.waitPacketSent();
        Serial.print(F("Relayed from "));
        Serial.print(from, DEC);
        Serial.println(F(" to Pump"));       
      }
      else if(RfBuffer[2] == RelayAddress)
      {
        ClearBuffer();
        uint8_t x = random(250);
        RfBuffer[0] = '<';
        RfBuffer[1] = 'A';
        RfBuffer[2] = from;
        RfBuffer[3] = 2;
        RfBuffer[4] = x;
        RfBuffer[5] = '>';
        rf22.sendto(RfBuffer, sizeof(RfBuffer), from);
        rf22.waitPacketSent();
        Serial.print(F("Ping replied to: "));
        Serial.println(from, DEC );       
      }
      Serial.println(F("--------"));
      ErrorCount = 0;     
    }
  }
  else
  {
    Serial.println(F("recv failed"));
    ErrorCount ++;
    if (ErrorCount > 4)
    {
      Serial.println(F("Re initialising"));
      ErrorCount = 0;
      //InitialiseRF();
      //delay(3000);
    }
  }
}

int ClearBuffer()
{
  for (int i = 0; i < BUFFERLENGTH; i++)
  {
    RfBuffer[i]= 0;
  }
}

void InitialiseRF()
{
  if (!rf22.init())
    Serial.println(F("RF22 init failed"));
  else
  {
    rf22.setModemConfig(RF22::GFSK_Rb2_4Fd36);
    rf22.setTxPower(RF22_TXPOW_20DBM);
  }
}

void test_rssi()
{
  rf22.setModeRx();
  uint8_t rssi = rf22.lastRssi();
  Serial.print(F("RSSI"));
  Serial.print(F(": "));
  Serial.println(rssi, DEC);
  rf22.setModeIdle();
}


nickgammon

I can't see anything obviously wrong. All I can suggest is looking at the serial prints and work out in what part of the code it is hanging.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Coding Badly

I am running a 328 using the internal oscillator at 8 Mhz dividing down to 1 Mhz as I am using a 3.3v power source.


That is not necessary.  3.3 volts / 8 MHz is well within specifications.

Quote
I know that to use serial (UART) communications with this setup, I have to use a slow baud rate due to the inaccuracy of the internal clock.


Slow baud rates don't necessarily help.  You're exchanging thin badly timed bits for fat badly timed bits.  They're just as badly timed no matter how wide they are.

For a given temperature / voltage the internal oscillator can be tuned to within 1% which is more than good enough for serial communications.

lemming

Thanks guys.

I will try a few more Serial.print commands hoping that they are not what causes the problem. (Come to think of it, I will try it without Serial.prints to check this theory).

@ Coding Badly
The unit will be solar powered on a hilltop so may experience 0 - 50 degrees Centigrade inside its enclosure depending on the time of day and angle of the sun, so I would have trouble tuning it to a specific temperature. I might resort to a crystal after all, even though they use a bit more power.

Thanks for the heads up on the serial speed. I have been on the wrong track there.

What is the purpose of the clock divisor?
What is the difference between 8 Mhz divided by 8 and using 1 Mhz without divisor?


Coding Badly

#6
Apr 29, 2012, 09:12 am Last Edit: Apr 29, 2012, 09:26 am by Coding Badly Reason: 1
The unit will be solar powered on a hilltop so may experience 0 - 50 degrees Centigrade inside its enclosure depending on the time of day and angle of the sun, so I would have trouble tuning it to a specific temperature.


Let's check the datasheet and see how the processor will do (I assume the processor is actually a 328► P ◄)...

Figure 29-367.ATmega328P: Calibrated 8 MHz RC Oscillator Frequency vs. Temperature

I'll use the 3.0 V curve (the curve shapes are the same and we're only interested in the range not the actual values).  Zero is about 7.875 MHz.  50 is about 8.075 MHz.  That gives us a range of 0.2 MHz.  If you tune for 25 degrees and the tuning gets the processor within 1% of 8 MHz the full range is 7.82 MHz to 8.18 MHz or ±2.25%.  A good rule of thumb is ±4% for serial communications.  Should work well (no guarantee from the management  ;)).

Quote
I might resort to a crystal after all, even though they use a bit more power.


If the glove doesn't fit, you must acquit.  Oh wait.  Wrong catch-phrase.  When in doubt, get the crystal out.  That's better.

Quote
What is the purpose of the clock divisor?


No matter what is providing a clock signal (internal oscillator, crystal, resonator, watchdog oscillator) it can be divided into a lower frequency by changing the clock divisor.  In the Arduino world, the divisor is generally only used to reduce the internal oscillator from 8 MHz to 1 MHz.  Note: Processors ship from the factory with that configuration (internal divided by 8​).

Quote
What is the difference between 8 Mhz divided by 8 and using 1 Mhz without divisor?


They are one and the same.  The internal oscillator only runs at 8 MHz.  By applying a divisor that frequency can be reduced to 1 MHz (eight is not the only divisor; there 9 total divisor values available).

skyjumper

Keep in mind one of the very important points Coding Badly made, which is that the error rate of your serial communications is determined by the relationship between the clock speed and the baud rate. Slower is not necessarily less error prone at all. The data sheet lists these tolerances, but I did this math for a boot loader I was working on, and it turned out that for an AVR clocked at 16 MHz, 38,400 was the least error prone baud rate. It was much better than the slower and faster rates.

If the temp variation causes a 4% change in clock frequency, and you are using a baud rate with say a 3% error at 1 MHz, I think you're going to have a problem.

@Coding Badly, why do you feel he is better off without a crystal?


Coding Badly

@Coding Badly, why do you feel he is better off without a crystal?


I don't have enough information to have an opinion one way or the other.  Which is why my post is ambiguous...  Should work well (no guarantee from the management  smiley-wink).  ---versus--- When in doubt, get the crystal out.

Using an external crystal does require more power.  If low-power is paramount then running from the internal oscillator may be worth the risk.  If reliable serial communications is paramount then running from an external crystal may be worth the extra power consumption.  Only @lemming can make that decision.


Quote
If the temp variation causes a 4% change in clock frequency, and you are using a baud rate with say a 3% error at 1 MHz, I think you're going to have a problem.


That's unlikely.  Hardware based serial communications has a high probability of working up to ±10% total clock error.  The ±4% I recommend provides a nice cushion.

In addition, if @lemming has control of both sides he can use a zero-error baud rate.

skyjumper

Well I absolutely agree that he should use a 0% error baud rate if he can. And he can use this web page to help him pick one:

http://www.wormfood.net/avrbaudcalc.php?postbitrate=1200&postclock=1&bit_rate_table=on

But a 10% error I think, if I recall correctly and maybe I don't, is well outside Atmel's spec. I thought that spec was well under 5%. I suppose I should go check the data sheet now...

And BTW, I didn't mean to imply anything by asking why you seemed to suggest he not use a crystal. I was just curious. I don't know what his power budget is of course.


Coding Badly


You're right.  ±5% is a reasonable practical limit.  Beyond that it becomes increasingly difficult to differentiate between the last bit and the stop bit.  Guess I shouldn't believe everything I read on the internet.  ;)   (or I should do a better job of remembering the details)

Go Up