John Bradnam
Published © GPL3+

ATtiny1614 Frequency Meter

A tiny frequency meter built using the ATtiny1614 microprocessor.

IntermediateFull instructions provided20 hours2,740
ATtiny1614 Frequency Meter

Things used in this project

Hardware components

Microchip ATtiny1614 microprocessor
See Eagle Files for other components used
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

STL files for your slicing software. 0.3mm layer height, no supports, orientate on build plate first

Schematics

Schematic - Display & MPU Board

PCB - Display & MPU board

Schematic - Power & Preamp board

PCB - Power & Preamp board

Eagle Files

Schematics & PCB's in Eagle Format

Code

Audio_Frequency_Meter_V4.ino

C/C++
/**
 * AUDIO FREQUENCY METER V4
 * Based on code from David Johnson-Davies - www.technoblogy.com - 16th March 2021
 * 2021-03-21 John Bradnam (jbrad2089@gmail.com)
 *    - Limited maximum frequency to 99999Hz (Design is primarily for audio frequencies)
 *    - Replaced OLED screen with 5 digit 7-Segment display
 * 
 * ATTiny1614 Pins mapped to Ardunio Pins
 *
 *             +--------+
 *         VCC + 1   14 + GND
 * (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
 *       1 PA5 + 3   12 + PA2 9  (MISO)
 * (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
 *       3 PA7 + 5   10 + PA0 11 (UPDI)
 * (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
 * (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
 *             +--------+
 *             
 * BOARD: ATtiny1614/1604/814/804/414/404/214/204
 * Chip: ATtiny1614
 * Clock Speed: 20MHz
 * millis()/micros(): "TCA0 (default on 0-series)"
 * Programmer: jtag2updi (megaTinyCore)
*/

//#include <avr/io.h>
//#include <avr/interrupt.h>
#include "LedControl.h"

#define MAX7219_DATA 0 //PA4
#define MAX7219_CLK 1 //PA5
#define MAX7219_LOAD 2 //PA6

#define FREQ_IN 8 //PA1
// PA2 is connected to PA3
// 32.768kHz crystal connected to TOSC1/PB3 and TOSC2/PB2

#define MAX7219_BRIGHTNESS 4
#define MAX7219_TOTAL_DIGITS 5

//LED order 7,3,2,4,6
int8_t digits[] = {6, 4, 2, 3, 7};

LedControl lc=LedControl(MAX7219_DATA,MAX7219_CLK,MAX7219_LOAD,1);

uint32_t overflowCounter;

//---------------------------------------------------------------------
//Set up frequency meter

void setup() 
{
  //The MAX72XX is in power-saving mode on startup, we have to do a wakeup call
  lc.shutdown(0,false);
  //Set the brightness to a medium values
  lc.setIntensity(0,MAX7219_BRIGHTNESS);
  //and clear the display
  lc.clearDisplay(0);

  TCDSetup();
  RTCSetup();
  EvsysSetup();
}

//---------------------------------------------------------------------
// Real-Time Clock Setup

volatile uint16_t MSByte;
volatile uint32_t Counter;

void RTCSetup () 
{
  uint8_t temp;
  // Initialize 32.768kHz Oscillator:

  // Disable oscillator:
  temp = CLKCTRL.XOSC32KCTRLA & ~CLKCTRL_ENABLE_bm;

  // Enable writing to protected register
  CPU_CCP = CCP_IOREG_gc;
  CLKCTRL.XOSC32KCTRLA = temp;

  while (CLKCTRL.MCLKSTATUS & CLKCTRL_XOSC32KS_bm); // Wait until XOSC32KS is 0
  
  temp = CLKCTRL.XOSC32KCTRLA & ~CLKCTRL_SEL_bm;    // Use External Crystal
  
  // Enable writing to protected register
  CPU_CCP = CCP_IOREG_gc;
  CLKCTRL.XOSC32KCTRLA = temp;
  
  temp = CLKCTRL.XOSC32KCTRLA | CLKCTRL_ENABLE_bm;  // Enable oscillator
  
  // Enable writing to protected register
  CPU_CCP = CCP_IOREG_gc;
  CLKCTRL.XOSC32KCTRLA = temp;
  
  // Initialize RTC
  while (RTC.STATUS > 0);                           // Wait until registers synchronized
  
  RTC.PER = 1023;                                   // Set period 1 second
  RTC.CLKSEL = RTC_CLKSEL_TOSC32K_gc;               // 32.768kHz External Crystal Oscillator  
  RTC.CTRLA = RTC_PRESCALER_DIV32_gc | RTC_RTCEN_bm;// Prescaler /32 and enable
}

//---------------------------------------------------------------------
// Timer/Counter TCD0 Setup

volatile boolean Ready = false;                     // New reading ready?

void TCDSetup () 
{ 
  TCD0.CTRLB = TCD_WGMODE_ONERAMP_gc;               // Set one ramp waveform mode
  TCD0.CMPBCLR = 0xFFF;                             // Count up to maximum
  TCD0.INPUTCTRLB = TCD_INPUTMODE_EDGETRIG_gc;      // Capture and reset counter
  TCD0.EVCTRLB = TCD_CFG_ASYNC_gc | TCD_ACTION_bm | TCD_TRIGEI_bm; // Enable event
  TCD0.INTCTRL = TCD_OVF_bm | TCD_TRIGB_bm;         // Enable interrupts

  // Ensure ENRDY bit is set
  while(!(TCD0.STATUS & TCD_ENRDY_bm));
  
  // External clock, no prescaler, enable timer
  TCD0.CTRLA = TCD_CLKSEL_EXTCLK_gc | TCD_CNTPRES_DIV1_gc | TCD_ENABLE_bm;
}

//---------------------------------------------------------------------
// Timer/Counter TCD0 overflow interrupt counts MSByte

ISR (TCD0_OVF_vect) 
{
  TCD0.INTFLAGS = TCD_OVF_bm;                       // Clear overflow interrupt flag
  MSByte++;
}

//---------------------------------------------------------------------
// Timer/Counter TCD0 capture interrupt

ISR (TCD0_TRIG_vect) 
{
  PORTA.IN = PIN4_bm;                               // Toggle LED on
  TCD0.INTFLAGS = TCD_TRIGB_bm;                     // Clear capture interrupt flag
  Counter = TCD0.CAPTUREB;
  Counter = (uint32_t)MSByte<<12 | Counter;
  MSByte = 0;
  Ready = true;
  PORTA.IN = PIN4_bm;                               // Toggle LED off
  TCD0.INTFLAGS = TCD_OVF_bm;                       // Clear overflow interrupt flag
}

//---------------------------------------------------------------------
// Event System Setup

void EvsysSetup (void) 
{
  EVSYS.ASYNCCH1 = EVSYS_ASYNCCH1_RTC_OVF_gc;       // Event generated from RTC OVF
  EVSYS.ASYNCUSER7 = EVSYS_ASYNCUSER7_ASYNCCH1_gc;  // Event causes a TCD0 capture
  
  EVSYS.ASYNCCH0 = EVSYS_ASYNCCH0_PORTA_PIN1_gc;    // PA1 is an event generator
  EVSYS.ASYNCUSER8 = EVSYS_ASYNCUSER8_ASYNCCH0_gc;  // ASYNCUSER8 is EVOUT0 (PA2)
  PORTMUX.CTRLA = PORTMUX_EVOUT0_bm;                // Enable EVOUT0
  PORTA.PIN1CTRL = PORT_INVEN_bm;                   // Invert input
}

//---------------------------------------------------------------------
// Main program loop

void loop() 
{ 
  uint32_t temp;
  unsigned long start = millis();
  while (!Ready) 
  {
    if (millis() - start > 1000) 
    {
      Counter = 0;
      break;
    }
  }
  Ready = false;
  
  cli(); 
  temp = Counter; 
  sei();
  writeNumber(temp);
}

//---------------------------------------------------------------------
// Display a number on the LED display
//   num - Number to display between 0 and 99999

void writeNumber(long num)
{
  if (num > 9999999)
  {
    //Display "Error"
    lc.setChar(0, digits[4], 'E', false);
    lc.setRow(0, digits[3], 0x05);
    lc.setRow(0, digits[2], 0x05);
    lc.setChar(0, digits[1], 'o', false);
    lc.setRow(0, digits[0], 0x05);
  }
  else
  {
    bool X1000 = false;
    if (num > 99999)
    {
      X1000 = true;
      num = (num + 500) / 1000;
    }
    for (int i = 0; i < MAX7219_TOTAL_DIGITS; i++)
    {
      if (X1000 && i == 0)
      {
        lc.setChar(0, digits[i], 'H', false);
      }
      else if (num > 0 || i == 0)
      {
        lc.setDigit(0, digits[i], num % 10, false);
        num = num / 10;
      }
      else
      {
        lc.setChar(0, digits[i], ' ', false);
        num = num / 10;
      }
    }
  }
}

Credits

John Bradnam

John Bradnam

141 projects • 167 followers

Comments