Sie sind hier

SMD-Bauteil gelötet - und dann?

Fehlermeldung

Deprecated function: Array and string offset access syntax with curly braces is deprecated in include_once() (line 20 of /mnt/web216/e3/48/5667948/htdocs/includes/file.phar.inc).

Die Lötung - entweder von Hand oder mit dem Lötofen - mag ja auf den ersten Blick funktioniert haben, aber wie kann man prüfen, ob es nicht doch irgendwo verdeckte und versteckte Kurzschlüsse oder Unterbrechungen gibt oder ob das Bauteil überhaup 'heil' geblieben ist?

Zumindest am Beispiel des Micro-Controllers Atmel ATxmega32A4U im Gehäuse TQFP44 kann ich zeigen, wie man vorgehen könnte, um zu ermitteln, ob es sich überhaupt lohnt, nach der Lötung im Ofen (oder auch von Hand) die Platine fertig zu bestücken. Dabei wird vorausgesetzt, daß der größere - und vielleicht auch teurere - Teil der Platine konventionell zu bestücken ist.

Um überhaupt einen Test durchführen zu können, muß der Controller so weit in Betrieb genommen werden, daß er geflasht werden kann (bei mir über den PDI-Anschluß) und daß seine Stromversorgung vorhanden ist. Diese Teile müssen komplett vorhanden sein. Idealerweise sind die Ports noch nirgends angeschlossen (hängt vom Einzelfall ab, ob das immer so geht). Insbesondere sollten an den I/O-Ports noch keine Kondensatoren angeschlossen sein (bei den ADC-Eingängen oder dem Referenzspannungseingang kann schon mal ein Kondensator mit 100 nF eingeplant sein).

Der einfachste Test ist natürlich zunächst die Messung der Stronaufnahme bei korrekt anliegender Betriebsspannung und bei noch nicht programmiertem Controller. In meinem Fall hatte ich 2 mA gemessen, was nicht völlig unplausibel war. Jedenfalls lag schon mal kein Kurzschluß auf der Versorgungsspannung vor.

Der nächste Schritt besteht darin, den Controller über den PDI-Eingang mit dem Programmiergerät zu verbinden. Aus den AVR-Studio heraus kann man jetzt einerseits die Betriebsspannung messen, die am Controller anliegt und - viel wichtiger - z.B: die Seriennummer auslesen. Die Nummer an sich ist egal. Aber - zum auslesen werden einerseits die PDI-Leitungen benutzt und andererseits muß der Controller 'arbeiten'. Ist er etwa beim löten 'verglüht', dann wird bei der Seriennummer nichts brauchbares mehr herauskommen, während die Betriebsspannung durchaus unauffällig sein kann. Das auslesen der Seriennummer überprüft also die Kommunikation mit dem Controller. Man kann sich jetzt ein wenig bei den Fuses umsehen; aber verändern braucht man erst einmal nichts.

Ist bis jetzt noch nichts auffälliges passiert, was der Nutzung des Controller im Wege stehen könnte, kann man weiter machen und ein kleines Testprogramm in den Controller übertragen. Das Coding für meinen Fall ist hier aufgeführt:

  1. /*
  2.  * Created: 02.09.2013 21:47:32
  3.  * Author: DL8NCI
  4.  * Copyrigth by DL8NCI
  5.  *
  6.  * Überprüfung (fast) aller GPIO-Pins auf Kurzschlüsse zu anderen GPIO-Pins.
  7.  * Ausgenommen PA0, weil dort schon der 100 nF Kondensator gegen Masse geschaltet war.
  8.  * Ausgenommen ebenfalls alle VCC und GND-Pins sowie Reset und PDI.
  9.  * Standard-Takt ist 2 MHz, ohne Fuses einstellen zu müssen.
  10.  *
  11.  */
  12.  
  13. #ifndef F_CPU
  14. // wird für <util/delay.h> benötigt
  15. #define F_CPU 2000000UL
  16. #endif
  17.  
  18. #include <avr/io.h>
  19. #include <util/delay.h>
  20.  
  21. // ein paar Makros
  22. #define DIAG_A(x) { PORTA.DIR = x; PORTA.OUT = x; _delay_ms(1); if ((PORTA.IN!=x) || ((PORTB.IN & 0x0f)!=0x00) || (PORTC.IN!=0x00) || (PORTD.IN!=0x00) || ((PORTE.IN &0x0f)!=0x00) || ((PORTR.IN &0x03)!=0x00)) _delay_ms(10); PORTA.OUT = 0; PORTA.DIR = 0; }
  23. #define DIAG_B(x) { PORTB.DIR = x; PORTB.OUT = x; _delay_ms(1); if ((PORTA.IN!=0x00) || ((PORTB.IN & 0x0f)!=x) || (PORTC.IN!=0x00) || (PORTD.IN!=0x00) || ((PORTE.IN &0x0f)!=0x00) || ((PORTR.IN &0x03)!=0x00)) _delay_ms(10); PORTB.OUT = 0; PORTB.DIR = 0; }
  24. #define DIAG_C(x) { PORTC.DIR = x; PORTC.OUT = x; _delay_ms(1); if ((PORTA.IN!=0x00) || ((PORTB.IN & 0x0f)!=0x00) || (PORTC.IN!=x) || (PORTD.IN!=0x00) || ((PORTE.IN &0x0f)!=0x00) || ((PORTR.IN &0x03)!=0x00)) _delay_ms(10); PORTC.OUT = 0; PORTC.DIR = 0; }
  25. #define DIAG_D(x) { PORTD.DIR = x; PORTD.OUT = x; _delay_ms(1); if ((PORTA.IN!=0x00) || ((PORTB.IN & 0x0f)!=0x00) || (PORTC.IN!=0x00) || (PORTD.IN!=x) || ((PORTE.IN &0x0f)!=0x00) || ((PORTR.IN &0x03)!=0x00)) _delay_ms(10); PORTD.OUT = 0; PORTD.DIR = 0; }
  26. #define DIAG_E(x) { PORTE.DIR = x; PORTE.OUT = x; _delay_ms(1); if ((PORTA.IN!=0x00) || ((PORTB.IN & 0x0f)!=0x00) || (PORTC.IN!=0x00) || (PORTD.IN!=0x00) || ((PORTE.IN &0x0f)!=x) || ((PORTR.IN &0x03)!=0x00)) _delay_ms(10); PORTE.OUT = 0; PORTE.DIR = 0; }
  27. #define DIAG_R(x) { PORTR.DIR = x; PORTR.OUT = x; _delay_ms(1); if ((PORTA.IN!=0x00) || ((PORTB.IN & 0x0f)!=0x00) || (PORTC.IN!=0x00) || (PORTD.IN!=0x00) || ((PORTE.IN &0x0f)!=0x00) || ((PORTR.IN &0x03)!=x )) _delay_ms(10); PORTR.OUT = 0; PORTR.DIR = 0; }
  28.  
  29. int main(void) {
  30.  
  31. // alle Ports auf Eingang setzten und mit einem Pull-Down-Widerstand versehen
  32.  
  33. PORTA.DIR = 0x00; // alle Bits
  34. PORTA.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  35. PORTA.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  36. PORTA.PIN2CTRL = PORT_OPC_PULLDOWN_gc;
  37. PORTA.PIN3CTRL = PORT_OPC_PULLDOWN_gc;
  38. PORTA.PIN4CTRL = PORT_OPC_PULLDOWN_gc;
  39. PORTA.PIN5CTRL = PORT_OPC_PULLDOWN_gc;
  40. PORTA.PIN6CTRL = PORT_OPC_PULLDOWN_gc;
  41. PORTA.PIN7CTRL = PORT_OPC_PULLDOWN_gc;
  42.  
  43. PORTB.DIR = 0x00; // Bit 0-3
  44. PORTB.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  45. PORTB.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  46. PORTB.PIN2CTRL = PORT_OPC_PULLDOWN_gc;
  47. PORTB.PIN3CTRL = PORT_OPC_PULLDOWN_gc;
  48.  
  49. PORTC.DIR = 0x00; // alle Bits
  50. PORTC.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  51. PORTC.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  52. PORTC.PIN2CTRL = PORT_OPC_PULLDOWN_gc;
  53. PORTC.PIN3CTRL = PORT_OPC_PULLDOWN_gc;
  54. PORTC.PIN4CTRL = PORT_OPC_PULLDOWN_gc;
  55. PORTC.PIN5CTRL = PORT_OPC_PULLDOWN_gc;
  56. PORTC.PIN6CTRL = PORT_OPC_PULLDOWN_gc;
  57. PORTC.PIN7CTRL = PORT_OPC_PULLDOWN_gc;
  58.  
  59. PORTD.DIR = 0x00; // alle Bits
  60. PORTD.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  61. PORTD.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  62. PORTD.PIN2CTRL = PORT_OPC_PULLDOWN_gc;
  63. PORTD.PIN3CTRL = PORT_OPC_PULLDOWN_gc;
  64. PORTD.PIN4CTRL = PORT_OPC_PULLDOWN_gc;
  65. PORTD.PIN5CTRL = PORT_OPC_PULLDOWN_gc;
  66. PORTD.PIN6CTRL = PORT_OPC_PULLDOWN_gc;
  67. PORTD.PIN7CTRL = PORT_OPC_PULLDOWN_gc;
  68.  
  69. PORTE.DIR = 0x00; // Bit 0-3
  70. PORTE.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  71. PORTE.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  72. PORTE.PIN2CTRL = PORT_OPC_PULLDOWN_gc;
  73. PORTE.PIN3CTRL = PORT_OPC_PULLDOWN_gc;
  74.  
  75. PORTR.DIR = 0x00; // Bits 0-1
  76. PORTR.PIN0CTRL = PORT_OPC_PULLDOWN_gc;
  77. PORTR.PIN1CTRL = PORT_OPC_PULLDOWN_gc;
  78.  
  79.  
  80. while(1) {
  81.  
  82. // DIAG_A(0x01); wg. C gegen Masse nicht testbar
  83. DIAG_A(0x02);
  84. DIAG_A(0x04);
  85. DIAG_A(0x08);
  86. DIAG_A(0x10);
  87. DIAG_A(0x20);
  88. DIAG_A(0x40);
  89. DIAG_A(0x80);
  90.  
  91. DIAG_B(0x01);
  92. DIAG_B(0x02);
  93. DIAG_B(0x04);
  94. DIAG_B(0x08);
  95.  
  96. DIAG_C(0x01);
  97. DIAG_C(0x02);
  98. DIAG_C(0x04);
  99. DIAG_C(0x08);
  100. DIAG_C(0x10);
  101. DIAG_C(0x20);
  102. DIAG_C(0x40);
  103. DIAG_C(0x80);
  104.  
  105. DIAG_D(0x01);
  106. DIAG_D(0x02);
  107. DIAG_D(0x04);
  108. DIAG_D(0x08);
  109. DIAG_D(0x10);
  110. DIAG_D(0x20);
  111. DIAG_D(0x40);
  112. DIAG_D(0x80);
  113.  
  114. DIAG_E(0x01);
  115. DIAG_E(0x02);
  116. DIAG_E(0x04);
  117. DIAG_E(0x08);
  118.  
  119. DIAG_R(0x01);
  120. DIAG_R(0x02);
  121. }
  122. }

Am Anfang wird die Taktfrequenz gesetzt, die bei den ATxmegas standardmäßig 2 MHz aus dem internen Oszillator ist. Man braucht also für die Taktquelle keine Fuse zu setzen (man kann auch keine setzten!). Die beiden Includes werden einerseits benötigt, um überhaupt auf die Ports zugreifen zu können und um die Möglichkeit zu haben, auf einfache Weise kleine Verzögerungen zu bewirken.

Die nachfolgenden Makros haben im Grunde alle den selben Aufbau. Jedes Makro bedient einen der Ports (A, B, C, D, E und R) wobei aber nicht jeder Port mit 8 bits nutzbar ist. Port R beispielsweise bedient nur die beiden niedrigsten Bits. Grundidee für alle Makros ist folgende: Setzte alle Ports auf Eingang und ziehe ihn über einen Pull-Down-Widerstand zusätzlich richtung Masse. Setzte einen einzigen Port (ein Bit, einen Pin) auf Ausgang und setzte seinen Wert auf VCC bzw. logisch 1. Gibt es keine Kurzschlüsse, so muß jeder Eingang immer noch den Wert 0 beim lesen liefern - ausgenommen derjenige Pin, der gerade Ausgang ist. Der Testpuls auf dem 'aktiven' Pin hat eine Länge von 1 ms. Entsprechen die zurückgelesenen Signalpegel der Erwartung, dann wird der Testpuls abgeschaltet und es wird zum nächsten Pin fortgeschritten. Gibt es aber eine Unregelmäßigkeit, dann wird der Testpuls um 10 ms verlängert. Man kann also leicht mit einem Oszilloskop jetzt zu jedem geprüften Pin feststellen, ob er unauffällig ist oder ob es ein Problem gibt. Zur Kontrolle kann man auch einen Kurzschluß zum Nachbarpin provozieren - der Puls am Oszi wird dann etwa 11 ms lang sein, während er ohne Kurzschluß 1 ms Länge hat. Es ist zunächst nicht erkennbar, mit welchem Pin der Kurzschluß besteht. Aber der heißeste Kandidat ist einer der beiden Nachbarn, der ja seinerseits auch einen langen Impuls haben muß.

Die folgenden Bilder zeigen je ein Oszillogramm für den normalen Fall (1 ms Pulslänge) und eines für den Kurzschlußfall (11 ms Pulslänge)

Der Normalfall

Der Fehlerfall

Es ist also ein leichtes, durch Blick auf den Bildschirm des Oszilloskopes festzustellen, ob auf dem gerade untersuchten Pin ein Kurzschluß vorliegt, oder nicht.

Natürlich können nicht alle Konstellationen abgedeckt werden. Die Grenzen sind bei diesem kleinen Testprogramm überschritten, wenn Kurzschlüsse zur Masse oder zur Betriebsspannung zu suchen sind - was nicht heißt, daß man dafür keinen Algorithmus finden könnte. Ein Massekurzschluß wird sicher über kurz oder lang zur endgültigen Zerstörung des Ports (als Ausgang) führen. Analog natürlich auch ein Kurzschluß gegen die Betriebspannung, die den Port, als Ausgang konfiguriert, in die ewigen Jagdgründe befördert, wenn der Pegel 0 ist.

Trotz dieser kleinen Einschränkungen hat sich das Testprogramm bereits als nützlich erwiesen, da es zeigen konnte, daß es keine Auffälligkeiten nach der Lötung gab und daß es sich somit lohnt, die Platine weiter zu bestücken. Die einzige gefundene Auffälligkeit war eine Portleitung, die über einen 100 nF Kondensator gegen Masse geschaltet war. Dieses Port-Bit wurde dann aus der Prüfung herausgenommen. Daher die Bemerkung weiter oben im Text über das fortlassen aller Kondensatoren an den Port-Pins.

Deutsch