Forum: Mikrocontroller und Digitale Elektronik Ladekurve Kondensator, Analog Comparator, ACI


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Hugo P. (portisch)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe jetzt einige Zeit im Netz gesucht aber keine Lösung gefunden - 
vielleicht stehe ich auch einfach auf dem Schlauch...

Ich habe einen Atmega328p und möchte über den Analog Comparator den 
Ladezustand eines Kondensators messen um damit die Kapazität bestimmen 
zu können. Es sollen ca. 1pF-500pF gemessen werden.

Nun habe ich von dem ACSR Register und dem ACI Bit gelesen. Mir fehlt 
aber eine Vorlage um das auf Papier bringen zu können.

Ich möchte die interne ACBG Referenz verwenden und damit dann die Zeit 
messen bis der Kondensator (bis ACBG) aufgeladen ist.

Gibt es da ein Beispiel in C?

von Route_66 H. (route_66)


Bewertung
0 lesenswert
nicht lesenswert
Hugo P. schrieb:
> Gibt es da ein Beispiel in C?

Ein Beispiel für Zeitmessungen in C?
Nicht eins, sondern ganz sicher tausende und mehr!

Such dir eins aus.

von Wolfgang (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hugo P. schrieb:
> Ich habe einen Atmega328p und möchte über den Analog Comparator den
> Ladezustand eines Kondensators messen um damit die Kapazität bestimmen
> zu können. Es sollen ca. 1pF-500pF gemessen werden.

Ab gewisser Genauigkeitsanforderungen würde ich 1pF als "sportlich" 
bezeichnen.

von Hugo P. (portisch)


Bewertung
0 lesenswert
nicht lesenswert
Habe jetzt das probiert - jedoch macht der Atmega328 immer einen Reset!?
1
/*
2
 * AVRGCC1.c
3
 *
4
 * Created: 04.04.2018 15:02:16
5
 */ 
6
7
#include <avr/io.h>
8
#include <util/delay.h>
9
#include <avr/wdt.h>
10
#include <avr/interrupt.h>
11
12
#define CHARGE_C PC0
13
#define DISCHARGE_C PC1
14
15
#ifndef cbi
16
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
17
#endif
18
#ifndef sbi
19
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
20
#endif
21
22
#define  util_GetBitMask(bit)          (1<<(bit))
23
#define  util_IsBitSet(x,bit)          (((x)&(util_GetBitMask(bit)))!=0u)
24
25
void io_init(void)
26
{
27
  /* disable pullup on analog channels */
28
  cbi(DDRD, PD6);
29
  cbi(DDRD, PD7);
30
  PORTD &= ~(1 << PD6);                      // no Pull-up
31
  PORTD &= ~(1 << PD7);                      // no Pull-up
32
  
33
  ACSR |=
34
    (0 << ACD) |    //Comparator ON
35
    (1 << ACBG) |    //Connect 1.1V reference to AIN0
36
    (1 << ACIE) |    //Comparator Interrupt enable
37
    (0 << ACIC) |    //input capture disabled
38
    (1 << ACIS1) |    //set interrupt on falling edge (AIN1 > AIN0)
39
    (0 << ACIS0);    
40
    
41
  ACSR |= (1 << ACI);
42
}
43
44
int main(void)
45
{
46
  cli();
47
  wdt_disable();
48
  wdt_reset();
49
  
50
  DDRB = 0xFF;
51
  DDRC = 0xFF;
52
  DDRD = 0xFF;
53
  
54
  PORTB = 0;
55
  PORTC = 0;
56
  PORTD = 0;
57
  
58
  io_init();
59
  
60
  // global interrupt enable
61
  sei();    
62
  
63
    while(1)
64
    {
65
    // discharge C
66
    // set to input
67
    cbi(DDRC, CHARGE_C);        
68
    // set to output
69
    sbi(DDRC, DISCHARGE_C);
70
    
71
    // switch to 0 for discharge
72
    cbi(PORTC, DISCHARGE_C);
73
    // wait until C is discharged  
74
    _delay_ms(100);
75
    
76
    // set to input
77
    cbi(DDRC, DISCHARGE_C);        
78
    // set to output
79
    sbi(DDRC, CHARGE_C);
80
    
81
    // start charging
82
    sbi(PORTC, CHARGE_C);  
83
    
84
    // wait for interrupt
85
    while(!util_IsBitSet(ACSR, ACI))
86
      wdt_reset();
87
    
88
    _delay_ms(100);
89
    }
90
}
91
92
ISR(ANA_COMP_vect)
93
{
94
  // clear interrupt
95
  sbi(ACSR, ACI);
96
}

Ich komme bis zu dem while(!util_IsBitSet(ACSR, ACI)) - dann gibt es 
einen Reset. Im Debugger sehe ich, dass das ACI Flag gesetzt ist.

von Boris O. (bohnsorg) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Das Bit soll ja gesetzt sein, wenn der Komparator den gewünschten 
Zustand erkannt hat...was er vielleicht hat? Ich finde es auch ein wenig 
krude, die Ein- und Ausgabe-Steuerung auf die Makros zu verteilen und 
dahinter PC1 und PC0 zu legen. Ich hätte eher mit etwas in der Form DDRC 
= (1 << PC0) | (1 << PC1) gerechnet, irgendwie in Funktionen gekapselt. 
(Der Compiler macht daraus bei Bedarf ohnehin Inline-Konstrukte.)

von Hmmm (Gast)


Bewertung
0 lesenswert
nicht lesenswert
ist der gezeigte Code wirklich von dir?

von Hugo P. (portisch)


Bewertung
0 lesenswert
nicht lesenswert
Das ganze ist ja noch im Aufbau und nur für Tests gedacht. Jedoch komme 
ich nicht darauf warum der AVR einen Reset macht!?

In die ISR Routine kommt er gar nicht mehr.

: Bearbeitet durch User
von Reiner_Gast (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hugo P. schrieb:
> ISR(ANA_COMP_vect)
> {
>   // clear interrupt
>   sbi(ACSR, ACI);
> }
> Ich komme bis zu dem while(!util_IsBitSet(ACSR, ACI)) - dann gibt es
> einen Reset. Im Debugger sehe ich, dass das ACI Flag gesetzt ist.

Die While Schleife dürfte nie verlassen werden, weil laut Datenblatt das 
Bit bei Ausführung der ISR gelöscht wird:

"Bit 4 – ACI: Analog Comparator Interrupt Flag
This bit is set by hardware when a comparator output event triggers the 
interrupt mode defined by ACIS1 and
ACIS0. The Analog Comparator interrupt routine is executed if the ACIE 
bit is set and the I-bit in SREG is set.
ACI is cleared by hardware when executing the corresponding interrupt 
handling vector. Alternatively, ACI is
cleared by writing a logic one to the flag."

Hast du auch einen Schaltplan zu deinem Projekt?

von Hugo P. (portisch)


Bewertung
0 lesenswert
nicht lesenswert
Der Fehler:
1
ISR(ANA_COMP_vect)

Es gehört:
1
ISR(ANALOG_COMP_vect)

Nun gibt es keinen Reset mehr!
Der Compiler hat aber nicht gemeckert...

von Hugo P. (portisch)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt habe ich noch eine Frage. Ich habe nun den Source so umgebaut:
1
/*
2
 * AVRGCC1.c
3
 *
4
 * Created: 04.04.2018 15:02:16
5
 */ 
6
7
#include <avr/io.h>
8
#include <util/delay.h>
9
#include <avr/wdt.h>
10
#include <avr/interrupt.h>
11
#include "uart/uart.h"
12
#define BAUD  9600L
13
14
#define CHARGE_C PC0
15
#define DISCHARGE_C PC1
16
17
#ifndef cbi
18
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
19
#endif
20
#ifndef sbi
21
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
22
#endif
23
24
#define  util_GetBitMask(bit)          (1<<(bit))
25
#define  util_IsBitSet(x,bit)          (((x)&(util_GetBitMask(bit)))!=0u)
26
27
uint8_t average[100] = {0};
28
int actual_value;
29
30
void io_init(void)
31
{
32
  // all pins output
33
  DDRB = 0xFF;
34
  DDRC = 0xFF;
35
  DDRD = 0xFF;
36
  
37
  PORTB = 0;
38
  PORTC = 0;
39
  PORTD = 0;
40
    
41
  /* disable pull up on analog channels */  
42
  cbi(DDRD, PD6);      // switch to input
43
  cbi(DDRD, PD7);      // switch to input
44
  PORTD &= ~(1 << PD6);  // no Pull-up
45
  PORTD &= ~(1 << PD7);  // no Pull-up
46
  
47
  ACSR |=
48
    (0 << ACD) |    //Comparator ON
49
    (1 << ACBG) |    //Connect 1.1V reference to AIN0
50
    (0 << ACIE) |    //Comparator Interrupt enable
51
    (1 << ACIC) |    //input capture disabled
52
    (1 << ACIS1) |    //set interrupt on falling edge (AIN1 > AIN0)
53
    (0 << ACIS0);    
54
    
55
  ACSR |= (1 << ACI);
56
}
57
58
void discharge_c(void)
59
{
60
  // set to input
61
  cbi(DDRC, CHARGE_C);        
62
  // set to output
63
  sbi(DDRC, DISCHARGE_C);
64
    
65
  // switch to 0 for discharge
66
  cbi(PORTC, DISCHARGE_C);  
67
}
68
69
int main(void)
70
{
71
  cli();
72
  MCUSR = 0;
73
  wdt_disable();
74
75
  io_init();
76
  
77
  // timer 1 counter init
78
    TCCR1A =  0; //16 bit counter normal mode
79
  TCCR1B = (1 << ICNC1) | (1 << CS10); // INPUT CAPTURE EDGE DETECTOR SELECT TO RISING EDGE & and counter prescaler
80
  TIMSK1 = (1 << ICIE1);// enable input capture interrupt  
81
  
82
  // Clear all flags before beginning
83
  TIFR1 = (1 << ICF1);
84
  ACSR |= (1 << ACI);  
85
  
86
  // initialize uart  
87
  uart_init((UART_BAUD_SELECT((BAUD),F_CPU)));  
88
  
89
  uint8_t tifr;
90
  uart_putc(0xFF);
91
  
92
    while(1)
93
    {
94
    for (actual_value = 0; actual_value < sizeof(average); actual_value++)
95
    {
96
      // discharge C
97
      discharge_c();
98
      // wait until C is discharged  
99
      
100
      
101
      if (actual_value == sizeof(average) - 1)
102
        _delay_ms(20);
103
      else
104
        _delay_ms(10);
105
    
106
      // set to input
107
      cbi(DDRC, DISCHARGE_C);        
108
      // set to output
109
      sbi(DDRC, CHARGE_C);
110
      
111
      // start charging
112
      ACSR |= (1 << ACI);  
113
      
114
      // start counting
115
      ICR1 = 0;
116
      TIFR1 = _BV(ICF1) | _BV(OCF1A); // clear input capture and output compare flag bit
117
      PORTD |= ( 1 << PD5 );
118
      // start charging
119
      sbi(PORTC, CHARGE_C);  
120
      
121
      // wait until output is high
122
      while(! ( PINC & ( 1 << CHARGE_C )))
123
        ICR1 = 0;
124
    
125
      // wait for timer counter interrupt
126
      while(! (tifr = (TIFR1 & (_BV(ICF1) | _BV(OCF1A)))));
127
      
128
      if(!(tifr & _BV(OCF1A))) // check for overflow bit
129
      {
130
        // stop counting
131
        average[actual_value] = ICR1;        
132
        PORTD &= ~( 1 << PD5 );
133
        
134
        if (actual_value == sizeof(average) - 1)
135
        {
136
          uint32_t sum = 0;
137
    
138
          for (int loop = 0; loop < sizeof(average); loop++)
139
            sum = sum + average[loop];
140
      
141
          sum /= sizeof(average);
142
143
          uart_putc((sum >> 8) & 0xFF);
144
          uart_putc(sum & 0xFF);    
145
        }          
146
      }
147
      else
148
      PORTD &= ~( 1 << PD5 );  
149
    }      
150
    }
151
}

Mit dem Oszi messe ich eine Zeit von ~205µs. Ich habe einen 12MHz Quarz 
und einen Prescaler von 1. Somit sollte in ICR1 ein Wert von ~2500 
rauskommen. Ich bekomme aber bei 100 Werten im Mittel zwischen 120-140 
Counts.

Das sind dann 1/12000000 * 130 == ~11µs ??
Der PD5 wird nur als Trigger für das Oszi hergenommen (Channel 3, blau).
Channel 1, gelb, ist die Spannung am Kondensator.

von Hugo P. (portisch)


Bewertung
0 lesenswert
nicht lesenswert
EDIT: uint8_t average[100] = {0};
Kann nicht gehen in einem uint8_t array uint16_t Werte abzulegen.

Jetzt bekomme ich einen Wert von ~2475:
Das sind dann ca. 0,00020625 Sekunden. Passt also!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.