/* * c't-Sim - Robotersimulator fuer den 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 ct-Bot.c * @brief Demo-Hauptprogramm * @author Benjamin Benz (bbe@heise.de) * @date 26.12.05 */ #include "ct-Bot.h" #ifdef MCU #include #include // #include #include #include "bot-2-pc.h" #endif #ifdef PC #include "bot-2-sim.h" #include "tcp.h" #include "tcp-server.h" #include #include #include #include #include #endif #ifdef TWI_AVAILABLE #include "TWI_driver.h" #endif #include #include #include "global.h" #include "display.h" #include "led.h" #include "ena.h" #include "shift.h" #include "delay.h" #include "uart.h" #include "adc.h" #include "timer.h" #include "sensor.h" #include "log.h" #include "motor.h" #include "sensor-low.h" #include "bot-logic/bot-logik.h" #include "mouse.h" #include "command.h" #include "ir-rc5.h" #include "rc5.h" #include "timer.h" #include "mmc.h" #include "map.h" #include "mmc-emu.h" #include "mini-fat.h" /* Nimmt den Status von MCU(C)SR bevor dieses Register auf 0x00 gesetzt wird */ #ifdef DISPLAY_SCREEN_RESETINFO uint8 reset_flag; #endif /* Enthaelt den Start des Heaps und ermoeglicht z.B. die Berechnung des verwendeten RAMs */ #ifdef MCU extern unsigned char __heap_start; #endif #ifdef TEST_AVAILABLE_COUNTER #include uint8 resetsEEPROM __attribute__ ((section (".eeprom")))=0; uint8 resetInfoEEPROM __attribute__ ((section (".eeprom"))); uint8 resets; #endif /*! * Der Mikrocontroller und der PC-Simulator brauchen ein paar Einstellungen, * bevor wir loslegen koennen. */ void init(void){ #ifdef MCU PORTA=0; DDRA=0; //Alles Eingang alles Null PORTB=0; DDRB=0; PORTC=0; DDRC=0; PORTD=0; DDRD=0; // Watchdog aus! wdt_disable(); timer_2_init(); // Ist das ein Power on Reset ? #ifdef __AVR_ATmega644__ if ((MCUSR & 1) ==1 ) { MCUSR &= ~1; // Bit loeschen #else if ((MCUCSR & 1) ==1 ) { MCUCSR &= ~1; // Bit loeschen #endif delay(100); asm volatile("jmp 0"); } delay(100); #ifdef DISPLAY_SCREEN_RESETINFO #ifdef __AVR_ATmega644__ reset_flag = MCUSR & 0x1F; //Lese Grund fuer Reset und sichere Wert MCUSR = 0; //setze Register auf 0x00 (loeschen) #else reset_flag = MCUCSR & 0x1F; //Lese Grund fuer Reset und sichere Wert MCUCSR = 0; //setze Register auf 0x00 (loeschen) #endif #endif #endif #ifdef UART_AVAILABLE uart_init(); #endif #ifdef BOT_2_PC_AVAILABLE bot_2_pc_init(); #endif #ifdef PC bot_2_sim_init(); #endif #ifdef DISPLAY_AVAILABLE display_init(); display_update=1; #endif #ifdef LED_AVAILABLE LED_init(); #endif motor_init(); bot_sens_init(); #ifdef BEHAVIOUR_AVAILABLE bot_behave_init(); #endif #ifdef MCU #ifdef RC5_AVAILABLE ir_init(); #endif #endif #ifdef MMC_AVAILABLE mmc_init(); #endif #ifdef MAUS_AVAILABLE maus_sens_init(); #endif #ifdef MAP_AVAILABLE map_init(); #endif #ifdef DISPLAY_BEHAVIOUR_AVAILABLE behaviour_page = 1; #endif #ifdef TWI_AVAILABLE Init_TWI(); Close_TWI(); #endif } #ifdef DISPLAY_AVAILABLE /*! * Zeigt ein paar Informationen an */ void display(void){ #ifdef DISPLAY_BEHAVIOUR_AVAILABLE /*! * Definitionen fuer die Verhaltensanzeige */ #undef TEST_AVAILABLE_COUNTER Behaviour_t *ptr = behaviour; int8 colcounter = 0; int8 linecounter = 0; int8 firstcol = 0; #endif #ifdef MCU #ifndef LOG_DISPLAY_AVAILABLE #ifdef DISPLAY_SCREENS_AVAILABLE uint16 frei; // enthaelt im Screen 5 den freien RAM-Speicher #endif // DISPLAY_SCREENS_AVAILABLE #endif #endif #ifdef TEST_AVAILABLE_COUNTER static int counter=0; #endif if (display_update >0) #ifdef DISPLAY_SCREENS_AVAILABLE switch (display_screen) { case 0: #endif display_cursor(1,1); display_printf("P=%03X %03X D=%03d %03d ",sensLDRL,sensLDRR,sensDistL,sensDistR); display_cursor(2,1); display_printf("B=%03X %03X L=%03X %03X ",sensBorderL,sensBorderR,sensLineL,sensLineR); display_cursor(3,1); display_printf("R=%2d %2d F=%d K=%d T=%d ",sensEncL % 10,sensEncR %10,sensError,sensDoor,sensTrans); display_cursor(4,1); #ifdef RC5_AVAILABLE #ifdef MAUS_AVAILABLE display_printf("I=%04X M=%05d %05d",RC5_Code,sensMouseX,sensMouseY); #else display_printf("I=%04X",RC5_Code); #endif #else #ifdef MAUS_AVAILABLE display_printf("M=%05d %05d",sensMouseX,sensMouseY); #endif #endif #ifdef DISPLAY_SCREENS_AVAILABLE break; case 1: #ifdef TIME_AVAILABLE display_cursor(1,1); display_printf("Zeit: %04d:%03d", timer_get_s(), timer_get_ms()); #endif #ifdef BEHAVIOUR_AVAILABLE display_cursor(2,1); display_printf("TS=%+4d %+4d",target_speed_l,target_speed_r); #endif #ifdef SRF10_AVAILABLE display_cursor(2,15); display_printf("US%+4d",sensSRF10); #endif display_cursor(3,1); display_printf("RC=%+4d %+4d",sensEncL,sensEncR); display_cursor(4,1); display_printf("Speed= %04d",(int16)v_center); break; case 2: display_cursor(1,1); #ifdef DISPLAY_BEHAVIOUR_AVAILABLE /*! * zeilenweise Anzeige der Verhalten */ display_printf("Verhalten (Pri/Akt)%d",behaviour_page); colcounter = 0; linecounter = 2; /* je nach Seitenwahl die ersten Saetze ueberlesen bis richtige Seite */ firstcol = (behaviour_page -1)*6; /*! * max. 3 Zeilen mit 6 Verhalten anzeigbar wegen Ueberschrift * Seitensteuerung bei mehr Verhalten */ while((ptr != NULL)&& (linecounter<5)) { if ((ptr->priority >= PRIO_VISIBLE_MIN) &&(ptr->priority <= PRIO_VISIBLE_MAX)) { if (colcounter >= firstcol) { display_cursor(linecounter,((colcounter % 2)* 12)+1); #ifdef DISPLAY_DYNAMIC_BEHAVIOUR_AVAILABLE display_printf(" %3d,%2d",ptr->priority,ptr->active); #else display_printf(" %3d,%2d",ptr->priority,ptr->active_new); #endif colcounter++; /* bei colcounter 0 neue Zeile */ if (colcounter % 2 == 0) linecounter++; } else colcounter ++; } ptr = ptr->next; } #endif #ifdef DISPLAY_ODOMETRIC_INFO /* Zeige Positions- und Geschwindigkeitsdaten */ display_cursor(1,1); display_printf("heading: %3d ",(int16)heading); display_cursor(2,1); display_printf("x: %3d y: %3d ",(int16)x_pos,(int16)y_pos); display_cursor(3,1); display_printf("v_l: %3d v_r: %3d ",(int16)v_left,(int16)v_right); #ifdef MEASURE_MOUSE_AVAILABLE display_cursor(4,1); display_printf("squal: %3d v_c: %3d",maus_get_squal(),(int16)v_mou_center); #endif #endif #ifdef TEST_AVAILABLE_COUNTER display_printf("Screen 3"); display_cursor(2,1); display_printf("count %d",counter++); display_cursor(3,1); display_printf("Reset-Counter %d",resets); #endif break; case 3: #ifdef DISPLAY_SCREEN_RESETINFO display_cursor(1,1); /* Zeige den Grund fuer Resets an */ display_printf("MCU(C)SR - Register"); display_cursor(2,1); display_printf("PORF :%d WDRF :%d",binary(reset_flag,0),binary(reset_flag,3)); display_cursor(3,1); display_printf("EXTRF:%d JTRF :%d",binary(reset_flag,1),binary(reset_flag,4)); display_cursor(4,1); display_printf("BORF :%d",binary(reset_flag,2)); #endif #ifdef DISPLAY_MMC_INFO #ifdef MMC_INFO_AVAILABLE { uint32 size = 0; uint8 csd[16]; static uint8 mmc_state= 0xFF; uint8 dummy= mmc_init(); // hat sich was geaendert? if (dummy!=mmc_state) { mmc_state=dummy; uint8 i; for (i=0;i<16;i++) csd[i]=0; display_cursor(1,1); if (mmc_state !=0){ display_printf("MMC not init (%d) ",mmc_state); }else { size=mmc_get_size(); mmc_read_csd(csd); display_printf("MMC= %4d MByte ",size >> 20); } #ifndef MMC_WRITE_TEST_AVAILABLE display_cursor(3,1); for (i=0;i<16;i++){ if (i == 8) display_cursor(4,1); if (i%2 == 0) display_printf(" "); display_printf("%02x",csd[i]); } #endif // MMC_WRITE_TEST_AVAILABLE } #ifdef MMC_WRITE_TEST_AVAILABLE if (mmc_state == 0){ static uint16 time = 0; if (TIMER_GET_TICKCOUNT_16-time > MS_TO_TICKS(200)){ time = TIMER_GET_TICKCOUNT_16; uint8 result = mmc_test(); if (result != 0){ display_cursor(3,1); display_printf("mmc_test()=%u :( ", result); } } } #endif // MMC_WRITE_TEST_AVAILABLE } #else #ifdef MMC_VM_AVAILABLE #ifdef PC display_cursor(3,1); display_printf("mmc_emu_test() = %u ", mmc_emu_test()); #endif // PC #endif // MMC_VM_AVAILABLE #endif // MMC_INFO_AVAILABLE #endif break; case 4: /* Ausgabe des freien RAMs */ #ifdef MCU #ifndef LOG_DISPLAY_AVAILABLE frei = SP - (uint16) &__heap_start; display_cursor(1,1); display_printf("free RAM: %4d ",frei); #endif #endif break; } #endif } #endif #ifdef TEST_AVAILABLE /*! Zeigt den internen Status der Sensoren mit den LEDs an */ void show_sensors(void){ uint8 led=0x00; led_t * status = (led_t *)&led; #ifdef TEST_AVAILABLE_ANALOG (*status).rechts = (sensDistR >> 9) & 0x01; (*status).links = (sensDistL >> 9) & 0x01; (*status).rot = (sensLineL >> 9) & 0x01; (*status).orange = (sensLineR >> 9) & 0x01; (*status).gelb = (sensLDRL >> 9) & 0x01; (*status).gruen = (sensLDRR >> 9) & 0x01; (*status).tuerkis = (sensBorderL >> 9) & 0x01; (*status).weiss = (sensBorderR >> 9) & 0x01; #endif #ifdef TEST_AVAILABLE_DIGITAL (*status).rechts = sensEncR & 0x01; (*status).links = sensEncL & 0x01; (*status).rot = sensTrans & 0x01; (*status).orange = sensError & 0x01; (*status).gelb = sensDoor & 0x01; #ifdef MAUS_AVAILABLE (*status).gruen = (sensMouseDX >>1) & 0x01; (*status).tuerkis = (sensMouseDY >>1) & 0x01; #endif #ifdef RC5_AVAILABLE (*status).weiss = RC5_Code & 0x01; #endif #endif LED_set(led); } #endif #ifdef PC /*! * Zeigt Informationen zu den moeglichen Kommandozeilenargumenten an. * Das Programm wird nach Anzeige des Hilfetextes per exit() beendet. */ void usage(void){ puts("USAGE: ct-Bot [-t host] [-T] [-h] [-s] [-M from] [-c FILE ID SIZE]"); puts("\t-t\tHostname oder IP Adresse zu der Verbunden werden soll"); puts("\t-T\tTestClient"); puts("\t-s\tServermodus"); puts("\t-M from\tKonvertiert eine Bot-map in eine PGM-Datei"); puts("\t-c \tErzeugt eine Mini-Fat-Datei fuer den Bot."); puts("\t FILE\tDateiname"); puts("\t ID \tDie ID aus ASCII-Zeichen"); puts("\t SIZE\tDie Nutzgroesse der Datei in KByte"); puts("\t-h\tZeigt diese Hilfe an"); exit(1); } #endif #ifdef MCU /*! * Hauptprogramm des Bots. Diese Schleife kuemmert sich um seine Steuerung. */ int main (void){ #endif #ifdef PC /*! * Hauptprogramm des Bots. Diese Schleife kuemmert sich um seine Steuerung. */ int main (int argc, char *argv[]){ //Zum debuggen der Zeiten: #ifdef DEBUG_TIMES struct timeval start, stop; #endif int ch; int start_server = 0; /*!< Wird auf 1 gesetzt, falls -s angegeben wurde */ int start_test_client =0; /*!< Wird auf 1 gesetzt, falls -T angegeben wurde */ char *hostname = NULL; /*!< Speichert den per -t uebergebenen Hostnamen zwischen */ int convert =0; /*!< Wird auf 1 gesetzt, wenn die Karte konvertiert werden soll */ int create =0; /*!< Wird auf 1 gesetzt, wenn eine neue Datei fuer Bot-mini-fat erzeugt werden soll */ char *from = NULL; /*!< Speichert den per -M uebergebenen Quellnamen zwischen */ // Die Kommandozeilenargumente komplett verarbeiten while ((ch = getopt(argc, argv, "hsTt:M:c:")) != -1) { switch (ch) { case 's': // Servermodus [-s] wird verlangt start_server = 1; break; case 'T': start_test_client=1; break; case 't': // Hostname, auf dem ct-Sim laeuft wurde // uebergeben. Der String wird in hostname // gesichert. { const int len = strlen(optarg); hostname = malloc(len + 1); if (NULL == hostname) exit(1); strcpy(hostname, optarg); } break; case 'M': // Dateiname, fuer die Map wurde // uebergeben. Der String wird in from // gesichert. { int len = strlen(optarg); from = malloc(len + 1); if (NULL == from) exit(1); strcpy(from, optarg); convert=1; } break; case 'c': // Datei fuer den Bot (mini-fat soll erzeugt werden // Der Name wird in hostname gesichert. { int len = strlen(optarg); from = malloc(len + 1); if (NULL == from) exit(1); strcpy(from, optarg); create=1; } break; case 'h': default: // -h oder falscher Parameter, Usage anzeigen usage(); } } argc -= optind; argv += optind; if (start_server != 0) { // Soll der TCP-Server gestartet werden? printf("ARGV[0]= %s\n",argv[0]); tcp_server_init(); tcp_server_run(100); } else { #ifdef MAP_AVAILABLE /* Karte in pgm konvertieren */ if (convert !=0) { printf("Konvertiere Karte %s in PGM %s\n",from,"map.pgm"); read_map(from); map_to_pgm("map.pgm"); exit(0); } #endif // MAP_AVAILABLE if (create !=0) { printf("optind= %d argc=%d\n",optind, argc); if (argc != 2){ usage(); exit(1); } char * id; id = malloc(strlen(argv[0])); strcpy(id,argv[0]); char * s; s = malloc(strlen(argv[1])); strcpy(s,argv[1]); int size = atoi(s); create_mini_fat_file(from,id,size); printf("Erstelle eine Mini-Fat-Datei (%s) mit %d kByte fuer den Bot. ID=%s \n",from,size,id); // read_map(from); // map_to_pgm("map.pgm"); exit(0); } printf("c't-Bot\n"); if (hostname) // Hostname wurde per Kommandozeile uebergeben tcp_hostname = hostname; else { // Der Zielhost wird per default durch das Macro IP definiert und // tcp_hostname mit einer Kopie des Strings initialisiert. tcp_hostname = malloc(strlen(IP) + 1); if (NULL == tcp_hostname) exit(1); strcpy(tcp_hostname, IP); } if (start_test_client !=0) { tcp_test_client_init(); tcp_test_client_run(100); } } #endif #ifdef TEST_AVAILABLE_MOTOR uint16 calls=0; /*!< Im Testfall zaehle die Durchlaeufe */ #endif init(); #ifdef WELCOME_AVAILABLE display_cursor(1,1); display_printf("c't-Roboter"); LED_set(0x00); #ifdef LOG_AVAILABLE LOG_DEBUG(("Hallo Welt!")); #endif #endif #ifdef TEST_AVAILABLE_COUNTER display_screen=2; resets=eeprom_read_byte(&resetsEEPROM)+1; eeprom_write_byte(&resetsEEPROM,resets); /* Lege den Grund für jeden Reset im EEPROM ab */ eeprom_write_byte(&resetInfoEEPROM+resets,reset_flag); #endif /*! Hauptschleife des Bot */ for(;;){ #ifdef PC receive_until_Frame(CMD_DONE); #ifdef DEBUG_TIMES //Zum debuggen der Zeiten: GETTIMEOFDAY(&start, NULL); int t1=(start.tv_sec - stop.tv_sec)*1000000 + start.tv_usec - stop.tv_usec; printf("Done-Token (%d) in nach %d usec ",received_command.data_l,t1); #endif #endif #ifdef MCU bot_sens_isr(); #endif #ifdef TEST_AVAILABLE show_sensors(); #endif // Testprogramm, dass den Bot erst links, dann rechtsrum dreht #ifdef TEST_AVAILABLE_MOTOR calls++; if (calls == 1) motor_set(BOT_SPEED_SLOW,-BOT_SPEED_SLOW); else if (calls == 501) motor_set(-BOT_SPEED_SLOW,BOT_SPEED_SLOW); else if (calls== 1001) motor_set(BOT_SPEED_STOP,BOT_SPEED_STOP); else #endif // hier drin steckt der Verhaltenscode #ifdef BEHAVIOUR_AVAILABLE if (sensors_initialized ==1 ) bot_behave(); #ifdef LOG_AVAILABLE //else //LOG_DEBUG(("sens not init")); #endif #endif #ifdef MCU #ifdef BOT_2_PC_AVAILABLE // static int16 lastTimeCom =0; bot_2_pc_inform(); // Den PC ueber Sensorern und aktuatoren informieren bot_2_pc_listen(); // Kommandos vom PC empfangen // if (timer_get_s() != lastTimeCom) { // sollte genau 1x pro Sekunde zutreffen // lastTimeCom = timer_get_s(); // } #endif #endif #ifdef LOG_AVAILABLE //LOG_DEBUG(("BOT TIME %d s", timer_get_s())); #endif // Alles Anzeigen #ifdef DISPLAY_AVAILABLE display(); #endif #ifdef MCU // delay(10); #endif #ifdef PC command_write(CMD_DONE, SUB_CMD_NORM ,(int16*)&simultime,0,0); flushSendBuffer(); //Zum debuggen der Zeiten: #ifdef DEBUG_TIMES GETTIMEOFDAY(&stop, NULL); int t2=(stop.tv_sec - start.tv_sec)*1000000 +stop.tv_usec - start.tv_usec; printf("Done-Token (%d) out after %d usec\n",simultime,t2); #endif #endif } /*! Falls wir das je erreichen sollten ;-) */ return 1; }