Inhaltsverzeichnis
Planung
Erst mal die Planung:
- 1. Der I2C-Slave für die M32 soll genauso arbeiten, wie der RP6Base I2C-Slave (RP6Base_I2CSlave.c) in den Demos.
- 2. Er soll möglichst (fast) alle Funktionen/Ressourcen der M32 über I2C "fernsteuerbar" bzw. abfragbar machen.
- 3. Er soll als I2C-Master eine andere M32, die CCPRO M128 oder die M256 WiFi akzeptieren.
- 4. Er soll die I2C-Adresse 12 bekommen.
- 5. Er soll über XBUS INT2 mit dem Master verbunden sein.
- 6. Er soll wie der Base-Slave auch eine Timeout-Funktion haben.
- 7. Er soll folgende Befehle (commands) über I2C verstehen:
// Commands: #define CMD_CONFIGIOS 0 #define CMD_SETIOS 1 #define CMD_CONFIG 2 #define CMD_SETLEDS 3 #define CMD_DISCHARGEPEAKDETECTOR 4 #define CMD_GETMICROPHONEPEAK 5 #define CMD_SETMEM_CS2 6 #define CMD_WRITESPI 7 #define CMD_WRITEWORDSPI 8 #define CMD_READSPI 9 #define CMD_READWORDSPI 10 #define CMD_SET_WDT 11 #define CMD_SET_WDT_RQ 12 #define CMD_SET_HEARTBEAT 13 #define CMD_SPI_EEPROM_WRITEBYTE 14 #define CMD_SPI_EEPROM_WRITEWORD 15 #define CMD_SPI_EEPROM_ENABLEWRITE 16 #define CMD_SPI_EEPROM_DISABLEWRITE 17 #define CMD_SPI_EEPROM_READBYTE 18 #define CMD_SPI_EEPROM_READWORD 19 #define CMD_SPI_EEPROM_GETSTATUS 20 #define CMD_INITLCD 21 #define CMD_CLEARLCD 22 #define CMD_CLEARPOSLCD 23 #define CMD_WRITECHARLCD 24 #define CMD_WRITEINTEGERLCD 25 #define CMD_SETCURSORPOSLCD 26 #define CMD_BEEP 27 #define CMD_SETBEEPERPITCH 28 #define CMD_SOUND 29
Dabei setzt Befehl 0 die 8 freien I/O-Pins der M32 als Ein- oder Ausgänge. Befehl 1 schaltet die einzelnen I/O-Portpins. Befehl 2 ist Platzhalter ohne Funktion (z.B. zur Konfiguration des Slave). Befehl 3 schaltet die LEDs. Befehle 4, 5 gehen mit dem Mikro um. Befehle 6-10 sind die SPI-Befehle. Befehle 11,12 gehören zum Watchdog-Timer (wie bei der Base!). Befehl 13 schaltet die LCD Heartbeat (Herzschlag) Funktion. Befehle 14-20 lesen und schreiben von/aus dem SPI-EEPROM auf der M32. Befehle 21-26 steuern das LCD auf der M32 an. Befehle 27-29 steuern den Sound mit dem Beeper.
- 8. Er soll folgende Register zum Lesen durch den Master vorhalten:
#define I2C_REG_STATUS1 0 #define I2C_REG_STATUS2 1 #define I2C_REG_IO_STATUS 2 #define I2C_REG_MEM_CS2 3 #define I2C_REG_SPIBYTE 4 #define I2C_REG_SPIWORD_L 5 #define I2C_REG_SPIWORD_H 6 #define I2C_REG_SPIEEPROMSTATUS 7 #define I2C_REG_SPIEEPROMBYTE 8 #define I2C_REG_SPIEEPROMWORD_L 9 #define I2C_REG_SPIEEPROMWORD_H 10 #define I2C_REG_ADC_4_L 11 #define I2C_REG_ADC_4_H 12 #define I2C_REG_ADC_3_L 13 #define I2C_REG_ADC_3_H 14 #define I2C_REG_ADC_2_L 15 #define I2C_REG_ADC_2_H 16 #define I2C_REG_ADC_6_L 17 #define I2C_REG_ADC_6_H 18 #define I2C_REG_ADC_5_L 19 #define I2C_REG_ADC_5_H 20 #define I2C_REG_ADC_7_L 21 #define I2C_REG_ADC_7_H 22 #define I2C_REG_ADC_MIC_L 23 #define I2C_REG_ADC_MIC_H 24 #define I2C_REG_ADC_KEYPAD_L 25 #define I2C_REG_ADC_KEYPAD_H 26 #define I2C_REG_RELEASEDKEYNUMBER 27 #define I2C_REG_PRESSEDKEYNUMBER 28 #define I2C_REG_LEDS 29
Die REGs 0,1 sind die Interrupt- und Status-Register wie beim Base-Slave. IO-Status (REG 2) sind die 8 freien I/O-Ports (sofern auf Eingänge geschaltet). REGs 3-10 sind Leseregister der SPI- und SPI-EEPROM-Funktionen. REGs 11-22 sind die freien ADC-Kanäle der M32. REGs 23,24 sind der ADC-Wert des Mikro. REGs 25,26 sind der ADC-Keypad-Wert. REGs 27,28 sind die Nummern der zuletzt losgelassenen bzw. gedrückten Taste. Mit REG 29 läßt sich der aktuelle Stand der 4 LEDs auslesen (an/aus).
- 9. Er soll auf die RP6Control Library V1.32beta aufsetzen. Grund: Die aktuelle Lib V1.32beta ist voll kompatibel zur neuesten Version 1.3 und stellt mit eigenen Tasks schon regelmäßig die ADC-Werte und Werte der I/O-Ports zur Verfügung.
- 10. Bei Timeout soll die M32 funktionsfähig bleiben (Base-Slave bleibt dann in einer Endlosschleife stehen und muss resettet werden!).
I2C-Slave
Hier mal eine erste Version des M32 I2C-Slave.
Bitte testet diese Version! Was sollte noch geändert/verbessert werden? Fehler?
Datei RP6Control_I2CSlave.c:
/* * **************************************************************************** * RP6 ROBOT SYSTEM - RP6 CONTROL M32 EXAMPLES * **************************************************************************** * Example: I2C Slave * Author(s): Dirk * **************************************************************************** * Description: * * A very common thing that many users will want to do with their M32 is * to control it with a second controller which has more free resources. * (more free memory, free I/O Ports and ADCs, faster, etc. pp. * for example the RP6 C-Control PRO M128 or M256 WIFI expansion Module) * * This programs allows you to control the RP6 CONTROL M32 completely via * I2C-Bus as a slave device! * * ############################################################################ * The Robot does NOT move in this example! You can simply put it on a table * next to your PC and you should connect it to the PC via the USB Interface! * ############################################################################ * **************************************************************************** */ /*****************************************************************************/ // Includes: #include "RP6ControlLib.h" // The RP6 Control Library v1.32beta. // Always needs to be included! #include "RP6I2CslaveTWI.h" // Include the I²C-Bus Slave Library /*****************************************************************************/ // The Slave Address on the I2C Bus can be specified here: #define RP6Control_I2C_SLAVE_ADR 12 /*****************************************************************************/ // This bitfield contains the main interrupt event status bits. This can be // read out and any Master devices can react on the specific events. union { uint8_t byte; struct { uint8_t timeout:1; uint8_t mem_cs2Change:1; uint8_t insChange:1; uint8_t keyChange:1; uint8_t keypressed:1; uint8_t unused:3; }; } interrupt_status; // Some status bits with current settings and other things. union { uint8_t byte; struct { uint8_t old_mem_cs2:1; uint8_t mem_cs2:1; uint8_t watchDogTimer:1; uint8_t wdtRequest:1; uint8_t wdtRequestEnable:1; uint8_t heartbeat:1; uint8_t unused:2; }; } status; /*****************************************************************************/ /** * Generates Interrupt Signal and starts Software Watchdog */ void signalInterrupt(void) { I2CTWI_dataWasRead = 0; DDRD |= EINT2; // XBUS INT2 PORTD |= EINT2; if(status.watchDogTimer) startStopwatch2(); } /** * Clears Interrupt */ void clearInterrupt(void) { stopStopwatch2(); setStopwatch2(0); status.wdtRequest = false; PORTD &= ~EINT2; // XBUS INT2 DDRD &= ~EINT2; } freeIOs_t old_ins; uint8_t old_releasedKeyNumber; uint8_t update_count = 0; /** * This function needs to be called frequently in the main loop. It updates * some values (currently only mem_cs2Change, insChange, keyChange and * keypressed status, but this may be expanded in future). */ void task_update(void) { if(getStopwatch4() > 250) { // Update mem_cs2 status: status.mem_cs2 = PINB & MEM_CS2; update_count++; setStopwatch4(0); } if(update_count > 5) { // Update mem_cs2Change: if(!interrupt_status.mem_cs2Change && (status.mem_cs2 != status.old_mem_cs2)) { status.old_mem_cs2 = status.mem_cs2; interrupt_status.mem_cs2Change = true; signalInterrupt(); } else if(interrupt_status.mem_cs2Change && (status.mem_cs2 == status.old_mem_cs2)) { interrupt_status.mem_cs2Change = false; signalInterrupt(); } // Update insChange: if(!interrupt_status.insChange && (ins.byte != old_ins.byte)) { old_ins = ins; interrupt_status.insChange = true; signalInterrupt(); } else if(interrupt_status.insChange && (ins.byte == old_ins.byte)) { interrupt_status.insChange = false; signalInterrupt(); } // Update keyChange: if(!interrupt_status.keyChange && (releasedKeyNumber != old_releasedKeyNumber)) { old_releasedKeyNumber = releasedKeyNumber; interrupt_status.keyChange = true; signalInterrupt(); } else if(interrupt_status.keyChange && (releasedKeyNumber == old_releasedKeyNumber)) { interrupt_status.keyChange = false; signalInterrupt(); } update_count = 0; } // Update keypressed status: interrupt_status.keypressed = pressedKeyNumber; } /*****************************************************************************/ // I2C Registers that can be read by the Master. Their names should // be self-explanatory and directly relate to the equivalent variables/functions // in the RP6Library #define I2C_REG_STATUS1 0 #define I2C_REG_STATUS2 1 #define I2C_REG_IO_STATUS 2 #define I2C_REG_MEM_CS2 3 #define I2C_REG_SPIBYTE 4 #define I2C_REG_SPIWORD_L 5 #define I2C_REG_SPIWORD_H 6 #define I2C_REG_SPIEEPROMSTATUS 7 #define I2C_REG_SPIEEPROMBYTE 8 #define I2C_REG_SPIEEPROMWORD_L 9 #define I2C_REG_SPIEEPROMWORD_H 10 #define I2C_REG_ADC_4_L 11 #define I2C_REG_ADC_4_H 12 #define I2C_REG_ADC_3_L 13 #define I2C_REG_ADC_3_H 14 #define I2C_REG_ADC_2_L 15 #define I2C_REG_ADC_2_H 16 #define I2C_REG_ADC_6_L 17 #define I2C_REG_ADC_6_H 18 #define I2C_REG_ADC_5_L 19 #define I2C_REG_ADC_5_H 20 #define I2C_REG_ADC_7_L 21 #define I2C_REG_ADC_7_H 22 #define I2C_REG_ADC_MIC_L 23 #define I2C_REG_ADC_MIC_H 24 #define I2C_REG_ADC_KEYPAD_L 25 #define I2C_REG_ADC_KEYPAD_H 26 #define I2C_REG_RELEASEDKEYNUMBER 27 #define I2C_REG_PRESSEDKEYNUMBER 28 #define I2C_REG_LEDS 29 // These variables contain the results of the functions readSPI(), // readWordSPI(), SPI_EEPROM_readByte[s](), SPI_EEPROM_getStatus() after the // Master sent one of the commands CMD_READSPI, CMD_READWORDSPI, // CMD_SPI_EEPROM_READBYTE/WORD, CMD_SPI_EEPROM_GETSTATUS. So these variables // are updated only "on demand" and NOT permanently! uint8_t spibyte; uint16_t spiword; uint8_t spieepromstatus; uint8_t spieeprombyte; uint16_t spieepromword; /** * This very important function updates ALL registers that the Master can read. * It is called frequently out of the Main loop. */ void task_updateRegisters(void) { if(!I2CTWI_readBusy) { I2CTWI_readRegisters[I2C_REG_STATUS1] = (uint8_t)(interrupt_status.byte); I2CTWI_readRegisters[I2C_REG_STATUS2] = (uint8_t)(status.byte); I2CTWI_readRegisters[I2C_REG_IO_STATUS] = (uint8_t)(ins.byte); I2CTWI_readRegisters[I2C_REG_MEM_CS2] = (uint8_t)(status.mem_cs2); I2CTWI_readRegisters[I2C_REG_SPIBYTE] = (uint8_t)(spibyte); I2CTWI_readRegisters[I2C_REG_SPIWORD_L] = (uint8_t)(spiword); I2CTWI_readRegisters[I2C_REG_SPIWORD_H] = (uint8_t)(spiword>>8); I2CTWI_readRegisters[I2C_REG_SPIEEPROMSTATUS] = (uint8_t)(spieepromstatus); I2CTWI_readRegisters[I2C_REG_SPIEEPROMBYTE] = (uint8_t)(spieeprombyte); I2CTWI_readRegisters[I2C_REG_SPIEEPROMWORD_L] = (uint8_t)(spieepromword); I2CTWI_readRegisters[I2C_REG_SPIEEPROMWORD_H] = (uint8_t)(spieepromword>>8); I2CTWI_readRegisters[I2C_REG_ADC_4_L] = (uint8_t)(adc4); I2CTWI_readRegisters[I2C_REG_ADC_4_H] = (uint8_t)(adc4>>8); I2CTWI_readRegisters[I2C_REG_ADC_3_L] = (uint8_t)(adc3); I2CTWI_readRegisters[I2C_REG_ADC_3_H] = (uint8_t)(adc3>>8); I2CTWI_readRegisters[I2C_REG_ADC_2_L] = (uint8_t)(adc2); I2CTWI_readRegisters[I2C_REG_ADC_2_H] = (uint8_t)(adc2>>8); I2CTWI_readRegisters[I2C_REG_ADC_6_L] = (uint8_t)(adc6); I2CTWI_readRegisters[I2C_REG_ADC_6_H] = (uint8_t)(adc6>>8); I2CTWI_readRegisters[I2C_REG_ADC_5_L] = (uint8_t)(adc5); I2CTWI_readRegisters[I2C_REG_ADC_5_H] = (uint8_t)(adc5>>8); I2CTWI_readRegisters[I2C_REG_ADC_7_L] = (uint8_t)(adc7); I2CTWI_readRegisters[I2C_REG_ADC_7_H] = (uint8_t)(adc7>>8); I2CTWI_readRegisters[I2C_REG_ADC_MIC_L] = (uint8_t)(adcMic); I2CTWI_readRegisters[I2C_REG_ADC_MIC_H] = (uint8_t)(adcMic>>8); I2CTWI_readRegisters[I2C_REG_ADC_KEYPAD_L] = (uint8_t)(adcKeypad); I2CTWI_readRegisters[I2C_REG_ADC_KEYPAD_H] = (uint8_t)(adcKeypad>>8); I2CTWI_readRegisters[I2C_REG_RELEASEDKEYNUMBER] = (uint8_t)(releasedKeyNumber); I2CTWI_readRegisters[I2C_REG_PRESSEDKEYNUMBER] = (uint8_t)(pressedKeyNumber); I2CTWI_readRegisters[I2C_REG_LEDS] = (uint8_t)(externalPort.LEDS); if(I2CTWI_dataWasRead && I2CTWI_dataReadFromReg == 0) clearInterrupt(); } } /*****************************************************************************/ // Command Registers - these can be written by the Master. // The other registers (read registers) can NOT be written to. The only way to // communicate with the M32 is via specific commands. // Of course you can also add more registers if you like... // ---------------------- #define I2C_REGW_CMD 0 #define I2C_REGW_CMD_PARAM1 1 #define I2C_REGW_CMD_PARAM2 2 #define I2C_REGW_CMD_PARAM3 3 #define I2C_REGW_CMD_PARAM4 4 #define I2C_REGW_CMD_PARAM5 5 #define I2C_REGW_CMD_PARAM6 6 // ---------------------- uint8_t cmd; uint8_t param1; uint8_t param2; uint8_t param3; uint8_t param4; uint8_t param5; uint8_t param6; /** * Checks if a new Command has been received and also reads all * paramters associated with this command. * It returns true if a new command has been received. */ uint8_t getCommand(void) { if(I2CTWI_writeRegisters[I2C_REGW_CMD] && !I2CTWI_writeBusy) { cmd = I2CTWI_writeRegisters[I2C_REGW_CMD]; // store command register I2CTWI_writeRegisters[I2C_REGW_CMD] = 0; // clear command register (!!!) param1 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM1]; // parameters 1-6... param2 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM2]; param3 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM3]; param4 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM4]; param5 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM5]; param6 = I2CTWI_writeRegisters[I2C_REGW_CMD_PARAM6]; return true; } return false; } /*****************************************************************************/ // Command processor: // Commands: #define CMD_CONFIGIOS 0 #define CMD_SETIOS 1 #define CMD_CONFIG 2 #define CMD_SETLEDS 3 #define CMD_DISCHARGEPEAKDETECTOR 4 #define CMD_GETMICROPHONEPEAK 5 #define CMD_SETMEM_CS2 6 #define CMD_WRITESPI 7 #define CMD_WRITEWORDSPI 8 #define CMD_READSPI 9 #define CMD_READWORDSPI 10 #define CMD_SET_WDT 11 #define CMD_SET_WDT_RQ 12 #define CMD_SET_HEARTBEAT 13 #define CMD_SPI_EEPROM_WRITEBYTE 14 #define CMD_SPI_EEPROM_WRITEWORD 15 #define CMD_SPI_EEPROM_ENABLEWRITE 16 #define CMD_SPI_EEPROM_DISABLEWRITE 17 #define CMD_SPI_EEPROM_READBYTE 18 #define CMD_SPI_EEPROM_READWORD 19 #define CMD_SPI_EEPROM_GETSTATUS 20 #define CMD_INITLCD 21 #define CMD_CLEARLCD 22 #define CMD_CLEARPOSLCD 23 #define CMD_WRITECHARLCD 24 #define CMD_WRITEINTEGERLCD 25 #define CMD_SETCURSORPOSLCD 26 #define CMD_BEEP 27 #define CMD_SETBEEPERPITCH 28 #define CMD_SOUND 29 uint8_t rw_buffer[3]; /** * This function checks if commands have been received and processes them. */ void task_commandProcessor(void) { if(getCommand()) { switch(cmd) { case CMD_CONFIGIOS: if(param1) setFreeIOsToOUT(); else setFreeIOsToIN(); break; case CMD_SETIOS: setOUTs(param1); break; case CMD_CONFIG: break; case CMD_SETLEDS: setLEDs(param1); break; case CMD_DISCHARGEPEAKDETECTOR: dischargePeakDetector(); break; case CMD_GETMICROPHONEPEAK: adcMic = getMicrophonePeak(); break; case CMD_SETMEM_CS2: if(param1) PORTB |= MEM_CS2; else PORTB &= ~MEM_CS2; break; case CMD_WRITESPI: writeSPI(param1); break; case CMD_WRITEWORDSPI: writeWordSPI((param1<<8)+param2); break; case CMD_READSPI: spibyte = readSPI(); break; case CMD_READWORDSPI: spiword = readWordSPI(); break; case CMD_SET_WDT: status.watchDogTimer = param1 ? true : false; break; case CMD_SET_WDT_RQ: status.wdtRequestEnable = param1 ? true : false; break; case CMD_SET_HEARTBEAT: status.heartbeat = param1 ? true : false; break; case CMD_SPI_EEPROM_WRITEBYTE: SPI_EEPROM_writeByte((param1<<8)+param2, param3); break; case CMD_SPI_EEPROM_WRITEWORD: rw_buffer[0] = param3; rw_buffer[1] = param4; SPI_EEPROM_writeBytes((param1<<8)+param2, &rw_buffer[0], 2); break; case CMD_SPI_EEPROM_ENABLEWRITE: SPI_EEPROM_enableWrite(); break; case CMD_SPI_EEPROM_DISABLEWRITE: SPI_EEPROM_disableWrite(); break; case CMD_SPI_EEPROM_READBYTE: spieeprombyte = SPI_EEPROM_readByte((param1<<8)+param2); break; case CMD_SPI_EEPROM_READWORD: SPI_EEPROM_readBytes((param1<<8)+param2, &rw_buffer[0], 2); spieepromword = (rw_buffer[0]<<8)+rw_buffer[1]; break; case CMD_SPI_EEPROM_GETSTATUS: spieepromstatus = SPI_EEPROM_getStatus(); break; case CMD_INITLCD: initLCD(); break; case CMD_CLEARLCD: clearLCD(); break; case CMD_CLEARPOSLCD: clearPosLCD(param1, param2, param3); break; case CMD_WRITECHARLCD: writeCharLCD(param1); break; case CMD_WRITEINTEGERLCD: writeIntegerLCD((param1<<8)+param2, param3); break; case CMD_SETCURSORPOSLCD: setCursorPosLCD(param1, param2); break; case CMD_BEEP: beep(param1, (param2<<8)+param3); break; case CMD_SETBEEPERPITCH: setBeeperPitch(param1); break; case CMD_SOUND: sound(param1, (param2<<8)+param3, (param4<<8)+param5); break; } } } /** * This is the Software watchdog function. After any interrupt event, a timer is * started and if a certain amount of time has passed by with no reaction from * the Master, the RP6 CONTROL M32 shows this by blinking with status LEDs 1..4 * for 3 seconds. After this the interrupt and interrupt status are cleared. * Usually the Master program has errors or is locked up if it does not react, * so the blinking LEDs show, that there is a communication problem. */ void task_MasterTimeout(void) { if(status.watchDogTimer) { static uint8_t blinkflag = 0; if(getStopwatch2() > 3000) // 3 seconds timeout for the master to react on { // our interrupt events - if he does not react, we // blink with LEDs 1..4 for another 3 seconds! interrupt_status.timeout = true; if(getStopwatch2() > 6000) { setLEDs(0b0000); // Clear LEDs interrupt_status.byte = 0; // Clear interrupt status setStopwatch4(0); clearInterrupt(); // Clear interrupt } } else if(getStopwatch3() > 250) { status.wdtRequest = true; signalInterrupt(); setStopwatch3(0); } if(interrupt_status.timeout) { if(getStopwatch5() > 200) { if(blinkflag) { setLEDs(0b1001); blinkflag = 0; } else { setLEDs(0b0110); blinkflag = 1; } setStopwatch5(0); } } } } /** * LCD Heartbeat function */ void task_LCDHeartbeat(void) { if(status.heartbeat) { if(getStopwatch1() > 500) { static uint8_t heartbeat = false; if(heartbeat) { clearPosLCD(1, 15, 1); heartbeat = false; } else { setCursorPosLCD(1, 15); writeStringLCD_P("*"); heartbeat = true; } setStopwatch1(0); } } } /*****************************************************************************/ // Main - The program starts here: int16_t main(void) { initRP6Control(); clearLCD(); setLEDs(0b1111); mSleep(500); setLEDs(0b0000); I2CTWI_initSlave(RP6Control_I2C_SLAVE_ADR); status.byte = 0; interrupt_status.byte = 0; // TEST TEST TEST TEST TEST TEST status.watchDogTimer = true; status.heartbeat = true; // TEST TEST TEST TEST TEST TEST startStopwatch1(); // For LCDHeartbeat function startStopwatch3(); startStopwatch4(); startStopwatch5(); while(true) { task_commandProcessor(); task_update(); task_updateRegisters(); task_RP6M32System(); task_MasterTimeout(); task_LCDHeartbeat(); } return 0; }
I2C-Master (RP6v2 M256 WiFi)
Siehe auch
Weblinks
Autoren
--Dirk 19:20, 12. Sep 2012 (CET)