DualCore -> már a multé...

     Pár napja sikerült összerakni egy hárommagos projektet. Abba vagyok most belebolondulva. Úgy érzem, ez lesz a végleges modell, ami meg is marad a projektben. Az első proci feladata a kommunikáció, a második proci lesz a matekes, a harmadik meg majd vezérli a szervókat...

     Véletlenül rátaláltam, hogyan lehet blokkdiagramot generálni a projektrajzból, így be is linkelném a legutóbbi blokkrajzomat.


     Egyelőre elég csupasz... Még lesz pár dolog, ami nincs berakva. Még egy képet meg szeretnék osztani itt. Az új fejlesztőlapomról lenne szó. Pont ugyanolyan, mint a régi, csak sokkal többet tud. Egy C220-as Merci, buherált 545 lóerős motorral.



     A processzorokon futó programok a következők:

#include <xparameters.h>
#include <xuartlite_l.h>
#include <xuartlite.h>
#include <xstatus.h>
#include <stdio.h>
#include <stdlib.h>
#include <mb_interface.h>
#include <xintc_l.h> 
#include <xutil.h>
#include <xio.h>  

//====================================================
// Definitions
//====================================================
#define BAUDRATE 115200
#define USE_PARITY 0
#define PARITY_ODD 1
#define DATABITS 8
#define MICRON_MEM XPAR_MICRON_RAM_MEM0_BASEADDR
#define WIDTH 178
#define HEIGHT 143
//====================================================
// GLOBAL VARIABLES
//====================================================
unsigned int counter = 0;
unsigned int c2 = 0;
Xuint8 buchstabe;
Xuint8 flag=0;
Xuint8 x=0,y=0;
Xuint32 data,temp;
Xuint32* mem = (Xuint32*) MICRON_MEM;
XUartLite serial,second_serial;

void cam_ack()
{
    char x[2];
        x[0]=10;
        x[1]=13;
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)(x), 2);            
}

//====================================================
// INTERRUPT HANDLER FUNCTION
//====================================================

void serial_interrupt_handler(void* addr_p)
{ 
}

void second_serial_interrupt_handler(void* addr_p)
{ 
    int empty;
            empty = XUartLite_IsReceiveEmpty(XPAR_RS232_SECOND_BASEADDR);
            if (empty == FALSE )
            {
                buchstabe = XUartLite_RecvByte(XPAR_RS232_SECOND_BASEADDR);    
                if(flag == 1)
                {
                    data = buchstabe;
                    mem[c2]=data;
                    if(buchstabe == 3)
                    {
                        xil_printf("Harmas.%d\n",c2);
                    }
                    c2++;
                    xil_printf("%c",buchstabe);    
                }
                else xil_printf("%c",buchstabe);                                
            }
}

//====================================================
// DELAY FUNCTION
//====================================================

void udelay(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<26; j++);
}

void init_serial()
{
    XUartLite_Config UART_Configuration;
    UART_Configuration.DeviceId = XPAR_RS232_PORT_DEVICE_ID;
    UART_Configuration.RegBaseAddr = XPAR_RS232_PORT_BASEADDR;
    UART_Configuration.BaudRate = BAUDRATE;
    UART_Configuration.UseParity = USE_PARITY;
    UART_Configuration.ParityOdd = PARITY_ODD;
    UART_Configuration.DataBits = DATABITS; 

    XUartLite_Config UART_Second_Configuration;
    UART_Second_Configuration.DeviceId = XPAR_RS232_SECOND_DEVICE_ID;
    UART_Second_Configuration.RegBaseAddr = XPAR_RS232_SECOND_BASEADDR;
    UART_Second_Configuration.BaudRate = BAUDRATE;
    UART_Second_Configuration.UseParity = USE_PARITY;
    UART_Second_Configuration.ParityOdd = PARITY_ODD;
    UART_Second_Configuration.DataBits = DATABITS; 
    
    XUartLite_CfgInitialize (&serial, &UART_Configuration, XPAR_RS232_PORT_BASEADDR) ;
    XUartLite_CfgInitialize (&second_serial, &UART_Second_Configuration, XPAR_RS232_SECOND_BASEADDR) ;
}

int main ()
{
    int            i,j;
    unsigned int   oldstate;
    int count = 0;
     
     init_serial();
     xil_printf("CPU 0 joined.\r\n");
    
    XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
                                 XPAR_XPS_INTC_0_RS232_PORT_INTERRUPT_INTR,
                                 (XInterruptHandler)serial_interrupt_handler,
                                 (void *)XPAR_RS232_PORT_BASEADDR);    
                                 
    XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
                                 XPAR_XPS_INTC_0_RS232_SECOND_INTERRUPT_INTR,
                                 (XInterruptHandler)second_serial_interrupt_handler,
                                 (void *)XPAR_RS232_SECOND_BASEADDR);                                     
    
    XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
                                           //ez   V    =(XPAR_RS232_PORT_INTERRUPT_MASK&&XPAR_RS232_SECOND_INTERRUPT_MASK)
    XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, 0X000003);
    
    //XUartLite serial,second_serial;
    XUartLite_Initialize(&serial,XPAR_RS232_PORT_DEVICE_ID);
    XUartLite_Initialize(&second_serial,XPAR_RS232_SECOND_DEVICE_ID);
    
    XUartLite_SetRecvHandler(&serial,
                                     (XUartLite_Handler)  serial_interrupt_handler,  
                                     (void *)XPAR_RS232_PORT_BASEADDR);  
    XUartLite_SetRecvHandler(&second_serial,
                                     (XUartLite_Handler)  second_serial_interrupt_handler,  
                                     (void *)XPAR_RS232_SECOND_BASEADDR);  
    
    XUartLite_EnableInterrupt(&serial);  
    XUartLite_EnableInterrupt(&second_serial);  
    
    microblaze_enable_interrupts();

//------------------------------------
//  CMUcam3 interraction
//------------------------------------

        cam_ack();
        udelay(1000);
        
        xil_printf("\nReset the Camera\n");
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("RS"), 8);        
        cam_ack();
        udelay(500000);

       
        cam_ack();
        udelay(1000);
        
        xil_printf("\nCR 18 36 -> YCrCb, auto white balance on\n");
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("CR 18 36"), 8);        
        cam_ack();
        udelay(100000);
        
        xil_printf("HR 0 -> Resolution 88*143\n");
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("HR 0"), 8);        
        cam_ack();
        udelay(100000);
        
        xil_printf("NF 30 -> Noise Filtering(wtf)\n");
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("NF 30"), 8);        
        cam_ack();
        udelay(100000);        

        xil_printf("Get the Luminance layer, from YCrCb\n");
        flag=1;
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("SF 1"), 8);        
        cam_ack();
        while(c2<12736);
        flag=0;
        for(i=0;i<c2;i++)
        {
            xil_printf("%d\n",mem[i]);
        }
        while(1);
    return 0;
} 


     Ebben a kódrészletben kommunikálok az új babámmal, egy CMUCam3-assal. Van rajta egy CMUCam2 emulátor program, ami értelmezi az utasításokat, majd visszaküldi a megfelelő információt. Ebben a programban a következő lépéseken megyünk hosszan :
          1. "\r" szólunk a kamerának, hogy figyeljen ide, illetve töröljük az eddigi hibás parancsokat (mert lehet, hogy van már ott valami, ami nem kellene ott legyen...)
          2. "RS\n\r" Reset the Camera. Újraindítjuk a kamerát, aminek hatására a regisztereibe visszakerülnek az alapértelmezett beállítások.
          3. "CR 18 36\n\r" YCrCb színkódolás beállítása, illetve automatikus fekéregyensúly beállítása (erre majd később lehet visszatérek)
          4. "HR 0\n\r" Képfelbontás beállítása : 174*144 kb... Ez nagyjából akkora, mint egy bélyeg.


Annak ellenére, hogy ilyen kicsi a kép, sok sok információt hordozhat magában. Ebből már lehet színkövetést, vonalkövetést, meg sok más érdekes dolgot készíteni. Ebből én csak egy színtartományt használok fel. Nagyjából ugyanaz van rajta, mint a képen, csak fekete-fehérben.

          5. "NF 30\n\r" Noise Filtering vagyis zajszűrés. A paraméterként megadott szám franc tudja mit jelent, sajnos nekem is csak feltételezéseim vannak ezzel kapcsolatban. Az angol leírás számomra nem egyértelmű. "This command controls the Noise Filter setting. It accepts a value that determines how many consecutive active pixels before the current pixel are required before the pixel should be detected (default 2). The range is between 0 and 255."

          6. "SF 1\n\r" Send Frame. Ezzel a paranccsal indítjuk el a pixelek egymásutáni küldését. A paraméterként küldött számmal ki tudjuk választani, hogy melyik színcsatornát szeretnénk látni. Ebben az esetben ez az Y színcsatorna, vagyis a Luminance, magyarul asszem színerősség.
          Most látom fontosnak megemlíteni, mit is csinál a megszakítás kezelő függvény. Tesztelési fázisban két megszakítás kezelő függvény volt, az egyik az egyik soros port RX vonalára, a másik a másik soros port RX vonalára. Azért interrupt, hogy a program futását ne kelljen felfüggeszteni, amíg az adatokra várakozunk.
     Az első öt lépés esetében sokmindent nem csinál a megszakítás kezelő. Egyedüli feladata kiírni a standard soros kimenetre a másik soros porton érkezett adatokat. Az első öt lépés eszetében minden egyes parancsra egy ACK érkezik a kamerától, ezeket láthatjuk a következő képen :
Az a sok karakter a végén maga a kép, csak ASCII karakterekben kódolva. Ezeket a pixel értékeket kell elmenteni a micronRam-ba, későbbi feldolgozás végett. Azért nem a StrataFlash memóriát használjuk, mert annak írása és olvasása meghaladja az én agyi képességeimet. A MicronRam írása és olvasása egyszerű tömbműveletként képzelhető el. Fogunk egy mutatót (pointert), amivel rámutatunk a memória kezdőcímére, majd azzal végigmászunk a memórián.

#define MICRON_MEM XPAR_MICRON_RAM_MEM0_BASEADDR
(...)
Xuint32* mem = (Xuint32*) MICRON_MEM;
(...)

void second_serial_interrupt_handler(void* addr_p)
{ 
    int empty;
            empty = XUartLite_IsReceiveEmpty(XPAR_RS232_SECOND_BASEADDR);
            if (empty == FALSE )
            {
                buchstabe = XUartLite_RecvByte(XPAR_RS232_SECOND_BASEADDR);    
                if(flag == 1)
                {
                    data = buchstabe;
                    mem[c2]=data;
                    if(buchstabe == 3)
                    {
                        xil_printf("Harmas.%d\n",c2);
                    }
                    c2++;                        
                }
                else xil_printf("%c",buchstabe);                                
            }
}

(...)
void main()
{
(...)
        xil_printf("Get the Luminance layer, from YCrCb\n");
        flag=1;
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)("SF 1"), 8);        
        cam_ack();
(...)
return 0;
}

     Gondolom ehhez már sok kommentet nem kell fűzni. Mielőtt lekérem a pixelértékeket, beállítom a flaget, ami azt mutatja, hogy képinformáció fog jönni a soros porton, s hogy azt másképp kell kezelni. Ennél elegánsabb megoldás az lehetett volna, ha a megszakítás kezelő függvényt cserélem ki egy másikra. Lehet meg is csinálom...

     Két próbakép, amit a CmuCam3-assal készítettem, legkisebb felbontásban.



Röviden ennyi lenne mára. Most fogok neki, átírom a megszakítás kezelő függvényt, hogy ott helyben számoljon integrálképet is. Ha van valami új, akkor még jelentkezem...

Flash_Ram Iras es olvasas

      Ki is mondta azt, hogy nem lehet a Flash Ramot programbol írni???



// This Code is for Read, Write and Erase

#include "xparameters.h"
#include "stdio.h"
#include "xutil.h"
#include "xuartlite_l.h"
#include "xio.h"
//#include "xgpio_l.h"

main()
{
Xuint16 send_emc16;
Xuint16 read_emc16;
Xuint8 read_uart_command,send_emc1,send_emc2,read_emc1,read_emc2;
int i,addr_c,addr_c1,j,l,k,m;
xil_printf("\n\rFlash read write erase\n\r");  

    while(1)
    {
            //XGpio_mSetDataReg(0x40000000, 1, 0); // ADV and clk GPIO_0
            //Waiting for Command
            read_uart_command = XUartLite_RecvByte ( XPAR_RS232_PORT_BASEADDR )  ; // UART

            //unlocking blocks
            if(read_uart_command == '~')
            {
              addr_c = XPAR_INTEL_FLASH_MEM0_BASEADDR;
              for(i=0;i<4;i++)
                 {
                    XIo_Out16(addr_c, 0x60);
                    XIo_Out16(addr_c, 0xD0);    
                     for(j=0;j<1000000;j++);
                    XIo_Out16(XPAR_INTEL_FLASH_MEM0_BASEADDR, 0x90);
                    read_emc16 = XIo_In16 (XPAR_INTEL_FLASH_MEM0_BASEADDR + 0x02);
                    xil_printf("\n\r0x%x ", read_emc16);
                    addr_c = addr_c + 0x4000;
                 }
                xil_printf("\n\rFINISHED Unblocking\n\r");
            }
             
            //erasing FLASH
            if(read_uart_command == '!')
            {
                XIo_Out16(XPAR_INTEL_FLASH_MEM0_BASEADDR, 0x20);
                XIo_Out16(XPAR_INTEL_FLASH_MEM0_BASEADDR, 0xD0);    
            xil_printf("\n\rErasing FLASH\n\r"); // all loops below this line are written to provide delay and     
                    for(l=0;l<1;l++)   // formatting for ERASE operation only
                        {  
                          for(j=0;j<6;j++)
                              {
                                for(k=0;k<5;k++)
                                     {
                                    xil_printf(".");    
                                for(m=0;m<100000;m++);                                   
                                     }
                                xil_printf("\n\b\b\b\b\b");
                              }
                        }
                xil_printf("\n\rFINISHED Erasing\n\r");
            }



            //reading flash
            if(read_uart_command == '@')
            {
                xil_printf("\n\rreading flash\n\r");

                addr_c = XPAR_INTEL_FLASH_MEM0_BASEADDR ;

                //command
                for(i=0;i<1000000;i++) 
                {
                  XIo_Out16 (addr_c, 0xFF);
                }

                // reading logic
                for(i=0;i<3000;i++)
                {
                    read_emc16 = XIo_In16 (addr_c);
                    read_emc1 = read_emc16;
                    read_emc2 = read_emc16 >> 8;
                    xil_printf("%d\n", read_emc1);
                    xil_printf("%d\n", read_emc2);
                    addr_c = addr_c + 2;
                }
            }



            // writing FLASH
            if(read_uart_command == '#')
            {
                xil_printf("\r\nWriting Flash chip");

                addr_c = XPAR_INTEL_FLASH_MEM0_BASEADDR ;

                for(i=0;i<3000;i++)
                {
            //    send_emc1 = XUartLite_RecvByte (XPAR_RS232_PORT_BASEADDR);
            //    send_emc2 = XUartLite_RecvByte (XPAR_RS232_PORT_BASEADDR);
                send_emc1 = 1;
                send_emc2 = 2;
                send_emc16 = ((send_emc1) | (send_emc2 << 8));
                XIo_Out16 (addr_c, 0x40);
                XIo_Out16 (addr_c, send_emc16);
                addr_c = addr_c + 2;
                for(m=0;m<100;m++);
                }
                xil_printf("\n\rFINISHED Writing\n\r");
            }

            //Status register data
            if(read_uart_command == '$')
            {
                xil_printf("\r\nReading Status register data");
                //XIo_Out16 (0x86000000, 0x70);
                read_emc16 = XIo_In16 (0x86000000+0x00011);
                xil_printf("\n\r0x%x ", read_emc16);
            }
     
    }// end while
}// end main()

Microblaze Mutex

     Mai bejegyzés témája a közös hozzáférés a perifériákhoz. Valós szituáció : adott két ember és adott egy darab telefon. A telefon bent van egy szobában. Mindketten akarnak telefonálni időnként. Lehet, hogy egyszerre, lehet hogy időben távol egymástól. Ennek szinkronizálásan nem nehéz emberek esetében. Simán megkérdezi, az egyik, hogy mikor akarja használni a másik, s beosztják az időt. Vagy mikor eszükbe jut, mennek, s megnézik, hogy foglalt a telefon vagy nem. Ha foglalt, akkor később újrapróbálkoznak, ha meg nem foglalt, akkor lehet dumálni rajta.

     Ugyanígy kell elképzelni a MicroBlaze procikat is. Ketten akarják a soros portot használni, de lehetőleg úgy, hogy ne zavarják egymást közben, vagyis ne tudja megszakítani az egyik a másik beszélgetését. Erre találták ki a mutexet. Ez olyan, mint a zár az ajtón. Ha bementem, bezárom magam mögött az ajtót, ha végeztem, akkor kizárom, s kijövök. Ha megyek, s látom, hogy be van zárva, akkor újrapróbálkozom később. Röviden ennyi.

      Program elkészítésének lépései : Legelőször kell egy kétmagos projekt. Ennek elkészítése nem képezi szerves részét ennek a leírásnak. Ha az megvan, hozzáadunk az egyik processzorhoz egy soros kommunikációs eszközt, majd egy PLBBridge-t. Ez a bridge - magyarul híd, ahogy a neve is mutatja, kapcsolatot teremt a két processzor PLB buszrendszere között. A buszrendszerről annyit kell tudni, hogy ezen keresztül kommunikál a processzor a hozzá rendelt perifériákkal, úgy, hogy megcímezi őket, aztán a cím után küldi a megfelelő adatot. Ha a buszon van valaki, akinek ugyanaz a címe, akkor az értelmezi az adatokat és válaszol, vagy esetenként elvégez (vagy nem végez el) valamit. Ha a buszra olyan adat-utasítás páros kerül, amelyiknek a címe nem mutat egyezést egyik buszon lévő perifériával sem, akkor vagy hiba keletkezik, vagy csak simán elvész az adat.

     Ebből a szempontból elég jól meg van tervezve az XPS fejlesztőkörnyezet, mert akármit nem is enged megcímezni. Ha biztosak akarunk lenni abban, hogy valaki magáénak érzi az általunk PLB buszra rakott adatot, akkor előtte érdemes meggyőződni arról, hogy az illető perifériának a címe szerepel az xparameters.h állományban. Ebbe az állományba gyűjti össze az XPS azoknak a perifériáknak a címeit, amikhez az illető processzornak hozzáférése van. Persze ettől függeltenül lehet vaktában is lövöldözni a PLB buszra, hátha eltalálunk valakit. Mintha feladnánk egy levelet Bukarestbe a Liszt Ferenc utca 24 szám alá Gitano Armandonak, remélve, hogy megkapja. Legrosszabb, ami történhet, hogy a levél visszajön. Viszont ha valós periféria kap meg egy nem neki szánt üzenetet, az komolyabb galibát is okozhat.

     Időközben az XPS fenyegetőzik : WARNING:Security:40 - Your license for 'SDK' expires in 3 days.

     Második lépés a mutex hozzáadása az eddigiekhez. A mutexet is rá kell kötni mindkét PLB buszra. Ha ez is megvan, és a Generate Addresses gomb megnnyomása sem halmozott el irdatlanul nagy mennyiségű hibával, akkor máris tovább léphetünk a programozásra. Viszont ha hibaüzenetünk van, azon már csak a Google segíthet.

    A program szerkezetileg ugyanúgy néz ki, mint az eddig megírt programjaink, csak újradeifiniáljuk (direkt írtam egybe) az outbyte függvényt, illetve minden xil_printf hívás előtt lock-oljuk a mutexet, majd utána unlock-oljuk. Az outbyte függvény ujradefiniálása azért fontos, hogy kényszerítsük a xil_printf-et, hogy az általunk megadott sorosporton keresztül kommunikáljon a külvilággal.

Forráskód az egyik processzorra (nálam a főprocira ) :

#include <xparameters.h>
#include <xmutex.h>
#include <xuartlite_l.h>
#include <xstatus.h>
#include <stdio.h>
#include <stdlib.h>
#include <mb_interface.h>

/************************** Constant Definitions *****************************/


#define MUTEX_NUM               0
#define SHAREDCONSOLE_BASEADDR  XPAR_RS232_PORT_BASEADDR

/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/



/************************** Function Prototypes ******************************/

void init_lock ();
void init_hw ();
void deinit_hw ();
void MLOCK ();
void MUNLOCK ();
void outbyte (char c);

/* mutex -- Our main mutex lock */

XMutex mutex;


volatile unsigned int *sharedstate = (unsigned int *)XPAR_SHARED_BRAM_IF_CNTLR_0_TOP_BASEADDR;

/* We are either CPU1 or CPU2. 
   Code below relies on the value being one of these two */

unsigned int cpu_status[2] = { 1000, 2000 };

void udelay(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<26; j++);
}

int main ()
{
    int            i;
    unsigned int   oldstate;
    int count = 0;

    init_lock();

    xil_printf ("--------------------------------------------------------------------\r\n");
    xil_printf ("Dual Processor Synchronization Demo!\r\n");
    xil_printf ("--------------------------------------------------------------------\r\n");
    xil_printf ("Desc -- CPU0 and CPU1 will synchronize their output to this\r\n");
    xil_printf ("        console using XPS Mutex Locks.\r\n");    
    xil_printf ("--------------------------------------------------------------------\r\n");
    xil_printf ("SHAREDCONSOLE: CPU0 and CPU1 rendezvousing...");
//    fflush (stdout);
//

    /* Rendezvous first to enable co-ordinated output */

    *sharedstate = 0x1;
    while (*sharedstate != 0x0)
        ;

    while (count++ < 10) {
        
        MLOCK ();

        oldstate = *sharedstate;
        *sharedstate = cpu_status[XPAR_CPU_ID]++;
        xil_printf ("CPU(%d) -- changing shared state from %d", XPAR_CPU_ID, oldstate);
        xil_printf (" to...");
        udelay (500000);
        xil_printf ("%d.\r\n", *sharedstate);
        udelay (500000);

        MUNLOCK ();
        udelay (50000);
    }

    MLOCK ();
    xil_printf ("SHAREDCONSOLE: CPU(%d) Ends.\r\n", XPAR_CPU_ID);
    MUNLOCK ();

    while (*sharedstate != 0x0)
        ;
    xil_printf ("SHAREDCONSOLE: End of Demo!\r\n");
    xil_printf ("--------------------------------------------------------------------\r\n");

}

/* We purposefully redefine outbyte here to force output from xil_printf and xil_xil_printf 
   to go to the UART we want it to go to, rather than the configured STDOUT */

void outbyte (char c)
{
    XUartLite_SendByte(SHAREDCONSOLE_BASEADDR, c);
}

void init_lock ()
{
    XMutex_Config *cfg;
    
    cfg = XMutex_LookupConfig (XPAR_MUTEX_0_IF_0_DEVICE_ID);
    if (cfg == NULL) {
        xil_printf ("SHAREDCONSOLE: Startup error!\r\n");
        abort ();
    }
    XMutex_CfgInitialize (&mutex, cfg, cfg->BaseAddress);
}

void MLOCK ()
{
#ifndef NO_SYNC
    XMutex_Lock (&mutex, MUTEX_NUM);                    /* Acquire lock */
#endif
}

void MUNLOCK ()
{
    u32 status;
#ifndef NO_SYNC
    status = XMutex_Unlock (&mutex, MUTEX_NUM);         /* Release lock */
    if (status != XST_SUCCESS)
        abort ();
#endif
}


Forráskód a másik procira :

/***************************** Include Files *********************************/

#include <xparameters.h>
#include <xmutex.h>
#include <xuartlite_l.h>
#include <xstatus.h>
#include <stdio.h>
#include <stdlib.h>
#include <mb_interface.h>


/************************** Constant Definitions *****************************/
#define MUTEX_NUM               0
#define SHAREDCONSOLE_BASEADDR  XPAR_RS232_PORT_BASEADDR 


/**************************** Type Definitions *******************************/


/***************** Macros (Inline Functions) Definitions *********************/



/************************** Function Prototypes ******************************/

void init_lock ();
void MLOCK ();
void MUNLOCK ();
void outbyte (char c);

void udelay(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<26; j++);
}



/* mutex -- Our main mutex lock */

XMutex mutex;

volatile unsigned int *sharedstate = (unsigned int *)XPAR_SHARED_BRAM_IF_CNTLR_0_BOTTOM_BASEADDR;

unsigned int cpu_status[2] = { 1000, 2000 };

int main ()
{
    int            i;
    unsigned int   oldstate;
    int count = 0;

    init_lock();


    /* Rendezvous first to enable co-ordinated output */
    while (*sharedstate != 0x1)
        ;
    xil_printf ("done\r\n");
    *sharedstate = 0x0;

    while (count++ < 10) {
        
        MLOCK ();

        oldstate = *sharedstate;
        *sharedstate = cpu_status[XPAR_CPU_ID]++;
        xil_printf ("CPU(%d) -- changing shared state from %d", XPAR_CPU_ID, oldstate);
        xil_printf (" to...");
        udelay (500000);
        xil_printf ("%d.\r\n", *sharedstate);
        udelay (500000);

        MUNLOCK ();
        udelay (50000);
    }

    MLOCK ();
    xil_printf ("SHAREDCONSOLE: CPU(%d) Ends.\r\n", XPAR_CPU_ID);
    MUNLOCK ();

    *sharedstate = 0x0;

}

/* We purposefully redefine outbyte here to force output from xil_printf and xil_xil_printf 
   to go to the UART we want it to go to, rather than the configured STDOUT */

void outbyte (char c)
{
    XUartLite_SendByte(SHAREDCONSOLE_BASEADDR, c);
}

void init_lock ()
{
    XMutex_Config *cfg;
    
    cfg = XMutex_LookupConfig (XPAR_MUTEX_0_IF_1_DEVICE_ID);
    if (cfg == NULL) {
        xil_printf ("SHAREDCONSOLE: Startup error!\r\n");
        abort ();
    }
    XMutex_CfgInitialize (&mutex, cfg, cfg->BaseAddress);
}

void MLOCK ()
{
#ifndef NO_SYNC
    XMutex_Lock (&mutex, MUTEX_NUM);                    /* Acquire lock */
#endif
}

void MUNLOCK ()
{
    u32 status;
#ifndef NO_SYNC
    status = XMutex_Unlock (&mutex, MUTEX_NUM);         /* Release lock */
    if (status != XST_SUCCESS)
        abort ();
#endif
}


     A kódhoz sok hozzáfűznivalóm már nincs. Megkerült ennek is az eredeti helye és eredeti szerzője (mint már említettem, ezek csak "feldolgozások"...) Az eredeti leírásra mutat a következő link:
http://www.xilinx.com/support/documentation/application_notes/xapp996.pdf
     Ugyanott megtalálható az eredeti forráskódra mutató link is. Igaz, kicsit kell vele dolgozni, meg kell az a bizonyos Isteni szikra is, viszont az alap már megvan.

     Ennyi mára.

Leporolni a tudást

     Egyszer régen, amikor még sok sok szabadidőm volt, elkészítettem pár bonyolultabb programot, gondolván a jövőmre is. Ezeknek fogtam neki ujból, kicsit nézzem át őket. Volt ott osztott memória kezelés, mailbox és mutex. Ezekkel azért jó tisztában lenni, hogy a legjobb megoldást válasszuk egy komplexebb projekt esetén.

     Abból az alapfeltevésből indulunk ki, hogy van egy dualcore-os procink, amelyikben a processzorok szeretnének egymással és a külvilággal kommunikálni. Egymással elég könnyen tudnak. Deklarálunk egy osztott memória részt, amihez mindkét proci hozzáfér, oda beírják a gondjaikat, a másik proci meg kiolvassa. Itt szembesülünk először az ütemezési problémával, vagyis azzal, hogy melyik proci mikor ír és mikor olvas arról a közös memóriáról, illetve honnan tudja, hogy a másik proci már feldolgozta-e az ott lévő adatot, vagy sem. Ez egy nagyon komoly hátrány. Viszont megvan az az előnye, hogy nagyon könnyű kezelni. Még egy ovodás gyerek is (pár perces magyarázat után) meg tudja érteni, hogyan is működik.


     A következő lépés a boldogság felé vezető úton a mailbox lehetne, ami arra jó, hogy a processzorok üzeneteket küldözgessenek egymásnak. Legelső lépés inicializálni a mailboxot:

    XMbox mbox;
    XMbox_Config *cfg;
    cfg = XMbox_LookupConfig (XPAR_MAILBOX_0_IF_0_DEVICE_ID);
    status = XMbox_CfgInitialize (&mbox, cfg, cfg->BaseAddress);

     Itt megvizsgáljuk a status változó értékét, ami ha XST_SUCCESS, akkor sikeresen inicializáltuk a mailboxot, ha valami egyéb, akkor meg nem. Ezután következet az üzenet küldés, ami lehet blokkolásos, illetve nem blokkolásos. A kettő között az a különbség, hogy a blokkolásos üzemmódban az egyik proci addig vár, amíg a másik nem fogadja az ő üzenetét.

     Ezt postással és levéllel úgy lehetne elmagyarázni, hogy jön a postás, és nincs postaláda. Ennek következtében a postás kénytelen addig várni a kapu előtt, amig én ki nem megyek, hogy elvegyem tőle a levelet. Viszont ha én épp mással vagyok elfoglalva (annak fontosságától függetlenül), akkor lehet, hogy a postás sokáig fog várni, amig én kimegyek. Viszont ugyanez van fordítva is. Ha én akarok levelet feladni, akkor addig kell várjak a levéllel a kapu előtt, amíg arra nem jár a postás és elveszi tőlem. Ez akkor jó, ha bizonyos folyamatokat szinkronizálni kell, mivel ebből a blokkolásos üzemmódból csak akkor lépnek tovább, ha az üzenetcsere megtörtént. Tanultuk ezt anno Operációs Rendszerekből.

     A másik üzenetküldési forma (ahogy azt már sejteni lehetett) a nem blokkolásos üzenetküldés. Ilyenkor jön a postás, bedobja a levelet a postaládába, majd továbbmegy. Amikor időm és kedvem lesz, majd kimegyek és ürítem a postaládát (egyenesen a kukába).

     A teljes projekt a következőképpen néz ki :

     Első processzor :

#include <string.h>
#include <xstatus.h>
#include <xparameters.h>
#include <xmbox.h>

#include <mb_interface.h>
#include <stdio.h>
#include "xutil.h"

#define MSGSIZ  1024
const char *role = "CPU0";

char data[];

float   conv_cycles_to_secs (u32 cycles);

u32 *sendmsg = (u32*) data;
u32  recvmsg[MSGSIZ/sizeof(u32)];
char *hello  = "HELO";

int main (void) 
{
    

    XMbox mbox;
    XMbox_Config *cfg;
    int status, i;
    u32 rcvd, sent, nbytes, ts;

    xil_printf ("--------------------------------------------------------------------\r\n");
    xil_printf ("---------------Dual Processor Producer Consumer Demo!---------------\r\n");
    xil_printf ("--------------------------------------------------------------------\r\n");
    xil_printf ("------- CPU0 sends data packets to CPU1 via an XPS Mailbox.---------\r\n");
    xil_printf ("--------------------------------------------------------------------\r\n");

    xil_printf ("(%s)\t: Starts.\r\n", role);
    xil_printf ("(%s)\t: Initializing the mailbox...", role);

    cfg = XMbox_LookupConfig (XPAR_MAILBOX_0_IF_0_DEVICE_ID);

    status = XMbox_CfgInitialize (&mbox, cfg, cfg->BaseAddress);
    if (status != XST_SUCCESS) {
        xil_printf ("failed!\r\n");
        return status;
    }

    xil_printf ("done!\r\n");

    /* First both processors rendezvous with each other. 
       They do this by exchanging HELO messages */

    /* Send the hello */
    xil_printf ("(%s)\t: Sending a HELO.\r\n", role);
    XMbox_WriteBlocking (&mbox, (u32*)hello, 4);
    xil_printf ("(%s)\t: Awaiting a HELO...", role);
    while (1) {
        XMbox_ReadBlocking (&mbox, recvmsg, 4);
        if (memcmp (hello, recvmsg, 4) == 0) 
            break;
    }
    xil_printf ("Got it!\r\n");

    /* Now exchange the data */
    for (i = 0; i < 5; i++) {

        xil_printf ("\r\n");
        xil_printf ("(%s)\t: Sending data packet (%d) -->\r\n", role, i);
        while (nbytes != MSGSIZ) {
            /* Write a message to the mbox */
            status = XMbox_Write (&mbox, sendmsg + (nbytes/sizeof(u32)), 
                                  MSGSIZ - nbytes, &sent);
            if (status != XST_FIFO_NO_ROOM)        
                nbytes += sent;
        }              
        xil_printf ("(%s)\t: Successfully sent %d bytes .\r\n", 
                role, MSGSIZ);
    }

    xil_printf ("(%s)\t: End of Demo.\r\n", role);
    xil_printf ("--------------------------------------------------------------------\r\n");
    return XST_SUCCESS;

}


     Második processzor :

#include <string.h>
#include <xstatus.h>
#include <xparameters.h>
#include <xmbox.h>
#include <mb_interface.h>
#include <stdio.h>

/************************** Constant Definitions *****************************/

#define MSGSIZ  1024


const char *role = "CPU1";

char data[];

u32 *sendmsg = (u32*) data;
u32  recvmsg[MSGSIZ/sizeof(u32)];
char *hello  = "HELO";

int main ()
{
    XMbox mbox;
    XMbox_Config *cfg;
    int status, i;
    u32 rcvd, sent, nbytes, ts;

//    xil_printf ("--------------------------------------------------------------------\r\n");
//    xil_printf ("Dual Processor Producer Consumer Demo!\r\n", role);
//    xil_printf ("--------------------------------------------------------------------\r\n");
//    xil_printf ("Desc -- CPU0 sends data packets to CPU1 via an XPS Mailbox.\r\n");
//    xil_printf ("--------------------------------------------------------------------\r\n");
//
//    xil_printf ("(%s)\t: Starts.\r\n", role);
//    xil_printf ("(%s)\t: Initializing the mailbox...", role);

    cfg = XMbox_LookupConfig (XPAR_MAILBOX_0_IF_1_DEVICE_ID);

    status = XMbox_CfgInitialize (&mbox, cfg, cfg->BaseAddress);
    if (status != XST_SUCCESS) {
//        xil_printf ("failed!\r\n");
        return status;
    }

//    xil_printf ("done!\r\n");

    /* First both processors rendezvous with each other. 
       They do this by exchanging HELO messages */

//    xil_printf ("(%s)\t: Awaiting a HELO...", role);
    while (1) {
        /* Try to recv a hello */
        XMbox_ReadBlocking (&mbox, recvmsg, 4);
        if (memcmp (hello, recvmsg, 4) == 0) 
            break;
    }
//    xil_printf ("Got it!\r\n");
    
    /* Send back a hello */
//    xil_printf ("(%s)\t: Sending back a HELO.\r\n", role);
    XMbox_WriteBlocking (&mbox, (u32*)hello, 4);

    /* Now exchange the data */
    for (i = 0; i < 5; i++) {

//        xil_printf ("\r\n");
//        xil_printf ("(%s)\t: Receiving data packet (%d) -->\r\n", role, i);
        nbytes = 0;
        while (nbytes != MSGSIZ) {
            /* Read a message from the mbox */
            status = XMbox_Read (&mbox, recvmsg + (nbytes/sizeof(u32)), 
                                 MSGSIZ - nbytes, &rcvd);
            if (status != XST_NO_DATA)
                nbytes += rcvd;
        }

//        xil_printf ("(%s)\t: Successfully rcvd %d bytes.\r\n", 
//                role, MSGSIZ);
        
        if (memcmp (recvmsg, data, MSGSIZ)) {
//            xil_printf ("(%s)\t: Error! Rcvd data does not match expected.\r\n", 
//                    role);
            return XST_FAILURE;
        }
 //       else
//            xil_printf ("(%s)\t: Success! Rcvd data does match expected.\r\n", 
//                    role);        
    }

//    xil_printf ("(%s)\t: End of Demo.\r\n", role);
//    xil_printf ("--------------------------------------------------------------------\r\n");
    return XST_SUCCESS; 
}


     Itt fontos megjegyezni, hogy a második processzorban a kiíratások mind ki vannak kommentelve, mivel a soros kommunikációs egységhez való hozzáférés még nincs implementálva. Oda kell majd a mutex, ami arra lesz jó, hogy mindkét proci hozzáférjen a soros eszközhöz anélkül, hogy egymást zavarnák. Ezen kívül azt is fontos megjegyezni, hogy a program nem az én kretivitásom terméke. Sajnos az eredetire mutató linkem már nincs.

     A mutex példát majd a következő bejegyzésben.

Megszületett

      Elkészült a robotom mechanikai terve. Sok hozzáfűznivalóm nincs. Egy subler kell hozzá, Inventor, meg egy kis kreativitás. Ha nincs kreatív ötletünk, akkor lopjunk :P ...



     Kb így fog kinézni a valóságban is, csak sokkal több kábel lesz rajta (sajna az Invetorban nincs kábeltervező...). Legkisebb járat 2mm-es. Gondolkodóba ejtett, hogy melyik CNC képes ilyen pontosságra. Lemezvastagság 4mm. Olyan kis aranyosnak néz ki ilyen vastag lábakkal...

      És most következzenek a részletek : Jobb láb :



     Majd a bal láb :



      Alapjában véve ugyanaz, mint a jobb oldali, csak tükrözve :P ... Test alsó része :



     Test felső része :



     Összeszerelve az alsó és felső rész :



     Erre már fel van "tervezve" az FPGA is (Nexys2), csak azért, hogy látható legyen, kb mekkora lesz (az FPGA kb 12*12 cm-es...)

     Remélem jövő héten elkészül, s már "rendes" képeket is tehetek fel róla...

     Addigis : többet nem tudok elmondani, kérem kapcsolja ki.

Kutyaistók vs. CustomIP

     Kemény meccs volt a mai... 11 órától 20:12-ig, (vagyis több mint 9 órát) azzal foglalkoztam, hogy készítsek egy szorzóáramkört VHDL-ben, s importáljam egy CustomIP-ként a Microblaze processzorhoz. De nem akárhogy. Úgy, hogy csak regisztercímzéssel menjen a beírás és a kiolvasás is. Ergó egyetlen portja sincs... Csak regiszterei.

     Recept a következő. Megírjuk a VHDL állományt a következő (vagy ehhez hasonló) módon:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;


entity mymux is
    Port ( a          : in  STD_LOGIC_VECTOR(31 downto 0);
              b          : in  STD_LOGIc_VECTOR(31 downto 0);
              result          : out STD_LOGIC_VECTOR(31 downto 0);
              clk            : in  STD_LOGIC);
end mymux;

architecture Behavioral of mymux is

begin

    countit: process(clk,a,b) 
        begin    
            if rising_edge(clk) then 
                result<=a*b;
            end if;    
        end process;
end Behavioral;


     Ha ezzel megvagyunk, akkor végignyomkodjuk a Hardware/Create or Import peripheral menüpont alatt előbukkanó ablakokat. Ezután megnyitjuk a júzer_logic.vhd állományt és kiegészítjük a következő részekkel:

architecture IMP of user_logic is

  --USER signal declarations added here, as needed for user logic
  COMPONENT mymux
    Port (    a          : in  STD_LOGIC_VECTOR(31 downto 0);
              b          : in  STD_LOGIc_VECTOR(31 downto 0);
              result     : out STD_LOGIC_VECTOR(31 downto 0);
              clk        : in  STD_LOGIC);
  END COMPONENT;
  signal out_value       :     STD_LOGIC_VECTOR(31 downto 0); 

      Bemásolunk a következő részt ugyanebbe a fájlba, csak egy kicsit lennebb:

  --USER logic implementation added here
  MITTUDOMEN : mymux
        port map
     (
        a                     => slv_reg0,
        b                     => slv_reg1,
        result                => out_value,
        clk                   => Bus2IP_Clk);

     Még egy lépés van hátra ebben az állományban. A kiíratás megbuherálása. Mert na... Mert azt úgy kell. mert hanem nem fog menni, viszont nem ad hibát. Csak idegesít. De nagyon...

 -- implement slave model software accessible register(s) read mux
  SLAVE_REG_READ_PROC : process( slv_reg_read_sel, slv_reg0, slv_reg1, slv_reg2, out_value ) is
  begin

    case slv_reg_read_sel is
      when "100" => slv_ip2bus_data <= slv_reg0;
      when "010" => slv_ip2bus_data <= slv_reg1;
      when "001" => slv_ip2bus_data <= "00000000000000000000000000000000" & out_value;
      when others => slv_ip2bus_data <= (others => '0');
    end case;

  end process SLAVE_REG_READ_PROC;


     Ezután már csak a java van hátra. Rescan User Repositories, majd mux berakni, rákötni az mp_plb buszra és megírni a programot, ami beüzemeli ezeket a prekonfigurált állományokat.  A forráskód, komment nélkül a következő:

#include "xparameters.h"

#include "stdio.h"

#include "xstatus.h"

#include "xuartlite.h"

#include "mux.h"

//====================================================

int main (void) {
unsigned int serial_data,mux_data;

  xil_printf("-- Entering main() --\r\n");
  xil_printf("Write the first number: ");
  serial_data = XUartLite_RecvByte(XPAR_RS232_PORT_BASEADDR);
  MUX_mWriteReg(XPAR_MUX_0_BASEADDR, MUX_SLV_REG0_OFFSET, serial_data);

  xil_printf("Write the second number: ");
  serial_data = XUartLite_RecvByte(XPAR_RS232_PORT_BASEADDR);
  MUX_mWriteReg(XPAR_MUX_0_BASEADDR, MUX_SLV_REG1_OFFSET, serial_data);
  
  mux_data = MUX_mReadReg(XPAR_MUX_0_BASEADDR, MUX_SLV_REG2_OFFSET);

  xil_printf("Mux return value : %d .\n",mux_data);

  xil_printf("-- Exiting main() --\r\n");
   return 0;
}


     Ha ez mind megvolt, akkor a következőt láthatjuk a soros konzol ablakban :


     ASCII "1" = 49, ASCII "2" = 50. S azt már mindenki tudja fejből, hogy 49*50 = 2450. S megint mindenki boldog. Csak szegény szarvas nem...

Microblaze lelkivilág...

     A mai bejegyzés egy kicsit rámutat a Microblaze softcore processzor lelkivilágának sötét bugyraira. Egy pár tipikus hibát szeretnék elemezni, ami szerintem egy idő után úgyis mindenkinél (aki aktivan foglalkozik a témával) elő fog jönni...

     Tehát. Adott a következő kód:

#include "xparameters.h"

#include "stdio.h"

#include "xutil.h"

#include "xuartlite.h"

#include "xuartlite_l.h"

#include "xintc_l.h"

#include "xgpio.h"

//====================================================
// Definitions
//====================================================

#define BAUDRATE 115200
#define USE_PARITY 0
#define PARITY_ODD 1
#define DATABITS 8

//====================================================
// GLOBAL VARIABLES
//====================================================
unsigned int counter = 0;
unsigned int c2 = 0;
int buchstabe;
XGpio leds;
XUartLite serial,second_serial;

//====================================================
// INTERRUPT HANDLER FUNCTION
//====================================================

void serial_interrupt_handler(void* addr_p)
{ 
    int empty;
        //XGpio_DiscreteWrite(&leds, 1, 255);
            empty = XUartLite_IsReceiveEmpty(XPAR_RS232_PORT_BASEADDR);
            if (empty == FALSE )
            buchstabe = XUartLite_RecvByte(XPAR_RS232_PORT_BASEADDR);
        //XGpio_DiscreteWrite(&leds, 1, buchstabe);
        xil_printf("::%c::",buchstabe);
}

void second_serial_interrupt_handler(void* addr_p)
{ 
    int empty;
        //XGpio_DiscreteWrite(&leds, 1, 255);
            empty = XUartLite_IsReceiveEmpty(XPAR_RS232_SECOND_BASEADDR);
            if (empty == FALSE )
            buchstabe = XUartLite_RecvByte(XPAR_RS232_SECOND_BASEADDR);
        //XGpio_DiscreteWrite(&leds, 1, buchstabe);
        xil_printf("XX%cXX",buchstabe);
}

//====================================================
// Delay
//====================================================

void ussleep(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<100; j++);
}


void mssleep(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<100; j++)
        ussleep(1);
}

void ssleep(unsigned int delay)
{
unsigned int j, i; 

for(i=0; i<delay; i++)
   for(j=0; j<100; j++)
        mssleep(1);
}


int main()
{
Xuint8 hello[4];
unsigned int i;
//-----------------
hello[0]=72;
hello[1]=69;
hello[2]=76;
hello[3]=79;
//-----------------
XUartLite_Config UART_Configuration;
UART_Configuration.DeviceId = XPAR_RS232_PORT_DEVICE_ID;
UART_Configuration.RegBaseAddr = XPAR_RS232_PORT_BASEADDR;
UART_Configuration.BaudRate = BAUDRATE;
UART_Configuration.UseParity = USE_PARITY;
UART_Configuration.ParityOdd = PARITY_ODD;
UART_Configuration.DataBits = DATABITS; 

XUartLite_Config UART_Second_Configuration;
UART_Second_Configuration.DeviceId = XPAR_RS232_SECOND_DEVICE_ID;
UART_Second_Configuration.RegBaseAddr = XPAR_RS232_SECOND_BASEADDR;
UART_Second_Configuration.BaudRate = BAUDRATE;
UART_Second_Configuration.UseParity = USE_PARITY;
UART_Second_Configuration.ParityOdd = PARITY_ODD;
UART_Second_Configuration.DataBits = DATABITS; 


 //UART init:
XUartLite_CfgInitialize (&serial, &UART_Configuration, XPAR_RS232_PORT_BASEADDR) ;
XUartLite_CfgInitialize (&second_serial, &UART_Configuration, XPAR_RS232_SECOND_BASEADDR) ;

xil_printf("First and Second UART configured to send any data.\n");
//-----------------


    XGpio_Initialize(&leds, XPAR_LEDS_8BIT_DEVICE_ID);
    XGpio_SetDataDirection(&leds, 1,0x0);  
    XGpio_DiscreteWrite(&leds, 1, 1);
    
    XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
                                 XPAR_XPS_INTC_0_RS232_PORT_INTERRUPT_INTR,
                                 (XInterruptHandler)serial_interrupt_handler,
                                 (void *)XPAR_RS232_PORT_BASEADDR);    
                                 
    XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
                                 XPAR_XPS_INTC_0_RS232_SECOND_INTERRUPT_INTR,
                                 (XInterruptHandler)second_serial_interrupt_handler,
                                 (void *)XPAR_RS232_SECOND_BASEADDR);                                     
    XGpio_DiscreteWrite(&leds, 1, 2);
    
    XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
    XGpio_DiscreteWrite(&leds, 1, 3);
    
    XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, XPAR_RS232_PORT_INTERRUPT_MASK);
    XGpio_DiscreteWrite(&leds, 1, 4);
    
    XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, XPAR_RS232_SECOND_INTERRUPT_MASK);
    XGpio_DiscreteWrite(&leds, 1, 5);
    
    //XUartLite serial,second_serial;
    XUartLite_Initialize(&serial,XPAR_RS232_PORT_DEVICE_ID);
    XUartLite_Initialize(&second_serial,XPAR_RS232_SECOND_DEVICE_ID);
    XGpio_DiscreteWrite(&leds, 1, 6);
    
    XUartLite_SetRecvHandler(&serial,
                                     (XUartLite_Handler)  serial_interrupt_handler,  
                                     (void *)XPAR_RS232_PORT_BASEADDR);  
    XUartLite_SetRecvHandler(&second_serial,
                                     (XUartLite_Handler)  second_serial_interrupt_handler,  
                                     (void *)XPAR_RS232_SECOND_BASEADDR);  
    XGpio_DiscreteWrite(&leds, 1, 7);
    
    XUartLite_EnableInterrupt(&serial);  
    XUartLite_EnableInterrupt(&second_serial);  
    XGpio_DiscreteWrite(&leds, 1, 8);
    
    microblaze_enable_interrupts();
    XGpio_DiscreteWrite(&leds, 1, 0);
    
    //------------------------------------
        while(XUartLite_IsSending(&serial));
        XUartLite_Send(&serial, (Xuint8*)(hello), 4);
        while(XUartLite_IsSending(&second_serial));
        XUartLite_Send(&second_serial, (Xuint8*)(hello), 4);
        //XGpio_DiscreteWrite(&leds, 1, (Xuint8)i);
    //------------------------------------
    while(1);
} 

     A program nem nagyon hosszú és szerintem könnyű is megérteni. Lényeg belőle : adott két soros bemenet. Azt szeretnénk elérni, hogy az egyik soros bemeneten érkező adat irányítsa a ledeket (8 biten beküldött karakter 8 ledet... semmi extra), a másik soros porton küldött adat pedig vissza legyen küldve az elsőn (vagyis egy sima átirányítás). Az elgondolásom a következő volt : írok két interrupt kezelő függvényt, egyet az első számú soros portra (erre megy a xil_printf() függvény is), ami a ledeket villogtatja, majd írok egy másik szintén megszakítás kezelő függvényt, amiben csak továbbküldöm az érkezett adatokat xil_printf segítségével.

     Induljunk ki abból, hogy a hardver kompozíció hibátlan, viszont a kód hibás. Hogyan jövünk rá, hogy mi a hiba? Az egyik legegyszerűbb változat az, amit Balázs is mondott, villogtatjuk a ledeket, mikor elértünk valahová. Ez megvolt. A program kiküldte a HELO üzenetet mindkét soros terminálra. És valami mégsem megy. Aki most megkapja a hibát, továbbolvasás nélkül, annak van egy söre... ;)

     Amit még tudni kell, hogy az interrupt kontrollerben prioritás szerint rendezni lehet a megszakítás kéréseket, annak érdekében, hogy egy alacsonyabb szintű megszakítás kiszolgálását félbe lehessen hagyni, ha egy magasabb szintű megszakítás kérés érkezik.

     Mégegy részlet a programbol, amire szükség lesz a mai debuggoláshoz a következő:

#define XPAR_INTC_SINGLE_BASEADDR 0x81800000
#define XPAR_INTC_SINGLE_HIGHADDR 0x8180FFFF
#define XPAR_INTC_SINGLE_DEVICE_ID XPAR_XPS_INTC_0_DEVICE_ID
#define XPAR_RS232_PORT_INTERRUPT_MASK 0X000001
#define XPAR_XPS_INTC_0_RS232_PORT_INTERRUPT_INTR 0
#define XPAR_RS232_SECOND_INTERRUPT_MASK 0X000002
#define XPAR_XPS_INTC_0_RS232_SECOND_INTERRUPT_INTR 1

     Miután háromszor újraírtam az interrupt kezelő függvényt kezdtem gyanakodni, hogy a hiba valahol máshol leledzik, és sorrol sorra megvizsgáltam a teljes programot. Legelső szarvashiba a következő volt:

 //UART init:
XUartLite_CfgInitialize (&serial, &UART_Configuration, XPAR_RS232_PORT_BASEADDR) ;
XUartLite_CfgInitialize (&second_serial, &UART_Configuration, XPAR_RS232_SECOND_BASEADDR) ;

      Minek kínlódtam én azzal, hogy két soros eszközt definiáljak, ha az inicializálásnál mindkettőhöz az &UART_Configuration prekonfigurált beállítás került? Ennek kellett egy gyors csere. Tovább tesztelve a progit rájöttem, hogy csak egyetlen interrupt aktiválódik, bármennyire is kínozom... S mindig az, amelyiket másodiknak írom be... Nem az számít, hogy melyik magasabb prioritású, hanem az, hogy a forráskódban melyiknek a maszkját írtam később... Lássuk a kódot megint:

    XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, XPAR_RS232_PORT_INTERRUPT_MASK);
    XGpio_DiscreteWrite(&leds, 1, 4);
    
    XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR, XPAR_RS232_SECOND_INTERRUPT_MASK);
    XGpio_DiscreteWrite(&leds, 1, 5);

     Aztán ide is megjött a megvilágosodás. Ha én veszek egy lapot s ráírom, hogy 0x00000001 majd letörlök mindent a lapról és ráírom, hogy 0x00000002, akkor a másodikat fogja látni mindenki, aki benéz oda. Binárisan felbontva a  feladatot :
Az "1" binárisan 8 biten: 00000001
A  "2" binárisan 8 biten: 00000010
Ez egy megszakítás maszk. Amikor egy megszakítás érkezik a megszakítás vonalra, a CPU megnézi, hogy szabad-e attól az egységtől megszakítást fogadni, vagy sem. S ezt az az egyes határozza meg, amit két sorral fennebb leírtam. Vagyis ha én 00000001-et írok, akkor csak az első soros porttól jöhet megszakítás. Ha 00000010-et írok, akkor csak a második soros port megszakításait kezelem. Ha viszont 00000011-et írok, akkor mindkét megszakítás kérést engedélyezem.

      Hibák kijavítva. Az eredmény az lett, amit vártam. Villogtak a ledek, jöttek a számok. Mindenki boldog.

Love at first sight

     A mai bejegyzés szervó vezérlés elméletet valamint egy FPGA-ra megvalósított változatot fog tárgyalni. Szükséges eszközök :
  • Futaba S3003 szervó
  • Nexys 2 FPGA fejlesztőlap

  • Külső tápegység 

  • PMOD CON3

  • Xilinx SDK
  • Firefox vagy hasonló böngésző
  • Photoshop
  • egy darab papír, olló és ragasztószalag


     Legelső lépés, keressünk rá google-ben, hogyan kell vezérelni egy szervómotrot. A szervómotor egy belső visszacsatoló- és vezérlőkörrel rendelkező kis motor, ami azért jó, mert nem kell mi azzal foglalkozzunk, hogy ellenőrizzük, vajon eljutott már a megfelelő pozícióba, vagy sem. (Persze most sokan felhördülnek, hogy terhelés mellett lehet olyan, hogy a fogaskerekek elcsúsznak egymás mellett, de kit érdekel az ilyen? :P)

     Vezérlése egy PWM jellel valósítható meg. Modelltől és tipustól függően más és más értékekkel. Az általánosan elfogadott változat a következő : 20 ms-os periodussal, perióduson belül 1-2 ms közötti kitöltési tényezővel juttathatjuk el az egyik végállásból a másik végállásba. 1.5 ms-os kitöltési tényező a középállás.

      Találtam sok hasonló ábrát, de a lényegük mindnek ugyanez.


     Frekvencia, kitöltési tényező, ennyi. Következő lépés megkeresni a szervó lábkiosztását. Ez is legtöbb szervónál ugyanaz, hogy könnyebben lehessen cserélni őket.  Fehér kábelen megy a jel, vagyis a kitöltési tényező, piros a pozitív, fekete a föld.


     Fogjuk, s rányomjuk a PMODCon3-as csatlakozóra, majd a csatlakozót összekötjük a labortáppal. Labortápon beállítunk valami 4.6 és 6 volt közötti értéket majd nekikezdünk programozni.

     Először megírni a customIP-t, mivel az SDK alapból nincs felszerelve ilyen elemekkel.

entity my_pwm is
    Port ( clk               : in  STD_LOGIC;
           max_count         : in  STD_LOGIC_VECTOR (31 downto 0);
            on_time           : in  STD_LOGIC_VECTOR (31 downto 0);
           pwm_out           : out STD_LOGIC;
              reset             : in  STD_LOGIC
              );
end my_pwm;

architecture Behavioral of my_pwm is

signal counter               : std_logic_vector(31 downto 0):=(others=>'0');


begin

    PwmCounter: process(clk, max_count)
        begin
                if clk'event and clk='1' then
                    if counter >= max_count then
                        counter <= (others=>'0');
                    else
                        counter <= counter + '1';    
                    end if;        
                end if;
        end process;

    PWM: process(counter, on_time, reset )
        begin
                if (counter<on_time) and reset = '0' then 
                    pwm_out <= '1';
                else     
                    pwm_out <= '0';
                end if;    
        end process;
        
    
   
end Behavioral;


     A PwmCounter processz segitségével generáljuk a megfelelő frekvenciát (50 Hz, vagyis az egy másodpercnyi órajelet 50 részre osztjuk fel ahhoz, hogy megkapjuk a max_count értékét). Mivel az én órajelem 50.000.000 Hz-es, igy nálam a max_count értéke 1.000.000 lesz.

    A PWM processzus felelős a kitöltési tényező beállításáért. Amíg a számláló értéke kisebb, mint az on_time értéke, addig logikai "1" van a kimeneten, amikor meg nagyobb, akkor logikai "0".

    Az így elkészült VHDL állományt importáljuk a Xilinx SDK fejlesztőkörnyezetbe, mint egy CustomIP. Ha ügyesek voltunk, meg is jelenik a többi IP alatt.


     Használatához szükség van a következő sorokra (teljes programot nem másolom be...)

//(...)
#include "servo_controller.h"


//====================================================
//             Definitions
#define SERVO_ADDRESS XPAR_SERVO_CONTROLLER_0_BASEADDR
#define FPGA_CLK 50000000 //50 MHz

unsigned int max_count=0;
unsigned int on_time=0;

//             END OF USER DEFINITIONS
//====================================================

//====================================================
//                    FUNCTIONS
void Set_Servo_Position(unsigned int value)
{
    if (value<=0)
        max_count=FPGA_CLK/50;
      on_time = 20000+(value*400);
   SERVO_CONTROLLER_mWriteSlaveReg0(SERVO_ADDRESS, 0, max_count);
    SERVO_CONTROLLER_mWriteSlaveReg1(SERVO_ADDRESS, 0, on_time); 
}
//(...)
//            END OF USER FUNCTIONS
//====================================================

 
int main (void) 
{
//(...)

    while(1)
    {
        xil_printf("Servo Position Value = 2\n");
        Set_Servo_Position(0);
        sleep(80);
        
        xil_printf("Servo Position Value = 115\n");
        Set_Servo_Position(115);
        sleep(50);

        xil_printf("Servo Position Value = 230\n");
        Set_Servo_Position(230);
        sleep(50);
    }
   return 0;
}


      Itt a programba beírt értékek nem teljesen találnak azzal az 1-2 ms-os leírással... Viszont próbálkozni kell... Oszcilloszkóp híján a szervó motoron. Nagyon kooperatív volt a kicsike...

      Jujj, majdnem elfelejtettem... Olló, papír, ragasztó. Kivágunk a papírból egy nyíl formájú darabot (akinek nem megy magától, az nyomtathat wordban nagy nyilat, s azt körbevágja...), aminek a hátára mindkét oldalán tapadós ragasztószalagot teszünk és felragasztjuk a szervó fejére. Ha ezzel megvagyunk és a program is működik, akkor a következő élményben lehet részünk :




     Akinek elsőre nem megy, az még ne adja fel. Kicsit nehéz eltalálni a szervónak a lelkivilágát, viszont nem lehetetlen...

Inverz Kinematika V2.0

      Elkészült. Végre. Igaz ráment a mai napom, meg a tegnapi, de legalább elkészült. Közel hetvenezer karakterleütés (az osztályok megírását is beleértve). Egy kicsit most fel fogom boncolni a programot.

     Kezdjük a legtetején.

     Van egy osztályunk, aminek az a neve, hogy PokTest. Ebben vannak tárolva a középre kirajzolt póknak a koordinátái, valamint ebbe van összefogva a hat pókláb. Lehet ugyanide lesz betéve még a póknak a feje is, ami szintén egy lábból lesz példányosítva. Legfontosabb metódusa a test kirajzolása, valamint a lábak végpontjainak újraszámolása.

     Eggyel lennebbi szint a Láb. Ez csak pár sor, viszont szükséges volt azért, hogy a két különböző nézetet (fentről és oldalról) össze tudjam fogni egy helyre.

        public Lab(Nezet oldal, Nezet fent)
        {
            oldalrol = oldal;
            felulrol = fent;
        }

     Ez a Lábnak a konstruktora. Két nézetet kell megadni. Egy oldalnézetet és egy felülnézetet.

     Mégeggyel lennebb van a nézet, mint osztály. A nézetnek már több tulajdonsága illetve több metódusa van.

        public int lfx, lfy;//lab-felso-x, lab-felso-y
        public int lkx, lky;//lab-kozepso
        public int lax, lay;//lab-also
        public static int L1 = 50;
        public static int L2 = 75;
        public double teta1, teta2, teta3;//szervok szogei
        public double teta1szog, teta2szog, teta3szog;
        public double eltolas1, eltolas2, eltolas3; // szervok pozicionalasabol adodik
        public double alfa;//az a szog, amit a vegpont es kezdopont osszekotese a kezdopont sikjaval bezar.
        public int rajzx, rajzy;
        public TextBox t1, t2, t3, t4, t5;
        public bool baloldali;
        public Nezet parja;
        public Graphics testRajz;
        private Pen myPen, whitePen, blackPen;

     Ezek azok az adattagok, amiket itt inicializálni kell (magyar kifejezéssel élve kezdőértéket kell adni nekik). A változónevek számomra jelentenek valamit, más számára nem hiszem. Például lkx nálam a láb középső forgójának X koordinátáját jelenti. Ugyanígy a többi. Teta1 az a szög, amit a felkar a vállal bezár (vagyis amit a robotkar felső része a fix pont által meghatározott síkkal bezár). Teta2 az alkar és felkar által bezárt szögek. Teta3 pedig a kinyujtott karunk arcunkkal bezárt szöge(sajnos ennél jobban nem tudom elmagyarázni itt...) Minderre azért van szükségem, mert ezek az értékek szervókkal lesznek beállítva és a szervóknak ki kell küldeni valamilyen vezérlőjelet. Erre majd később visszatérünk.

    Teta1 és teta2 az oldalnézeten számítódik ki, teta3 pedig a felülnézeten.

     A Nezet osztályban elég sok metódus van, ami megérne pár sor kommentet. Van Transzponálás, van Tukrozes, van Visszatukrozes, van Kiszamol_OldalNezet, Kiszamol_FelulNezet, Mozgat, LabTorol, LabKirajzol meg Rajzol. Egyet másolok ide be ezek közül. A legbonyolultabbat.

        public void Kiszamol_OldalNezet()
        {
            if (baloldali)
            {
                Tukroz(rajzx);
                Transzponal(0, rajzy); //eltoljuk a kart az origoba.

                alfa = Math.Atan(lay / (double)lax);

                teta1 = alfa - Math.Acos((double)(lax * lax + lay * lay + L1 * L1 - L2 * L2) / (double)(2 * L1 * Math.Sqrt(lax * lax + lay * lay)));
                teta2 = (double)Math.PI - Math.Acos((double)(L1 * L1 + L2 * L2 - lax * lax - lay * lay) / (double)(2 * L1 * L2));

                teta1szog = teta1 * 180 / (double)Math.PI;
                teta2szog = (teta2 + eltolas2) * 180 / (double)Math.PI;


                try
                {
                    t1.Text = teta1szog.ToString();
                    t2.Text = teta2szog.ToString();

                    t4.Text = lax.ToString();
                    t5.Text = lay.ToString();
                }
                catch
                {
                    //
                }

                lkx = lfx + (int)(Math.Cos(teta1+eltolas1) * L1);
                lky = lfy + (int)(Math.Sin(teta1+eltolas1) * L1);

                parja.lkx = parja.lfx + (int)(Math.Cos(parja.teta3+parja.eltolas3) * lkx);
                parja.lky = parja.lfy + (int)(Math.Sin(parja.teta3+parja.eltolas3) * lkx);

                parja.lax = parja.lfx + (int)(Math.Cos(parja.teta3+parja.eltolas3) * lax);
                parja.lay = parja.lfy + (int)(Math.Sin(parja.teta3+parja.eltolas3) * lax);

                Transzponal(0, -rajzy);
                VisszaTukroz(rajzx);

            }
            else
            {
                Transzponal(rajzx, rajzy); //eltoljuk a kart az origoba.

                alfa = Math.Atan(lay / (double)lax);

                teta1 = alfa - Math.Acos((double)(lax * lax + lay * lay + L1 * L1 - L2 * L2) / (double)(2 * L1 * Math.Sqrt(lax * lax + lay * lay)));
                teta2 = (double)Math.PI - Math.Acos((double)(L1 * L1 + L2 * L2 - lax * lax - lay * lay) / (double)(2 * L1 * L2));

                teta1szog = teta1 * 180 / (double)Math.PI;
                teta2szog = (teta2 + eltolas2) * 180 / (double)Math.PI;


                try
                {
                    t1.Text = teta1szog.ToString();
                    t2.Text = teta2szog.ToString();

                    t4.Text = lax.ToString();
                    t5.Text = lay.ToString();
                }
                catch
                {
                    //
                }

                lkx = lfx + (int)(Math.Cos(teta1) * L1);
                lky = lfy + (int)(Math.Sin(teta1) * L1);

                parja.lkx = parja.lfx + (int)(Math.Cos(parja.teta3) * lkx);
                parja.lky = parja.lfy + (int)(Math.Sin(parja.teta3) * lkx);

                parja.lax = parja.lfx + (int)(Math.Cos(parja.teta3) * lax);
                parja.lay = parja.lfy + (int)(Math.Sin(parja.teta3) * lax);

                Transzponal(-rajzx, -rajzy);
            }
        }

     Legelőször megvizsgáljuk, hogy bal vagy jobb lábról van-e szó, mivel a kettőt különbözőképpen kell számolni. A baloldalit tükrözni meg transzponálni, hogy a pozitív irányba nézzen, illetve, hogy a kezdőpont a (0,0) legyen. Aztán ki kell számolni pár szöget. Ezekről volt már szó korábban. A szögeket radiánból fokba alakítani, majd kiszámolni a szögek függvényében az új koordinátákat, nem csak a rajzon belül, hanem a neki megfelelő másik nézetben is az új érték után igazítani a paramétereket. Erről a részről szólnak a parja.lax, illetve parja.lay kezdetű sorok. Ezután következik a visszaalakítás rajznak megfelelő koordinátarendszerbe, illetve a kirajzolás. Persze az valahol máshol van megírva.

     Ennél a Nézet osztálynál lennebb már csak a Pont osztály van, amit franc tudja, hogy minek írtam meg, mert nem használom semmire. Lagalább nem töltöttem vele sok időt, amig elkészült.

     Ezen kívül ott van a main() valamint a grafikus felület megtervezése. A mainhez sokmindent nem fűznék hozzá, ott inkább a már megírt függvényeket hívogattam meg paramétereztem. A grafikus felület meg csak gomb-behuzogatás.

     S ha mindent jól csináltunk, a következő eredményhez jutunk :



     Az X és Y értékek frissítése még nem az igazi, viszont a programot én közel befejezettnek tekintem. Talán dobok még rá egy soros portot, s összekötöm direktben a FPGA-val. Persze csak majd robot-tesztelési fázisban. De addig még van egy kicsi.

Inverz Kinematika

     Jó reggelt mindenkinek. Gyönyörű napra ébredtünk. A nap hétágra süt, a madarak meg torkukszakadtából csiripelnek. Tegnap éjjel befejeztem az inverz kinematika programom első verzióját. Persze dokumentálni még nem igazán volt időm. Viszont van róla videóm. ;)



     A forráskódot még ki kell egy kicsit kommentezzem, hogy ne csak nekem legyen érthető.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Inverz_k
{
    public partial class Form1 : Form
    {
        System.Drawing.Pen whitePen; //Pen - ezzel rajzoljuk a vonalakat
        System.Drawing.Pen myPen;    // azert kell ketto belole, hogy le is tudjuk torolni az addigi rajzot.
        System.Drawing.Brush myBrush;
        System.Drawing.Graphics pg,pg2; // ez a ket panel, amire rajzolunk
        int x0, y0, x1, y1, x2, y2; // a robotkarunk harom pontja. 
        int L1, L2;                 // a robotkar karjainak hossza
        int[] a,b,c,d,e,f;          // ezekben tarolom a kirajzolt objektum koordinatait
        double teta1, teta2, teta3; // ezekben taroljuk a szervok elfordulasi szoget radianban
        double teta1szog, teta2szog, teta3szog;  // itt meg a szogeket fokokban
        double valosteta1, valosteta2, valosteta3; // erre azert van szukseg, mert lehet, hogy alapbol eltolas van a szogben
        double alfa; // ez a szog, amit a robotkar vegpontja a kezdopont sikjaval bezar.
        double atfogo; // ez meg a kezdopont-vegpontot osszekoto szakasz hossza
        public Form1()
        {
            InitializeComponent();
            whitePen = new Pen(System.Drawing.Color.White,4); // feher ceruza. Ezzel torlunk
            myPen = new Pen(System.Drawing.Color.Red,4);      // piros ceruza. Ezzel rajzolunk
            myBrush = new SolidBrush(System.Drawing.Color.Black); // ez nemtudom mire van :-S
            a = new int[2]; //
            b = new int[2]; //
            c = new int[2]; //itt megmondjuk, hogy a pontoknak ket komponensuk van. Egy x es egy Y 
            d = new int[2]; // vagy felulnezetbol egy X es egy Z
            e = new int[2]; // vagy valami hasonlo
            f = new int[2]; //
            pg = panel1.CreateGraphics(); //bal oldali panel. erre rajzolunk
            pg2 = panel2.CreateGraphics(); // jobb oldali panel
            L1 = 100; // femur hossza. 
            L2 = 150; // tibia hossza (ask google)
            x0 = 0;   // kezdopont X
            y0 = panel1.Height / 3; // kezdopont Y
            x1 = x0 + L1;
            y1 = y0;
            x2 = x1;
            y2 = y1 + L2;
            a[0] = x0;      //beallitjuk a koordinatatengely x koordinatajat
            a[1] = y0;      //beallitjuk a koordinatatengely y koordinatajat
            b[0] = x1;
            b[1] = y1;
            c[0] = x2;
            c[1] = y2;
            d[0] = 0;
            d[1] = panel2.Height / 2;
            e[0] = b[0];
            e[1] = d[1];
            f[0] = c[0];
            f[1] = e[1];
//            k_transz(x0, y0);
        }
        private void k_transz(int x, int y) // transzformaciora azert van szukseg, mert a kepletem
        {                                   //ugy szol, hogy az alap a 0,0 pontban helyezkedik el
            a[0] -= x;      //beallitjuk a koordinatatengely x koordinatajat
            a[1] -= y;      //beallitjuk a koordinatatengely y koordinatajat
            b[0] -= x;
            b[1] -= y;
            c[0] -= x;
            c[1] -= y;
        }

        private void draw(Pen p)
        {
            try
            {
                k_transz(x0, y0); //transzponalunk
                calculate();      // szamolunk
                k_transz((-x0), (-y0)); // visszatranszponalunk

                //itt jon a sok sok rajzolas. Vonalak, korok, miegymas

                pg.DrawEllipse(p, a[0] - 5, a[1] - 5, 10, 10);
                pg.DrawLine(p, a[0], a[1], b[0], b[1]);
                pg.DrawEllipse(p, b[0] - 5, b[1] - 5, 10, 10);
                pg.DrawLine(p, b[0], b[1], c[0], c[1]);
                pg.DrawEllipse(p, c[0] - 5, c[1] - 5, 10, 10);

                pg2.DrawEllipse(p, d[0] - 5, d[1] - 5, 10, 10);
                pg2.DrawLine(p, d[0], d[1], e[0], e[1]);
                pg2.DrawEllipse(p, e[0] - 5, e[1] - 5, 10, 10);
                pg2.DrawLine(p, e[0], e[1], f[0], f[1]);
                pg2.DrawEllipse(p, f[0] - 5, f[1] - 5, 10, 10);

            }
            catch
            { 
                //hibakezeles. Nincs.
            }
        }

        private void calculate()
        { 
            //itt szamoljuk ki, hogy az egyes reszek milyen szogben kell alljanak.
            //elso ket sor hetedikes matek
            atfogo = Math.Sqrt(c[0]*c[0]+c[1]*c[1]);
            alfa = Math.Atan(c[1]/(double)c[0]);

            //koszinusz tetel, MIT OCV-ben megtalalhato a leirasa.
            teta1 = alfa - Math.Acos((double)(c[0] * c[0] + c[1] * c[1] + L1 * L1 - L2 * L2) / (double)(2 * L1 * Math.Sqrt(c[0]*c[0]+c[1]*c[1])));
            teta2 = (double)Math.PI - Math.Acos((double)(L1 * L1 + L2 * L2 - c[0] * c[0] - c[1] * c[1]) / (double)(2 * L1 * L2));

            //atalakitas radianbol fokba
            teta1szog = teta1 * 180 / (double)Math.PI;
            teta2szog = teta2 * 180 / (double)Math.PI;

            //eltolasok kiszamitasa
            valosteta1 = teta1szog;
            valosteta2 = teta2szog - 90;

            //eredmeny kiiratasa
            tb_teta1.Text = valosteta1.ToString();
            tb_teta2.Text = valosteta2.ToString();

            //pontok poziciojanak kiszamitasa
            b[0] = a[0] + (int)(Math.Cos(teta1) * L1);
            b[1] = a[1] + (int)(Math.Sin(teta1) * L1);

            e[0] = d[0] + (int)(Math.Cos(teta3) * b[0]);
            e[1] = d[1] + (int)(Math.Sin(teta3) * b[0]);

            f[0] = d[0] + (int)(Math.Cos(teta3) * c[0]);
            f[1] = d[1] + (int)(Math.Sin(teta3) * c[0]);

        }

        //itt kovetkezik egy par gomb esemeny. Ha kattintunk a felfele vagy lefele vagy
        //jobbra-balra nyilakra, akkor a megfelelo erteket noveljuk, majd ujrarajzoljuk 
        private void b_fel_Click(object sender, EventArgs e)
        {
            draw(whitePen); //letoroljuk a rajzlapot
            c[1] -= 1;      //frissitjuk az erteket
            draw(myPen);    //ujrarajzoljuk
        }

        private void b_le_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            c[1] += 1;
            draw(myPen);
        }

        private void b_balra_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            c[0] -= 1; 
            draw(myPen);
        }

        private void b_jobbra_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            c[0] += 1;
            draw(myPen);
        }

        //egeresemeny. Ha lenyomott egergombot huzom a panel folott, akkor jon ez. 
        //ez a bal oldali rajzlapon
        private void panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left) //le van-e nyomva az eger?
            {
                draw(whitePen);//torol

                c[0] = e.X;  //frissit
                c[1] = e.Y;

                draw(myPen);   //rajzol
            }
        }

        //egeresemeny jobb oldali rajzlapon
        private void panel2_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                draw(whitePen);

                teta3 = Math.Atan((double)(e.Y-d[1]) / (double)(e.X-d[0])); // szamolunk
                c[0] = (int)Math.Sqrt((e.X)*(e.X)+(e.Y-d[1])*(e.Y-d[1])); //frissitunk
                teta3szog = teta3 * 180 / (double)Math.PI;
                tb_teta3.Text = teta3szog.ToString();
                draw(myPen);//rajzolunk
            }
        }

        //megint egy par gomb, ezek a jobboldaliak. 
        //itt van egy kis kerekitesi hiba, vagyis ha felmegyek es utana le, nem jutok vissza a 
        //kiindulopontba. Ez a tizedesresz elhanyagolasa miatt van. 
        private void b_fel2_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            f[1] -= 10;
            c[0] = (int)Math.Sqrt((f[0]) * (f[0]) + (f[1] - d[1]) * (f[1] - d[1]));//itt a kerekites.
            teta3 = Math.Atan((double)(f[1] - d[1]) / (double)(f[0] - d[0]));
            draw(myPen);
        }

        private void b_le2_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            f[1] += 10;
            c[0] = (int)Math.Sqrt((f[0]) * (f[0]) + (f[1] - d[1]) * (f[1] - d[1]));
            teta3 = Math.Atan((double)(f[1] - d[1]) / (double)(f[0] - d[0]));
            draw(myPen);
        }

        private void b_balra2_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            f[0] -= 10;
            c[0] = (int)Math.Sqrt((f[0]) * (f[0]) + (f[1] - d[1]) * (f[1] - d[1]));
            teta3 = Math.Atan((double)(f[1] - d[1]) / (double)(f[0] - d[0]));
            draw(myPen);
        }

        private void b_jobbra2_Click(object sender, EventArgs e)
        {
            draw(whitePen);
            f[0] += 10;
            c[0] = (int)Math.Sqrt((f[0]) * (f[0]) + (f[1] - d[1]) * (f[1] - d[1]));
            teta3 = Math.Atan((double)(f[1] - d[1]) / (double)(f[0] - d[0]));
            draw(myPen);
        }
    }
}




    Úgy érzem, hogy a program elég jól sikerült ahhoz, hogy érdemes legyen vele még egy kicsit foglalkozni. Arra gondoltam, hogy elkészítem Karé Halvorsen excel táblázatának a megfelelőjét. Persze az nem kis munka, meg nem kis odafigyelés. Az említett excel táblázat nem egy akármilyen táblázat. Tele van macro-val meg minden földi jóval.

     A tábla segitségével előre le tud gyártani bizonyos mozgás-szekvenciákat, amiket majd átküld a szervó vezérlőegységének, ami visszajátssza azt. Rengeteg videója van fent a srácnak youtubeon, azóta készített más robotokat is (leghíresebb az A-Pod, ami olyan, mint egy hangya...) A kedvencemet megosztanám itt is...



Ha nagy leszek, én is ilyet akarok. Csak sokkal szebbet ;)
Return top