Zybo ile Ultrasonik Sensör Uygulaması (Distance Measurement by Ultrasonic Sensor with Zybo)
-
Upload
melek-soenmez -
Category
Technology
-
view
317 -
download
11
Transcript of Zybo ile Ultrasonik Sensör Uygulaması (Distance Measurement by Ultrasonic Sensor with Zybo)
KOCAELİ ÜNİVERSİTESİ
ELEKTRONİK VE HABERLEŞME MÜHENDİSLİĞİ
BÖLÜMÜ
ZYBO İLE ULTRASONİK SENSÖR UYGULAMASI
6.DÖNEM PROJESİ
Yrd. Doç. Dr. Anıl Çelebi
MELEK SÖNMEZ
130207075
ÖZET
Ultrasonik sensörler araç park sensörleri, otomatik kapı sensörleri, hırsız algılama
sensörleri olarak günlük hayatta yaygın olarak kullanılır. Endüstriyel uygulamalarda
ultrasonik sensörler, sıvı seviyesi algılanmasında, akışkanlık algılamada, ahşap, kâğıt,
cam, şişe vb. fabrikalarda ürün algılamada ve daha birçok uygulamada kullanılırlar.
FPGAler ise sayısal işaret işleme, radar haberleşme sistemi, uzay, savunma sistemleri,
medikal resimleme, robotik, ses tanıma, şifreleme ve kod çözme gibi birçok alanda
kullanılmaktadır. İlerleyen teknoloji ve paralel işlem yapabilme yeteneğine duyulan
ihtiyacın artması ile FPGAler günlük hayata daha fazla adapte olmaktadır.
Bu projede, HC-SR04 ultrasonik sensörü ZYBO FPGA kartın ARM-Cortex-A9
işlemcisi ile programlanarak mesafe ölçümü uygulaması gerçeklenmiştir.
1. ZYBO İLE GÖMÜLÜ SİSTEMLER
1.1 VİVADO, BLOK TASARIM OLUŞTURMA
1.1.1. ÖRNEK TASARIM
Örnek bir tasarım olarak Zybo kart üzerinde bulunan switchlerin değerlerini yine Zybo
kart üzerinde bulunan ledlere yazdırma uygulaması anlatılacaktır.
Vivado 2015.4 programı çalıştırılır. Şekil 1.1 ekranında görülen create new project
seçeneği seçilir. Açılan pencerede next butonuna tıklanıp Şekil 1.2’deki yeni pencerede
projenin adı yazılır ve next butonuna basılır.
Şekil 1.1 Şekil 1.2
Yeni pencere Şekil 1.3’te RTL project seçeneği seçilip next butonuna tıklanır ve
eklenecek kaynak dosya varsa bu aşamadan sonra projeye eklenir. Eğer eklenecek
kaynak dosya, benzetim dosyası, xdc dosyası veya ip dosyası yoksa açılan yeni
pencerelerde 4 kere next butonuna tıklanır. Uygulamanın yapılacağı kartın seçileceği
ekrandan (Şekil 1.4) Zybo kart seçilir ve önce next sonra finish butonlarına tıklanarak
proje oluşturulur.
Şekil 1.3 Şekil 1.4
Burada önemli bir nokta, Zybo kartın dosyaları Vivado programı yüklenirken otomatik
olarak bilgisayara gelmez. Zybo.zip dosyası Xilinx’in sitesinden indirilerek bilgisayarda
C:\Xilinx\Vivado\2016.1\data\boards\board_parts adresine eklenir.
Blok tasarım oluşturmak için sol tarafta bulunan flow navigator panelinden ip integrator
menüsünden create block design seçilir(Şekil 1.5), açılır pencerede tasarımın adı yazılır
ve ok butonuna basılır(Şekil 1.6).
Şekil 1.5 Şekil 1.6
Diagram kenar çubuğu altında bulunan simgesine tıklanarak ip kataloğu açılır (Şekil
1.7.a) ve ZYNQ7 Processing System (Şekil 1.7.b) tasarıma eklenir. Diagram kenar
çubuğunun üst kısmında çıkan run block automation seçeneğine tıklanır (Şekil 1.8).
Şekil 1.7.a Şekil 1.7.b Şekil 1.8
Açılan pencerede ok butonuna tıklanır (Şekil 1.9) ve işlemci blok tasarımda Şekil
1.10’da gösterilen halini alır.
Şekil 1.9 Şekil 1.10
ZYNQ ip bloğuna çift tıklanarak ip ayarları istenilene göre düzenlenir (Şekil 1.11).
Şekil 1.11
Açılan pencerede peripheral i/o pins sekmesine tıklanarak (Şekil 1.12) tasarımda
kullanılacak olan giriş/çıkış portları aktif veya pasif edilir. Bu aşamada genellikle
UART1 portu pasif edilmez, çünkü Zybo kart ile bilgisayar arasında haberleşme bu port
ile yapılır.
Şekil 1.12
Yine aynı pencerede clock configuration sekmesinden tasarımda kaç tane clock
kullanılacağı ve bunların hızları ayarlayanabilir (Şekil 1.13).
Şekil 1.13
Tasarımda kesme uygulaması kullanılacaksa işlemcinin kesme özelliği re-customize ip
penceresindeki interrupts sekmesinden kullanılacak olan kesmeye uygun kesme
seçeneği aktifleştirilerek tasarım kesme uygulamasına uygun hale getirilir (Şekil 1.14).
Bu örnekte kesme uygulaması kullanılmayacağı kesme portları pasif bırakılır.
Şekil 1.14
İşlemcinin ip ayarları yapılandırıldıktan sonra Zybo kart üzerindeki led ve switchleri
tasarıma ekleyebilmek için ip kataloğundan axi gpio eklenir (Şekil 1.15). Ip tasarıma
eklendikten sonra oluşan (Şekil 1.16) run connection automation seçeneğine tıklanır.
Şekil 1.15 Şekil 1.16
Açılan pencerede tüm seçenekler işaretlenip GPIO seçeneğinde istenilen ip
seçilir(switchler için sws_4bits, ledler için leds_4bits) (Şekil 1.17) ve ok butonuna
basılarak ip ayarları tamamlanmış olur.
Şekil 1.17
Tasarım tamamlandığında blok şema Şekil 1.18’deki gibi olur.
Şekil 1.18
Tasarım bittikten sonra butonuna tıklanarak tasarımda bir hata olup olmadığı
kontrol edilir (Şekil 1.19).
Şekil 1.19
Oluşturulan tasarımın sentezlenebilmesi ve SDK’ya ve oradan da karta aktarılabilmesi
için sources bölmesinde design_1.bd (Şekil 1.20) isimli blok tasarıma fare ile sağ
tıklanıp (Şekil 1.21) önce generate output products seçilir daha sonra create hdl wrapper
seçilir ve tasarım sentezlenmeye hazır hale gelmiş olur.
Şekil 1.20 Şekil 1.21
Bu işlemler de tamamlandıktan sonra flow navigator panelinden run synthesis
seçeneğine tıklanır ve böylece sentezleme başlamış olur. Sentez bittikten sonra açılan
pencerede (Şekil 1.22) open synthesized design seçeneği işaretlenip ok butonuna
basılarak sentezlenmiş tasarım açılır.
Şekil 1.22
Sentezlenmiş tasarımın görüntüsü (pin atamaları, kart üzerinde pinlerin yerleşimi) Şekil
1.23’teki gibi olur. Tasarımın sentezlenmesi sonucunda kartta kullanılan bölgeleri
görmek için device sekmesi açılır.
Şekil 1.23
Şekil 1.24’te tasarımda kullanılan portların hangi pinlere atandıkları, çalışacakları
gerilim değerleri vb. özellikler gösterilmektedir. Bu örnek tasarımda port olarak sadece
gpio kullanıldığı için pin atamaları, gerilim değerleri bu pinlerin diğer özellikleri seçili
olarak sentezlenmiştir.
Şekil 1.24
Daha sonra yine flow navigator panelinden generate bitstream seçeneğine tıklanarak
SDK’ya geçirilmek üzere tasarım için bitstream oluşturulur.
Bitstream oluşturulduktan sonra file menüsündeki export hardware seçeneği ile (Şekil
1.25.a) tasarım SDK’ya aktarılmış olur (Şekil 1.25.b). Daha sonra yine file menüsünden
launch SDK seçeneği ile (Şekil 1.26) SDK programına geçilir.
Şekil 1.25.a Şekil 1.25.b Şekil 1.26
SDK programı açıldığında Şekil 1.27’deki gibi gözükür.
Şekil 1.27
File menüsünden new→application project seçilir (Şekil 1.28).
Şekil 1.28
Açılan pencerede proje adı girilip next butonuna basılır (Şekil 1.29.a), empty
application seçeneği işaretlenip finish butonuna tıklanır (Şekil 1.29.b).
Şekil 1.29.a Şekil 1.29.b
Bu işlemden sonra C/C++ dosyalarının ekleneceği “proje_adi” isimli bir klasör ve
“proje_adi_bsp” isimli başka bir klasör projeye eklenir (Şekil 1.30.a). “proje_adi” isimli
klasöre sağ tıklanıp import seçilir (Şekil 1.30.b). Açılan pencerede general→file system
seçilir (Şekil 1.30.c).
Şekil 1.30.a Şekil 1.30.b Şekil 1.30.c
Açılan yeni pencerede browse butonuyla eklenecek kaynak dosyanın klasörü seçilip
tamam butonuna tıklanır (Şekil 1.30.d). Ve Şekil 1.30.e’ de görülen ekranda eklenmek
istenen kaynak dosya/lar seçilip finish butonuna tıklanır.
Şekil 1.30.d Şekil 1.30.e
Aşağıda bu uygulama için yazılmış C kodu görülmektedir. Kodun içeriği ile ilgili
detaylar SDK kısmında detaylıca açıklanmıştır.
#include "xparameters.h"
#include "xgpio.h"
int main (void)
{
XGpio dip, led;
int i, dip_check;
xil_printf("-- Start of the Program --\r\n");
XGpio_Initialize(&dip, 0);
XGpio_SetDataDirection(&dip, 1, 0xffffffff);
XGpio_Initialize(&led, 1);
XGpio_SetDataDirection(&led, 1, 0x00000000);
while (1)
{
dip_check = XGpio_DiscreteRead(&dip, 1);
xil_printf("DIP Switch Status %x\r\n", dip_check);
XGpio_DiscreteWrite(&led, 1, dip_check);
for (i=0; i<9999999; i++);
}
}
Bu adımdan sonra projeyi karta yükleyip uygulamayı gerçekleyebilmek için (Şekil 1.31)
window→show view→other→terminal→terminal seçilip ok butonuna tıklanır (Şekil
1.32).
Şekil 1.31 Şekil 1.32
Usb-uart bağlantı kablosu ile kart bilgisayara bağlanır, Zybo kart güç düğmesinden
açılır. Ekranın sağ alt kısmında açılan terminal bölmesinde simgesine tıklanır (Şekil
1.33.a). Açılan terminal settings penceresinde connection type→serial, baud
rate→115200 seçilir (Şekil 1.33.c / Şekil 1.33.d). Port seçeneğini ayarlarken hangi
COM bağlantı portunun kullanıldığı aygıt yöneticisinde bağlantı noktalarına bakılarak
öğrenilebilir (Şekil 1.33.b).
Şekil 1.33.a Şekil 1.33.b
Şekil 1.33.c Şekil 1.33.d
Terminal bağlantısı yapıldıktan sonra imgesine tıklanır ve açılan pencerede program
butonuna tıklanarak FPGA programlanır (Şekil 1.34).
Şekil 1.34
Projenin uygulamasını kartta gerçekleyebilmek için “proje_adi” isimli klasöre sağ
tıklanıp run as→launch on hardware(GDB) seçilir (Şekil 1.35).
Şekil 1.35
Örnek uygulamanın çıktıları aşağıdaki görsellerde verilmiştir.
Şekil 1.36.a Şekil 1.36.b
Şekil 1.36.c Şekil 1.36.d
1.1.2. IP TASARIMI
Vivado 2015.4 programı çalıştırılır. Manage ip seçeneği tıklanır (Şekil 1.37) ve new ip
location seçilir.
Şekil 1.37
Açılan pencerede next butonuna tıklanıp ip tasarımının kaydedileceği dosya adresi
yazılıp finish butonuna tıklanır. Daha sonra tools→create and package ip seçilir (Şekil
1.38).
Şekil 1.38
Açılan pencerede next butonuna tıklanır, daha sonra create axi4 peripheral seçeneği
seçilip next butonuna tıklanır (Şekil 1.39).
Şekil 1.39
Sonraki pencerede ip tasarımının adı yazılıp next butonuna tıklanır (Şekil 1.40). Daha
sonra next ve finish butonlarına tıklanarak ip projesi oluşturulmuş olur.
Şekil 1.40
Bu işlemlerin sonunda Vivado yeni bir proje penceresi açar. Ip tasarımının verilog hdl
dilinde kodları bu pencerede yazılıp ip tasarımı paketlenir.
Kaynaklar bölmesinde myip_v1_0 kaynak dosyasına çift tıklanarak dosya açılır.
Şekil 1.41
Dosyanın 7.satırına giriş/çıkış portlarının parametreleri yazılır (Şekil 1.42).
// Users to add parameters here
parameter width = 8,
// User parameters ends
Şekil 1.42
Aynı dosyanın 18.satırında, ip tasarımında giriş/çıkış hangi portlar kullanılacaksa, port
tanımlaması yapılır (Şekil 1.43).
// Users to add ports here
input [width-1 : 0] giris,
output [width-1 : 0] cikis,
// User ports ends
Şekil 1.43
48.satırda tanımlanan parametreler, 52.satırda portlar alt fonksiyona bağlamak üzere
yazılır. Kaynaklar bölmesinde myip_v1_0_S00_AXI_inst isimli dosyaya çift tıklanıp
dosya açılır. Diğer dosyada 7. ve 18.satırlar için yapılan işlemler bu dosyada da yapılır.
Dosyanın 400.satırına inilir ve ip tasarımının fonksiyonelliği buraya yazılır. Daha sonra
flow navigator bölmesinden tasarım sentezlenir. Sentez tamamlandıktan sonra package
ip sekmesine gelinir compability seçeneğinde ikonuna tıklanıp add family explicitly
seçilip ZYNQ seçeneği seçilir ve ok butonuna tıklanarak ip tasarımı ZYNQ
mimarisinde de kullanılabilir hale gelmiş olur. File groups seçeneğinde merge file
groups yazısına tıklanır. Customization parameters sekmesinde hidden parameters
genişletip parametreye sağ tıklanıp import ip parameters seçilir ve ok butonuna
tıklanarak kullanıcı parametreleri de diğer parametrelere eklenmiş olur. Sekmelerde hata
yoksa (Şekil 1.44) re-view and package ip sekmesinde re-package ip butonuna
tıklanarak ip tasarımı paketlenmiş olur.
Şekil 1.44
1.1.3. OLUŞTURULAN IP TASARIMININ PROJEYE EKLENMESİ
butonuna tıklanarak proje ayarları açılır. Açılan pencerede ip sekmesi seçilir ve
simgesine tıklanarak eklenmek istenen ip projesinden ip_repo klasörü seçilip projeye
eklenir. Klasör eklendikten sonra açılan pencerede ok butonuna basılır ve proje ayarları
penceresinde apply butonuna basılarak ip tasarımı projeye eklenmiş olur.
Şekil 1.45 Şekil 1.46
1.2 SDK, C DİLİNDE PROGRAMLAMA
1.2.1 KÜTÜPHANELER
Sıkça kullanılan kütüphaneler ve işlevleri aşağıda listelenmiştir:
xparameters.h İşlemci ile ilgili paramatrelerin bulunduğu kütüphanedir.
xgpio.h Gpio ip’lerinin kodlanması için gerekli kütüphanedir.
xil_printf.h Terminale karakter dizisi veya bir değer bastırmak için kullanılan
xil_printf fonksiyonunun kütüphanesidir.
xtmrctr.h Axi timerın fonksiyonelliğini sağlayan kütüphanedir.
xscugic.h Kesme uygulaması için gerekli kütüphanedir.
xscutimer.h SCU(snoop control unit) timerın fonksiyonelliğini sağlayan
kütüphanedir.
1.2.2 KODA TİMERI DAHİL ETME
Axi timer aşağıda belirtilen fonksiyonlar ile aşağıda verilen kodlarda gösterildiği gibi
koda dahil edilir.
1. XTmrCtr_Initialize()
2. XTmrCtr_SetResetValue()
3. XTmrCtr_SetOptions()
4. XTmrCtr_SetHandler() //kesme uygulaması için kullanılır.
//----------------------------------------------------
// SETUP THE TIMER
//----------------------------------------------------
status = XTmrCtr_Initialize(&TMRInst0, TMR_0_DEVICE_ID);
if(status != XST_SUCCESS)
return XST_FAILURE;
XTmrCtr_SetHandler(&TMRInst0, TMR_Intr_Handler, &TMRInst0);
XTmrCtr_SetResetValue(&TMRInst0, 0, TMR_LOAD_0);
XTmrCtr_SetOptions(&TMRInst0, 0, XTC_INT_MODE_OPTION |
XTC_AUTO_RELOAD_OPTION);
XTmrCtr_Start(&TMRInst0, 0);
//----------------------------------------------------
// SETUP THE COUNTER TIMER
//----------------------------------------------------
status = XTmrCtr_Initialize(&TMRInst1, TMR_1_DEVICE_ID);
if(status != XST_SUCCESS)
return XST_FAILURE;
XTmrCtr_SetResetValue(&TMRInst1, 1, TMR_LOAD_1);
XTmrCtr_SetOptions(&TMRInst1, 1, XTC_DOWN_COUNT_OPTION |
XTC_AUTO_RELOAD_OPTION);
1.2.3 KESME UYGULAMASI
Kartın kesme uygulamalarına hazır hale gelmesi için port aktifleştirme işlemi Vivado
tarafında, kod fonksiyonelliği SDK tarafında yapılır. Kesme uygulaması konu başlığı
altında gpio(buton) kesmesinin ve timer kesmesinin kod fonksiyonelliği anlatılmaktadır.
BUTON KESMESİ [1]
Kesme uygulamasına başlamadan önce,
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define GPIO_INTERRUPT_ID XPS_GPIO_INT_ID
static XScuGic Intc; // Interrupt Controller Driver
static XGpioPs Gpio; //GPIO Device
tanımlamaları yapılır.
xil_exception.h ve xsucig.h kütüphaneleri koda eklenir. Kesme uygulamasını kesme
kontrolörüne (GIC) bağlamak ve kesme izinlerini vermek için,
//GIC config
XScuGic_Config *IntcConfig;
Xil_ExceptionInit();
//initialize the GIC
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
//connect to the hardware
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(
Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
yazılır.
xgpiops.h kütüphanesi eklenir ve,
void XGpioPs_IntrEnable(XGpioPs *InstancePtr, u8 Bank, u32 Mask);
void XGpioPs_IntrEnablePin(XGpioPs *InstancePtr, int Pin);
fonksiyonları tanımlanır.
Kesme servis rutininin sağlanabilmesi için,
XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
fonksiyonu tanımlanır.
Butona her basıldığında gerçekleştirilmek istenen fonksiyonellik,
static void IntrHandler(void *CallBackRef, int
Bank, u32 Status)
{
int delay;
XGpioPs *Gpioint = (XGpioPs *)
CallBackRef;
XGpioPs_IntrClearPin(Gpioint, pbsw);
printf(“****button pressed****\n\r”);
toggle = !toggle;
XGpioPs_WritePin(Gpioint, ledpin, toggle);
for( delay = 0; delay < LED_DELAY; delay++)
//wait
{}
}
kesme koduna gömülerek gerçekleştirilebilir.
TİMER KESMESİ [2]
Timer kütüphanesi koda dahil edilir (bu kesme örneğinde scutimer kullanılmıştır).
Timera yükleme değeri,
#define TIMER_LOAD_VALUE 0xFFFFFFFF
satırı ile tanımlanır.
Aşağıda verilen kod parçası yazılarak timer tanımlanır.
//timer initialisation
TMRConfigPtr = XScuTimer_LookupConfig
(TIMER_DEVICE_ID);
XScuTimer_CfgInitialize(&Timer,
TMRConfigPtr,TMRConfigPtr->BaseAddr);
//load the timer
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
Timer kesmesini aktifleştirip GIC’e bağlamak için,
//set up the timer interrupt
XScuGic_Connect(GicInstancePtr, TimerIntrId,
(Xil_ExceptionHandler)TimerIntrHandler,
(void *)TimerInstancePtr);
//enable the interrupt for the Timer at GIC
XScuGic_Enable(GicInstancePtr, TimerIntrId);
//enable interrupt on the timer
XScuTimer_EnableInterrupt(TimerInstancePtr);
yazılır. Timer kesmesi ile gerçekleştirilmek istenen fonksiyonellik,
static void TimerIntrHandler(void *CallBackRef)
{
XScuTimer *TimerInstancePtr =
(XScuTimer *) CallBackRef;
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
printf(“****Timer Event!!!!!!!!!!!!!****\n\r”);
kesme koduna gömülerek gerçekleştirilebilir.
1.3 BİR IP TASARIMINI PROGRAMDA KULLANMA
Ip tasarımları SDK tarafında,
IP_ADI_mWriteReg(BaseAdress, RegOffset)
IP_ADI_mReadReg(BaseAdress, RegOffset) fonksiyonları ile kullanılır.
Kullanılmak istenen ip nin adresine verilog programında adress editor
bölmesinden bakılıp (Şekil 1.47) kodda uygun fonksiyonlarda BaseAdress yazan
yerlere yazılır.
Şekil 1.47
2. HC-SR04, ULTRASONİK SENSÖR
2.1 ÇALIŞMA PRENSİBİ
Trigger(tetikleme) pininden en az 10us lik 5V pulse görderildiğinde sensör ultrasonik
verici (Tx) den 40KHz de 8 adet pulse gönderir ve ECHO pinini “H” ye çeker.
Gönderilen bu dalgalar Ultrasonik alıcı (Rx) sensöre ulaştığında ECHO pini “L”‘ye
düşürür. Ölçüm yaparken ECHO pinin “H” de kaldığı süre ölçülerek sesin havada
yayılma hızına göre hesap yapılarak mesafe bulunur. [3]
Şekil 2.1
2.2 IP TASARIMI
Ultrasonik sensörün ip tasarımında echo giriş, trigger çıkış olarak tanımlanır (Şekil 2.2).
Echo ve trigger portlarının birbirlerinin değerlerini etkilememesi için ikisi için de ayrı
birer register kullanılır. Bu projede kullanılan ip tasarımında slv_reg0 trigger portu için,
slv_reg1 echo portu için kullanılmıştır. Echo giriş pininin değerini okuyabilmek için ip
kodunda slv_reg1 yerine echo portu yazılır (Şekil 2.3).
// Users to add ports here
input echo,
output trigger,
// User ports ends
Şekil 2.2
assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;
always @(*)
begin
// Address decoding for reading registers
case ( axi_araddr[ADDR_LSB+OPT_MEM_ADDR_BITS:ADDR_LSB] )
2'h0 : reg_data_out <= slv_reg0;
2'h1 : reg_data_out <= echo;
2'h2 : reg_data_out <= slv_reg2;
2'h3 : reg_data_out <= slv_reg3;
default : reg_data_out <= 0;
endcase
end
Şekil 2.3
Trigger portuna SDK tarafında slv_reg0’a 1 değeri yazıldığında 1, 0 yazıldığında 0
değerlerinin yazılabilmesi fonksiyonelliğini sağlayacak kod parçası Şekil 2.5’te
verilmiştir. Şekil 2.4’te trigger fonksiyonelliğini sağlayan sonic fonksiyonun ip
tasarımında nasıl çağrıldığı gösterilmektedir.
// Add user logic here
sonic sonic(S_AXI_ACLK, S_AXI_ARESETN, axi_awaddr, slv_reg_wren,
slv_reg0, trigger);
// User logic ends
Şekil 2.4
module sonic(input S_AXI_ACLK,
input S_AXI_ARESETN,
input [3:0] axi_awaddr,
input slv_reg_wren,
input [31:0] slv_reg0,
output reg trigger);
always @(posedge S_AXI_ACLK )
begin
if ( S_AXI_ARESETN == 1'b0 )
trigger = 1'b0; //trigger
else if(slv_reg_wren && (axi_awaddr == 4'h0))
begin
if(slv_reg0 == 32'h1)
begin
trigger = 1'b1;
end
else if(slv_reg0 == 32'h0)
begin
trigger = 1'b0;
end
end
end
endmodule
Şekil 2.5
Şekil 2.6’da sonic fonksiyonunun RTL şeması, Şekil 2.7’de sonic fonksiyonunun
benzetim çıktısı verilmiştir.
Şekil 2.6
Şekil 2.7
Şekil 2.8’de ip tasarımının paket görüntüsü verilmiştir.
Şekil 2.8
2.3 C DİLİNDE FONKSİYONELLİK
HC-SR04 ultrasonik sensör işlevini ses dalgası yayarak gerçekleştirir. Sesin havada
yayılma hızı 340m/sn’dir. O halde 1us de ne kadar yol kat edeceği hesaplanırsa;
mesafe=340000mm/1000000us olur. Sinyal karşısındaki engele ulaşıp dönene kadar iki
birim zaman geçtiği için formül 2’ye bölünür. Mesafe(mm)=(süre(us)*34)/200 olarak
bulunur. Mesafe(cm)=(süre(us)*0.017) olur.
Bu formülün C dilinde fonksiyonelliği Şekil 2.9‘da verilmiştir.
int read_distance_sensor_cm()
{
int echo, echo_pulse_duration, i, j, k, toplam = 0;
for(i=0; i<=63; i++){
SONIC_mWriteReg(0x43C00000, 0, BIT0);
for(j=0 ; j<=20; j++); //20us delay for trigger pulse
SONIC_mWriteReg(0x43C00000, 0, 0);
echo = SONIC_mReadReg(0x43C00000, 4);
while ((echo & BIT0) == 0)
echo = SONIC_mReadReg(0x43C00000, 4);
XTmrCtr_Reset(&TMRInst1, 1); XTmrCtr_Start(&TMRInst1, 1);
echo = SONIC_mReadReg(0x43C00000, 4);
while ((echo & BIT0) == 1)
echo = SONIC_mReadReg(0x43C00000, 4);
XTmrCtr_Stop(&TMRInst1, 1);
echo_pulse_duration = XTmrCtr_ReadReg(0x42810000, 1, 8);
toplam = toplam+(int)(0.017*(TMR_LOAD_1-echo_pulse_duration ));
for(k=0; k<=49999; k++); }
return (toplam>>7);
}
Şekil 2.9
3. SSD
3.1 ÇALIŞMA PRENSİBİ
Digilent’in pmod SSD modülü ortak katot yapısıma sahiptir. Yani pinden 1 değeri
gönderildiğinde ilgili pinin ledi yanar. Pmod SSD modülü ile ilgili devre şeması ve pin
atamaları aşağıda verilmiştir.
Şekil 3.1.a Şekil 3.1.b
Şekil 3.1.c
3.2 IP TASARIMI
SSD ip tasarımında 8 bit ssd çıkış portu tanımlanır (Şekil 3.2).
// Users to add ports here
output wire [7:0] SSD,
// User ports ends
Şekil 3.2
Ip nin registerlarında bir oynama yapılmaz, ssd fonksiyonunda iki gösterge arasında
geçişi zamanlamak için 20 bitlik bir counter ve ssd lere hangi değerlerin atanacağının
belirlemek için 7 bitlik data0_ssd_data ve data1_ssd_data reg tipinde tanımlanır (Şekil
3.3).
reg [19:0] counter; //Counter for 20ms delay
reg [6:0] data0_ssd_data;
reg [6:0] data1_ssd_data;
Şekil 3.3
SDK tarafından yazılan değere göre ssd lere hangi değerlerin yazılacağı
fonksiyonelliğini sağlayan kod parçası aşağıda verilmiştir.
always @(posedge S_AXI_ACLK) begin
if (S_AXI_ARESETN == 1'b0) begin
SSD <= 8'd0;
counter <= 20'd0;
end else if(slv_reg_wren && (axi_awaddr == 3'h0))
begin
case(slv_reg0[3:0])
//A high on the anode will lit the related segment on the PMOD SSD
//module
//The order of the bits from MSB to LSB are G F E D C B A
// GFEDCBA
4'h0: data0_ssd_data <= 7'b0111111;
4'h1: data0_ssd_data <= 7'b0000110;
4'h2: data0_ssd_data <= 7'b1011011;
4'h3: data0_ssd_data <= 7'b1001111;
4'h4: data0_ssd_data <= 7'b1100110;
4'h5: data0_ssd_data <= 7'b1101101;
4'h6: data0_ssd_data <= 7'b1111101;
4'h7: data0_ssd_data <= 7'b0000111;
4'h8: data0_ssd_data <= 7'b1111111;
4'h9: data0_ssd_data <= 7'b1101111;
4'hA: data0_ssd_data <= 7'b1110111;
4'hB: data0_ssd_data <= 7'b1111100;
4'hC: data0_ssd_data <= 7'b0111001;
4'hD: data0_ssd_data <= 7'b1011110;
4'hE: data0_ssd_data <= 7'b1111001;
4'hF: data0_ssd_data <= 7'b1110001;
default: data0_ssd_data <= 7'b0000000;
endcase
case(slv_reg0[7:4])
//A high on the anode will lit the related segment on the PMOD SSD
//module
//The order of the bits from MSB to LSB are G F E D C B A
// GFEDCBA
4'h0: data1_ssd_data <= 7'b0111111;
4'h1: data1_ssd_data <= 7'b0000110;
4'h2: data1_ssd_data <= 7'b1011011;
4'h3: data1_ssd_data <= 7'b1001111;
4'h4: data1_ssd_data <= 7'b1100110;
4'h5: data1_ssd_data <= 7'b1101101;
4'h6: data1_ssd_data <= 7'b1111101;
4'h7: data1_ssd_data <= 7'b0000111;
4'h8: data1_ssd_data <= 7'b1111111;
4'h9: data1_ssd_data <= 7'b1101111;
4'hA: data1_ssd_data <= 7'b1110111;
4'hB: data1_ssd_data <= 7'b1111100;
4'hC: data1_ssd_data <= 7'b0111001;
4'hD: data1_ssd_data <= 7'b1011110;
4'hE: data1_ssd_data <= 7'b1111001;
4'hF: data1_ssd_data <= 7'b1110001;
default: data1_ssd_data <= 7'b0000000;
endcase
Şekil 3.4’te ssd ler arasında geçişi zamanlayan counter değişkeninin fonksiyonelliğini
sağlayan kod parçası verilmiştir.
if(counter == {20{1'b1}}) begin
counter <= 20'd0;
end else begin
counter <= counter + 1'b1;
end
if(!counter[9]) begin
SSD <= {1'b0,data0_ssd_data};
end else begin
SSD <= {1'b1,data1_ssd_data};
end
end
end
endmodule
Şekil 3.4
Şekil 3.5’te ip tasarımının paket görüntüsü verilmiştir.
Şekil 3.5
3.3 C DİLİNDE FONKSİYONELLİK
SSD ip tasarımının fonksiyonelliği Şekil 3.6’da verilen C kodu ile gerçeklenebilir.
#include "xparameters.h"
#include "xgpio.h"
#include "ssd_ip.h"
int main (void)
{
XGpio push;
int i, SSD, SSDReg;
xil_printf("-- Start of the Program --\r\n");
XGpio_Initialize(&push, 0x41200000);
XGpio_SetDataDirection(&push, 1, 0xFFFFFFFF);
while (1)
{
SSD = XGpio_DiscreteRead(&push,1);
xil_printf("Zybo DIP Switch Status %x\r\n", push);
SSD_IP_mWriteReg(0x43C00000, 0, SSD);
SSDReg = SSD_IP_mReadReg(0x43C00000, 0);
xil_printf("PMOD SSD Status %x\r\n", SSDReg);
for (i=0; i<99999999; i++);
}
}
Şekil 3.6
4. UYGULAMA
4.1 VİVADO, BLOK TASARIM
Bu projenin blok tasarımında,
ZYNQ işlemci sistemi
Buton gpio ip modülü
Led gpio ip modülü
Axi timer (periyot için)
Axi timer (mesafe hesaplanırken gerekli olan sayıcı için)
Sonic ip modülü
SSD ip modülü
Concat modülü (birden fazla kesmeyi işlemciye sürebilmek için) kullanılmıştır.
Şekil 4.1 Blok tasarım
4.2 SDK, C DİLİNDE PROGRAMLAMA
Aşağıdaki şekillerde biri periyot timerı diğeri sayıcı olarak kullanılan 2 timer ile
kodlanmış, her buton kesmesi ile ilgili ledi yakıp mesafe değerini ssd modülüne
yazdıran ve mesafeyi hesaplayan fonksiyonu içeren C kodu verilmiştir.
#include "xparameters.h"
#include "xgpio.h"
#include "xtmrctr.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "sonic.h"
#include "ssd_ip.h"
// Parameter definitions
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define TMR_0_DEVICE_ID 0
#define TMR_1_DEVICE_ID 1
#define BTNS_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define LEDS_DEVICE_ID XPAR_AXI_GPIO_1_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID
XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR
#define INTC_TMR_INTERRUPT_ID XPAR_FABRIC_AXI_TIMER_0_INTERRUPT_INTR
#define BTN_INT XGPIO_IR_CH1_MASK
#define TMR_LOAD_1 50000
#define TMR_LOAD_0 0xF8000000
#define BIT0 0x01
XGpio LEDInst, BTNInst;
XScuGic INTCInst;
XTmrCtr TMRInst0;
XTmrCtr TMRInst1;
static int ssd_value;
static int btn_value;
static int mesafe;
int i;
//----------------------------------------------------
// PROTOTYPE FUNCTIONS
//----------------------------------------------------
static void BTN_Intr_Handler(void *InstancePtr);
static void TMR_Intr_Handler(void *InstancePtr);
static int InterruptSystemSetup(XScuGic *XScuGicInstancePtr);
static int IntcInitFunction(u16 DeviceId, XTmrCtr *TmrInstancePtr,
XGpio
*GpioInstancePtr);
static int read_distance_sensor_cm();
void BTN_Intr_Handler(void *InstancePtr)
{
// Disable GPIO interrupts
XGpio_InterruptDisable(&BTNInst, BTN_INT);
// Ignore additional button presses
if ((XGpio_InterruptGetStatus(&BTNInst) & BTN_INT) !=BTN_INT) {
return;
}
btn_value = XGpio_DiscreteRead(&BTNInst, 1);
XGpio_DiscreteWrite(&LEDInst, 1, btn_value);
SSD_IP_mWriteReg(0x43C10000, 0, mesafe);
ssd_value = SSD_IP_mReadReg(0x43C10000, 0);
xil_printf("SSD = %x\r\n",ssd_value);
(void) XGpio_InterruptClear(&BTNInst, BTN_INT);
// Enable GPIO interrupts
XGpio_InterruptEnable(&BTNInst, BTN_INT);
}
void TMR_Intr_Handler(void *InstancePtr)
{
if (XTmrCtr_IsExpired(&TMRInst0, 0)){
XTmrCtr_Stop(&TMRInst0, 0);
XTmrCtr_Reset(&TMRInst0, 0);
XTmrCtr_Start(&TMRInst0, 0); }
}
int main (void)
{
int status;
// Initialise LEDs
status = XGpio_Initialize(&LEDInst, LEDS_DEVICE_ID);
if(status != XST_SUCCESS) return XST_FAILURE;
// Initialize Push Buttons
status = XGpio_Initialize(&BTNInst, BTNS_DEVICE_ID);
if(status != XST_SUCCESS) return XST_FAILURE;
// Set LEDs direction to outputs
XGpio_SetDataDirection(&LEDInst, 1, 0x00);
// Set all buttons direction to inputs
XGpio_SetDataDirection(&BTNInst, 1, 0xFF);
//----------------------------------------------------
// SETUP THE TIMER
//----------------------------------------------------
status = XTmrCtr_Initialize(&TMRInst0, TMR_0_DEVICE_ID);
if(status != XST_SUCCESS)
return XST_FAILURE;
XTmrCtr_SetHandler(&TMRInst0, TMR_Intr_Handler, &TMRInst0);
XTmrCtr_SetResetValue(&TMRInst0, 0, TMR_LOAD_0);
XTmrCtr_SetOptions(&TMRInst0, 0, XTC_INT_MODE_OPTION |
XTC_AUTO_RELOAD_OPTION);
//----------------------------------------------------
// SETUP THE COUNTER TIMER
//----------------------------------------------------
status = XTmrCtr_Initialize(&TMRInst1, TMR_1_DEVICE_ID);
if(status != XST_SUCCESS)
return XST_FAILURE;
XTmrCtr_SetResetValue(&TMRInst1, 1, TMR_LOAD_1);
XTmrCtr_SetOptions(&TMRInst1, 1, XTC_DOWN_COUNT_OPTION |
XTC_AUTO_RELOAD_OPTION);
// Initialize interrupt controller
status = IntcInitFunction(INTC_DEVICE_ID, &TMRInst0, &BTNInst);
if(status != XST_SUCCESS) return XST_FAILURE;
XTmrCtr_Start(&TMRInst0, 0);
xil_printf("-- Start of the Program --\r\n");
while(1){
mesafe = read_distance_sensor_cm();
xil_printf("mesafe(cm) = %x \r\n", mesafe);}
return 0;
}
//sensor fonksiyonu
int read_distance_sensor_cm()
{
int echo, echo_pulse_duration, i, j, k, toplam = 0;
for(i=0; i<=63; i++){
SONIC_mWriteReg(0x43C00000, 0, BIT0);
for(j=0 ; j<=20; j++); //20us delay for trigger pulse
SONIC_mWriteReg(0x43C00000, 0, 0);
echo = SONIC_mReadReg(0x43C00000, 4);
while ((echo & BIT0) == 0)
echo = SONIC_mReadReg(0x43C00000, 4);
XTmrCtr_Reset(&TMRInst1, 1); XTmrCtr_Start(&TMRInst1, 1);
echo = SONIC_mReadReg(0x43C00000, 4);
while ((echo & BIT0) == 1)
echo = SONIC_mReadReg(0x43C00000, 4);
XTmrCtr_Stop(&TMRInst1, 1);
echo_pulse_duration = XTmrCtr_ReadReg(0x42810000, 1, 8);
toplam = toplam+(int)(0.017*(TMR_LOAD_1-echo_pulse_duration ));
for(k=0; k<=49999; k++); }
return (toplam>>7);
}
int IntcInitFunction(u16 DeviceId, XTmrCtr *TmrInstancePtr,
XGpio *GpioInstancePtr)
{
XScuGic_Config *IntcConfig;
int status;
// Interrupt controller initialization
IntcConfig = XScuGic_LookupConfig(DeviceId);
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig,
IntcConfig->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// Call to interrupt setup
status = InterruptSystemSetup(&INTCInst);
if(status != XST_SUCCESS) return XST_FAILURE;
// Connect GPIO interrupt to handler
status = XScuGic_Connect(&INTCInst, INTC_GPIO_INTERRUPT_ID,
(Xil_ExceptionHandler) BTN_Intr_Handler,
(void *)GpioInstancePtr);
if(status != XST_SUCCESS) return XST_FAILURE;
// Connect timer interrupt to handler
status = XScuGic_Connect(&INTCInst, INTC_TMR_INTERRUPT_ID,
(Xil_ExceptionHandler)TMR_Intr_Handler,
(void *)TmrInstancePtr);
if(status != XST_SUCCESS) return XST_FAILURE;
// Enable GPIO interrupts interrupt
XGpio_InterruptEnable(GpioInstancePtr, 1);
XGpio_InterruptGlobalEnable(GpioInstancePtr);
// Enable GPIO and timer interrupts in the controller
XScuGic_Enable(&INTCInst, INTC_GPIO_INTERRUPT_ID);
XScuGic_Enable(&INTCInst, INTC_TMR_INTERRUPT_ID);
return XST_SUCCESS;
}
int InterruptSystemSetup(XScuGic *XScuGicInstancePtr)
{
// Enable interrupt
XGpio_InterruptEnable(&BTNInst, BTN_INT);
XGpio_InterruptGlobalEnable(&BTNInst);
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
XScuGicInstancePtr);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
4.3 UYGULAMA ÇIKTILARI
Bu projenin uygulama çıktıları aşağıdaki görsellerde verilmiştir.
Şekil 4.2 Şekil 4.3
Şekil 4.4 Şekil 4.5
Şekil 4.6 Şekil 4.7
5. KAYNAKÇA
[1] , [2] How to Use Interrupts on the Zynq SoC, Adam P. Taylor
[3] https://elektrokod.wordpress.com/2014/01/17/ultrasonik-mesafe-sensoru-uygulamasi