Operácie

Schaeffler FPGA: Rozdiel medzi revíziami

Zo stránky SensorWiki

Kocur (diskusia | príspevky)
Bez shrnutí editace
Kocur (diskusia | príspevky)
Bez shrnutí editace
 
(22 medziľahlých úprav od rovnakého používateľa nie je zobrazených.)
Riadok 2: Riadok 2:
= Modelovanie vnoreného systému na čipe FPGA - praktická časť =
= Modelovanie vnoreného systému na čipe FPGA - praktická časť =


 
<br><br>


Cieľom praktického cvičenia je navrhnúť hardvér a softvér pre vnorený mikropočítačový systém, využívajúci soft-core procesor MicroBlaze. Úlohou systému bude ovládať rýchlosť jednosmerného motora pomocou pulznej šírkovej modulácie (PWM) v otvorenej slučke. Procesor bude zaznamenávať signály z enkodéra motora a na základe týchto signálov vyhodnocovať uhlovú rýchlosť motora v jednotkách otáčok za minútu (ot/min).  
Cieľom praktického cvičenia je navrhnúť hardvér a softvér pre vnorený mikropočítačový systém, využívajúci soft-core procesor MicroBlaze. Úlohou systému bude ovládať rýchlosť jednosmerného motora pomocou pulznej šírkovej modulácie (PWM) v otvorenej slučke. Procesor bude zaznamenávať signály z enkodéra motora a na základe týchto signálov vyhodnocovať uhlovú rýchlosť motora v jednotkách otáčok za minútu (ot/min).  
Riadok 14: Riadok 14:
# Overte ovládanie a snímanie uhlovej rýchlosti DC motora pomocou experimentu.
# Overte ovládanie a snímanie uhlovej rýchlosti DC motora pomocou experimentu.


<br><br>


== 1. Naštudujte si funkciu H-mostíka a enkodéra DC motora ==


== Naštudujte si funkciu H-mostíka a enkodéra DC motora ==
<br><br>
 
 


=== H-mostík ===  
=== H-mostík ===  
Riadok 35: Riadok 35:
Enkodér motora je senzor, ktorý slúži na zisťovanie otáčok a polohy rotujúceho prvku motora. Enkodéry môžu byť absolútne alebo inkrementálne. Inkrementálne enkodéry merajú zmenu polohy rotujúceho prvku vzhľadom k svojej východiskovej polohy. Absolútne enkodéry dokážu zistiť aktuálnu polohu rotujúceho prvku bez ohľadu na jeho predchádzajúce polohy.  
Enkodér motora je senzor, ktorý slúži na zisťovanie otáčok a polohy rotujúceho prvku motora. Enkodéry môžu byť absolútne alebo inkrementálne. Inkrementálne enkodéry merajú zmenu polohy rotujúceho prvku vzhľadom k svojej východiskovej polohy. Absolútne enkodéry dokážu zistiť aktuálnu polohu rotujúceho prvku bez ohľadu na jeho predchádzajúce polohy.  


V našej aplikácií budeme využívať magnetický inkrementálny enkodér s dvoma halovými sondami. Zmenu magnetického poľa vytvára kotúč s troma magnetmi. Halova sonda tak zaznamená tri nábežné hrany na otáčku motora. Pri výpočte treba započítať aj prevodový pomer planétovej prevodovky motora, ktorý je 1:19. Hriadeľ motora za prevodovkou otáča 19x pomalšie ako motor.
V našej aplikácií budeme využívať magnetický inkrementálny enkodér s dvoma halovými sondami. Zmenu magnetického poľa vytvára kotúč s troma magnetmi. Halova sonda tak zaznamená tri nábežné hrany na otáčku motora. Pri výpočte treba započítať aj prevodový pomer planétovej prevodovky motora, ktorý je 1:19. Hriadeľ motora za prevodovkou otáča 19x pomalšie ako motor. Viac informácií o motore s enkodérom nájdete na [https://digilent.com/shop/dc-motor-gearbox-1-19-gear-ratio-custom-12v-motor-designed-for-digilent-robot-kits/ stránke výrobcu]
 
<br><br>


== 1. Navrhnite hardvér procesora s perifériami. ==
== 2. Navrhnite hardvér procesora s perifériami. ==




Riadok 44: Riadok 46:
* Vytvorte základnú štruktúru procesora microblaze s prerušeniami, ktorý pripojte na hodinový a reset signál. Použite pomocný nástroj Run Block Automation a nakonfigurujte procesor   
* Vytvorte základnú štruktúru procesora microblaze s prerušeniami, ktorý pripojte na hodinový a reset signál. Použite pomocný nástroj Run Block Automation a nakonfigurujte procesor   


[[Súbor:Vivado4.png|center|600px]]
<center>[[Súbor:Vivado4.png|center|600px]]</center>


<br><br>
<br><br>




[[Súbor:Vivado5.png|center|800px]]
<center>[[Súbor:Vivado5.png|center|800px]]</center>


<br><br>
<br><br>
Riadok 55: Riadok 57:
* Vytvorte hardvér pre UART komunikáciu, pripojte aj prerušenie na vstupný blok radiča prerušení. Výsledné zapojenie porovnajte s referenčným návrhom.  
* Vytvorte hardvér pre UART komunikáciu, pripojte aj prerušenie na vstupný blok radiča prerušení. Výsledné zapojenie porovnajte s referenčným návrhom.  


[[Súbor:Vivado8.png|center|800px]]
<center>[[Súbor:Vivado8.png]]</center>
 
 
* Vložte do návrhu časovač AXI Timer, pripojte na vstupy časovača capturetrig0 a capturetrig1 výstupy z enkodéra JA2 a JA3, ktoré vytvoríte pravým kliknutím na vstupný port IP jadra a následne zvolíte Make External.  Výstup IP bloku interrupt pripojte na radič prerušení.
 
<center>[[Súbor:Vivado9.png|center]]</center>
 
* Vložte do návrhu ďalší časovač, ktorý bude tentokrát slúžiť na generovanie PWM signálu, na výstup pwm0 pripojte RGB1_Green a port JA0. Na port JA1 pripojíme konštantu 0 tak, aby sa motor mohol otáčať len jedným smerom.  V tomto prípade interrupt pripájať nie je potrebné.
 
<center>[[Súbor:Vivado10.png]]</center>
 
* Otvorte IP Catalog pridajte nový repozitár vlastných IP blokov, ktorý sa nachádza v priečinku školských počítačov C:\vmsm\ip_repo. Importujte do návrhu IP blok s názvom Seven_segment_display_driver.  Vytvorte externé porty an a seg a pripojte ich k IP bloku. Následne pripojte IP blok na AXI zbernicu.
 
<center>[[Súbor:Vivado11.png]]</center>
 
* Importujte do hardvérového návrhu obsluhu tlačidiel použitím AXI GPIO bloku
 
<center>[[Súbor:Vivado12.png]]</center>
 
* Importujte do hardvérového návrhu  timer, pomocou ktorého budeme vyvolávať pravidelne sa opakujúce udalosti, napr. diskrétny PID regulátor
 
<center>[[Súbor:Vivado13.png]]</center>
 
* Overte si celkový dizajn s obrázkom. Vygenerujte HDL wrapper a bitstream, ktorý následne vyexportujte pre softvér Xilinx SDK.
 
[[Súbor:Vivado14.png]]
 
== 3. Navrhnite a otestujte softvér pre procesor na obsluhu PWM a enkodéra motora  ==
 
* V programe vytvorte nový aplikačný projekt na základe šablóny Hello World a otestujte jeho funkčnosť. Súbor helloworld.c premenujte na main.c.
* Importujte do projektu súbory periph.h a periph.c a analyzujte ich obsah. Súbory sa nachádzajú v priečinku C:\skolenie\
* Pridajte do hlavného súboru main.c,
** knižnice, direktívy a globálne premenné
** Prototypy pre funkcie na obsluhu prerušení
 
<br><br>
<source lang="c">
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xtmrctr.h"
#include "xintc.h"
#include "Seven_segment_display_driver.h"
#include "xgpio.h"
#include "periph.h"
 
#define PWM_PERIOD              500000    // PWM perioda 500 us
 
#define ENCODER_DEVICE_ID XPAR_TMRCTR_0_DEVICE_ID // ID  encoder
#define PWM_DEVICE_ID      XPAR_TMRCTR_1_DEVICE_ID // ID pre pwm
#define TIMER_DEVICE_ID XPAR_TMRCTR_2_DEVICE_ID // ID pre timer
#define GPIO_BUTTONS_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID //ID gpio periferie
#define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID // ID radica preruseni
 
#define ENCODER_INTERRUPT_ID XPAR_INTC_0_TMRCTR_0_VEC_ID  //ID prerusenia encodera
#define GPIO_INTERRUPT_ID XPAR_INTC_0_GPIO_0_VEC_ID //ID prerisenia gpio
#define TIMER_INTERRUPT_ID XPAR_INTC_0_TMRCTR_2_VEC_ID //ID prerusenia timer
#define COUNTER_INIT_VAL 0
#define DISP_DRV_BASE_ADDR XPAR_SEVEN_SEGMENT_DISPLA_0_S00_AXI_BASEADDR
 
#define TIMER_INTERVAL 10000000 // perioda
 
XTmrCtr TimerEncoder; /* 0 The instance of the Timer for Encoder */
XTmrCtr TimerPWM; /* 1 The instance of the Timer for PWM */
XTmrCtr TimerController; /* 2 The instance of the Timer for periodic events */
 
XIntc InterruptController; /* The instance of the Interrupt Controller */
 
XGpio Buttons;
 
int DutyCycle;
u32 Period;
u32 HighTime;
u8 Div;
volatile float rpm;  // Speed of DC Motor
volatile int time; // time period of last impulse from motor encoder probe A
static u16 GlobalIntrMask; // GPIO channel mask that is needed by the Interrupt Handler
 
u32 ControllerPeriod;
 
u32 soft_counter;
 
 
static void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber);
static void GpioHandler(void *CallbackRef);
static void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber);
 
</source>
 
<br><br>
=== Obsluha pre tlačidlá ===
Pre inicializáciu obsluhy tlačidiel je potrebné vo funkcii main() zavolať inicializačné funkcie
<br><br>
<source lang="c">
        init_gpio(&Buttons, GPIO_BUTTONS_DEVICE_ID, 0x1, 0x1);
init_gpio_intr(&Buttons, &InterruptController,
(Xil_ExceptionHandler) GpioHandler, GPIO_BUTTONS_DEVICE_ID,
INTC_DEVICE_ID, GPIO_INTERRUPT_ID, &GlobalIntrMask);
</source>
<br><br>
V inicializačnej funckií sme nastavili, že po stlačení tlačidla sa  vyvolá prerušenie procesora ktoré následné spustí funkciu void GpioHandler(void *CallbackRef). Základnú štruktúru takejto funkcie môžeme vidieť v nasledujúcej ukážke.
<br><br>
<source lang="c">
void GpioHandler(void *CallbackRef) {
XGpio *GpioPtr = (XGpio *) CallbackRef;
int val = XGpio_DiscreteRead(&Buttons, 1);


xil_printf("button event\n");


Takto sa sem píše obyčajný text.  
/* Clear the Interrupt */
XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
}
</source>
<br><br>
'''Úloha:''' Zistite aké hodnoty sa uložia do premennej val po stačení jednotlivých tlačidiel.
<br><br>
=== Obsluha časovača pre PWM ===


Takto '''tučný''', takto ''kurzíva''.
Inicializáciu časovača pre PWM realizujeme pomocou funkcie
<br><br>
<source lang="c">


Linky:
Period = PWM_PERIOD;
* Interné: [[Schaeffler Modul 3A]]
timer_pwm_init(&TimerPWM, PWM_DEVICE_ID, Period);


</source>


* Externé: [https://senzor.robotika.sk/mmp/nRF51_RM_v3.0.pdf nRF51 Series Reference Manual] (Version 3.0)
<br><br>
Samotná zmena pwm signálu je realizovaná pomocou sekvencie funkcií:
<br><br>
<source lang="c">
/* DisablePWM */
XTmrCtr_PwmDisable(&TimerPWM);
/* Confgure PWM */
HighTime = PWM_PERIOD * DivF;  // DivF is real (float) number from 0 to 1
DutyCycle = XTmrCtr_PwmConfigure(&TimerPWM, Period, HighTime);
/* Enable PWM */
XTmrCtr_PwmEnable(&TimerPWM);
</source>


<br><br>
'''Úloha:''' Upravte funkciu obsluhy tlačidiel tak, aby bola pomocou vybraných tlačidiel možná zmena Striedy PWM.  Zmenu plnenia PWM signálu môžete pozorovať na zelenej LED na vývojovej doske.


Obrázok vložíš takto:
<br><br>


  [[Súbor:FPGAchip.jpg|center|400px]]
=== Obsluha časovača pre enkodér motora ===


všimni si, že je nektívny a treba ho potom nahrať kliknutím na odkaz.
Časovač pre enkodér motora inicializujeme vo funkcií main() pomocou funkcie:


Takto sa vkladajú jednoduché zdrojáky:
<br><br>


<source lang="vhdl">
<source lang="c">
Q <= tmp;
timer_encoder_init(&TimerEncoder, &InterruptController, TimerEncoderHandler,
QBAR <= not tmp;
ENCODER_DEVICE_ID, INTC_DEVICE_ID, ENCODER_INTERRUPT_ID,
COUNTER_INIT_VAL);
</source>
</source>


Alebo aj takto cez záložky ak to má viac súborov:
<br><br>
Obsluha prerušenia sa vyvolá po každej nábežnej hrane senzora A. Do premennej time sa uloží hodnota počítadla, ktorý počítal hodinové impulzy od poslednej nábežnej hrany sondy A. Na základe počtu impulzov hodinového signálu sa dá vyhodnotiť čas, ktorý medzi dvoma nábežnými hranami enkodéra uplynul. Prepočtom je potom možné vyjadriť aj uhlovú rýchlosť motora v otáčkach za minútu.
 
<br><br>
 
<source lang="c">
void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber) {
XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
time = XTmrCtr_GetCaptureValue(InstancePtr, TmrCtrNumber);
XTmrCtr_SetResetValue(InstancePtr, TmrCtrNumber, RESET_VALUE);
XTmrCtr_Reset(InstancePtr, TmrCtrNumber);
 
rpm = 105263179 / time;
}
</source>
<br><br>
 
'''Úloha:''' Vytvorte vo funkcií main() nekonečnú slučku v ktorej budete zobrazovať na 7 segmentový displej rýchlosť motora. Otestujte funkčnosť displeja.
 
<br><br>
 
<source lang="c">
usleep(5000000);
int irpm = (int) rpm;
SEVEN_SEGMENT_DISPLAY_DRIVER_mWriteReg(DISP_DRV_BASE_ADDR, 0x0, irpm);
</source>
 
<br><br>
 
=== Obsluha pre časovač pravidelných udalostí ===
 
Inicializácia časovača pravidelných udalostí vo funckií main():
 
<source lang="c">
ControllerPeriod = TIMER_INTERVAL;
timer_contoller_init(&TimerController, &InterruptController,
TimerControllerHandler,
TIMER_DEVICE_ID, INTC_DEVICE_ID, TIMER_INTERRUPT_ID,
COUNTER_INIT_VAL, ControllerPeriod);
</source>
 
<br><br>
Funkcia prerušenia časovača:
<br><br>
 
<source lang="c">
void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber) {
XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
 
int irpm = (int) rpm;
}
</source>
 
<br><br>
 
'''Úloha:''' Upravte zdrojový kód tak, aby sa zobrazenie údajov na displej ako aj cez terminál realizovalo v pravidelných 1 sekundových  intervaloch pomocou funkcie prerušenia časovača Timer_2.
<br><br> 
'''Úloha:''' Naprogramujte softvérový counter, ktorý bude počítať všetky nábežné hrany z halovej sondy A počas jednej sekundy. Na základe počtu impulzov vypočítajte uhlovú rýchlosť motora v ot/min (rpm). Porovnajte oba spôsoby vyhodnotenia uhlovej rýchlosti pomocou experimentu.
 
'''Úloha:''' Upravte postupne intervaly prerušenia časovača na 100ms a 10ms. Experiment opakujte a výsledky pre jednotlivé vzájomne porovnajte .
 
 
 
<br><br>
 
 
 
 
 
 
 
 
Kompletné definície funkcií pre obsluhu prerušení


<tabs>
<tabs>
<tab name="R-S Flip-Flop.vhdl"><source lang="vhdl" style="background: LightYellow;">
<tab name="Obsluha pre tlačidlá">
library ieee;
<source lang="c" style="background: LightYellow;">
use ieee. std_logic_1164.all;
void GpioHandler(void *CallbackRef) {
use ieee. std_logic_arith.all;
XGpio *GpioPtr = (XGpio *) CallbackRef;
use ieee. std_logic_unsigned.all;
int val = XGpio_DiscreteRead(&Buttons, 1);
//button is pressed
entity SR_FF is
if (val != 0x0) {
  PORT( S,R,CLOCK: in std_logic;
if ((val == 0x1) && (Div < 10)) {
  Q, QBAR: out std_logic);
Div++;
end SR_FF;
} else if ((val == 0x4) && (Div > 0)) {
Div--;
Architecture behavioral of SR_FF is
}
float DivF = (float) Div / 10;
HighTime = PWM_PERIOD * DivF;
if (Div > 9) {
HighTime = PWM_PERIOD - 10;
} else if (Div == 0) {
HighTime = PWM_PERIOD;
}


begin
//xil_printf("Duty is %d0 %% \n", Div);
PROCESS(CLOCK)
variable tmp: std_logic;
begin
  if(CLOCK='1' and CLOCK'EVENT) then
  if(S='0' and R='0')then
  tmp:=tmp;
  elsif(S='1' and R='1')then
  tmp:='Z';
  elsif(S='0' and R='1')then
  tmp:='0';
  else
  tmp:='1';
  end if;
  end if;


  Q <= tmp;
XTmrCtr_PwmDisable(&TimerPWM);
  QBAR <= not tmp;
DutyCycle = XTmrCtr_PwmConfigure(&TimerPWM, Period, HighTime);
/* Enable PWM */
XTmrCtr_PwmEnable(&TimerPWM);
}
/* Clear the Interrupt */
XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
}
</source></tab>
<tab name="Obsluha pre časovač enkodéra">
<source lang="c" style="background: LightBlue;">
void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber) {
XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
time = XTmrCtr_GetCaptureValue(InstancePtr, TmrCtrNumber);
XTmrCtr_SetResetValue(InstancePtr, TmrCtrNumber, RESET_VALUE);
XTmrCtr_Reset(InstancePtr, TmrCtrNumber);


end PROCESS;
rpm = 105263179 / time;
soft_counter++;
}
</source></tab>


end behavioral;
<tab name="Obsluha pre časovač pravidelných udalostí">
<source lang="c" style="background: LightBlue;">
void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber) {
XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;


int irpm = (int) rpm;
float crpm = ((soft_counter * 60 ) / (19 * 3))*(100000000/TIMER_INTERVAL);
int icrpm = (int) crpm;
xil_printf("%d,",Div*10);
xil_printf("%d,",irpm);
xil_printf("%d\n",icrpm);
soft_counter = 0;
SEVEN_SEGMENT_DISPLAY_DRIVER_mWriteReg(DISP_DRV_BASE_ADDR, 0x0, irpm); //
}
</source></tab>
</source></tab>
<tab name="program02.py"><source lang="python" style="background: LightBlue;">
</tabs>  
from microbit import *


uart.init(baudrate=115200, bits=8, parity=None, stop=1)
<br><br>
<!--
== Bonusová úloha: Realizácia diskrétneho PID regulátora ==
 
<tabs>
<tab name="Globálne premenné">
<source lang="c" style="background: LightBlue;">
int irpm_avg = 0;
int i = 0;
 
float uk = 0, ukm1 = 0;
float q0 = 0.01384;
float q1 = -0.01269;
int w = 0;
int ek = 0, ekm1 = 0;


while True:
    accX = accelerometer.get_x()
   
    uart.write('%d\r\n' % (accX))


    sleep(100)
</source></tab>
    display.set_pixel(1,1,5)
 
    sleep(100)
<tab name="Obsluha pre tlačidlá">
    display.set_pixel(1,1,0)
<source lang="c" style="background: LightBlue;">
void GpioHandler(void *CallbackRef) {
XGpio *GpioPtr = (XGpio *) CallbackRef;
int val = XGpio_DiscreteRead(&Buttons, 1);
//button is pressed
if (val != 0x0) {
if (val == 0x1) {
w = 450;
} else if (val == 0x4) {
w = 350;
}
} /* Clear the Interrupt */
XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
 
}
 
</source></tab>
 
<tab name="Obsluha pre časovač pravidelných udalostí">
<source lang="c" style="background: LightGreen;">
void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber) {
XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
 
int irpm = (int) rpm;
irpm_avg += irpm;
i++;
 
ek = w - irpm;
 
uk = ukm1 + q0 * ek + q1 * ekm1;
 
if (uk > 10)
uk = 10;
if (uk < 0)
uk = 0;
 
int i_uk = (int) uk;
int u_milis = (int) (uk - i_uk)*1000;
float DivF = uk / 10;
HighTime = PWM_PERIOD * DivF;
if (uk == 10) {
HighTime = PWM_PERIOD - 10;
} else if (uk == 0) {
HighTime = PWM_PERIOD;
}
 
XTmrCtr_PwmDisable(&TimerPWM);
DutyCycle = XTmrCtr_PwmConfigure(&TimerPWM, Period, HighTime);
/* Enable PWM */
XTmrCtr_PwmEnable(&TimerPWM);
 
if ((i % 50) == 0) {
//i = 0;
irpm_avg = (int) (irpm_avg / 50);
xil_printf("w=%d e=%d u=%d.%3d, y=%d \n",w,ek,i_uk,u_milis,irpm);
SEVEN_SEGMENT_DISPLAY_DRIVER_mWriteReg(DISP_DRV_BASE_ADDR, 0x0, irpm_avg); //
}
ekm1 = ek;
ukm1 = uk;
}
 
</source></tab>
</source></tab>
</tabs>
</tabs>
-->


<BR><BR>
<BR><BR>


A takto kľučové slová


[[Category:FPGA]]
[[Category:FPGA]]

Aktuálna revízia z 11:50, 29. november 2023

Modelovanie vnoreného systému na čipe FPGA - praktická časť



Cieľom praktického cvičenia je navrhnúť hardvér a softvér pre vnorený mikropočítačový systém, využívajúci soft-core procesor MicroBlaze. Úlohou systému bude ovládať rýchlosť jednosmerného motora pomocou pulznej šírkovej modulácie (PWM) v otvorenej slučke. Procesor bude zaznamenávať signály z enkodéra motora a na základe týchto signálov vyhodnocovať uhlovú rýchlosť motora v jednotkách otáčok za minútu (ot/min).


Úlohy

  1. Naštudujte si funkciu H-mostíka a enkodéra DC motora
  2. Navrhnite hardvér procesora s perifériami.
  3. Navrhnite a otestujte softvér pre procesor na obsluhu PWM a enkodéra motora.
  4. Overte ovládanie a snímanie uhlovej rýchlosti DC motora pomocou experimentu.



1. Naštudujte si funkciu H-mostíka a enkodéra DC motora



H-mostík

H-mostík je elektronický obvod používaný na riadenie smeru a veľkosti prúdu, ktorý tečie cez záťaž. Pozostáva z štyroch tranzistorov a vytvára tak kruhovú sústavu, ktorá dokáže meniť polaritu prúdu prechádzajúceho cez záťaž. Táto vlastnosť umožňuje použitie H-mostíka v rôznych aplikáciách, ako napríklad pri riadení motora, serva alebo pri riadení intenzity svetla LED. H-mostíky sú kľúčovými súčasťami v mnohých robotických aplikáciách a elektrotechnických projektov, kde umožňujú presné a účinné riadenie výkonu.

Pmod DHB1 je dvojkanálový H-mostík, ktorý dokáže ovládať 2 DC motory, je pripojiteľný pomocou konektoru Pmod. Pin EN1 môže byť realizovaný ako PWM signál, pri plnej striede (Duty) sa motor bude točiť maximálnou rýchlosťou. Pomocou DIR1 sa určí smer otáčania motora. Signály S1A a S1B slúžia ako výstupy z enkodéra, pomocou ktorých bude snímať rýchlosť otáčania motora. Viac informácií o H-mostíku nájdete na stránke výrobcu



Inkrementálny enkodér DC motora

Enkodér motora je senzor, ktorý slúži na zisťovanie otáčok a polohy rotujúceho prvku motora. Enkodéry môžu byť absolútne alebo inkrementálne. Inkrementálne enkodéry merajú zmenu polohy rotujúceho prvku vzhľadom k svojej východiskovej polohy. Absolútne enkodéry dokážu zistiť aktuálnu polohu rotujúceho prvku bez ohľadu na jeho predchádzajúce polohy.

V našej aplikácií budeme využívať magnetický inkrementálny enkodér s dvoma halovými sondami. Zmenu magnetického poľa vytvára kotúč s troma magnetmi. Halova sonda tak zaznamená tri nábežné hrany na otáčku motora. Pri výpočte treba započítať aj prevodový pomer planétovej prevodovky motora, ktorý je 1:19. Hriadeľ motora za prevodovkou otáča 19x pomalšie ako motor. Viac informácií o motore s enkodérom nájdete na stránke výrobcu



2. Navrhnite hardvér procesora s perifériami.

  • V programe Vivado otvorte projekt umiestnený v adresári C:\skolenie\VIVADO\embedded_system_design.
  • Vytvorte základnú štruktúru procesora microblaze s prerušeniami, ktorý pripojte na hodinový a reset signál. Použite pomocný nástroj Run Block Automation a nakonfigurujte procesor






  • Vytvorte hardvér pre UART komunikáciu, pripojte aj prerušenie na vstupný blok radiča prerušení. Výsledné zapojenie porovnajte s referenčným návrhom.


  • Vložte do návrhu časovač AXI Timer, pripojte na vstupy časovača capturetrig0 a capturetrig1 výstupy z enkodéra JA2 a JA3, ktoré vytvoríte pravým kliknutím na vstupný port IP jadra a následne zvolíte Make External. Výstup IP bloku interrupt pripojte na radič prerušení.
  • Vložte do návrhu ďalší časovač, ktorý bude tentokrát slúžiť na generovanie PWM signálu, na výstup pwm0 pripojte RGB1_Green a port JA0. Na port JA1 pripojíme konštantu 0 tak, aby sa motor mohol otáčať len jedným smerom. V tomto prípade interrupt pripájať nie je potrebné.
  • Otvorte IP Catalog pridajte nový repozitár vlastných IP blokov, ktorý sa nachádza v priečinku školských počítačov C:\vmsm\ip_repo. Importujte do návrhu IP blok s názvom Seven_segment_display_driver. Vytvorte externé porty an a seg a pripojte ich k IP bloku. Následne pripojte IP blok na AXI zbernicu.
  • Importujte do hardvérového návrhu obsluhu tlačidiel použitím AXI GPIO bloku
  • Importujte do hardvérového návrhu timer, pomocou ktorého budeme vyvolávať pravidelne sa opakujúce udalosti, napr. diskrétny PID regulátor
  • Overte si celkový dizajn s obrázkom. Vygenerujte HDL wrapper a bitstream, ktorý následne vyexportujte pre softvér Xilinx SDK.

3. Navrhnite a otestujte softvér pre procesor na obsluhu PWM a enkodéra motora

  • V programe vytvorte nový aplikačný projekt na základe šablóny Hello World a otestujte jeho funkčnosť. Súbor helloworld.c premenujte na main.c.
  • Importujte do projektu súbory periph.h a periph.c a analyzujte ich obsah. Súbory sa nachádzajú v priečinku C:\skolenie\
  • Pridajte do hlavného súboru main.c,
    • knižnice, direktívy a globálne premenné
    • Prototypy pre funkcie na obsluhu prerušení



#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xtmrctr.h"
#include "xintc.h"
#include "Seven_segment_display_driver.h"
#include "xgpio.h"
#include "periph.h"

#define PWM_PERIOD              	500000    // PWM perioda 500 us

#define ENCODER_DEVICE_ID			XPAR_TMRCTR_0_DEVICE_ID // ID  encoder
#define PWM_DEVICE_ID       		XPAR_TMRCTR_1_DEVICE_ID // ID pre pwm
#define TIMER_DEVICE_ID				XPAR_TMRCTR_2_DEVICE_ID	// ID pre timer
#define GPIO_BUTTONS_DEVICE_ID		XPAR_AXI_GPIO_0_DEVICE_ID //ID gpio periferie
#define INTC_DEVICE_ID				XPAR_INTC_0_DEVICE_ID	// ID radica preruseni

#define ENCODER_INTERRUPT_ID		XPAR_INTC_0_TMRCTR_0_VEC_ID  //ID prerusenia encodera
#define GPIO_INTERRUPT_ID			XPAR_INTC_0_GPIO_0_VEC_ID //ID prerisenia gpio
#define TIMER_INTERRUPT_ID			XPAR_INTC_0_TMRCTR_2_VEC_ID	//ID prerusenia timer
#define COUNTER_INIT_VAL			0
#define DISP_DRV_BASE_ADDR 			XPAR_SEVEN_SEGMENT_DISPLA_0_S00_AXI_BASEADDR

#define TIMER_INTERVAL				10000000 // perioda

XTmrCtr TimerEncoder; 				/* 0 The instance of the Timer for Encoder */
XTmrCtr TimerPWM; 					/* 1 The instance of the Timer for PWM */
XTmrCtr TimerController;			/* 2 The instance of the Timer for periodic events */

XIntc InterruptController; 			/* The instance of the Interrupt Controller */

XGpio Buttons;

int DutyCycle;
u32 Period;
u32 HighTime;
u8 Div;
volatile float rpm;  			// Speed of DC Motor
volatile int time;				// time period of last impulse from motor encoder probe A
static u16 GlobalIntrMask; 		// GPIO channel mask that is needed by the Interrupt Handler

u32 ControllerPeriod;

u32 soft_counter;


static void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber);
static void GpioHandler(void *CallbackRef);
static void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber);



Obsluha pre tlačidlá

Pre inicializáciu obsluhy tlačidiel je potrebné vo funkcii main() zavolať inicializačné funkcie

        init_gpio(&Buttons, GPIO_BUTTONS_DEVICE_ID, 0x1, 0x1);
	init_gpio_intr(&Buttons, &InterruptController,
			(Xil_ExceptionHandler) GpioHandler, GPIO_BUTTONS_DEVICE_ID,
			INTC_DEVICE_ID, GPIO_INTERRUPT_ID, &GlobalIntrMask);



V inicializačnej funckií sme nastavili, že po stlačení tlačidla sa vyvolá prerušenie procesora ktoré následné spustí funkciu void GpioHandler(void *CallbackRef). Základnú štruktúru takejto funkcie môžeme vidieť v nasledujúcej ukážke.

void GpioHandler(void *CallbackRef) {
	XGpio *GpioPtr = (XGpio *) CallbackRef;
	int val = XGpio_DiscreteRead(&Buttons, 1);

	xil_printf("button event\n");

	/* Clear the Interrupt */
	XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
}



Úloha: Zistite aké hodnoty sa uložia do premennej val po stačení jednotlivých tlačidiel.

Obsluha časovača pre PWM

Inicializáciu časovača pre PWM realizujeme pomocou funkcie

Period = PWM_PERIOD;
timer_pwm_init(&TimerPWM, PWM_DEVICE_ID, Period);



Samotná zmena pwm signálu je realizovaná pomocou sekvencie funkcií:

	/* DisablePWM */
	XTmrCtr_PwmDisable(&TimerPWM);
	/* Confgure PWM */
	HighTime = PWM_PERIOD * DivF;  // DivF is real (float) number from 0 to 1
	DutyCycle = XTmrCtr_PwmConfigure(&TimerPWM, Period, HighTime);
	/* Enable PWM */
	XTmrCtr_PwmEnable(&TimerPWM);



Úloha: Upravte funkciu obsluhy tlačidiel tak, aby bola pomocou vybraných tlačidiel možná zmena Striedy PWM. Zmenu plnenia PWM signálu môžete pozorovať na zelenej LED na vývojovej doske.



Obsluha časovača pre enkodér motora

Časovač pre enkodér motora inicializujeme vo funkcií main() pomocou funkcie:



	timer_encoder_init(&TimerEncoder, &InterruptController, TimerEncoderHandler,
	ENCODER_DEVICE_ID, INTC_DEVICE_ID, ENCODER_INTERRUPT_ID,
	COUNTER_INIT_VAL);



Obsluha prerušenia sa vyvolá po každej nábežnej hrane senzora A. Do premennej time sa uloží hodnota počítadla, ktorý počítal hodinové impulzy od poslednej nábežnej hrany sondy A. Na základe počtu impulzov hodinového signálu sa dá vyhodnotiť čas, ktorý medzi dvoma nábežnými hranami enkodéra uplynul. Prepočtom je potom možné vyjadriť aj uhlovú rýchlosť motora v otáčkach za minútu.



void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber) {
	XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
	time = XTmrCtr_GetCaptureValue(InstancePtr, TmrCtrNumber);
	XTmrCtr_SetResetValue(InstancePtr, TmrCtrNumber, RESET_VALUE);
	XTmrCtr_Reset(InstancePtr, TmrCtrNumber);

	rpm = 105263179 / time;
}



Úloha: Vytvorte vo funkcií main() nekonečnú slučku v ktorej budete zobrazovať na 7 segmentový displej rýchlosť motora. Otestujte funkčnosť displeja.



usleep(5000000);
int irpm = (int) rpm;
SEVEN_SEGMENT_DISPLAY_DRIVER_mWriteReg(DISP_DRV_BASE_ADDR, 0x0, irpm);



Obsluha pre časovač pravidelných udalostí

Inicializácia časovača pravidelných udalostí vo funckií main():

	ControllerPeriod = TIMER_INTERVAL;
	timer_contoller_init(&TimerController, &InterruptController,
			TimerControllerHandler,
			TIMER_DEVICE_ID, INTC_DEVICE_ID, TIMER_INTERRUPT_ID,
			COUNTER_INIT_VAL, ControllerPeriod);



Funkcia prerušenia časovača:

void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber) {
	XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;

	int irpm = (int) rpm;
	
}



Úloha: Upravte zdrojový kód tak, aby sa zobrazenie údajov na displej ako aj cez terminál realizovalo v pravidelných 1 sekundových intervaloch pomocou funkcie prerušenia časovača Timer_2.

Úloha: Naprogramujte softvérový counter, ktorý bude počítať všetky nábežné hrany z halovej sondy A počas jednej sekundy. Na základe počtu impulzov vypočítajte uhlovú rýchlosť motora v ot/min (rpm). Porovnajte oba spôsoby vyhodnotenia uhlovej rýchlosti pomocou experimentu.

Úloha: Upravte postupne intervaly prerušenia časovača na 100ms a 10ms. Experiment opakujte a výsledky pre jednotlivé vzájomne porovnajte .








Kompletné definície funkcií pre obsluhu prerušení

void GpioHandler(void *CallbackRef) {
	XGpio *GpioPtr = (XGpio *) CallbackRef;
	int val = XGpio_DiscreteRead(&Buttons, 1);
	//button is pressed
	if (val != 0x0) {
		if ((val == 0x1) && (Div < 10)) {
			Div++;
		} else if ((val == 0x4) && (Div > 0)) {
			Div--;
		}
		float DivF = (float) Div / 10;
		HighTime = PWM_PERIOD * DivF;
		if (Div > 9) {
			HighTime = PWM_PERIOD - 10;
		} else if (Div == 0) {
			HighTime = PWM_PERIOD;
		}

		//xil_printf("Duty is %d0 %% \n", Div);

		XTmrCtr_PwmDisable(&TimerPWM);
		DutyCycle = XTmrCtr_PwmConfigure(&TimerPWM, Period, HighTime);
		/* Enable PWM */
		XTmrCtr_PwmEnable(&TimerPWM);
	}
	/* Clear the Interrupt */
	XGpio_InterruptClear(GpioPtr, GlobalIntrMask);
}
void TimerEncoderHandler(void *CallBackRef, u8 TmrCtrNumber) {
	XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;
	time = XTmrCtr_GetCaptureValue(InstancePtr, TmrCtrNumber);
	XTmrCtr_SetResetValue(InstancePtr, TmrCtrNumber, RESET_VALUE);
	XTmrCtr_Reset(InstancePtr, TmrCtrNumber);

	rpm = 105263179 / time;
	soft_counter++;
}
void TimerControllerHandler(void *CallBackRef, u8 TmrCtrNumber) {
	XTmrCtr *InstancePtr = (XTmrCtr *) CallBackRef;

	int irpm = (int) rpm;
	float crpm = ((soft_counter * 60 ) / (19 * 3))*(100000000/TIMER_INTERVAL);
	int icrpm = (int) crpm;
	xil_printf("%d,",Div*10);
	xil_printf("%d,",irpm);
	xil_printf("%d\n",icrpm);
	soft_counter = 0;
	SEVEN_SEGMENT_DISPLAY_DRIVER_mWriteReg(DISP_DRV_BASE_ADDR, 0x0, irpm); //
}