So, let's try toggling a pin on/off as fast as possible, and measure how fast it goes. We'll use Pin 13 (the one that 'Blink' uses).
Here's a first, naive, version - this is just 'Blink' but with the comments, and the delay(1000); instructions edited out.
Code: Select all
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LED_BUILTIN, LOW);
}
And that does a complete cycle (high and low) in 5.56 microseconds. Now we try adding the 'shift into high speed mode' described by Ralph in his video. This just means adding one #include and one call to set the clock_prescale in setup():
Code: Select all
#include <avr/power.h>
void setup() {
clock_prescale_set(clock_div_1);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(LED_BUILTIN, LOW);
}
Sure enough, now it runs twice as fast with each complete cycle taking 2.78 microseconds.
But Arduino programmers know that digitalWrite() is slow, and we can do better by writing directly to the PORT registers. Pin 13 is bit 5 of PORTB, so if we're not bothered about any other pins on the same port (Pins 8, 9, 10, 11, 12) we can just write 0x20 to PORTB to set pin 13 high, and 0x00 to set it low. Now we have:
Code: Select all
#include <avr/power.h>
void setup() {
clock_prescale_set(clock_div_1);
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
PORTB = 0x20;
PORTB = 0x00;
}
Now it does a complete cycle in 192 nanoseconds (0.192 microseconds), that's over fourteen times faster, but the waveform is no longer symmetrical, with the 'low' part about five times as long as the high part. That's partly because of the overhead in the Arduino's loop() function - which we can eliminate by using an infinite while() loop inside setup.
Code: Select all
#include <avr/power.h>
void setup() {
clock_prescale_set(clock_div_1);
pinMode(LED_BUILTIN, OUTPUT);
while (true) {
PORTB = 0x20;
PORTB = 0x00;
}
}
void loop() {
}
Now we're down to 96 nanoseconds, but there's still an asymmetry because the processor is basically doing three things:
set the pin high
set the pin low
jump back to the start of the loop
The jump instruction takes time to execute, and that means the low part of the output waveform is still twice as long as the high.
The Atmel Mega has a weird feature, where if you write a '1' to the PIN register when in output mode then it toggles the output to the opposite state. The PIN register is the one that you'd normally read when an I/O pin is in input mode. This is an unusual feature which most other microcontroller families don't share - if it works, it should even out the generated waveform, and because we're only writing to bit 5, the other pins of PORTB, if they were being used for anything, wouldn't be affected. It's also a good test of how compatible the LG chip is with the Atmel one.
Code: Select all
#include <avr/power.h>
void setup() {
clock_prescale_set(clock_div_1);
pinMode(LED_BUILTIN, OUTPUT);
while (true) {
PINB = 0x20;
}
}
void loop() {
}
Sure enough it works. The waveform is even, and each complete cycle now takes slightly longer (128 nanoseconds) - which is to be expected because the microcontroller is now doing two pin change operations and two jumps for each complete cycle, rather than two pin change operations and a single jump.
At 32 MHz, each clock cycle of the microcontroller is just 31.25 nanoseconds, so it looks as though the compiler has optimized our code into just 2 cycles per loop - which would be impossible on an Atmel microcontroller (it takes 2 cycles to write to a port register and 2 cycles to jump). This LGT8F controller is not only running at twice the clock speed, it's also doing port writes and jumps in a single clock cycle! So for a simple program like this, it actually runs FOUR times faster than a regular Arduino chip could do! We can use some assembly code to perform the same function. We can use the processor's SBI (set bit in) instruction, and its rjmp (relative jump) instruction. sbi 0x03, 5 sets bit 5 (and only bit 5) of the register at address 3 (which is PINB).
Code: Select all
#include <avr/power.h>
void setup() {
clock_prescale_set(clock_div_1);
pinMode(LED_BUILTIN, OUTPUT);
asm (
"loop: \n"
"sbi 0x03, 5 \n" // write to bit 5 of PINB - this toggles the output
"rjmp loop \n"
);
}
void loop() {
}
That generates exactly the same output waveform, with a cycle time of 128 nanoseconds.
Conclusion: the chip is definitely running at 32MHz, also executes at least some operations in half as many clock cycles as an Atmel processor, and otherwise shows very good Atmel chip compatibility, so far.
My next post in this thread will be about the Analogue input resolution...