Grundsatzfrage zu AD-Wandler in Freerunnung Mode mit mehreren Kanälen

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Grundsatzfrage zu AD-Wandler in Freerunnung Mode mit mehreren Kanälen

      Hallo zusammen.

      Ich habe eine Grundsatzfrage zum AD-Wandler vom ATMega im Free-Running-Mode.

      Wenn ich das für einen Kanal brauche, sagen wir mal ADC0, dann konfiguriere ich den AD-MUX für den Freerun Mode und Kanal 0
      Und wenn ich jetzt nach jeder Wandlung eine ISR Aufrufe, kann ich dort immer den ADC-Wert von ADC0 auslesen und in ner Variable zwischenspeichern.

      Das ist kein Problem.

      Aber wenn ich jetzt sagen wir mal ADC0, dann ADC1 und dann ADC2 wandeln möchte, wird es schon komplizierter.

      Also wenn ich am Anfang den ADC0 konfiguriere und die Wandlung starte, wird ADC0 gewandelt. Die ISR wird aufgerufen und ich speichere den ADC0 wert in einer eigenen Variable. Jetzt möchte ich in der ISR auf den ADC1 (nächster Kanal) umschalten. Kann ich ja machen (MUX-Register ändern).

      Jetzt stellt sich aber die Frage. Wenn ich noch in der ISR bin und den Kanal ändere, wird in der Zeit bereits der ADC0 nochmal gewandelt? Oder wird die Wandlung erst fortgesetzt, wenn die ISR verlassen wurde?

      Ein Programm habe ich hierzu nicht, das kann man sich ja grob vorstellen. Ist ja erst mal ne Grundsatzfrage.
      Kaum macht man es richtig - und schon geht's!
    • elektron wrote:

      In der ISR: stop adc, mux ändern, start adc.
      Würde gehen ist aber nicht nötig. einfach den mux ändern und den nächsten Wert verwerfen.
      Laut Datenblatt soll der erste Wert nach der Änderung verworfen werden um Fehler durch den Ladungsunterschied der Kanäle zu verhindern. Bei Einzelmessungen konnte ich das nicht bestätigen. (1Mhz Systemtakt)
      Im Freerun ist es aber sicher nötig da er beim Aufruf der ISR schon wieder dabei ist zu konvertieren und dann der Kanal umgeschaltet wird.
    • Danke für den Link.
      Im Dokument wurde aber leider nicht die Umschaltung der Kanäle behandelt.
      Freerun mit einem Kanal ist ja kein Problem.

      Aber das verstehe ich jetzt nicht:
      Wieso sollte ich auf einzelne ADC's keinen Zugriff haben? Oder verstehe ich da was falsch?

      Es wird ja immer der Kanal gewandelt, der im Mux-Register angegeben ist, oder nicht?
      Ich will ja nicht mit GetADC() die Werte holen. Das hätte ich vielleicht erwähnen sollen.

      Und mit Config lege ich ja nicht den ADC3 oder so fest. Sondern Referenz, Prescaler etc.
      Der ADC0 wird erst mal voreingestellt. Soweit ich das im Simulator verfolgen kann.

      Im Datenblatt sind die Infos bezüglich Freerun Mode etwas verstreut.

      In einem Zeitdiagramm zum Freerun-Mode wird das MUX-Register übernommen zwischen dem 13 ADC-Takt und dem 1. ADC-Takt der Folgewandlung. Offensichtlich muss man in der Interrupt Routine das MUX-Register ändern, nachdem mindestens ein ADC-Takt vergangen ist. Der Wert wird dann übernommen, wenn die laufende Wandlung beendet ist (nächster Interrupt) und die nächste Wandlung startet. Also wieder zwischen dem 13. und den 1. ADC-Takt.

      Mit ADC-Takt meine ich dabei den Clock, den der ADC bekommt.

      Nach Config ADC=Free,Preescaler=Auto, ... wird für den Prescaler 128 eingestellt (Der darf ja nicht mehr als 200kHz bekommen, wenn der mit 10 Bit auflösen soll).

      Bei 16MHz CPU-Takt wäre der ADC-Takt demnach 125kHz. Oder anders gesagt: Ein ADC Takt sind 128 CPU Takte.

      Gerechnet wird ab dem Zeitpunkt, bei dem das ADIF-Flag (ADC-Interrupt) gesetzt wird. Dann kommt ja der Sprung in die ISR und das Auslesen des Wertes. Das braucht alles CPU-Takte. Nach mehr als 128 CPU-Takte kann man dann den MUX-Wert setzen.

      Das ist alles verwirrend, weil man immer im Voraus denken muss.

      Irgendwo im Datenblatt habe ich auch gelesen, dass im Freerun-Mode die nächste Wandung direkt startet, unabhängig davon, ob das ADIF gesetzt ist oder nicht. Daher muss der Wert für den nächsten Kanal schon im MUX-Register stehen, bevor der Übergang von 13. zum 1. Takt statt findet.

      Hintergrund der Sache ist, ob und wie es geht, wenn man mehrere Analog-Kanäle loggen oder Sampeln will.

      Ich dachte, die Kanäle einfach im Hintergrund wandeln zu und die Werte in Variablen bereitstellen. Und wenn ich einen Wert brauche, wird der nicht erst gewandelt, sondern aus der Variable ausgelesen. Damit habe ich keine Latenzzeit von der Software.

      Gibt es noch Vorschäge oder Ideen?
      Kaum macht man es richtig - und schon geht's!
    • Pluto25 wrote:

      elektron wrote:

      In der ISR: stop adc, mux ändern, start adc.
      Würde gehen ist aber nicht nötig. einfach den mux ändern und den nächsten Wert verwerfen.Laut Datenblatt soll der erste Wert nach der Änderung verworfen werden um Fehler durch den Ladungsunterschied der Kanäle zu verhindern. Bei Einzelmessungen konnte ich das nicht bestätigen. (1Mhz Systemtakt)
      Im Freerun ist es aber sicher nötig da er beim Aufruf der ISR schon wieder dabei ist zu konvertieren und dann der Kanal umgeschaltet wird.
      Wenn ich den 1. Wert immer verwerfe, dann habe ich nur halbe Sämpelrate.
      Ich denke es sollte auch mit voller Speed gehen.

      Weil schon die nächste Konvertierung gestartet wurde, wenn man in der ISR ankommt, kann man natürlich die laufende Wandlung nicht mehr ändern. Der nächste MUX-Wert muss also schon in der vorherigen ISR gesetzt werden.

      Ich weis, es ist verwirrend.

      Aber man kann vielleicht geschickt mit ner Variable steuern, welcher Kanal einzustellen ist und welcher auszuwerten ist.
      Kaum macht man es richtig - und schon geht's!
    • Also die Stelle, wo das mit den Ladungsunterschieden stehen soll, konnte ich im Datenblatt nicht finden.

      Aber die Passage, wie man das MUX-Register ändern kann.

      Auszug Datenblatt:

      Changing Channel r Reference Selection
      The MUXn and REFS1:0 bits in the ADMUX Register are single buffered through a temporary
      register to which the CPU has random access. This ensures that the channels and reference
      selection only takes place at a safe point during the conversion. The channel and reference
      selection is continuously updated until a conversion is started. Once the conversion starts, the
      channel and reference selection is locked to ensure a sufficient sampling time for the ADC. Continuous
      updating resumes in the last ADC clock cycle before the conversion completes (ADIF in
      ADCSRA is set). Note that the conversion starts on the following rising ADC clock edge after
      ADSC is written. The user is thus advised not to write new channel or reference selection values
      to ADMUX until one ADC clock cycle after ADSC is written.
      If both ADFR and ADEN is written to one, an interrupt event can occur at any time. If the
      ADMUX Register is changed in this period, the user cannot tell if the next conversion is based
      on the old or the new settings.


      ADMUX can be safely updated in the following ways:



      1. When ADFR or ADEN is cleared
      2. During conversion, minimum one ADC clock cycle after the trigger event
      3. After a conversion, before the Interrupt Flag used as trigger source is cleared

      When updating ADMUX in one of these conditions, the new settings will affect the next ADC
      conversion.

      Ich habe die Stelle fett markiet. Da steht (wenn ich das richtig übersetze) das nach mindestens einen ADC-Tykt
      warten soll, bevor das MUX-Register geändert werden darf. Das sind dann die 128 CPU-Takte (gerechnet am Setzen des Flags ADIF).

      Das 3. verstehe ich aber nicht. Kann das jemand verständlich erklären?

      Ein Stück weiter unten ist dann im Datenblatt nochmal angegeben:

      In Free Running mode, always select the channel before starting the first conversion. The channel
      selection may be changed one ADC clock cycle after writing one to ADSC. However, the
      simplest method is to wait for the first conversion to complete, and then change the channel
      selection. Since the next conversion has already started automatically, the next result will reflect
      the previous channel selection. Subsequent conversions will reflect the new channel selection.
      Kaum macht man es richtig - und schon geht's!
    • Ich habe mal versucht, die Wandlung mit Freeran über mehrere Kanäle in einen Pseudocode darzustellen.

      BASCOM Source Code

      1. ' AD-Wandler Freerun-Mode über mehrere Kanäle
      2. ' Den Code habe ich zur Veranschaulichung meiner Frage geschrieben.
      3. $Regfile = "m8def.dat"
      4. $HWStack = 32
      5. $SWStack = 32
      6. $Framesize = 32
      7. $Crystal = 16000000
      8. ' Zwischenspeicher für die 3 ADC-Kanäle ADC0 bis ADC2
      9. Dim ADC_0 as Word
      10. Dim ADC_1 as Word
      11. Dim ADC_2 as Word
      12. Dim ADC_KANAL as Byte ' Gewandelter Kanal für folgenden Interrupt
      13. ' Makros zum Starten und Stoppen des Freerun-Mode
      14. Macro ADC_START
      15. Set ADCSRA.ADFR ' Freerun-Mode an
      16. Set ADCSRA.ADSC ' Wandlung starten
      17. End Macro
      18. Macro ADC_STOP
      19. Reset ADCSRA.ADFR ' Beendet Freerun-Mode
      20. End Macro
      21. ' ADC konfigurieren
      22. Config ADC = Single , Prescaler = Auto , Reference = Internal_2.56
      23. On ADC ISR_ADC ' ISR festlegen
      24. Enable ADC ' ADC-Interrupt freigeben
      25. Enable Interrupts
      26. ' Freerun-Mode anstoßen
      27. ADC_Kanal = 0
      28. ADC_START
      29. Do
      30. Loop
      31. ' ISR für 3 Kanäle die nacheinander im Freerun Mode gewandelt werden.
      32. ' Beim Setzen des MUX-Registers muss man 2 Schritte im Voraus denken, da bei
      33. ' beim Aufruf der ISR bereits wieder eine Wandlung gestartet hat.
      34. ISR_ADC:
      35. Select Case ADC_Kanal
      36. Case 0 ' Kanal 0 auswerten
      37. ' Kanal 0 auslesen
      38. ' MUX für Kanal 2 (setzen nach 128 Takten nachdem ADIF gesetzt wurde)
      39. ADC_Kanal = 1
      40. Case 1 ' Kanal 1 auswerten
      41. ' Kanal 1 auslesen
      42. ' MUX für Kanal 0 (seten nach 128 Takten)
      43. ADC_Kanal = 2
      44. Case 2 ' Kanal 2 auswerten
      45. ' Kanal 2 auslesen
      46. ' MUX für Kanal 1 setzen
      47. ADC_Kanal = 0
      48. End Select
      49. Return
      Display All
      So etwa stelle ich mir das vor.

      In der ISR wird der übernächste Kanal gesetzt, der zu wandeln ist. Man muss hier 2 Schritte im Voraus denken.
      So könnte es Meiner Meinung nach funktionieren.
      Kaum macht man es richtig - und schon geht's!
    • Klingt aufwändig. So müsste die Belegung beachtet werden.

      Source Code

      1. $regfile = "attiny13a.dat"
      2. $crystal = 9600000
      3. $hwstack=16
      4. $swstack = 16
      5. $framesize = 16
      6. Config Base = 0
      7. Open "COMb.1:9600,8,n,1,inverted" For Output As #1
      8. Dim A As Byte , Ia As Byte
      9. Dim Wert(4) As Word , Tempw As Word
      10. Dim Templ As Byte At Tempw Overlay
      11. Dim Temph As Byte At Tempw + 1 Overlay
      12. Config Adc = Free , Prescaler = Auto , Reference = Avcc
      13. On Adc Adc_isr
      14. Enable Adc
      15. Do
      16. Disable Interrupts 'damit der SW-Uart keinen Quatsch ausgibt
      17. For A = 0 To 3
      18. Print #1 , "Kanal " + Str(a) + "=" + Str(wert(a))
      19. Waitms 100
      20. Next
      21. Print #1,
      22. Enable Interrupts
      23. Waitms 500
      24. Loop
      25. End
      26. Adc_isr:
      27. Admux = Ia 'Ref=Vcc
      28. Templ = Adcl
      29. Temph = Adch
      30. Wert(ia) = Tempw
      31. Incr Ia
      32. If Ia >= 4 Then Ia = 0
      33. Return
      Display All
    • @Pluto25
      Und wie stellst du sicher, dass der gewandelte Kanal im richtigen Array-Index landet?

      Also ich hätte gerne ADC0-Wert in Array Wert(0), ADC1-Wert in Wert(1) etc.
      Das wird bei deinem Code nicht korrekt sein.

      Im Prinzip mache ich es aber auch nicht anders als du.
      ADC-Wert auslesen und Mux setzen in der ISR und einen Kanal weiter schalten. Dann ISR verlassen.

      Hast du deinen Code ausprobiert?
      Kaum macht man es richtig - und schon geht's!
    • Mitch64 wrote:

      Und wie stellst du sicher, dass der gewandelte Kanal im richtigen Array-Index landet?
      Gar nicht, daher die Belegung beachten. Natürlich könnte man das noch umsortieren aber da es zeitkritisch ist wäre die Hardwarelösung besser.

      Mitch64 wrote:

      Hast du deinen Code ausprobiert?
      Ja, daher kam ich auf die Unterbrechung beim Print.
      Und ADC(2)=Kanal 0 ??
      Hier die verbesserte Version:

      Source Code

      1. $regfile = "attiny13a.dat"
      2. $crystal = 9600000
      3. $hwstack=16
      4. $swstack = 16
      5. $framesize = 16
      6. Config Base = 0
      7. Open "COMb.1:115200,8,n,1,inverted" For Output As #1
      8. Dim A As Byte , Ia As Byte
      9. Dim Temp(5)as Word , Tempw As Word
      10. Dim Wert(4) As Word At Temp + 2 Overlay
      11. Dim Templ As Byte At Tempw Overlay
      12. Dim Temph As Byte At Tempw + 1 Overlay
      13. Config Adc = Free , Prescaler = Auto , Reference = Avcc
      14. On Adc Adc_isr
      15. Enable Adc
      16. Do
      17. Disable Interrupts 'damit der SW-Uart keinen Quatsch ausgibt
      18. Temp(4) = Temp(0)
      19. For A = 0 To 3
      20. Print #1 , "K " + Str(a) + "=" + Str(wert(a))
      21. Next
      22. Print #1,
      23. Enable Interrupts
      24. Waitms 500
      25. Loop
      26. End
      27. Adc_isr:
      28. Templ = Adcl
      29. Temph = Adch
      30. Temp(ia) = Tempw
      31. Incr Ia
      32. If Ia >= 4 Then Ia = 0
      33. Admux = Ia 'Ref=Vcc
      34. Return
      Display All
      Jetzt stimmen die Kanalnummern.

      The post was edited 1 time, last by Pluto25 ().

    • @Pluto25
      Du könntest deine Variante noch verbessern, indem du 2 Buffer verwendest. Einen in den die ISR schreibt und einen, den du zur Ausgabe benutzt. Und dann den Interrupt nur so lange deaktivierst, bis die Daten von der ISR in den 2. Buffer übergeben sind. So habe ich es gemacht.

      Damit blockierst du nicht die Wandlung, bis seriell die Daten übertragen sind.

      Ich habe mein Konzept weiter verfolgt und quasi über die Select Case die Daten geholt und die MUX gesetzt. Um die CPU möglichst gering zu belasten habe ich die ISR_ADC in Assembler geschrieben.

      Hier mein funktionierender und getesteter Code:

      BASCOM Source Code

      1. ' AD-Wandler Freerun-Mode über mehrere Kanäle
      2. ' Funktionierender Code, Kanaldaten werden korrekten in die Kanal-Variablen abgelegt.
      3. ' Bei CPU-Takt = 16MHz wird bei Auto-Prescaler (Config ADC) ein Prescaler von 128 eingestellt.
      4. ' Der AD-Wandler bekommt daher ein ADC-Takt vom 16MHz / 128 = 125kHz.
      5. ' Alle 13 ADC-Takte ist eine Wandlung abgeschlossen. Somit ergibt sich eine
      6. ' Pulsfrequenz am PortB.1 von 9615 Hz. Da wir 4 Kanäle verwenden entspricht das einer
      7. ' Sampelfrequenz von 9615 / 4 = 2403 Hz.
      8. ' Durch Verwendung von Assembler in der ISR_ADC haben wir eine CPU-Auslastung von 3,4%.
      9. $Regfile = "m8def.dat"
      10. $HWStack = 32
      11. $SWStack = 32
      12. $Framesize = 32
      13. $Crystal = 16000000
      14. $Baud = 19600
      15. ' Zwischenspeicher für die 3 ADC-Kanäle ADC0 bis ADC2
      16. Dim ADC_0 as Word
      17. Dim ADC_1 as Word
      18. Dim ADC_2 as Word
      19. Dim ADC_3 as Word
      20. Dim ADC_KANAL as Byte ' Gewandelter Kanal für folgenden Interrupt
      21. ' Variablen um die Kanaldaten auszugeben
      22. Dim K0 as Word
      23. Dim K1 as Word
      24. Dim K2 as Word
      25. Dim K3 as Word
      26. ' Makros zum Starten und Stoppen des Freerun-Mode
      27. Macro ADC_START
      28. Set ADCSRA.ADFR ' Freerun-Mode an
      29. Set ADCSRA.ADSC ' Wandlung starten
      30. End Macro
      31. Macro ADC_STOP
      32. Reset ADCSRA.ADFR ' Beendet Freerun-Mode
      33. End Macro
      34. ' Am Pin wird pro Aufruf von Label ISR_ADC ein Puls ausgegeben
      35. ' Dient zur Kontrolle der Sampelfrequenz
      36. Config PortB.1 = Output ' Pin für Oszi-Kontrolle
      37. ' ADC konfigurieren
      38. Config ADC = Free , Prescaler = Auto , Reference = Internal_2.56
      39. On ADC ISR_ADC NoSave ' ISR festlegen
      40. Enable ADC ' ADC-Interrupt freigeben
      41. Enable Interrupts
      42. ' Freerun-Mode anstoßen
      43. ADC_Kanal = 0
      44. ADC_START ' Wandlung muss angestoßen werden
      45. Do
      46. Disable Interrupts
      47. K0 = ADC_0
      48. K1 = ADC_1
      49. K2 = ADC_2
      50. K3 = ADC_3
      51. Enable Interrupts
      52. Print "K0:" ; K0 ; " K1:" ; K1 ; " K2:" ; K2 ; " K4:" ; K3
      53. Waitms 500
      54. Loop
      55. ' ISR für 4 Kanäle die nacheinander im Freerun Mode gewandelt werden.
      56. ' Beim Setzen des MUX-Registers muss man 2 Schritte im Voraus denken, da
      57. ' beim Aufruf der ISR bereits wieder eine Wandlung gestartet hat.
      58. ' Durchschnittlich 56,5 Takte incl. Ein und Rücksprung.
      59. ' CPU-Auslastung bei CPU-Clock 16MHz = 3,4% (gerundet).
      60. ISR_ADC:
      61. !SBI PortB,1 ' Control-Pin Oszi an
      62. !PUSH r16 ' verwendete Register sichern
      63. !IN r16,sreg
      64. !PUSH r16
      65. !PUSH r17
      66. !PUSH r24
      67. !PUSH r25
      68. !IN r24,ADCL ' ADC-Wert einlesen
      69. !IN r25,ADCH
      70. !LDS r16,{ADC_Kanal} ' Kanal-Nr lesen
      71. !CPI r16,0
      72. !BREQ ISR_ADC_0 ' Sprung bei Kanal=0
      73. !CPI r16,1
      74. !BREQ ISR_ADC_1 ' Sprung bei Kanal=1
      75. !CPI r16,2
      76. !BREQ ISR_ADC_2 ' Sprung bei Kanal=2
      77. !CPI r16,3
      78. !BREQ ISR_ADC_3 ' Sprung bei Kanal=3
      79. !RJMP ISR_ADC_EXIT
      80. ISR_ADC_0: ' Kanal 0 lesen
      81. !STS {ADC_0 + 0},r24
      82. !STS {ADC_0 + 1},r25
      83. !LDI r24,$C2 ' AREF internal, Kanal 2 setzen
      84. !OUT ADMUX,r24
      85. !RJMP ISR_ADC_EXIT
      86. ISR_ADC_1: ' Kanal 1 lesen
      87. !STS {ADC_1 + 0},r24
      88. !STS {ADC_1 + 1},r25
      89. !LDI r24,$C3 ' AREF internal, Kanal 3 setzen
      90. !OUT ADMUX,r24
      91. !RJMP ISR_ADC_EXIT
      92. ISR_ADC_2: ' Kanal 2 lesen
      93. !STS {ADC_2 + 0},r24
      94. !STS {ADC_2 + 1},r25
      95. !LDI r24,$C0 ' AREF internal, Kanal 0 setzen
      96. !OUT ADMUX,r24
      97. !RJMP ISR_ADC_EXIT
      98. ISR_ADC_3: ' Kanal 3 lesen
      99. !STS {ADC_3 + 0},r24
      100. !STS {ADC_3 + 1},r25
      101. !LDI r24,$C1 ' AREF internal, Kanal 1 setzen
      102. !OUT ADMUX,r24
      103. ISR_ADC_EXIT:
      104. !INC r16 ' Kanal +1
      105. !CPI r16,4 ' Prüfen, ob letzter Kanal
      106. !SBIC SREG,1 ' Z-Flag
      107. !CLR r16 ' Kanal auf 0 setzen
      108. !STS {ADC_Kanal},r16
      109. !POP r25 ' verwendete Register wieder herstellen
      110. !POP r24
      111. !POP r17
      112. !POP r16
      113. !OUT SREG,r16
      114. !POP r16
      115. !CBI PortB,1 ' Control-Pin Oszi aus
      116. Return
      Display All
      Zugegeben, mein Code ist etwas komplexer als deiner, dafür kann man sicher die Kanäle unterscheiden.
      CPU-Auslastung liegt bei 3,4% bei 16MHz CPU-Takt. Gewandelt werden 4 Kanäle. Sample-Rate je Kanal ist somit bei 2004Hz.

      Habe alles wichtige auch im Code dokumentiert.

      Die ISR_ADC wird mit 9615 Hz aufgerufen. Teilt man die Frequenz durch die Anzahl kanäle erhält man die Samplerate pro Kanal. Bei nur einem Kanal wäre die Sampelrate bei 9615Hz, bei 8 Kanälen bei 1201Hz pro Kanal. Das nur mal am Rande.

      Will man jetzt nicht nur die Werte in den 4 Variablen ablegen, sondern über eine gewisse Dauer ein Signal aufzeichnen, dann schreibt die ISR dann eben die Werte in das entsprechende Buffer-Array und beendet dann den Freerun-Mode, wenn der Buffer voll ist. Der Code ist hierbei logischerweise anzupassen. Aber ohne viel Aufwand machbar.

      Hoffe mal, dass das Demo hier auch mal nützlich ist.

      Danke an alle für eure Hilfe!

      Vielleicht gibt's ja noch Vorbesserungs-Verschläge?
      Kaum macht man es richtig - und schon geht's!
    • Du meinst in meinem Code?

      An und an wird mal ein Zeichen falsch übertragen habe ich bemerkt.

      Habe 19200 Baud eingestellt.

      Mit Baudrate wie du (115200) habe ich das Problem noch immer.
      Anstelle alle Interrupt abzuschalten nur den ADC-Interrupt zu deaktivieren brachte auch keine Besserung.

      Habe dann noch SerialOut=Buffered gemacht, aber Problem bleibt.

      Warum sich die beiden Interrupts in die Haare kriegen weiß ich jetzt auch nicht.

      Ich muss mal die ISR checken ob da alle verwendeten Register wieder richtig hergestellt werden.
      Sonst weiß ich jetzt im Moment auch nicht.
      Kaum macht man es richtig - und schon geht's!

      The post was edited 1 time, last by Mitch64 ().

    • So scheint es stabil zu laufen.

      Baudrate 38400, ADC deaktivieren (nicht alle Interrupts) und ohne SerialOut-Buffer.

      Hier der Code nochmal:

      BASCOM Source Code

      1. ' AD-Wandler Freerun-Mode über mehrere Kanäle
      2. ' Funktionierender Code, Kanaldaten werden korrekten in die Kanal-Variablen abgelegt.
      3. ' Bei CPU-Takt = 16MHz wird bei Auto-Prescaler (Config ADC) ein Prescaler von 128 eingestellt.
      4. ' Der AD-Wandler bekommt daher ein ADC-Takt vom 16MHz / 128 = 125kHz.
      5. ' Alle 13 ADC-Takte ist eine Wandlung abgeschlossen. Somit ergibt sich eine
      6. ' Pulsfrequenz am PortB.1 von 9615 Hz. Da wir 4 Kanäle verwenden entspricht das einer
      7. ' Sampelfrequenz von 9615 / 4 = 2403 Hz.
      8. ' Durch Verwendung von Assembler in der ISR_ADC haben wir eine CPU-Auslastung von 3,4%.
      9. $Regfile = "m8def.dat"
      10. $HWStack = 32
      11. $SWStack = 32
      12. $Framesize = 32
      13. $Crystal = 16000000
      14. $Baud = 38400
      15. ' Zwischenspeicher für die 3 ADC-Kanäle ADC0 bis ADC2
      16. Dim ADC_0 as Word
      17. Dim ADC_1 as Word
      18. Dim ADC_2 as Word
      19. Dim ADC_3 as Word
      20. Dim ADC_KANAL as Byte ' Gewandelter Kanal für folgenden Interrupt
      21. ' Variablen um die Kanaldaten auszugeben
      22. Dim K0 as Word
      23. Dim K1 as Word
      24. Dim K2 as Word
      25. Dim K3 as Word
      26. ' Makros zum Starten und Stoppen des Freerun-Mode
      27. Macro ADC_START
      28. Set ADCSRA.ADFR ' Freerun-Mode an
      29. Set ADCSRA.ADSC ' Wandlung starten
      30. End Macro
      31. Macro ADC_STOP
      32. Reset ADCSRA.ADFR ' Beendet Freerun-Mode
      33. End Macro
      34. ' Am Pin wird pro Aufruf von Label ISR_ADC ein Puls ausgegeben
      35. ' Dient zur Kontrolle der Sampelfrequenz
      36. Config PortB.1 = Output ' Pin für Oszi-Kontrolle
      37. ' ADC konfigurieren
      38. Config ADC = Free , Prescaler = Auto , Reference = Internal_2.56
      39. On ADC ISR_ADC NoSave ' ISR festlegen
      40. Enable ADC ' ADC-Interrupt freigeben
      41. Enable Interrupts
      42. ' Freerun-Mode anstoßen
      43. ADC_Kanal = 0
      44. ADC_START ' Wandlung muss angestoßen werden
      45. Do
      46. Disable ADC
      47. K0 = ADC_0
      48. K1 = ADC_1
      49. K2 = ADC_2
      50. K3 = ADC_3
      51. Enable ADC
      52. Print "K0:" ; K0 ; " K1:" ; K1 ; " K2:" ; K2 ; " K4:" ; K3
      53. Waitms 500
      54. Loop
      55. ' ISR für 4 Kanäle die nacheinander im Freerun Mode gewandelt werden.
      56. ' Beim Setzen des MUX-Registers muss man 2 Schritte im Voraus denken, da
      57. ' beim Aufruf der ISR bereits wieder eine Wandlung gestartet hat.
      58. ' Durchschnittlich 56,5 Takte incl. Ein und Rücksprung.
      59. ' CPU-Auslastung bei CPU-Clock 16MHz = 3,4% (gerundet).
      60. ISR_ADC:
      61. !SBI PortB,1 ' Control-Pin Oszi an
      62. !PUSH r16 ' verwendete Register sichern
      63. !IN r16,sreg
      64. !PUSH r16
      65. !PUSH r17
      66. !PUSH r24
      67. !PUSH r25
      68. !IN r24,ADCL ' ADC-Wert einlesen
      69. !IN r25,ADCH
      70. !LDS r16,{ADC_Kanal} ' Kanal-Nr lesen
      71. !CPI r16,0
      72. !BREQ ISR_ADC_0 ' Sprung bei Kanal=0
      73. !CPI r16,1
      74. !BREQ ISR_ADC_1 ' Sprung bei Kanal=1
      75. !CPI r16,2
      76. !BREQ ISR_ADC_2 ' Sprung bei Kanal=2
      77. !CPI r16,3
      78. !BREQ ISR_ADC_3 ' Sprung bei Kanal=3
      79. !RJMP ISR_ADC_EXIT
      80. ISR_ADC_0: ' Kanal 0 lesen
      81. !STS {ADC_0 + 0},r24
      82. !STS {ADC_0 + 1},r25
      83. !LDI r24,$C2 ' AREF internal, Kanal 2 setzen
      84. !OUT ADMUX,r24
      85. !RJMP ISR_ADC_EXIT
      86. ISR_ADC_1: ' Kanal 1 lesen
      87. !STS {ADC_1 + 0},r24
      88. !STS {ADC_1 + 1},r25
      89. !LDI r24,$C3 ' AREF internal, Kanal 3 setzen
      90. !OUT ADMUX,r24
      91. !RJMP ISR_ADC_EXIT
      92. ISR_ADC_2: ' Kanal 2 lesen
      93. !STS {ADC_2 + 0},r24
      94. !STS {ADC_2 + 1},r25
      95. !LDI r24,$C0 ' AREF internal, Kanal 0 setzen
      96. !OUT ADMUX,r24
      97. !RJMP ISR_ADC_EXIT
      98. ISR_ADC_3: ' Kanal 3 lesen
      99. !STS {ADC_3 + 0},r24
      100. !STS {ADC_3 + 1},r25
      101. !LDI r24,$C1 ' AREF internal, Kanal 1 setzen
      102. !OUT ADMUX,r24
      103. ISR_ADC_EXIT:
      104. !INC r16 ' Kanal +1
      105. !CPI r16,4 ' Prüfen, ob letzter Kanal
      106. !SBIC SREG,1 ' Z-Flag
      107. !CLR r16 ' Kanal auf 0 setzen
      108. !STS {ADC_Kanal},r16 ' Kanal speichern für nächsten Interrupt
      109. !POP r25 ' verwendete Register wieder herstellen
      110. !POP r24
      111. !POP r17
      112. !POP r16
      113. !OUT SREG,r16
      114. !POP r16
      115. !CBI PortB,1 ' Control-Pin Oszi aus
      116. Return
      Display All
      Kaum macht man es richtig - und schon geht's!
    • Das liegt wohl daran, dass du den Uart mit Software emulierst.

      Beim Display ist das aber nicht so offensichtlich. Die Timings werden ja eher langsamer als zu schnell.
      Also am I2C sollte es nicht liegen.

      Bei mir konnte ich jetzt keinen Fehler in der ISR finden. Und rein von der Logik kann es eigentlich nicht sein, dass sich die Interrupts vom UART und ADC in die Haare kriegen.

      Da scheint wohl noch ein Software-Problem vorzuliegen. Aber wenn meine ISR ausscheidet,
      was bleibt dann noch übrig?

      Könnte es vielleicht noch mit der Fehlerrate bei der seriellen Übertragung ein Problem geben?
      Quarzbedingt geht die Baudrate eben nicht glatt auf. Da sollte man dann mal ein 3,6864MHZ oder ein 14,7456MHz Quarz probieren.
      Kaum macht man es richtig - und schon geht's!
    • Mitch64 wrote:

      Könnte es vielleicht noch mit der Fehlerrate bei der seriellen Übertragung ein Problem geben?
      Bei mir ganz klar: der int schreddert das Timing. Bei Dir unwarscheinlich der HW Uart gibt das Byte zeitrichtig aus und wenn das nächste verzögert gesendet wird kann das kein Problem werden. Eine hohe Fehlerrate (zu schnell) ergäbe falsche Zeichen ab einer bestimmten Stelle und (viel) zu langsam würde jedes Zeichen fälschen wobei die Anzahl immer stimmt. Quarze sind eigendlich nur bei hohen Baudraten (>56k) empfehlenswert. Hier "spechen" fast alle ohne Quarz miteinander egal wie warm/kalt.
      Beim Display kommt immer nur eine Ziffer an. Das liegt vermutlich daran das der PCF "aussteigt" Das SCL Signal sieht sehr zerschossen aus. (oder er erinnert sich daran das er die falsche Adresse hat ;( )

      Mitch64 wrote:

      Und rein von der Logik kann es eigentlich nicht sein, dass sich die Interrupts vom UART und ADC in die Haare kriegen.
      Es sei denn der Stack wäre zu klein. Benutzt die HW Ausgabe denn einen Int?
    • Ja ich verwende den Hardware-Uart.
      Und ob der einen Interrupt benutzt?

      Zumindest muss generell Enable Interrupts aktiviert sein. Sonst gehts die Hardware-Ausgabe nicht.
      Ich nehme an, hier wird nur das Interrupt-Flag abgefragt (gepollt).

      Aber wenn man Buffer verwendet (SerialIn|SerialOut=Buffered), dann werden auf jeden Fall Interrupts verwendet.


      Zu der Verwendung von Quarzen oder nicht bei UARTs kann ich nur eins sagen.
      Ich habe mir mal einen Wolf gesucht, weil die serielle Kommunikation immer wieder Fehler hatte.
      Hatte alles mögliche probiert und am Ende lag es daran, dass ich keinen Quarz verwendet hatte.

      Seither verwende ich grundsätzlich immer einen Quarz, sobald die serielle Schnittstelle verwendet wird.
      Seither habe ich keine Probleme mehr.
      Kaum macht man es richtig - und schon geht's!
    • Fehlerbeseitigung bei der seriellen Übertragung.

      Ich habe den RxD vom Mega mit einem PullUp auf VCC gezogen. Dadurch hängt der Pin nicht in der Luft und empfängt unkontrolliert wirre Zeichen. Das hat definitiv schon eine Besserung gebracht.
      Alternativ wäre auch Ausschalten des Empfängers gegangen.

      Ich habe dann mit verschiedenen Baudraten rumprobiert (CPU-Takt 16MHz). 38400 Baud ging noch fehlerfrei, ab 57600 Baud und höher wieder Fehler.
      Habe dann das Quarz getauscht und einen 17,7456MHz eingesetzt. Dann ließ sich die Baudrate fehlerfrei auf 57600 steigern. Bei Baud=115200 sind die Fehler noch da, aber lange nicht so massiv wie am Anfang.

      Ich habe das mal geloggt, damit man das sehen kann.

      Die Dateinamen geben die eingestellte Baudrate und die Quarzfrequenz an.

      @Admins
      Die einzelnen Dateien (Textdateien mit Endung log) konnte ich nicht hochladen. Fehlermeldung: "Die Datei hat eine ungültiges Dateiendung."
      Als Zip gings.
      Files
      • Bascomforum.zip

        (1.03 kB, downloaded 4 times, last: )
      Kaum macht man es richtig - und schon geht's!