Running nano at 8 MHz based on external 16 MHz

Hi!

I have one project based on Arduino nano. I would like to switch to something simpler (minimal possible configuration).
I'm using function to receive data via some software serial (non standard) - it's based on delays and delayMicroseconds.

I'm familiar with MicroCore and burning bootloader.

When i set nano to internal 8 MHz clock, received data is garbage - as expected, since it is quite inaccurate. So i was wondering if i can use quite accurate 16 MHz nano clock and divide it by 2 or even more.

Should i use:

#ifdef F_CPU
#undef F_CPU
#endif

#define F_CPU 8000000L

...

clock_prescale_set (clock_div_2);

Or must I use "build.f_cpu=800000L"?

And when i must use delay_us?

Or maybe internal clock is ok, but there is problem somewhere else?

Best regards!

What is your reason for wanting to run at 8 MHz?

The problem with the first approach is this will only define that macro in the translation unit of the files that contain the definition. So if you do that in the sketch, it's not going to have any effect on the code that uses F_CPU in libraries, which are in their own translation units.

The build.f_cpu platform property defined in boards.txt is used to define the F_CPU macro via a -D compilation flag, which causes it to be visible in all translation units.

Battery usage reduction, parts number reduction - as i was hoping to run from internal 8 MHz. It won't. So i wanted to check if it will run from stable "external". When it start i can check internal. And i will know where the problem is.

Ok, based on this topic: Reduce Clockspeed Arduino UNO - #5 by Capstone18
I've burned 8MHz MiniCore on 16MHz Nano and add modified my sketch. It gives me:

12:49:52.716 -> Prescale 1: 1 s
12:49:52.716 -> .
12:49:53.711 -> .
12:49:55.736 -> L⸮)⸮H
12:49:55.736 -> ⸮⸮⸮y⸮
12:49:57.231 -> Prescale 1: 1 s
12:49:57.231 -> .
12:49:58.260 -> .

My full sketch:

#define LED 2

void setup() {  
  pinMode(LED, OUTPUT);
  pinMode(13, OUTPUT);
  
  Serial.begin(500000);
}

void loop() {

  digitalWrite(13, HIGH);

  clock_prescale(0);
  delay(10);
  Serial.println();
  Serial.println("Prescale 0: 1 s");
  Serial.println(".");
  digitalWrite(LED, HIGH);
  delay1sWithMicroseconds();
  digitalWrite(LED, LOW);
  Serial.println(".");

  delay(2000);

  clock_prescale(1);
  delay(10);
  Serial.println();
  Serial.println("Prescale 1: 1 s");
  Serial.println(".");
  digitalWrite(LED, HIGH);
  delay1sWithMicroseconds();
  digitalWrite(LED, LOW);
  Serial.println(".");

  delay(2000);
}

void delay1sWithMicroseconds(){
  for (int i = 0; i < 100; i++){
    delayMicroseconds(10000);
  }
}

void clock_prescale(uint8_t factor)
{
  if (factor > 8) factor = 8;
  CLKPR = (1 << CLKPCE);
  CLKPR = factor;
}

This seems to be correct, although it is not accurate 1s... I'll check my test program:

#define RXPIN 2
#define TXPIN 3
#define BAUDRATE 9600
#define BITTIME 1000000/BAUDRATE
#define HALFBITTIME 500000/BAUDRATE

void setup() {
  clock_prescale(1);
  
//  Serial.begin(500000);
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("RS Test ok");
  pinMode(RXPIN,INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.write(GPSread());
}

char GPSread()
{
  char rxdata=0;
  while(!digitalRead(RXPIN)); //wait until rxline get high if low
  while(digitalRead(RXPIN)); //wait until rxline goes low
  delayMicroseconds(HALFBITTIME); //wait to get the center of bit time
  
  for(byte i=0;i<8;i++)
  {
    delayMicroseconds(BITTIME);
    rxdata = rxdata | (digitalRead(RXPIN)<<i);
  }
  
  delayMicroseconds(HALFBITTIME); //wait till the stop bit time begins
  return rxdata;
}

void clock_prescale(uint8_t factor)
{
  if (factor > 8) factor = 8;
  CLKPR = (1 << CLKPCE);
  CLKPR = factor;
}

but this does not work for 8MHz. Also, it works when clock_prescale(0) and burned bootloader is 16 MHz (so standard Nano). Whats going on?

Also "RS Test ok" shows correctly.

It is not completely clear what you are trying to achieve, but here is some information about running a system at a "non-natural" clock speed : Is there a simple way to reduce clock speed on an UNO?

Lower clock rates are often necessary for battery operation ( say 3.3volts). If you use a Nano on 3.3 volts and retain the 16MHz resonator/crystal, you can fix the CPU rate in a special boards.txt entry and set the pre-scaler early in setup.

The internal 8MHz oscillator may not be very accurate.

For long term battery operation, a Nano is not ideal because it has lots of power hungry components. But 3.3 volt operation can also be useful if you have peripherals which operate only at this voltage. I did once a Nano 3.3volt conversion for exactly this reason: Arduino Nano CH340G 3.3 volt conversion

Can you use software serial in your sketch to interpret the data stream from your GPS device instead of what appears to be a direct digital interpretation of the RX pin ?

It is not completely clear what you are trying to achieve, but here is some information about running a system at a "non-natural" clock speed : Is there a simple way to reduce clock speed on a UNO?

Ok, short story long: I'm building a GPS logger. The goal is to build a simple device that can track for a few days without charging. It is built of FLASH, GPS, NANO, and two 18650.
I was hoping to replace NANO with barebone 8A (got a few). I know Its power-saving functions look bad when compared to 328p, so I'll probably go with 168/328p. Eighter I was hoping to use an internal 8MHz clock (or even slower, since in general CPU just waits for pieces of information from GPS, writes down, and goes sleep) to reduce power usage.
Two 18650 gives 7.2-8.4V. In the current build, it is just burned in voltage regulators (NANO is 5V, GPS is 1.8V, Flash 2.7v) - that's just waste, so I wanted to clock down 328p to use two 18650 in parallel.

So, my idea was to change things step by step:
nano -> nano with internal 8MHz -> barebone

But, since the first conversion didn't work I've decided to try Nano with a stable clock, that's why I'm trying to run nano from 16MHz with a clock divider.

I've read this topic earlier, and I was inspired by it to my solution. Isn't it what I'm doing currently? Burn bootloader and program 16MHz Nano as if it was 8MHz and add divider as soon as possible.

I can do anything! :wink:
Do You mean to pass incoming data bit by bit to PC?

That appears OK in principle. A Nano is not ideal for this though. You are better off with a simpler design such as a barebones or a pro-mini which, depending on the clone, may have solder jumpers to isolate the power led, voltage regulator etc.

I have a project published here : Arduino NRF24L01 Mailbox Monitor/Notifier which is a barebones, but with a 16MHz crystal which is convenient for loading the standard bootloader, but is pre-scaled down, in normal use, to 4MHz so that it can run on batteries down to 1.8 volt (just look at the transmitter part).

What I meant was this:

Which appears to be some sort of manual interpretation of a serial bit stream.

add this before anything else in setup() and I think you should be fime

	// change system clock pre-scaler to 2, run CPU at 8 MHz from 16MHz crystal
	CLKPR = 1<<CLKPCE;
	CLKPR = 0<<CLKPCE | 0<<CLKPS3 | 0<<CLKPS2 | 0<<CLKPS1 | 1<<CLKPS0;
#define RXPIN 2
#define TXPIN 3
#define BAUDRATE 9600
#define BITTIME 1000000/BAUDRATE
#define HALFBITTIME 500000/BAUDRATE

#define LED 9

unsigned long tajm = 0;

void setup() {
  //  clock_prescale(1);
  
  // change system clock pre-scaler to 2, run CPU at 8 MHz from 16MHz crystal
  CLKPR = 1<<CLKPCE;
  CLKPR = 0<<CLKPCE | 0<<CLKPS3 | 0<<CLKPS2 | 0<<CLKPS1 | 1<<CLKPS0;
  
  Serial.begin(500000);
  //Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println("RS Test ok");
  pinMode(RXPIN,INPUT);
  pinMode(LED, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.write(GPSread());
  if (millis() > tajm){
    digitalWrite(LED, !digitalRead(LED));
    tajm += 1000;
  }
}

char GPSread()
{
  char rxdata=0;
  while(!digitalRead(RXPIN)); //wait until rxline get high if low
  while(digitalRead(RXPIN)); //wait until rxline goes low
  delayMicroseconds(HALFBITTIME); //wait to get the center of bit time
  
  for(byte i=0;i<8;i++)
  {
    delayMicroseconds(BITTIME);
    rxdata = rxdata | (digitalRead(RXPIN)<<i);
  }
  
  delayMicroseconds(HALFBITTIME); //wait till the stop bit time begins
  return rxdata;
}

void clock_prescale(uint8_t factor)
{
  if (factor > 8) factor = 8;
  CLKPR = (1 << CLKPCE);
  CLKPR = factor;
}

Gives me garbage like f⸮ff⸮⸮~⸮⸮⸮⸮怘⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮⸮憞⸮⸮x⸮⸮⸮x怞`. Also, "RS test ok" doesn't work anymore.

Me no get it.

OK. If that part works, then leave it or treat it as a potential optimisation to look at in the future.
"Never touch a running system" comes to mind here.

This appears rather odd:

With

  // change system clock pre-scaler to 2, run CPU at 8 MHz from 16MHz crystal
  CLKPR = 1<<CLKPCE;
  CLKPR = 1<<CLKPCE | 0<<CLKPS3 | 0<<CLKPS2 | 0<<CLKPS1 | 1<<CLKPS0;

starting message works. With original 0<<CLKPCE in second line LED is on for 2 sec... Passthrough does NOT.

Nah, quite usual speed. On modern devices using 9600 etc has no sense - its pointless slow.
This value was to prevent hardware RS influence software serial read. With 9600 same situation.

But it still does not work!

Isn't it better to set the prescaler correctly from the run of the bootloader and always operate at 8MHz?
It is discussed in this issues.

Serial.begin(500000);

Since you are changing the clock speed from 16MHz to 8MHz in your code, did you remember to set the serial monitor to 1/2 the baud rate - 250,000 in this case.

Will this make any difference except that I could flash with bootloader? I'm now forced to use ICSP.

Definitely incorrect.

To be clear, once again.

On 16MHz passthrough, the sketch DOES work.

Flashing Arduino with program compiled for 8MHz (by setting MiniCore to think this is External 8MHz) and changing prescaler to 2 I get:

  • working hello message: "RS Test ok",
  • garbage on GPS data received.

Also, isn't:

and

Exactly the same?

Only if factor is 1. Look at the datasheet for the ATmega328P:

Edit:

But why not make life a bit simpler and get a device with a native 8MHz clock frequency:

I thought about it. I even have one on my desktop. But I have already built this device (custom PCB with everything nicely attached to battery holder). I wanted to test it before moving to barebone 8a/168/328p.
I've already decided to build it anyway - as finding a solution seems to be too much... but I'll reprogram it to use hardware serial for both: PC and GPS communication.
Arduino sends data to PC only at boot, I'll use N-MOSFET to disable GPS until everything is sent. No data is meant to be sent from PC to Arduino.