I want a 108kHz square wave output with a 50% duty cycle on an ATTiny85 with code.
I read very many posts, searched the internet, asked ChatGPT, .... but I still don't have any idea how that works, if the ATTiny85 is capable to do that, if I need an external crystal, etc. etc. - some source say "no problem", other sources say "isn't possible".
Maybe you can bring some light into the darkness! Thank you very much!
/*
Program to generate (about) 108 kHz rectangle signal with ATTiny 85
2023-08-21
ec2021
Forum: https://forum.arduino.cc/t/attiny85-108khz-square-wave-50-duty-cycle/1160510/4
*/
// outPin defines the output pin of the signal
constexpr byte outPin = PB0;
// state holds the actual state the output shall have (HIGH or LOW)
// and it starts with LOW
byte state = LOW;
void setup() {
// set outPin to OUTPUT mode
pinMode(outPin, OUTPUT);
}
void loop() {
// write state (which is toggling between LOW and HIGH) to the output pin
digitalWrite(outPin,state);
// keep the state for 5 microseconds and the time it takes
// for one single "no operation" microcode of the ATtiny 85
delayMicroseconds(5);
asm volatile("nop\n\t");
// invert state (if it was LOW -> HIGH, if HIGH -> LOW)
state = !state;
}
So the code is only switching the state of the output pin to HIGH (or LOW), waits a given time and then inverts the signal.
A signal of 108 kHz has a periode of 1/108000 s = 9.259 microseconds. The time from digitalWrite() to digitalWrite() is therefore about 4.6 microseconds.
I played around with the delayMicroseconds() until I was close to it (5 µs -> 110 kHz) .Adding a variable (like int count;) and doing a count++; in loop() took too long. So I finally added a "no operation" to finetune the result.
So it was trial and error (which is not bad if done systematically).
Hope you have an oscilloscope at hand: It might be that you have to finetune also depending on the brand/oscillator your device has.
Not sure what clock frequency your ATtiny was running on, but a 16Mhz Arduino (Uno) has AFAIK a delayMicrosecond resolution (steps) of 4us. So no difference if you use 3us or 5us.
Leo..
I went into this because I was curious what an ATtiny85 would be able to do just with some lines of code and simple digitalWrite() and was surprised ...
With my ATtiny board (which a colleague donated me years ago without documentation just to test it ) I checked the sketch from above with
4 µs delay -> 138 kHz
5 µs delay -> 109 kHz
6 µs delay -> 90 kHz
(Frequencies rounded)
So it makes a difference (though I would not have expected it when I started).
PB0 is toggled in an interrupt so that it is easy to change the sketch to PB1, PB2, PB3, ...
#include <avr/io.h>
#include <avr/interrupt.h>
void comp_match() {
DDRB |= (1 << PB0); // set PB0 as output
TCCR0A = 0x00;
TCCR0B = 0x00;
TCCR0B |= (1 << CS00); // No prescaling
TCCR0A |= (1 << WGM01); // Toggle mode and compare match mode
OCR0A = 75; // Compare value can be calculated as frequency = Clock / OCR0A
// A value of 75 leads to about 108,6 kHz at a clock rate of 8150000 Hz
TCNT0 = 0;
sei(); // Enabling global interrupt
TIMSK |= (1 << OCIE0A);
}
ISR(TIMER0_COMPA_vect) {
PORTB ^= (1 << PB0); //Toggling port PB0
}
void setup() {
comp_match();
};
void loop() {
}
/*
The expected frequency can be calculated as freq = clock rate / compare value
In this specific case the clock rate is around 8150000 Hz.
(See frequency 81 kHz for compare value 100)
Empirical data measured 2023-08-23 with a noname ATtiny 85 board
compare value Frequency
1..37 -> 217.3 kHz
38 -> 211,5kHz
40 -> 201 kHz
50 -> 162 kHz
60 -> 135 kHz
70 -> 116,3 kHz
75 -> 108,6 kHz
76 -> 107,3 kHz
80 -> 102 kHz
100 -> 81,8 kHz
200 -> 41,1 kHz
255 -> 32,3 kHz
*/
The functionality of the timer registers are explained in detail in the linked source.
The sketch compiles with Arduino IDE 2.1.1.
Again the results may/will differ depending on the specific brand/board/clock rate.
My god. What kind of awesome dude you are! Thank you! I'll definitely try this the days! You can't imagine how far you push my project with all of this!
The timing depends on the clock rate of your board/controller...
In the sketch from post #8 you can influence the timing by changing the value of OCR0A:
OCR0A = 75; // Compare value can be calculated as frequency = Clock / OCR0A
// A value of 75 leads to about 108,6 kHz at a clock rate of 8150000 Hz
With the results you got try 37 or 38 ...
Good luck!
P.S.: In the code from post #2 you can play with
delayMicroseconds(5);
and the line
asm volatile("nop\n\t");
E.g. set the delay to 2 or 3 micros and measure the results. If it is close but still too quick copy and paste the "asm" line. It adds a "No Operation" assembler command. Each "nop" delays the controller a little bit.
It finally depends on what you are doing with the 108 kHz. Signal...
I think it is quite close if we consider how we create it.
What happens if you comment the asm line out? It will become quicker, but how much?
Another try can be to go to 3 micros delay and add further lines with the nop command until you come close to your target. That's "empirical engineering" also called trial and error...
Sorry, just now carefully read your well documented tests ... However you could add lots of nop lines to come closer to 108. No guarantee.
(Take care with ms = milliseconds, we are talking microseconds here!).