OK. as principle you say "yes".
I tried to find information how the different constants for interrupt-enable-bits
like
OCIE1A); // enable timer compare interrupt
OCIE1B); // enable timer compare interrupt
ICIE1);
etc. are related to the parameter used in the ISR like
TIMER1_OVF_vect
TIMER1_CAPT_vect
TIMER1_ICR1_vect
But I had no succes so far.
I looked up the table in the datasheet from atmel
But this did not help
I googled for tutorials that would explain them all but only found tutorials for
compare match A / compare match B
Maybe my basic code is just not useful for creating the DCC-bitstream as asked from the thread opener
This basic code uses compare match A register for the duty-cycle simply set to half the period
and uses the ICR1-register to define the period / the frequency of the fast-pwm-signal
This code changes between 2 * 58 = 116 µsec = 8.6 kHz and 2 * 100 = 200 µsec = 5 kHz frequency
As long a changing the values for
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
is done in loop() this works
code fast-pwm mode 14 TOP = ICR1
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 13; // onboard-LED uno, mega
constexpr float PWM_bitIsOne = 8620.69;
constexpr float PWM_bitIsZero = 5000.0;
constexpr uint16_t PERIOD_bitIsOne = round(F_CPU / PWM_bitIsOne);
constexpr uint16_t PERIOD_bitIsZero = round(F_CPU / PWM_bitIsZero);
boolean bitIs1 = true;
void setupTimer1() {
// Configure Timer 1 for X kHz PWM on pin 9 = PB1 = OC1A.
DDRB |= _BV(PB1); // set pin as output
TCCR1B = 0; // stop timer
TCCR1A = _BV(COM1A1) // non-inverting PWM on OC1A
| _BV(WGM11); // mode 14: fast PWM, TOP = ICR1
TCCR1B = _BV(WGM12) // ditto
| _BV(WGM13) // ditto
| _BV(CS10); // clock @ F_CPU
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
}
void setup() {
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
setupTimer1();
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 250);
if ( TimePeriodIsOver(MyTestTimer, 5000) ) {
if (bitIs1) {
bitIs1 = false;
cli();
ICR1 = PERIOD_bitIsZero - 1; // period
OCR1A = PERIOD_bitIsZero / 2; // duty cycle 1/2 = 50%
sei();
Serial.print(PWM_bitIsZero);
Serial.println(" kHz");
}
else {
bitIs1 = true;
cli();
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
sei();
Serial.print(PWM_bitIsOne);
Serial.println(" kHz");
}
}
}
// helper-functions
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) );
}
// easy to use helper-function for non-blocking timing
// explanation see here
// Example-code for timing based on millis() easier to understand through the use of example-numbers / avoiding delay()
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
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) );
}
}
All my attempts to change these values at the end of each PWM-cycle failed so far
Is it possible to invoke an interrupt on ICR1-match?
if yes where can I look up the constant name for the parameter used in the ISR
I mean the analogic constant name like
Timer1 COMP1A-register COMP1A-match-vector constant-name "TIMER1_COMPA_vect"
or if this does not work at all with TOP = ICR1
is there a different mode for fast PWM that defines a different register to set the PWM-frequency and can invoke an interrupt?
// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
#define dbgi(myFixedText, variableName,timeInterval) \
{ \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
}
#define dbgc(myFixedText, variableName) \
{ \
static long lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
#define dbgcf(myFixedText, variableName) \
{ \
static float lastState; \
if ( lastState != variableName ){ \
Serial.print( F(#myFixedText " " #variableName" changed from ") ); \
Serial.print(lastState); \
Serial.print( F(" to ") ); \
Serial.println(variableName); \
lastState = variableName; \
} \
}
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *
unsigned long MyTestTimer = 0; // Timer-variables MUST be of type unsigned long
const byte OnBoard_LED = 13; // onboard-LED uno, mega
constexpr float PWM_bitIsOne = 8620.69;//17241.379; // 15e3; // 15 kHz
constexpr float PWM_bitIsZero = 5000.0; // 15e3; // 15 kHz
constexpr uint16_t PERIOD_bitIsOne = round(F_CPU / PWM_bitIsOne);
constexpr uint16_t PERIOD_bitIsZero = round(F_CPU / PWM_bitIsZero);
constexpr uint16_t PERIOD_Diff = PERIOD_bitIsZero - PERIOD_bitIsOne;
volatile boolean bitIs1 = true;
volatile byte ISRCnt = 0;
void setupTimer1() {
// Configure Timer 1 for X kHz PWM on pin 9 = PB1 = OC1A.
DDRB |= _BV(PB1); // set pin as output
TCCR1B = 0; // stop timer
TCCR1A = _BV(COM1A1) // non-inverting PWN on OC1A
| _BV(WGM11); // mode 14: fast PWM, TOP = ICR1
TCCR1B = _BV(WGM12) // ditto
| _BV(WGM13) // ditto
| _BV(CS10); // clock @ F_CPU
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
dbg("TS", PERIOD_bitIsOne);
dbg("TS", PERIOD_bitIsZero);
}
void setup() {
Serial.begin(115200);
Serial.println("Setup-Start");
PrintFileNameDateTime();
setupTimer1();
}
ISR(TIMER1_COMPA_vect) {
ISRCnt++;
if (ISRCnt == 2) {
ISRCnt = 0;
if (bitIs1) {
bitIs1 = false;
ICR1 = PERIOD_bitIsZero - 1; // period
OCR1A = PERIOD_bitIsZero / 2; // duty cycle 1/2 = 50%
}
else {
bitIs1 = true;
ICR1 = PERIOD_bitIsOne - 1; // period
OCR1A = PERIOD_bitIsOne / 2; // duty cycle 1/2 = 50%
}
}
}
void loop() {
BlinkHeartBeatLED(OnBoard_LED, 250);
}
// helper-functions
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__) );
}
// easy to use helper-function for non-blocking timing
// explanation see here
// https://forum.arduino.cc/t/example-code-for-timing-based-on-millis-easier-to-understand-through-the-use-of-example-numbers-avoiding-delay/974017
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - startOfPeriod >= TimePeriod ) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
}
else return false; // actual TimePeriod is NOT yet over
}
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) );
}
}
