Hi, I want to add the smoothing to the calculated values so that I can take an average of multiple readings and display them. My problem is it is easy to do with adc value but how do i do it on this program :
#include "SevSeg.h"
SevSeg sevseg; //Instantiate a seven segment controller object
volatile float freqCounter = 0.000;
float preMillis = 0.000;
int indicator=A0;
void isr() //interrupt service routine
{
freqCounter++;
}
void setup()
{
Serial.begin(9600);
pinMode(indicator,OUTPUT);
byte numDigits = 3;
byte digitPins[] = {A3, A2, A1};
byte segmentPins[] = {3,4,5,6,7,8,9,10};
bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
byte hardwareConfig = COMMON_ANODE; // See README.md for options
bool updateWithDelays = false; // Default 'false' is Recommended
bool leadingZeros = true; // Use 'true' if you'd like to keep the leading zeros
bool disableDecPoint = false; // Use 'true' if your decimal point doesn't exist or isn't connected
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments,
updateWithDelays, leadingZeros, disableDecPoint);
sevseg.setBrightness(45);
displayNumber(freqCounter);
delay(10);
attachInterrupt(0, isr, RISING);
}
void loop()
{
noInterrupts () ; // read and reset counter in critical section
float frequency = freqCounter;
freqCounter = 0;
interrupts () ;
preMillis = millis(); // setup time for next sampling
while((millis()-preMillis)<1000)
displayNumber(frequency); // while waiting multiplex the display constantly.
}
void displayNumber(float frequency)
{
if (frequency < 1000)
{
sevseg.setNumber(frequency);
}
else if (frequency >= 1000.00 && frequency <10000.00)
{
sevseg.setNumberF(frequency/1000,2);
}
else if (frequency >=10000.00 && frequency <100000.00)
{
sevseg.setNumberF(frequency/1000,1);
}
else if (frequency >=100000.00 && frequency <540000.00)
{
sevseg.setNumber(frequency/1000);
}
else
{
sevseg.setChars("OVA");
}
Serial.println(frequency);
sevseg.refreshDisplay();
}
float filteredFreq;
void setup()
{
get a sample and set 'filteredFreq' to a initial value
}
void loop()
{
filteredFreq = 99% filteredFreq + 1% newFreq;
}
A low-pass filter might not be the right filter for you. In some situations a moving-average filter is needed.
If someone asks on this forum about a smoothing filter, then many with reply with their own favorite filter. Don't select a filter with code that you don't understand.
A few notes:
Always use 'unsigned long' for millis().
Does the refreshDisplay() needs to be called all the time ? Then put that in the loop() and use a millis-timer of 1 second to capture the frequency.
Use the millis values to calculate the real elapsed millis, and use that for the calculation of the frequency.
Can you add "Pin" to a variable that is a pin number ?
If you put your 'digitPins' and 'segmentPins' above the setup() function, then all the used pins are together. You can make them 'const'.
It is advised to use "digitalPinToInterrupt" with 'attachInterrupt()'.
The variable 'freqCounter' is a incremental counter. Please make that a 'unsigned long'.
I fixed the code for the Serial Output , but it still doesn't go above 63KHz. Can you please help me about that, the original code used the 8MHz external clock while mine is using the 16MHz. Still it saturates at 63KHz. TTL Signal is being fed to PD5 of Atmega8A.
/****************************************************
Chip type : ATmega8
Program type : Application
Clock frequency : 8.000000 MHz
Memory model : Small
External RAM size : 0
Data Stack size : 256
*****************************************************/
#define F_CPU 16000000UL
#include <stdlib.h> // this library is used to display variables on lcd
volatile unsigned long freq; // to store value of frequency value
volatile int dur; //i=number of overflows in one second
volatile int i;
// dur to store the value of TCNT1 register
char buffer[8]; // to store the frequency value as a string to be displayed on lcd
float freqf;
// used to display the fractions of frequency with the suitable unit as shown later
// Timer 1 overflow interrupt service routine
ISR(TIMER0_OVF_vect)
{
// Place your code here
i++ ; // count the number of overflows in one second
}
// Declare your global variables here
int main()
{
// Declare your local variables here
// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: On
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x04;
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
// Global enable interrupts
sei();
Serial.begin(9600);
while (1)
{
// Place your code here
TIMSK=0x04;
TCCR1B=0x07;
_delay_ms(1000);
TCCR1B=0x00;
TIMSK=0x00;
dur=TCNT1;
freq = dur + i*65536;
Serial.println(TCNT1);
TCNT1=0x0000;
i=0;
if(freq>=1000000)
{
freqf=(float)freq/1000000;
Serial.println(freqf);
}
else if (freq>=1000)
{
freqf=(float)freq/1000;
Serial.println(freqf);
}
else
{
Serial.println(freqf);
}
};
}
while (1)
{
// Place your code here
TIMSK=0x04;
TCCR1B=0x07;
_delay_ms(1000);
TCCR1B=0x00;
TIMSK=0x00;
dur=TCNT1;
freq = dur + (uint32_t)i*65536UL; // could even use a right shift instead of multiply
Serial.println( i ) ; // **new**
Serial.println(TCNT1);
Serial.println( freq ) ; // **new**
TCNT1=0x0000;
i=0;
if(freq>=1000000UL) // **new** UL
{
freqf=(float)freq/1000000.0 ; // **new** .0
Serial.println(freqf);
}
else if (freq>=1000)
{
freqf=(float)freq/1000;
Serial.println(freqf);
}
else
{
Serial.println(freqf); // freqf **this has not been set here**
}
};
}
also the additional print statements.
Did you see any compiler warnings ?
That looks OK but let's see the output the program generates when fed a signal of more than 63kHz with the additional print statements and other changes suggested in post #49.
However, this appears rather low if it is referring to the system clock:
I'm looking at this again:
Does it fail only when you use a physical ATmega8A but works perfectly with a simulated ATMega8A ?
The uploading output that you showed earlier does not include the compilation output where, for example, you see also, for example, how much memory the sketch uses. Can you find that (for the physical ATmega8A) and add that as well as showing the output, with the additional changes and print statements already requested in post #51.
The differences between a simulated and physical system can be hardware related: clock speed, fuse settings etc. etc.
Maybe also add the following after the Serial.begin(9600) :
Sir, I resolved the problem for going more than 100KHz by using a different method I found online in someone's code. I am using the PD5 pin now for the input and used the external interrupt. But now when I try to code in arduino it gives me an error.
C code is as follows which works fine and gives the result in Serial Monitor.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
/* TIMER0_PRELOAD_VAL=255-((F_CPU/Prescaler)*Time - 1).
For F_CPU 16 MHz, Time 1 msec is 6. */
#define TIMER0_PRELOAD_VAL 6
void startcounter(uint16_t);
uint16_t gate_time; // The time in msec that the gate will remain open.
volatile uint16_t timer0_ticks; // The number of Timer0 ticks.
uint8_t counter1_overflows; // The number of Timer / Counter1 overflows.
volatile uint8_t ready_flag; // Flag of completion of the measurement.
volatile uint32_t counter_val; // The total value of the measurement.
int main(void){
DDRB |= (1<<PB0);
Serial.begin(9600);
sei();
uint16_t gate=1000; // Σε milliseconds.
uint32_t frequency;
uint8_t i; // A little trick to not "clog"
// the UART when we have a short gate time.
while(1){
i++;
startcounter(gate);
while(!ready_flag){};
PORTB ^= (1<<PB0); // Strobe LED.
frequency = counter_val*(1000/gate);
if (i>=1000/gate) {
Serial.println(frequency);
i=0;
};
};
return 0;
}
void startcounter(uint16_t msecs){
gate_time=msecs;
counter1_overflows=0;
timer0_ticks=0;
ready_flag=0;
TIMSK |= (1<<TOIE1); // Enable Overflow Interrupt for Timer / Counter1.
TIMSK |= (1<<TOIE0); // Enable Overflow Interrupt for Timer0.
// Initialize Timer0. Causes Interrupt each msec.
TCNT0 = TIMER0_PRELOAD_VAL; // Preload with the price for a time of 1 msec.
TCCR0 |= (1<<CS01)|(1<<CS00); // Activation with Prescaler 64.
// Reset TCNT1?
TCNT1=0;
// Increase TCNT1 by applying pulse to PD5.
TCCR1B |= (1<<CS10)|(1<<CS11)|(1<<CS12);
}
ISR(TIMER1_OVF_vect){
counter1_overflows++;
}
ISR(TIMER0_OVF_vect){
uint16_t counter1_ticks;
// Counter value of Counter1.
counter1_ticks = TCNT1;
TCNT0 += TIMER0_PRELOAD_VAL; // Preload TCNT0 for 1 msec.
timer0_ticks++;
// Check if gate_time has been completed. If not ISR exit?
if (timer0_ticks < gate_time) return;
// If we lost a Timer / Counter1 interrupt increase the meter.
if (bit_is_set(TIFR,TOV1)) counter1_overflows++;
TIMSK &= ~((1<<TOIE1)|(1<<TOIE0)); // Disable Interrupt overflow for Timer0, Timer / counter1.
TCCR1B &= ~((1<<CS10)|(1<<CS11)|(1<<CS12)); // Stop Timer / Counter1.
TCCR0 &= ~( (1<<CS01)|(1<<CS00)); // Stop Timer0.
counter_val = (counter1_overflows*65536)+counter1_ticks;
ready_flag = 1;
}
But when I rewrite it for arduino as follows:
#define TIMER0_PRELOAD_VAL 6
void startcounter(unsigned short);
unsigned short gate_time; // The time in msec that the gate will remain open.
volatile unsigned short timer0_ticks; // The number of Timer0 ticks.
int counter1_overflows; // The number of Timer / Counter1 overflows.
volatile unsigned char ready_flag; // Flag of completion of the measurement.
volatile uint32_t counter_val; // The total value of the measurement.
unsigned short gate=1000;
uint32_t frequency;
uint8_t i; // A little trick to not "clog"
// the UART when we have a short gate time.
void setup() {
DDRB |= (1<<PB0);
Serial.begin(9600);
// put your setup code here, to run once:
noInterrupts();
startcounter(gate);
interrupts();
}
void loop() {
i++;
startcounter(gate);
while(!ready_flag){};
PORTB ^= (1<<PB0); // Strobe LED.
frequency = counter_val*(1000/gate);
if (i>=1000/gate)
{
Serial.println(frequency);
i=0;
};
}
void startcounter(unsigned short msecs){
gate_time=msecs;
counter1_overflows=0;
timer0_ticks=0;
ready_flag=0;
TIMSK |= (1<<TOIE1); // Enable Overflow Interrupt for Timer / Counter1.
TIMSK |= (1<<TOIE0); // Enable Overflow Interrupt for Timer0.
// Initialize Timer0. Causes Interrupt each msec.
TCNT0 = TIMER0_PRELOAD_VAL; // Preload with the price for a time of 1 msec.
TCCR0 |= (1<<CS01)|(1<<CS00); // Activation with Prescaler 64.
// Reset TCNT1?
TCNT1=0;
// Increase TCNT1 by applying pulse to PD5.
TCCR1B |= (1<<CS10)|(1<<CS11)|(1<<CS12);
}
ISR(TIMER1_OVF_vect){
counter1_overflows++;
}
ISR(TIMER0_OVF_vect){
uint16_t counter1_ticks;
// Counter value of Counter1.
counter1_ticks = TCNT1;
TCNT0 += TIMER0_PRELOAD_VAL; // Preload TCNT0 for 1 msec.
timer0_ticks++;
// Check if gate_time has been completed. If not ISR exit?
if (timer0_ticks < gate_time) return;
// If we lost a Timer / Counter1 interrupt increase the meter.
if (bit_is_set(TIFR,TOV1)) counter1_overflows++;
TIMSK &= ~((1<<TOIE1)|(1<<TOIE0)); // Disable Interrupt overflow for Timer0, Timer / counter1.
TCCR1B &= ~((1<<CS10)|(1<<CS11)|(1<<CS12)); // Stop Timer / Counter1.
TCCR0 &= ~( (1<<CS01)|(1<<CS00)); // Stop Timer0.
counter_val = (counter1_overflows*65536)+counter1_ticks;
ready_flag = 1;
}
Gives me error:
Warning: Board breadboard:avr:atmega328bb doesn't define a 'build.board' preference. Auto-set to: AVR_ATMEGA328BB
C:\Users\DESIGN~1\AppData\Local\Temp\arduino_build_808190/..\arduino_cache_29448\core\core_c0b58a630375852fd283f9fcec03bc34.a(wiring.c.o): In function __vector_9': C:\Users\designerVeeKay\AppData\Local\Arduino15\packages\MiniCore\hardware\avr\2.1.3\cores\MCUdude_corefiles/wiring.c:159: multiple definition of __vector_9'
sketch\sketch_may07a.ino.cpp.o:C:\Users\designerVeeKay\Documents\Arduino\sketch_may07a/sketch_may07a.ino:69: first defined here
collect2.exe: error: ld returned 1 exit status
exit status 1
Error compiling for board ATmega8.
If you use main() you don't get this problem. However, if you use setup() and loop() then the Arduino environment takes over the timer0 overflow vector (for millis(), delay(), etc.) so you get a conflict if you try also to use it in your program.
I resolved this by sticking to the AVR C programming instead on going with Arduino. But now my display keep flickering. I have changed the delay values between 1ms to 100ms but problem remains there. Can you please have a look if I am missing something here.
Note: I am using 7 Segment Common Cathode display with a=pd2, b=pd3, c=pd4, d=pd6, e=pd7, f= pb0, g=pb1, dp=pb2.
Display 1 = pc0, display 2 = pc1, display 3= pc2.
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
/* TIMER0_PRELOAD_VAL=255-((F_CPU/Prescaler)*Time - 1).
For F_CPU 16 MHz, Time 1 msec is 6. */
#define TIMER0_PRELOAD_VAL 6
void startcounter(uint16_t);
uint16_t gate_time; // The time in msec that the gate will remain open.
volatile uint16_t timer0_ticks; // The number of Timer0 ticks.
uint8_t counter1_overflows; // The number of Timer / Counter1 overflows.
volatile uint8_t ready_flag; // Flag of completion of the measurement.
volatile uint32_t counter_val; // The total value of the measurement.
int main()
{
DDRD = (1<<2) | (1<<3) | (1<<4) | (1<<6) | (1<<7);
DDRB = (1<<0) | (1<<1) | (1<<2);
DDRC = (1<<0) | (1<<1) | (1<<2);
sei();
uint16_t gate=1000; // Se milliseconds.
uint32_t frequency;
uint8_t i; // A little trick to not "clog"
// the UART when we have a short gate time.
while(1)
{
i++;
startcounter(gate);
while(!ready_flag){};
frequency = counter_val*(1000/gate);
if (i>=1000/gate)
displayNumber(frequency);
i=0;
};
return 0;
}
void startcounter(uint16_t msecs){
gate_time=msecs;
counter1_overflows=0;
timer0_ticks=0;
ready_flag=0;
TIMSK |= (1<<TOIE1); // Enable Overflow Interrupt for Timer / Counter1.
TIMSK |= (1<<TOIE0); // Enable Overflow Interrupt for Timer0.
// Initialize Timer0. Causes Interrupt each msec.
TCNT0 = TIMER0_PRELOAD_VAL; // Preload with the price for a time of 1 msec.
TCCR0 |= (1<<CS01)|(1<<CS00); // Activation with Prescaler 64.
// Reset TCNT1?
TCNT1=0;
// Increase TCNT1 by applying pulse to PD5.
TCCR1B |= (1<<CS10)|(1<<CS11)|(1<<CS12);
}
ISR(TIMER1_OVF_vect){
counter1_overflows++;
}
ISR(TIMER0_OVF_vect){
uint16_t counter1_ticks;
// Counter value of Counter1.
counter1_ticks = TCNT1;
TCNT0 += TIMER0_PRELOAD_VAL; // Preload TCNT0 for 1 msec.
timer0_ticks++;
// Check if gate_time has been completed. If not ISR exit?
if (timer0_ticks < gate_time) return;
// If we lost a Timer / Counter1 interrupt increase the meter.
if (bit_is_set(TIFR,TOV1)) counter1_overflows++;
TIMSK &= ~((1<<TOIE1)|(1<<TOIE0)); // Disable Interrupt overflow for Timer0, Timer / counter1.
TCCR1B &= ~((1<<CS10)|(1<<CS11)|(1<<CS12)); // Stop Timer / Counter1.
TCCR0 &= ~( (1<<CS01)|(1<<CS00)); // Stop Timer0.
counter_val = (counter1_overflows*65536)+counter1_ticks;
ready_flag = 1;
}
void displayNumber(int num)
{
int i;
for (i = 0 ; i < 3 ; i++) // display trailing digit first so can do %, / loop
{
pickDigit (i) ;
pickNumber(num % 10);
num /= 10 ;
_delay_ms(1);
}
}
void pickDigit(int x)
{
// Initially turn _all_ lines off to avoid ghosting
clearLEDs () ;
// select digit (still dark)
switch(x)
{
case 0:
PORTC = (1<<3);
break;
case 1:
PORTC = (1<<2);
break;
case 2:
PORTC = (1<<1);
break;
default:
PORTC = (1<<0);
break;
}
}
//Function to display a number:
void pickNumber(int x)
{
switch(x)
{
default:
zero();
break;
case 1:
one();
break;
case 2:
two();
break;
case 3:
three();
break;
case 4:
four();
break;
case 5:
five();
break;
case 6:
six();
break;
case 7:
seven();
break;
case 8:
eight();
break;
case 9:
nine();
break;
}
}
void clearLEDs()
{
PORTD = (1<<2) | (1<<3) | (1<<4) | (1<<6) | (1<<7);
PORTB = (1<<0) | (1<<1) | (1<<2);
}
void zero()
{
PORTD = (0<<2) | (0<<3) | (0<<4) | (0<<6) | (0<<7);
PORTB = (0<<0) | (1<<1) | (1<<2);
}
void one()
{
PORTD = (1<<2) | (0<<3) | (0<<4) | (1<<6) | (1<<7);
PORTB = (1<<0) | (1<<1) | (1<<2);
}
void two()
{
PORTD = (0<<2) | (0<<3) | (1<<4) | (0<<6) | (0<<7);
PORTB = (1<<0) | (0<<1) | (1<<2);
}
void three()
{
PORTD = (0<<2) | (0<<3) | (0<<4) | (0<<6) | (1<<7);
PORTB = (1<<0) | (0<<1) | (1<<2);
}
void four()
{
PORTD = (1<<2) | (0<<3) | (0<<4) | (1<<6) | (1<<7);
PORTB = (0<<0) | (0<<1) | (1<<2);
}
void five()
{
PORTD = (0<<2) | (1<<3) | (0<<4) | (0<<6) | (1<<7);
PORTB = (0<<0) | (0<<1) | (1<<2);
}
void six()
{
PORTD = (0<<2) | (1<<3) | (0<<4) | (0<<6) | (0<<7);
PORTB = (0<<0) | (0<<1) | (1<<2);
}
void seven()
{
PORTD = (0<<2) | (0<<3) | (0<<4) | (1<<6) | (1<<7);
PORTB = (1<<0) | (1<<1) | (1<<2);
}
void eight()
{
PORTD = (0<<2) | (0<<3) | (0<<4) | (0<<6) | (0<<7);
PORTB = (0<<0) | (0<<1) | (1<<2);
}
void nine()
{
PORTD = (0<<2) | (0<<3) | (0<<4) | (1<<6) | (1<<7);
PORTB = (0<<0) | (0<<1) | (1<<2);
}
To have a clean, flicker free multiplexed display, the digits must be cycled every about 4ms or less.
You appear to be blocking the display (actually the whole code) for 1 second periods during the sampling of the input pin.
You could use another timer, timer2 running at say 250Hz, to drive the display. Reducing the sampling period might help a bit but may impact the accuracy/resolution of the results.
Before the while(1) in main, configure timer2 to generate interrupts at 250Hz (approx).
In the ISR of timer2 call displayNumber().
Modify and complete displayNumber() as below:
void displayNumber()
{
// on each call, calculates and displays the number at the next digit position
// call every 4 ms by a timer
static int i = 0 ; // static variables are initialised once only
pickDigit (i) ;
// calculate the number (x) to be displayed at postion 'i' based on the frequency.
pickNumber( x ) ; // display it
if ( ++i >= 4 ) i = 0 ;
}