Kendali Motor Servo Menggunakan PWM dari Timer
December 30, 2012 49 Comments
motor servo adalah sebuah aktuator yang bergerak dalam poros yang mempunyai spesifikasi untuk control posisi sudut yang presisi. Banyak jenis motor servo. Ada yang dikontrol secara serial ataupun dengan PWM. Kali ini kita akan mencoba megatur posisi dari sebuah motor servo dengan menggunakan PWM yang dibangkitkan dari timer1. PWM harus diatur agar memenuhi standar sinyal input untuk motor servo sehingga motor servo dapat bergerak sesuai dengan perintah yang kita kirimkan lewat PWM.
Review motor servo.
Nilai input PWM pada setiap servo terkadang berbeda sesuai merek. Misalnya saja Servo Futaba tipe Futaba S3003 – Standard Servo.
0.388ms = 0 degree.
1.264ms = 90 degrees. (neutral position)
2.14ms = 180 degrees.
Akan tetapi kita coba dengan setting pada proteus seperti berikut.
Mari kita mulai.Buat sebuah folder untuk project yang akan kita buat. Semua file project akan disimpan di folder ini. Buat folder dengan nama motor servo.
Pertama buat new project, lalu masukan chip ATMega8535, ambil dari library.
masukan juga komponen motor servo
Lalu rangkai seperti gambar sirkuit di bawah ini.
Untuk komponen VCC dan Ground bisa di drag dari Terminals mode.
Sekarang kita beralih ke codevisionAVR. Buat new project. Klik File, lalu New.
Kemudian pilih Project, lalu klik OK.
Jika muncul pertanyaan seperti di bawah ini. Klik Yes.
Setelah itu kita lakukan pengaturan untuk project yang akan kita buat. Pertama setting chip yang akan digunakan. Kita gunakan ATmega8535 dan nilai clock yaitu 16 MHz. (dalam pemrograman, tanda titik berarti koma, untuk memisahkan bilangan decimal. Missal 3,7 maka diketik 3.7 dan begitu pula jika kita memasukan nilai clock 11,059200MHz maka diketik 11.059200 MHz.
Lalu kita lakukan setting pada bagian Timer1. Kita akan gunakan Timer1 ini untuk membangkitkan sinyal PWM yang digunakan untuk mengontrol motor servo. Lakukan setting di bawah ini.
kita akan menggunakan mode
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 250.000 kHz
// Mode: Fast PWM top=ICR1
// OC1A output: Non-Inv.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
Dari informasi diatas kita dapati bahwa counter akan berjalan dalam interval waktu T=1/250kHz yaitu 4us, dan akan menggunaka mode Fast PWM. Dengan nilai TOP counter yaitu ICR1 1387 atau 4999. Hal ini dikarenakan kita membutuhkan sinya PWM dengan lebar perioda 20ms atau frekuensi 50Hz untuk mengendalikan servo. Berikut perhitungannya.
Sedangkan nilai lebar pulsa high dari PWM atau lebar dutty cycle positif akan ditentukan oleh register OCR1.
Dutty cycle: perbandingan lebar perioda HIGH dengan lebar perioda gelombang.
perhitungan OCR1 untuk menghasilkan lebar pulsa dari 1ms – 2ms (sesuai settingan servo di simulasi proteus) adalah.
OCR1A= (((sudut + 90)/90)*1000)/4;
Dimana nilai 1ms – 2ms tersebut akan menghasilkan nilai sudut dari -900 sampai 900. Perhitungan di atas didapat berdasarkan bahwa couter berjalan dalam interval 250kHz atau 4us sehingga untuk menghasilkan gelombang 1ms – 2ms adalah.
Atau jika kita menggunakan MS-Exel untuk mendapat persamaannya maka setelah membuat table dari data sumbu x untuk sudut x={-90,90} dan y untuk OCR1A={250,500} maka kita bisa mendapatkan persamaan linier dengan cara klik kanan pada garis yang ada di table dan klik Add Trendline.
Lalu setting seperti berikut.
Maka akan muncul persamaan.
y = 1.388x + 375
dan kita ganti nilai x dan y sehingga menjadi.
OCR1A = (unsigned int)((1.388*(float)sudut) + 375);
Sekarang kita masukan ke program.
/***************************************************** Chip type : ATmega8535 Program type : Application AVR Core Clock frequency: 16,000000 MHz Memory model : Small External RAM size : 0 Data Stack size : 128 *****************************************************/ #include #include // Declare your global variables here void set_servo(int sudut) { unsigned int buff_sudut = (unsigned int)((1.388*(float)sudut) + 375); OCR1AH = (unsigned char)(((buff_sudut & (unsigned int)0xFF00))>>8); OCR1AL = (unsigned char) (buff_sudut & (unsigned int)0x00FF); } void main(void) { // Declare your local variables here // Input/Output Ports initialization // Port A initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTA=0x00; DDRA=0x00; // Port B initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTB=0x00; DDRB=0x00; // Port C initialization // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T PORTC=0x00; DDRC=0x00; // Port D initialization // Func7=In Func6=In Func5=Out Func4=In Func3=In Func2=In Func1=In Func0=In // State7=T State6=T State5=0 State4=T State3=T State2=T State1=T State0=T PORTD=0x00; DDRD=0x20; // Timer/Counter 0 initialization // Clock source: System Clock // Clock value: Timer 0 Stopped // Mode: Normal top=0xFF // OC0 output: Disconnected TCCR0=0x00; TCNT0=0x00; OCR0=0x00; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 250,000 kHz // Mode: Fast PWM top=ICR1 // OC1A output: Non-Inv. // OC1B output: Discon. // Noise Canceler: Off // Input Capture on Falling Edge // Timer1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off TCCR1A=0x82; TCCR1B=0x1B; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x13; ICR1L=0x87; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; // Timer/Counter 2 initialization // Clock source: System Clock // Clock value: Timer2 Stopped // Mode: Normal top=0xFF // OC2 output: Disconnected ASSR=0x00; TCCR2=0x00; TCNT2=0x00; OCR2=0x00; // External Interrupt(s) initialization // INT0: Off // INT1: Off // INT2: Off MCUCR=0x00; MCUCSR=0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x00; // USART initialization // USART disabled UCSRB=0x00; // Analog Comparator initialization // Analog Comparator: Off // Analog Comparator Input Capture by Timer/Counter 1: Off ACSR=0x80; SFIOR=0x00; // ADC initialization // ADC disabled ADCSRA=0x00; // SPI initialization // SPI disabled SPCR=0x00; // TWI initialization // TWI disabled TWCR=0x00; while (1) { // Place your code here set_servo(-90); delay_ms(1000); set_servo(0); delay_ms(2000); set_servo(90); delay_ms(1000); } }
Lalu klik Make the project.
Klik OK, maka akan ter-generate file .hex. File inilah yang akan diupload ke dalam mikrokontroler.
Seperti biasa jangan lupa lakukan pengaturan di chip ATMEGA8535.
Klik pada ikon folder Program File. Browse file .hex yang sudah kita generate tadi di codevisionAVR.
Atur CKSEL Fuses menjadi Ext. Clock dan Clock Frequency dengan nilai clock 16MHz yang sudah kita atur juga pada codevisionAVR.
Klik OK.
Setelah proses setting selesai, maka langsung saja klik Play.
Untuk melihat sinyal yang mengatur motor servo bisa kita tambahkan osiloskop pada rangkaian proteus yang sudah kita buat.
Kita capture di jalur input servo.
Maka bisa dilihat sinyal PWM-nya.
gan, pils post juga cara kendali servo via uart dgn ngirim data posisi sudut maka servo akan bergerak sesuai posisi yg d input ?
trimakasih (y)
agar memudahkan dalam menerima data, agan bisa liat post yg ini https://wangready.wordpress.com/2012/12/30/receive-data-uart-menggunakan-scanf-codevisionavr/. jadi kirim datanya seperti AT command. misal LED 1 SERVO 90.
gan,, rumus 64 (1+TOP) itu maksudnya apa?dapet darimana ya ?
klo 64 itu nilai preskaler: nilai crystal/preskaler_timer = frekuensi timer
TOP berarti nilai dari ICR, nilai jumlah batas counter
gan gimana kalo ada 3 servo yang di kendalikan? ada tutorialnya ato website referensi
trimakasih
bisa aja gan, asal di mikronya ada fitur yang sama: timer yg ada register ICR nya biar bisa disetting buat bikin PWM servo….referensi gk punya
gan ane masih newbee mau tanya
apa itu cuma bisa lewat port oc1a aja ya?
iya gan, setau ane atmega8535 cuma bisa port tsb, klo port timer yg lain gk cukup buat counter nya…kecuali bikin PWM manual…
gk tau tp kalo atmega versi lain….
untuk mencari OCR1A
= (((sudut + 90)/90)*1000)/4
misal dipakai sudut -90
(-90 + 90)=0
0/90*1000/4=0
kok hasil nya 0 gan ?
ganti aja gan sama yg ini, ini udah disederhanakan persamaannya sehingga tidak ada nilai nol yg dibagi
OCR1A = (unsigned int)((1.388*(float)sudut) + 375);
gan aku mau buat pintu otomatis pake servo ,,,,gimana supaya mutar 180 drajat gan ???
buat pintu beneran mah harus pake servo gede gan, mending pake pneumatic….
bukannya servo biasa jg bisa 180 derajat gan, tinggal kasih pwm aja…
Reblogged this on Jejak catatan mahasiswa Pendidikan Teknik Elektro.
pak, kalo ngontrol motor servo dengan serial itu gmn caranya?
int derajat;
while(1)
{
scanf(“servo=%d”,&derajat);
servo(derajat);
}
jadi tinggal kirim aja lewat serial servo=nilai derajat nya, fungsi servonya ada di atas gan
gan,kalo mau bikin sudutnya 30,60, sama 90 gimana ya?
gan, kalau ngontrol sudut servo dengan pwm melalui interupt gimana caranya??
gan bagaimana caranya kalau kita ingin menggerakkan 45 derajat kiri dan kanan ??
tinggal pakai aja nilai -45 dan 45 pada fungsi set_servo gan
sudah saya coba dan simulasikan di proteus gan , saya ubah nilainya di set_sevo -45 dan 45 tapi tetap aja gan di +90 dan -90, apa ada yg harus diubah di timer1nya atau di programnya saja gan,minta solusinya gan???
settingan servo di proteus nya sudah sesuai belum gan…
gan itu kenapa harus T=1/250KHz = 4?
maksudnya 4us gan. maaf salah ketik. Itu dari rumus T=1/f
Perioda=1/frekuensi
Itu kalau mau menggerakan dari 90 ke 0 ke -90 itu gmana gan?
tinggal panggil aja fungsinya gan….
set_servo(90); //set servo ke 90 derajat
delay_ms(1000); //tahan posisi servo selama 1000ms
set_servo(0); //set servo ke 0 derajat
delay_ms(1000); //tahan posisi servo selama 1000ms
set_servo(-90); //set servo ke -90 derajat
delay_ms(1000); //tahan posisi servo selama 1000ms
saya udah buat seperti yang di atas,,, servonya ada bunyi sedikit,, tapi gak muter,, apa suplai powernya yang kurang ya gan,,,mohon dijawab,,, buat skripsi soalnya
bisa jadi gan powernya. coba cek jg pwm nya pake osiloskop.
bro kok punyaku ga bisa di set sudut ya misal di set sudut 30 tetep masuk sudut 90
gimana ya?
masa gan… xtalnya pake yg berapa gan?? kalo post diatas pake 16MHz.
coba cek tanpa persamaan gan, kasih aja nilai PWM langsung
16MHz juga bro
sekarang malah ga bisa gerak
apa ada masalah sama power supply ya?
coba cek gan sinyal pwm nya pake osciloscop, atau agan coba cek pake simulasi proteus dulu
Numpang nanya saya, pake atmega2560 saya ikutin program diatas lalu coba compile muncul error OCR1A undefined symbol, solusinya gimana ya ? apa saya harus gunain OCR1AH ato OCR1AL untuk deklarasi rumus tersebut?
kok error ya, kalo saya pake CVAVR versi 1.xx bisa sih gan… hmmm mungkin bisa coba pake ini:
void set_servo(int sudut)
{
int ConvertOCR = (((sudut + 90)/90)*1000)/4;
OCR1AL = convertOCR & 255;
OCR1AH = convertOCR & 65280;
}
OCR1A = (unsigned int)((1.388*(float)sudut) + 375);
gan itu nilai unsigned int diisi berapa?terus nilai float diisi berapa?
dan nilai 1388, 375 itu didapat dari mana?
mohon dijawab gan, buat tugas akhir soalnya
(unsigned int) dan (float) itu untuk perubahan type data gan, silahkan cari di tutorial bahasa C… 1388 n 375 itu didapat dari persamaan sudut ke pwm, silahkan baca di artikelnya, sudah sy jelaskan…
motor servo saya kan 360 derajat gan, apa bisa dibuat maksimal dengan putaran 90 derajat?soalnya range sudut yang saya perlukan antara 0-90 derajat gan
diatur aja gan nilai pwm nya
berarti apakah nilai unsign int dan float itu harus diisi juga gan?
tidak diisi gan
Gan, saya coba langsung upload ke sismin tpi muternya gak sampe 90 derajat ya ?
Terus syaa ganti gradiennya 1.388 jadi 2.5 jdi bisa 180 derajat, ini ada kemungkinan ngerusak gak ??
bisa jd tiap merek nilai perbandingan perioda pwm nya berbeda gan. kalo yg di artikel ini sy hanya simulasi aja. untuk pwm servo merek lain mungkin bisa cari di datasheetnya. keknya gk ngerusak gan…
gan, saya pakai atmega 328 waktu compile muncul undefined symbol OCR1A.
kalau pakai rumus yang ini, sudut hasil servo tidak sesuai sudut di program dan maks sampai 90 derajat. mohon bantuannya
void set_servo(int sudut)
{
int ConvertOCR = (((sudut + 90)/90)*1000)/4;
OCR1AL = convertOCR & 255;
OCR1AH = convertOCR & 65280;
}
Compilernya pake apa gan?? CodevisionAVR apa atmel studio?? Coba cek adrressing untuk OCR1A ada apa enggak di compilernya…
Kalo untuk rumus disesuaikan aja sma spek dari servo…
pakai cvavr. anehnya kalau pake atmega 16 ndak error, begitu build baru pake atmega 328 langsung error undefined..
saya blum paham yg ini
OCR1AL = convertOCR & 255;
OCR1AH = convertOCR & 65280;
nilai 255 dan 65280 dari perhitungan apa udah dri sananya segitu gan?
Maaf gan ternyata kodingan saya salah. Silahkan coba kodingan di atas. Sudah saya update. compiler saya CVAVR versi 2.
berikut fungsi servonya:
unsigned int buff_sudut = (unsigned int)((1.388*(float)sudut) + 375);
OCR1AH = (unsigned char)(((buff_sudut & (unsigned int)0xFF00))>>8);
OCR1AL = (unsigned char) (buff_sudut & (unsigned int)0x00FF);
berdasarkan perhitungan, nilai OCR1A harus antara 250 decimal/0x00F4 hex (-90 derajat) dan 500 decimal/0x01FA hex (90 derajat). Akan tetapi, register OCR1A adalah 16 bit. maka register OCR1A bisa dipecah jadi OCR1AL (low bit) dan OCR1AH (high bit). kalo di atmega8535 kalau pun pake OCR1A (16 bit) langsung tidak error gan. misal saya isi OCR1A = 500;. tapi kalo di agan error, pake yang register OCR1AL dan OCR1AH aja. kalo pake OCR1AL dan OCR1AH urutan kode nya yang high bit dulu:
OCR1AH = (unsigned char)(((buff_sudut & (unsigned int)0xFF00))>>8);
OCR1AL = (unsigned char) (buff_sudut & (unsigned int)0x00FF);
jangan dibalik jadi
OCR1AL = (unsigned char) (buff_sudut & (unsigned int)0x00FF);
OCR1AH = (unsigned char)(((buff_sudut & (unsigned int)0xFF00))>>8);
karena setelah dicompile hasilnya pwmnya gk sesuai walaupun di compiler tidak error. mungkin ini terkait akses register yang tertimpa register sebelumnya.
Jangan lupa, pastikan memakai nilai oscilator crystal yang sama dengan setingan kodingan di atas, yaitu 16MHz.
oke gan saya coba.. makasih infonya 👍👍
gan cara gerakin microservo dengan waktu yg telah saya tentukan menggunakan rtcds1307, menggunakan aplikasi codevision avr ,, gimana ya caranya , mohon bantuannya gan?
masih kurang mengerti di bagian kode berikut :
unsigned int buff_sudut = (unsigned int)((1.388*(float)sudut) + 375);
OCR1AH = (unsigned char)(((buff_sudut & (unsigned int)0xFF00))>>8);
OCR1AL = (unsigned char) (buff_sudut & (unsigned int)0x00FF);
gan ini pake internal oscilator 4Mhz gabisa ya? walau disesuaikan sama nilai – nilai lain