Dirk (Diskussion | Beiträge) K (→Dual-Slave Master) |
Dirk (Diskussion | Beiträge) K (→Autoren) |
||
(39 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 99: | Zeile 99: | ||
* 9. Er soll auf die RP6Control Library [http://www.roboternetz.de/community/showthread.php?47198-RP6Control-M32-Neue-Library-Version-1.3beta 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. | * 9. Er soll auf die RP6Control Library [http://www.roboternetz.de/community/showthread.php?47198-RP6Control-M32-Neue-Library-Version-1.3beta 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!). | * 10. Bei Timeout soll die M32 funktionsfähig bleiben (Base-Slave bleibt dann in einer Endlosschleife stehen und muss resettet werden!). | ||
+ | * 11. Es soll neben dem Watchdog Event Handler noch drei weitere Event Handler geben: | ||
+ | ** Pegel-Wechsel des Portpins PB1 (mem_cs2) | ||
+ | ** Pegel-Wechsel der freien IO-Ports PD5, PD6, PC2..7 (ins) | ||
+ | ** Tasten-Betätigung | ||
Zeile 114: | Zeile 118: | ||
==Slave== | ==Slave== | ||
− | + | Hier das I2C-Slave Programm für die RP6 CONTROL M32. | |
− | + | '''ACHTUNG: Dieses Programm benötigt die RP6 Control Library in der Version [http://www.roboternetz.de/community/showthread.php?47198-RP6Control-M32-Neue-Library-Version-1.3beta V1.32beta]!!!''' | |
Datei RP6Control_I2CSlave.c: | Datei RP6Control_I2CSlave.c: | ||
<pre>/* | <pre>/* | ||
Zeile 494: | Zeile 498: | ||
* started and if a certain amount of time has passed by with no reaction from | * 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 | * 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. | + | * for 3 seconds. After this the interrupt and interrupt status are cleared and |
+ | * the watchdog timer is disabled. | ||
* Usually the Master program has errors or is locked up if it does not react, | * 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. | * so the blinking LEDs show, that there is a communication problem. | ||
Zeile 511: | Zeile 516: | ||
setLEDs(0b0000); // Clear LEDs | setLEDs(0b0000); // Clear LEDs | ||
interrupt_M32status.byte = 0; // Clear interrupt status | interrupt_M32status.byte = 0; // Clear interrupt status | ||
+ | M32status.watchDogTimer = false; // Disable watchdog timer | ||
setStopwatch4(0); | setStopwatch4(0); | ||
clearInterrupt(); // Clear interrupt | clearInterrupt(); // Clear interrupt | ||
Zeile 606: | Zeile 612: | ||
} | } | ||
</pre> | </pre> | ||
− | |||
=I2C-Master (RP6v2 M256 WiFi)= | =I2C-Master (RP6v2 M256 WiFi)= | ||
+ | |||
==Vorbereitung== | ==Vorbereitung== | ||
Um für die Funktionsnamen der Sound- und EEPROM-Befehle keine Probleme zu bekommen, müssen wir die aktuelle RP6M256Lib.h (Version 1.1 - 16.07.2012) anpassen. | Um für die Funktionsnamen der Sound- und EEPROM-Befehle keine Probleme zu bekommen, müssen wir die aktuelle RP6M256Lib.h (Version 1.1 - 16.07.2012) anpassen. | ||
Zeile 652: | Zeile 658: | ||
Zusätzlich sind die drei Event Handler aktiv: Betätigt man z.B. eine Taste auf der M32, wird dieser "Event" auf dem WIFI-Terminal des RobotLoaders angezeigt. Angezeigt werden auch Pegel-Wechsel der freien I/O-Ports der M32 oder des Pins mem_cs2 (wenn er zuvor auf Eingang geschaltet wurde!). | Zusätzlich sind die drei Event Handler aktiv: Betätigt man z.B. eine Taste auf der M32, wird dieser "Event" auf dem WIFI-Terminal des RobotLoaders angezeigt. Angezeigt werden auch Pegel-Wechsel der freien I/O-Ports der M32 oder des Pins mem_cs2 (wenn er zuvor auf Eingang geschaltet wurde!). | ||
+ | |||
+ | '''Testaufbau''': | ||
+ | - RP6BASE: Programmspeicher gelöscht | ||
+ | - M32: RP6Control_I2CSlave.c (siehe [[RP6Control_M32:_I2C-Slave#Slave|Slave]]!) | ||
+ | - M256 WiFi: RP6M256_M32_I2CMaster.c (siehe unten!) | ||
+ | - M32 über PROG_UART Stecker mit PC verbunden | ||
+ | - M256 WiFi über WLAN mit PC verbunden | ||
+ | - WIFI-Terminal-Fenster des RobotLoaders ausgeklappt | ||
+ | - Start des Systems über Serial Loader | ||
Datei RP6M256_M32_I2CMaster.c: | Datei RP6M256_M32_I2CMaster.c: | ||
Zeile 659: | Zeile 674: | ||
* **************************************************************************** | * **************************************************************************** | ||
* Example: M32 I2C Master | * Example: M32 I2C Master | ||
− | * Author(s): | + | * Author(s): Dirk |
* **************************************************************************** | * **************************************************************************** | ||
* Description: | * Description: | ||
Zeile 1.068: | Zeile 1.083: | ||
====Versionen==== | ====Versionen==== | ||
− | * '''V1.00''' vom .. | + | * '''V1.00''' vom 22.09.2012 |
====Programm==== | ====Programm==== | ||
− | ... | + | Dieses Demo-Programm zeigt die '''Ansteuerung der RP6BASE''' zunächst mit einem kleinen MOVE-/ROTATE-Test, dann die '''Ansteuerung der RP6 CONTROL M32''' mit einer Demo auf dem M32 LCD, einen kleinen Sound-Test, einen Schreib-/Lesetest auf das SPI-EEPROM und danach ein Lauflicht mit den LEDs der M32 UND mit den LEDs der RP6BASE,- alles über I2C von der M256 WiFi gesteuert. |
+ | |||
+ | Zusätzlich sind die drei '''Event Handler der M32''' aktiv: Betätigt man z.B. eine Taste auf der M32, wird dieser "Event" auf dem WIFI-Terminal des RobotLoaders angezeigt. Angezeigt werden auch Pegel-Wechsel der freien I/O-Ports der M32 oder des Pins mem_cs2 (wenn er zuvor auf Eingang geschaltet wurde!). | ||
− | + | Auch die fünf '''Event Handler der RP6BASE''' sind aktiv: Das ACS steuert die LEDs der M256 WiFi an, macht Ausgaben auf dem LCD der M256 WiFi und auf dem Serial Terminal des RobotLoaders, wenn das USB-Interface an die M256 WiFi angeschlossen ist. Natürlich wird auch über IRCOMM RC5-Code empfangen, die Batteriespannung und der Bewegungsstatus überwacht und es werden die Bumper ausgewertet. | |
− | <pre></pre> | + | |
+ | '''Testaufbau''': | ||
+ | - RP6BASE: RP6Base_I2CSlave.c (aus den Demos des RP6!) | ||
+ | - M32: RP6Control_I2CSlave.c (siehe [[RP6Control_M32:_I2C-Slave#Slave|Slave]]!) | ||
+ | - M256 WiFi: RP6M256_M32_I2CMaster_02.c (siehe unten!) | ||
+ | - M256 WiFi über WLAN und über PROG_UART Stecker mit PC verbunden | ||
+ | - Beide Terminal-Fenster des RobotLoaders ausgeklappt | ||
+ | - Start des Systems über Serial Loader | ||
+ | |||
+ | Datei RP6M256_M32_I2CMaster_02.c: | ||
+ | <pre>/* | ||
+ | * **************************************************************************** | ||
+ | * RP6 ROBOT SYSTEM - RP6 CONTROL M256 Examples | ||
+ | * **************************************************************************** | ||
+ | * Example: M32 I2C Master 02 | ||
+ | * Author(s): Dirk | ||
+ | * **************************************************************************** | ||
+ | * Description: | ||
+ | * In this example we show how we can control the RP6BASE AND the RP6 CONTROL | ||
+ | * M32 with the RP6v2 M256 WiFi via I2C. | ||
+ | * | ||
+ | * ############################################################################ | ||
+ | * 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! | ||
+ | * You should also connect to it via WIFI. | ||
+ | * ############################################################################ | ||
+ | * **************************************************************************** | ||
+ | */ | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // Includes: | ||
+ | |||
+ | #include "RP6M256Lib.h" // The RP6 M256 Library. | ||
+ | // Always needs to be included! | ||
+ | #include "RP6I2CmasterTWI.h" // Include the I2C-Bus Master Library | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | /*****************************************************************************/ | ||
+ | // Include our new "RP6M256 I2C Master library": | ||
+ | // (This is the library for accessing the RP6BASE!) | ||
+ | |||
+ | #include "RP6M256_I2CMasterLib.h" | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // Include our new "RP6M256 M32 I2C Master library": | ||
+ | // (This is the library for accessing the RP6 CONTROL M32!) | ||
+ | |||
+ | #include "RP6M256_M32_I2CMasterLib.h" | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // M32 state changed event handlers: | ||
+ | // (In these functions you may do what is necessary in order to react | ||
+ | // on the events! Here we only display informations about the changed | ||
+ | // items on the WIFI terminal for testing purposes.) | ||
+ | |||
+ | /** | ||
+ | * M32 mem_cs2 state changed event handler | ||
+ | */ | ||
+ | void mem_cs2StateChanged(void) | ||
+ | { | ||
+ | writeString_P_WIFI("\n\n===> mem_cs2 state changed!!!\n"); | ||
+ | writeString_P_WIFI("Mem_cs2 state: "); | ||
+ | if(M32status.mem_cs2) writeString_P_WIFI("High\n\n"); | ||
+ | else writeString_P_WIFI("Low\n\n"); | ||
+ | } | ||
+ | |||
+ | freeIOs_t last_ins; | ||
+ | |||
+ | /** | ||
+ | * M32 ins state changed event handler | ||
+ | */ | ||
+ | void insStateChanged(void) | ||
+ | { | ||
+ | writeString_P_WIFI("\n\n===> ins state changed!!!\n"); | ||
+ | writeString_P_WIFI("Portpin -> 76543210\n"); | ||
+ | writeString_P_WIFI("[Last ins: "); | ||
+ | writeIntegerLength_WIFI(last_ins.byte, BIN, 8); | ||
+ | writeString_P_WIFI("]\nIns state: "); | ||
+ | writeIntegerLength_WIFI(ins.byte, BIN, 8); | ||
+ | writeChar_WIFI('\n'); | ||
+ | writeChar_WIFI('\n'); | ||
+ | last_ins = ins; | ||
+ | } | ||
+ | |||
+ | uint8_t last_releasedKeyNumber; | ||
+ | |||
+ | /** | ||
+ | * M32 key state changed event handler | ||
+ | */ | ||
+ | void keyStateChanged(void) | ||
+ | { | ||
+ | writeString_P_WIFI("\n\n===> key state changed!!!\n"); | ||
+ | writeString_P_WIFI("ADC_KEYPAD value: "); | ||
+ | writeInteger_WIFI(adcKeypad, DEC); | ||
+ | writeChar_WIFI('\n'); | ||
+ | writeString_P_WIFI("Key is pressed: "); | ||
+ | if(interrupt_M32status.keypressed) writeString_P_WIFI("Yes\n"); | ||
+ | else writeString_P_WIFI("No\n"); | ||
+ | writeString_P_WIFI("[Last rel. key number: "); | ||
+ | writeInteger_WIFI(last_releasedKeyNumber, DEC); | ||
+ | writeString_P_WIFI("]\nReleased key number: "); | ||
+ | writeInteger_WIFI(releasedKeyNumber, DEC); | ||
+ | writeChar_WIFI('\n'); | ||
+ | last_releasedKeyNumber = releasedKeyNumber; | ||
+ | writeString_P_WIFI("Pressed key number: "); | ||
+ | writeInteger_WIFI(pressedKeyNumber, DEC); | ||
+ | writeChar_WIFI('\n'); | ||
+ | writeChar_WIFI('\n'); | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | |||
+ | /** | ||
+ | * The same as sleep() but this delays for time*1ms. | ||
+ | * This function calls task_checkPCINT15(), task_I2CTWI(), | ||
+ | * task_LCDHeartbeat() and task_checkINT() while sleeping. | ||
+ | * | ||
+ | * Example: | ||
+ | * mSleepMaster(100); // delay 100 * 1ms = 100ms = 0.1s | ||
+ | * mSleepMaster(1000); // delay 1000 * 1ms = 1000ms = 1s | ||
+ | * | ||
+ | */ | ||
+ | void mSleepMaster(uint16_t time) | ||
+ | { | ||
+ | while(time--) | ||
+ | { | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_LCDHeartbeat(); | ||
+ | task_checkINT(); | ||
+ | task_I2CTWI(); | ||
+ | sleep(10); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // M32 test functions: | ||
+ | |||
+ | /** | ||
+ | * Here we do a small M32 LED test | ||
+ | * (a running light with direction changing)! | ||
+ | * For timing we use Stopwatch #2. | ||
+ | */ | ||
+ | void task_runningM32Light(void) | ||
+ | { | ||
+ | static uint8_t runLight = 1; | ||
+ | static uint8_t dir; // Moving direction for running light | ||
+ | if(getStopwatch2() > 100) // 100ms | ||
+ | { | ||
+ | // Set status LEDs to the value of the variable runLight: | ||
+ | setM32LEDs(runLight); | ||
+ | |||
+ | // Shift the LED bit left or right depending on direction: | ||
+ | if(dir == 0) | ||
+ | runLight <<= 1; | ||
+ | else | ||
+ | runLight >>= 1; | ||
+ | |||
+ | // Change the direction if we reached one of the two outer LEDs: | ||
+ | if(runLight > 7 ) | ||
+ | dir = 1; | ||
+ | else if (runLight < 2 ) | ||
+ | dir = 0; | ||
+ | |||
+ | // Reset Stopwatch2: | ||
+ | setStopwatch2(0); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Here we do a small M32 LCD test | ||
+ | * | ||
+ | */ | ||
+ | void testM32LCD(void) | ||
+ | { | ||
+ | clearM32LCD(); | ||
+ | writeCharM32LCD('M'); | ||
+ | setCursorPosM32LCD(0, 2); | ||
+ | writeCharM32LCD('3'); | ||
+ | setCursorPosM32LCD(0, 4); | ||
+ | writeCharM32LCD('2'); | ||
+ | setCursorPosM32LCD(0, 6); // Writes M 3 2 | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | writeStringLengthM32LCD("1234567890", 5, 2); // Writes 34567 | ||
+ | setCursorPosM32LCD(1, 0); | ||
+ | writeStringM32LCD(" Hello world!"); | ||
+ | mSleepMaster(3000); | ||
+ | showScreenM32LCD("showScreenM32LCD", "Test 67890123456"); | ||
+ | mSleepMaster(3000); | ||
+ | clearPosM32LCD(1, 5, 4); // Clears 6789 | ||
+ | mSleepMaster(3000); | ||
+ | clearM32LCD(); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | writeIntegerM32LCD(12345, DEC); | ||
+ | setCursorPosM32LCD(1, 0); | ||
+ | writeIntegerLengthM32LCD(12345, DEC, 7); // Writes 0012345 | ||
+ | mSleepMaster(3000); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Here we do a small M32 sound test | ||
+ | * | ||
+ | */ | ||
+ | void testM32Sound(void) | ||
+ | { | ||
+ | sound(Tone_Cis2, 300, 200); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Fis2, 200, 100); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Ais2, 100, 100); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Dis3, 50, 100); | ||
+ | mSleepMaster(1000); | ||
+ | sound(Tone_Dis3, 300, 200); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Ais2, 200, 100); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Fis2, 100, 100); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | sound(Tone_Cis2, 50, 100); | ||
+ | mSleepMaster(3000); | ||
+ | } | ||
+ | |||
+ | #define SPI_EEPROM_ADR 10 | ||
+ | |||
+ | /** | ||
+ | * Here we do a small M32 SPI-EEPROM test | ||
+ | * | ||
+ | */ | ||
+ | void testM32SPIEEPROM(void) | ||
+ | { | ||
+ | uint8_t i, data8; uint8_t dataBuf[4]; | ||
+ | clearM32LCD(); | ||
+ | data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); // Reads from addr 10 | ||
+ | writeStringM32LCD("Before: "); | ||
+ | writeIntegerM32LCD(data8, DEC); // Writes data to M32 LCD | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | SPI_EEPROM_writeByte(SPI_EEPROM_ADR, 10); // Writes 10 to addr 10 | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | SPI_EEPROM_writeByte(SPI_EEPROM_ADR + 1, 11); // Writes 11 to addr 11 | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | SPI_EEPROM_writeWord(SPI_EEPROM_ADR + 2, 3085); // Writes 12, 13 to addr | ||
+ | task_checkPCINT15(); // 12, 13 | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | setCursorPosM32LCD(1, 0); | ||
+ | writeStringM32LCD("After: "); | ||
+ | writeIntegerM32LCD(data8, DEC); | ||
+ | mSleepMaster(3000); | ||
+ | clearM32LCD(); | ||
+ | SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Reads 4 bytes | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | writeStringM32LCD("B: "); | ||
+ | i = 0; | ||
+ | for(; i < 4; i++) { | ||
+ | writeIntegerM32LCD(dataBuf[i], DEC); // Writes data to M32 LCD | ||
+ | writeCharM32LCD(' '); | ||
+ | } | ||
+ | dataBuf[0] = 33; dataBuf[1] = 34; | ||
+ | dataBuf[2] = 35; dataBuf[3] = 36; | ||
+ | SPI_EEPROM_writeBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Writes 33..36 | ||
+ | task_checkPCINT15(); // starting at addr 10 | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | dataBuf[0] = 0; dataBuf[1] = 0; | ||
+ | dataBuf[2] = 0; dataBuf[3] = 0; // Resets data buffer | ||
+ | SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); | ||
+ | task_checkPCINT15(); | ||
+ | task_I2CTWI(); | ||
+ | task_checkINT(); | ||
+ | setCursorPosM32LCD(1, 0); | ||
+ | writeStringM32LCD("A: "); | ||
+ | i = 0; | ||
+ | for(; i < 4; i++) { | ||
+ | writeIntegerM32LCD(dataBuf[i], DEC); | ||
+ | writeCharM32LCD(' '); | ||
+ | } | ||
+ | mSleepMaster(3000); | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // RP6BASE state changed event handlers: | ||
+ | |||
+ | /** | ||
+ | * This is just the same as in the last example - but inside of the | ||
+ | * ACS Event Handler that you already know from the Base Unit. | ||
+ | * You can even use the same variables - obstacle_left and obstacle_right! | ||
+ | */ | ||
+ | void acsStateChanged(void) | ||
+ | { | ||
+ | writeString_P("ACS state changed L: "); | ||
+ | if(obstacle_left) | ||
+ | { | ||
+ | writeChar('o'); | ||
+ | setCursorPosLCD(1, 11); | ||
+ | writeStringLCD_P("LEFT"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | writeChar(' '); | ||
+ | clearPosLCD(1, 11, 4); | ||
+ | } | ||
+ | writeString_P(" | R: "); | ||
+ | if(interrupt_status.obstacleRight) | ||
+ | { | ||
+ | writeChar('o'); | ||
+ | setCursorPosLCD(1, 0); | ||
+ | writeStringLCD_P("RIGHT"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | writeChar(' '); | ||
+ | clearPosLCD(1, 0, 5); | ||
+ | } | ||
+ | if(obstacle_left && obstacle_right) | ||
+ | { | ||
+ | setLEDs(0b0110); | ||
+ | writeString_P(" MIDDLE!"); | ||
+ | setCursorPosLCD(1, 7); | ||
+ | writeStringLCD_P("MID"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | setLEDs(0b0000); | ||
+ | clearPosLCD(1, 7, 3); | ||
+ | } | ||
+ | |||
+ | setLED1(obstacle_right); | ||
+ | setLED4(obstacle_left); | ||
+ | |||
+ | |||
+ | writeChar('\n'); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * The same as for the ACS Event Handler above applies for the Bumpers | ||
+ | * Event Handler! | ||
+ | */ | ||
+ | void bumpersStateChanged(void) | ||
+ | { | ||
+ | // Bumper status changed, output current state and play sounds: | ||
+ | writeString_P("Bumpers changed: "); | ||
+ | if(bumper_right && bumper_left) | ||
+ | { | ||
+ | writeString_P("MIDDLE!"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | if(bumper_left) | ||
+ | { | ||
+ | writeString_P("LEFT!"); | ||
+ | } | ||
+ | else if(bumper_right) | ||
+ | { | ||
+ | writeString_P("RIGHT!"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | writeString_P("FREE!"); | ||
+ | } | ||
+ | } | ||
+ | writeChar('\n'); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * And the RC5 Event Handler is the same as on the Robot Base, too. | ||
+ | */ | ||
+ | void receiveRC5Data(RC5data_t rc5data) | ||
+ | { | ||
+ | // Output the received data: | ||
+ | writeString_P("RC5 Reception --> Toggle Bit:"); | ||
+ | writeChar(rc5data.toggle_bit + '0'); | ||
+ | writeString_P(" | Device Address:"); | ||
+ | writeInteger(rc5data.device, DEC); | ||
+ | writeString_P(" | Key Code:"); | ||
+ | writeInteger(rc5data.key_code, DEC); | ||
+ | writeChar('\n'); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * This is a new Event Handler and it gets called when the Battery Voltage | ||
+ | * is getting low! The Parameter isVoltageLow is true, when the voltage | ||
+ | * is low and false, when the voltage is OK. | ||
+ | */ | ||
+ | void batteryVoltageLow(uint8_t isVoltageLow) | ||
+ | { | ||
+ | if(isVoltageLow) | ||
+ | { | ||
+ | writeString_P("\nBattery Voltage low: "); | ||
+ | // Send Battery voltage to UART: | ||
+ | writeIntegerLength((((adcBat/102.4f)+0.1f)), DEC, 2); | ||
+ | writeChar('.'); | ||
+ | writeIntegerLength((((adcBat/1.024f)+10)), DEC, 2); | ||
+ | writeString_P("V\n"); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | writeString_P("\nBattery Voltage is OK!\n"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // RP6BASE functions: | ||
+ | |||
+ | /** | ||
+ | * This function prints out all ADC values and motor parameters: | ||
+ | * power, desired speed, measured speed and driven distance. | ||
+ | * | ||
+ | * It first calls "getAllSensors()" from the library which reads all | ||
+ | * the sensor values we use here from the Slave Controller. | ||
+ | * Then you can use just the same variables as on the RP6Base to get | ||
+ | * the ADC values. | ||
+ | */ | ||
+ | void printAllSensorValues(void) | ||
+ | { | ||
+ | getAllSensors(); | ||
+ | writeString_P_WIFI("\nRead Sensor Values:\n"); | ||
+ | writeString_P_WIFI("PL:");writeIntegerLength_WIFI(mleft_power,DEC,3); | ||
+ | writeString_P_WIFI(" | PR:");writeIntegerLength_WIFI(mright_power,DEC,3); | ||
+ | writeString_P_WIFI(" | VL:");writeIntegerLength_WIFI(mleft_speed,DEC,3); | ||
+ | writeString_P_WIFI(" | VR:");writeIntegerLength_WIFI(mright_speed,DEC,3); | ||
+ | writeString_P_WIFI(" | DL:");writeIntegerLength_WIFI(mleft_des_speed,DEC,3); | ||
+ | writeString_P_WIFI(" | DR:");writeIntegerLength_WIFI(mright_des_speed,DEC,3); | ||
+ | writeChar_WIFI('\n'); | ||
+ | writeString_P_WIFI("DSTL:");writeIntegerLength_WIFI(mleft_dist,DEC,5); | ||
+ | writeString_P_WIFI(" | DSTR:");writeIntegerLength_WIFI(mright_dist,DEC,5); | ||
+ | writeChar_WIFI('\n'); | ||
+ | writeString_P_WIFI("LSL:");writeIntegerLength_WIFI(adcLSL,DEC,4); | ||
+ | writeString_P_WIFI(" | LSR:");writeIntegerLength_WIFI(adcLSR,DEC,4); | ||
+ | writeString_P_WIFI(" | MCL:");writeIntegerLength_WIFI(adcMotorCurrentLeft,DEC,4); | ||
+ | writeString_P_WIFI(" | MCR:");writeIntegerLength_WIFI(adcMotorCurrentRight,DEC,4); | ||
+ | writeString_P_WIFI(" | BAT:");writeIntegerLength_WIFI(adcBat,DEC,4); | ||
+ | writeString_P_WIFI(" | AD0:");writeIntegerLength_WIFI(adc0,DEC,4); | ||
+ | writeString_P_WIFI(" | AD1:");writeIntegerLength_WIFI(adc1,DEC,4); | ||
+ | writeChar_WIFI('\n'); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Here we do a small RP6BASE LED test | ||
+ | * (a running light with direction changing)! | ||
+ | * For timing we use Stopwatch #3. | ||
+ | */ | ||
+ | void task_runningLight(void) | ||
+ | { | ||
+ | static uint8_t runLight = 1; | ||
+ | static uint8_t dir; // Moving direction for running light | ||
+ | if(getStopwatch3() > 100) // 100ms | ||
+ | { | ||
+ | // Set status LEDs to the value of the variable runLight: | ||
+ | setRP6LEDs(runLight); | ||
+ | |||
+ | // Shift the LED bit left or right depending on direction: | ||
+ | if(dir == 0) | ||
+ | runLight <<= 1; | ||
+ | else | ||
+ | runLight >>= 1; | ||
+ | |||
+ | // Change the direction if we reached one of the two outer LEDs: | ||
+ | if(runLight > 31 ) | ||
+ | dir = 1; | ||
+ | else if (runLight < 2 ) | ||
+ | dir = 0; | ||
+ | |||
+ | // Reset Stopwatch3: | ||
+ | setStopwatch3(0); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // RP6BASE test functions: | ||
+ | |||
+ | /** | ||
+ | * Here we do a small RP6BASE move test | ||
+ | * (a MOVE and ROTATE demo, see M256 WiFi Example_09_Move!)! | ||
+ | */ | ||
+ | void moveDemo(void) | ||
+ | { | ||
+ | setLEDs(0b1001); | ||
+ | showScreenLCD("MOVE", "FWD"); | ||
+ | writeString_P_WIFI("\nMoving Forwards...\n"); | ||
+ | move(70, FWD, DIST_MM(100), BLOCKING); | ||
+ | setLEDs(0b1000); | ||
+ | showScreenLCD("ROTATE", "LEFT"); | ||
+ | writeString_P_WIFI("\nRotating Left...\n"); | ||
+ | rotate(60, LEFT, 90, BLOCKING); | ||
+ | setLEDs(0b1001); | ||
+ | showScreenLCD("MOVE", "FWD"); | ||
+ | writeString_P_WIFI("\nMoving Forwards...\n"); | ||
+ | move(70, FWD, DIST_MM(100), BLOCKING); | ||
+ | setLEDs(0b0001); | ||
+ | showScreenLCD("ROTATE", "RIGHT"); | ||
+ | writeString_P_WIFI("\nRotating Right...\n"); | ||
+ | rotate(60, RIGHT, 90, BLOCKING); | ||
+ | mSleepMaster(3000); | ||
+ | setLEDs(0b0000); | ||
+ | clearLCD(); | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // I2C Requests: | ||
+ | |||
+ | /** | ||
+ | * The I2C_requestedDataReady Event Handler | ||
+ | */ | ||
+ | void I2C_requestedDataReady(uint8_t dataRequestID) | ||
+ | { | ||
+ | if(!checkM32Status(dataRequestID)) | ||
+ | { | ||
+ | if(!checkRP6Status(dataRequestID)) | ||
+ | { | ||
+ | // Here you can check other sensors/microcontrollers with their | ||
+ | // own request IDs - if there are any... | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // I2C Error handler | ||
+ | |||
+ | /** | ||
+ | * This function gets called automatically if there was an I2C Error like | ||
+ | * the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30). | ||
+ | * | ||
+ | */ | ||
+ | void I2C_transmissionError(uint8_t errorState) | ||
+ | { | ||
+ | writeString_P_WIFI("\nI2C ERROR - TWI STATE: 0x"); | ||
+ | writeInteger_WIFI(errorState, HEX); | ||
+ | writeChar_WIFI('\n'); | ||
+ | } | ||
+ | |||
+ | /*****************************************************************************/ | ||
+ | // Main function - The program starts here: | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | initRP6M256(); // Always call this first! The Processor will not work | ||
+ | // correctly otherwise. | ||
+ | |||
+ | initLCD(); // Initialize the LC-Display (LCD) | ||
+ | // Always call this before using the LCD! | ||
+ | |||
+ | setLEDs(0b1111); | ||
+ | mSleep(500); | ||
+ | setLEDs(0b0000); | ||
+ | |||
+ | writeString_P_WIFI("\n\nRP6BASE and RP6 CONTROL M32 I2C Master Example Program!\n"); | ||
+ | writeString_P("\n\nRP6BASE and RP6 CONTROL M32 I2C Master Example Program!\n"); | ||
+ | |||
+ | // IMPORTANT: | ||
+ | I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation | ||
+ | // with 100kHz SCL Frequency | ||
+ | |||
+ | // Register the new M32 event handlers: | ||
+ | MEM_CS2_setStateChangedHandler(mem_cs2StateChanged); | ||
+ | INS_setStateChangedHandler(insStateChanged); | ||
+ | KEY_setStateChangedHandler(keyStateChanged); | ||
+ | |||
+ | // Register the RP6BASE event handlers: | ||
+ | ACS_setStateChangedHandler(acsStateChanged); | ||
+ | BUMPERS_setStateChangedHandler(bumpersStateChanged); | ||
+ | IRCOMM_setRC5DataReadyHandler(receiveRC5Data); | ||
+ | BATTERY_setLowVoltageHandler(batteryVoltageLow); | ||
+ | |||
+ | // Register the event handlers: | ||
+ | I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); | ||
+ | I2CTWI_setRequestedDataReadyHandler(I2C_requestedDataReady); | ||
+ | |||
+ | setLEDs(0b1111); | ||
+ | |||
+ | // Write a text message to the LCD: | ||
+ | showScreenLCD("################", "################"); | ||
+ | mSleep(1500); | ||
+ | showScreenLCD("RP6v2-M256-WIFI ", "Example Program"); | ||
+ | mSleep(2500); | ||
+ | showScreenLCD("RP6BASE and M32", " I2C Master"); | ||
+ | mSleep(2500); | ||
+ | clearLCD(); | ||
+ | |||
+ | setLEDs(0b0000); | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | startStopwatch1(); // For LCDHeartbeat function | ||
+ | // Enable LCD Heartbeat function on M32 Slave: | ||
+ | I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_HEARTBEAT, true); | ||
+ | // (The LCD Heartbeat function lets a '*' character blink with 1Hz on | ||
+ | // the LC-Display of the Slave if the Slave functions properly.) | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | // Enable Software Watchdog timer on M32 Slave: | ||
+ | I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_WDT, true); | ||
+ | // (This timer shows blinking LEDs on the Slave if the Master Controller | ||
+ | // does not react on interrupt requests after a few seconds. This can | ||
+ | // help to find communication problems ...) | ||
+ | waitForM32TransmitComplete(); | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | // Setup RP6BASE ACS power: | ||
+ | I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_ACS_POWER, ACS_PWR_MED); | ||
+ | waitForM32TransmitComplete(); | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | // RP6BASE test program: | ||
+ | moveDemo(); | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | // Enable Watchdog for Interrupt requests: | ||
+ | I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT, true); | ||
+ | // Enable timed watchdog requests: | ||
+ | I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT_RQ, true); | ||
+ | |||
+ | startStopwatch2(); // For runningM32Light function | ||
+ | startStopwatch3(); // For runningLight function | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | // M32 test programs: | ||
+ | testM32LCD(); // M32 LCD test routine | ||
+ | |||
+ | testM32Sound(); // M32 sound test routine | ||
+ | |||
+ | testM32SPIEEPROM(); // M32 SPI-EEPROM test routine | ||
+ | |||
+ | // --------------------------------------- | ||
+ | |||
+ | last_ins.byte = 255; // All M32 ins normally high | ||
+ | |||
+ | while(true) | ||
+ | { | ||
+ | task_checkPCINT15(); // RP6 M32 main task | ||
+ | task_I2CTWI(); | ||
+ | task_LCDHeartbeat(); | ||
+ | task_runningM32Light(); // RP6 M32 running light | ||
+ | |||
+ | task_checkINT(); // RP6BASE main task | ||
+ | task_I2CTWI(); | ||
+ | task_runningLight(); // RP6BASE running light | ||
+ | } | ||
+ | |||
+ | return 0; | ||
+ | }</pre> | ||
====Probleme==== | ====Probleme==== | ||
+ | Ein grundsätzliches Problem bei der parallelen Nutzung der beiden Master-Libs für die Base und M32 ist, dass die jeweiligen Funktionen, falls sie länger dauern oder blockierend sind, nur die zugehörige Interrupt-Routine bedienen. Das kann dann bei aktiviertem Watchdog zu einem Timeout bei der jeweils anderen Plattform führen. | ||
+ | |||
+ | Dafür habe ich noch keine Lösung (außer den Watchdog Timer abzuschalten), solange wir '''zwei''' Master-Libraries haben wollen. Für eine endgültige Lösung dieses Problems müßte man beide Master-Libs zusammenführen und Warteschleifen in allen Funktionen überarbeiten. Daran werde ich mich nicht machen, weil es reicht, einen Watchdog Timer für die RP6BASE zu haben (damit kein Unglück bei Bewegungen des Roboters entsteht!). Auf den M32 Watchdog kann ich da gut verzichten. Er wird in dieser Version des M32 I2C-Slaves auch dauerhaft abgeschaltet, wenn es zu einem Timeout kommt. | ||
==M32 I2C Master Library== | ==M32 I2C Master Library== | ||
Zeile 2.316: | Zeile 3.009: | ||
} | } | ||
}</pre> | }</pre> | ||
+ | |||
=Siehe auch= | =Siehe auch= | ||
Zeile 2.322: | Zeile 3.016: | ||
* [[RP6 - Programmierung]] | * [[RP6 - Programmierung]] | ||
* [[RP6v2]] | * [[RP6v2]] | ||
+ | * [[RP6 Sensor Board und Xtra Module]] | ||
* [[RP6 - Morse-Code]] | * [[RP6 - Morse-Code]] | ||
+ | * [[RP6 Multi IO Projekt - Software]] | ||
+ | * [[RP6 ArduIO - Software]] | ||
* [[CCRP5]] | * [[CCRP5]] | ||
+ | * [[Wild Thumper]] | ||
+ | * [[Wild Thumper - Programmierung]] | ||
* [[Yeti]] | * [[Yeti]] | ||
* [[Asuro]] | * [[Asuro]] | ||
+ | <br/><br/> | ||
=Weblinks= | =Weblinks= | ||
Zeile 2.338: | Zeile 3.038: | ||
=Autoren= | =Autoren= | ||
− | --[ | + | --[http://www.roboternetz.de/community/members/1972-Dirk Dirk] 19:25, 09. Sep 2015 (CET) |
Aktuelle Version vom 9. September 2015, 18:25 Uhr
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!).
- 11. Es soll neben dem Watchdog Event Handler noch drei weitere Event Handler geben:
- Pegel-Wechsel des Portpins PB1 (mem_cs2)
- Pegel-Wechsel der freien IO-Ports PD5, PD6, PC2..7 (ins)
- Tasten-Betätigung
I2C-Slave
Versionen
- V1.20 vom 21.09.2012
- Doppelter Event Handler Aufruf Bug gefixt
- V1.10 vom 18.09.2012
- Bug in task_update() gefixt
- V1.02 vom 16.09.2012
- M32 LCD wird jetzt initialisiert
- Die M32 IO-Portpins lassen sich einzeln als Ein-/Ausgänge konfigurieren
- V1.00 vom 15.09.2012
Slave
Hier das I2C-Slave Programm für die RP6 CONTROL M32.
ACHTUNG: Dieses Programm benötigt die RP6 Control Library in der Version V1.32beta!!!
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_M32status; // Some status bits with current settings and other things. typedef union { uint8_t byte; struct { uint8_t heartbeat:1; uint8_t mem_cs2:1; uint8_t watchDogTimer:1; uint8_t wdtRequest:1; uint8_t wdtRequestEnable:1; uint8_t unused:3; }; } M32status_t; M32status_t M32status; /*****************************************************************************/ /** * Generates Interrupt Signal and starts Software Watchdog */ void signalInterrupt(void) { I2CTWI_dataWasRead = 0; DDRD |= EINT2; // XBUS INT2 PORTD |= EINT2; if(M32status.watchDogTimer) startStopwatch2(); } /** * Clears Interrupt */ void clearInterrupt(void) { stopStopwatch2(); setStopwatch2(0); M32status.wdtRequest = false; interrupt_M32status.mem_cs2Change = false; interrupt_M32status.insChange = false; interrupt_M32status.keyChange = false; PORTD &= ~EINT2; // XBUS INT2 DDRD &= ~EINT2; } freeIOs_t old_ins; M32status_t old_M32status; 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, mem_cs2 * and keypressed status, but this may be expanded in future). */ void task_update(void) { if(getStopwatch4() > 250) { // Update mem_cs2 status: M32status.mem_cs2 = PINB & MEM_CS2; update_count++; setStopwatch4(0); } // Update keypressed status: if(pressedKeyNumber) interrupt_M32status.keypressed = true; else interrupt_M32status.keypressed = false; if(update_count > 5) { // Update mem_cs2Change: if(!interrupt_M32status.mem_cs2Change && (M32status.mem_cs2 != old_M32status.mem_cs2)) { old_M32status.mem_cs2 = M32status.mem_cs2; interrupt_M32status.mem_cs2Change = true; signalInterrupt(); } // Update insChange: if(!interrupt_M32status.insChange && (ins.byte != old_ins.byte)) { old_ins = ins; interrupt_M32status.insChange = true; signalInterrupt(); } // Update keyChange: if(!interrupt_M32status.keyChange && (releasedKeyNumber != old_releasedKeyNumber)) { old_releasedKeyNumber = releasedKeyNumber; interrupt_M32status.keyChange = true; signalInterrupt(); } update_count = 0; } } /*****************************************************************************/ // 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_M32status.byte); I2CTWI_readRegisters[I2C_REG_STATUS2] = (uint8_t)(M32status.byte); I2CTWI_readRegisters[I2C_REG_IO_STATUS] = (uint8_t)(ins.byte); I2CTWI_readRegisters[I2C_REG_MEM_CS2] = (uint8_t)(M32status.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: DDRC &= 0b00000011; DDRC |= (param1 & 0b11111100); DDRD &= 0b10011111; DDRD |= (param1 & 0b01100000); 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: M32status.watchDogTimer = param1 ? true : false; break; case CMD_SET_WDT_RQ: M32status.wdtRequestEnable = param1 ? true : false; break; case CMD_SET_HEARTBEAT: M32status.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 and * the watchdog timer is disabled. * 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(M32status.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_M32status.timeout = true; if(getStopwatch2() > 6000) { setLEDs(0b0000); // Clear LEDs interrupt_M32status.byte = 0; // Clear interrupt status M32status.watchDogTimer = false; // Disable watchdog timer setStopwatch4(0); clearInterrupt(); // Clear interrupt } } else if(getStopwatch3() > 250) { M32status.wdtRequest = true; signalInterrupt(); setStopwatch3(0); } if(interrupt_M32status.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(M32status.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: int main(void) { initRP6Control(); initLCD(); setLEDs(0b1111); mSleep(500); setLEDs(0b0000); sound(180, 80, 25); sound(220, 80, 0); I2CTWI_initSlave(RP6Control_I2C_SLAVE_ADR); M32status.byte = 0; interrupt_M32status.byte = 0; old_ins.byte = 255; // All ins normally high 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)
Vorbereitung
Um für die Funktionsnamen der Sound- und EEPROM-Befehle keine Probleme zu bekommen, müssen wir die aktuelle RP6M256Lib.h (Version 1.1 - 16.07.2012) anpassen.
Die folgenden Zeilen müssen auskommentiert werden:
... uint8_t SPI_EEPROM_readByte(uint16_t memAddr); void SPI_EEPROM_writeByte(uint16_t memAddr, uint8_t data); void SPI_EEPROM_enableWrite(void); void SPI_EEPROM_disableWrite(void); uint8_t SPI_EEPROM_getStatus(void); void SPI_EEPROM_writeBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length); void SPI_EEPROM_readBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length); ... void beep(unsigned char pitch, unsigned int time); void setBeeperPitch(uint8_t pitch); #define sound(_pitch_,_time_,_delay_) {beep(_pitch_,_time_);mSleep(_delay_ + _time_);} ...
Keine Sorge! Dadurch verändert sich in der RP6M256Lib nichts, weil es alle diese Funktionen nicht gibt (es gibt ja auch kein SPI-EEPROM und keinen Beeper auf der M256 WiFi)!
Demos
Single-Slave Master
Dies ist die Demo zur Ansteuerung der RP6 CONTROL M32 durch die RP6v2 M256 WiFi.
Versionen
- V1.30 vom 21.09.2012
- Kleinere Änderungen
- V1.20 vom 18.09.2012
- Sound Test ergänzt
- SPI-EEPROM Tests ergänzt
- Drei Event Handler ergänzt:
- mem_cs2 Pegel-Wechsel
- IO-Port Pegel-Wechsel
- Tasten-Betätigung
- V1.10 vom 16.09.2012
- LCD Tests hinzugefügt
- V1.00 vom 15.09.2012
Programm
Dieses Demo-Programm zeigt eine Demo auf dem M32 LCD, einen kleinen Sound-Test, einen Schreib-/Lesetest auf das SPI-EEPROM und danach ein Lauflicht mit den LEDs der M32,- über I2C von der M256 WiFi gesteuert.
Zusätzlich sind die drei Event Handler aktiv: Betätigt man z.B. eine Taste auf der M32, wird dieser "Event" auf dem WIFI-Terminal des RobotLoaders angezeigt. Angezeigt werden auch Pegel-Wechsel der freien I/O-Ports der M32 oder des Pins mem_cs2 (wenn er zuvor auf Eingang geschaltet wurde!).
Testaufbau: - RP6BASE: Programmspeicher gelöscht - M32: RP6Control_I2CSlave.c (siehe Slave!) - M256 WiFi: RP6M256_M32_I2CMaster.c (siehe unten!) - M32 über PROG_UART Stecker mit PC verbunden - M256 WiFi über WLAN mit PC verbunden - WIFI-Terminal-Fenster des RobotLoaders ausgeklappt - Start des Systems über Serial Loader
Datei RP6M256_M32_I2CMaster.c:
/* * **************************************************************************** * RP6 ROBOT SYSTEM - RP6 CONTROL M256 Examples * **************************************************************************** * Example: M32 I2C Master * Author(s): Dirk * **************************************************************************** * Description: * In this example we show how to react on interrupt requests from the * Microcontroller on the RP6 CONTROL M32. It sets the signal INT2 (that's what * it is called on the RP6 hardware...) which is connected to the external * interrupt PCINT 15 of the Microcontroller. We will NOT use the interrupt * routine to react on the interrupts. Instead we simply poll the interrupt * pin and check if there is an request. This works better together with * the I2C Bus Master routines of the RP6Library. They are also interrupt * based and thus we can not directly start a new transmission out of the * interrupt service routine of a Pin Change Interrupt. * It would not make any difference in this case, but we could get new * problems when there is a I2C Bus transfer in progress when we get * an interrupt event. Then it could get cancelled and replaced by the * new request, which would cause problems sometimes. * * When an Interrupt Event occurs on the Slave Controller, the program * initiates an I2C-Bus request for the first 3 Status Registers. * In the Status registers some bits change depending on what has caused * the interrupt Event (e.g. a key on the RP6 CONTROL M32 has been pressed * or released...) so we can determine what happened and can react on it * accordingly. * * ############################################################################ * 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! * You should also connect to it via WIFI. * ############################################################################ * **************************************************************************** */ /*****************************************************************************/ // Includes: #include "RP6M256Lib.h" // The RP6 M256 Library. // Always needs to be included! #include "RP6I2CmasterTWI.h" // Include the I2C-Bus Master Library /*****************************************************************************/ /*****************************************************************************/ // Include our new "RP6M256 M32 I2C Master library": // (This is the library for accessing the RP6 CONTROL M32!) #include "RP6M256_M32_I2CMasterLib.h" /*****************************************************************************/ // M32 state changed event handlers: // (In these functions you may do what is necessary in order to react // on the events! Here we only display informations about the changed // items on the WIFI terminal for testing purposes.) /** * M32 mem_cs2 state changed event handler */ void mem_cs2StateChanged(void) { writeString_P_WIFI("\n\n===> mem_cs2 state changed!!!\n"); writeString_P_WIFI("Mem_cs2 state: "); if(M32status.mem_cs2) writeString_P_WIFI("High\n\n"); else writeString_P_WIFI("Low\n\n"); } freeIOs_t last_ins; /** * M32 ins state changed event handler */ void insStateChanged(void) { writeString_P_WIFI("\n\n===> ins state changed!!!\n"); writeString_P_WIFI("Portpin -> 76543210\n"); writeString_P_WIFI("[Last ins: "); writeIntegerLength_WIFI(last_ins.byte, BIN, 8); writeString_P_WIFI("]\nIns state: "); writeIntegerLength_WIFI(ins.byte, BIN, 8); writeChar_WIFI('\n'); writeChar_WIFI('\n'); last_ins = ins; } uint8_t last_releasedKeyNumber; /** * M32 key state changed event handler */ void keyStateChanged(void) { writeString_P_WIFI("\n\n===> key state changed!!!\n"); writeString_P_WIFI("ADC_KEYPAD value: "); writeInteger_WIFI(adcKeypad, DEC); writeChar_WIFI('\n'); writeString_P_WIFI("Key is pressed: "); if(interrupt_M32status.keypressed) writeString_P_WIFI("Yes\n"); else writeString_P_WIFI("No\n"); writeString_P_WIFI("[Last rel. key number: "); writeInteger_WIFI(last_releasedKeyNumber, DEC); writeString_P_WIFI("]\nReleased key number: "); writeInteger_WIFI(releasedKeyNumber, DEC); writeChar_WIFI('\n'); last_releasedKeyNumber = releasedKeyNumber; writeString_P_WIFI("Pressed key number: "); writeInteger_WIFI(pressedKeyNumber, DEC); writeChar_WIFI('\n'); writeChar_WIFI('\n'); } /*****************************************************************************/ /** * The same as sleep() but this delays for time*1ms. * This function calls task_checkPCINT15(), task_I2CTWI() * and task_LCDHeartbeat() while sleeping. * * Example: * mSleepMaster(100); // delay 100 * 1ms = 100ms = 0.1s * mSleepMaster(1000); // delay 1000 * 1ms = 1000ms = 1s * */ void mSleepMaster(uint16_t time) { while(time--) { task_checkPCINT15(); task_I2CTWI(); task_LCDHeartbeat(); sleep(10); } } /*****************************************************************************/ // M32 test functions: /** * Here we do a small M32 LED test * (a running light with direction changing)! * For timing we use Stopwatch #2. */ void task_runningM32Light(void) { static uint8_t runLight = 1; static uint8_t dir; // Moving direction for running light if(getStopwatch2() > 100) // 100ms { // Set status LEDs to the value of the variable runLight: setM32LEDs(runLight); // Shift the LED bit left or right depending on direction: if(dir == 0) runLight <<= 1; else runLight >>= 1; // Change the direction if we reached one of the two outer LEDs: if(runLight > 7 ) dir = 1; else if (runLight < 2 ) dir = 0; // Reset Stopwatch2: setStopwatch2(0); } } /** * Here we do a small M32 LCD test * */ void testM32LCD(void) { clearM32LCD(); writeCharM32LCD('M'); setCursorPosM32LCD(0, 2); writeCharM32LCD('3'); setCursorPosM32LCD(0, 4); writeCharM32LCD('2'); setCursorPosM32LCD(0, 6); // Writes M 3 2 task_checkPCINT15(); task_I2CTWI(); writeStringLengthM32LCD("1234567890", 5, 2); // Writes 34567 setCursorPosM32LCD(1, 0); writeStringM32LCD(" Hello world!"); mSleepMaster(3000); showScreenM32LCD("showScreenM32LCD", "Test 67890123456"); mSleepMaster(3000); clearPosM32LCD(1, 5, 4); // Clears 6789 mSleepMaster(3000); clearM32LCD(); task_checkPCINT15(); task_I2CTWI(); writeIntegerM32LCD(12345, DEC); setCursorPosM32LCD(1, 0); writeIntegerLengthM32LCD(12345, DEC, 7); // Writes 0012345 mSleepMaster(3000); } /** * Here we do a small M32 sound test * */ void testM32Sound(void) { sound(Tone_Cis2, 300, 200); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Fis2, 200, 100); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Ais2, 100, 100); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Dis3, 50, 100); mSleepMaster(1000); sound(Tone_Dis3, 300, 200); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Ais2, 200, 100); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Fis2, 100, 100); task_checkPCINT15(); task_I2CTWI(); sound(Tone_Cis2, 50, 100); mSleepMaster(3000); } #define SPI_EEPROM_ADR 10 /** * Here we do a small M32 SPI-EEPROM test * */ void testM32SPIEEPROM(void) { uint8_t i, data8; uint8_t dataBuf[4]; clearM32LCD(); data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); // Reads from addr 10 writeStringM32LCD("Before: "); writeIntegerM32LCD(data8, DEC); // Writes data to M32 LCD task_checkPCINT15(); task_I2CTWI(); SPI_EEPROM_writeByte(SPI_EEPROM_ADR, 10); // Writes 10 to addr 10 task_checkPCINT15(); task_I2CTWI(); SPI_EEPROM_writeByte(SPI_EEPROM_ADR + 1, 11); // Writes 11 to addr 11 task_checkPCINT15(); task_I2CTWI(); SPI_EEPROM_writeWord(SPI_EEPROM_ADR + 2, 3085); // Writes 12, 13 to addr task_checkPCINT15(); // 12, 13 task_I2CTWI(); data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); task_checkPCINT15(); task_I2CTWI(); setCursorPosM32LCD(1, 0); writeStringM32LCD("After: "); writeIntegerM32LCD(data8, DEC); mSleepMaster(3000); clearM32LCD(); SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Reads 4 bytes task_checkPCINT15(); task_I2CTWI(); writeStringM32LCD("B: "); i = 0; for(; i < 4; i++) { writeIntegerM32LCD(dataBuf[i], DEC); // Writes data to M32 LCD writeCharM32LCD(' '); } dataBuf[0] = 33; dataBuf[1] = 34; dataBuf[2] = 35; dataBuf[3] = 36; SPI_EEPROM_writeBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Writes 33..36 task_checkPCINT15(); // starting at addr 10 task_I2CTWI(); dataBuf[0] = 0; dataBuf[1] = 0; dataBuf[2] = 0; dataBuf[3] = 0; // Resets data buffer SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); task_checkPCINT15(); task_I2CTWI(); setCursorPosM32LCD(1, 0); writeStringM32LCD("A: "); i = 0; for(; i < 4; i++) { writeIntegerM32LCD(dataBuf[i], DEC); writeCharM32LCD(' '); } mSleepMaster(3000); } /*****************************************************************************/ // I2C Requests: /** * The I2C_requestedDataReady Event Handler */ void I2C_requestedDataReady(uint8_t dataRequestID) { if(!checkM32Status(dataRequestID)) { // Here you can check other sensors/microcontrollers with their // own request IDs - if there are any... } } /*****************************************************************************/ // I2C Error handler /** * This function gets called automatically if there was an I2C Error like * the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30). * */ void I2C_transmissionError(uint8_t errorState) { writeString_P_WIFI("\nI2C ERROR - TWI STATE: 0x"); writeInteger_WIFI(errorState, HEX); writeChar_WIFI('\n'); } /*****************************************************************************/ // Main function - The program starts here: int main(void) { initRP6M256(); // Always call this first! The Processor will not work // correctly otherwise. initLCD(); // Initialize the LC-Display (LCD) // Always call this before using the LCD! setLEDs(0b1111); mSleep(500); setLEDs(0b0000); writeString_P_WIFI("\n\nRP6 CONTROL M32 I2C Master Example Program!\n"); // IMPORTANT: I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation // with 100kHz SCL Frequency // Register the new M32 event handlers: MEM_CS2_setStateChangedHandler(mem_cs2StateChanged); INS_setStateChangedHandler(insStateChanged); KEY_setStateChangedHandler(keyStateChanged); // Register the event handlers: I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); I2CTWI_setRequestedDataReadyHandler(I2C_requestedDataReady); setLEDs(0b1111); // Write a text message to the LCD: showScreenLCD("################", "################"); mSleep(1500); showScreenLCD("RP6v2-M256-WIFI ", "Example Program"); mSleep(2500); showScreenLCD("RP6 CONTROL M32", " I2C Master"); mSleep(2500); clearLCD(); setLEDs(0b0000); // --------------------------------------- startStopwatch1(); // For LCDHeartbeat function // Enable LCD Heartbeat function on M32 Slave: I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_HEARTBEAT, true); // (The LCD Heartbeat function lets a '*' character blink with 1Hz on // the LC-Display of the Slave if the Slave functions properly.) // --------------------------------------- // Enable Software Watchdog timer on M32 Slave: I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_WDT, true); // (This timer shows blinking LEDs on the Slave if the Master Controller // does not react on interrupt requests after a few seconds. This can // help to find communication problems ...) waitForM32TransmitComplete(); startStopwatch2(); // For runningM32Light function // --------------------------------------- // M32 test programs: testM32LCD(); // M32 LCD test routine testM32Sound(); // M32 sound test routine testM32SPIEEPROM(); // M32 SPI-EEPROM test routine // --------------------------------------- last_ins.byte = 255; // All M32 ins normally high while(true) { task_checkPCINT15(); // RP6 M32 main task task_I2CTWI(); task_LCDHeartbeat(); task_runningM32Light(); // RP6 M32 running light } return 0; }
Dual-Slave Master
Dies ist die Demo zur Ansteuerung der RP6BASE UND der RP6 CONTROL M32 GLEICHZEITIG durch die RP6v2 M256 WiFi.
Versionen
- V1.00 vom 22.09.2012
Programm
Dieses Demo-Programm zeigt die Ansteuerung der RP6BASE zunächst mit einem kleinen MOVE-/ROTATE-Test, dann die Ansteuerung der RP6 CONTROL M32 mit einer Demo auf dem M32 LCD, einen kleinen Sound-Test, einen Schreib-/Lesetest auf das SPI-EEPROM und danach ein Lauflicht mit den LEDs der M32 UND mit den LEDs der RP6BASE,- alles über I2C von der M256 WiFi gesteuert.
Zusätzlich sind die drei Event Handler der M32 aktiv: Betätigt man z.B. eine Taste auf der M32, wird dieser "Event" auf dem WIFI-Terminal des RobotLoaders angezeigt. Angezeigt werden auch Pegel-Wechsel der freien I/O-Ports der M32 oder des Pins mem_cs2 (wenn er zuvor auf Eingang geschaltet wurde!).
Auch die fünf Event Handler der RP6BASE sind aktiv: Das ACS steuert die LEDs der M256 WiFi an, macht Ausgaben auf dem LCD der M256 WiFi und auf dem Serial Terminal des RobotLoaders, wenn das USB-Interface an die M256 WiFi angeschlossen ist. Natürlich wird auch über IRCOMM RC5-Code empfangen, die Batteriespannung und der Bewegungsstatus überwacht und es werden die Bumper ausgewertet.
Testaufbau: - RP6BASE: RP6Base_I2CSlave.c (aus den Demos des RP6!) - M32: RP6Control_I2CSlave.c (siehe Slave!) - M256 WiFi: RP6M256_M32_I2CMaster_02.c (siehe unten!) - M256 WiFi über WLAN und über PROG_UART Stecker mit PC verbunden - Beide Terminal-Fenster des RobotLoaders ausgeklappt - Start des Systems über Serial Loader
Datei RP6M256_M32_I2CMaster_02.c:
/* * **************************************************************************** * RP6 ROBOT SYSTEM - RP6 CONTROL M256 Examples * **************************************************************************** * Example: M32 I2C Master 02 * Author(s): Dirk * **************************************************************************** * Description: * In this example we show how we can control the RP6BASE AND the RP6 CONTROL * M32 with the RP6v2 M256 WiFi via I2C. * * ############################################################################ * 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! * You should also connect to it via WIFI. * ############################################################################ * **************************************************************************** */ /*****************************************************************************/ // Includes: #include "RP6M256Lib.h" // The RP6 M256 Library. // Always needs to be included! #include "RP6I2CmasterTWI.h" // Include the I2C-Bus Master Library /*****************************************************************************/ /*****************************************************************************/ // Include our new "RP6M256 I2C Master library": // (This is the library for accessing the RP6BASE!) #include "RP6M256_I2CMasterLib.h" /*****************************************************************************/ // Include our new "RP6M256 M32 I2C Master library": // (This is the library for accessing the RP6 CONTROL M32!) #include "RP6M256_M32_I2CMasterLib.h" /*****************************************************************************/ // M32 state changed event handlers: // (In these functions you may do what is necessary in order to react // on the events! Here we only display informations about the changed // items on the WIFI terminal for testing purposes.) /** * M32 mem_cs2 state changed event handler */ void mem_cs2StateChanged(void) { writeString_P_WIFI("\n\n===> mem_cs2 state changed!!!\n"); writeString_P_WIFI("Mem_cs2 state: "); if(M32status.mem_cs2) writeString_P_WIFI("High\n\n"); else writeString_P_WIFI("Low\n\n"); } freeIOs_t last_ins; /** * M32 ins state changed event handler */ void insStateChanged(void) { writeString_P_WIFI("\n\n===> ins state changed!!!\n"); writeString_P_WIFI("Portpin -> 76543210\n"); writeString_P_WIFI("[Last ins: "); writeIntegerLength_WIFI(last_ins.byte, BIN, 8); writeString_P_WIFI("]\nIns state: "); writeIntegerLength_WIFI(ins.byte, BIN, 8); writeChar_WIFI('\n'); writeChar_WIFI('\n'); last_ins = ins; } uint8_t last_releasedKeyNumber; /** * M32 key state changed event handler */ void keyStateChanged(void) { writeString_P_WIFI("\n\n===> key state changed!!!\n"); writeString_P_WIFI("ADC_KEYPAD value: "); writeInteger_WIFI(adcKeypad, DEC); writeChar_WIFI('\n'); writeString_P_WIFI("Key is pressed: "); if(interrupt_M32status.keypressed) writeString_P_WIFI("Yes\n"); else writeString_P_WIFI("No\n"); writeString_P_WIFI("[Last rel. key number: "); writeInteger_WIFI(last_releasedKeyNumber, DEC); writeString_P_WIFI("]\nReleased key number: "); writeInteger_WIFI(releasedKeyNumber, DEC); writeChar_WIFI('\n'); last_releasedKeyNumber = releasedKeyNumber; writeString_P_WIFI("Pressed key number: "); writeInteger_WIFI(pressedKeyNumber, DEC); writeChar_WIFI('\n'); writeChar_WIFI('\n'); } /*****************************************************************************/ /** * The same as sleep() but this delays for time*1ms. * This function calls task_checkPCINT15(), task_I2CTWI(), * task_LCDHeartbeat() and task_checkINT() while sleeping. * * Example: * mSleepMaster(100); // delay 100 * 1ms = 100ms = 0.1s * mSleepMaster(1000); // delay 1000 * 1ms = 1000ms = 1s * */ void mSleepMaster(uint16_t time) { while(time--) { task_checkPCINT15(); task_I2CTWI(); task_LCDHeartbeat(); task_checkINT(); task_I2CTWI(); sleep(10); } } /*****************************************************************************/ // M32 test functions: /** * Here we do a small M32 LED test * (a running light with direction changing)! * For timing we use Stopwatch #2. */ void task_runningM32Light(void) { static uint8_t runLight = 1; static uint8_t dir; // Moving direction for running light if(getStopwatch2() > 100) // 100ms { // Set status LEDs to the value of the variable runLight: setM32LEDs(runLight); // Shift the LED bit left or right depending on direction: if(dir == 0) runLight <<= 1; else runLight >>= 1; // Change the direction if we reached one of the two outer LEDs: if(runLight > 7 ) dir = 1; else if (runLight < 2 ) dir = 0; // Reset Stopwatch2: setStopwatch2(0); } } /** * Here we do a small M32 LCD test * */ void testM32LCD(void) { clearM32LCD(); writeCharM32LCD('M'); setCursorPosM32LCD(0, 2); writeCharM32LCD('3'); setCursorPosM32LCD(0, 4); writeCharM32LCD('2'); setCursorPosM32LCD(0, 6); // Writes M 3 2 task_checkPCINT15(); task_I2CTWI(); task_checkINT(); writeStringLengthM32LCD("1234567890", 5, 2); // Writes 34567 setCursorPosM32LCD(1, 0); writeStringM32LCD(" Hello world!"); mSleepMaster(3000); showScreenM32LCD("showScreenM32LCD", "Test 67890123456"); mSleepMaster(3000); clearPosM32LCD(1, 5, 4); // Clears 6789 mSleepMaster(3000); clearM32LCD(); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); writeIntegerM32LCD(12345, DEC); setCursorPosM32LCD(1, 0); writeIntegerLengthM32LCD(12345, DEC, 7); // Writes 0012345 mSleepMaster(3000); } /** * Here we do a small M32 sound test * */ void testM32Sound(void) { sound(Tone_Cis2, 300, 200); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Fis2, 200, 100); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Ais2, 100, 100); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Dis3, 50, 100); mSleepMaster(1000); sound(Tone_Dis3, 300, 200); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Ais2, 200, 100); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Fis2, 100, 100); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); sound(Tone_Cis2, 50, 100); mSleepMaster(3000); } #define SPI_EEPROM_ADR 10 /** * Here we do a small M32 SPI-EEPROM test * */ void testM32SPIEEPROM(void) { uint8_t i, data8; uint8_t dataBuf[4]; clearM32LCD(); data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); // Reads from addr 10 writeStringM32LCD("Before: "); writeIntegerM32LCD(data8, DEC); // Writes data to M32 LCD task_checkPCINT15(); task_I2CTWI(); task_checkINT(); SPI_EEPROM_writeByte(SPI_EEPROM_ADR, 10); // Writes 10 to addr 10 task_checkPCINT15(); task_I2CTWI(); task_checkINT(); SPI_EEPROM_writeByte(SPI_EEPROM_ADR + 1, 11); // Writes 11 to addr 11 task_checkPCINT15(); task_I2CTWI(); task_checkINT(); SPI_EEPROM_writeWord(SPI_EEPROM_ADR + 2, 3085); // Writes 12, 13 to addr task_checkPCINT15(); // 12, 13 task_I2CTWI(); task_checkINT(); data8 = SPI_EEPROM_readByte(SPI_EEPROM_ADR); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); setCursorPosM32LCD(1, 0); writeStringM32LCD("After: "); writeIntegerM32LCD(data8, DEC); mSleepMaster(3000); clearM32LCD(); SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Reads 4 bytes task_checkPCINT15(); task_I2CTWI(); task_checkINT(); writeStringM32LCD("B: "); i = 0; for(; i < 4; i++) { writeIntegerM32LCD(dataBuf[i], DEC); // Writes data to M32 LCD writeCharM32LCD(' '); } dataBuf[0] = 33; dataBuf[1] = 34; dataBuf[2] = 35; dataBuf[3] = 36; SPI_EEPROM_writeBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); // Writes 33..36 task_checkPCINT15(); // starting at addr 10 task_I2CTWI(); task_checkINT(); dataBuf[0] = 0; dataBuf[1] = 0; dataBuf[2] = 0; dataBuf[3] = 0; // Resets data buffer SPI_EEPROM_readBytes(SPI_EEPROM_ADR, &dataBuf[0], 4); task_checkPCINT15(); task_I2CTWI(); task_checkINT(); setCursorPosM32LCD(1, 0); writeStringM32LCD("A: "); i = 0; for(; i < 4; i++) { writeIntegerM32LCD(dataBuf[i], DEC); writeCharM32LCD(' '); } mSleepMaster(3000); } /*****************************************************************************/ // RP6BASE state changed event handlers: /** * This is just the same as in the last example - but inside of the * ACS Event Handler that you already know from the Base Unit. * You can even use the same variables - obstacle_left and obstacle_right! */ void acsStateChanged(void) { writeString_P("ACS state changed L: "); if(obstacle_left) { writeChar('o'); setCursorPosLCD(1, 11); writeStringLCD_P("LEFT"); } else { writeChar(' '); clearPosLCD(1, 11, 4); } writeString_P(" | R: "); if(interrupt_status.obstacleRight) { writeChar('o'); setCursorPosLCD(1, 0); writeStringLCD_P("RIGHT"); } else { writeChar(' '); clearPosLCD(1, 0, 5); } if(obstacle_left && obstacle_right) { setLEDs(0b0110); writeString_P(" MIDDLE!"); setCursorPosLCD(1, 7); writeStringLCD_P("MID"); } else { setLEDs(0b0000); clearPosLCD(1, 7, 3); } setLED1(obstacle_right); setLED4(obstacle_left); writeChar('\n'); } /** * The same as for the ACS Event Handler above applies for the Bumpers * Event Handler! */ void bumpersStateChanged(void) { // Bumper status changed, output current state and play sounds: writeString_P("Bumpers changed: "); if(bumper_right && bumper_left) { writeString_P("MIDDLE!"); } else { if(bumper_left) { writeString_P("LEFT!"); } else if(bumper_right) { writeString_P("RIGHT!"); } else { writeString_P("FREE!"); } } writeChar('\n'); } /** * And the RC5 Event Handler is the same as on the Robot Base, too. */ void receiveRC5Data(RC5data_t rc5data) { // Output the received data: writeString_P("RC5 Reception --> Toggle Bit:"); writeChar(rc5data.toggle_bit + '0'); writeString_P(" | Device Address:"); writeInteger(rc5data.device, DEC); writeString_P(" | Key Code:"); writeInteger(rc5data.key_code, DEC); writeChar('\n'); } /** * This is a new Event Handler and it gets called when the Battery Voltage * is getting low! The Parameter isVoltageLow is true, when the voltage * is low and false, when the voltage is OK. */ void batteryVoltageLow(uint8_t isVoltageLow) { if(isVoltageLow) { writeString_P("\nBattery Voltage low: "); // Send Battery voltage to UART: writeIntegerLength((((adcBat/102.4f)+0.1f)), DEC, 2); writeChar('.'); writeIntegerLength((((adcBat/1.024f)+10)), DEC, 2); writeString_P("V\n"); } else { writeString_P("\nBattery Voltage is OK!\n"); } } /*****************************************************************************/ // RP6BASE functions: /** * This function prints out all ADC values and motor parameters: * power, desired speed, measured speed and driven distance. * * It first calls "getAllSensors()" from the library which reads all * the sensor values we use here from the Slave Controller. * Then you can use just the same variables as on the RP6Base to get * the ADC values. */ void printAllSensorValues(void) { getAllSensors(); writeString_P_WIFI("\nRead Sensor Values:\n"); writeString_P_WIFI("PL:");writeIntegerLength_WIFI(mleft_power,DEC,3); writeString_P_WIFI(" | PR:");writeIntegerLength_WIFI(mright_power,DEC,3); writeString_P_WIFI(" | VL:");writeIntegerLength_WIFI(mleft_speed,DEC,3); writeString_P_WIFI(" | VR:");writeIntegerLength_WIFI(mright_speed,DEC,3); writeString_P_WIFI(" | DL:");writeIntegerLength_WIFI(mleft_des_speed,DEC,3); writeString_P_WIFI(" | DR:");writeIntegerLength_WIFI(mright_des_speed,DEC,3); writeChar_WIFI('\n'); writeString_P_WIFI("DSTL:");writeIntegerLength_WIFI(mleft_dist,DEC,5); writeString_P_WIFI(" | DSTR:");writeIntegerLength_WIFI(mright_dist,DEC,5); writeChar_WIFI('\n'); writeString_P_WIFI("LSL:");writeIntegerLength_WIFI(adcLSL,DEC,4); writeString_P_WIFI(" | LSR:");writeIntegerLength_WIFI(adcLSR,DEC,4); writeString_P_WIFI(" | MCL:");writeIntegerLength_WIFI(adcMotorCurrentLeft,DEC,4); writeString_P_WIFI(" | MCR:");writeIntegerLength_WIFI(adcMotorCurrentRight,DEC,4); writeString_P_WIFI(" | BAT:");writeIntegerLength_WIFI(adcBat,DEC,4); writeString_P_WIFI(" | AD0:");writeIntegerLength_WIFI(adc0,DEC,4); writeString_P_WIFI(" | AD1:");writeIntegerLength_WIFI(adc1,DEC,4); writeChar_WIFI('\n'); } /** * Here we do a small RP6BASE LED test * (a running light with direction changing)! * For timing we use Stopwatch #3. */ void task_runningLight(void) { static uint8_t runLight = 1; static uint8_t dir; // Moving direction for running light if(getStopwatch3() > 100) // 100ms { // Set status LEDs to the value of the variable runLight: setRP6LEDs(runLight); // Shift the LED bit left or right depending on direction: if(dir == 0) runLight <<= 1; else runLight >>= 1; // Change the direction if we reached one of the two outer LEDs: if(runLight > 31 ) dir = 1; else if (runLight < 2 ) dir = 0; // Reset Stopwatch3: setStopwatch3(0); } } /*****************************************************************************/ // RP6BASE test functions: /** * Here we do a small RP6BASE move test * (a MOVE and ROTATE demo, see M256 WiFi Example_09_Move!)! */ void moveDemo(void) { setLEDs(0b1001); showScreenLCD("MOVE", "FWD"); writeString_P_WIFI("\nMoving Forwards...\n"); move(70, FWD, DIST_MM(100), BLOCKING); setLEDs(0b1000); showScreenLCD("ROTATE", "LEFT"); writeString_P_WIFI("\nRotating Left...\n"); rotate(60, LEFT, 90, BLOCKING); setLEDs(0b1001); showScreenLCD("MOVE", "FWD"); writeString_P_WIFI("\nMoving Forwards...\n"); move(70, FWD, DIST_MM(100), BLOCKING); setLEDs(0b0001); showScreenLCD("ROTATE", "RIGHT"); writeString_P_WIFI("\nRotating Right...\n"); rotate(60, RIGHT, 90, BLOCKING); mSleepMaster(3000); setLEDs(0b0000); clearLCD(); } /*****************************************************************************/ // I2C Requests: /** * The I2C_requestedDataReady Event Handler */ void I2C_requestedDataReady(uint8_t dataRequestID) { if(!checkM32Status(dataRequestID)) { if(!checkRP6Status(dataRequestID)) { // Here you can check other sensors/microcontrollers with their // own request IDs - if there are any... } } } /*****************************************************************************/ // I2C Error handler /** * This function gets called automatically if there was an I2C Error like * the slave sent a "not acknowledge" (NACK, error codes e.g. 0x20 or 0x30). * */ void I2C_transmissionError(uint8_t errorState) { writeString_P_WIFI("\nI2C ERROR - TWI STATE: 0x"); writeInteger_WIFI(errorState, HEX); writeChar_WIFI('\n'); } /*****************************************************************************/ // Main function - The program starts here: int main(void) { initRP6M256(); // Always call this first! The Processor will not work // correctly otherwise. initLCD(); // Initialize the LC-Display (LCD) // Always call this before using the LCD! setLEDs(0b1111); mSleep(500); setLEDs(0b0000); writeString_P_WIFI("\n\nRP6BASE and RP6 CONTROL M32 I2C Master Example Program!\n"); writeString_P("\n\nRP6BASE and RP6 CONTROL M32 I2C Master Example Program!\n"); // IMPORTANT: I2CTWI_initMaster(100); // Initialize the TWI Module for Master operation // with 100kHz SCL Frequency // Register the new M32 event handlers: MEM_CS2_setStateChangedHandler(mem_cs2StateChanged); INS_setStateChangedHandler(insStateChanged); KEY_setStateChangedHandler(keyStateChanged); // Register the RP6BASE event handlers: ACS_setStateChangedHandler(acsStateChanged); BUMPERS_setStateChangedHandler(bumpersStateChanged); IRCOMM_setRC5DataReadyHandler(receiveRC5Data); BATTERY_setLowVoltageHandler(batteryVoltageLow); // Register the event handlers: I2CTWI_setTransmissionErrorHandler(I2C_transmissionError); I2CTWI_setRequestedDataReadyHandler(I2C_requestedDataReady); setLEDs(0b1111); // Write a text message to the LCD: showScreenLCD("################", "################"); mSleep(1500); showScreenLCD("RP6v2-M256-WIFI ", "Example Program"); mSleep(2500); showScreenLCD("RP6BASE and M32", " I2C Master"); mSleep(2500); clearLCD(); setLEDs(0b0000); // --------------------------------------- startStopwatch1(); // For LCDHeartbeat function // Enable LCD Heartbeat function on M32 Slave: I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_HEARTBEAT, true); // (The LCD Heartbeat function lets a '*' character blink with 1Hz on // the LC-Display of the Slave if the Slave functions properly.) // --------------------------------------- // Enable Software Watchdog timer on M32 Slave: I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SET_WDT, true); // (This timer shows blinking LEDs on the Slave if the Master Controller // does not react on interrupt requests after a few seconds. This can // help to find communication problems ...) waitForM32TransmitComplete(); // --------------------------------------- // Setup RP6BASE ACS power: I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_ACS_POWER, ACS_PWR_MED); waitForM32TransmitComplete(); // --------------------------------------- // RP6BASE test program: moveDemo(); // --------------------------------------- // Enable Watchdog for Interrupt requests: I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT, true); // Enable timed watchdog requests: I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_WDT_RQ, true); startStopwatch2(); // For runningM32Light function startStopwatch3(); // For runningLight function // --------------------------------------- // M32 test programs: testM32LCD(); // M32 LCD test routine testM32Sound(); // M32 sound test routine testM32SPIEEPROM(); // M32 SPI-EEPROM test routine // --------------------------------------- last_ins.byte = 255; // All M32 ins normally high while(true) { task_checkPCINT15(); // RP6 M32 main task task_I2CTWI(); task_LCDHeartbeat(); task_runningM32Light(); // RP6 M32 running light task_checkINT(); // RP6BASE main task task_I2CTWI(); task_runningLight(); // RP6BASE running light } return 0; }
Probleme
Ein grundsätzliches Problem bei der parallelen Nutzung der beiden Master-Libs für die Base und M32 ist, dass die jeweiligen Funktionen, falls sie länger dauern oder blockierend sind, nur die zugehörige Interrupt-Routine bedienen. Das kann dann bei aktiviertem Watchdog zu einem Timeout bei der jeweils anderen Plattform führen.
Dafür habe ich noch keine Lösung (außer den Watchdog Timer abzuschalten), solange wir zwei Master-Libraries haben wollen. Für eine endgültige Lösung dieses Problems müßte man beide Master-Libs zusammenführen und Warteschleifen in allen Funktionen überarbeiten. Daran werde ich mich nicht machen, weil es reicht, einen Watchdog Timer für die RP6BASE zu haben (damit kein Unglück bei Bewegungen des Roboters entsteht!). Auf den M32 Watchdog kann ich da gut verzichten. Er wird in dieser Version des M32 I2C-Slaves auch dauerhaft abgeschaltet, wenn es zu einem Timeout kommt.
M32 I2C Master Library
Hier die M32 I2C Master Library, die die Befehle enthält, um die RP6 CONTROL M32 über I2C (fast) auf dieselbe Weise zu steuern, wie das mit einem Programm auf der M32 direkt der Fall wäre.
Versionen
- V1.10 vom 21.09.2012
- Neue Funktionen SPI_EEPROM_writeWord() und SPI_EEPROM_readWord() ergänzt
- Ton Frequenzen definiert
- Doppelter Event Handler Aufruf Bug gefixt
- getAllRegisters(): registerBuf[3] Bug gefixt
- V1.00 vom 18.09.2012
- Drei State Changed Handler hinzugefügt und getestet
- SPI-EEPROM Funktionen vervollständigt und getestet
- Bug in der Funktion sound() gefixt
- V0.92beta vom 16.09.2012
- Die LCD Funktionen sind vollständig und getestet
- V0.91beta vom 16.09.2012
- Die IO-Portpins lassen sich jetzt einzeln als Ein-/Ausgänge konfigurieren
- Neue Funktion setFreeIOs()
- Namen von Variablen und Funktionen wurden geändert, um die RP6M256_I2CMasterLib zur Steuerung der RP6v2Base gleichzeitig einbinden zu können. Damit kann die M256 WiFi die Base und M32 parallel als Slaves ansteuern.
- V0.90beta vom 15.09.2012
Header
Datei RP6M256_M32_I2CMasterLib.h:
/* * **************************************************************************** * RP6 ROBOT SYSTEM - RP6 CONTROL M256 Examples * **************************************************************************** * Example: M32 I2C Master Library * Author(s): Dirk * **************************************************************************** * Description: * Header file for new library. * * **************************************************************************** */ #ifndef RP6M256_M32_I2CMASTERLIB_H #define RP6M256_M32_I2CMASTERLIB_H /*****************************************************************************/ // Includes: #include "RP6M256Lib.h" #include "RP6I2CmasterTWI.h" // Define the RP6 CONTROL M32 address here: #define I2C_RP6_M32_ADR 12 /*****************************************************************************/ // These are the same register/command definitions as you can find them in // the I2C Bus Slave Example program for RP6 CONTROL M32: #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 #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 /*****************************************************************************/ // The Status Struct - here we write the data of the main status register. // It is the same definition as it can be found in the RP6Control_I2CSlave // program! 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_M32status; // Some slave status bits with current settings and other things: union { uint8_t byte; struct { uint8_t heartbeat:1; uint8_t mem_cs2:1; uint8_t watchDogTimer:1; uint8_t wdtRequest:1; uint8_t wdtRequestEnable:1; uint8_t unused:3; }; } M32status; // These variables contain the results of the M32 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. These variables // can be found identically in the RP6Control_I2CSlave program! uint8_t spibyte; uint16_t spiword; uint8_t spieepromstatus; uint8_t spieeprombyte; uint16_t spieepromword; /*****************************************************************************/ // M32 slave variables and typedefs // Free M32 IO portpins: typedef union { uint8_t byte; struct { unsigned pd5:1; // IO_PD5 unsigned pd6:1; // IO_PD6 unsigned pc2:1; // IO_PC2 unsigned pc3:1; // IO_PC3 unsigned pc4:1; // IO_PC4 unsigned pc5:1; // IO_PC5 unsigned pc6:1; // IO_PC6 unsigned pc7:1; // IO_PC7 }; } freeIOs_t; extern freeIOs_t ins; extern freeIOs_t outs; // Free M32 ADCs: extern uint16_t adc7; extern uint16_t adc6; extern uint16_t adc5; extern uint16_t adc4; extern uint16_t adc3; extern uint16_t adc2; extern uint16_t adcKeypad; extern uint16_t adcMic; // Key pressed/released number: extern uint8_t releasedKeyNumber; extern uint8_t pressedKeyNumber; // M32 status LEDs: typedef union { uint8_t byte; struct { uint8_t LEDS:4; uint8_t LCDD:4; }; struct { uint8_t LED4:1; uint8_t LED3:1; uint8_t LED2:1; uint8_t LED1:1; uint8_t D0:1; uint8_t D1:1; uint8_t D2:1; uint8_t D3:1; }; } externalPort_t; extern externalPort_t externalPort; // M32 PORTA A/D Convertor channels: #define ADC_7 7 #define ADC_6 6 #define ADC_5 5 #define ADC_4 4 #define ADC_3 3 #define ADC_2 2 #define ADC_KEYPAD 1 #define ADC_MIC 0 /*****************************************************************************/ // Master functions #define PCINT15_STATUS_CHECK 15 void task_LCDHeartbeat(void); void getAllRegisters(void); void task_checkPCINT15(void); uint8_t checkM32Status(uint8_t dataRequestID); void WDT_setRequestM32Handler(void (*requestM32Handler)(void)); void waitForM32TransmitComplete(void); void MEM_CS2_setStateChangedHandler(void (*mem_cs2Handler)(void)); void INS_setStateChangedHandler(void (*insHandler)(void)); void KEY_setStateChangedHandler(void (*keyHandler)(void)); /*****************************************************************************/ // M32 Slave functions uint16_t readM32ADC(uint8_t channel); void setFreeIOs(uint8_t ddios); void setFreeIOsToIN(void); void setFreeIOsToOUT(void); void updateOUTs(void); void setOUTs(uint8_t out); void writeM32SPI(uint8_t data); uint8_t readM32SPI(void); uint16_t readWordM32SPI(void); void writeWordM32SPI(uint16_t data); void writeBufferM32SPI(uint8_t *buffer, uint8_t length); void readBufferM32SPI(uint8_t *buffer, uint8_t length); #define SPI_EEPROM_READ 0x03 // Read byte(s) #define SPI_EEPROM_WRITE 0x02 // Write byte(s) #define SPI_EEPROM_WREN 0x06 // Write Enable #define SPI_EEPROM_WRDI 0x04 // Write Disable #define SPI_EEPROM_RDSR 0x05 // Read Status Register #define SPI_EEPROM_WRSR 0x01 // Write Status Register #define SPI_EEPROM_STAT_WIP 1 // Write in Progress Bit #define SPI_EEPROM_STAT_WEL 2 // Write Enable Latch Bit #define SPI_EEPROM_STAT_BP0 4 // Block Protect 0 Bit #define SPI_EEPROM_STAT_BP1 8 // Block Protect 1 Bit #define SPI_EEPROM_STAT_SRWD 128 // Status Register Write Protect #define SPI_EEPROM_PAGESIZE 64 uint8_t SPI_EEPROM_readByte(uint16_t memAddr); void SPI_EEPROM_writeByte(uint16_t memAddr, uint8_t data); void SPI_EEPROM_enableWrite(void); void SPI_EEPROM_disableWrite(void); uint8_t SPI_EEPROM_getStatus(void); void SPI_EEPROM_writeWord(uint16_t memAddr, uint16_t data); uint16_t SPI_EEPROM_readWord(uint16_t memAddr); void SPI_EEPROM_writeBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length); void SPI_EEPROM_readBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length); void outputExt(void); void setM32LEDs(uint8_t leds); void initM32LCD(void); void clearM32LCD(void); void clearPosM32LCD(uint8_t line, uint8_t pos, uint8_t length); void writeCharM32LCD(uint8_t ch); void writeStringM32LCD(char *string); void writeStringLengthM32LCD(char *string, uint8_t length, uint8_t offset); void showScreenM32LCD(char *line1, char *line2); void writeIntegerM32LCD(int16_t number, uint8_t base); void writeIntegerLengthM32LCD(int16_t number, uint8_t base, uint8_t length); void setCursorPosM32LCD(uint8_t line, uint8_t pos); uint8_t getM32PressedKeyNumber(void); void dischargePeakDetector(void); uint16_t getMicrophonePeak(void); // Define tone frequencies (well temperament): // Great Octave #define Tone_H 2 // 123Hz // Small Octave #define Tone_c 16 // 131Hz #define Tone_cis 30 // 139Hz #define Tone_d 42 // 147Hz #define Tone_dis 54 // 156Hz #define Tone_e 65 // 165Hz #define Tone_f 76 // 175Hz #define Tone_fis 86 // 185Hz #define Tone_g 96 // 196Hz #define Tone_gis 105 // 208Hz #define Tone_a 113 // 220Hz #define Tone_ais 121 // 233Hz #define Tone_h 128 // 247Hz // ' Octave #define Tone_C1 136 // 262Hz #define Tone_Cis1 142 // 277Hz #define Tone_D1 149 // 294Hz #define Tone_Dis1 155 // 311Hz #define Tone_E1 160 // 330Hz #define Tone_F1 166 // 349Hz #define Tone_Fis1 171 // 370Hz #define Tone_G1 175 // 392Hz #define Tone_Gis1 180 // 415Hz #define Tone_A1 184 // 440Hz #define Tone_Ais1 188 // 466Hz #define Tone_H1 192 // 494Hz // '' Octave #define Tone_C2 195 // 523Hz #define Tone_Cis2 199 // 554Hz #define Tone_D2 202 // 587Hz #define Tone_Dis2 205 // 622Hz #define Tone_E2 208 // 659Hz #define Tone_F2 210 // 698Hz #define Tone_Fis2 213 // 740Hz #define Tone_G2 215 // 784Hz #define Tone_Gis2 217 // 831Hz #define Tone_A2 219 // 880Hz #define Tone_Ais2 221 // 932Hz #define Tone_H2 223 // 988Hz // ''' Octave #define Tone_C3 225 // 1047Hz #define Tone_Cis3 227 // 1109Hz #define Tone_D3 228 // 1175Hz #define Tone_Dis3 230 // 1245Hz #define Tone_E3 231 // 1319Hz #define Tone_F3 233 // 1397Hz #define Tone_Fis3 234 // 1480Hz #define Tone_G3 235 // 1568Hz #define Tone_Gis3 236 // 1661Hz #define Tone_A3 237 // 1760Hz #define Tone_Ais3 238 // 1865Hz #define Tone_H3 239 // 1976Hz // '''' Octave #define Tone_C4 240 // 2093Hz #define Tone_Cis4 241 // 2217Hz #define Tone_D4 242 // 2349Hz #define Tone_Dis4 242 // 2489Hz #define Tone_E4 243 // 2637Hz #define Tone_F4 244 // 2794Hz #define Tone_Fis4 244 // 2960Hz #define Tone_G4 245 // 3136Hz #define Tone_Gis4 246 // 3322Hz #define Tone_A4 246 // 3520Hz #define Tone_Ais4 247 // 3729Hz #define Tone_H4 247 // 3951Hz // ''''' Octave #define Tone_C5 248 // 4186Hz void beep(unsigned char pitch, unsigned int time); void setBeeperPitch(uint8_t pitch); void sound(uint8_t pitch, unsigned int time, uint16_t delay); /*****************************************************************************/ #endif
Library
Datei RP6M256_M32_I2CMasterLib.c:
/* * **************************************************************************** * RP6 ROBOT SYSTEM - RP6 CONTROL M256 Examples * **************************************************************************** * Example: M32 I2C Master Library * Author(s): Dirk * **************************************************************************** * Description: * * This is our new Library that allows us to control the M32 nearly the same * as it was with RP6ControlLib. * Some details are different, but in general it is the same. * * **************************************************************************** */ /*****************************************************************************/ // Includes: #include "RP6M256_M32_I2CMasterLib.h" /*****************************************************************************/ // M32 mem_cs2 State changed handler: void MEM_CS2_stateChanged_DUMMY(void){} static void (*MEM_CS2_stateChangedHandler)(void) = MEM_CS2_stateChanged_DUMMY; void MEM_CS2_setStateChangedHandler(void (*mem_cs2Handler)(void)) { MEM_CS2_stateChangedHandler = mem_cs2Handler; } /*****************************************************************************/ // M32 ins State changed handler: void INS_stateChanged_DUMMY(void){} static void (*INS_stateChangedHandler)(void) = INS_stateChanged_DUMMY; void INS_setStateChangedHandler(void (*insHandler)(void)) { INS_stateChangedHandler = insHandler; } /*****************************************************************************/ // M32 key State changed handler: void KEY_stateChanged_DUMMY(void){} static void (*KEY_stateChangedHandler)(void) = KEY_stateChanged_DUMMY; void KEY_setStateChangedHandler(void (*keyHandler)(void)) { KEY_stateChangedHandler = keyHandler; } /*****************************************************************************/ // WDT Request M32 Event Handler: void WDT_request_M32DUMMY(void){} static void (*WDT_requestM32Handler)(void) = WDT_request_M32DUMMY; void WDT_setRequestM32Handler(void (*requestM32Handler)(void)) { WDT_requestM32Handler = requestM32Handler; } /*****************************************************************************/ /** * A small useful routine, to show that the program is running and not * locked up. It lets a '*' character blink with 1Hz on the LC-Display. * When you change the program and it seems to lock up under certain * conditions, you can see if at least the main loop is still working * or if only the I2C Bus Interface is locked up. */ void task_LCDHeartbeat(void) { static uint8_t heartbeat = false; if(getStopwatch1() > 500) { if(heartbeat) { clearPosLCD(1, 15, 1); heartbeat = false; } else { setCursorPosLCD(1, 15); writeStringLCD_P("*"); heartbeat = true; } setStopwatch1(0); } } /*****************************************************************************/ // M32 variables // Here we define the same variables as can be found in the RP6Control // Library. You can use them exactly the same as in a program for the RP6 // CONTROL M32! // Free M32 IO portpins: freeIOs_t ins; freeIOs_t outs; // Free M32 ADCs: uint16_t adc7; uint16_t adc6; uint16_t adc5; uint16_t adc4; uint16_t adc3; uint16_t adc2; uint16_t adcKeypad; uint16_t adcMic; // Key pressed/released number: uint8_t releasedKeyNumber; uint8_t pressedKeyNumber; // M32 status LEDs: externalPort_t externalPort; /*****************************************************************************/ uint8_t registerBuf[30]; /** * In order to use the same register names as in the RP6ControlLib, * this function reads all ADC channels into the same values * as in the RP6ControlLib. * Of course this function needs some time to read all these * 30 registers via the I2C Bus. */ void getAllRegisters(void) { I2CTWI_readRegisters(I2C_RP6_M32_ADR, 0, registerBuf, 30); interrupt_M32status.byte = registerBuf[0]; M32status.byte = registerBuf[1]; ins.byte = registerBuf[2]; M32status.mem_cs2 = registerBuf[3] ? true : false; spibyte = registerBuf[4]; spiword = registerBuf[5] + (registerBuf[6]<<8); spieepromstatus = registerBuf[7]; spieeprombyte = registerBuf[8]; spieepromword = registerBuf[9] + (registerBuf[10]<<8); adc4 = registerBuf[11] + (registerBuf[12]<<8); adc3 = registerBuf[13] + (registerBuf[14]<<8); adc2 = registerBuf[15] + (registerBuf[16]<<8); adc6 = registerBuf[17] + (registerBuf[18]<<8); adc5 = registerBuf[19] + (registerBuf[20]<<8); adc7 = registerBuf[21] + (registerBuf[22]<<8); adcMic = registerBuf[23] + (registerBuf[24]<<8); adcKeypad = registerBuf[25] + (registerBuf[26]<<8); releasedKeyNumber = registerBuf[27]; pressedKeyNumber = registerBuf[28]; externalPort.LEDS = registerBuf[29]; } /*****************************************************************************/ /*****************************************************************************/ // PCINT15 Check: #define PCINT15_STATUS_CHECK 15 uint8_t blockM32 = false; /** * This function has to be called VERY frequently out of the main loop. * Bigger delays result in slower reaction to Interrupt requests of the * Slave. * This function initiates a request of the first 3 Registers of the I2C Bus * Slave Controller - these Bytes contain status bits, which tell us what * caused the Interrupt request. * They are checked in the requested data ready handler which should call * the function checkM32Status below. */ void task_checkPCINT15(void) { if(!blockM32 && (PINJ & INT2_PI15)) // XBUS INT2 -> PJ6 (PCINT15) { blockM32 = true; // Block further requests and wait until // this request has been processed. I2CTWI_requestRegisterFromDevice(I2C_RP6_M32_ADR, PCINT15_STATUS_CHECK, 0, 3); } } /*****************************************************************************/ // Bitmasks for comparison: #define MASK_KEYPRESSED 0b00010000 // keypressed bit #define MASK_KEYCHANGE 0b00001000 // key Change bit #define MASK_INSCHANGE 0b00000100 // ins Change bit #define MASK_MEM_CS2CHANGE 0b00000010 // mem_cs2 Change bit #define MASK_TIMEOUT 0b00000001 // timeout bit uint8_t messageM32Buf[16]; /** * Call this function from the requestedDataReady Handler for the I2C Bus. * It will then check if the request was a PCINT15 Request from this library * and then it will process the event and call event handlers if neccessary. * It returns true if it was a PCINT15 request and false otherwise. * This way you can check if it was a request that you initiated or a * request initiated in the library... */ uint8_t checkM32Status(uint8_t dataRequestID) { if(dataRequestID == PCINT15_STATUS_CHECK) { // Get received data: I2CTWI_getReceivedData(messageM32Buf, 3); interrupt_M32status.byte = messageM32Buf[0]; // Update interrupt status // ------------------------------------ // Check if mem_cs2 status has changed: M32status.byte = messageM32Buf[1]; // Update status if(interrupt_M32status.mem_cs2Change) { MEM_CS2_stateChangedHandler(); } // ------------------------------------ // Check if ins status has changed: ins.byte = messageM32Buf[2]; // Update IO status if(interrupt_M32status.insChange) { INS_stateChangedHandler(); } // ------------------------------------ // Check if key status has changed: if(interrupt_M32status.keyChange) { I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_KEYPAD_L, messageM32Buf, 4); adcKeypad = messageM32Buf[0] + (messageM32Buf[1]<<8); releasedKeyNumber = messageM32Buf[2]; pressedKeyNumber = messageM32Buf[3]; KEY_stateChangedHandler(); } // ------------------------------------ // Check for WDT request: if(M32status.wdtRequest) WDT_requestM32Handler(); blockM32 = false; // clear block flag! return true; // It was a PCINT15 request --> return true } return false; // It was not a PCINT15 request --> return false } /** * Waits until I2C transmission to M32 is complete. * */ void waitForM32TransmitComplete(void) { while(I2CTWI_isBusy() || TWI_operation != I2CTWI_NO_OPERATION) task_I2CTWI(); mSleep(5); task_checkPCINT15(); while(I2CTWI_isBusy() || TWI_operation != I2CTWI_NO_OPERATION) task_I2CTWI(); } /*****************************************************************************/ // M32 slave functions /** * Read ADC channel (10 bit -> result is an integer from 0 to 1023). * The channels (ADC_MIC etc.) are defined in the RP6Control.h and * RP6M256_M32_I2CMasterLib.h files. * */ uint16_t readM32ADC(uint8_t channel) { switch(channel) { case ADC_MIC: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_MIC_L, registerBuf, 2); adcMic = registerBuf[0] + (registerBuf[1]<<8); return adcMic; break; case ADC_KEYPAD: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_KEYPAD_L, registerBuf, 2); adcKeypad = registerBuf[0] + (registerBuf[1]<<8); return adcKeypad; break; case ADC_2: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_2_L, registerBuf, 2); adc2 = registerBuf[0] + (registerBuf[1]<<8); return adc2; break; case ADC_3: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_3_L, registerBuf, 2); adc3 = registerBuf[0] + (registerBuf[1]<<8); return adc3; break; case ADC_4: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_4_L, registerBuf, 2); adc4 = registerBuf[0] + (registerBuf[1]<<8); return adc4; break; case ADC_5: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_5_L, registerBuf, 2); adc5 = registerBuf[0] + (registerBuf[1]<<8); return adc5; break; case ADC_6: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_6_L, registerBuf, 2); adc6 = registerBuf[0] + (registerBuf[1]<<8); return adc6; break; case ADC_7: I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_7_L, registerBuf, 2); adc7 = registerBuf[0] + (registerBuf[1]<<8); return adc7; break; } return 0; } /** * This function sets all free ports (PC2..7, PD5, PD6) to in- or * output depending on ddios! * * Input (ddios): Bit Port * 0 PD5 * 1 PD6 * 2 PC2 * 3 PC3 * 4 PC4 * 5 PC5 * 6 PC6 * 7 PC7 * * Example: setFreeIOs(0b01100001); sets PD5, PC5, PC6 to output * and the other portpins to input. * */ void setFreeIOs(uint8_t ddios) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_CONFIGIOS, ddios); } /** * This function switches all free ports (PC2..7, PD5, PD6) to input! */ void setFreeIOsToIN(void) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_CONFIGIOS, 0); } /** * This function switches all free ports (PC2..7, PD5, PD6) to output! */ void setFreeIOsToOUT(void) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_CONFIGIOS, 255); } /** * Update the ports with current value from the global outs variable! * If one or more port pins were switched to output before, they will be * set (high) or cleared (low). * * Example: * * outs.byte = 0b00101001; * updateOUTs(); * // This clears all ports and sets the ports IO_PD5, IO_PC3 * // and IO_PC5! * * // Other possibility: * outs.pc2 = true; * updateOUTs(); * // This sets IO_PC2 and does not affect any other port! */ void updateOUTs(void) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SETIOS, outs.byte); } /** * Set IO ports - this is very handy if you want to set all ports! * * Example: * * setOUTs(0b00101001); * // This clears all ports and sets the ports IO_PD5, IO_PC3 * // and IO_PC5! */ void setOUTs(uint8_t out) { outs.byte = out; updateOUTs(); } /** * Writes a single Databyte to the SPI Interface. */ void writeM32SPI(uint8_t data) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_WRITESPI, data); waitForM32TransmitComplete(); } /** * Reads a single Databyte from the SPI Interface. */ uint8_t readM32SPI(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_READSPI); waitForM32TransmitComplete(); I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_SPIBYTE, registerBuf, 1); spibyte = registerBuf[0]; return spibyte; } /** * Reads TWO Bytes from the SPI Interface and returns them as * a 16 Bit value. */ uint16_t readWordM32SPI(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_READWORDSPI); waitForM32TransmitComplete(); I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_SPIWORD_L, registerBuf, 2); spiword = registerBuf[0] + (registerBuf[1]<<8); return spiword; } /** * Writes two Bytes contained in the 16 Bit parameter "data". */ void writeWordM32SPI(uint16_t data) { I2CTWI_transmit4Bytes(I2C_RP6_M32_ADR, 0, CMD_WRITEWORDSPI, (data>>8), data); waitForM32TransmitComplete(); } /** * This function writes up to 255 Bytes to the SPI Interface. * The numer of bytes in the Buffer that shall be written is given * by the parameter length. */ void writeBufferM32SPI(uint8_t *buffer, uint8_t length) { uint8_t i = 0; for(; i < length; i++) { writeM32SPI(buffer[i]); } } /** * Reads "length" Bytes from SPI Interface into the buffer. */ void readBufferM32SPI(uint8_t *buffer, uint8_t length) { uint8_t i = 0; for(; i < length; i++) { buffer[i] = readM32SPI(); } } /** * Reads a single Byte from the external EEPROM. */ uint8_t SPI_EEPROM_readByte(uint16_t memAddr) { I2CTWI_transmit4Bytes(I2C_RP6_M32_ADR, 0, CMD_SPI_EEPROM_READBYTE, (memAddr>>8), memAddr); waitForM32TransmitComplete(); I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_SPIEEPROMBYTE, registerBuf, 1); spieeprombyte = registerBuf[0]; return spieeprombyte; } /** * Write a single data byte to the specified EEPROM address. */ void SPI_EEPROM_writeByte(uint16_t memAddr, uint8_t data) { uint8_t msg[5]; msg[0] = 0; msg[1] = CMD_SPI_EEPROM_WRITEBYTE; msg[2] = (memAddr>>8); msg[3] = memAddr; msg[4] = data; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 5); waitForM32TransmitComplete(); } /** * Enable Write Mode */ void SPI_EEPROM_enableWrite(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_SPI_EEPROM_ENABLEWRITE); waitForM32TransmitComplete(); } /** * Disable Write Mode */ void SPI_EEPROM_disableWrite(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_SPI_EEPROM_DISABLEWRITE); waitForM32TransmitComplete(); } /** * Returns EEPROM Status register - for checking if EEPROM is busy. * Writing takes about 5ms. */ uint8_t SPI_EEPROM_getStatus(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_SPI_EEPROM_GETSTATUS); waitForM32TransmitComplete(); I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_SPIEEPROMSTATUS, registerBuf, 1); spieepromstatus = registerBuf[0]; return spieepromstatus; } /** * Write a data word (16 Bit) to the specified EEPROM address. * THE PAGESIZE OF THE EEPROM IS 64 BYTES! * You can NOT cross a page boundary! For example if you write a word * starting at address 63, you will NOT write the word to the EEPROM * byte addresses 63 and 64, but instead only to address 63 and then * it continues at byte 0 and overwrites that byte! So it is a good * idea to write words only to EVEN addresses (0, 2, 4, ...)! */ void SPI_EEPROM_writeWord(uint16_t memAddr, uint16_t data) { uint8_t msg[6]; msg[0] = 0; msg[1] = CMD_SPI_EEPROM_WRITEWORD; msg[2] = (memAddr>>8); msg[3] = memAddr; msg[4] = (data>>8); msg[5] = data; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 6); waitForM32TransmitComplete(); } /** * Reads a data word (16 Bit) from the external EEPROM. */ uint16_t SPI_EEPROM_readWord(uint16_t memAddr) { I2CTWI_transmit4Bytes(I2C_RP6_M32_ADR, 0, CMD_SPI_EEPROM_READWORD, (memAddr>>8), memAddr); waitForM32TransmitComplete(); I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_SPIEEPROMWORD_L, registerBuf, 2); spieepromword = registerBuf[0] + (registerBuf[1]<<8); return spieepromword; } /** * Write "length" Bytes from the Buffer to the EEPROM. */ void SPI_EEPROM_writeBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length) { uint16_t memAddr = startAddr; uint8_t i = 0; for(; i < length; i++) { SPI_EEPROM_writeByte(memAddr, buffer[i]); memAddr++; } } /** * Reads "length" Bytes into the Buffer "buffer" from startAdr on. * You can read the complete EEPROM into a buffer at once - if it is large enough. */ void SPI_EEPROM_readBytes(uint16_t startAddr, uint8_t *buffer, uint8_t length) { uint16_t memAddr = startAddr; uint8_t i = 0; for(; i < length; i++) { buffer[i] = SPI_EEPROM_readByte(memAddr); memAddr++; } } /** * Same as on RP6ControlLib - takes value from externalPort register and sets * LEDs on the M32. */ void outputExt(void) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SETLEDS, externalPort.byte); } /** * Same as setLEDs from RP6ControlLib or RP6M256Lib. Sets LEDs on the M32. * We need to call it setM32LEDs because setLEDs function is already * defined in RP6M256Lib. */ void setM32LEDs(uint8_t leds) { externalPort.LEDS = leds; outputExt(); } /** * Initialize the LCD. Always call this before using the LCD! * */ void initM32LCD(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_INITLCD); waitForM32TransmitComplete(); } /** * Clears the whole LCD! */ void clearM32LCD(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_CLEARLCD); waitForM32TransmitComplete(); } /** * Clears some characters after the given position. */ void clearPosM32LCD(uint8_t line, uint8_t pos, uint8_t length) { uint8_t msg[5]; msg[0] = 0; msg[1] = CMD_CLEARPOSLCD; msg[2] = line; msg[3] = pos; msg[4] = length; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 5); waitForM32TransmitComplete(); } /** * Write a single character to the LCD. * * Example: * * writeCharM32LCD('R'); * writeCharM32LCD('P'); * writeCharM32LCD('6'); * writeCharM32LCD(' '); * writeCharM32LCD('0'); * writeCharM32LCD(48); // 48 is ASCII code for '0' * writeCharM32LCD(49); // '1' * writeCharM32LCD(50); // '2' * writeCharM32LCD(51); // '3' * //... * * would output: * RP6 00123 * at the current cursor position! * use setCursorPosM32LCD function to move the cursor to a * different location! */ void writeCharM32LCD(uint8_t ch) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_WRITECHARLCD, ch); waitForM32TransmitComplete(); } /** * Writes a String from SRAM to the LCD. */ void writeStringM32LCD(char *string) { while(*string) writeCharM32LCD(*string++); } /** * Writes a string with specified length and offset from SRAM to the LCD. * If it is a null terminated string, output will be stopped at the * end. It does not need to be null terminated, but it is recommended * to use only null terminated strings/buffers, otherwise the function could * output any SRAM memory data stored after the string until it reaches a 0 * or the specified length! * * Example: * * writeStringLengthM32LCD("RP6 Robot System",16,0); * // would output: "RP6 Robot System\n" * writeStringLengthM32LCD("RP6 Robot System",11,4); * // would output: "Robot System" * writeStringLengthM32LCD("RP6 Robot System",40,4); * // would output: "Robot System" * // No matter if the specified length is 40 characters! * */ void writeStringLengthM32LCD(char *string, uint8_t length, uint8_t offset) { for(string = &string[offset]; *string && length; length--) writeCharM32LCD(*string++); } /** * This function is useful for displaying text screens on the LCD. * It clears the whole LCD and writes the two Strings to line 1 and * line 2. */ void showScreenM32LCD(char *line1, char *line2) { clearM32LCD(); writeStringM32LCD(line1); setCursorPosM32LCD(1, 0); writeStringM32LCD(line2); } /** * Write a number (with specified base) to the LCD. * * Example: * * // Write a hexadecimal number to the LCD: * writeIntegerM32LCD(0xAACC,16); * // Instead of 16 you can also write "HEX" as this is defined in the * // RP6M256Lib.h : * writeIntegerM32LCD(0xAACC, HEX); * // Other Formats: * writeIntegerM32LCD(1024,DEC); // Decimal * writeIntegerM32LCD(511,OCT); // Octal * writeIntegerM32LCD(0b11010111,BIN); // Binary */ void writeIntegerM32LCD(int16_t number, uint8_t base) { uint8_t msg[5]; msg[0] = 0; msg[1] = CMD_WRITEINTEGERLCD; msg[2] = (number>>8); msg[3] = number; msg[4] = base; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 5); waitForM32TransmitComplete(); } /** * Same as writeIntegerM32LCD, but with defined length. * This means this routine will add leading zeros to the number if length is * larger than the actual value or cut the upper digits if length is smaller * than the actual value. * * Example: * * // Write a hexadecimal number to the LCD: * writeIntegerLengthM32LCD(0xAACC, 16, 8); * // Instead of 16 you can also write "HEX" as this is defined in the * // RP6M256Lib.h : * writeIntegerLengthM32LCD(0xAACC, HEX, 8); * // Other Formats: * writeIntegerLengthM32LCD(1024,DEC,6); // Decimal * writeIntegerLengthM32LCD(511,OCT,4); // Octal * writeIntegerLengthM32LCD(0b11010111,BIN,8); // Binary */ void writeIntegerLengthM32LCD(int16_t number, uint8_t base, uint8_t length) { char buffer[17]; itoa(number, &buffer[0], base); int8_t cnt = length - strlen(buffer); if(cnt > 0) { for(; cnt > 0; cnt--, writeCharM32LCD('0')); writeStringM32LCD(&buffer[0]); } else writeStringLengthM32LCD(&buffer[0],length,-cnt); } /** * Sets the cursor position on LCD. */ void setCursorPosM32LCD(uint8_t line, uint8_t pos) { I2CTWI_transmit4Bytes(I2C_RP6_M32_ADR, 0, CMD_SETCURSORPOSLCD, line, pos); waitForM32TransmitComplete(); } /** * Checks which key is pressed - returns the key number, * or 0, if no key is pressed. * */ uint8_t getM32PressedKeyNumber(void) { I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_PRESSEDKEYNUMBER, registerBuf, 1); pressedKeyNumber = registerBuf[0]; return pressedKeyNumber; } /** * This function discharges the Capacitor of the peak detection circuit * used for the Microphone. This is required to remove any previous * charge from the capacitor. * * Normally this function should NOT be used, because the m32 slave * program does all this automatically! * */ void dischargePeakDetector(void) { I2CTWI_transmit2Bytes(I2C_RP6_M32_ADR, 0, CMD_DISCHARGEPEAKDETECTOR); } /** * Reads the Microphone peak detector. * */ uint16_t getMicrophonePeak(void) { I2CTWI_readRegisters(I2C_RP6_M32_ADR, I2C_REG_ADC_MIC_L, registerBuf, 2); adcMic = registerBuf[0] + (registerBuf[1]<<8); return adcMic; } /** * You can use this function to make the beeper beep ;) * The M32 "beep" function does not generate a delay for the * sound and a delay between two sounds. The M32 "sound" macro, * which uses the function, adds the required delays. * * This function is NOT BLOCKING, but the beep lasts [time] * milliseconds! So you have to wait [time] ms before * starting the next beep or sound!! */ void beep(unsigned char pitch, unsigned int time) { uint8_t msg[5]; msg[0] = 0; msg[1] = CMD_BEEP; msg[2] = pitch; msg[3] = (time>>8); msg[4] = time; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 5); while(I2CTWI_isBusy() || TWI_operation != I2CTWI_NO_OPERATION) task_I2CTWI(); } /** * This function has no timing stuff, but otherwise * it has the same effect as "beep". It only sets the pitch * and this can be used to generate tone sequences which * would sound bad if the beeper turns of for a very short time * in between - such as alarm tones or special melodies etc. */ void setBeeperPitch(uint8_t pitch) { I2CTWI_transmit3Bytes(I2C_RP6_M32_ADR, 0, CMD_SETBEEPERPITCH, pitch); } /** * You can use this function to make the beeper beep ;) * like with the function "beep", but it adds a delay between * two sounds. * * "sound(pitch,time,delay)" * * 0 = lowest frequency * 255 = highest frequency * * Example: * sound(150,50,25); * sound(200,50,25); * * This function is BLOCKING during the sound latency, because * on the M32 slave the "sound" macro is blocking as well! */ void sound(uint8_t pitch, unsigned int time, uint16_t delay) { uint8_t msg[7]; msg[0] = 0; msg[1] = CMD_SOUND; msg[2] = pitch; msg[3] = (time>>8); msg[4] = time; msg[5] = (delay>>8); msg[6] = delay; I2CTWI_transmitBytes(I2C_RP6_M32_ADR, &msg[0], 6); while(I2CTWI_isBusy() || TWI_operation != I2CTWI_NO_OPERATION) task_I2CTWI(); uint16_t latency = time + delay; while(latency--) // M32 sound() macro is blocking! { task_checkPCINT15(); task_I2CTWI(); sleep(10); // Sleep 1ms } }
Siehe auch
- RP6
- RP6 - Programmierung
- RP6v2
- RP6 Sensor Board und Xtra Module
- RP6 - Morse-Code
- RP6 Multi IO Projekt - Software
- RP6 ArduIO - Software
- CCRP5
- Wild Thumper
- Wild Thumper - Programmierung
- Yeti
- Asuro
Weblinks
Autoren
--Dirk 19:25, 09. Sep 2015 (CET)