TCCR1A not declared in this scope?

I've found the code below for a weather station that i've already built and all the functions work very well on the arduino uno but when i try to compile the same code on an arduino uno wifi rev 2, I get the error TCCR1A not declared in this scope. I have already tried enabling register emulation for 328p but that doesn't fix the issue. I already know this issue is that the registers are different between the arduino uno and the uno wifi rev2 but I have absolutely no idea of how to convert them. Any help would be greatly appreciated. Thanks!

FLOAT_weather_08.ino (7.1 KB)

#include <LiquidCrystal.h>
#include <Arduino.h>
#include <Adafruit_BME280.h>

#include <Wire.h>



//LiquidCrystal_PCF8574 lcd(0x3f);  // set the LCD address to 0x3f for a 20 chars and 4 line display

//Wind Direction
int show = 0;
int error = 0;
int ledPin = 13;
byte direct = 1;//begin with North
char* compass[] = {"N  ", "NNE", "NE ", "ENE", "E  ", "ESE", "SE ", "SSE", "S  ", "SSW", "SW ", "WSW", "W  ", "WNW", "NW ", "NNW","???"};
byte pointer = 0;

//Wind Speed
float radius = 0.065; //metres from center pin to middle of cup
volatile int seconds = 0;
volatile int revolutions = 0; //counted by interrupt
volatile int rps = -1; // read revs per second (5 second interval)
volatile int mps = 0;  //wind speed metre/sec

//Rainfall
volatile double rainfall = 0;

//Barometer and Thermometer
double  temperature   = 0.0;
double  barometer = 0.0;
Adafruit_BME280 bmp;

void setup() {
  //The angle of wind direction is given here for each pin on its own
  //if two opins are activated, ie magnet halfway between, the direction
  //is interpolated so that 16 angles are reported, not just the 8 shown.
  //you may need to experiment with the magnet to get this accurate and
  //reliable.  The alignment of the reed switches should not be critical.
  pinMode( 4, INPUT_PULLUP); //angle ==   0 degrees
  pinMode( 5, INPUT_PULLUP); //angle ==  45 degrees
  pinMode( 6, INPUT_PULLUP); //angle ==  90 degrees
  pinMode( 7, INPUT_PULLUP); //angle == 135 degrees
  pinMode( 8, INPUT_PULLUP); //angle == 180 degrees
  pinMode( 9, INPUT_PULLUP); //angle == 225 degrees
  pinMode(10, INPUT_PULLUP); //angle == 270 degrees
  pinMode(11, INPUT_PULLUP); //angle == 315 degrees
  pinMode(ledPin, OUTPUT);

  Serial.begin(115200);
  Serial.println("LCD...");
  while (! Serial);
  Serial.println("Begin: check for LCD");

  // See http://playground.arduino.cc/Main/I2cScanner
  Wire.begin();
  Wire.beginTransmission(0x3f);
  error = Wire.endTransmission();
  if (error) {
    Serial.print("Error: ");
    Serial.print(error);
    Serial.println(": LCD not found.");
  } // if
  
   // initialize the lcd
  show = 0;
  pinMode(12, OUTPUT); //controls LCD Back Light
  digitalWrite(12, HIGH);
  //also controls LCD Back Light
  //Anemometer
  pinMode(2, INPUT_PULLUP);
  attachInterrupt(0, rps_fan, FALLING);
  //Rainfall
  pinMode(3, INPUT_PULLUP);
  attachInterrupt(1, tipbuckets, FALLING);
  //Barometer
  bmp.begin();    //start the barometer and temp packages

  // Initialize Timer1 for a 1 second interrupt
  // Thanks to http://www.engblaze.com/ for this section, see their Interrupt Tutorial
  cli();          // disable global interrupts
  TCCR1A = 0;     // set entire TCCR1A register to 0
  TCCR1B = 0;     // same for TCCR1B
  // set compare match register to desired timer count:
  OCR1A = 15624;
  // turn on CTC mode:
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR1B |= (1 << CS10);
  TCCR1B |= (1 << CS12);
  // enable timer compare interrupt:
  TIMSK1 |= (1 << OCIE1A);
  // enable global interrupts:
  sei();
} // setup()

//Routine Driven by Interrupt, trap 1 second interrupts, and output every 5 seconds
ISR(TIMER1_COMPA_vect) {
  seconds++;
  if (seconds == 5) { //make 5 for each output
    seconds = 0;
    rps = revolutions;
    revolutions = 0;
  }
}

// executed every time the interrupt 0 (pin2) gets low, ie one rev of cups.
void rps_fan() {
  revolutions++;
}//end of interrupt routine

// executed every time the interrupt 1 (pin3) gets low, ie one tip of buckets.
void tipbuckets() {
  rainfall++;
 //this has not been calibrated as it will depend on your own printing dimensions
 //as a rough guide though, this system has 50 tips per 100ml, over a radius of 4.25cm
 //one tip is approx 2ml, or 2cmCubed. Area of catchment= pi*4.25*4.25=56.7 cm Squared
 //Rainfal per tip is 2/56.7=25th of a ml
}//end of interrupt routine


void loop() {
  //lcd.setBacklight(255);
  
  
  while (true) {
    int pointer = -1;
    direct = (((PINB & B00001111) << 4) | ((PIND & B11110000) >> 4)) ^ B11111111;
    //chuck out any non readings where the magnet doesn't work
    switch (direct) {
      case 1:
        pointer = 0;
        break;
      case 3:
        pointer = 1;
        break;
      case 2:
        pointer = 2;
        break;
      case 6:
        pointer = 3;
        break;
      case 4:
        pointer = 4;
        break;
      case 12:
        pointer = 5;
        break;
      case 8:
        pointer = 6;
        break;
      case 24:
        pointer = 7;
        break;
      case 16:
        pointer = 8;
        break;
      case 48:
        pointer = 9;
        break;
      case 32:
        pointer = 10;
        break;
      case 96:
        pointer = 11;
        break;
      case 64:
        pointer = 12;
        break;
      case 192:
        pointer = 13;
        break;
      case 128:
        pointer = 14;
        break;
      case 129:
        pointer = 15;
        break;
      default:
        pointer = 16;
        // if nothing else matches, do the default
        // default 16, "???" mainly for debugging
        break;
    }
    //Serial.print(direct);
    //Serial.print("\t -> ");
    //Serial.print(pointer);
    //Serial.print("\t -> ");
    hexBinDump(direct); //working beautifully
    
    
    Serial.println("Wind: ");
    if (rps != -1) { //Update every 5 seconds, this will be equal to reading frequency (Hz)x5.
      float kmph = rps * 3.1414 * 2 * radius * 12 * 60 / 1000;
      if (direct != 0) {
        Serial.println(compass[pointer]);
      }
      
      Serial.println("@ ");
      if (kmph < 10) {
        Serial.println(" ");
      }
       //kilometres per hour to one decimal place
      Serial.println("kmph: ");
   Serial.println(kmph, 1);
    }
    //Barometer and Temperature
    temperature = (double)bmp.readTemperature();   //internal temperature
    barometer = (double)bmp.readPressure() / 100.0; //Pa reduced to mBar
    
    Serial.println("P:");
      if (barometer < 1000) {
        Serial.println(" ");
      }
    Serial.println(barometer,0);
    Serial.println("kPa ");
    Serial.println(11, 2);
    Serial.println("T:");
      if (temperature < 10) {
        Serial.println(" ");
      }
    Serial.println(temperature,1);
    Serial.println("C ");
    //Rainfall 
    
    Serial.println("Rainfall: ");
    Serial.println(rainfall,1);
    Serial.println("mm   ");
    delay(1000);
  }
} // loop()

//Useful to invoke to debug the byte Array
void hexBinDump(int myNos) {
  //Serial.println("T A3 10100011 07 00000111 02 00000010 AA 10101010 F0 11110000 06 00000110 FF 11111111 07 00000111 33 00110011 60 01100000");
  byte mask = B10000000;
  if ((myNos & B11110000) == 0) { //alter if you need to change how many bytes you are checking
    Serial.print("0");
  }
  Serial.print(myNos, HEX);
  Serial.print(" ");
  for (int k = 0; k < 8; k++) {
    if (myNos & mask) {
      Serial.print("1");
    }
    else {
      Serial.print("0");
    }
    mask = mask >> 1;
  }
  Serial.println();
}

The Uno Wifi rev2 uses the ATmega4809, which uses very different timers than the '328. Perhaps someone has already converted the code, but if not, it will take some study of both data sheets to get it done.

At a very quick glance, it doesn't seem to need timers really. The whole thing could easily be re-written to be much more portable.

If it is counting pulses in a 5 second interval, which I think it is, a timer is not required to keep track of the 5 second interval... millis() can do that just fine. Also by timing intervals between pulses instead, the update would be faster and the speed would be more accurate.

Good point. The entire timer code could be replaced by a couple of lines using millis().

This part initializes a one-second interrupt. Try replacing that block with this

	// normal counting
	TCA0_SINGLE_CTRLB = 0;
	
	// enable overflow interrupt
	TCA0_SINGLE_INTCTRL = TCA_SINGLE_OVF_bm;
	
	// set TOP value
	TCA0_SINGLE_PER = 15624;
	
	// start timer at 15625 Hz
	TCA0_SINGLE_CTRLA = TCA_SINGLE_CLKSEL_DIV1024_gc | TCA_SINGLE_ENABLE_bm;

and this

ISR(TIMER1_COMPA_vect) {

with this

ISR(TCA0_OVF_vect) {
1 Like

Thank you so much for converting this for me!! It works perfect now except for one line. It gives the error "PINB was not defined in this scope. I tried just deleting it but that stopped all functionality of the code so if you have any idea what the the problem is with this line please let me know. Thanks in advance and again good job on converting the registers. Thanks!!

direct = (((PINB & B00001111) << 4) | ((PIND & B11110000) >> 4)) ^ B11111111;

The definitions of PINB and also the timer register mirror more or less the names of the registers in the hardware data sheet. So to gain an understanding of the differences, it is advantageous to look in them.

It is again, a difference between the two processors. The line in question, I think, reads two ports and assembles 8 bits from 4 from different ports (they are probably connected to something that represents 8 combined bits). Then it inverts all of them. It is often called "direct port manipulation" because it addresses the hardware ports directly. It is mostly used for speed.

Depending on your application, you may, or may not, need to do it. More commonly, it's done with digitalRead() and digitalWrite().

If you have a strong reason to optimize code at a low level, you should encapsulate it as a function or method of a class, so that it is easy to update when porting it.

Yes, and beyond that, the direct port manipulation to read the state of 8 magnets giving 16 wind directions is easily replaced by digitalRead(). The few microseconds saved is not important for measuring wind direction. It's another case of code you don't really understand being used when not needed.

I found this, I have been working with the Every which uses also the ATmega4809:

@pert reported some time ago

There is some attempt to provide a compatibility layer that emulates the ATmega328P registers (which you can disable via Tools > Registers emulation , but clearly this is far from a complete emulation.

Which is def the case. As said in that post, either because they ("they") haven't gotten to every corner, or cannot.

There will be no substitute for knowing as much about the '4809 as you do or might or should know about the '328p.

On the other hand, the '4809 has some additional functionality - I was albe use something the '328p just doesn't have to solve at least one problem I had porting code in a very slick manner directly.

a7

I know that it is a problem with switching between the two processors but does anyone know how i can port this. I really have no experience with anything on this level of arduino or anything to do with binary, so if anyone could guide me on how to port this it would be greatly appreciated.
Thanks!

I think this will do it

//direct = (((PINB & B00001111) << 4) | ((PIND & B11110000) >> 4)) ^ B11111111;

direct = (digitalRead(11)*128 + digitalRead(10)*64 +digitalRead(9)*32 +digitalRead(8)*16
 + digitalRead(7)*8 + digitalRead(6)*4 + digitalRead(5)*2 + digitalRead(4)) ^B1111111;

thank you everyone for your help with porting this script but i have decided to use an arduino uno to handle all measurements and such and have it transmit the final data to an arduino uno wifi over the I2C bus to be sent to a web server. Thanks!

The equivalent of PINx in the Atmega4809 is PORTx_IN

direct = (((PORTB_IN & 0x0F) << 4) | ((PORTD_IN & 0xF0) >> 4)) ^ 0xFF;