Using standard analog frequency generators, usually you have a range selector, a coarse setting and fine setting knobs to adjust the exact frequency. Using microprocessors, you can only obtain multiples of the clock cycle as timing factor for the resulting frequency. This limits not only the maximum obtainable frequency but also the steps between two adjacent frequencies.
The picture shows on the top an Arduino-UNO-R3 clone that is equipped with buttons and extra LEDs. Below you see an Arduino-UNO-R4 connected to a breadboard with three buttons and a TTL-IC to deliver a higher output that the R4 can give.
In both installations, the red button is connected to Pin-4 and decreases the frequency by one step, the yellow button is connected to Pin-3 and will set the frequency to a centre value, and the green button is connected to Pin-2 and increases the frequency by one step.
The output comes from Pin-9. Using the R3, a complementary square wave is available at Pin-10. To achieve this with the R4, you need a TTL inverter.
In the example that is shown below, I decided to set the centre frequency to 17200 Hertz. At this frequency, a radio station is broadcasting only twice a year. In order to prepare for the reception of that radio station it can be useful to generate that exact frequency yourself.
The code will work for both types of UNOs, R3 and R4.
/*
generate frequency as precise as possible
should work with UNO-R3 & UNO-R4
set to initial frequency of 17200 Hz.
Using UNO R3 ATmega328P complementry
outputs at pin-9 and pin-10.
Steps: 32 Hz
Using UNO R4 you will need to connect an
inverter at pin-9 to get complementary outputs
steps: 6 Hz
see also:
https://docs.arduino.cc/tutorials/generic/secrets-of-arduino-pwm/
*/
enum key { NONE, DOWN, CENTER, UP };
const byte OUTPUT_PIN1 = 9; // OC1A
const byte OUTPUT_PIN2 = 10; // OC1B
const byte DOWN_KEY = 4;
const byte CENTER_KEY = 3;
const byte UP_KEY = 2;
const float F0 = 17200; // SAQ
#if defined(ARDUINO_ARCH_RENESAS)
#include <pwm.h>
PwmOut PWM_PIN(OUTPUT_PIN1);
const int Q0 = F_CPU / F0;
#elif defined(__AVR__);
const int Q0 = F_CPU / (F0 * 2);
#endif
int q = Q0;
void setup() {
long t = millis() + 2000;
Serial.begin(9600);
while (!Serial || (millis() < t));
Serial.println(__FILE__);
Serial.println(Q0);
pinMode(DOWN_KEY, INPUT_PULLUP);
pinMode(CENTER_KEY, INPUT_PULLUP);
pinMode(UP_KEY, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(OUTPUT_PIN1, OUTPUT);
pinMode(OUTPUT_PIN2, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
#if defined(__AVR__)
// mode = 8, prescaler = 1:
TCCR1A = B10110000;
TCCR1B = B00010001;
#endif
setFreq(CENTER);
}
void loop() {
key keys = getKeys();
if (keys == NONE) return;
wait4release();
setFreq(keys);
}
void setFreq(key k) {
switch (k) {
case DOWN: q++; break;
case CENTER: q = Q0; break;
case UP: q--;
}
Serial.print("q = ");
Serial.print(q);
Serial.print(" f = ");
#if defined(__AVR__)
Serial.print(F_CPU / (q * 2));
ICR1 = q;
OCR1A = q / 2;
OCR1B = q / 2;
#elif defined(ARDUINO_ARCH_RENESAS)
Serial.print(F_CPU / q);
PWM_PIN.end();
PWM_PIN.begin(F_CPU / q, 50.0f);
#endif
Serial.println(" Hz");
}
key getKeys() {
if (digitalRead(DOWN_KEY) == LOW) return DOWN;
if (digitalRead(CENTER_KEY) == LOW) return CENTER;
if (digitalRead(UP_KEY) == LOW) return UP;
return NONE;
}
void wait4release() {
key b;
do {
b = getKeys();
delay(10);
}
while (b != NONE);
}
The cover image shows an ARDUINO UNO R4 (could be either MINIMA or WiFi) connected to a 16-keys keyboard equipped with an 8-digit LED display. The connection is very simple: at the ICSP header you have all the pins you need.
The "=" acts as a RETURN key, setting the frequency entered by the digit keys, the "divide" key removes the last digit entered, and the plus and minus keys work as expected, at least at frequencies lower than the square root of the frequency, the microcontroller is clocked by. At higher frequencies larger steps will be performed.
/*
Rechteck-Generator
für 1 Hz bis 24 MHz
fuer ARDUINO UNO R4 Minima und WiFi
Tasten auf dem QYF-TM1638 Keyboard:
0 - 9
":" ---> loesche letztes Zeichen
"=" ---> setze diese Frequenz
"+" ---> erhoehe Frequenz um 1
"-" ---> erniedrige Frequenz um 1
*/
#include <pwm.h>
int ch = 3;
long freq, oldFreq;
PwmOut pwm(ch);
#include <TM1638QYF.h>
const byte PIN_STROBE = 11;
const byte PIN_DATA = 12;
const byte PIN_CLOCK = 13;
TM1638QYF module(PIN_DATA, PIN_CLOCK, PIN_STROBE);
int led_tx = -1;
void setup() {
#ifdef ARDUINO_UNO_R4_MINIMA
led_tx = LED_TX;
// nicht fuer WiFi
#endif
pinMode(led_tx, OUTPUT);
boolean state;
long t = millis() + 2000;
Serial.begin(9600);
while (!Serial && (millis() < t)) {
digitalWrite(led_tx, state);
state = !state;
delay(200);
}
digitalWrite(led_tx, LOW);
pinMode(led_tx, INPUT);
Serial.println("\n" __FILE__);
module.setupDisplay(true, 7);
module.setDisplayToString("FrEq-GEN");
}
void loop() {
char key = getKey();
// wenn KEINE Taste: zurueck
if (!key) return;
// wenn TASTE, dann warte bis Taste losgelassen
do {} while (getKey());
switch (key) {
case '0' ... '9':
Serial.print(key);
freq = freq * 10 + key - '0';
ledPrint(freq);
return;
case '/': freq = freq / 10;
ledPrint(freq);
return;
case '+':
freq = getNewFreq(oldFreq, 1);
setFreq(freq, true);
break;
case '-':
freq = getNewFreq(oldFreq, -1);
setFreq(freq, true);
break;
case '=':
if (freq != 0)
setFreq(freq, false);
return;
}
}
/*
wo ist die Grenze, bei der f1 != f2 ist?
F_CPU F_CPU
----- - ----- = 1
div div+1
F_CPU*(div+1)-F_CPU*div=div*(div+1)
Quadratische Gleichung nach div auflösen:
div =-1/2+-SQRT(1/4+F_CPU)
*/
const int lim = sqrt(F_CPU);
int getNewFreq(int alt, int inc) {
if (alt < lim) return alt + inc;
int divisor = F_CPU / alt - inc;
return F_CPU / divisor;
}
void setFreq(long f, boolean print) {
if (f > 24000000) return;
ledPrint(f);
pwm.end();
pwm.begin(f, 50.0f);
if (print) Serial.println(f);
else Serial.println(" Hz");
oldFreq = freq;
freq = 0;
}
void ledPrint(long n) {
char c[9];
ltoa(freq, c, 10);
strcat(c, " HZ");
module.setDisplayToString(c);
}
char getKey() {
word buttons = module.getButtons();
char myKeys[] = "789/456x123-0.=+";
if (buttons) {
for (word i = 0, b = 1; b; i++, b = b * 2) {
if (buttons == b) return myKeys[i];
}
}
return 0;
}
Comments