Ingatan: Evaluasi Sirkuit PCB

Dahulu, sekitar beberapa tahun yang lalu, saya pernah mengevaluasi board PCB baru untuk dicek kualitas sinyalnya. Setelah itu melakukan rework atau perbaikan kualitas sinyal yang jelek. Caranya yaitu dengan menambahkan/mengganti komponen resistor atau capasitor pada jalur yang telah ada. Yang saya pahami saat itu adalah jika sinyal berosilasi, ya, tambahkan low pass filter komponen RC (resistor capasitor). Pengetahuan saya hanya sebatas low pass filter, dan itu pun hanya metode kira-kira saja, trial and error. Saya sempat menemui beberapa bentuk sinyal diantaranya sinyal overshoot, undershoot, step noise, sirip hiu, dan beberapa bentuk sinyal akibat crosstalk. Terlebih jika mengevaluasi board baru dengan kabel FFC yang panjang yang biasanya bermasalah dengan kualitas sinyal. Ada yang mudah untuk dicounter-measure, ada juga yang sulit sekali. Lalu, mucul banyak pertanyaan dalam benak saya, bagaimana cara meng-counter sinyal-sinyal tersebut? Apakah bisa dihitung? Apakah bisa disimulasikan? Karena jika semua bisa dikalkulasikan/diperkirakan dengan baik maka hal ini akan dengan cepat diselesaikan. Karena waktu itu sibuk dengan pekerjaan, jadi tidak sempat mencari tahu penyebabnya dan membuat simulasinya.
Hampir satu tahun berlalu setelah resign dari perusahaan tersebut, tanpa sengaja saya melihat salah satu video dari Robert Feranec tentang perancangan PCB, terlebih untuk high speed data, bahwa dalam high speed data, urusan impedance harus diperhatikan. Sepertinya pertanyaan-pertanyan saya dahulu mulai mendapat sedikit pencerahan. Sinyal yang saya evaluasi memang mempunyai nilai rise time sekitaran 50ns kalau tidak salah. Kesimpulan dari video itu, intinya impedance dari mulai signal driver sebagai pengirim sinyal, trace (jalur) PCB sebagai transmission line, dan load impedance di bagian penerima sinyal harus mempunyai impendance yang sama untuk mendapatkan transfer daya maksimum serta mengurangi reflection yang bisa mengakibatkan ringing (sinyal berosilasi).

Image link.

Sumber sinyal dan load bisa berupa IC. Sedangkan untuk transmision line-nya adalah jalur pada PCB, konektor, atau pun kabel. Saya jadi teringat dengan mata kuliah elektro magnetik waktu kuliah dulu. Sayang sekali saya kurang menyimak mata pelajaran tersebut padahal ternyata berguna dalam memahami design PCB. Hehe.
OK, lanjut. Jadi nilai impedance dari transmision line harus disesuaikan agar characteristic impedance Z0 nya mempunyai nilai yang sama dengan ZS dan ZL. Sebagai contoh, untuk koneksi USB biasanya mengharuskan impedance sebesar 90 Ohm. Untuk sinyal LVDS sekitar 100 Ohm. Sedangkan standard untuk nilai impedance adalah 50 Ohm. Tapi, saya tidak menemukan bagaimana menentukan impedance untuk sebuah pin IO dari microcontroller. Saya hanya menemukan jawaban di forum ini link , link dan link tapi informasinya kurang memuaskan.

Image link.

Berikut bentuk model elektronik dari sebuah trace pada PCB.

Image link.


Pada sebuah PCB, Z0 ini dipengaruhi oleh konstanta dielektrikdari bahan pembuat PCB tersebut, misal FR-4STD mempunyai konstata dielektrik 4.6.

Selanjutnya dipengaruhi oleh lebar jalur, ketebalan tembaga, dan jarak antara jalur dengan reference plane (ground plane) Link. Perhitungan dalam mendisain PCB bisa pakai aplikasi Saturn PCB Toolkit, mulai dari menghitung impedance, crosstalk, dll.

Selain itu, hasil dari pencarian saya di internet tentang control impedance PCB yaitu websitenya JLCPCB yang menyediakan calculator untuk menghitung lebar jalur yang direkomendasikan berdasarkan impedance yang diinginkan.

Setelah menonton beberaapa video dan membaca referensi, ternyata mendisain PCB yang baik itu sulit dan harus mendetail. Berikut beberapa hal penyebab penurunan kualitas sinyal:

  • Impedance mismatch
  • Frequency respon
  • Crosstalk
  • Noise
  • Jitter


OK, karena penasaran, saya coba mensimulasikan beberapa bentuk sinyal berdasarkan rasa penasaran saya waktu kerja dahulu. Saya sebenarnya ingin mencoba memakai software HyperLynx Signal Integrity. Akan tetapi, ternyata software tersebut berbayar. Maka dari itu, kali ini saya menggunakan software gratis bernama Qucs (Linux, Mac OS, dan Windows).
1.Kasus 1: Ringing
Kasus sinyal ringing pernah saya temui dan lumayan sering. Berdasarkan pengetahuan saya kala itu, saya menambahkan rangkaian RC sebagai low pass filter. Akan tetapi, setelah membaca beberapa atikel, saya mendapat pemahaman baru. Sinyal ringing sendiri bisa bersumber dari power supply, tetapi bisa juga akibat dari jalur/trace yang panjang. Jika kita sudah mempunyai jalur yang pendek dan masih terjadi ringing, maka bisa jadi disebabkan oleh parasitic inductance dan capacitance dari komponen. Jika mempunyai jalur yang panjang, ringing disebabkan reflection dari sinyal dikarenakan impedance mismatch.

Sekarang saya akan coba simulasikan pengaruh panjang trace terhadap ringing dengan patokan critical length. Jika panjang jalur sama atau melebihi critical length, maka harus dilakukan kontrol impedance pada transmission line tersebut yaitu harus match impedance. Critical length dirumuskan dengan L < 2 xRT, dimana L dalam inch dan RT (rise time) dalam ns.

Untuk meng-generate sinyal kotak yang smooth saya menggunakan bessel lowpass filter untuk memfilter sinyal kotak rise time 0ns dengan frekuensi cut-off 1GHz. Nilai rise timenya Tr = 0.35/f3dB dengan perhitungan bahwa nilai rise time dihitung pada sasat perpindahan sinyal kotak 10% menuju 90% dari voltage sinyal HIGH. Karena jika menggunakan nilai rise time dari Qucs, betuk sinyalnya kaku.

Model Ideal transmission line di Qucs mempunyai propagation velocity 3×108 m/s atau sekitar 11.8 inch/ns, yang merupakan kecepatan transmisi di udara. Sedangkan untuk sebuah PCB dengan bahan FR4 memiliki kecepatan sekitar 6 inch/ns. Maka kita akan masukan rumus velocity factor 6/11.8 = 0.51 ke perhitungan transmission line. Nilai R_driver dibuat 20 Ohm agar Zsource missmatch impedance dengan Z0 (characteristic impedance) transmission line. Untuk nilai variable di load, kurang lebih saya mengikuti nilai di dokumen berikut: Rload = 60M Ohm sedangkan untuk input capacitancenya saya buat kecil saja sehingga bisa diabaikan karena efek input capacitance ini akan mempengaruhi respon sinyal terhadap waktu.

Nilai rise time dari sinyalnya sekitar 0.35 ns. Maka, berdasarkan rumus di atas, max length nya adalah 2 x 0.35 = 0.7 inch = 17.78 mm. Saya coba dengan panjang 15 mm dan tidak terjadi ringing. Berarti pada jalur trace yang pendek matching impedance bisa diabaikan.

Kemudian saya ubah menjadi 17.78 mm dan mulai terjadi ringing. Berarti sesuai rumus L < 2 xRT, jika L panjang maka trace perlu matching impedance.

Semakin bertambah panjang transmission line, maka semakin besar amplitudo ringingnya. Saya coba dengan panjang 40 mm.

Lalu bagaimana jika jalur kita mengharuskan panjangnya melebihi panjang maksimum dari rumus di atas? Maka kita harus memperhatikan impedance dari transmission line agar match. Berikut sinyal ringing yang saya simulasikan dengan panjang trace 100 mm atau 10cm, serta pemasangan termination resistor sebagai solusinya di aplikasi Qucs agar matching impedance. Kali ini saya abaikan velocity factor dari FR-4, jadi tidak saya masukan ke rumus. Dan juga saya hanya mensimulasikan termination resistor series dan parallel saja.

Berikut adalah simulasi untuk melihat efek dari missmatch impedance dan setelah pemasangan termination resistor series dan parallel.

Source Termination: Add series resistor at driver. Value RT = Z0 – RS. If RS is small, then RT = Z0.

Load Termination: Add parallel resistor at receiver. Value such that RL // RT = Z0. If RL is large, then RT = Z0.

Penambahan termination resistor menghasilkan sinyak yang OK.
Ada beberapa metode dalam menambahkan termination resistor, cek link berikut.

Kelebihan series termination:

  • Simpel, hanya memerlukan satu resistor
  • Konsumsi daya rendah
  • Sebagai pembatas arus ketika mendrive beban capasitif yang besar, hal ini juga menigkatkan performa jitter dengan mengurangi ground bouce.

Sedangkan kelemahannya adalah:

  • Meningkatkan rise time dan fall time sinyal di sisi load. Hal ini terkadang tidak bisa ditoleransi dalam beberapa aplikasi high speed.
  • Tidak dapat mendrive banyak load.

Sedangkan untuk parallel termination:

  • Bisa menghasilkan sinyal yang lebih jernih dan mengeliminasi reflection pada sisi beban. Termination ini harus dipasang sedekat mungkin dengan beban.
  • Konsumsi arus yang besar. Tidak direkomendasika untuk aplikasi low power.
  • Falling edge akan lebih cepat dari pada rising edge, maka memungkinkan perubahan duty cycle pada sinyal clock.

Berikut adalah kurva arus dari masing-masing termination. Parallel termination mempunyai Irms yang besar yaitu 0.0112 A. Sedangkan series termination mempunyai Irms yang kecil yaitu 0.00256 A.

Selain itu, series termination hanya dapat mengeliminasi reflection pada sisi end load saja. Dan hal ini tidak baik untuk topology banyak receiver.

Sedangkan parallel termination bisa mengeliminasi reflection di semua point karena itu cara ini cocok untuk topology banyak receiver seperti bus clock. Tetapi sinyal yang dihasilkan menjadi terpotong karena efek pembagi tegangan dari RS dan RT.

Kita lihat hasil simulasi berikut yang menunjukan reflection sinyal yang di ukur di node VReflection.

Berikut contoh reflection signal pada sebuah kabel tanpa termination.

Lalu bagaimana jika saya tambahkan komponen input capacitance yang tadinya kita coba abaikan dengan mensetting kecil sekali, sekarang kita ubah menjadi 15pF.

Simulasi L < 2xRT

Dengan penambahan variable input capacitance 15pF, sinyal yang tadinya pada nilai L = 15 mm tidak ada osilasi, sekarang menjadi ada osilasi. Padahal nilai critical lenghtnya 17.78 mm. Selain itu, signal propagation delaynya bertambah lebar.

Simulasi pada L = 40 mm.

Simulasi L > 2xRT, resistor termination

Penambahan variable input capacitance 15pF menjadikan bentuk sinyal lebih kompleks.

Permasalahan impedance memang menjadi hal penting dalam signal integrity. Untuk mengukur impedance, salah satu metodologi yang digunakan adalah TDR (Time Domain Reflectometry). TDR mengukur reflection dari sinyal pulse yang ditransmisikan melalui sebuah transmission line (jalur PCB, kabel, konektor, dll) yang akan diamati.

Pengukuran TDR dideskripsikan dengan Reflection Coefficient ρ (rho). ketika ZL sama dengan Z0 maka match impedance dan tidak ada reflection.

Ketika ZL terbaca nol yang menandakan short circuit. Sinyal reflected wave sama dengan incident wave tapi mempunyai polaritas yang berlawanan. Rho bernilai -1.

Ketika ZL bernilai tak hingga atau open circuit, reflected wave sama dengan incident wave dan mempunyai polaritas yang sama. Rho bernilai 1.

Nilai dari ZL dan Z0 dapat dihitung dengan persamaan berikut.

Pada zaman sekarang banyak perangkat osiloskop yang sudah bisa mengukur TDR. Pengukuran TDR biasa ditampilkan dengan satuan volt, ohm atau rho pada sisi vertical dan time/waktu pada sisi horizontal. Berikut beberapa efek sinyal yang dihasilkan terhadap beberapa bentuk transmission line dan load.

Rise time, settling time, dan pulse aberraton dapat mempengaruhi pengukuran TDR.

Saya coba mensimulasikan pengukuran TDR ini di software Qucs. Saya menggunakan sebuah pulse dengan rise time yang cepat dengan nilai 10 ps dan saya tambah delay agar sinyal tidak rapat di time nol, maka saya kasih delay 500ps.

Dari kurva rho bisa kita ukur panjang dari line yang match impedance yaitu dari 5.22e-10 s sampai 2.5e-09 s. Dengan asumsi kecepatan sinyal pada ideal transmission line di Qucs sama dengan velocity di udara, 11.8 inch/ns, dan panjang Tp transmission line sama dengan 2Tp panjang di kurva rho, maka bisa dihitung panjang transmission line. Len = ((2.5E-9ns – 0.522E-9ns)*11.8inch/ns)/2 menghasilkan nilai 11.67 inch atau sekitar 296.42 mm mendekati ~300mm. Pada kurva Z_firstorder juga bisa dilihat impedance di load sekitar 1e4 atau 10K Ohm.

Saya coba denga nilai yang bervariasi.

Pada rho bernilai 1 menandakan load bernilai besar seperti open circuit terletak pada time 1.92e-9 s. Sekilas bisa kita lihat nilai impedance yang terukur sekitar 100, 50 dan 120 Ohm dengan perbandingan panjang menyerupai 0.1, 0.04, 0.07 m. Ini hanya sebatas simulasi. Mungkin pada pengukuran aslinya, masalahnya akan lebih kompleks.

2.Kasus 2: Clock Distribution.
Dahulu saya menemukan sinyal seperti sirip hiu ketika harus mengevaluasi sebuah sinyal yang terkoneksi antara main board dan sebuah panel board baru dengan koneksi kabel FFC yang lumayan panjang. Ya, seingat saya sepert itu. Kalau tidak salah di jalur bus clock yang didistribusikan ke 3 IC shift register untuk LCD driver dan keypad. Sinyal tersebut masuk kategori NG karena nilai rise timenya terlalu besar sehingga tidak masuk standard. Oleh karena itu, dengan pengetahuan yang terbatas, alias ilmu kira-kira, saya mencoba memodif rangkaian circuit nya dengan trial and error nilai komponrn RC di jalur tersebut. Susah sekali mendapatkan sinyal yang OK. Akhirnya saya mencukupkan usaha saya, yang penting tetap memenuhi standar electrical characteristic di datasheet. Beginilah kira-kira bentuk sinyalnya. Saya coba mensimulasikannya di Qucs dengan nilai perkiraan bahwa output impedance sebuah IC driver adalah ideal 50 Ohm dan input impedance receiver adalah 1M merujuk pada dokumen ini link. Sedangkan transmission line impedancenya kita setting 10 Ohm. Berikut hasilnya.

Ternyata sinyal sirip hiu tersebut muncul ketika terjadi penurunan impedance antara 50 Ohm di Rs menuju 10 Ohm di transmission line. Standarnya, jalur PCB itu harus punya impedance 50 Ohm. Tetapi saya masih belum yakin hal ini dikarenakan impedance di trace PCB. Saya sempat ukur sinyal di PCBnya saja tanpa dihubungkan ke panel board, hasilnya OK. Berati PCB trace di main board tidak bermasalah. Lalu, saya menemukan sebuah artikel yang menyebutkan bahwa ketika mendistribusikan sinyal clock, maka akan beresiko mengurangi kualitas sinyal.

Dalam artikel tersebut disebutkan bahwa jika satu sumber clock digunakan untuk men-drive banyak beban, maka akan terjadi penurunan kualitas sinyal jika total load/beban melebihi kapasitas sumber clock. Gejala yang muncul biasanya adalah clipping, symmetry imbalance, penurunan amplitude sinyal dan perubahan rise dan fall time, dan hal ini seiring juga dengan peningkatan frekuensi sumber clock.
Saya teringat, memang design clock source ini dibagi ke 3 IC shift register di panel board dan mempunyai frekuensi yang besar. Lalu, berdasarkan artikel ini bahwa IC CMOS mempunyai input capacitance sekitar 15pF dan input resistance sekitar 60M Ohm. Ya, memang setiap device mempunyai nilai paracitic capacitance.

Mungkin inilah penyebabnya (input capacitance) yang menyebabkan nilai rise time menjadi besar. Jika jalur clock di distribusikan ke banyak komponen, maka beresiko memperlebar rise time. Saya juga menemukan dokumen ini yang menyebutkan bahwa drive strength dari sebuah pin output juga mempengaruhi nilai rise time. Drive strength adalah salah satu fitur dari pin mikrokontroller yang bisa mengatur kemampuan pin untuk bisa memilih arus maksimum yang bisa dikeluarkan untuk mendrive load. Diatur melalui register dengan pilihan biasanya 2, 4, 6,dan 8mA.

Lalu saya coba mensimulasikannya di Qucs dan transmission linenya saya tidak pakai karena saya anggap masalahnya bukan di jalur PCBnya. Untuk mensimulasikan drive strength saya menggunakan resistor limiter untuk membatasi arus dari voltage source. Nilainya adalah 1650, 825, 550, 412.5 untuk merepresntasikan drive strength 2, 4, 6, dan 8 mA dengan menggunakan rumus V=IxR. Untuk setingan clock source, saya menggunakan sebuah datasheet dari color LCD 1.4 inch untuk mensimulasikan kebutuhan clock paling kritis dari board panel. Nilai rise time saya pakai 30ns dan frekuensi 1.84MHz atau perioda 543ns. Pertama, saya coba mendrive satu load dengan drive strength 2mA atau dengan resistor limiter 1650 Ohm. Ketika source clock mendrive satu load, sinyalnya masih OK.

Akan tetapi, ketika mendrive 3 load, nilai rise time bertambah.

Terkadang, rise time yang cepat juga akan mengakibatkan forward crosstalk. Semakin cepat rise time makan semakin besar pula amplitude crosstalk dan semakin tajam pula sinyal crosstalknya. Akan tetapi, jika memang rise time nya terlalu besar dan menjadi di luar batas spesifikasi, maka harus diperbaiki. Berdasarkan dokumen sebelumnya bahwa dengan meningkatkan drive strength maka nilai rise time akan semain cepat. Sekarang kita akan coba untuk mengganti drive strength menjadi 8 mA atau dengan mengganti resistor limiter menjadi sekitar 412 Ohm.

Ya, hasilnya rise time menjadi lebih cepat. Jika dilihat dari spike konsumsi arus, untuk drive strength 2mA (1650 Ohm) 1 load adalah 1.16mA, dan drive strength 2mA (1650 Ohm) 3 load adalah 1.64mA, terlihat konsumsi arus tidak sampai 3 kali dari load. Tetapi jika driver strength 8mA (412 Ohm) 3 load adalah sekitar 4mA, konsumsi arus sekitar 3 kali bahkan lebih dari yang 2mA 1 load.

Jika kita memperhatikan impedance, berikut beberapa tipe routing untuk multiload clock.

Daisy Chain Routing/Fly-by Topology/ Multi-drop Topology 

With Stubs

Semakin pendek stub, maka semakin sedikit reflection. Karena stub yang panjang bisa mengakibatkan perbedaan impedance dan akan berpengaruh terhadap sinyal. Berikut perbedaan sinyal akibat panjang stub.

-Witout Stubs

Kelemahan dari tipe routing seperti ini adalah ada perbedaan time propagation delay dari setiap device. Device yang lebih dekat mempunyai time propagation delay yang sedikit. Tentu saja untuk device yang memerlukan akurasi waktu, tipe ini tidak cocok. Memory DDR3 dan DDR4 memugkinkan untuk memakai memakai topology ini. Sedangkan memory DDR2 biasanya memakai T-topology. T-topology akan mengakibatkan akumulasi perbedaan impedance di cabangnya. Sehingga akan bermasalah pada frequensi yang tinggi. Karena itu, DDR3 dan DDR4 biasanya memakai fly-by topology.

Star Routing

Pada topology ini, sinyal berjalan dengan waktu yang sama. Jalur harus sama untuk menghindari waktu sinyal yang tidak simetris terhadap time delay.

T-topology

Kontrol Posisi Motor DC Encoder Menggunakan Kontrol PID – Arduino

Spesifikasi: Mikrokontroler: Arduino Nano, Motor Driver: Modul L298, Motor DC dengan Encoder 2 channel, Potensiometer sebagai input posisi, Power supply 5V.

Blok Diagram Sistem Elektronika.

Blok diagram kontrol PID.

Pengaturan pin Arduino untuk motor DC.

Pengaturan pin Arduino untuk sensor encoder.

Source code untuk video di atas sebagai berikut.

#include <util/atomic.h> // For the ATOMIC_BLOCK macro
//motor directory
#define CW  0
#define CCW 1
 
//motor control pin
#define motorDirPin 7
#define motorPWMPin 9
#define enablePin 8
 
//encoder pin
#define encoderPinA 2
#define encoderPinB 4
 
//encoder var
volatile int encoderPos = 0;//variable fungsi interrupt
int encoderPosRead = 0;
int prevEncoderPosRead = 0;
int deltaPos;
float speedMotor;
 
//PID control
float Kp          = 0.2;
float Ki          = 20;
float Kd          = 0;

//variable rumus PID
int   targetPos;
int   error;
int   prevError;
float   integral;
float   derivative;
float   dt = 0.060; // second
int     dtMillis = dt*1000;
float   control;
float   controlRad;
float   prevControlRad;
float   derControlRad;
float   control2PWM;
long int currTime;
long int prevTime;
long int delTime;

//variable pengaturan PWM motor
int   velocity;

//mode program
char modeProg;
 
//fungsi external interrupt encoder
void doEncoderA()
{
  digitalRead(encoderPinB)?encoderPos++:encoderPos--;
}
 
void setup()
{
  int PWMTest = 0;
  //setup interrupt
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), doEncoderA,RISING);
 
  //setup motor driver
  pinMode(motorDirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, HIGH);
 
  Serial.begin(9600);//setting serial
  analogReadResolution(8);//setting adc 8bit
  
  modeProg = 0; //pilih mode test arah motor 1 atau run 0
  if(modeProg==1) //cek arah putar
  {
    while(1)
    {
      digitalWrite(motorDirPin, CW);
      analogWrite(motorPWMPin, 125); 

      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
        encoderPosRead = encoderPos;    
      }
      
      Serial.print("arah_putar:");
      Serial.println(encoderPosRead);// serial ploter
      delay(dtMillis); 
    }
  }
  else if(modeProg==2)//Mode cek speed
  {
    int PWMvelocity = 100; //test perbandingan speed dan PWM
    analogWrite(motorPWMPin, PWMvelocity);
    for(int i=0;i<10;i++)
    {
      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
        encoderPosRead = encoderPos;    
      }
      deltaPos = encoderPosRead - prevEncoderPosRead;
      prevEncoderPosRead = encoderPosRead;
      
      //encoder pada motor ini, satu putaran 360 derajat didapatkan 100 step
      //jadi kecepatan motor dihitung dalam rotasi/detik
      speedMotor = deltaPos/dt/100;//menghitung kecepatan  
      
      Serial.print("raw_speed:");
      Serial.println(speedMotor);
      //Serial.print(" ");
      //Serial.print("PWM:");
      //Serial.println(PWMvelocity);
      
      //pengendalian agar nilai dt sama tiap cycle
      //tidak menggunakana delay
      //berdasarkan pengukuran, setiap serial print memakan waktu 5ms
      //anggap saja kita mengambil dt aman di 60ms
      do 
      {
        currTime = millis();
        delTime = currTime-prevTime;
      }while(delTime<dtMillis);
    
      prevTime = currTime;
    }
    //stop motor
    digitalWrite(motorDirPin, CW);
    analogWrite(motorPWMPin, 0);  
    while(1);    
  }
}
 
void loop()
{
  //========Mode run==============

  //ADC max 255
  //Low pass filter
  targetPos = analogRead(A0);
   
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
    encoderPosRead = encoderPos; //sinyal posisi
  }

  //PID sinyal posisi
  error   = targetPos - encoderPosRead;
  integral += error * dt;
  derivative = (error - prevError)/dt;
  control = (Kp*error) + (Ki*integral) + (Kd*derivative);

  //konversi sinyal PID posisi ke rad, 1 rad = 100 step
  controlRad = control/100;
  
  //konversi sinyal posisi (rad) ke kecepatan (rad/s)
  derControlRad = (controlRad - prevControlRad)/dt;
  prevControlRad = controlRad;
    
  //konversi dari sinyal kecepatan (rad/s) ke PWM
  velocity = min(max(derControlRad/0.06, -255), 255);
  if(velocity >= 0)
  {
      digitalWrite(motorDirPin, CW);
      analogWrite(motorPWMPin, velocity); 
  }
  else
  {
      digitalWrite(motorDirPin, CCW);
      analogWrite(motorPWMPin, 255+velocity);
  }
  
  //===========Serial: ploter
  Serial.print("target:");
  Serial.print(targetPos);
  Serial.print(" ");
  Serial.print("posisi:");
  Serial.println(encoderPosRead);// serial ploter

  //pengendalian agar nilai dt sama tiap cycle
  //tidak menggunakana delay
  //berdasarkan pengukuran, setiap serial print memakan waktu 5ms
  //anggap saja kita mengambil dt aman di 60ms
  do 
  {
    currTime = millis();
    delTime = currTime-prevTime;
  }while(delTime<dtMillis);

  prevTime = currTime;
  prevError = error;
}

Pada video sebelumnya sudah dibuat kontrol posisi motor DC sederhan menggunakan kontrol proporsional saja pada aplikasi simulasi online Tinkercad. Videonya sebagai berikut.

Tuning gain kontrol PID.

Terjadi kendala pada kontrol posisi ini. Respon motor pada PWM rendah tidak linear.

PosisiPWM/60msradrad/s
0000.00 
0500.00 
01000.00 
01500.00 
02000.00 
1250.010.17 
2300.020.33 
5350.050.83 
7400.071.17 
9450.091.50 
12500.122.00 
16550.162.67 
19600.193.17 
21650.213.50 
25700.254.17 
28750.284.67 
31800.315.17 
32850.325.33 
34900.345.67 
37950.376.17 
401000.46.67 
421050.427.00 
431100.437.17 
461150.467.67 
481200.488.00 
491250.498.17 
521300.528.67 
541350.549.00 
561400.569.33 
581450.589.67 
581500.589.67 
611550.6110.17 
631600.6310.50 
651650.6510.83 
671700.6711.17 
681750.6811.33 
701800.711.67 
701850.711.67 
741900.7412.33 
751950.7512.50 
762000.7612.67 
782050.7813.00 
822100.8213.67 
822150.8213.67 
832200.8313.83 
862250.8614.33 
872300.8714.50 
872350.8714.50 
892400.8914.83 
902450.915.00 
902500.915.00 
932550.9315.50 

Maka dari itu dibuat kondisi 2 buah PID yang pertama pada kondisi |error|<30 dan |error|>=30. Hasilnya lebih memuaskan dan responsif.

#include <util/atomic.h> // For the ATOMIC_BLOCK macro
//motor directory
#define CW  0
#define CCW 1
 
//motor control pin
#define motorDirPin 7
#define motorPWMPin 9
#define enablePin 8
 
//encoder pin
#define encoderPinA 2
#define encoderPinB 4
 
//encoder var
volatile int encoderPos = 0;//variable fungsi interrupt
int encoderPosRead = 0;
int prevEncoderPosRead = 0;
int deltaPos;
float speedMotor;
 
//PID control
float Kp;
float Ki;
float Kd;
float KpNormal    = 0.3;
float KiNormal    = 0;
float KdNormal    = 0;
//untuk error di bawah 30, motor tidak berputar tidak linear
float Kp30        = 2.3;
float Ki30        = 0;
float Kd30        = 0.05;

//variable rumus PID
int   targetPos;
int   error;
int   prevError;
float   integral;
float   derivative;
float   dt = 0.060; // second
int     dtMillis = dt*1000;
float   control;
float   controlRad;
float   prevControlRad;
float   derControlRad;
float   control2PWM;
long int currTime;
long int prevTime;
long int delTime;

//variable pengaturan PWM motor
int   velocity;

//mode program
char modeProg;
 
//fungsi external interrupt encoder
void doEncoderA()
{
  digitalRead(encoderPinB)?encoderPos++:encoderPos--;
}
 
void setup()
{
  int PWMTest = 0;
  //setup interrupt
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), doEncoderA,RISING);
 
  //setup motor driver
  pinMode(motorDirPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  digitalWrite(enablePin, HIGH);
 
  Serial.begin(9600);//setting serial
  analogReadResolution(8);//setting adc 8bit
  
  modeProg = 0; //pilih mode test arah motor 1 atau run 0
  if(modeProg==1) //cek arah putar
  {
    while(1)
    {
      digitalWrite(motorDirPin, CW);
      analogWrite(motorPWMPin, 125); 

      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
        encoderPosRead = encoderPos;    
      }
      
      Serial.print("arah_putar:");
      Serial.println(encoderPosRead);// serial ploter
      delay(dtMillis); 
    }
  }
  else if(modeProg==2)//Mode cek posisi vs pwm per 60 ms
  {
    for(int j=0;j<=255;j+=5)
    {
      velocity = j; //test perbandingan speed dan PWM
      prevTime = millis();       
      analogWrite(motorPWMPin, velocity);        
        
      do 
      { 
        currTime = millis();
        delTime = currTime-prevTime;
      }while(delTime<dtMillis);
     
      ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
        encoderPosRead = encoderPos; //sinyal posisi
        encoderPos = 0;
      }
      
      //encoder pada motor ini, satu putaran 360 derajat didapatkan 100 step
      //kecepatan motor dihitung dalam 60 ms, satuan rad/s
      digitalWrite(motorDirPin, CW);
      analogWrite(motorPWMPin, 0);  
      
      //Serial.print("posisi:");
      Serial.println(encoderPosRead);
      
      //pengendalian agar nilai dt sama tiap cycle
      //tidak menggunakana delay       
      do 
      {
        currTime = millis();
        delTime = currTime-prevTime;
      }while(delTime<1500);
    }     
    while(1);
  }
}
 
void loop()
{
  //========Mode run==============

  //ADC max 255
  //Low pass filter
  targetPos = analogRead(A0);
   
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {//fungsi membaca variable dari interrupt
    encoderPosRead = encoderPos; //sinyal posisi
  }

  //PID sinyal posisi
  error   = targetPos - encoderPosRead;  
  integral += error * dt;
  derivative = (error - prevError)/dt;
  
  //PID dibedakan pada error 30
  //karena pada PWM di bawah 30 kurang responsif
  //PWM vs kecepatan tidak linear
  if(abs(error)<30) 
  {  
     Kp = Kp30;
     Ki = Ki30;
     Kd = Kd30;
  }else
  {
     Kp = KpNormal;
     Ki = KiNormal;
     Kd = KdNormal;
  }
  control = (Kp*error) + (Ki*integral) + (Kd*derivative);
    
  //konversi dari sinyal posisi ke PWM
  velocity = min(max(control*2.5+11, -255), 255);
  if(velocity >= 0)
  { 
      //if(velocity<25)velocity = 25;
      digitalWrite(motorDirPin, CW);
      analogWrite(motorPWMPin, velocity); 
  }
  else
  {
      //if(velocity>-25)velocity = -25;
      digitalWrite(motorDirPin, CCW);
      analogWrite(motorPWMPin, 255+velocity);
  }
  
  //===========Serial: ploter
  Serial.print("target:");
  Serial.print(targetPos);
  //Serial.print("delT:");
  //Serial.print(delTime);
  Serial.print(" ");
  Serial.print("posisi:");
  Serial.println(encoderPosRead);// serial ploter

  //pengendalian agar nilai dt sama tiap cycle
  //tidak menggunakana delay
  //berdasarkan pengukuran, setiap serial print memakan waktu 5ms
  //anggap saja kita mengambil dt aman di 60ms
  do 
  {
    currTime = millis();
    delTime = currTime-prevTime;
  }while(delTime<dtMillis);

  prevTime = currTime;
  prevError = error;
}

Plot Pakai 1 PID.

Plot setelah pakai 2 PID.

Kontrol Posisi Motor DC – Encoder Berbasis Arduino (Tinkercad)

Aplikasi simulasi online Tinkercad ini lumayan cukup untuk bisa menjalankan simulasi sistem kontrol sederhana walaupun terkadang macet (hang up).

//motor directory
#define CW 	0
#define CCW 1

// motor control pin
#define 	motorDirPin  7
#define 	motorPWMPin  9
#define 	EnablePin 	 8

// encoder pin
#define 	encoderPinA  2
#define 	encoderPinB  4

//encoder var
int	encoderPos 			= 0;

// PID control
float 	Kp 				= 3.2;
int 	targetPos		= 100;
int 	error;
int 	control;
int 	velocity;

void doEncoderA()
{  	
  digitalRead(encoderPinB)?encoderPos--:encoderPos++;
}

void setup()
{  
  //setup interrupt
  pinMode(encoderPinA, INPUT_PULLUP);
  pinMode(encoderPinB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(encoderPinA), doEncoderA, RISING);
 
  //setup motor driver
  pinMode		(motorDirPin, 	OUTPUT);
  pinMode		(EnablePin, 	OUTPUT);
  digitalWrite	(EnablePin, 	HIGH);
   
  Serial.begin(9600);
}

void loop()
{  	
    error 		= targetPos - encoderPos;
  	control 	= (Kp*error);
  
    velocity = min(max(control,-255), 255);
  	if(velocity>=0)
    {
        digitalWrite(motorDirPin, CW);
  		analogWrite(motorPWMPin, velocity);
  	}
  	else 
  	{
        digitalWrite(motorDirPin, CCW);  		
  		analogWrite(motorPWMPin,(255+velocity));
  	} 
  	Serial.println(encoderPos);
}

Link project di tinkercad: https://www.tinkercad.com/things/i4ylPoYeNeO

Aplikasi pada hardware: https://wangready.wordpress.com/2020/11/20/kontrol-posisi-motor-dc-menggunakakontrol-pid-arduino/

Redmi Note 5A – Ganti Kamera Belakang dan Baterai

Belajar Arduino Uno Membuat Traffic Light (Lampu Lalulintas) – Simulasi Online Tinkercad

Belajar mudah Arduino tanpa harus mahal-mahal membeli alat dan komponen. Tinkercad merupakan aplikasi simulasi online dari Autodesk. Pada aplikasi ini kita bisa melakukan simulasi design 3D model dan electronika. Meski tidak sekompleks Proteus, akan tetapi aplikasi ini dirasa cukup untuk belajar arduino. Dan yang paling penting, aplikasi ini bebas digunakan, tidak berbayar berbeda halnya dengan Proteus yang berlisensi (berbayar).

Link project ini di tinkercad https://www.tinkercad.com/things/9eAGULSOw2y

Membagi Antena Untuk 2 TV

Membagi antena untuk 2 TV bisa menggunakan splitter. Akan tetapi terkadang menyebabkan sinyal melemah. Dengan menggunakan Signal Booster Antena TV/CATV 2 Way, selain dibagi menjadi 2 jalur juga sinyal akan dikuatkan karena sudah terdapat signal amplifier di dalamnya. Di toko online harganya sekitar 50rb-an. Alat ini menggunakan jack CATV.

Alat ini memiliki penguat sinyal di dalamnya sebesar 20dB. Bahkan, beberapa orang menggunakan alat ini sebagai pengganti booster. Dengan menempatkan alat ini dekat dengan antena, penguatan akan maksimal. Akan tetapi, jangan sampai terkena air hujan karena dikhawatirkan konslet.

Akan tetapi, untuk daerah yang sinyal TV-nya kurang bagus, lebih baik ditambahkan booster agar kualitas sinyalnya jernih.

Tips Upgrade RAM Laptop/Komputer

Berikut beberapa hal yang harus diperhatikan ketika akan menambah RAM.

  1. Baca Manual book agar tau maksimum capacity RAM yang bisa di support oleh motherboard.

2. Tipe RAM, apakah tipe RAM yang digunakan di motherboard kalian. Apakah DDR2, DDR3, DDR4.

3. Untuk DDR3, perhatikan apakah tipe DDR3 standar atau DDR3L ( low voltage).

4. Size RAM yang akan ditambahkan. Gunakan kombinasi dual channel untuk hasil yang lebih optimal. Selain itu, perhatikan juga apakah di kemudian hari punya rencana untuk upgrade lagi menjadi lebih besar atau tidak, perhatikan dengan jumlah slot RAM yang tersedia di motherboard.

5. Perhatikan frekuensi RAM agar sesuai dengan spesifikasi.

6. RAM 1 rank atau 2 rank yang cocok dengan motherboard.

7. Awas RAM palsu atau rekondisi.

reference:

-RAM 1 rank VS 2 rank — ref: https://www.youtube.com/watch?v=JtVRs…

-single channel VS dual Channel — ref: https://www.youtube.com/watch?v=-k5wA…

Menghitung Nilai Resistor Pull-up/Pull-down

Sumber: http://www.ti.com/lit/an/slva485/slva485.pdf
A. Pull-up Resistor
1. High State



2. Low State



3. Contoh Menghitung nilai range resistor pull-up berdasarkan datasheet


Menghitung nilai range R pull-up maksimum.


Menghitung nilai range R pull-up minimum.


Maka pilihan nilai resistor, range-nya berkisar pada nilai berikut.

B. Pull-down Resistor
1. High State



2. Low State



3. Contoh Menghitung nilai range resistor pull-down berdasarkan datasheet


Menghitung nilai range R pull-down maksimum.


Menghitung nilai range R pull-down minimum.


Maka pilihan nilai resistor, range-nya berkisar pada nilai berikut.

Define Array in C

ref: https://stackoverflow.com/questions/9846920/define-array-in-c
Contoh kasusny adalah saya ingin membuat/mendefinisikan data array yang bisa saya gunakan dan mengambil dari sebuah header file .h pada sebuah sistem embedded???

Jangan simpan data di header file. Simpan data di file C atau C++ biasa. Kemudian gunakan header file untuk mengakses data.
sebagai contoh, pada sebuah file images.c :

#include "images.h"
const byte numbers1[MAX_NUMBERS1] = { ... };
byte numbers2[MAX_NUMBERS2];       // will be initialsied to 0

kemudian pada file images.h :

#ifndef _IMAGES_H_
#define _IMAGES_H_

typedef unsigned char byte;
#define MAX_NUMBERS1 (450)
        // different constants in case you change something        
#define MAX_NUMBERS2 (450)      
       // even better if you can do const static int MAX_NUMBERS1=450; 
       // but depends on the compiler
extern const byte numbers1[MAX_NUMBERS1] = { ... };
extern byte numbers2[MAX_NUMBERS2];       // will be initialised to 0

#endif

Maka dengan demikian semua file .c pada program dapat mengakses variable array tersebut. Biasanya menyimpan definition dari sebuah variable di header file adalah ide yang buruk.
Sebuah deklarasi variable, misal extern byte numbers2[MAX_NUMBERS2]; memberitahukan compiler C bahwa ada sebuah variable array numbers2 tertulis di program. Jika linker tidak mendapati definisi tersebut maka akan menyebabkan error karena memory tidak akan mengalokasikan slot untuk variable tersebut.
Definisi dari sebuah variable (tanpa extern), misal byte numbers2[MAX_NUMBER2]; akan memberi tahu compiler C bahwa ada variable array dengan nama numbers2 dan memory harus mengalokasikan slot data pada object code dari source file.
Slot memory untuk numbers2 tidak akan dialokasikan oleh compiler C ketika dia melihat ada deklarasi (didahului oleh extern), dialokasikan bila melihat actual definition (tanpa extern).
Jadi, jika kita menggunakan actual definition dari variable apa saja di header file, dan memanggilnya di di banyak file .c, compiler C akan mengalokasikan memory untuk variable tersebut juga lebih dari satu kali. Kemudian linker akan mengeluarkan error (biasanya error multiple definitions of the same name).
Biasanya ada masalah yang sering tidak kita sadari. Ketika kita membuat program pertama kali, dan header file digunakan hanya pada satu program, program bisa dicompile tanpa error. Namun di lain waktu, jika ada program kedua yang memanggil header file tersebut (bisa saja seiring berjalannya waktu kita membagi program utama kita menjadi dua .c yang berbeda), linker akan mengeluarkan error ‘multiple definitions error’. Hal ini akan sangat membingungkan karena yang kita compile secara algoritma tidak ada perubahan.
Kesimpulan: Janga pernah mengalokasikan memory untuk sebuah variable dengan menyimpannya di header file. Yang disimpan di header file cukup variable definition saja.

IOT MQTT: Simple Node-Red + Raspberry Pi

Berikut adalah project sederhana untuk membuat iot dengan menggunakan node red dashboard untuk memonitor push button & mengontrol LED. Node-Red diinstall pada PC dan bertindak sebagai broker. Sedangkan raspberry pi sebagai client. Push button pada GPIO 21 & LED pada GPIO 22.
Skenarionya, HP Android akan mengontrol raspberry pi dengan mengakses halaman web dari node-red.
Langkah pertama adalah install node-red di PC.

Kemudian check npm dan node command.

Kemudian install node-red

kemudian jalankan node-red

Sekarang coba untuk akses dari web browser.

Kemudian kita akan install node-red dashboard dengan cara klik menu dan pilih manage pallete

Lalu install node-red dashboard.

Kemudian cobalah untuk membuat flow sebagai berikut lalu klik deploy mengaktifkan.

Gunakan menu debug untuk melihat message.

Setelah terlihat icon connected pada blok mqtt maka komunikasi sudah siap. Klik tanda panah pada menu dashboard untuk membukan web dashboard.

Install mqqt client pada raspberry pi.

https://eclipse.org/paho/clients/python/

Lalu buat file python .py dan ¬¬¬¬kita jalankan kode berikut pada raspberry pi.

import paho.mqtt.client as mqtt
import RPi.GPIO as GPIO
import time

def on_connect(client, userdata,flags, rc):
	if rc==0:
		print("connected OK")
	else:
		print("Bad connection Returned code=",rc)

def on_message(client, userdata, message):
	print("message received" ,str(message.payload.decode("utf-8")))
	print("message topic=",message.topic)
	print("message qos=",message.qos)
	print("message retain flag=",message.retain)
	if (str(message.payload.decode("utf-8")) == "true"):
		GPIO.output(22, GPIO.HIGH)
	else:
		GPIO.output(22, GPIO.LOW)		

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(21, GPIO.IN)
GPIO.setup(22, GPIO.OUT)

broker="10.20.56.34"
client=mqtt.Client("telu")
client.on_connect = on_connect
client.on_message = on_message
print("Connecting to broker",broker)
client.loop_start()
client.connect(broker,1883,60)
bool2send = False
while True:
	client.subscribe("edg/SlideRaspi")
	if (GPIO.input(21) == False):
		bool2send = True
	else:
		bool2send = False
	client.publish("edg/StatButton",str(bool2send))
	print("publish" ,str(bool2send))
	time.sleep(1)
client.loop_stop()
client.disconnect()

lalu jalankan pada raspberry pi dengan perintah sudo python [nama file].py
Bisa juga diatur agan program tersebut diset sebagai program autorun agar dijalankan secara otomatis saat raspberry pi pertama dinyalakan.
Setelah node-red pada PC dijalankan dan program client pada raspberry dijalankan, maka raspberry pi sudah bisa dikontrol lewat Android.


——————————————————
ref:
youtube.com/watch?v=O-FDqkhCryA
youtube.com/watch?v=xWzz8wGgdkU

Step by step installing and configuring Mosquitto with Windows 7


https://developer.ibm.com/recipes/tutorials/generating-random-numbers-in-nodered/
https://eclipse.org/paho/clients/python/
youtube.com/watch?v=Pb3FLznsdwI
https://raspberrypi.stackexchange.com/questions/51621/installing-all-python-modules
https://learn.adafruit.com/playing-sounds-and-using-buttons-with-raspberry-pi/install-python-module-rpi-dot-gpio