Generating 77.5 kHz

In order to better test my dcf77 library DCF77 Library | Blinkenlight under realistic conditions I want to implement a simple signal emulator. First step is to create a 77 500 Hz carrier. I tried with the code below. I already verified that the timing is correct (by adding digitalWrite + analysis with DSO). However I fail to get any signal on pin 3 (PD3, OC2B).

Obviously something with my timer2 configuration must be wrong. But what? Has anyone any hints for me?

// 16000000 / 77500 = 206 + 70/155 = 206 + 14/31
const uint8_t ticks_per_period = 206;
// fractional ticks
const uint8_t nominator = 14;
const uint8_t denominator = 31;

const uint8_t pwm_full_carrier      = ticks_per_period / 2;  // ~50  %   duty cycle
const uint8_t pwm_modulated_carrier = ticks_per_period / 8;  // ~12.5% duty cycle


ISR(TIMER2_COMPA_vect) {
    static uint8_t accumulated_fractional_ticks;

    accumulated_fractional_ticks += nominator;
    if (accumulated_fractional_ticks < denominator) {
        OCR2A = ticks_per_period - 1;
    } else {
        OCR2A = ticks_per_period;
        accumulated_fractional_ticks -= denominator;
    }
}

void setup_timer_0() {
    // disable timer 0 interrupts
    TIMSK0 = 0;
}

void setup_timer_1() {
    // disable timer1 interrupts
    TIMSK1 = 0;

    // do not toggle or change timer IO pins
    TCCR1A = 0;       

    // (16000000 / 256) - 1
    OCR1A = 62500 - 1;
    
    // Mode 4, CTC using OCR1A | set prescaler to 256
    TCCR1B = (1<<WGM12) | (1<<CS12);
}

void setup_timer_2() {
     // disable timer2 interrupts during setup
    TIMSK2 = 0;

    // enable OC2B pin for output (digital pin 3)
    DDRD |= 1<<3;

    // The fast Pulse Width Modulation or fast PWM mode (WGM22:0 = 3 or 7) provides a high fre-
    // quency PWM waveform generation option. The fast PWM differs from the other PWM option by
    // its single-slope operation. The counter counts from BOTTOM to TOP then restarts from BOT-
    // TOM. TOP is defined as 0xFF when WGM2:0 = 3, and OCR2A when MGM2:0 = 7. In non-
    // inverting Compare Output mode, the Output Compare (OC2x) is cleared on the compare match
    // between TCNT2 and OCR2x, and set at BOTTOM. In inverting Compare Output mode, the out-
    // put is set on compare match and cleared at BOTTOM.


    TCCR2A = 1<< WGM20 || 1<<WGM21 || // Fast PWM
             1<<COM2B1;               // Clear OC2B on Compare Match

    TCCR2B = 1<<CS20 || // no Prescaler      
             1<<WGM22;  // Fast PWM

    OCR2A = ticks_per_period - 1;  // period length

    OCR2B = pwm_full_carrier;  // duty cycle
    
    // enable match interrupts
    TIMSK2 = 1<<OCIE2A;
}

void setup() {
    setup_timer_0();
    setup_timer_1();
    setup_timer_2();
}

void modulate(const uint8_t duration) {
}

void loop() {
}

You says:

However I fail to get any signal on pin 3 (PD3, OC2B).

But DS says:

The Output Compare Register A contains an 8-bit value that is continuously compared with the
counter value (TCNT2). A match can be used to generate an Output Compare interrupt, or to
generate a waveform output on the OC2A pin.

You have to declare pin 3 as an output to enable the timer hardware to put a PWM signal on it.

• Bits 5:4 – COM2B1:0: Compare Match Output B Mode
These bits control the Output Compare pin (OC2B) behavior. If one or both of the COM2B1:0 bits are set, the
OC2B output overrides the normal port functionality of the I/O pin it is connected to. However, note that the Data
Direction Register (DDR) bit corresponding to the OC2B pin must be set in order to enable the output driver.

You have to declare pin 3 as an output to enable the timer hardware to put a PWM signal on it.

I was thinking same, but OP set pin as output, check the code

Edited:
Yes, now I see, he is also setting OCR2B. So my comments above be disregard.

Other things I could think, as there is adaptation math to change freq. period, probably, Timer2 is driven up above 150 kHz, and stuck forewer in the ISR. Try to comments out freq. variation lines

I cannot find an error in your timer 2 setup. I would check if pin 3 is still working by applying a standard PWM to it (using analogWrite(3, 128)) and measuring with the scope.
You set up timer 1 to a 1 second interval but you don't use that. Is that intended for later use?

Timer 1 is intended for later use. I already verified and confirmed that pin 3 is working as it should. There must be something wrong with my setup but I still have no idea what it might be.

I added digitalWrite calls to toggle Pin 5 explicitly into the ISR. Then I had a look with my DSO. --> It is not stuck in the ISR.

-Udo

My suggestion, use serial whenever doubts in configuration, print out all TIMER registers content in HEX and verify bit by bit if all right.
In my experience, subtraction from unsigned variable never works correctly on arduino (drawback of optimization), in your case you may be surprised getting out from OCR2A/B not what you expect to see.

My suggestion, use serial whenever doubts in configuration, print out all TIMER registers content in HEX and verify bit by bit if all right.

Not a good idea in an ISR. Never use methods of the Serial object in interrupt context, you might produce a dead lock.

In my experience, subtraction from unsigned variable never works correctly on arduino (drawback of optimization), in your case you may be surprised getting out from OCR2A/B not what you expect to see.

Subtraction works perfectly with unsigned variables it's just a bit different from the signed variants. If you how overflow and underflow works, this is absolutely no problem. But there is only one subtraction of unsigned variables and there the underflow is avoided by the if-clause.

@OP: Have you tried to deactivate the interrupt, just to see if the pin starts putting out PWM? Maybe there's some strange relation in hardware between the interrupt and the PWM output. I would try to start with the simple situations and add one feature after the other until it breaks. So we know at least which feature is responsible for the missing output.

Not a good idea in an ISR. Never use methods of the Serial object in interrupt context, you might produce a dead lock.

I haven't say in the ISR.

Subtraction works perfectly with unsigned variables it's just a bit different from the signed variants.

And you are mister avr-gcc compiler expert?

In my experience, subtraction from unsigned variable never works correctly on arduino (drawback of optimization), in your case you may be surprised getting out from OCR2A/B not what you expect to see.

It has always worked perfectly for me. Could you post an example of how it fails?

I find a problem in original code. Here what were written in the registers:

TCCR2A = 1
TCCR2B = 1
OCR2A = CE
OCR2A DEC = 206
OCR2B = 67
OCR2B DEC = 103
TIMSK2 = 2

Here correct values:

TCCR2A = 23
TCCR2B = 9
OCR2A = CE
OCR2A DEC = 206
OCR2B = 67
OCR2B DEC = 103
TIMSK2 = 2

This time the problem was not related to subtraction from unsigned, just wrong syntaxis . Correct version - working like a charm:

// 16000000 / 77500 = 206 + 70/155 = 206 + 14/31
const uint8_t ticks_per_period = 206;
// fractional ticks
const uint8_t nominator = 14;
const uint8_t denominator = 31;

const uint8_t pwm_full_carrier      = ticks_per_period / 2;  // ~50  %   duty cycle
const uint8_t pwm_modulated_carrier = ticks_per_period / 8;  // ~12.5% duty cycle


ISR(TIMER2_COMPA_vect) {
    static uint8_t accumulated_fractional_ticks;

    accumulated_fractional_ticks += nominator;
    if (accumulated_fractional_ticks < denominator) {
        OCR2A = ticks_per_period - 1;
    } else {
        OCR2A = ticks_per_period;
        accumulated_fractional_ticks -= denominator;
    }
}

void setup_timer_0() {
    // disable timer 0 interrupts
    TIMSK0 = 0;
}

void setup_timer_1() {
    // disable timer1 interrupts
    TIMSK1 = 0;

    // do not toggle or change timer IO pins
    TCCR1A = 0;       

    // (16000000 / 256) - 1
    OCR1A = 62500 - 1;
    
    // Mode 4, CTC using OCR1A | set prescaler to 256
    TCCR1B = (1<<WGM12) | (1<<CS12);
}

void setup_timer_2() {
     // disable timer2 interrupts during setup
    TCCR2A = 0;
    TCCR2B = 0;
    TIMSK2 = 0;

    // enable OC2B pin for output (digital pin 3)
    DDRD |= 1<<3;

    // The fast Pulse Width Modulation or fast PWM mode (WGM22:0 = 3 or 7) provides a high fre-
    // quency PWM waveform generation option. The fast PWM differs from the other PWM option by
    // its single-slope operation. The counter counts from BOTTOM to TOP then restarts from BOT-
    // TOM. TOP is defined as 0xFF when WGM2:0 = 3, and OCR2A when MGM2:0 = 7. In non-
    // inverting Compare Output mode, the Output Compare (OC2x) is cleared on the compare match
    // between TCNT2 and OCR2x, and set at BOTTOM. In inverting Compare Output mode, the out-
    // put is set on compare match and cleared at BOTTOM.


    TCCR2A |= (1<< WGM20) | (1<<WGM21) | // Fast PWM
              (1<<COM2B1);               // Clear OC2B on Compare Match

    TCCR2B |= (1<<CS20) | // no Prescaler      
              (1<<WGM22);  // Fast PWM

    OCR2A = ticks_per_period - 1;  // period length

    OCR2B = pwm_full_carrier;  // duty cycle
    
    // enable match interrupts
    TIMSK2 |= (1<<OCIE2A);
}

void setup() {
    setup_timer_0();
    setup_timer_1();
    setup_timer_2();
  Serial.begin(115200);  
}

void modulate(const uint8_t duration) {
}

void loop() 
{
  char incomingByte;
  uint8_t reg_t;
  
   if (Serial.available() > 0) {   
    incomingByte = Serial.read();
    // "x" command - DEBUG
    if (incomingByte == 'x') {

      reg_t = TCCR2A;
       Serial.print("\n\tTCCR2A  = ");
       Serial.println(reg_t, HEX);
      reg_t = TCCR2B;
       Serial.print("\n\tTCCR2B  = ");
       Serial.println(reg_t, HEX);
      reg_t = OCR2A;
       Serial.print("\n\tOCR2A  = ");
       Serial.println(reg_t, HEX);
       Serial.print("\n\tOCR2A DEC  = ");
       Serial.println(reg_t, DEC);
      reg_t = OCR2B;
       Serial.print("\n\tOCR2B  = ");
       Serial.println(reg_t, HEX);
       Serial.print("\n\tOCR2B DEC = ");
       Serial.println(reg_t, DEC);
      reg_t = TIMSK2;
       Serial.print("\n\tTIMSK2  = ");
       Serial.println(reg_t, HEX);
    }     
  }
}

It has always worked perfectly for me. Could you post an example of how it fails?

Yes, I will check my sketches, and post back when I find.

I double checked the signed vs. unsigned issue. Of course it is a valid point but in my case not applicable.

Thanks to Magician for locating the real issue. I confused binary and logic or. Interestingly I messed this up only in parts of my code. Now it works exactly as it should :slight_smile:

You are welcome. The point is, use serial all the time, nice debugging feature we only have on arduino.
I can't find the sketch, that bugged me with signed/unsigned problem in the past. Probably, it was on earlier IDE, 0.22 or 1.0
But I recollect another issue I had with Leonardo board, here is the ~2 years old discussion:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=125907&highlight=
The problem is inability to set ADTS bit on AtMega32u4. The was no any mistakes in syntaxis, bit just stubbornly refused to set. Serial print help me to find, that one of the header file in the avr library has an error. Reported long time ago:

And know what? I just checked my IDE 1.0.5 and bug still there!

#define ADCSRB _SFR_MEM8(0x7B)
#define ADTS0 0
#define ADTS1 1
#define ADTS2 2
#define ADTS3 4 <<<-
#define MUX5 5
#define ACME 6
#define ADHSM 7

Fixed release 1.6.1, so no it won't be in 1.0.5

Hey all.

Maybe not exactly the correct topic, but it is close.

Well, if you want to generate a 77.5 signal, then buy the Module LCD I2C + Serial Interface and Keypad Control. :slight_smile:
Received it from the postman yesterday, and implemented it to my arduino project today.
I2C is used becouse i needed more ports for this project ( Marine reef tank controller for my brother).
Thats why the code is a bit overdocumented, well my brother needs to understand it too. :slight_smile:
I have created a few projects involving programming but i am new to arduino.
Started up the program clock and DCF77 didnt want to synchronise? Why, it worked like a charm with the DF-ROBOT shield?
And by change (lucky me) i found out that the backlight was the trouble maker, there is a possibility to set the backlight between 0 and 255. Everything between 50 and 160 blocks out the DCF77 signal.
For me, its not a problem, i can work around it. But if you intend to use this controler in combination with DCF77, be warned.
It is still is a nice controler and has much more potention than the last one i tryed, so i keep it in the project.

/* IDE 1.5.6-r2
   Arduino Duemanilov ATMEGA328
   LCD Keypad shield DF-ROBOT 16x2 
   or LCD Module 20x4 with I2C + Serial Interface and Keypad Control. http://www.web4robot.com/LCDCtrl.html 
                                                                      http://www.web4robot.com/files/LCDI2Cw.zip
   Real time clock DS1307RTC.
   DFC77 atomic clock receiver (Conrad).
   
   Goal is, show a stable (realtime) clock on the display, synchronised with DCF77.   
*/

#include <Time.h> 
#include <Wire.h> 
#include <DS1307RTC.h>            // a basic DS1307 library that returns time as a time_t
#include <LCDI2Cw.h>              // LCD Module with I2C / Serial Interface and Keypad Control
//#include <LiquidCrystal.h>      // LCD 16x2 DF-ROBOT Shield                     
//LiquidCrystal lcd(8,9,4,5,6,7); // LCD 16x2 DF-ROBOT Shield
#include "DCF77.h"                // Becouse this is a Conrad DCF77, it gives only inverse data, a ! in front of !digitalread in DCF77.cpp is used.
LCDI2Cw lcd(20, 4, 0x4C);         // LCD Module 20x4 with I2C / Serial Interface and Keypad Control I2C Adress

#define DCF_PIN 2	          // Connection pin to DCF77 device
#define DCF_INTERRUPT 0		  // Interrupt number associated with pin

time_t time;
DCF77 DCF = DCF77(DCF_PIN,DCF_INTERRUPT);

byte Atomic[8] = {    // this is a code to write a single lcd character block
  0b00000,
  0b10010,
  0b10000,
  0b01100,
  0b00011,
  0b01000,
  0b11100,
};


void setup()  {
//    lcd.begin(16, 2);           // if selected DF-ROBOT Shield
  lcd.begin();                    // if selected LCD Module I2C / Serial Interface and Keypad Control
  lcd.backlight(175);             // if selected LCD Module I2C / Serial Interface and Keypad Control
  lcd.contrast(30);               // if selected LCD Module I2C / Serial Interface and Keypad Control
  lcd.createChar(5, Atomic);      // create receiver character Atomic
  DCF.Start();
  setSyncProvider(RTC.get);       // function to get the real time from the RTC
}

int update = 0;

void loop()
{ 
  if(minute() == 0) { update = 0; }        // check every hour (at xx:00 minutes) to start a new cycle, it only resets the atomic indicator.
                                           // if you dont do this the atomic indicator will stay on for ever after any succesfull update,
                                           // even if it fails later on (synchronise is always running in the background, but it does not start the flash).                                       
                                           // i will change this to once a day, so that i know for sure that it updates at least once a day.
                                           // when the synchronising fails, the atomic character keeps flashing untill there is an update. 
                                           
  if (update ==0)                          // when the dcf77 signal is not established yet
  {
  lcd.setCursor(15,0);
  int sensorValue = !digitalRead(DCF_PIN); // again inverse pin 2 raw input, the DCF77 library seems not to be consulted at this stage?
                                           // the raw input from pin 2 gives me a little indication if i receive a correct signal.
                                           // the atomic indicator has to flash with the same regularity as the secconds.
                                           // if not, you receive a bad signal. The actualy raw pulses are very short, 100 / 200 ms. 
                                           // so i stretch them out to 400 ms,to give the lcd display the time to show it properly.
                                           // it does not affect the 100 / 200 ms pulses, those pulses are essential to make it work. 
                                           
    lcd.setCursor(15,0);
    if (sensorValue ==1) { lcd.write(5); delay(400); } // flash from pin 2, show atomic character
    if (sensorValue ==0) { lcd.print(" ");}  
  }
  time_t DCFtime = DCF.getTime(); // Check if new DCF77 time is available
  if (DCFtime!=0)
  {
    lcd.setCursor(15,0);
    lcd.write(5);                 // if yes, show atomic character continuously.
    setTime(DCFtime);             // update the new time to the internal time.
    RTC.set(now());               // update the new time to the real time module DS1307RTC.
    update = 1;                   // indication that the cycle is completed.
  }
  digitalClockDisplay();          // show the time and date on the display.
}

void digitalClockDisplay(){
 lcd.setCursor(4, 0);             // Set LCD cursor position (column, row)
  if (hour() < 10) {              // show the clock HH:MM:SS, next line DD MM YYYY
    lcd.print("0");
  } 
lcd.print(hour());
lcd.print(":");
  if (minute() < 10) { lcd.print("0"); }
lcd.print(minute());
   lcd.print(":");
  if (second() < 10) { lcd.print("0"); } 
lcd.print(second());
   lcd.print(" ");
lcd.setCursor(3, 1);
  if (day() < 10) { lcd.print("0"); }
lcd.print(day());
  lcd.print(" ");
  if (month() < 10) { lcd.print("0"); }
lcd.print(month());
  lcd.print(" ");
lcd.print(year());
}