Forum: Mikrocontroller und Digitale Elektronik Frequenz eines Feuchtigkeits Sensors messen


von julius (Gast)


Lesenswert?

hi,


habe diesen Frequenzzähler Code hier im Forum gefunden und ein bisschen 
erweitert um per HC-05 den Messwert zu senden.

Ein Echo Program funktioniert mit der aufgebauten Schaltung - 
Verkabelung stimmt also.
Der Feuchtigkeits Sensor ist der vom giess-o-mat (unter Artikeln).
Data ist an T0/PD4 angeschlossen, VCC an VCC, GND an GND

Der Frequenzzähler Code ist für einen Atmega8 geschrieben, habe die 
Register auf den Atmega168 angepasst.
Aber dabei ging wohl was Schief, sieht jemand den Fehler?

Die Kommentare am Ende der Zeilen stammen noch von 8er Code.




1
/*********************************************************************
2
3
Frequenzzähler (Berich: 1Hz bis etwa theor. 8MHz). Mit Vorteiler 
4
einfach erweiterbar für höhere Messfrequenzen
5
6
Dieses Programm stellt die am T0 Eingang (PD4) eines  Atmega8 (16Mhz) 
7
anliegende Frequenz (TTL Pegel Rechteck) auf einem LCD Hz-genau dar. 
8
Weiterhin gibt es die Möglichkeit eines Abgleiches der Frequenz 
9
mittels Korrekturfaktor bei Bedarf wegen der unvermeidlichen 
10
"Quarz-Schwingfrequenzabweichung"). Die Abweichungen sind 
11
ohne Abgleich innerhalb 0,03% Bereich (linear) bei annähernd konstanter
12
Raumtemperatur. Für normale Zwecke völlig ausreichend. 
13
14
Zum Abgleich wird die Variable "korrekturfaktor" anpasst. d.h. es wird
15
eine bekannte, stabile Frequenz (Rechteck, TTL - sollte mindestens 
16
10Khz, besser 1Mhz sein) angelegt und der angezeite Wert ermittelt.
17
Anschließend wird der wahre Wert durch den angezeigten Wert geteilt 
18
und sinnvoll auf 5 oder 6 Stellen nach dem Komma gerundent. z.b.
19
20
100000Hz/100026Hz=0.99974
21
22
Dieser Wert wird dann der Variable "korrekturfaktor" zugeteilt.
23
Genauigkeit nach Abgleich 1Hz genau. Zumindestens bei meinen 
24
Referenzmessungen. Viel Spass damit! 
25
26
Swen
27
28
*********************************************************************/
29
30
#include <avr/io.h>
31
#include <avr/interrupt.h>
32
#include "avr/interrupt.h"
33
34
#define F_CPU 8000000
35
#define USART_BAUDRATE 9600
36
//#define BAUD 9600
37
//#define BAUD 76800
38
//#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
39
#define BAUD_PRESCALE ((uint16_t) ((F_CPU / ((USART_BAUDRATE) * 16.0)) + .5) - 1)
40
41
42
43
int count = 0, a = 0;
44
float korrekturfaktor = 1; //hier anpassen für Abgleich (z.b 0.99974)
45
int ztmp = 0;
46
unsigned long freq = 0;
47
char anz[20];
48
volatile int s=0; 
49
volatile unsigned short z=0;
50
51
52
53
/*********************************
54
Timer 0 und 2 Interruptroutinen
55
**********************************/
56
57
ISR(TIMER2_COMPA_vect) // Löst aus alle (16Mhz/1024/125) 8ms
58
{
59
s++;
60
}
61
62
ISR(TIMER0_OVF_vect) // Eingangsignal Overflow Zähler Timer 0
63
{
64
z++;
65
TIFR0 = (1<<TOV0); //ov flag timer 0 zurücksetzen
66
}
67
68
/*************************
69
Hauptfunktion
70
**************************/
71
72
73
74
int main(){
75
76
    //UCSR0B = (1 << RXEN0) | (1 << TXEN0);   // Turn on the transmission and reception circuitry
77
    UCSR0B = (1 << TXEN0);   // Turn on the transmission circuitry
78
    
79
    //UCSR0C = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes
80
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // Use 8-bit character sizes
81
82
    UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
83
    UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register
84
85
   
86
        
87
    TCCR2A |= (1<<WGM21); // CTC Mode Aktivierung Timer2
88
    TCCR2A |= (1<<COM2A0) | (1<<COM2A1); // set OCR Flag bei "Compare Match"
89
    TCCR0B |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Ext Flanke Interrupt (T0)
90
    TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20); //  Prescaler Timer2 auf 1024 setzen
91
    OCR2A  |= 124; // Output Compare register Timer2 auf 124 (für Loop 0-124 => 125)
92
    TIMSK2 |= (1<<OCIE2A); // Enable comp match flag interrupt Timer 2 
93
    TIMSK2 |= (1<<TOIE0); // Enable overflow flag interrupt Timer 0
94
95
    sei();
96
97
    char ReceivedByte;
98
99
    while(1)
100
    {
101
102
           
103
        if (s==125){ // "Zähltor": Abarbeitung jede Sekunde (16MHz/1024/125/125)
104
            count = TCNT0;
105
            s=0;
106
            ztmp = z;
107
            z= 0;
108
            TCNT0 = 0;
109
            freq = 0.5+((256UL*ztmp + count)*korrekturfaktor); //siehe Bemerkung unten
110
        
111
        // notwendig wenn RX nicht aktiviert ist?
112
        //while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data have been received and is ready to be read from UDR
113
        //ReceivedByte = UDR0; // Fetch the received byte value into the variable "ByteReceived"
114
115
        // zum testen a zurück senden
116
    while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
117
    UDR0 = 'a'; // Echo back the received byte back to the computer
118
119
        }  
120
        /*********************************************************************************
121
        - freq: Ermittlung der Frequenz: Anzahl Timer 0 Overflows + aktuellen Timer 0 Count
122
        - die + 0,5 sind zum korrektem "Runden" ->Fließkommazahl zu Ganzzahl
123
        - "korrekturfaktor" ist die SW-Korrektur zur Kompensation der lin.Quarzungenauigkeit 
124
        **********************************************************************************/
125
126
    }
127
}

von Tom Thomsen (Gast)


Lesenswert?

julius schrieb:
> Der Frequenzzähler Code ist für einen Atmega8 geschrieben, habe die
> Register auf den Atmega168 angepasst.
> Aber dabei ging wohl was Schief, sieht jemand den Fehler?

Du zeigst den (funktionsfähigen) Code für den ATmega8 nicht. Wer weiss, 
was sich da alles geändert hat und woran du merkst, dass beim Anpassen 
etwas schief gegangen ist.

von julius (Gast)


Lesenswert?

Habe gerade zufällig herausgefunden das die a's im hc-05 ankommen.

Erst den oberen Code geladen, dann den alten echo code
(Dieser sendet das empfangende byte zurück)
...nach dem senden eines beliebigen chars kommt ein aaaaaaa in 
verschiedenen Längen zurück.
Je nachdem wie lange man zwischen den Code uploads wartet.

Ich vermute das der atmega168 die a's an das HC-05 weiterleitet, dieses 
die Daten aber nie sendet. Kann das sein?

von julius (Gast)


Lesenswert?

Original Code aus:
Beitrag "Frequenzzähler 8Mhz ohne Vorteiler - Atmega8"

Ich vermute das der funktioniert.
1
/*********************************************************************
2
3
Frequenzzähler (Berich: 1Hz bis etwa theor. 8MHz). Mit Vorteiler 
4
einfach erweiterbar für höhere Messfrequenzen
5
6
Dieses Programm stellt die am T0 Eingang (PD4) eines  Atmega8 (16Mhz) 
7
anliegende Frequenz (TTL Pegel Rechteck) auf einem LCD Hz-genau dar. 
8
Weiterhin gibt es die Möglichkeit eines Abgleiches der Frequenz 
9
mittels Korrekturfaktor bei Bedarf wegen der unvermeidlichen 
10
"Quarz-Schwingfrequenzabweichung"). Die Abweichungen sind 
11
ohne Abgleich innerhalb 0,03% Bereich (linear) bei annähernd konstanter
12
Raumtemperatur. Für normale Zwecke völlig ausreichend. 
13
14
Zum Abgleich wird die Variable "korrekturfaktor" anpasst. d.h. es wird
15
eine bekannte, stabile Frequenz (Rechteck, TTL - sollte mindestens 
16
10Khz, besser 1Mhz sein) angelegt und der angezeite Wert ermittelt.
17
Anschließend wird der wahre Wert durch den angezeigten Wert geteilt 
18
und sinnvoll auf 5 oder 6 Stellen nach dem Komma gerundent. z.b.
19
20
100000Hz/100026Hz=0.99974
21
22
Dieser Wert wird dann der Variable "korrekturfaktor" zugeteilt.
23
Genauigkeit nach Abgleich 1Hz genau. Zumindestens bei meinen 
24
Referenzmessungen. Viel Spass damit! 
25
26
Swen
27
28
*********************************************************************/
29
30
#include <avr/io.h>
31
#include "lcd-routines.h"
32
#include "avr/interrupt.h"
33
34
int count = 0, a = 0;
35
float korrekturfaktor = 1; //hier anpassen für Abgleich (z.b 0.99974)
36
int ztmp = 0;
37
unsigned long freq = 0;
38
char anz[20];
39
volatile int s=0; 
40
volatile unsigned short z=0;
41
42
/*****************************************************************
43
Funktion für Unwandlung Unsigned Long to Char (Für LCD Ausgabe)
44
******************************************************************/
45
46
void uLongtoChar(unsigned long u,char* Buffer) 
47
{
48
  int i = 0;
49
  int j;
50
  char tmp;
51
   // die einzelnen Stellen der Zahl berechnen
52
    do {
53
      Buffer[i++] = '0' + u % 10; // = String "0" (=48) + Rest von u/10
54
      u = u/10;
55
    } while( u > 0 );
56
   // den String in sich spiegeln (d.h. Reihenfolge umkehren)
57
    for( j = 0; j < i / 2; ++j ) {
58
      tmp = Buffer[j];
59
      Buffer[j] = Buffer[i-j-1];
60
      Buffer[i-j-1] = tmp;
61
    }
62
    Buffer[i] = '\0';
63
}
64
65
/*********************************
66
Timer 0 und 2 Interruptroutinen
67
**********************************/
68
69
ISR(TIMER2_COMP_vect) // Löst aus alle (16Mhz/1024/125) 8ms
70
{
71
s++;
72
}
73
74
ISR(TIMER0_OVF_vect) // Eingangsignal Overflow Zähler Timer 0
75
{
76
z++;
77
TIFR = (1<<TOV0); //ov flag timer 0 zurücksetzen
78
}
79
80
/*************************
81
Hauptfunktion
82
**************************/
83
84
int main(){
85
lcd_init(); // LCD init
86
TCCR2 |= (1<<WGM21); // CTC Mode Aktivierung Timer2
87
TCCR2 |= (1<<COM20) | (1<<COM21); // set OCR Flag bei "Compare Match"
88
TCCR0 |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Ext Flanke Interrupt (T0)
89
TCCR2 |= (1<<CS22) | (1<<CS21) | (1<<CS20); //  Prescaler Timer2 auf 1024 setzen
90
OCR2  |= 124; // Output Compare register Timer2 auf 124 (für Loop 0-124 => 125)
91
TIMSK |= (1<<OCIE2); // Enable comp match flag interrupt Timer 2 
92
TIMSK |= (1<<TOIE0); // Enable overflow flag interrupt Timer 0
93
94
sei();
95
96
while(1)
97
{
98
  if (s==125){ // "Zähltor": Abarbeitung jede Sekunde (16MHz/1024/125/125)
99
  count = TCNT0;
100
  s=0;
101
  ztmp = z;
102
  z= 0;
103
  TCNT0 = 0;
104
  lcd_clear();
105
  freq = 0.5+((256UL*ztmp + count)*korrekturfaktor); //siehe Bemerkung unten
106
/*********************************************************************************
107
- freq: Ermittlung der Frequenz: Anzahl Timer 0 Overflows + aktuellen Timer 0 Count
108
- die + 0,5 sind zum korrektem "Runden" ->Fließkommazahl zu Ganzzahl
109
- "korrekturfaktor" ist die SW-Korrektur zur Kompensation der lin.Quarzungenauigkeit 
110
**********************************************************************************/
111
112
  uLongtoChar(freq,anz);   // Unwandlung Long zu String für Displayausgabe
113
  set_cursor (0,1);     // Cursor Pos setzen
114
  lcd_string( anz );     // Anzeige der ermittelten Frequenz
115
  set_cursor (8,1);     // Cursor Pos setzen
116
  lcd_string( "Hz" );   // Anzeige Hz
117
  }
118
}
119
return 0;
120
}

von Tom Thomsen (Gast)


Lesenswert?

p.s.
Und ganz toll wäre es, den Code als Anhang zu posten, damit man ihn 
einfacher herunterladen kann.

julius schrieb:
> #define F_CPU 8000000

Hier würde ich mal eine unsigned long Konstante vorschlagen

von julius (Gast)


Lesenswert?

Verwende bisher immer diesen Fuse Calculator:
http://www.engbedded.com/fusecalc/

bei einem atmega168 kann man aber nur external crystal resonator 8- Mhz 
auswählen, nicht direkt 16 oder 20.
Bedeutet das jeder "external crystal resonator" angefangen von 8 
aufwärts funktioniert wenn F_CPU richtig definiert ist mit diesen Fuses?

von Ich (Gast)


Lesenswert?

julius schrieb:
> Ich vermute das der atmega168 die a's an das HC-05 weiterleitet, dieses
> die Daten aber nie sendet. Kann das sein?

Ich glaube die HC-05 muss man mit einem seiner IOs zwischen Command und 
Daten Mode umschalten, das steht nicht immer klar dabei. Ansonsten muss 
die Baudrate des HC-05 und des AVRs passen.

hoffe das hilft..

von julius (Gast)


Lesenswert?

Nein, die HC-05 laufen per default auf Daten.
Ausserdem steht oben drin das das Modul und die Schaltung mit einem 
"echo" Program funktionieren.
Die while Schleife für das Senden ist aus dem getestetem Code.

von julius (Gast)


Lesenswert?

Habe den Code vereinfacht, wenn diese Zeile:
ReceivedByte = calculate_freq();


ausdokumentiert wird kommt der empfangene Buchstabe zurück, wenn die 
Zeile aktiv ist würde ich a oder b erwarten. es kommt aber gar nichts 
zurück.
Warum?


[c]
#include <avr/io.h>
#include <avr/interrupt.h>

#define F_CPU 16000000
#define USART_BAUDRATE 9600
#define BAUD 9600
//#define BAUD 76800
//#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define BAUD_PRESCALE ((uint16_t) ((F_CPU / ((USART_BAUDRATE) * 16.0)) + 
.5) - 1)
#include <util/setbaud.h>

void my_timer_init(void) {
    TCCR2A |= (1<<WGM21); // CTC Mode Aktivierung Timer2
    TCCR2A |= (1<<COM2A0) | (1<<COM2A1); // set OCR Flag bei "Compare 
Match"
    TCCR0B |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Ext Flanke 
Interrupt (T0)
    TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20); //  Prescaler Timer2 
auf 1024 setzen
    OCR2A  |= 124; // Output Compare register Timer2 auf 124 (für Loop 
0-124 => 125)
    TIMSK2 |= (1<<OCIE2A); // Enable comp match flag interrupt Timer 2
    TIMSK2 |= (1<<TOIE0); // Enable overflow flag interrupt Timer 0
}

int count = 0, a = 0;
float korrekturfaktor = 1; //hier anpassen für Abgleich (z.b 0.99974)
int ztmp = 0;
unsigned long freq = 0;
char anz[20];
volatile int s=0;
volatile unsigned short z=0;


char ReceivedByte;

char calculate_freq(void) {

        if (s==125){


        return 'a';
        }
  return 'b';
}



int main (void)
{
  char ReceivedByte;

  UCSR0B = (1 << RXEN0) | (1 << TXEN0);   // Turn on the transmission 
and reception circuitry
    //UCSR0C = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit 
character sizes
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // Use 8-bit character sizes

    UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate 
value into the high byte of the UBRR register
  UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value 
into the low byte of the UBRR register

  my_timer_init();
  sei();

  for (;;) // Loop forever
  {
        while ((UCSR0A & (1 << RXC0)) == 0) {}; // Do nothing until data 
have been received and is ready to be read from UDR
    ReceivedByte = UDR0; // Fetch the received byte value into the 
variable "ByteReceived"

  ReceivedByte = calculate_freq();
        while ((UCSR0A & (1 << UDRE0)) == 0) {}; // Do nothing until UDR 
is ready for more data to be written to it
    UDR0 = ReceivedByte; // Echo back the received byte back to the 
computer


  }
}
[c]

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.