Než začnete číst, rád bych předeslal, že článek není návodem ke stavbě plnohodnotného měřáku voltampérových charakteristik. Ten by si jistě zasloužil kvalitnější zpracování než to co uvidíte zde. Jde jen o takový malý pokus zjistit co všechno se dá s analogovou výbavou moderních AVR poskládat. A mám za to že právě takový měřič je vhodná demonstrace. V tomto tutoriálu namixujeme AD a DA převodník spolu s operačním zesilovačem a několika diskrétními součástkami do koktejlu, který zvládne docela obstojně proměřit volt-ampérovou charakteristiku různých polovodičových prvků. Poslouží například ke zjišťování prahových napětí LED diod nebo ke stanovení dynamického odporu nízkonapěťových zenerek a podobně.
Jádrem zařízení je zdroj proudu řízený napětím z DA převodníku. Napětí z DA převodníku se přivádí na neinvertující vstup operačního zesilovače. Stejné napětí se operační zesilovač snaží udržovat na snímacím odporu (RSNS) otevíráním a zavíráním tranzistoru Q1. Řízením napětí na RSNS dochází také k řízení proudu skrze něj (Ohmův zákon) a veškerý proud tekoucí RSNS teče i testovaným zařízením (DUT - Device Under Test). Nastavený proud odpovídá Udac/RSNS. Protože má celá aplikace běžet z 5V napájení, je žádoucí aby úbytek napětí na RSNS byl co nejmenší a aby zbýval co největší napěťový prostor pro testovanou součástku. Vyloženě se tedy nabízí využít DA převodník s 0.55V referencí. Zvolíte-li snímací rezistor RSNS=47R bude maximální nastavitelný proud roven 0.55/47 = 11.8mA. Krok zdroje proudu bude v takovém případě přibližně 46uA (11.8/256). Rozsah můžete snadno zvětšit nebo zmenšit jinou volbou snímacího odporu. Při změně ale musíte dávat pozor na výkonové zatížení tranzistoru Q1. Protože chceme 5V rozsah využít co nejvíce, musíme při výběru operačního zesilovače sáhnout po nějaké (Input/Output) rail-to-rail variantě. Takových naštěstí existuje nespočet (většina určená pro provoz s 5V napájením). Já zvolil levný MCP6002.
Ke stanovení voltampérové charakteristiky potřebujeme měřit napětí na testovaném prvku a proud který skrze něj protéká. Měření proudu je v našem případě jednoduché, stačí měřit napětí na RSNS. O něm víme, že se pohybuje v pásmu 0 až 0.55V, takže k měření můžeme využít opět 0.55V referenci a udržet si tak maximální rozlišení. Topologie našeho obvodu neumožňuje přímo měřit napětí na DUT, musíme nejprve změřit napětí na kolektoru Q1 a od něj odečíst napětí na Rsns. Protože napětí na kolektoru může dosahovat až 5V, musíme pro měření volit větší referenci. Největší jakou máme k dispozici je 4.34V. Tento fakt nám trochu kazí měřicí rozsah a reálně tak můžeme měřit napětí na DUT někde k 3.8V (4.34V-0.55V). To pořád stačí na všechny typy běžných LED. Přirozeně je možné systém vylepšit tak aby měřicí rozsah pokrýval skoro celých 5V, ale jak už jsem napsal v úvodu - není cílem udělat dokonalé zařízení, ale ukázat že i s málem to stojí za to (jsme tu pro zábavu). V poslední řadě ještě doplním několik drobností. Pull-down rezistor R2 slouží k tomu aby výstupní napětí na PA6 drželo spolehlivě 0V i když je DAC vypnutý. Zdroj proudu je tak nastaven na "minimum" a nehrozí riziko, že by nedopatřením spálil nějaký testovaný prvek. R1 zase poněkud snižuje zisk dvojice OZ + Q1 aby nedocházelo k nežádoucím oscilacím. Q1 může být v podstatě libovolný PNP tranzistor, typ 2N3906 jsem zvolil jen proto že byl zrovna po ruce. Snímací rezistor je dobré si proměřit a do programu zapsat jeho přesný odpor. Zapojení si můžete prohlédnout na schematu.
Modelová aplikace bude pracovat velice jednoduše. Po stisku tlačítka na PB4, spustí proces měření. V něm postupně projde vše 256 hodnota DA převodníku a nastaví všechny možné proudy od 0 do 11.8mA. Pro každý z nich změří napětí i proud na DUT a výsledek pošle UARTem na terminál v PC. Formát dat bude CSV (Comma Separated Values), tedy čárkou oddělené hodnoty. Oddělení každého řádku bude kombinací znaků '\n'+'\r'. Terminálový program (Realterm, Putty nebo i Hyper terminál) může ukládat data rovnou do souboru a vy je pak můžete zpracovat klidně pomocí tabulkového procesoru nebo jiného programu. Po skončení procedury nastaví program výstup DAC na nulu aby testovanou součástku ani zdroj proudu zbytečně nezatěžoval. Pak opět počká na stisk a celou akci zopakuje kolikrát budete chtít.
Konfiguraci UARTu, DAC i ADC už znáte z předchozích tutoriálů, takže se jim nemusím věnovat a mohu okomentovat jen dvě klíčové funkce. Než se do toho pustím poznamenám že veškerá měření provádím s použitím "akumulace" 64x. Takže náš AD převodník je virtuálně 16bitový. Jak jsme viděli v tutoriálu ADC1 získáme tím reálně nějaké rozlišení navíc.
/* měření VACH polovodičových prvků pomocí DAC a ADC DAC out (PA6) zapojen do invertujícího vstupu OZ (MCP6002 R-R 5V OZ) a vybaven pull-down odporem 200k výstup OZ vede přes rezistor 330R na Bázi PNP tranzistoru (2N3906) Emitor PNP tranzistoru vede na 5V napájení Kolektor PNP vede na DUT (zkoumaný prvek) a zároveň na PA5 (AIN5) - měří se napětí na prvku druhá svorka DUT vede na PA7 (AIN7), na neinvertující vstup OZ a na snímací rezistor (Rsns) Snímací rezistor je zbývajícím koncem připojen na GND napětí na DUT je rovno Vain5-Vain7 proud skrze DUT je roven Vain7/Rsns Na PB4 je proti GND zapojeno tlačítko kterým se spouští měření (tlačítko na Xnano kitu) */ #define F_CPU 20000000UL #include <avr/io.h> #include <util/delay.h> #include <stdio.h> // kvůli printf_P() #include <avr/pgmspace.h> // kvůli makru PSTR #define RSNS 464UL // Ohm (46.4 Ohm měřených) - při změně odporu dosaďte v jednotkách 0.1 Ohm #define VREF055 550UL // mV (550mV měřených) #define VREF43 4350UL // mV (4350mV dopočítaných z poloviny rozsahu) // nastavený proud by měl zhruba odpovídat I = Vout/Rsns (Vout může být 0-0.55V, proud se tedy pohybuje mezi 0 až 11.7mA) void clock_20MHz(void); void init_dac(void); void init_adc(void); void init_usart(void); int usart_putchar(char var, FILE *stream); void set_current(uint8_t ival); void adc_get(uint16_t* voltage, uint16_t* current); void sweep(void); void measure_point(uint8_t ipoint); // kouzelná formule pro implementaci printf static FILE mystdout = FDEV_SETUP_STREAM(usart_putchar, NULL, _FDEV_SETUP_WRITE); int main(void){ PORTA.DIRCLR = PIN6_bm; // výstup DAC PORTA.PIN6CTRL |= PORT_ISC_INPUT_DISABLE_gc; // vypnout digitální buffer na výstupu DAC PORTB.OUTCLR = PIN4_bm; // tlačítko na Xnano kitu (proti GND) je vstup PORTB.PIN4CTRL |= PORT_PULLUPEN_bm; // pull up pro tlačítko stdout = &mystdout; // mapujeme printf na UART clock_20MHz(); // taktujeme na plný výkon init_usart(); // UART na PA1 (Tx) - skrze mEDBG jako USB->UART převodník (na Xnano kitu) init_dac(); // nastartovat DAC s 0.55V referencí a minimální hodnotou na výstupu init_adc(); // nastartovat ADC _delay_us(25); // počkat na stabilizaci referencí while (1){ while(PORTB.IN & PIN4_bm){} // čekej na stisknutí tlačítka sweep(); // proveď jednu sadu měření } } void measure_point(uint8_t ipoint){ uint16_t v,c,volt,current; DAC0.DATA = ipoint; // nastav novou hodnotu proudu do DUT _delay_us(10); // dej operáku trochu času proud stabilizovat (asi nebude potřeba, hodně čekáme v adc_get) adc_get(&v,&c); // změř napětí na kolektoru a na Rsns // trocha matematiky (odečtu napětí kolektoru od napětí Rsns a dostanu napětí na DUT) // data z ADC přebírám s oversamplingem 64 (tedy v rozsahu 0-64*1023) // napětí v jednotkách mV .. tady bych ocenil floaty :D volt = ((uint32_t)v*VREF43 - (uint32_t)c*VREF055 + (64UL*1023UL)/2UL)/(64UL*1023UL); // výpočet proudu v jednotkách 0.01mA - škaredá matematika se snad správným zaokrouhlováním current = ((uint64_t)c*VREF055*1000 + 32*1023ULL*RSNS)/(64*1023ULL*RSNS); printf_P(PSTR("%u.%03u,%u.%02u\r\n"),volt/1000,volt%1000,current/100,current%100); // pošleme výsledek na terminál } void sweep(void){ uint16_t i; // projdeme celý rozsah DAC (256 hodnot proudu do DUT) for(i=0;i<255;i++){ measure_point(i); } DAC0.DATA = 0; // po skončení snížíme výstupní signál (a proud DUT) na minimum // menší hodnotu lze dosáhnout vypnutím DAC (na PA6 je pulldown 220k) } void adc_get(uint16_t* voltage, uint16_t* current){ uint16_t tmp; VREF.CTRLA = VREF_ADC0REFSEL_0V55_gc; // zvolíme referenci 0.55V _delay_us(25); // počkáme na stabilizaci reference ADC0.MUXPOS = ADC_MUXPOS_AIN7_gc; // zvolíme vstup PA7 (napětí na snímacím odporu (Rsns) - tedy proud) ADC0.COMMAND = ADC_STCONV_bm; // spustit převod while(ADC0.COMMAND & ADC_STCONV_bm){} // počkat na dokončení převodu tmp = ADC0.RES; // vyčíst výsledek převodu *current = tmp; // vracíme výsledek i s akumulací (64x) ! VREF.CTRLA = VREF_ADC0REFSEL_4V34_gc; // přepneme na vyšší referenci - budeme měřit větší napětí _delay_us(25); // počkáme na stabilizaci reference ADC0.MUXPOS = ADC_MUXPOS_AIN5_gc; // Měříme napětí na kolektoru (napětí na DUT + na Rsns) ADC0.COMMAND = ADC_STCONV_bm; // spustit převod while(ADC0.COMMAND & ADC_STCONV_bm){} // počkat na dokončení převodu tmp = ADC0.RES; // vyčíst výsledek převodu *voltage = tmp; // vracíme výsledek i s akumulací (64x) ! } // odešle jeden znak (formát pro "printf" implementaci) int usart_putchar(char var, FILE *stream){ while(!(USART0.STATUS & USART_DREIF_bm)){} // počkat než bude místo v odesílacím bufferu USART0.TXDATAL = var; // naložit data k odeslání return 0 ; } void init_usart(void){ PORTA.OUTSET = PIN1_bm; // log.1 PA1 (Tx) - neutrální hodnota na Tx PORTA.DIRSET = PIN1_bm; // PA1 (Tx) - je výstup PORTMUX.CTRLB = PORTMUX_USART0_bm; // Remapujeme Tx na PA1 USART0.BAUD = 2083; // nastavit baudrate 38400 (64*20M/(16*38400)) USART0.CTRLB |= USART_TXEN_bm; // povolit vysílač (od teď přebere UART kontrolu nad PA1) } void init_adc(void){ VREF.CTRLA = VREF_ADC0REFSEL_4V34_gc; // vybrat referenci pro ADC (bude se měnit) ADC0.CTRLA = ADC_RESSEL_10BIT_gc; // zvolit rozlišení 10bit ADC0.CTRLB = ADC_SAMPNUM_ACC64_gc; // maximální akumulace výsledku (64x) // vzorkovací kondenzátor 5pF, prescaler 20M/32 = 625kHz, použít vnitřní referenci ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV32_gc | ADC_REFSEL_INTREF_gc; ADC0.MUXPOS = ADC_MUXPOS_AIN5_gc; // zvolit vstup pro ADC (bude se měnit) ADC0.SAMPCTRL = 3; // 2+3 = 5 period (8us) vzorkovací čas ADC0.CTRLA |= ADC_ENABLE_bm; // spustit ADC } void init_dac(void){ VREF.CTRLA = VREF_DAC0REFSEL_0V55_gc; // zapneme DAC 0.55V referenci VREF.CTRLB = VREF_DAC0REFEN_bm; // reference má běžet i při vypnutém DAC (není nutné) DAC0.CTRLA = DAC_ENABLE_bm | DAC_OUTEN_bm; // spustíme DAC a připojíme mu jeho výstup (PA6) DAC0.DATA = 0; // ze začátku nulové výstupní napětí (nulový proud do DUT) } void clock_20MHz(void){ // vybrat 20MHz RC oscilátor _PROTECTED_WRITE(CLKCTRL.MCLKCTRLA, CLKCTRL_CLKSEL_OSC20M_gc); // vypnout prescaler _PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0); }
Rovnou řeknu, že na první pohled zařízení funguje překvapivě dobře. Vcelku rychle jsem si připravil sadu dat pro různobarevné LED, klasickou křemíkovou diodu a pro zajímavost i TL431 a několik zenerek. Posledně zmíněným jsem vyměnil snímací odpor za 22R a zvedl tak proudový rozsah na 25mA. Nedivte se popiskům v angličtině. Chci návod hodit i na AVRfreaks a navíc Octave v němž jsem grafy zpracoval má s diakritikou trochu problémy. Věřím že jsou grafy tak jasné že si popisek ani nevšimnete, natož tak aby vám vadila angličtina.
Nejzajímavější je asi poslední zenerova dioda (3V3). Během měření její charakteristiky narostlo napětí na kolektoru nad 4.34V a dostalo se mimo rozsah našeho ADC. Ten s dalším růstem napětí pořád měřil hodnotu 4.34V a odečítal od ní rostoucí úbytek napětí na RSNS. Což delo k falešné informací, že napětí na diodě klesá. Aplikace může tento problém snadno detekovat. Stačí aby hlídala zda napětí z ADC došlo do "saturace" (1023 respektive 64x1023). Na fotce níže si můžete prohlédnout zapojení na kontaktním poli. Testovaná součástka (zenerka) je v levé části snímku.
Závěrem zmíním pár poznámek nebo námětů jak lze zapojení zdokonalit.
Home
| V1.00 6.3.2019 /
| By Michal Dudka (m.dudka@seznam.cz) /