Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

[SOLVED] Atmega128 problem counting RPM of FAN

Status
Not open for further replies.

dxtr123

Newbie level 3
Joined
Dec 20, 2015
Messages
3
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
43
Can anyone can help my why this code doesn't work. The timer interrupts doesn't count the rpm. The purpose of project is to find the rpm of Fan and display in 7segments. In specifications of Fan the rpm is 10000. I'm using atmega128 and the pin of IC is PIND4 and also to display in 7segments must be use this port. Here is the code I'm trying to do this project:


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
 
#define F_CPU   14745600
    
//index shows number from 0-9 that can be shown in 7segments
char Numbers[] = {63, 6, 91, 79, 102, 109, 125, 7, 127, 111};
volatile uint16_t rpm = 0;  //Revolution per minute
volatile uint16_t rps=0;   //Revolution per second
volatile char DisplayBuffer[8]={0,0,0,0,0,0,0,0};
 
volatile uint16_t cnttime = 0;
volatile uint16_t count=0;    //Main revolution counter
 
void display(void)
{
    unsigned int i;
 
    for( i=0;i<8;i++)
    {   
        PORTD = DisplayBuffer[i];
        PORTB = i;
        if(PORTD==0)
        {
            PORTD = ~Numbers[0];    
 
        }   
       _delay_ms(1);
    }
}
 
//prepare numbers for display
void breakup(uint16_t num) 
{
    unsigned int i=7;
    int a=0;
    
    if(num==0)
    {
        unsigned int h;
        for(h=0;h<8;h++)
        {
            DisplayBuffer[i]=~Numbers[0];
        }
    }
    else
    { 
        while (num!=0)
        {
           a = num%10;
           DisplayBuffer[i]=~Numbers[a];
           num=num/10;
           i--;
        }   
    }
}
 
void initSegments(void) 
{
    DDRD = 0xFF;
    DDRB = 0x07;
 
    PORTD =  0xFF;// tung off all segments
    PORTB = 0xFF;
    
    unsigned int i;
    for( i=0;i<8;i++)
    {   
       PORTD=~Numbers[0];//fill with 0
       PORTB=i;
    }
    
}
 
void initCount(void)
{
    DDRD = 0x00;//all ports input
    PORTD = 0xFF;   
}
 
void InitTimer(void)
{   
    TCNT1=0;
    TCCR1A = 0x00;
 
   //Timer Clock = 1/256 of sys clock
   //Mode = CTC (Clear Timer On Compare)
    TCCR1B|=((1<<ICNC1)|(1<<ICES1)|(1<<WGM12)|(1<<CS12)); //prescaler 256  14745600/256=57600
    
    OCR1A=57600; //1s
    
 
    TIMSK|=((1<<TICIE1)|(1<<OCIE1A));
}
 
ISR(TIMER1_CAPT_vect)//
{
   //CPU Jumps here automatically when  pin detect a falling edge
   count++;
 
}
 
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
   rps = count;
   rpm = rps*60;
    
    cnttime++;
    
    //test to show every 5 sec
    if(cnttime==5)
    {
        
        initSegments();
 
        unsigned int h;
        for(h=0;h<10000;h++)
        {
        breakup(rps);
        display();
        }
 
        DDRD = 0x00;
        PORTD = 0xFF;
        
        PORTB = 0x00;
        cnttime=0;
        
        initCount();
    }
    
    count=0;
}
 
int main() 
{
    initCount();
 
    InitTimer();
        
    //Enable interrupts globaly
    sei();
 
    while(1) 
    {   
 
    }
    return 0;
}

 
Last edited by a moderator:

Hi,

first a have to say I´m no C specialist, but I try to read it...

I don´t understand this:
Code:
unsigned int h;
        for(h=0;h<10000;h++)
        {
        breakup(rps);
        display();
        }
it seems it loops 10000 times.
* this may take a lot of time. It is within an ISR, so other interrupts are disabled during this time. Usually ISR should be short in time.
* you use "int". Is it an 8 bit or 16 bit int? To count up to 10000 you need a 16 bit int.
--> Better practice is to do the display in the main loop. When measurement is finished then update the display_rps value and set a flag to show the main loop that a new value is available.
--> in main loop check the flag. When it is set. you may clear it immedaiately and process the display data. display_rps --> rpm --> BCD --> 7-segment. During this processing the measurement is running in ISR.

Klaus

- - - Updated - - -

Added:

If you do the display update only every 5 s, then count for 5s and multiply the value with 12 instead of 60.

If power consumtion is an issue, then you should consider to enable idle_mode in the main loop.

Klaus
 

Thanks Klaus for your suggestions. I made some changes in the code but the result of rps and rpm is still 0. The reason for that loop is to make displaying numbers in 7segments for a short time.
I redused in 1000 counting in loop. I set that part of code in the main loop. ISR and the timer should be disable while displaying numbers because the ICP as an alternate function is in PIND4. Because the 7segments uses also PORTD for display and if I count while displaying the rps=1000 or rps=2000 which is to big, because the rpm of FAN max is 10000 so the rps must be around 166. Here is the code with changes:

Code:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define F_CPU	14745600
	
//index shows number from 0-9 that can be shown in 7segments
char Numbers[] = {63, 6, 91, 79, 102, 109, 125, 7, 127, 111};
volatile uint16_t rpm = 0;	//Revolution per minute
volatile uint16_t rps=0;   //Revolution per second
volatile char DisplayBuffer[8]={0,0,0,0,0,0,0,0};

volatile uint16_t cnttime = 0;
volatile uint16_t count=0;    //Main revolution counter
unsigned char ignore = 0; //when 0 counts, when 1 doesn't count

void display(void)
{
	unsigned int i;

	for( i=0;i<8;i++)
	{   
	    PORTD = DisplayBuffer[i];
		PORTB = i;
	   	if(PORTD==0)
	    {
        	PORTD = ~Numbers[0];	

		}  	
	   _delay_ms(1);
	}
}

//prepare numbers for display
void breakup(uint16_t num) 
{
	unsigned int i=7;
	int a=0;
	
	if(num==0)
	{
		unsigned int h;
		for(h=0;h<8;h++)
		{
			DisplayBuffer[i]=~Numbers[0];
		}
	}
	else
	{ 
		while (num!=0)
		{
		   a = num%10;
		   DisplayBuffer[i]=~Numbers[a];
		   num=num/10;
		   i--;
		}	
	}
}

void initSegments(void) 
{
	DDRD = 0xFF;
	DDRB = 0x07;

	PORTD =  0xFF;// tung off all segments
	PORTB = 0xFF;
	
	unsigned int i;
	for( i=0;i<8;i++)
	{   
	   PORTD=~Numbers[0];//fill with 0
	   PORTB=i;
	}
	
}

void initCount(void)
{
	DDRD = 0x00;//all ports input
	PORTD = 0xFF;	
}

void InitTimer(void)
{	
	TCNT1=0;
	TCCR1A = 0x00;

   //Timer Clock = 1/256 of sys clock
   //Mode = CTC (Clear Timer On Compare)
	TCCR1B|=((1<<ICNC1)|(1<<ICES1)|(1<<WGM12)|(1<<CS12)); //prescaler 256  14745600/256=57600
	
	OCR1A=57600; //1s
	

	TIMSK|=((1<<TICIE1)|(1<<OCIE1A));
}

ISR(TIMER1_CAPT_vect)//
{
   //CPU Jumps here automatically when  pin detect a falling edge
   if(ignore==0)
   count++;

}

SIGNAL(SIG_OUTPUT_COMPARE1A)
{
   ignore = 1;
   rps = count;
   rpm = rps*60;
    
	cnttime++;
	
	count=0;
}

int main() 
{
	initCount();

	InitTimer();
		
	//Enable interrupts globaly
    sei();

	while(1) 
	{	
			//test to show every 1 sec
			if(cnttime==1)
			{	
				TCCR1B &= 0b11111000;//disable timer
				
				initSegments();

				unsigned int h;
				for(h=0;h<1000;h++)
				{
				breakup(rps);
				display();
				}

				DDRD = 0x00;
				PORTD = 0xFF;
		
				PORTB = 0x00;
				cnttime=0;
				ignore =0;
	
				initCount();
				InitTimer();
				
			}
	}
	return 0;
}
 

Hi,

To debug you program you should (step by step)
* test if the display routine works - with fixed segment values
* Then if display and BCD coding works - with fixed rpm
* Then if multiplying of rps to rpm, coding and display works - with fixed rps
* then if the frequency counter works ..

******

ISR and the timer should be disable while displaying numbers because the ICP as an alternate function is in PIND4.

PIND4 has alternating functions in your design???

To avoid influence of main loop and ISR you should use "atomic read modify write" operations in main loop.

One of the benefits of using interrupts especially is that the display and measurement can operate "the same time" while they don´t influence each other.

****
I´d combine two functions with timer1
* precise 1 s measurement time (as is) and
* multiplexing of display

To do this you should select a timeout of about 2.5ms (for each display digits = 50Hz update rate) OCR1A = 144-1 = 143 (your 57600 should actualy be 57599 because it counts 0...57599)
And use a variable to count 0..399 for exact 1s.

Every time you enter the ISR you could display the next digit.
and every 400th interrupt (exactly 1s) you could do the same what you do now

(maybe this fixes the problem with pind4 automatically)

****

Klaus
 
Thankyou Klaus. I solved the problem using two nodes. Counting I did in node 3 then I send those data throught USART at node 2 and then I displayed in 7segments.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top