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?
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.
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.
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.
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.)
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
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?
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...
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.
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!
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.