Forum: Mikrocontroller und Digitale Elektronik PIC16F1705 ADC Samples


von Harry R. (harryr)


Lesenswert?

Hallo Leute,

mir ist kein wirklich gutes Subjekt eingefallen, daher habe ich es mal 
sehr allgemein formuliert. Ihr wisst sicher, dass ich als Newbie etwas 
Probleme habe, das was ich will auch in Worte/Begrifflichkeiten zu 
fassen,
die µC-kompatibel sind. Daher finde ich es auch schwer mir Antworten zu 
ergooglen (klar, wenn schon die Frage schlecht formuliert ist :-)

Was will ich ?

Ich möchte für einen Zeitabschnitt/Periode (nehmen wir hier mal 1msec)
analoge Werte via ADC sampeln. Die werden aufaddiert und dann der 
Mittelwert für diese Periode berechnet. Die Anzahl der Samples sei erst 
mal egal, Hauptsache ist, dass die Periode (relativ) exakt eingehalten 
wird.

Da es bei µC keinen Systemzeit/etc gibt wäre ein (grober Algorithmus 
ungefähr folgender :

1 Starte einen Timer/Zähler

2 Lese die Werte aus dem ADC ein

3 Wenn der Zähler abgelaufen ist : Mittelwertberechnung und dann etwas 
Programmlogik.

4 Zurück zu 1

Für euch sicher kein Problem mir dieses Vorgehen etwas genauer zu 
beschreiben, bzw C-Codefragmente, Tutorials oder ähnliches als Hilfe 
beizusteuern ?

Viele Grüße
Harry

von Franko P. (sgssn)


Lesenswert?

Hallo
Du könntest dich an dein Problem herantasten. So machen das im 
Allgemeinen alle. Zuerst mal ein ADC-programm zum Laufen bringen, dann 
einen Timer und dann beides verbinden. Weiss nicht, ob dein PIC vom MCC 
unterstützt wird, sonst kannst du da schon mal die Basis-Funktionen 
bekommen. Arbeitest du mit MPLAB X und dem XC8?

Und das hier gibzs auch noch:
https://github.com/sowd/pic16F1715_Analog_to_UART


Gruß

: Bearbeitet durch User
von Harry R. (harryr)


Lesenswert?

Franko P. schrieb:
> Hallo
> Du könntest dich an dein Problem herantasten. So machen das im
> Allgemeinen alle. Zuerst mal ein ADC-programm zum Laufen bringen, dann
> einen Timer und dann beides verbinden. Weiss nicht, ob dein PIC vom MCC
> unterstützt wird, sonst kannst du da schon mal die Basis-Funktionen
> bekommen. Arbeitest du mit MPLAB X und dem XC8?
>
> Gruß
Klar, aber manchmal möchte ich mich etwas schneller herantasten.
ADC habe ich soweit fertig, dass ausgelesen werden kann.

Der PIC wird von MCC unterstützt, Basis ist da, Begriffe wie FOSC FOSV4 
etc
finde ich verwirrend ....
Im Moment scheint mir folgender Link zur Timerprogrammierung 
vielversprechend:
https://deepbluembedded.com/timer-modules/

Ein allgemeines Problem bei der Suche nach Tutorials ist, dass viele 
schon irgendwie outdatet sind und man das erst merkt, wenn man viel Zeit 
verbraten hat.

Komme ich mit der "The general formula for Timer1 Module .." zum Ziel 
eine
Periode von 1msec zu implementieren ?

Viele Grüße

von Franko P. (sgssn)


Lesenswert?

Bei Samples ist es durchaus sinnvoll nur solche zu beachten, die auch 
für deinen PIC sind. Leider ist Microchip nicht kompatibel zu sich...

"Komme ich mit der "The general formula for Timer1 Module .." zum Ziel
eine
Periode von 1msec zu implementieren ?"

sagt mir nix. Wo steht das?

von Harry R. (harryr)


Lesenswert?

Franko P. schrieb:
> Bei Samples ist es durchaus sinnvoll nur solche zu beachten, die auch
> für deinen PIC sind. Leider ist Microchip nicht kompatibel zu sich...
>
> "Komme ich mit der "The general formula for Timer1 Module .." zum Ziel
> eine
> Periode von 1msec zu implementieren ?"
>
> sagt mir nix. Wo steht das?
Ziemlich in der Mitte der Seite auf die der Link zeigt.
VG

von Franko P. (sgssn)


Lesenswert?

Es wäre besser, wenn du dir das im Datenblatt anschaust.

Figure 25-1, Timer1 Block Diagramm sagt dir, was da alles dran ist. Wie 
schon gesagt, andere PICs bieten nicht unbedingt eine gute Beratung.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

man muss versuchen das Grundprinzip zu verstehen, weil das Prinzip ist 
immer gleich. Leider finde ich auch für andere PICs keine Timer AppNote. 
Aber 3 Anleitungen auf Youtube. Ob die qualitativ gut sind weiß ich 
nicht, wäre jedenfalls ein Anfang.

https://www.youtube.com/watch?v=I3x519D9NC0
https://www.youtube.com/watch?v=pAs9Up6Ap2M
https://www.youtube.com/watch?v=vD2jqWxuGEM

Auch wenn die sich nicht explizit auf deinen µC Typ beziehen kann man 
das adaptieren. Die Registernamen sollten gleich bzw. ähnlich sein. Mit 
einmal Timer Kapitel lesen im Manual sollte eine Anpassung möglich sein. 
Du musst verstehen was die warum machen und notfalls die gleiche 
Funktionalität in den Registern zusammensuchen. Also versteife dich 
nicht so sehr auf die Register- und Bitnamen.

von Harry R. (harryr)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> man muss versuchen das Grundprinzip zu verstehen, weil das Prinzip ist
> immer gleich. Leider finde ich auch für andere PICs keine Timer AppNote.
> Aber 3 Anleitungen auf Youtube. Ob die qualitativ gut sind weiß ich
> nicht, wäre jedenfalls ein Anfang.
>
> https://www.youtube.com/watch?v=I3x519D9NC0
> https://www.youtube.com/watch?v=pAs9Up6Ap2M
> https://www.youtube.com/watch?v=vD2jqWxuGEM
>
> Auch wenn die sich nicht explizit auf deinen µC Typ beziehen kann man
> das adaptieren. Die Registernamen sollten gleich bzw. ähnlich sein. Mit
> einmal Timer Kapitel lesen im Manual sollte eine Anpassung möglich sein.
> Du musst verstehen was die warum machen und notfalls die gleiche
> Funktionalität in den Registern zusammensuchen. Also versteife dich
> nicht so sehr auf die Register- und Bitnamen.

Danke für die Links.
Wie (fast immer) behandelt man in den Tutorials die Erzeugung eines 
"genauen" delays. Ich habe aber nach so etwas wie "Solange 1msec noch 
nicht vorbei ist" gesucht, denn in delays mit beliebigen Schleifen wird 
einfach nur "sinnlos" Zeit verbraten. Im Grunde genommen implementiere 
ich eine primitive Nebenläufigkeit mittels Zeitscheiben. Also so etwas 
wie:
1
wiederhole unendlich
2
{
3
  Eine Periode Task1
4
  Eine Periode Task2
5
  ...
6
  Eine Periode TaskN
7
}


Mit Hilfe von MCC habe ich nun folgendes gemacht:

o es wird eine Flagvariable instantiert, die mir zeigen soll, wenn eine 
Periode (hier: 1msec) abgelaufen ist:
1
volatile bool periodFinished = false;


o Timer0 wird so konfiguriert , dass er nach 1msec einen Interrupt 
auslöst
1
void TMR0_Initialize(void)
2
{
3
    // Set TMR0 to the options selected in the User Interface
4
  
5
    // PSA not_assigned; PS 1:2; TMRSE Increment_hi_lo; mask the nWPUEN and INTEDG bits
6
    OPTION_REG = (uint8_t)((OPTION_REG & 0xC0) | (0xD8 & 0x3F)); 
7
  
8
    // TMR0 131; 
9
    TMR0 = 0x83;
10
  
11
    // Load the TMR value to reload variable
12
    timer0ReloadVal= 131;
13
14
    // Clear Interrupt flag before enabling the interrupt
15
    INTCONbits.TMR0IF = 0;
16
17
    // Enabling TMR0 interrupt
18
    INTCONbits.TMR0IE = 1;
19
20
    periodFinished = false;
21
    // Set Default Interrupt Handler
22
    TMR0_SetInterruptHandler(TMR0_DefaultInterruptHandler);
23
}


o in der zugeordneten ISR wird ein Flag auf true gesetzt, wenn die msec 
vorbei ist.
1
void TMR0_DefaultInterruptHandler(void){
2
  periodFinished = true;
3
}

Jedes mal, wenn ich im main einen Programmzweig  habe, der genau eine
Periode dauern soll wird davor der Timer neu initialisiert
und dann solange der Zweig abgearbeitet, bis die Periode vorbei ist
1
while (!periodFinished)
2
{  
3
// TuwasSinvolles};
4
}

Was ich noch nicht gut gelöst habe ist, dass ich jedes mal den Timer neu 
initialisiere. Mir wäre eine startTimer und eine stopTimer-Function 
lieber.

Also:

o Initialisieren des Timers am Anfang von main()

Schleife :

a) Aufruf von startTimer wenn eine Periode Arbeit anliegt

b) Arbeiten bis Periode vorbei :  (periodFinished == true)

c) danach kann der Timer erst mal ruhen : stopTimer

d) zurück zu a)


So könnte ein startTimer stopTimer aussehen:
1
void startTimer()
2
{
3
  periodFinished = false;
4
  Irgendwie den Timer starten
5
}
6
7
void stopTimer()
8
{
9
  Irgendwie den Timer stopen/einfrieren deaktivieren
10
}

Wenn mir jemand einen Tipp geben könnte, wie ich start/stopTimer 
implementiere
wäre ich sehr dankbar.

Der verwendete µC ist übrigens ein PIC16F1705

Danke und Grüße

von Franko P. (sgssn)


Lesenswert?

Ich weiss nicht, warum du dich so gegen das Lesen des datenblattes 
sperrst.
Dieser PIC z.B. hat für mich eine Besonderheit, da der Timer0 nicht 
gestoppt oder gestartet werden kann. Der Timer 1 schon. T1CONbits.TMR1ON 
= 1; startet den Timer. Aber wenn du so eine Abneigung gegen Datnblätter 
hast, wäre Arduino oder ein anderer Compiler, wo es für alle Funktionen 
eine Funktion in einer C-Bibliothek gibt, besser für dich geeignet.

Gruß

von Harry R. (harryr)


Lesenswert?

Franko P. schrieb:
> Ich weiss nicht, warum du dich so gegen das Lesen des datenblattes
> sperrst.
> Dieser PIC z.B. hat für mich eine Besonderheit, da der Timer0 nicht
> gestoppt oder gestartet werden kann. Der Timer 1 schon. T1CONbits.TMR1ON
> = 1; startet den Timer. Aber wenn du so eine Abneigung gegen Datnblätter
> hast, wäre Arduino oder ein anderer Compiler, wo es für alle Funktionen
> eine Funktion in einer C-Bibliothek gibt, besser für dich geeignet.
>
> Gruß
Ich sperre mich nicht, ich decodiere vdie Datenblätter nicht exakt 
genug.
 :-)
Danke für den Hinweis

von Peter D. (peda)


Lesenswert?

Harry R. schrieb:
> Hauptsache ist, dass die Periode (relativ) exakt eingehalten
> wird.

Nun, das ist kein Problem, da die ADCs mit konstantem Takt laufen. Du 
läßt also den ADC durchlaufen, zählst die Anzahl der ADC-Interrupts mit 
und hast damit eine konstante Periode.
In den ADC-Interrupts addierst Du die Ergebnisse auf und teilst sie z.B. 
alle 256 Wandlungen /256, d.h. schmeißt das untere Byte der Summe weg.

: Bearbeitet durch User
von Volker S. (vloki)


Lesenswert?

Mir scheint bisher das ganze recht wenig zielgerichtet.

Im Datenblatt 20.2.5 AUTO-CONVERSION TRIGGER sind unter anderem die 
Timer 2,4,6 sowie die beiden CCP Module gelistet, welche bei einem Match 
(Timer == Periode, bzw. Compare) den ADC automatisch periodisch starten 
können.

Wenn der ADC fertig ist löst er, sofern entsprechend konfiguriert, einen 
Interrupt aus und man kann den Wert abspeichern bzw. aufaddieren
Dabei zählt man einfach mit, wie viele Werte man schon hat, berechnet 
bei entsprechender Anzahl den Mittelwert...

von Dirk F. (dirkf)


Lesenswert?

Hallo,
wenn dein PIC von MCC unterstützt wird,
dann benutze doch einfach die generierten Funktionen:

Hier ein Beispiel vom PIC32MZ:
1
void TMR4_Initialize(void);
2
void TMR4_Start(void);
3
void TMR4_Stop(void);
4
void TMR4_PeriodSet(uint16_t period);
5
uint16_t TMR4_PeriodGet(void);
6
uint16_t TMR4_CounterGet(void);
7
uint32_t TMR4_FrequencyGet(void);
8
void TMR4_InterruptEnable(void);
9
void TMR4_InterruptDisable(void);
10
void TMR4_CallbackRegister( TMR_CALLBACK callback_fn, uintptr_t context );

von Dirk F. (dirkf)


Lesenswert?

oder so ohne Interrupt per polling:
1
bool TMR4_PeriodHasExpired(void);

von Harry R. (harryr)


Lesenswert?

Volker S. schrieb:
> Mir scheint bisher das ganze recht wenig zielgerichtet.
>
> Im Datenblatt 20.2.5 AUTO-CONVERSION TRIGGER sind unter anderem die
> Timer 2,4,6 sowie die beiden CCP Module gelistet, welche bei einem Match
> (Timer == Periode, bzw. Compare) den ADC automatisch periodisch starten
> können.
>
> Wenn der ADC fertig ist löst er, sofern entsprechend konfiguriert, einen
> Interrupt aus und man kann den Wert abspeichern bzw. aufaddieren
> Dabei zählt man einfach mit, wie viele Werte man schon hat, berechnet
> bei entsprechender Anzahl den Mittelwert...

Das liest sich wie ein gutes Verfahren, ich werde es bei einer 
Optimierung des Programms ausprobieren. Im Moment liegt mein Focus 
darauf zu testen, ob meine Idee funktionieren könnte, es ist (noch) 
unoptimiert und relativ geradeaus implementiert.

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.