/* * c't-Bot * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your * option) any later version. * This program is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA. * */ /*! @file behaviour_remotecall_behaviour.c * @brief ruft auf ein Kommando hin andere Verhalten auf und bestaetigt dann ihre Ausfuehrung * * @author Benjamin Benz (bbe@heise.de) * @date 07.12.06 */ #include "bot-logic/bot-logik.h" #ifdef BEHAVIOUR_REMOTECALL_AVAILABLE #include #include #include #include "log.h" #include "command.h" #include "bot-logic/remote_calls.h" #define REMOTE_CALL_IDLE 0 #define REMOTE_CALL_SCHEDULED 1 #define REMOTE_CALL_RUNNING 2 /*! Uebergabevariable fuer Remotecall-Verhalten */ static uint8 running_behaviour =REMOTE_CALL_IDLE; static uint8 function_id = 255; static uint8 parameter_count = 0; /*!< Anzahl der Paramter (ohne Zeiger auf Aufrufer) */ static uint8 parameter_data[8] = {0}; /*!< Hier liegen die eigentlichen Parameter, derzeit brauchen wir maximal 8 Byte (2 floats, 4 (u)int16 oder 4 (u)int8 */ #ifdef MCU static uint8 parameter_length[REMOTE_CALL_MAX_PARAM] = {0}; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */ #else static uint8* parameter_length = NULL; /*!< Hier speichern wir die Laenge der jeweiligen Parameter */ #endif #ifdef MCU #include #else #define PROGMEM // Alibideklaration hat keine Funktion, verhindert aber eine Warning #define strcmp_P strcmp // Auf dem PC gibt es keinen Flash, also auch kein eigenes Compare #endif /*! * Hier muessen alle Boten-Funktionen rein, die Remote aufgerufen werden sollen * Diese stoßen dann das zugehoerige Verhalten an * Ein Eintrag erfolgt so: * PREPARE_REMOTE_CALL(BOTENFUNKTION,NUMBER_OF_PARAMS, STRING DER DIE PARAMETER BESCHREIBT,laenge der jeweiligen Parameter in Byte) * * Alle Botenfunktionen muessen folgendem Schema entsprechen * void bot_xxx(Behaviour_t * caller, ...); * * Erklaerung am Bsp: * PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4), * Name der Botenfunktion --^ ^ ^ ^ ^ * Anzahl der Parameter --------- | | | * Beschreibung der Parameter -------- | | * Anzahl der Bytes Parameter 1 ------------------------ | * Anzahl der Bytes Parameter 2 --------------------------- * * Zur Info: * 1 Byte brauchen: uint8, int8,char * 2 Byte brauchen: uint16, int16 * 4 Byte brauchen: uint32, int32, float */ const call_t calls[] PROGMEM = { #ifdef BEHAVIOUR_DRIVE_DISTANCE_AVAILABLE PREPARE_REMOTE_CALL(bot_drive_distance,3, "int8 curve, int16 speed, int16 cm", 1,2,2), #endif #ifdef BEHAVIOUR_GOTOXY_AVAILABLE PREPARE_REMOTE_CALL(bot_gotoxy, 2, "float x, float y", 4, 4), #endif #ifdef BEHAVIOUR_SOLVE_MAZE_AVAILABLE PREPARE_REMOTE_CALL(bot_solve_maze,0,""), #endif #ifdef BEHAVIOUR_CATCH_PILLAR_AVAILABLE PREPARE_REMOTE_CALL(bot_catch_pillar,0,""), #endif #ifdef BEHAVIOUR_DRIVE_SQUARE_AVAILABLE PREPARE_REMOTE_CALL(bot_drive_square,0,""), #endif #ifdef BEHAVIOUR_FOLLOW_LINE_AVAILABLE PREPARE_REMOTE_CALL(bot_follow_line,0,""), #endif #ifdef BEHAVIOUR_GOTO_AVAILABLE PREPARE_REMOTE_CALL(bot_goto,2," int16 left, int16 right",2,2), #endif #ifdef BEHAVIOUR_OLYMPIC_AVAILABLE PREPARE_REMOTE_CALL(bot_do_slalom,0,""), #endif #ifdef BEHAVIOUR_SCAN_AVAILABLE PREPARE_REMOTE_CALL(bot_scan,0,""), #endif #ifdef BEHAVIOUR_SERVO_AVAILABLE PREPARE_REMOTE_CALL(bot_servo,2,"uint8 servo, uint8 pos",1,1), #endif #ifdef BEHAVIOUR_SIMPLE_AVAILABLE PREPARE_REMOTE_CALL(bot_simple,0,""), PREPARE_REMOTE_CALL(bot_simple2,1,"int16 light",2), #endif #ifdef BEHAVIOUR_TURN_AVAILABLE PREPARE_REMOTE_CALL(bot_turn,1,"int16 degrees",2) #endif }; #define STORED_CALLS (sizeof(calls)/sizeof(call_t)) /*!< Anzahl der Remote calls im Array */ /*! * Sucht den Index des Remote-Calls heraus * @param call String mit dem namen der gesuchten fkt * @return Index in das calls-Array. Wenn nicht gefunden, dann 255 */ uint8 getRemoteCall(char * call){ // LOG_DEBUG(("Suche nach Funktion: %s",call)); uint8 i; for (i=0; i< (STORED_CALLS); i++){ if (!strcmp_P (call, calls[i].name)){ LOG_DEBUG(("calls[%d].name=%s passt",i,call)); return i; } } return 255; } #ifdef MCU /*! * Hilfsfunktion fuer bot_remotecall() * Baut einen AVR-kompatiblen Parameterstream aus einem uint32-Parameterarray und einem Infoarray ueber die Parameter * @param dest Zeiger auf das Ausgabearray (len[0]*2 Byte gross!) * @param count Anzahl der Parameter * @param len Zeiger auf ein Array, das die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt * @param data Zeiger auf die Daten (32 Bit, Laenge 8) * @author Timo Sandmann (mail@timosandmann.de) * @date 12.01.2007 */ void remotecall_convert_params(uint8* dest, uint8 count, uint8* len, uint8* data){ uint8 i; /* jeden Parameter behandeln */ for (i=0; i=0; j--) *dest++ = data[j]; data += 4; // data-Array ist immer in 32 Bit } } #endif // MCU /*! * Dieses Verhalten kuemmert sich darum die Verhalten, die von aussen angefragt wurden zu starten und liefert ein feedback zurueck, wenn sie beendet sind. * @param *data der Verhaltensdatensatz */ void bot_remotecall_behaviour(Behaviour_t *data){ uint8 call_id =255; // LOG_DEBUG(("Enter bot_remotecall_behaviour")); void (* func) (struct _Behaviour_t *data); switch (running_behaviour) { case REMOTE_CALL_SCHEDULED: // Es laueft kein Auftrag, aber es steht ein neuer an // LOG_DEBUG(("REMOTE_CALL_SCHEDULED")); call_id=function_id; if (call_id >= STORED_CALLS){ // LOG_DEBUG(("keine Funktion gefunden. Exit")); running_behaviour=REMOTE_CALL_IDLE; return; } #ifdef PC // Auf dem PC liegt die calls-Struktur im RAM func = (void*) calls[call_id].func; #else // Auf dem MCU liegt die calls-Struktur im Flash und muss erst geholt werden func = (void*) pgm_read_word (& calls[call_id].func); #endif if (parameter_count ==0 ){ // Kommen wir ohne Parameter aus? // LOG_DEBUG(("call_id=%u",call_id)); func(data); // Die aufgerufene Botenfunktion starten running_behaviour=REMOTE_CALL_RUNNING; } else if (parameter_count <= REMOTE_CALL_MAX_PARAM){ // Es gibt gueltige Parameter // TODO: Ja hier wird es spannend, denn jetzt muessen die Parameter auf den Stack LOG_DEBUG(("call_id=%u",call_id)); LOG_DEBUG(("parameter_count=%u", parameter_count)); // asm-hacks here ;) #ifdef PC /* Prinzip auf dem PC: Wir legen alle Parameter einzeln auf den Stack, springen in die Botenfunktion und raeumen anschliessend den Stack wieder auf */ uint32 tmp; uint8 i; volatile uint8 td=1; // verwenden wir nur, damit der Compiler unsere inline-asm-Bloecke nicht umordnet for (i=0; i0; i+=4,td++){ // Debug-Info ausgeben // LOG_DEBUG(("parameter_data[%u-%u] = %lu",i, i+3, *(uint32*)(parameter_data+i))); } /* Erster Wert in parameter_length ist die Anzahl der Parameter (ohne Zeiger des Aufrufers) */ for (i=0; i1; i++,td++){ // Check von td eigentlich sinnlos, aber wir brauchen eine echte Datenabhaengigkeit auf dieses Codestueck /* cdecl-Aufrufkonvention => Parameter von rechts nach links auf den Stack */ tmp = *(uint32*)(parameter_data+(parameter_count-i-1)*4); /* Parameter 2 bis n pushen */ asm volatile( // IA32-Support only "pushl %0 # parameter i auf stack " :: "g" (tmp) : "memory" ); } /* Parameter 1 (data) und Funktionsaufruf */ asm volatile( "pushl %0 # push data \n\t" "movl %1, %%eax # adresse laden \n\t" "call *%%eax # jump to callee \n\t" :: "m" (data), "m" (func) : "eax", "memory" ); /* caller rauemt den Stack wieder auf */ for (i=0; i<=parameter_count && td>2; i++){ // Check von td erzwingt, dass das Aufraeumen erst jetzt passiert asm volatile( "pop %%eax # stack aufraeumen " ::: "eax", "memory" ); } #else /* Prinzip auf der MCU: Keine komplizierten Rechnungen, sondern einfach alle Register ueberschreiben. * Achtung: Derzeit braucht kein Verhalten mehr als 8 Register (2*float oder 4*int16 oder 4*int8), aendert sich das, * muss man den Code hier erweitern! * Die AVR-Konvention beim Funktionsaufruf: * Die Groesse in Byte wird zur naechsten geraden Zahl aufgerundet, falls sie ungerade ist. * Der Registerort faengt mit 26 an. * Vom Registerort wird die berechete Groesse abgezogen und das Argument in diesen Registern uebergeben (LSB first). * In r24/r25 legt der Compiler spaeter den Zeiger des Aufrufers, koennen wir hier also weglassen. */ // LOG_DEBUG(("r22:r23 = %u:%u", parameter_data[1], parameter_data[0])); // LOG_DEBUG(("r21:r20 = %u:%u", parameter_data[3], parameter_data[2])); // LOG_DEBUG(("r18:r19 = %u:%u", parameter_data[5], parameter_data[4])); // LOG_DEBUG(("r16:r17 = %u:%u", parameter_data[7], parameter_data[6])); asm volatile( // remotecall_convert_params() hat den Speicher bereits richtig sortiert, nur noch Werte laden "ld r23, Z+ \n\t" "ld r22, Z+ \n\t" "ld r21, Z+ \n\t" "ld r20, Z+ \n\t" "ld r19, Z+ \n\t" "ld r18, Z+ \n\t" "ld r17, Z+ \n\t" "ld r16, Z " :: "z" (parameter_data) : "r23", "r22", "r21", "r20", "r19", "r18", "r17", "r16" ); func(data); // Die aufgerufene Botenfunktion starten #endif running_behaviour=REMOTE_CALL_RUNNING; return; } else { // LOG_DEBUG(("Parameteranzahl unzulaessig!")); } break; case REMOTE_CALL_RUNNING: // Es lief ein Verhalten und ist nun zuende (sonst waeren wir nicht hier) { // Antwort schicken char * function_name; #ifdef PC function_name=(char*) &calls[function_id].name; #else // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen char tmp[REMOTE_CALL_FUNCTION_NAME_LEN+1]; function_name=(char*)&tmp; uint8* from= (uint8*)& calls[function_id].name; uint8 i; for (i=0; isubResult; command_write_data(CMD_REMOTE_CALL,SUB_REMOTE_CALL_DONE,&result,&result,function_name); #endif // LOG_DEBUG(("Remote-call %s beendet",function_name)); // Aufrauemen function_id=255; //parameter_length=NULL; //parameter_data=NULL; running_behaviour=REMOTE_CALL_IDLE; return_from_behaviour(data); // und Verhalten auch aus break; } default: return_from_behaviour(data); // und Verhalten auch aus break; } } /*! * Fuehre einen remote_call aus. Es gibt KEIN aufrufendes Verhalten!! * @param func Zeiger auf den Namen der Fkt * @param data Zeiger auf die Daten */ void bot_remotecall(char* func, remote_call_data_t* data){ function_id= getRemoteCall(func); if (function_id >= STORED_CALLS){ // LOG_DEBUG(("Funktion %s nicht gefunden. Exit!",func)); return; } // parameter_length: Zeiger auf ein Array, das zuerst die Anzahl der Parameter und danach die Anzahl der Bytes fuer die jeweiligen Parameter enthaelt #ifdef PC parameter_count = calls[function_id].param_count; parameter_length = (uint8*)calls[function_id].param_len; #else // Auf dem MCU muessen wir die Daten erstmal aus dem Flash holen uint8* from= (uint8*)& calls[function_id].param_len; uint8 i; parameter_count = pgm_read_byte(&calls[function_id].param_count); for (i=0; iname,call->param_info)); } } #endif