280 Hz is pretty slow. Even for an Arduino nano.
So here is a different approach:
This code configures a timer-interrupt to run at 16800Hz which is 60 * 280
You need dutycycles 50% = 1/2 80% = 4/5 and 25% = 1/4
This means the timer-interrupt counts from 0 up to 60
If counter reaches fraction 1/2 or 4/5 or 1/4 switch pulse to LOW
for a basenumber of 60 the fractions are
30/60 = 1/2 = 50%
48/60 = 4/5 = 80%
15/60 = 1/4 = 25%
If 10 pulses are created switch to next case of the switch-case-break-statement with the next dutycycle.
The counter-values where switching from HIGH to LOW are calculated in the code.
I measured the frequency with my digital storage oscilloscope (DSO).
The frequency is 280,395 Hz Which is pretty close to 280,00Hz
The small deviation is due to the fact that the timer works only with binary numbers as dividers with which you can't create a continuum of exact frequencies like
279,5Hz 279,6 Hz... 280,0 Hz, 280,1 Hz ....
instead there is a certain granularity in the frequencies.
The code has a function that automatically chooses the right values for the registers for a given frequency
The PWM iscreated by a switch-case-break-statement and counter-variables
This creates an infinite pulsetrain of
at 280,395 Hz
// this demo-code belongs to the public domain
// this code demonstrates how to blink the onboard-LED
// of an Arduino-Uno (connected to IO-pin 13)
// in a nonblocking way using timing based on function millis()
// and creating a pwm-signal "in the backround"
// through setting up a timer-interrupt through configuring timer2
// A T T E N T I O N ! R E M A R K
// some other libraries that make use of timer2 may conflict with this
// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope
#define dbgi(myFixedText, variableName,timeInterval) \
do { \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
} while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi
void PrintFileNameDateTime() {
Serial.println( F("Code running comes from file ") );
Serial.println( F(__FILE__));
Serial.print( F(" compiled ") );
Serial.print(F(__DATE__));
Serial.print( F(" ") );
Serial.println(F(__TIME__));
}
boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - periodStartTime >= TimePeriod )
{
periodStartTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
unsigned long MyTestTimer = 0; // variables MUST be of type unsigned long
const byte OnBoard_LED = 13;
void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
static unsigned long MyBlinkTimer;
pinMode(IO_Pin, OUTPUT);
if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
}
}
const unsigned long pwmBaseFrequency = 16800;
const unsigned long pulseFreq = 280;
volatile unsigned long pwmCounter = 0;
volatile unsigned long periodCount = pwmBaseFrequency / pulseFreq;
volatile unsigned long duty80 = (periodCount * 4) / 5;
volatile unsigned long duty25 = (periodCount * 1) / 4;
volatile unsigned long duty50 = (periodCount * 1) / 2 ;
void setupTimerInterrupt(unsigned long ISR_call_frequency) {
const byte Prescaler___8 = (1 << CS21);
const byte Prescaler__32 = (1 << CS21) + (1 << CS20);
const byte Prescaler__64 = (1 << CS22);
const byte Prescaler_128 = (1 << CS22) + (1 << CS20);
const byte Prescaler_256 = (1 << CS22) + (1 << CS21);
const byte Prescaler1024 = (1 << CS22) + (1 << CS21) + (1 << CS20);
const unsigned long CPU_Clock = 16000000;
const byte toggleFactor = 1;
unsigned long OCR2A_value;
cli();//stop interrupts
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
TCCR2A |= (1 << WGM21); // turn on CTC mode
TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt
// the prescaler must be setup to a value that the calculation
// of the value for OCR2A is below 256
TCCR2B = Prescaler___8;
OCR2A_value = (CPU_Clock / ( 8 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("1 setup: timer ", OCR2A_value);
if (OCR2A_value > 256) { // if value too big
TCCR2B = Prescaler__32; // set higher prescaler
OCR2A_value = (CPU_Clock / ( 32 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("setup: prescaler 32", OCR2A_value);
}
if (OCR2A_value > 256) { // if value too big
TCCR2B = Prescaler__64;// set higher prescaler
OCR2A_value = (CPU_Clock / ( 64 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("setup: prescaler 64", OCR2A_value);
}
if (OCR2A_value > 256) { // if value too big
TCCR2B = Prescaler_128;// set higher prescaler
OCR2A_value = (CPU_Clock / ( 128 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("setup: prescaler 128", OCR2A_value);
}
if (OCR2A_value > 256) { // if value too big
TCCR2B = Prescaler_256; // set higher prescaler
OCR2A_value = (CPU_Clock / ( 256 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("setup: prescaler 256", OCR2A_value);
}
if (OCR2A_value > 256) { // if value too big
TCCR2B = Prescaler1024; // set higher prescaler
OCR2A_value = (CPU_Clock / ( 1024 * ISR_call_frequency * toggleFactor) ) - 1;
dbg("setup: prescaler 1024", OCR2A_value);
}
OCR2A = OCR2A_value; // finally set the value of OCR2A
sei();//allow interrupts
dbg("setup: timer done", OCR2A_value);
dbg("setup",periodCount);
dbg("setup",duty50);
dbg("setup",duty80);
dbg("setup",duty25);
}
unsigned long pwmCount = 0;
const byte PWM_Pin = 4;
const byte Clock_Pin = 5;
void setup() {
Serial.begin(115200);
Serial.println( F("Setup-Start") );
PrintFileNameDateTime();
pinMode(PWM_Pin, OUTPUT);
pinMode(Clock_Pin, OUTPUT);
setupTimerInterrupt(pwmBaseFrequency);
pwmCount = 0;
}
const byte sc_50percentDuty = 1;
const byte sc_80percentDuty = 2;
const byte sc_25percentDuty = 3;
byte dutyState = sc_50percentDuty;
byte pulseCount = 0;
volatile byte pwmState;
void flap() {
pwmCount++;
digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );
if (pwmCount == periodCount) {
pwmCount = 0;
digitalWrite(PWM_Pin, !digitalRead(PWM_Pin) );
}
switch (dutyState) {
case sc_50percentDuty:
if (pwmCount == duty50) { // if end of HIGH-time reached
digitalWrite(PWM_Pin, LOW); // set LOW
pulseCount++;
if (pulseCount == 11) {
pulseCount = 0;
dutyState = sc_80percentDuty;
}
}
break;
case sc_80percentDuty:
if (pwmCount == duty80) { // if end of HIGH-time reached
digitalWrite(PWM_Pin, LOW); // set LOW
pulseCount++;
if (pulseCount == 11) {
pulseCount = 0;
dutyState = sc_25percentDuty;
}
}
break;
case sc_25percentDuty:
if (pwmCount == duty25) { // if end of HIGH-time reached
digitalWrite(PWM_Pin, LOW); // set LOW
pulseCount++;
if (pulseCount == 10) {
pulseCount = 0;
dutyState = sc_50percentDuty;
}
}
break;
}
}
ISR(TIMER2_COMPA_vect) {
flap();
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 200);
}
best regards Stefan