抵抗器とネオンの物語

指のDC回路の計算、または3値論理用のDACを読みましょう



しかし、最初に、ネオン、どのようなロシア人が彼らを嫌っていますか?



それで、私は再び三元鉄片と一緒にいますが、この記事ではそれらが背景として機能し、今日は抵抗器に関する記事があります。 IN-12またはIN-15タイプのガス放電ランプを挿入できるショールをいくつか密封しましたが、時計を作りたくありませんでした:)







最も単純なショールには、シリンダーを照らすための一対のsk6812 LED、1ダースのトランジスター、およびそれらを制御する595eシフトレジスターが搭載されています。 これはスカーフのように見えるもので、単一のランプを持ちますが、同時に、希望の数のランプを実現するために長いソーセージに組み立てることができます。







ikaktysに助けてくれてありがとう! ネオンの女性と同時に、私が以前ブレッドボードに集めて詳細に説明していた三元カウンター分けて封印しました。







私の3進カウンターは、3つの電圧レベル(-5、0、5ボルト)で表される平衡型3進数システムを使用していることを思い出させてください。 そのステータスは、2色のLEDで表示されます。赤は負の値、消灯はゼロ、緑は正の値です。



これで十分だと思われますが、なぜネオンがあるのでしょうか? 三人の道が私を導く場所に非常に心から興味を持っている私の友人は、色盲であることが判明しました! そのため、2色LEDの代替案を考えなければなりませんでした。 そして、ここにはネオンボックスがあり、小数表示が便利です。



ヘビとハリネズミを渡る、または友達をバイナリーとターナリーのロジックにする方法



私が知っているように、脳のアルドゥイノを持っているので、私はアルドゥイノの助けを借りてネオンを制御します。 つまり、ネオンドライバーはバイナリロジックで動作し、3進数に基づいて情報を出力する必要があります。 私は解読者を作るのが面倒で、すでにアルデュインを持っているので、三元信号を使用して単純にアルデュインをADCに追加することにしました。幸いなことに、十分な空きレッグがあります。 次に、現在の行がADC領域の3分の1であるかを確認します。これにより、Arduins内の3進値が得られます。



運が悪いのは1つだけです。Arduinaは、グラウンドと5ボルトの間のアナログ信号を測定したいと考えており、3進信号はマイナス5〜5の広がりを持っています。 ちなみに、他のエリアでは-5〜5 Vのarduino電圧を測定する必要がある場合があります。 たとえば、最近、DCモーターの巻線の電流強度を測定する必要があり、ホールセンサーから-5〜5の信号が正確に得られました。



つまり、電圧レベルを2回スケーリングし、正の領域にシフトする必要があります。 これを行う最も簡単な方法は、次の抵抗分割器のように各3進ラインに掛けることです。







3進信号はVin(-5〜+5 V)に入り、Arduinoの電力はVref(5 V)になり、Arduino ADCによってVoutがオンになります。 これにより、VoutがADC作業領域(0〜5 V)に収まるように、必要な抵抗値を選択する方法が問題になります。



おそらく頭の中でほとんどこれを行う方法を知っている人がいるでしょうが、私は彼らに属していません、そして私は学校レベルでのみ物理学を知っています。 私の最も奥深い知識は、コンセントをなめる必要がないということです。 しかし、私はウィキペディアを読んで、オームの法則、キルヒホッフの法則、および線形方程式を解く能力を身につけています。



開始するには、次のようにタスクを設定しましょう:抵抗R1、R2、R3、および電圧VrefとVinを知り、各抵抗を流れる電流を見つけ、同時に出力電圧Voutを見つけます。



各抵抗器を流れる電流の方向(矢印で表示)を任意に選択しましょう。 方向の選択を「間違えた」場合、単純に現在の強さは負になります。



次に、回路ノードのキルヒホッフの法則 (図の太字の黒い点で示されているもの)を書き留めます。結果の電流の合計は、入力電流の合計、つまりI1 + I3 = I2に等しくなります。



次に、閉ループの2番目のキルヒホッフ規則は、抵抗器の両端の電圧の合計がループの合計EMFに等しいことを示しています。



2つの回路を選択できます。1つは共通電圧Vrefで、もう1つは電圧Vinです。 3つの方程式をすべて記述します。







同じシステムをマトリックス形式で書き直しますが、私は手で解決するのが面倒なので、シンボリック計算用のソフトウェアでは、マトリックスの方が明らかに便利です。







そして、システムの3x3マトリックスを反転することにより、目的の電流I1、I2、I3を見つけることができます。







次に、出力電圧Voutは、見つかったばかりのI2から見つけることができます。







これは問題ありませんが、一般に、既知の抵抗とVinでVoutを見つけるのではなく、Vinの範囲を知って、VoutがゼロとVrefの間に収まるように抵抗を選択します。



式のVrefの代わりにアルデュインの5ボルトの電源に置き換えてみましょう。100kOhmの任意の抵抗R1を選択します(分圧器があるので、抵抗の1つを選択できます)。 次に、2つの式を記述します。Vin= -5 Vの場合、Voutはゼロに等しく、Vin = 5 Vの場合、Voutは、たとえば4.9 Vに等しくなります。つまり、次の式のシステムが得られました。







一般に、多項式が得られ、手で数えることができますが、なぜですか? 私は賢明に数えます、 ここで以下のコードを実行できます:



var("R1,R2,R3,Vin,Vout,Vref") A=matrix([[1,-1,1],[R1,R2,0],[0,R2,R3]]) b=matrix([[0],[Vin],[Vref]]) I=(A.inverse()*b).simplify_full() I2=I[1][0] eq1=(4.9==(I2*R2).substitute(Vin= 5,Vref=5,R1=10^5)) eq2=(0 ==(I2*R2).substitute(Vin=-5,Vref=5,R1=10^5)) solve([eq1,eq2],R2,R3)
      
      





solveコマンドの出力は次のとおりです。



[[R2 == 0, R3 == 0], [R2 == 2450000, R3 == 100000]]







抵抗器の定格は厳密に正の値でなければならないため、明らかに不可能な答えは捨てます。 合計で、ソルバーは、R1 = R3 = 100 kOhm、R2 = 2.45メガオームを選択すると、Vref = 5 Vで給電されると、入力電圧範囲Vin = [-5 V、+ 5 V]がVoutノードに表示されることを示します。範囲[0 V、4.9 V]。 やった!



気配りのある読者への質問:出力範囲を0-5 Vではなく0-4.9 Vに選択したのはなぜですか?



私が使用しているコードは次のとおりです。



非表示のテキスト
 #define F_CPU 16000000L #include <avr/io.h> #include <avr/interrupt.h> #include <util/atomic.h> #include <util/delay.h> #include <stdlib.h> #include <stdio.h> #include <avr/pgmspace.h> // PSTR /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define INPUT2(port,pin) DDR ## port &= ~_BV(pin) #define OUTPUT2(port,pin) DDR ## port |= _BV(pin) #define CLEAR2(port,pin) PORT ## port &= ~_BV(pin) #define SET2(port,pin) PORT ## port |= _BV(pin) #define READ2(port,pin) ((PIN ## port & _BV(pin))?1:0) #define INPUT(x) INPUT2(x) #define OUTPUT(x) OUTPUT2(x) #define CLEAR(x) CLEAR2(x) #define SET(x) SET2(x) #define READ(x) READ2(x) #define WRITE(x,b) ((b)?(SET2(x)):(CLEAR2(x))) #define SK6812_DATA_PIN B,0 #define SHIFT_595_DATA_PIN B,1 #define SHIFT_595_CLOCK_PIN B,2 #define SHIFT_595_LATCH_PIN B,3 // IN12b: 0 1 2 3 4 5 6 7 8 9 . // IN15a: μ n % П k M m + - P nc uint16_t nixie_pins[] = {(1<<8), (1<<11), (1<<9), (1<<3), (1<<4), (1<<5), (1<<0), (1<<7), (1<<2), (1<<6), (1<<10)}; void push_nixie_symbol(uint8_t i) { uint16_t data = nixie_pins[i]; for (int8_t j=15; j>=0; j--) { CLEAR(SHIFT_595_CLOCK_PIN); _delay_us(10); if ((data>>j)&1) { SET(SHIFT_595_DATA_PIN); } else { CLEAR(SHIFT_595_DATA_PIN); } _delay_us(10); SET(SHIFT_595_CLOCK_PIN); _delay_us(10); } } void clock_nixie_latch() { SET(SHIFT_595_LATCH_PIN); _delay_us(10); CLEAR(SHIFT_595_LATCH_PIN); _delay_us(10); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void adc_init() { ADMUX = (1<<REFS0); // AREF = AVcc ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); // ADC Enable and prescaler of 128 } uint16_t adc_read(uint8_t ch) { ch &= 7; // prevent ch being >7 ADMUX = (ADMUX & 0xF8) | ch; // clear 3 lower bits before ORing ADCSRA |= (1<<ADSC); // start single convertion while (ADCSRA & (1<<ADSC)); // wait for the conversion to complete return ADC; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void uart_write(char x) { while ((UCSR0A & (1<<UDRE0))==0); // wait for empty receive buffer UDR0 = x; // send } uint8_t uart_char_is_waiting() { // returns 1 if a character is waiting, 0 otherwise return (UCSR0A & (1<<RXC0)); } char uart_read() { while (!uart_char_is_waiting()); char x = UDR0; return x; } int uart_putchar(char c, FILE *stream __attribute__((unused))) { uart_write(c); return 0; } int uart_getchar(FILE *stream __attribute__((unused))) { return uart_read(); } void uart_init() { UBRR0H = 0; // For divisors see table 19-12 in the atmega328p datasheet. UBRR0L = 16; // U2X0, 16 -> 115.2k baud @ 16MHz. UCSR0A = 1<<U2X0; // U2X0, 207 -> 9600 baud @ 16Mhz. UCSR0B = 1<<TXEN0; // Enable the transmitter. Reciever is disabled. UCSR0C = (1<<UDORD0) | (1<<UCPHA0); fdevopen(&uart_putchar, &uart_getchar); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define ASM_STRIP_PIN2(port,pin) "I" (_SFR_IO_ADDR(PORT ## port)), "I" (pin) #define ASM_STRIP_PIN(x) ASM_STRIP_PIN2(x) void __attribute__((noinline)) led_strip_write(uint8_t *colors, uint16_t count) { cli(); while (count--) { asm volatile( "ld __tmp_reg__, %a0+\n" "rcall led_strip_send_byte%=\n" "ld __tmp_reg__, %a0+\n" "rcall led_strip_send_byte%=\n" "ld __tmp_reg__, %a0+\n" "rcall led_strip_send_byte%=\n" "rjmp led_strip_asm_end%=\n" "led_strip_send_byte%=:\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "rcall led_strip_send_bit%=\n" "ret\n" "led_strip_send_bit%=:\n" "sbi %2, %3\n" "rol __tmp_reg__\n" "nop\n" "nop\n" "brcs .+2\n" "cbi %2, %3\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "brcc .+2\n" "cbi %2, %3\n" "ret\n" "led_strip_asm_end%=: " : "=b" (colors) : "0" (colors), ASM_STRIP_PIN(SK6812_DATA_PIN) ); } sei(); _delay_us(80); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define LED_COUNT 8 uint8_t red[] = {0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0}; uint8_t green[] = {128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0,128,0,0}; uint8_t gray[] = {128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128}; int main(void) { OUTPUT(SHIFT_595_DATA_PIN); OUTPUT(SHIFT_595_CLOCK_PIN); OUTPUT(SHIFT_595_LATCH_PIN); OUTPUT(SK6812_DATA_PIN); CLEAR(SHIFT_595_DATA_PIN); CLEAR(SHIFT_595_CLOCK_PIN); CLEAR(SHIFT_595_LATCH_PIN); CLEAR(SK6812_DATA_PIN); adc_init(); uart_init(); FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); stdin = stdout = &uart_stream; while(1) { uint16_t v0 = adc_read(0); uint16_t v1 = adc_read(1); uint16_t v2 = adc_read(2); int8_t t0 = v0<341 ? -1 : (v0>682 ? 1 : 0); int8_t t1 = v1<341 ? -1 : (v1>682 ? 1 : 0); int8_t t2 = v2<341 ? -1 : (v2>682 ? 1 : 0); int8_t value = t0+t1*3+t2*9; uint8_t ns0 = abs(value)%10; uint8_t ns1 = abs(value)/10; if (!ns1) ns1 = 10; uint8_t ns2 = value>0?7:(value<0?8:10); if (value>0) { led_strip_write(green, LED_COUNT); } else if (value<0) { led_strip_write(red, LED_COUNT); } else { led_strip_write(gray, LED_COUNT); } push_nixie_symbol(ns0); push_nixie_symbol(ns1); push_nixie_symbol(ns2); clock_nixie_latch(); fprintf_P(&uart_stream, PSTR("%d,%d,%d,%d, %d %d %d\r\n"), adc_read(0), adc_read(1), adc_read(2), value, ns2, ns1, ns0); _delay_ms(100); } return 0; }
      
      





そして、ランプの現在の値を10進数で表示した、私の3進カウンターの動作のビデオがあります。ADCアルデュインに巻かれた3つの同じ除数がはっきりと見えます。





ディスプレイの二重性にあまり戸惑うことはありません。ネオンは必須の要素ではなく、メイン出力は2色のLEDによって提供されるため、これは私の3進計算機の動作を損ないません。



タスクを複雑にし、DACに移動します



デジタルからアナログへのバイナリ変換



まず、バイナリDACのR-2R抵抗マトリックスを思い出してみましょう;これは次のようになります。







理論は、R4 = R5 = R、およびR1 = R2 = R3 = R6 = 2Rを選択すると、Voutノードの入力V1、V2、V3に2進数の3ビットを適用すると、デジタルに対応するアナログレベルが得られることを示しています入り口。



百科事典を読むのは良いことですが、これらのRと2Rの値はどのようにして得られたのでしょうか? 自分で見つけましょう。 したがって、DACトポロジは、以前と同様に、各抵抗を流れる電流の方向を任意に選択して提供されます。



計算方法は前の例とまったく同じです:最初に、与えられた抵抗値で電流強度を計算し、次にVoutを入力V1、V2、V3に接続するいくつかの方程式を書きます。これにより、目的の値が得られます。



したがって、3つのノードと3つの回路があり、その結果、6つの方程式があります。







マトリックス形式で書き直します。







そして、現在の強さは6x6マトリックスを回すことで見つけることができます







Voutは、3つの抵抗の電圧降下の合計として取得できます。







わかりやすくするために、R1、R2、R3、R4、R5、R6およびV1、V2、V3の関数としてVoutがどのように見えるかを示します。







かなり不快な表現ですよね? まあ、神は彼を祝福します、私たちは手で数えません。 したがって、与えられた抵抗定格に対して、DACには7つの非ゼロの入力電圧の組み合わせがあります。 Voutの7つの異なる値に対応する必要があります。 これにより、7つの方程式が得られ、どれが解けば、必要な抵抗値が得られます。



前と同様に、賢明な数え方をします。 ブラウザで実行できるコードは次のとおりです



 var("R1,R2,R3,R4,R5,R6,V1,V2,V3") A=matrix([[0,0,1,-1,0,0],[0,1,0,1,-1,0],[1,0,0,0,1,-1],[0,0,R3,R4,R5,R6],[0,R2,0,0,R5,R6],[R1,0,0,0,0,R6]]) b=matrix([[0],[0],[0],[V3],[V2],[V1]]) I=(A.inverse()*b).simplify_full() Vo=(I[5][0]*R6+I[4][0]*R5+I[3][0]*R4).simplify_full() eq7=(7/8==Vo.substitute(V1=1,V2=1,V3=1)) eq6=(6/8==Vo.substitute(V1=0,V2=1,V3=1)) eq5=(5/8==Vo.substitute(V1=1,V2=0,V3=1)) eq4=(4/8==Vo.substitute(V1=0,V2=0,V3=1)) eq3=(3/8==Vo.substitute(V1=1,V2=1,V3=0)) eq2=(2/8==Vo.substitute(V1=0,V2=1,V3=0)) eq1=(1/8==Vo.substitute(V1=1,V2=0,V3=0)) solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7],R2,R3,R4,R5,R6)
      
      





これが解決コマンドの出力です(負とゼロの抵抗値を持つすべてのソリューションを手で捨てました)。



[R2 == r11, R3 == r12, R4 == -1/2*r11 + r12, R5 == -1/2*R1 + r11, R6 == R1]







つまり、抵抗R1、R2、R3(ほぼ)を任意に選択でき、DACは正しく動作します。 同じ金種の3つすべてを使用すると、よく知られているR-2R行列が得られます。



デジタルからアナログへの3値コード変換



そして今、最も興味深いのは、3成分平衡システム用のデジタル-アナログコンバーターの開発です。 私が知る限り、誰もまだこれをやったことがありません、愚か者はほとんどいません:)



一般に、抵抗マトリックスはバイナリコードに最適ですが、3進信号を送信するとどうなりますか? V1 = V2 = V3 = -1の場合、マトリックスの出力は約-1になり、V1 = V2 = V3 = 0の場合、出力はゼロになり、V1 = V2 = V3 = 1の場合、出力は約1になります。最初の近似では、マトリックスは必要に応じて機能します。 うまく動作するように抵抗器を取り付けましょう。



マトリックストポロジは同じままで、Voutの式は変わりません。金種を検索するために方程式系を調整するだけです。 以前に7つの方程式があった場合、13になります。試してみましょう。



 var("R,R1,R2,R3,R4,R5,R6,V1,V2,V3") A=matrix([[0,0,1,-1,0,0],[0,1,0,1,-1,0],[1,0,0,0,1,-1],[0,0,R3,R4,R5,R6],[0,R2,0,0,R5,R6],[R1,0,0,0,0,R6]]) b=matrix([[0],[0],[0],[V3],[V2],[V1]]) I=(A.inverse()*b).simplify_full() Vo=(I[5][0]*R6+I[4][0]*R5+I[3][0]*R4).simplify_full() Vo=Vo.substitute(R1==R,R2==R,R3==R) eq13=(26/27==Vo.substitute(V1= 1,V2= 1,V3= 1)) eq12=(24/27==Vo.substitute(V1= 0,V2= 1,V3= 1)) eq11=(22/27==Vo.substitute(V1=-1,V2= 1,V3= 1)) eq10=(20/27==Vo.substitute(V1= 1,V2= 0,V3= 1)) eq09=(18/27==Vo.substitute(V1= 0,V2= 0,V3= 1)) eq08=(16/27==Vo.substitute(V1=-1,V2= 0,V3= 1)) eq07=(14/27==Vo.substitute(V1= 1,V2=-1,V3= 1)) eq06=(12/27==Vo.substitute(V1= 0,V2=-1,V3= 1)) eq05=(10/27==Vo.substitute(V1=-1,V2=-1,V3= 1)) eq04=( 8/27==Vo.substitute(V1= 1,V2= 1,V3= 0)) eq03=( 6/27==Vo.substitute(V1= 0,V2= 1,V3= 0)) eq02=( 4/27==Vo.substitute(V1=-1,V2= 1,V3= 0)) eq01=( 2/27==Vo.substitute(V1= 1,V2= 0,V3= 0)) sln=solve([eq01,eq02,eq03,eq04,eq05,eq06,eq07,eq08,eq09,eq10,eq11,eq12,eq13],R4,R5,R6) show(sln)
      
      





いつものように、このコードはブラウザ実行できます



さて、聞いて、システムに解決策があります。R1= R2 = R3 = R、R4 = R5 = 4 / 3R、R6 = 2Rとすると、実際の3値DACが得られます。



理論による理論ですが、実際にテストしてみましょう



DACの動作を確認するために、少し前に説明したのと同じ3進カウンターを使用します。 カウンターは三角のこぎりでクロックされます。回路はこちらから入手できます。 そして、3値DACの3つの入力でカウンターの3つのカテゴリを開始します。 テスト回路は次のようになります。







タイミングトライアングルの上にあるブレッドボード上で、レジスタマトリックスの下にあるブレッドボード上のカウンター。 このマトリックスには3つの抵抗があり、1 kOhm、1.33 kOhm、2 kOhmを選択しました。 3桁のカウンターは-13から+13までカウントし、出力では(約)-5 Vから(約)+5 Vのラダーが表示されると予想しています。オシロスコープを突く:







それぞれの触覚の三角形が、私たちにとってはしごの次のステップを生み出すことがはっきりとわかります。 うまくいく!



ボーナス:数学ソフトウェアをどれだけ信頼できますか?



今日、私たちはあらゆる種類のワニのソフトウェアを良いと考えました。 一般的に、私たちが計算したものをどれだけ信頼できますか? 私は多くのことを考えています、私はほとんどすべての数学的なパッケージにバグを見つけました。 たとえば、ここでは、セージについて話しているので、2年半前にバグレポートを送信したときに撮ったスクリーンショットです。







関数fの数値積分またはそのシンボリック計算は誰ですか? 彼らは別のサインです! ヤードは2017年の終わりになりました。 現在の状態を確認できます。 バージョンが変更され、エラーは引き続き発生しています。 したがって、数学的ソフトウェアを使用することはもちろん可能ですが、紙上で式を導出した後と同じ方法で結果を確認する必要があります



All Articles