Mac80
13.04.2012, 13:55
Hallo, ich bin auch neu hier und wollte mich schon mal ganz herzlich für die (indirekte) super Unterstützung im Forum bedanken.
Die Suchmaschine habe ich auch schon benutzt...
Mein Set ist: RP6-Base als Slave und RP6 M32 als Master. Es sind 3 SRF08 Sensoren angebaut, welche eingestellt und am ehemals RP6 Base-Master super funktionierten (modifiziertes Example Programm).
Mein Problem:
Die SRF Sensoren werden im Master-Slave Modus nicht korrekt ausgelesen/ angesprochen.
Der Code im Slave:
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE EXAMPLES
* ************************************************** **************************
* Example: I2C Slave
* Author(s): Dominik S. Herwald
* ************************************************** **************************
* Description:
*
* A very common thing that many users will want to do with their RP6 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-M32 expansion Module)
*
* This programs allows you to control the Robot Base Unit completely via
* I2C-Bus as a slave device!
*
* Also have a look at the RP6 CONTROL M32 example programs which can also
* be found on this CD-ROM - there you get examples of how to use
* this program!
*
* ************************************************** **************************
*/
/************************************************** ***************************/
// Includes:
#include "RP6RobotBaseLib.h"
#include "RP6I2CslaveTWI.h" // Include the I²C-Bus Slave Library
/************************************************** ***************************/
// The Slave Address on the I2C Bus can be specified here:
#define RP6BASE_I2C_SLAVE_ADR 10
#define SRF_LEFT_ADR 0xE6
#define SRF_MIDDLE_ADR 0xE4
#define SRF_RIGHT_ADR 0xE2
/************************************************** ***************************/
// 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 batLow:1;
uint8_t bumperLeft:1;
uint8_t bumperRight:1;
uint8_t RC5reception:1;
uint8_t RC5transmitReady:1;
uint8_t obstacleLeft:1;
uint8_t obstacleRight:1;
uint8_t driveSystemChange:1;
};
} interrupt_status;
// Some status bits with current settings and other things.
union {
uint8_t byte;
struct {
uint8_t powerOn:1;
uint8_t ACSactive:1;
uint8_t watchDogTimer:1;
uint8_t wdtRequest:1;
uint8_t wdtRequestEnable:1;
uint8_t unused:3;
};
} status;
// Drive Status register contains information about current movements.
// You can check if movements are complete, if the motors are turned
// on, if there were overcurrent events and for direction.
union {
uint8_t byte;
struct {
uint8_t movementComplete:1;
uint8_t motorsOn:1;
uint8_t motorOvercurrent:1;
uint8_t direction:2;
uint8_t unused:3;
};
} drive_status;
RC5data_t lastRC5Reception;
/************************************************** ***************************/
/**
* Generates Interrupt Signal and starts Software Watchdog
*/
void signalInterrupt(void)
{
I2CTWI_dataWasRead = 0;
extIntON();
if(status.watchDogTimer)
startStopwatch2();
}
/**
* Clears Interrupt
*/
void clearInterrupt(void)
{
stopStopwatch2();
setStopwatch2(0);
status.wdtRequest = false;
interrupt_status.RC5reception = false;
interrupt_status.driveSystemChange = false;
extIntOFF();
}
/**
* ACS Event Handler
*/
void acsStateChanged(void)
{
interrupt_status.obstacleLeft = obstacle_left;
interrupt_status.obstacleRight = obstacle_right;
signalInterrupt();
}
/**
* Bumpers Event Handler
*/
void bumpersStateChanged(void)
{
interrupt_status.bumperLeft = bumper_left;
if(bumper_right)
interrupt_status.bumperRight = true;
else
interrupt_status.bumperRight = false;
signalInterrupt();
}
/**
* RC5 Event Handler
*/
void receiveRC5Data(RC5data_t rc5data)
{
lastRC5Reception.toggle_bit = rc5data.toggle_bit;
lastRC5Reception.device = rc5data.device;
lastRC5Reception.key_code = rc5data.key_code;
interrupt_status.RC5reception = true;
signalInterrupt();
}
/**
* Motion Control Event Handler
*/
void motionControlStateChanged(void)
{
drive_status.movementComplete = isMovementComplete();
drive_status.motorOvercurrent = motion_status.overcurrent;
interrupt_status.driveSystemChange = true;
signalInterrupt();
}
uint16_t uBat_measure = 720;
uint8_t uBat_count = 0;
/**
* This function needs to be called frequently in the main loop. It updates
* some values (currently only Battery Voltage and Motor status, but this may
* be expanded in future).
*/
void task_update(void)
{
if(getStopwatch4() > 250)
{
uBat_measure += adcBat;
uBat_measure /= 2;
uBat_count++;
setStopwatch2(0);
}
if(uBat_count > 5)
{
if(!interrupt_status.batLow && uBat_measure < 560)
{
interrupt_status.batLow = true;
signalInterrupt();
}
else if(interrupt_status.batLow && uBat_measure > 580)
{
interrupt_status.batLow = false;
signalInterrupt();
}
uBat_count = 0;
}
drive_status.motorsOn = (mleft_power || mright_power);
drive_status.direction = getDirection();
}
/************************************************** ***************************/
// 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_MOTION_STATUS 2
#define I2C_REG_POWER_LEFT 3
#define I2C_REG_POWER_RIGHT 4
#define I2C_REG_SPEED_LEFT 5
#define I2C_REG_SPEED_RIGHT 6
#define I2C_REG_DES_SPEED_LEFT 7
#define I2C_REG_DES_SPEED_RIGHT 8
#define I2C_REG_DIST_LEFT_L 9
#define I2C_REG_DIST_LEFT_H 10
#define I2C_REG_DIST_RIGHT_L 11
#define I2C_REG_DIST_RIGHT_H 12
#define I2C_REG_ADC_LSL_L 13
#define I2C_REG_ADC_LSL_H 14
#define I2C_REG_ADC_LSR_L 15
#define I2C_REG_ADC_LSR_H 16
#define I2C_REG_ADC_MOTOR_CURL_L 17
#define I2C_REG_ADC_MOTOR_CURL_H 18
#define I2C_REG_ADC_MOTOR_CURR_L 19
#define I2C_REG_ADC_MOTOR_CURR_H 20
#define I2C_REG_ADC_UBAT_L 21
#define I2C_REG_ADC_UBAT_H 22
#define I2C_REG_ADC_ADC0_L 23
#define I2C_REG_ADC_ADC0_H 24
#define I2C_REG_ADC_ADC1_L 25
#define I2C_REG_ADC_ADC1_H 26
#define I2C_REG_RC5_ADR 27
#define I2C_REG_RC5_DATA 28
#define I2C_REG_LEDS 29
#define I2C_REG_SRF_LEFT_ADR 30
#define I2C_REG_SRF_MIDDLE_ADR 31
#define I2C_REG_SRF_RIGHT_ADR 32
uint16_t distance_left = 0;
uint16_t distance_right = 0;
uint16_t distance_middle = 0;
/**
* This very important function updates ALL registers that the Master can read.
* It is called frequently out of the Main loop.
*/
void task_updateRegisters(void)
{
if(!I2CTWI_readBusy)
{
I2CTWI_readRegisters[I2C_REG_STATUS1] = (uint8_t)(interrupt_status.byte);
I2CTWI_readRegisters[I2C_REG_STATUS2] = (uint8_t)(status.byte);
I2CTWI_readRegisters[I2C_REG_MOTION_STATUS] = (uint8_t)(drive_status.byte);
I2CTWI_readRegisters[I2C_REG_POWER_LEFT] = (uint8_t)(mleft_power);
I2CTWI_readRegisters[I2C_REG_POWER_RIGHT] = (uint8_t)(mright_power);
I2CTWI_readRegisters[I2C_REG_SPEED_LEFT] = (uint8_t)(getLeftSpeed());
I2CTWI_readRegisters[I2C_REG_SPEED_RIGHT] = (uint8_t)(getRightSpeed());
I2CTWI_readRegisters[I2C_REG_DES_SPEED_LEFT] = (uint8_t)(getDesSpeedLeft());
I2CTWI_readRegisters[I2C_REG_DES_SPEED_RIGHT] = (uint8_t)(getDesSpeedRight());
I2CTWI_readRegisters[I2C_REG_DIST_LEFT_L] = (uint8_t)(getLeftDistance());
I2CTWI_readRegisters[I2C_REG_DIST_LEFT_H] = (uint8_t)(getLeftDistance()>>8);
I2CTWI_readRegisters[I2C_REG_DIST_RIGHT_L] = (uint8_t)(getRightDistance());
I2CTWI_readRegisters[I2C_REG_DIST_RIGHT_H] = (uint8_t)(getRightDistance()>>8);
I2CTWI_readRegisters[I2C_REG_ADC_LSL_L] = (uint8_t)(adcLSL);
I2CTWI_readRegisters[I2C_REG_ADC_LSL_H] = (uint8_t)(adcLSL>>8);
I2CTWI_readRegisters[I2C_REG_ADC_LSR_L] = (uint8_t)(adcLSR);
I2CTWI_readRegisters[I2C_REG_ADC_LSR_H] = (uint8_t)(adcLSR>>8);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURL_L] = (uint8_t)(adcMotorCurrentLeft);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURL_H] = (uint8_t)(adcMotorCurrentLeft>>8);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURR_L] = (uint8_t)(adcMotorCurrentRight);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURR_H] = (uint8_t)(adcMotorCurrentRight>>8);
I2CTWI_readRegisters[I2C_REG_ADC_UBAT_L] = (uint8_t)(adcBat);
I2CTWI_readRegisters[I2C_REG_ADC_UBAT_H] = (uint8_t)(adcBat>>8);
I2CTWI_readRegisters[I2C_REG_ADC_ADC0_L] = (uint8_t)(adc0);
I2CTWI_readRegisters[I2C_REG_ADC_ADC0_H] = (uint8_t)(adc0>>8);
I2CTWI_readRegisters[I2C_REG_ADC_ADC1_L] = (uint8_t)(adc1);
I2CTWI_readRegisters[I2C_REG_ADC_ADC1_H] = (uint8_t)(adc1>>8);
I2CTWI_readRegisters[I2C_REG_LEDS] = (uint8_t)(statusLEDs.byte);
I2CTWI_readRegisters[I2C_REG_RC5_ADR] = (uint8_t)((lastRC5Reception.device)|(lastRC5Recept ion.toggle_bit<<5));
I2CTWI_readRegisters[I2C_REG_RC5_DATA] = (uint8_t)(lastRC5Reception.key_code);
I2CTWI_readRegisters[I2C_REG_SRF_LEFT_ADR] = (uint8_t)(distance_left);
I2CTWI_readRegisters[I2C_REG_SRF_MIDDLE_ADR] = (uint8_t)(distance_middle);
I2CTWI_readRegisters[I2C_REG_SRF_RIGHT_ADR] = (uint8_t)(distance_right);
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 Robot 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:
uint8_t leds = 1;
// Commands:
#define CMD_POWER_OFF 0
#define CMD_POWER_ON 1
#define CMD_CONFIG 2
#define CMD_SETLEDS 3
#define CMD_STOP 4
#define CMD_MOVE_AT_SPEED 5
#define CMD_CHANGE_DIR 6
#define CMD_MOVE 7
#define CMD_ROTATE 8
#define CMD_SET_ACS_POWER 9
#define CMD_SEND_RC5 10
#define CMD_SET_WDT 11
#define CMD_SET_WDT_RQ 12
// Parameters for CMD_SET_ACS_POWER:
#define ACS_PWR_OFF 0
#define ACS_PWR_LOW 1
#define ACS_PWR_MED 2
#define ACS_PWR_HIGH 3
/**
* This function checks if commands have been received and processes them.
*/
void task_commandProcessor(void)
{
if(getCommand())
{
switch(cmd)
{
case CMD_POWER_OFF: powerOFF(); status.powerOn = false; break;
case CMD_POWER_ON: powerON(); status.powerOn = true; break;
case CMD_CONFIG: break;
case CMD_SETLEDS: setLEDs(param1); break;
case CMD_STOP: stop(); break;
case CMD_MOVE_AT_SPEED: moveAtSpeed(param1, param2); break;
case CMD_CHANGE_DIR: changeDirection(param1); break;
case CMD_MOVE: move(param1, param2, ((param3<<8)+param4), false); break;
case CMD_ROTATE: rotate(param1, param2, ((param3<<8)+param4), false); break;
case CMD_SET_ACS_POWER:
switch(param1)
{
case ACS_PWR_OFF:
disableACS(); setACSPwrOff(); status.ACSactive = false;
break;
case ACS_PWR_LOW:
enableACS(); setACSPwrLow(); status.ACSactive = true;
break;
case ACS_PWR_MED:
enableACS(); setACSPwrMed(); status.ACSactive = true;
break;
case ACS_PWR_HIGH:
enableACS(); setACSPwrHigh(); status.ACSactive = true;
break;
}
break;
case CMD_SEND_RC5: IRCOMM_sendRC5(param1, param2); break;
case CMD_SET_WDT: status.watchDogTimer = param1 ? true : false; break;
case CMD_SET_WDT_RQ: status.wdtRequestEnable = param1 ? true : false; break;
}
}
}
/**
* This is the Software watchdog function. After any interrupt event, a timer is
* stated and if a certain amount of time has passed by with no reaction from
* the Master, the Robot is stopped to prevent any damages. Usually the Master
* program has errors or is locked up if it does not react, so this it is
* very good idea to stop moving.
*/
void task_MasterTimeout(void)
{
if(status.watchDogTimer)
{
if( getStopwatch2() > 3000) // 3 seconds timeout for the master to react on
{ // our interrupt events - if he does not react, we
// stop all operations!
cli();
IRCOMM_OFF();
setACSPwrOff();
OCR1AL = 0;
OCR1BL = 0;
TCCR1A = 0;
powerOFF();
writeString_P("\n\n##### EMERGENCY SHUTDOWN #####\n");
writeString_P("##### ALL OPERATIONS STOPPED TO PREVENT ANY DAMAGE! #####\n\n");
writeString_P("The Master Controller did not respond to the interrupt requests\n");
writeString_P("within the defined timeout! Maybe he's locked up!\n");
writeString_P("\nThe Robot needs to be resetted now.\n\n");
while(true) // Rest In Peace
{
setLEDs(0b100010);
uint8_t dly;
for(dly = 10; dly; dly--)
delayCycles(32768);
setLEDs(0b010100);
for(dly = 10; dly; dly--)
delayCycles(32768);
}
}
else if(getStopwatch3() > 250)
{
status.wdtRequest = true;
signalInterrupt();
setStopwatch3(0);
}
}
}
/************************************************** ***************************/
// Main - The program starts here:
int16_t main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);
I2CTWI_initSlave(RP6BASE_I2C_SLAVE_ADR);
ACS_setStateChangedHandler(acsStateChanged);
BUMPERS_setStateChangedHandler(bumpersStateChanged );
IRCOMM_setRC5DataReadyHandler(receiveRC5Data);
MOTIONCONTROL_setStateChangedHandler(motionControl StateChanged);
powerON();
startStopwatch1();
disableACS();
setACSPwrOff();
status.byte = 0;
interrupt_status.byte = 0;
drive_status.byte = 0;
status.watchDogTimer = false;
status.wdtRequestEnable = false;
startStopwatch3();
startStopwatch4();
while(true)
{
task_commandProcessor();
task_update();
task_updateRegisters();
task_RP6System();
task_MasterTimeout();
}
return 0;
}
und der Code im RP6 M32 Master:
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - RP6 CONTROL M32 Examples
* ************************************************** **************************
* Example: I2C Master 8 - Lib
* Author(s): Dominik S. Herwald
* ************************************************** **************************
* Description:
* To make it easier to understand, we have put several routines and definitions
* in a seperate file now.
* We made the library like the RP6Lib - all variable names are just the
* same as in the RP6Lib. Also some functions have the same names
* and parameters.
* There are also all the event handlers like the ACS and Bumpers event
* Handler - plus some new ones. In the next example we will add movement
* functions to the library...
*
* This makes it easier to reuse parts of programs written for the RP6Lib on
* RP6-M32.
*
* In this example we do just the same as in the last program, but we also
* output several sensor values on the serial Interface and enable the
* timed watchdog of the slave, which generates an interrupt every 500ms and
* forces the Master to respond. These requests are shown with a second
* blinking character on the LC-Display. If the Master does not respond within
* 3 seconds, the Robot gets stopped.
*
* ################################################## ##########################
* 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!
* ################################################## ##########################
* ************************************************** **************************
*/
/************************************************** ***************************/
#include "RP6ControlLib.h" // The RP6 Control Library.
#include "RP6I2CmasterTWI.h" // I2C Master Library
#include "RP6Control_I2CMasterLib.h"
// This Library has a lot of new functions for controlling the Robot Base unit
// with the RP6 CONTROL M32 via the I2C Bus.
// In the next example we will add some more functions to it and let one
// of the more complex example programs of the Robot Base run on it.
/************************************************** ***************************/
#define SRF_LEFT_ADR 0xE6
#define SRF_MIDDLE_ADR 0xE4
#define SRF_RIGHT_ADR 0xE2
#define MEASURE_US_LEFT_LOW 0
#define MEASURE_US_LEFT_HIGH 1
#define MEASURE_US_RIGHT_LOW 2
#define MEASURE_US_RIGHT_HIGH 3
#define MEASURE_US_MIDDLE_LOW 4
#define MEASURE_US_MIDDLE_HIGH 5
/**
* 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, 12);
writeStringLCD_P("LEFT");
}
else
{
writeChar(' ');
clearPosLCD(1, 12, 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)
{
externalPort.LEDS = 0b0110;
writeString_P(" MIDDLE!");
setCursorPosLCD(1, 7);
writeStringLCD_P("MID");
}
else
{
externalPort.LEDS = 0b0000;
clearPosLCD(1, 7, 3);
}
if(obstacle_left)
externalPort.LED1 = true;
if(obstacle_right)
externalPort.LED4 = true;
outputExt();
if(obstacle_left && obstacle_right)
{
sound(140,10,0);
}
else
{
if(obstacle_left)
sound(100,5,0);
if(obstacle_right)
sound(120,5,0);
}
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!");
sound(200,100,0);
}
else
{
if(bumper_left)
{
writeString_P("LEFT!");
sound(200,50,10);
sound(150,20,0);
}
else if(bumper_right)
{
writeString_P("RIGHT!");
sound(200,50,10);
sound(150,20,0);
}
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");
}
}
/**
* 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("\nRead Sensor Values:\n");
writeString_P("PL:");writeIntegerLength(mleft_power,DEC,3);
writeString_P(" | PR:");writeIntegerLength(mright_power,DEC,3);
writeString_P(" | VL:");writeIntegerLength(mleft_speed,DEC,3);
writeString_P(" | VR:");writeIntegerLength(mright_speed,DEC,3);
writeString_P(" | DL:");writeIntegerLength(mleft_des_speed,DEC,3);
writeString_P(" | DR:");writeIntegerLength(mright_des_speed,DEC,3);
writeChar('\n');
writeString_P("DSTL:");writeIntegerLength(mleft_dist,DEC,5);
writeString_P(" | DSTR:");writeIntegerLength(mright_dist,DEC,5);
writeChar('\n');
writeString_P("LSL:");writeIntegerLength(adcLSL,DEC,4);
writeString_P(" | LSR:");writeIntegerLength(adcLSR,DEC,4);
writeString_P(" | MCL:");writeIntegerLength(adcMotorCurrentLeft,DEC,4);
writeString_P(" | MCR:");writeIntegerLength(adcMotorCurrentRight,DEC,4);
writeString_P(" | BAT:");writeIntegerLength(adcBat,DEC,4);
writeString_P(" | AD0:");writeIntegerLength(adc0,DEC,4);
writeString_P(" | AD1:");writeIntegerLength(adc1,DEC,4);
//writeString_P(" | SRF_R:");writeIntegerLength(distance_right, DEC,4);
//writeString_P(" | SRF_M:");writeIntegerLength(distance_middle, DEC,4);
//writeString_P(" | SRF_L:");writeIntegerLength(distance_left, DEC,4);
writeChar('\n');
}
/**
* Heartbeat function
*/
void task_LCDHeartbeat(void)
{
if(getStopwatch1() > 500)
{
static uint8_t heartbeat = false;
if(heartbeat)
{
clearPosLCD(0, 15, 1);
heartbeat = false;
}
else
{
setCursorPosLCD(0, 15);
writeStringLCD_P("*");
heartbeat = true;
printAllSensorValues();
}
setStopwatch1(0);
}
}
/**
* Now we have a second "Heartbeat" display
* which shows if the Controller still reacts on
* the Watchdog requests from the Slave Controller!
* And it also shows if the slave Controller is
* still up and running.
*
* It will blink with a rate of about 2Hz when
* the watchdog requests are still active.
*/
void watchDogRequest(void)
{
static uint8_t heartbeat2 = false;
if(heartbeat2)
{
clearPosLCD(0, 14, 1);
heartbeat2 = false;
}
else
{
setCursorPosLCD(0, 14);
writeStringLCD_P("#");
heartbeat2 = true;
}
}
/************************************************** ***************************/
void task_SRF(void)
{
static uint8_t measureInProgress = false;
static uint8_t channel = 0;
if(!measureInProgress) // Start measurement ONCE only
{
if(TWI_operation == I2CTWI_NO_OPERATION) // If there is no request in progress...
{
if(channel == 0)
I2CTWI_transmit2Bytes(SRF_LEFT_ADR, 0, 81);
else if(channel == 1)
I2CTWI_transmit2Bytes(SRF_RIGHT_ADR, 0, 81);
else if(channel == 2)
I2CTWI_transmit2Bytes(SRF_MIDDLE_ADR, 0, 81);
measureInProgress = true;
setStopwatch2(0);
}
}
else if(getStopwatch2() > 1000) // 1000ms (measurement delay)
{
if(channel == 0)
{
I2CTWI_transmitByte(SRF_LEFT_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_LEFT_ADR, MEASURE_US_LEFT_HIGH, 1);
channel = 1;
}
else if(channel == 1)
{
I2CTWI_transmitByte(SRF_RIGHT_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_RIGHT_ADR, MEASURE_US_RIGHT_HIGH, 1);
channel = 2;
}
else if(channel == 2)
{
I2CTWI_transmitByte(SRF_MIDDLE_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_MIDDLE_ADR, MEASURE_US_MIDDLE_HIGH, 1);
channel = 0;
}
measureInProgress = false;
setStopwatch2(0);
}
}
// I2C Requests:
/**
* The I2C_requestedDataReady Event Handler is now a lot smaller.
* It is free for your own request routines.
* You can put them in the Block of the if condition.
* (similar to the RP6 Base Example programs...)
*/
void I2C_requestedDataReady(uint8_t dataRequestID)
{
// We need to check if this is an INT0 Request from the Robot Base unit.
// The Function call inside the if condition returns true if it was an
// interrupt request, so we have to negate it here and if it was NOT
// an interrupt request from the Robot base we can check any other sensors
// from which you may have requested data...
uint16_t distance_left = 0;
uint16_t distance_right = 0;
uint16_t distance_middle = 0;
uint8_t messageBuf[8]; //------------------- erzeugt Fehler im Terminal,wenn es hier steht
static uint8_t dist_tmp;
switch(dataRequestID)
{
case MEASURE_US_LEFT_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_LEFT_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_LEFT_ADR, MEASURE_US_LEFT_LOW, 1);
break;
case MEASURE_US_LEFT_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_left = messageBuf[0] + (dist_tmp << 8);
writeString_P("DistanceL: ");
writeInteger(distance_left, DEC);
writeString_P(" cm ");
break;
case MEASURE_US_RIGHT_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_RIGHT_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_RIGHT_ADR, MEASURE_US_RIGHT_LOW, 1);
break;
case MEASURE_US_RIGHT_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_right = messageBuf[0] + (dist_tmp << 8);
writeString_P("\t\tDistanceR: ");
writeInteger(distance_right, DEC);
writeString_P(" cm\n");
break;
case MEASURE_US_MIDDLE_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_MIDDLE_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_MIDDLE_ADR, MEASURE_US_MIDDLE_LOW, 1);
break;
case MEASURE_US_MIDDLE_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_middle = messageBuf[0] + (dist_tmp << 8);
writeString_P("\t\tDistanceM: ");
writeInteger(distance_middle, DEC);
writeString_P(" cm\n");
break;
}
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("\nI2C ERROR - TWI STATE: 0x");
writeInteger(errorState, HEX);
writeChar('\n');
}
/************************************************** ***************************/
// Main function - The program starts here:
int main(void)
{
initRP6Control();
initLCD();
writeString_P("\n\nRP6 CONTROL M32 I2C Master Example Program!\n");
writeString_P("\nInterrupts - part 2...\n");
// ---------------------------------------
// The Event Handlers can be set the same way as with the
// RP6Lib:
ACS_setStateChangedHandler(acsStateChanged);
BUMPERS_setStateChangedHandler(bumpersStateChanged );
IRCOMM_setRC5DataReadyHandler(receiveRC5Data);
// New LowBat Event Handler:
BATTERY_setLowVoltageHandler(batteryVoltageLow);
// New Watchdog Request Event Handler:
WDT_setRequestHandler(watchDogRequest);
// ---------------------------------------
// Init TWI Interface:
I2CTWI_initMaster(100);
I2CTWI_setRequestedDataReadyHandler(I2C_requestedD ataReady);
I2CTWI_setTransmissionErrorHandler(I2C_transmissio nError);
sound(180,80,25);
sound(220,80,25);
setLEDs(0b1111);
showScreenLCD("################", "################");
mSleep(500);
showScreenLCD("I2C-Master", "Example Program 3");
mSleep(1000);
setLEDs(0b0000);
// ---------------------------------------
// Setup ACS power:
I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_ACS_POWER, ACS_PWR_MED);
// 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);
showScreenLCD("ACS Status:", "");
startStopwatch1();
startStopwatch2();
while(true)
{
task_SRF();
task_LCDHeartbeat();
task_checkINT0();
task_I2CTWI();
}
return 0;
}
Im Praxisfall macht der RP6 "Sensortests" und je eine Messung mit dem SRF während ein IR Sensor einen Gegenstand detektiert. Nach einiger Zeit kommt eine "RC5 Reception --> Toggle Bit:0 | Device Adresse:0 | Key Code:0" Meldung im Terminal.
Ich schätze mal, das Lesen oder Schreiben von Register in RC5 Bereich falsch laufen.
Kann mir jemand damit helfen, oder zumindest in die richtige Richtung schieben?
Vielen Dank im Voraus.
Mfg, Mac80
Die Suchmaschine habe ich auch schon benutzt...
Mein Set ist: RP6-Base als Slave und RP6 M32 als Master. Es sind 3 SRF08 Sensoren angebaut, welche eingestellt und am ehemals RP6 Base-Master super funktionierten (modifiziertes Example Programm).
Mein Problem:
Die SRF Sensoren werden im Master-Slave Modus nicht korrekt ausgelesen/ angesprochen.
Der Code im Slave:
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - ROBOT BASE EXAMPLES
* ************************************************** **************************
* Example: I2C Slave
* Author(s): Dominik S. Herwald
* ************************************************** **************************
* Description:
*
* A very common thing that many users will want to do with their RP6 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-M32 expansion Module)
*
* This programs allows you to control the Robot Base Unit completely via
* I2C-Bus as a slave device!
*
* Also have a look at the RP6 CONTROL M32 example programs which can also
* be found on this CD-ROM - there you get examples of how to use
* this program!
*
* ************************************************** **************************
*/
/************************************************** ***************************/
// Includes:
#include "RP6RobotBaseLib.h"
#include "RP6I2CslaveTWI.h" // Include the I²C-Bus Slave Library
/************************************************** ***************************/
// The Slave Address on the I2C Bus can be specified here:
#define RP6BASE_I2C_SLAVE_ADR 10
#define SRF_LEFT_ADR 0xE6
#define SRF_MIDDLE_ADR 0xE4
#define SRF_RIGHT_ADR 0xE2
/************************************************** ***************************/
// 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 batLow:1;
uint8_t bumperLeft:1;
uint8_t bumperRight:1;
uint8_t RC5reception:1;
uint8_t RC5transmitReady:1;
uint8_t obstacleLeft:1;
uint8_t obstacleRight:1;
uint8_t driveSystemChange:1;
};
} interrupt_status;
// Some status bits with current settings and other things.
union {
uint8_t byte;
struct {
uint8_t powerOn:1;
uint8_t ACSactive:1;
uint8_t watchDogTimer:1;
uint8_t wdtRequest:1;
uint8_t wdtRequestEnable:1;
uint8_t unused:3;
};
} status;
// Drive Status register contains information about current movements.
// You can check if movements are complete, if the motors are turned
// on, if there were overcurrent events and for direction.
union {
uint8_t byte;
struct {
uint8_t movementComplete:1;
uint8_t motorsOn:1;
uint8_t motorOvercurrent:1;
uint8_t direction:2;
uint8_t unused:3;
};
} drive_status;
RC5data_t lastRC5Reception;
/************************************************** ***************************/
/**
* Generates Interrupt Signal and starts Software Watchdog
*/
void signalInterrupt(void)
{
I2CTWI_dataWasRead = 0;
extIntON();
if(status.watchDogTimer)
startStopwatch2();
}
/**
* Clears Interrupt
*/
void clearInterrupt(void)
{
stopStopwatch2();
setStopwatch2(0);
status.wdtRequest = false;
interrupt_status.RC5reception = false;
interrupt_status.driveSystemChange = false;
extIntOFF();
}
/**
* ACS Event Handler
*/
void acsStateChanged(void)
{
interrupt_status.obstacleLeft = obstacle_left;
interrupt_status.obstacleRight = obstacle_right;
signalInterrupt();
}
/**
* Bumpers Event Handler
*/
void bumpersStateChanged(void)
{
interrupt_status.bumperLeft = bumper_left;
if(bumper_right)
interrupt_status.bumperRight = true;
else
interrupt_status.bumperRight = false;
signalInterrupt();
}
/**
* RC5 Event Handler
*/
void receiveRC5Data(RC5data_t rc5data)
{
lastRC5Reception.toggle_bit = rc5data.toggle_bit;
lastRC5Reception.device = rc5data.device;
lastRC5Reception.key_code = rc5data.key_code;
interrupt_status.RC5reception = true;
signalInterrupt();
}
/**
* Motion Control Event Handler
*/
void motionControlStateChanged(void)
{
drive_status.movementComplete = isMovementComplete();
drive_status.motorOvercurrent = motion_status.overcurrent;
interrupt_status.driveSystemChange = true;
signalInterrupt();
}
uint16_t uBat_measure = 720;
uint8_t uBat_count = 0;
/**
* This function needs to be called frequently in the main loop. It updates
* some values (currently only Battery Voltage and Motor status, but this may
* be expanded in future).
*/
void task_update(void)
{
if(getStopwatch4() > 250)
{
uBat_measure += adcBat;
uBat_measure /= 2;
uBat_count++;
setStopwatch2(0);
}
if(uBat_count > 5)
{
if(!interrupt_status.batLow && uBat_measure < 560)
{
interrupt_status.batLow = true;
signalInterrupt();
}
else if(interrupt_status.batLow && uBat_measure > 580)
{
interrupt_status.batLow = false;
signalInterrupt();
}
uBat_count = 0;
}
drive_status.motorsOn = (mleft_power || mright_power);
drive_status.direction = getDirection();
}
/************************************************** ***************************/
// 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_MOTION_STATUS 2
#define I2C_REG_POWER_LEFT 3
#define I2C_REG_POWER_RIGHT 4
#define I2C_REG_SPEED_LEFT 5
#define I2C_REG_SPEED_RIGHT 6
#define I2C_REG_DES_SPEED_LEFT 7
#define I2C_REG_DES_SPEED_RIGHT 8
#define I2C_REG_DIST_LEFT_L 9
#define I2C_REG_DIST_LEFT_H 10
#define I2C_REG_DIST_RIGHT_L 11
#define I2C_REG_DIST_RIGHT_H 12
#define I2C_REG_ADC_LSL_L 13
#define I2C_REG_ADC_LSL_H 14
#define I2C_REG_ADC_LSR_L 15
#define I2C_REG_ADC_LSR_H 16
#define I2C_REG_ADC_MOTOR_CURL_L 17
#define I2C_REG_ADC_MOTOR_CURL_H 18
#define I2C_REG_ADC_MOTOR_CURR_L 19
#define I2C_REG_ADC_MOTOR_CURR_H 20
#define I2C_REG_ADC_UBAT_L 21
#define I2C_REG_ADC_UBAT_H 22
#define I2C_REG_ADC_ADC0_L 23
#define I2C_REG_ADC_ADC0_H 24
#define I2C_REG_ADC_ADC1_L 25
#define I2C_REG_ADC_ADC1_H 26
#define I2C_REG_RC5_ADR 27
#define I2C_REG_RC5_DATA 28
#define I2C_REG_LEDS 29
#define I2C_REG_SRF_LEFT_ADR 30
#define I2C_REG_SRF_MIDDLE_ADR 31
#define I2C_REG_SRF_RIGHT_ADR 32
uint16_t distance_left = 0;
uint16_t distance_right = 0;
uint16_t distance_middle = 0;
/**
* This very important function updates ALL registers that the Master can read.
* It is called frequently out of the Main loop.
*/
void task_updateRegisters(void)
{
if(!I2CTWI_readBusy)
{
I2CTWI_readRegisters[I2C_REG_STATUS1] = (uint8_t)(interrupt_status.byte);
I2CTWI_readRegisters[I2C_REG_STATUS2] = (uint8_t)(status.byte);
I2CTWI_readRegisters[I2C_REG_MOTION_STATUS] = (uint8_t)(drive_status.byte);
I2CTWI_readRegisters[I2C_REG_POWER_LEFT] = (uint8_t)(mleft_power);
I2CTWI_readRegisters[I2C_REG_POWER_RIGHT] = (uint8_t)(mright_power);
I2CTWI_readRegisters[I2C_REG_SPEED_LEFT] = (uint8_t)(getLeftSpeed());
I2CTWI_readRegisters[I2C_REG_SPEED_RIGHT] = (uint8_t)(getRightSpeed());
I2CTWI_readRegisters[I2C_REG_DES_SPEED_LEFT] = (uint8_t)(getDesSpeedLeft());
I2CTWI_readRegisters[I2C_REG_DES_SPEED_RIGHT] = (uint8_t)(getDesSpeedRight());
I2CTWI_readRegisters[I2C_REG_DIST_LEFT_L] = (uint8_t)(getLeftDistance());
I2CTWI_readRegisters[I2C_REG_DIST_LEFT_H] = (uint8_t)(getLeftDistance()>>8);
I2CTWI_readRegisters[I2C_REG_DIST_RIGHT_L] = (uint8_t)(getRightDistance());
I2CTWI_readRegisters[I2C_REG_DIST_RIGHT_H] = (uint8_t)(getRightDistance()>>8);
I2CTWI_readRegisters[I2C_REG_ADC_LSL_L] = (uint8_t)(adcLSL);
I2CTWI_readRegisters[I2C_REG_ADC_LSL_H] = (uint8_t)(adcLSL>>8);
I2CTWI_readRegisters[I2C_REG_ADC_LSR_L] = (uint8_t)(adcLSR);
I2CTWI_readRegisters[I2C_REG_ADC_LSR_H] = (uint8_t)(adcLSR>>8);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURL_L] = (uint8_t)(adcMotorCurrentLeft);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURL_H] = (uint8_t)(adcMotorCurrentLeft>>8);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURR_L] = (uint8_t)(adcMotorCurrentRight);
I2CTWI_readRegisters[I2C_REG_ADC_MOTOR_CURR_H] = (uint8_t)(adcMotorCurrentRight>>8);
I2CTWI_readRegisters[I2C_REG_ADC_UBAT_L] = (uint8_t)(adcBat);
I2CTWI_readRegisters[I2C_REG_ADC_UBAT_H] = (uint8_t)(adcBat>>8);
I2CTWI_readRegisters[I2C_REG_ADC_ADC0_L] = (uint8_t)(adc0);
I2CTWI_readRegisters[I2C_REG_ADC_ADC0_H] = (uint8_t)(adc0>>8);
I2CTWI_readRegisters[I2C_REG_ADC_ADC1_L] = (uint8_t)(adc1);
I2CTWI_readRegisters[I2C_REG_ADC_ADC1_H] = (uint8_t)(adc1>>8);
I2CTWI_readRegisters[I2C_REG_LEDS] = (uint8_t)(statusLEDs.byte);
I2CTWI_readRegisters[I2C_REG_RC5_ADR] = (uint8_t)((lastRC5Reception.device)|(lastRC5Recept ion.toggle_bit<<5));
I2CTWI_readRegisters[I2C_REG_RC5_DATA] = (uint8_t)(lastRC5Reception.key_code);
I2CTWI_readRegisters[I2C_REG_SRF_LEFT_ADR] = (uint8_t)(distance_left);
I2CTWI_readRegisters[I2C_REG_SRF_MIDDLE_ADR] = (uint8_t)(distance_middle);
I2CTWI_readRegisters[I2C_REG_SRF_RIGHT_ADR] = (uint8_t)(distance_right);
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 Robot 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:
uint8_t leds = 1;
// Commands:
#define CMD_POWER_OFF 0
#define CMD_POWER_ON 1
#define CMD_CONFIG 2
#define CMD_SETLEDS 3
#define CMD_STOP 4
#define CMD_MOVE_AT_SPEED 5
#define CMD_CHANGE_DIR 6
#define CMD_MOVE 7
#define CMD_ROTATE 8
#define CMD_SET_ACS_POWER 9
#define CMD_SEND_RC5 10
#define CMD_SET_WDT 11
#define CMD_SET_WDT_RQ 12
// Parameters for CMD_SET_ACS_POWER:
#define ACS_PWR_OFF 0
#define ACS_PWR_LOW 1
#define ACS_PWR_MED 2
#define ACS_PWR_HIGH 3
/**
* This function checks if commands have been received and processes them.
*/
void task_commandProcessor(void)
{
if(getCommand())
{
switch(cmd)
{
case CMD_POWER_OFF: powerOFF(); status.powerOn = false; break;
case CMD_POWER_ON: powerON(); status.powerOn = true; break;
case CMD_CONFIG: break;
case CMD_SETLEDS: setLEDs(param1); break;
case CMD_STOP: stop(); break;
case CMD_MOVE_AT_SPEED: moveAtSpeed(param1, param2); break;
case CMD_CHANGE_DIR: changeDirection(param1); break;
case CMD_MOVE: move(param1, param2, ((param3<<8)+param4), false); break;
case CMD_ROTATE: rotate(param1, param2, ((param3<<8)+param4), false); break;
case CMD_SET_ACS_POWER:
switch(param1)
{
case ACS_PWR_OFF:
disableACS(); setACSPwrOff(); status.ACSactive = false;
break;
case ACS_PWR_LOW:
enableACS(); setACSPwrLow(); status.ACSactive = true;
break;
case ACS_PWR_MED:
enableACS(); setACSPwrMed(); status.ACSactive = true;
break;
case ACS_PWR_HIGH:
enableACS(); setACSPwrHigh(); status.ACSactive = true;
break;
}
break;
case CMD_SEND_RC5: IRCOMM_sendRC5(param1, param2); break;
case CMD_SET_WDT: status.watchDogTimer = param1 ? true : false; break;
case CMD_SET_WDT_RQ: status.wdtRequestEnable = param1 ? true : false; break;
}
}
}
/**
* This is the Software watchdog function. After any interrupt event, a timer is
* stated and if a certain amount of time has passed by with no reaction from
* the Master, the Robot is stopped to prevent any damages. Usually the Master
* program has errors or is locked up if it does not react, so this it is
* very good idea to stop moving.
*/
void task_MasterTimeout(void)
{
if(status.watchDogTimer)
{
if( getStopwatch2() > 3000) // 3 seconds timeout for the master to react on
{ // our interrupt events - if he does not react, we
// stop all operations!
cli();
IRCOMM_OFF();
setACSPwrOff();
OCR1AL = 0;
OCR1BL = 0;
TCCR1A = 0;
powerOFF();
writeString_P("\n\n##### EMERGENCY SHUTDOWN #####\n");
writeString_P("##### ALL OPERATIONS STOPPED TO PREVENT ANY DAMAGE! #####\n\n");
writeString_P("The Master Controller did not respond to the interrupt requests\n");
writeString_P("within the defined timeout! Maybe he's locked up!\n");
writeString_P("\nThe Robot needs to be resetted now.\n\n");
while(true) // Rest In Peace
{
setLEDs(0b100010);
uint8_t dly;
for(dly = 10; dly; dly--)
delayCycles(32768);
setLEDs(0b010100);
for(dly = 10; dly; dly--)
delayCycles(32768);
}
}
else if(getStopwatch3() > 250)
{
status.wdtRequest = true;
signalInterrupt();
setStopwatch3(0);
}
}
}
/************************************************** ***************************/
// Main - The program starts here:
int16_t main(void)
{
initRobotBase();
setLEDs(0b111111);
mSleep(500);
setLEDs(0b000000);
I2CTWI_initSlave(RP6BASE_I2C_SLAVE_ADR);
ACS_setStateChangedHandler(acsStateChanged);
BUMPERS_setStateChangedHandler(bumpersStateChanged );
IRCOMM_setRC5DataReadyHandler(receiveRC5Data);
MOTIONCONTROL_setStateChangedHandler(motionControl StateChanged);
powerON();
startStopwatch1();
disableACS();
setACSPwrOff();
status.byte = 0;
interrupt_status.byte = 0;
drive_status.byte = 0;
status.watchDogTimer = false;
status.wdtRequestEnable = false;
startStopwatch3();
startStopwatch4();
while(true)
{
task_commandProcessor();
task_update();
task_updateRegisters();
task_RP6System();
task_MasterTimeout();
}
return 0;
}
und der Code im RP6 M32 Master:
/*
* ************************************************** **************************
* RP6 ROBOT SYSTEM - RP6 CONTROL M32 Examples
* ************************************************** **************************
* Example: I2C Master 8 - Lib
* Author(s): Dominik S. Herwald
* ************************************************** **************************
* Description:
* To make it easier to understand, we have put several routines and definitions
* in a seperate file now.
* We made the library like the RP6Lib - all variable names are just the
* same as in the RP6Lib. Also some functions have the same names
* and parameters.
* There are also all the event handlers like the ACS and Bumpers event
* Handler - plus some new ones. In the next example we will add movement
* functions to the library...
*
* This makes it easier to reuse parts of programs written for the RP6Lib on
* RP6-M32.
*
* In this example we do just the same as in the last program, but we also
* output several sensor values on the serial Interface and enable the
* timed watchdog of the slave, which generates an interrupt every 500ms and
* forces the Master to respond. These requests are shown with a second
* blinking character on the LC-Display. If the Master does not respond within
* 3 seconds, the Robot gets stopped.
*
* ################################################## ##########################
* 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!
* ################################################## ##########################
* ************************************************** **************************
*/
/************************************************** ***************************/
#include "RP6ControlLib.h" // The RP6 Control Library.
#include "RP6I2CmasterTWI.h" // I2C Master Library
#include "RP6Control_I2CMasterLib.h"
// This Library has a lot of new functions for controlling the Robot Base unit
// with the RP6 CONTROL M32 via the I2C Bus.
// In the next example we will add some more functions to it and let one
// of the more complex example programs of the Robot Base run on it.
/************************************************** ***************************/
#define SRF_LEFT_ADR 0xE6
#define SRF_MIDDLE_ADR 0xE4
#define SRF_RIGHT_ADR 0xE2
#define MEASURE_US_LEFT_LOW 0
#define MEASURE_US_LEFT_HIGH 1
#define MEASURE_US_RIGHT_LOW 2
#define MEASURE_US_RIGHT_HIGH 3
#define MEASURE_US_MIDDLE_LOW 4
#define MEASURE_US_MIDDLE_HIGH 5
/**
* 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, 12);
writeStringLCD_P("LEFT");
}
else
{
writeChar(' ');
clearPosLCD(1, 12, 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)
{
externalPort.LEDS = 0b0110;
writeString_P(" MIDDLE!");
setCursorPosLCD(1, 7);
writeStringLCD_P("MID");
}
else
{
externalPort.LEDS = 0b0000;
clearPosLCD(1, 7, 3);
}
if(obstacle_left)
externalPort.LED1 = true;
if(obstacle_right)
externalPort.LED4 = true;
outputExt();
if(obstacle_left && obstacle_right)
{
sound(140,10,0);
}
else
{
if(obstacle_left)
sound(100,5,0);
if(obstacle_right)
sound(120,5,0);
}
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!");
sound(200,100,0);
}
else
{
if(bumper_left)
{
writeString_P("LEFT!");
sound(200,50,10);
sound(150,20,0);
}
else if(bumper_right)
{
writeString_P("RIGHT!");
sound(200,50,10);
sound(150,20,0);
}
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");
}
}
/**
* 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("\nRead Sensor Values:\n");
writeString_P("PL:");writeIntegerLength(mleft_power,DEC,3);
writeString_P(" | PR:");writeIntegerLength(mright_power,DEC,3);
writeString_P(" | VL:");writeIntegerLength(mleft_speed,DEC,3);
writeString_P(" | VR:");writeIntegerLength(mright_speed,DEC,3);
writeString_P(" | DL:");writeIntegerLength(mleft_des_speed,DEC,3);
writeString_P(" | DR:");writeIntegerLength(mright_des_speed,DEC,3);
writeChar('\n');
writeString_P("DSTL:");writeIntegerLength(mleft_dist,DEC,5);
writeString_P(" | DSTR:");writeIntegerLength(mright_dist,DEC,5);
writeChar('\n');
writeString_P("LSL:");writeIntegerLength(adcLSL,DEC,4);
writeString_P(" | LSR:");writeIntegerLength(adcLSR,DEC,4);
writeString_P(" | MCL:");writeIntegerLength(adcMotorCurrentLeft,DEC,4);
writeString_P(" | MCR:");writeIntegerLength(adcMotorCurrentRight,DEC,4);
writeString_P(" | BAT:");writeIntegerLength(adcBat,DEC,4);
writeString_P(" | AD0:");writeIntegerLength(adc0,DEC,4);
writeString_P(" | AD1:");writeIntegerLength(adc1,DEC,4);
//writeString_P(" | SRF_R:");writeIntegerLength(distance_right, DEC,4);
//writeString_P(" | SRF_M:");writeIntegerLength(distance_middle, DEC,4);
//writeString_P(" | SRF_L:");writeIntegerLength(distance_left, DEC,4);
writeChar('\n');
}
/**
* Heartbeat function
*/
void task_LCDHeartbeat(void)
{
if(getStopwatch1() > 500)
{
static uint8_t heartbeat = false;
if(heartbeat)
{
clearPosLCD(0, 15, 1);
heartbeat = false;
}
else
{
setCursorPosLCD(0, 15);
writeStringLCD_P("*");
heartbeat = true;
printAllSensorValues();
}
setStopwatch1(0);
}
}
/**
* Now we have a second "Heartbeat" display
* which shows if the Controller still reacts on
* the Watchdog requests from the Slave Controller!
* And it also shows if the slave Controller is
* still up and running.
*
* It will blink with a rate of about 2Hz when
* the watchdog requests are still active.
*/
void watchDogRequest(void)
{
static uint8_t heartbeat2 = false;
if(heartbeat2)
{
clearPosLCD(0, 14, 1);
heartbeat2 = false;
}
else
{
setCursorPosLCD(0, 14);
writeStringLCD_P("#");
heartbeat2 = true;
}
}
/************************************************** ***************************/
void task_SRF(void)
{
static uint8_t measureInProgress = false;
static uint8_t channel = 0;
if(!measureInProgress) // Start measurement ONCE only
{
if(TWI_operation == I2CTWI_NO_OPERATION) // If there is no request in progress...
{
if(channel == 0)
I2CTWI_transmit2Bytes(SRF_LEFT_ADR, 0, 81);
else if(channel == 1)
I2CTWI_transmit2Bytes(SRF_RIGHT_ADR, 0, 81);
else if(channel == 2)
I2CTWI_transmit2Bytes(SRF_MIDDLE_ADR, 0, 81);
measureInProgress = true;
setStopwatch2(0);
}
}
else if(getStopwatch2() > 1000) // 1000ms (measurement delay)
{
if(channel == 0)
{
I2CTWI_transmitByte(SRF_LEFT_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_LEFT_ADR, MEASURE_US_LEFT_HIGH, 1);
channel = 1;
}
else if(channel == 1)
{
I2CTWI_transmitByte(SRF_RIGHT_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_RIGHT_ADR, MEASURE_US_RIGHT_HIGH, 1);
channel = 2;
}
else if(channel == 2)
{
I2CTWI_transmitByte(SRF_MIDDLE_ADR, 2);
I2CTWI_requestDataFromDevice(SRF_MIDDLE_ADR, MEASURE_US_MIDDLE_HIGH, 1);
channel = 0;
}
measureInProgress = false;
setStopwatch2(0);
}
}
// I2C Requests:
/**
* The I2C_requestedDataReady Event Handler is now a lot smaller.
* It is free for your own request routines.
* You can put them in the Block of the if condition.
* (similar to the RP6 Base Example programs...)
*/
void I2C_requestedDataReady(uint8_t dataRequestID)
{
// We need to check if this is an INT0 Request from the Robot Base unit.
// The Function call inside the if condition returns true if it was an
// interrupt request, so we have to negate it here and if it was NOT
// an interrupt request from the Robot base we can check any other sensors
// from which you may have requested data...
uint16_t distance_left = 0;
uint16_t distance_right = 0;
uint16_t distance_middle = 0;
uint8_t messageBuf[8]; //------------------- erzeugt Fehler im Terminal,wenn es hier steht
static uint8_t dist_tmp;
switch(dataRequestID)
{
case MEASURE_US_LEFT_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_LEFT_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_LEFT_ADR, MEASURE_US_LEFT_LOW, 1);
break;
case MEASURE_US_LEFT_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_left = messageBuf[0] + (dist_tmp << 8);
writeString_P("DistanceL: ");
writeInteger(distance_left, DEC);
writeString_P(" cm ");
break;
case MEASURE_US_RIGHT_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_RIGHT_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_RIGHT_ADR, MEASURE_US_RIGHT_LOW, 1);
break;
case MEASURE_US_RIGHT_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_right = messageBuf[0] + (dist_tmp << 8);
writeString_P("\t\tDistanceR: ");
writeInteger(distance_right, DEC);
writeString_P(" cm\n");
break;
case MEASURE_US_MIDDLE_HIGH:
I2CTWI_getReceivedData( messageBuf, 2 );
dist_tmp = messageBuf[0];
I2CTWI_transmitByte(SRF_MIDDLE_ADR, 3);
I2CTWI_requestDataFromDevice(SRF_MIDDLE_ADR, MEASURE_US_MIDDLE_LOW, 1);
break;
case MEASURE_US_MIDDLE_LOW:
I2CTWI_getReceivedData( messageBuf, 2 );
distance_middle = messageBuf[0] + (dist_tmp << 8);
writeString_P("\t\tDistanceM: ");
writeInteger(distance_middle, DEC);
writeString_P(" cm\n");
break;
}
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("\nI2C ERROR - TWI STATE: 0x");
writeInteger(errorState, HEX);
writeChar('\n');
}
/************************************************** ***************************/
// Main function - The program starts here:
int main(void)
{
initRP6Control();
initLCD();
writeString_P("\n\nRP6 CONTROL M32 I2C Master Example Program!\n");
writeString_P("\nInterrupts - part 2...\n");
// ---------------------------------------
// The Event Handlers can be set the same way as with the
// RP6Lib:
ACS_setStateChangedHandler(acsStateChanged);
BUMPERS_setStateChangedHandler(bumpersStateChanged );
IRCOMM_setRC5DataReadyHandler(receiveRC5Data);
// New LowBat Event Handler:
BATTERY_setLowVoltageHandler(batteryVoltageLow);
// New Watchdog Request Event Handler:
WDT_setRequestHandler(watchDogRequest);
// ---------------------------------------
// Init TWI Interface:
I2CTWI_initMaster(100);
I2CTWI_setRequestedDataReadyHandler(I2C_requestedD ataReady);
I2CTWI_setTransmissionErrorHandler(I2C_transmissio nError);
sound(180,80,25);
sound(220,80,25);
setLEDs(0b1111);
showScreenLCD("################", "################");
mSleep(500);
showScreenLCD("I2C-Master", "Example Program 3");
mSleep(1000);
setLEDs(0b0000);
// ---------------------------------------
// Setup ACS power:
I2CTWI_transmit3Bytes(I2C_RP6_BASE_ADR, 0, CMD_SET_ACS_POWER, ACS_PWR_MED);
// 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);
showScreenLCD("ACS Status:", "");
startStopwatch1();
startStopwatch2();
while(true)
{
task_SRF();
task_LCDHeartbeat();
task_checkINT0();
task_I2CTWI();
}
return 0;
}
Im Praxisfall macht der RP6 "Sensortests" und je eine Messung mit dem SRF während ein IR Sensor einen Gegenstand detektiert. Nach einiger Zeit kommt eine "RC5 Reception --> Toggle Bit:0 | Device Adresse:0 | Key Code:0" Meldung im Terminal.
Ich schätze mal, das Lesen oder Schreiben von Register in RC5 Bereich falsch laufen.
Kann mir jemand damit helfen, oder zumindest in die richtige Richtung schieben?
Vielen Dank im Voraus.
Mfg, Mac80