Tetrix and NXC

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Tetrix and NXC

Post by mattallen37 »

The other day, I bought a used Tetrix set (actually the Tetrix, 9797 NXT set combo.

I remember all the talk Ford was a part of, in regard to NXC drivers, so I was a tad bit concerned about getting the controllers to work with NXC. However, I was very happily surprised to have all fears shattered by nice, solid drivers within only a couple hours (including lots of testing :D).

I posted a (fairly long) article about it on my blog. I have the library, and well as an example posted in the article, but I'll post it here anyhow. Here is the library:

Code: Select all

#define TETRIX_ADDRESS_1 0x02         //Daisey chain position 1
#define TETRIX_ADDRESS_2 0x04         //Daisey chain position 2
#define TETRIX_ADDRESS_3 0x06         //Daisey chain position 3
#define TETRIX_ADDRESS_4 0x08         //Daisey chain position 4

#define TETRIX_MOTOR_BRAKE 0          //Motor brake
#define TETRIX_MOTOR_FLOAT -128       //0x80, Motor float

#define TETRIX_MOTOR_REV 0x08         //Reverse bit

#define TETRIX_SERVO_TIME_RESET 0x00  //Restart 10 second timer
#define TETRIX_SERVO_OFF 0xFF         //Turn off/float all servos
#define TETRIX_SERVO_STAY_ON 0xAA     //Disable 10 second timer

void TetrixI2CWrite(byte port, byte addr, byte reg, byte data[])
{
  byte cmdbuf[];                         //register data
  int loop, n, nByteReady;

  ArrayBuild(cmdbuf, addr, reg, data);

  loop = STAT_COMM_PENDING;              //Wait until bus is free
  while (loop == STAT_COMM_PENDING ){    //        ''
    loop = I2CStatus(port, nByteReady);  //        ''
  }                                      //        ''

  n = I2CWrite(port, 0, cmdbuf);        //When the I2C bus is ready, send the message you built
  while (I2CStatus(port, nByteReady) ==  STAT_COMM_PENDING); //Wait until bus is free
}

inline void TetrixSetup(byte port){
  asm{
    setin IN_TYPE_LOWSPEED , port, TypeField        // IN_TYPE_LOWSPEED (not 9v)
    setin IN_TYPE_NO_SENSOR , port, InputModeField

    setin IN_TYPE_SWITCH, port, InvalidDataField
  SensorStillInvalid:
    getin __ResetSensorTmp, port, InvalidDataField
    brtst NEQ, SensorStillInvalid, __ResetSensorTmp
  };
}

char TetrixMotorControlData[4];

void TetrixMotors(byte port, byte addr, char motor1, char motor2, byte options1 = 0, byte options2 = 0){
  TetrixMotorControlData[0]=options1;
  TetrixMotorControlData[3]=options2;
  TetrixMotorControlData[1]=motor1;
  TetrixMotorControlData[2]=motor2;
  TetrixI2CWrite(port, addr, 0x44, TetrixMotorControlData);
}

byte TetrixServoControlData[8];

void TetrixServos(byte port, byte addr, byte servo1, byte servo2, byte servo3, byte servo4, byte servo5, byte servo6, byte step_time = 0, byte pwm = TETRIX_SERVO_TIME_RESET){
  TetrixServoControlData[0]=step_time;
  TetrixServoControlData[1]=servo1;
  TetrixServoControlData[2]=servo2;
  TetrixServoControlData[3]=servo3;
  TetrixServoControlData[4]=servo4;
  TetrixServoControlData[5]=servo5;
  TetrixServoControlData[6]=servo6;
  TetrixServoControlData[7]=pwm;
  TetrixI2CWrite(port, addr, 0x41, TetrixServoControlData);
}
And here is an example program:

Code: Select all

#include "MyTetrix lib.nxc"

#define TETRIX_PORT S1

char Motor1;
char Motor2;

int Servo1, Servo2, Servo3, Servo4, Servo5, Servo6;

task main(){
  TetrixSetup(TETRIX_PORT);
  while(true){

    Motor1=Random(200)-100;
    Motor2=Random(200)-100;

    TetrixMotors(TETRIX_PORT, TETRIX_ADDRESS_1, Motor1, Motor2, 0x00, TETRIX_MOTOR_REV);

    Servo1=Random(200)+27;
    Servo2=Random(200)+27;
    Servo3=Random(200)+27;
    Servo4=Random(200)+27;
    Servo5=Random(200)+27;
    Servo6=Random(200)+27;

    TetrixServos(TETRIX_PORT, TETRIX_ADDRESS_2, Servo1, Servo2, Servo3, Servo4, Servo5, Servo6, 0, TETRIX_SERVO_TIME_RESET);

    ClearScreen();

    NumOut(0, LCD_LINE1, Motor1);
    NumOut(0, LCD_LINE2, Motor2);
    
    NumOut(0, LCD_LINE3, Servo1);
    NumOut(0, LCD_LINE4, Servo2);
    NumOut(0, LCD_LINE5, Servo3);
    NumOut(0, LCD_LINE6, Servo4);
    NumOut(0, LCD_LINE7, Servo5);
    NumOut(0, LCD_LINE8, Servo6);
    
    Wait(1000);
  }
}
And here is a quote from my blog, specifically about the example:
Matt's Blog wrote:The example is just a while(true) loop, that sets the motors' and servos' parameters to random values every second (plus code run time). The example program, is just that, an example. It's just a PoC (Proof of Concept) program.

To use the example, connect the motor controller to input port 1 of the NXT, and daisy chain the servo controller to the other port of the motor controller. Make sure that the motors can physically do anything (because of the Random values), and that you have the controllers connected to the 12v battery (and the switch is on).
If you want to actually use the drivers, please read the entire article. I explain many things in it.

If you have any questions, please let me know.

@Ford, if you want me to, I could try to (blindly) add support for the encoders. I don't have any Tetrix encoders though, so testing on my own will be impossible.
I done wrote:Way OT, but I didn't know you could use a URL as the origin of a quote... cool!
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Tetrix and NXC

Post by HaWe »

Thank you matt,
actually the motor control itself always was working fine already with my (Xander's) old program, but just when polling encoders simulateously and continuously by an independent task then the system crashes.
Wait states (constants, and wait until bus ready) have been inserted sufficiently.

Code: Select all

// program Tetrix_Motor_Driver ver. 0.15

#define printf5( _x, _y, _format1,_format2,_format3,_format4,_format5,_value1,_value2,_value3,_value4,_value5) { \
  string sval1 = FormatNum(_format1, _value1); \
  string sval2 = FormatNum(_format2, _value2); \
  string sval3 = FormatNum(_format3, _value3); \
  string sval4 = FormatNum(_format4, _value4); \
  string sval5 = FormatNum(_format5, _value5); \
  string s =sval1+sval2+sval3+sval4+sval5; \
  TextOut(_x, _y, s); \
}

#define printf2( _x, _y, _format1, _format2, _value1, _value2) { \
  string sval1 = FormatNum(_format1, _value1); \
  string sval2 = FormatNum(_format2, _value2); \
  string s =sval1+sval2; \
  TextOut(_x, _y, s); \
}

#define printf1( _x, _y, _format1, _value1) { \
  string sval1 = FormatNum(_format1, _value1); \
  TextOut(_x, _y, sval1); \
}

void TXMotorOn(byte NXTport, byte TXmotor, char percentage)
{
  byte retLen = 0;
  char modeMsg[];
  char powerMsg[];
  byte devAddr, modeReg, powerReg;
  char IOresult;

  // addresses and registers
  devAddr = (TXmotor+2)&14;
  modeReg = 0x44 + (TXmotor % 2)*3;
  powerReg= 0x45 + (TXmotor % 2);

  ArrayBuild(modeMsg, devAddr, modeReg, 0);    // 0= PWM percentage mode
  ArrayBuild(powerMsg, devAddr, powerReg, percentage);
  
  // Send the first message as soon as the bus is ready

  while ((I2CCheckStatus(NXTport) == STAT_COMM_PENDING) && (I2CCheckStatus(NXTport) != NO_ERR)) Wait(1);
  IOresult=I2CWrite(NXTport, retLen, modeMsg);
  if (IOresult != NO_ERR)   {
    printf5(0,48-(8*TXmotor),"%d","%3d","%2d","%5d","%4d",TXmotor,modeMsg[1],modeMsg[2],powerMsg[2],IOresult);
    return;
  }
  printf5(0,48-(8*TXmotor),"%d","%3d","%2d","%5d","%4d",TXmotor,modeMsg[1],modeMsg[2],powerMsg[2],IOresult);
  Wait(10);

  // Send the second message when the first one is done

  while ((I2CCheckStatus(NXTport) == STAT_COMM_PENDING) && (I2CCheckStatus(NXTport) != NO_ERR)) Wait(1);
  IOresult=I2CWrite(NXTport, retLen, powerMsg);
  if (IOresult != NO_ERR){
    printf5(0,48-(8*TXmotor),"%d","%3d","%2d","%5d","%4d",TXmotor,modeMsg[1],modeMsg[2],powerMsg[2],IOresult);
    return;
  }

  printf5(0,48-(8*TXmotor),"%d","%3d","%2d","%5d","%4d",TXmotor,modeMsg[1],modeMsg[2],powerMsg[2],IOresult);

  Wait(1);
}

void ResetTXMotorCounter(byte NXTport, byte TXmotor)
{
  byte retLen = 0;
  char modeMsg[];
  byte devAddr, modeReg;
  char IOresult;

  // addresses and registers
  devAddr = (TXmotor+2)&14;
  modeReg = 0x44 + (TXmotor % 2)*3;

  ArrayBuild(modeMsg, devAddr, modeReg, 3);      // 3 = reset enc value

   // Send the reset message as soon as the bus is ready

  while ((I2CCheckStatus(NXTport) == STAT_COMM_PENDING) && (I2CCheckStatus(NXTport) != NO_ERR)) Wait(1);
  IOresult=I2CWrite(NXTport, retLen, modeMsg);
  if (IOresult != NO_ERR)   { return;  };
  Wait(10);

}

long GetTXMotorCounter(byte NXTport, byte TXmotor)
{
  byte devAddr;
  const byte msgLen = 4;
  byte sendMsg[];
  byte replyMsg[];
  byte encStartReg;

  devAddr = (TXmotor+2)&14;
  if (TXmotor==0) encStartReg = 0x4C;
  else
  if (TXmotor==1) encStartReg = 0x50;

  ArrayBuild(sendMsg, devAddr, encStartReg);
  // ArrayBuild(sendMsg, DGPS_I2C_ADDR, START_OF_ENCODER_REGISTER);   // Only -one- register, the first one, is requested

  while ((I2CCheckStatus(NXTport) == STAT_COMM_PENDING) && (I2CCheckStatus(NXTport) != NO_ERR)) Wait(1);

  // if(!I2CBytes(NXTport, sendMsg, msgLen, replyMsg)) return ;

  if(I2CBytes(NXTport, sendMsg, msgLen, replyMsg)) {
     return replyMsg[3] + (replyMsg[2] << 8) + (replyMsg[1] << 16) + (replyMsg[0] << 24);
  }

}


task showValues()  {
  long encoder0, encoder1;
  ClearScreen();
  TextOut(0,56,"M Rg md pow error");

  while(1) {
    encoder0= GetTXMotorCounter(0, 0);
    encoder1= GetTXMotorCounter(0, 1);
    Wait(50);
    printf1(0,8,"M1=%7d", encoder0);
    printf1(0,0,"M2=%7d", encoder1);
  }
}

task main(){

  SetSensorType(S1, SENSOR_TYPE_LOWSPEED);
  Wait(20);
  ResetSensor(S1);
  Wait(50);
  StartTask(showValues);

  while (true) {                // test loop
    PlaySound (SOUND_DOUBLE_BEEP);

    TXMotorOn(0, 0, 50);
    TXMotorOn(0, 1, 20);
    Wait(2000);

    TXMotorOn(0, 0,  0);  // brake
    TXMotorOn(0, 1,  0);  // brake
    Wait(2000);

    TXMotorOn(0, 0, -50);
    TXMotorOn(0, 1, -20);
    Wait(2000);

    TXMotorOn(0, 0,  0);   // brake
    TXMotorOn(0, 1, -128);   // coast
    Wait(2000);

    ResetTXMotorCounter(0,0);
    ResetTXMotorCounter(0,1);
    Wait(2000);
  }
}
The underlying problem behind the problem ;) :
I need the encoder values for odometry, so a quite quick polling (each 10-20 ms) is needed.
Motor control and encoder polling have to be provided by 2 independend tasks because
1) the odometry has to calculate the position for the navigation module in an independent background task, while
2) the drive propulsion (forward, backwards, stop, coast) are commanded by a motor control task depending on the environment (avoid obstacles, follow astar routings, drive to cans and bottles in order to grab them).

Thanks a lot for your efforts so far!

ps
I will try your code adding an encoder polling task by myself!
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Tetrix and NXC

Post by mattallen37 »

Helmut, try this library:

Code: Select all

#define TETRIX_ADDRESS_1 0x02         //Daisey chain position 1
#define TETRIX_ADDRESS_2 0x04         //Daisey chain position 2
#define TETRIX_ADDRESS_3 0x06         //Daisey chain position 3
#define TETRIX_ADDRESS_4 0x08         //Daisey chain position 4

#define TETRIX_MOTOR_BRAKE 0          //Motor brake
#define TETRIX_MOTOR_FLOAT -128       //0x80, Motor float

#define TETRIX_MOTOR_REV 0x08         //Reverse bit

#define TETRIX_SERVO_TIME_RESET 0x00  //Restart 10 second timer
#define TETRIX_SERVO_OFF 0xFF         //Turn off/float all servos
#define TETRIX_SERVO_STAY_ON 0xAA     //Disable 10 second timer

void TetrixI2CRead(byte port, byte addr, byte reg, byte cnt, byte& outbuf[])
{
  byte cmdbuf[];  // register number holder
	int             loop;
	byte            nByteReady = 0;

  ArrayBuild(cmdbuf, addr, reg);

  loop = STAT_COMM_PENDING;              //Wait until bus is free
  while (loop == STAT_COMM_PENDING ){    //        ''
    loop = I2CStatus(port, nByteReady);  //        ''
  }                                      //        ''

  I2CBytes(port, cmdbuf, cnt, outbuf)    //Read the registers
  while (I2CStatus(port, nByteReady) ==  STAT_COMM_PENDING); //Wait until bus is free
}

void TetrixI2CWrite(byte port, byte addr, byte reg, byte data[])
{
  byte cmdbuf[];                         //register data
  int loop, n, nByteReady;

  ArrayBuild(cmdbuf, addr, reg, data);

  loop = STAT_COMM_PENDING;              //Wait until bus is free
  while (loop == STAT_COMM_PENDING ){    //        ''
    loop = I2CStatus(port, nByteReady);  //        ''
  }                                      //        ''

  n = I2CWrite(port, 0, cmdbuf);        //When the I2C bus is ready, send the message you built
  while (I2CStatus(port, nByteReady) ==  STAT_COMM_PENDING); //Wait until bus is free
}

inline void TetrixSetup(byte port){
  asm{
    setin IN_TYPE_LOWSPEED , port, TypeField        // IN_TYPE_LOWSPEED (not 9v)
    setin IN_TYPE_NO_SENSOR , port, InputModeField

    setin IN_TYPE_SWITCH, port, InvalidDataField
  SensorStillInvalid:
    getin __ResetSensorTmp, port, InvalidDataField
    brtst NEQ, SensorStillInvalid, __ResetSensorTmp
  };
}

void TetrixEncoders(byte port, byte addr, long motor1enc, long motor2enc){
  byte outbuf[8];
  TetrixI2CRead(port, addr, 0x4C, 8, outbuf);
  motor1enc=((outbuf[0]*0x1000000)+(outbuf[1]*0x10000)+(outbuf[2]*0x100)+outbuf[3]);
  motor2enc=((outbuf[4]*0x1000000)+(outbuf[5]*0x10000)+(outbuf[6]*0x100)+outbuf[7]);
}

int TetrixVoltage(byte port, byte addr){
  int voltage;
  byte outbuf[2];                              //not sure if I need the "2", but it works fine with it
  TetrixI2CRead(port, addr, 0x54, 2, outbuf);
  voltage=((outbuf[0]*4)+outbuf[1])*20;        //multiply by 20, to get mv
  return voltage;
}

char TetrixMotorControlData[4];

void TetrixMotors(byte port, byte addr, char motor1, char motor2, byte options1 = 0, byte options2 = 0){
  TetrixMotorControlData[0]=options1;
  TetrixMotorControlData[3]=options2;
  TetrixMotorControlData[1]=motor1;
  TetrixMotorControlData[2]=motor2;
  TetrixI2CWrite(port, addr, 0x44, TetrixMotorControlData);
}

byte TetrixServoControlData[8];

void TetrixServos(byte port, byte addr, byte servo1, byte servo2, byte servo3, byte servo4, byte servo5, byte servo6, byte step_time = 0, byte pwm = TETRIX_SERVO_TIME_RESET){
  TetrixServoControlData[0]=step_time;
  TetrixServoControlData[1]=servo1;
  TetrixServoControlData[2]=servo2;
  TetrixServoControlData[3]=servo3;
  TetrixServoControlData[4]=servo4;
  TetrixServoControlData[5]=servo5;
  TetrixServoControlData[6]=servo6;
  TetrixServoControlData[7]=pwm;
  TetrixI2CWrite(port, addr, 0x41, TetrixServoControlData);
}
With this example:

Code: Select all

#include "MyTetrix lib.nxc"

#define TETRIX_PORT S1

char Motor1;
char Motor2;
long Motor1Enc;
long Motor2Enc;

float voltage;

int Servo1, Servo2, Servo3, Servo4, Servo5, Servo6;

int i=-100;

task main(){
  TetrixSetup(TETRIX_PORT);
  SetLongAbort(true);
  while(true){
    Servo1=i+127;
    Servo2=i+127;
    Servo3=i+127;
    Servo4=i+127;
    Servo5=i+127;
    Servo6=i+127;

    TetrixServos(TETRIX_PORT, TETRIX_ADDRESS_2, Servo1, Servo2, Servo3, Servo4, Servo5, Servo6, 0, TETRIX_SERVO_TIME_RESET);
    Wait(5);
    Motor1=i;
    Motor2=i;

    TetrixMotors(TETRIX_PORT, TETRIX_ADDRESS_1, Motor1, Motor2, 0x00, TETRIX_MOTOR_REV);
    Wait(5);

    TetrixEncoders(TETRIX_PORT, TETRIX_ADDRESS_1, Motor1Enc, Motor2Enc);

    ClearScreen();

    NumOut(0, LCD_LINE1, Motor1);
    NumOut(50, LCD_LINE1, Motor2);
    
    NumOut(0, LCD_LINE2, Motor1Enc);
    NumOut(50, LCD_LINE2, Motor2Enc);

    string Voltage = "Battery = " + NumToStr(voltage) + "v";
    TextOut(0, LCD_LINE3, Voltage);

    NumOut(0, LCD_LINE6, Servo1);
    NumOut(50, LCD_LINE6, Servo2);
    NumOut(0, LCD_LINE7, Servo3);
    NumOut(50, LCD_LINE7, Servo4);
    NumOut(0, LCD_LINE8, Servo5);
    NumOut(50, LCD_LINE8, Servo6);

    Wait(10);

    if(ButtonPressed(BTNEXIT, false)){                               //Exit the program safely
      TetrixMotors(TETRIX_PORT, TETRIX_ADDRESS_1, -128, -128, 0, 0);
      TetrixServos(TETRIX_PORT, TETRIX_ADDRESS_2, Servo1, Servo2, Servo3, Servo4, Servo5, Servo6, 0, TETRIX_SERVO_OFF);
      break;                                       //Jump out of the while(true) loop, so the program ends
    }
    i++;
    if(i>100)i=-100;
  }
}
Again, make sure the motors can physically do anything.

I also added a function to read the voltage from the motor controller. It returns mv, with 20mv resolution. For some reason though, the program crashes very often if I use it with the function that reads the encoders.

The example above runs fine on my system, but I obviously always get 0 for the motor encoders, because I don't have any :cry:. Try it though, and tell me if it works for you. I know you want two separate tasks, one for controlling, and one for reading the encoders. To do that, you should use a mutex.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Tetrix and NXC

Post by HaWe »

matt,
I tried your code. The motors ramp up and down fwd and rev.
The display shows the PwrPercentages
but for the rest it shows only zeros over and over. :(

ps
I can't use mutexes because my program has to have a subsumption architecture.
It consists of 10-20 running tasks of different individual priorities (continuous backgroundtasks (navigator, sensor_polling, read_BT_Mailbox), individual started/stopped tasks for the BT network (BT remote motor control), and seperate behaviour tasks (cruise, avoid_obstacle, follow_astar, follow_bug2, approach_target, catch_target, obey_sound_cmd, go_recharge, idle...) which are controlled by an superordinate arbitrator task).
So every access to the HTTetrix controller must be able to be done at any time by any task provided the i2c bus is ready.
Encoder readings have a very high priority because they are needed by a very high priority odometry navigator task. Mutexes simply torpedo the subsumption hierarchy.
Attachments
mattTetrix.jpg
mattTetrix.jpg (5.91 KiB) Viewed 4504 times
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Tetrix and NXC

Post by mattallen37 »

Hmm, the LCD_LINE2 0s shouldn't be 0. Are you sure you have the encoders connected properly, both physically and electronically?
So every access to the HTTetrix controller must be able to be done at any time by any task provided the i2c bus is ready.
That's what the mutex would be for...
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Tetrix and NXC

Post by HaWe »

the encoders are working well with my own program (until it hangs up), and their values are shown correctly on the screen (until the screen freezes).
And no: no way for mutexes. All i2c functions on the related sensor port must have access to all devices in the moment the bus is ready (as usual for i2c buses).
Have a look at my example program how it has to work like.
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Tetrix and NXC

Post by mattallen37 »

I realized I made a mistake in the encoder library function. Use this library:

Code: Select all

#define TETRIX_ADDRESS_1 0x02         //Daisey chain position 1
#define TETRIX_ADDRESS_2 0x04         //Daisey chain position 2
#define TETRIX_ADDRESS_3 0x06         //Daisey chain position 3
#define TETRIX_ADDRESS_4 0x08         //Daisey chain position 4

#define TETRIX_MOTOR_BRAKE 0          //Motor brake
#define TETRIX_MOTOR_FLOAT -128       //0x80, Motor float

#define TETRIX_MOTOR_REV 0x08         //Reverse bit

#define TETRIX_SERVO_TIME_RESET 0x00  //Restart 10 second timer
#define TETRIX_SERVO_OFF 0xFF         //Turn off/float all servos
#define TETRIX_SERVO_STAY_ON 0xAA     //Disable 10 second timer

void TetrixI2CRead(byte port, byte addr, byte reg, byte cnt, byte& outbuf[])
{
  byte cmdbuf[];  // register number holder
	int             loop;
	byte            nByteReady = 0;

  ArrayBuild(cmdbuf, addr, reg);

  loop = STAT_COMM_PENDING;              //Wait until bus is free
  while (loop == STAT_COMM_PENDING ){    //        ''
    loop = I2CStatus(port, nByteReady);  //        ''
  }                                      //        ''

  I2CBytes(port, cmdbuf, cnt, outbuf)    //Read the registers
  while (I2CStatus(port, nByteReady) ==  STAT_COMM_PENDING); //Wait until bus is free
}

void TetrixI2CWrite(byte port, byte addr, byte reg, byte data[])
{
  byte cmdbuf[];                         //register data
  int loop, n, nByteReady;

  ArrayBuild(cmdbuf, addr, reg, data);

  loop = STAT_COMM_PENDING;              //Wait until bus is free
  while (loop == STAT_COMM_PENDING ){    //        ''
    loop = I2CStatus(port, nByteReady);  //        ''
  }                                      //        ''

  n = I2CWrite(port, 0, cmdbuf);        //When the I2C bus is ready, send the message you built
  while (I2CStatus(port, nByteReady) ==  STAT_COMM_PENDING); //Wait until bus is free
}

inline void TetrixSetup(byte port){
  asm{
    setin IN_TYPE_LOWSPEED , port, TypeField        // IN_TYPE_LOWSPEED (not 9v)
    setin IN_TYPE_NO_SENSOR , port, InputModeField

    setin IN_TYPE_SWITCH, port, InvalidDataField
  SensorStillInvalid:
    getin __ResetSensorTmp, port, InvalidDataField
    brtst NEQ, SensorStillInvalid, __ResetSensorTmp
  };
}

void TetrixEncoders(byte port, byte addr, long & motor1enc, long & motor2enc){
  byte outbuf[8];
  TetrixI2CRead(port, addr, 0x4C, 8, outbuf);
  motor1enc=((outbuf[0]*0x1000000)+(outbuf[1]*0x10000)+(outbuf[2]*0x100)+outbuf[3]);
  motor2enc=((outbuf[4]*0x1000000)+(outbuf[5]*0x10000)+(outbuf[6]*0x100)+outbuf[7]);
}

int TetrixVoltage(byte port, byte addr){
  int voltage;
  byte outbuf[2];                              //not sure if I need the "2", but it works fine with it
  TetrixI2CRead(port, addr, 0x54, 2, outbuf);
  voltage=((outbuf[0]*4)+outbuf[1])*20;        //multiply by 20, to get mv
  return voltage;
}

char TetrixMotorControlData[4];

void TetrixMotors(byte port, byte addr, char motor1, char motor2, byte options1 = 0, byte options2 = 0){
  TetrixMotorControlData[0]=options1;
  TetrixMotorControlData[3]=options2;
  TetrixMotorControlData[1]=motor1;
  TetrixMotorControlData[2]=motor2;
  TetrixI2CWrite(port, addr, 0x44, TetrixMotorControlData);
}

byte TetrixServoControlData[8];

void TetrixServos(byte port, byte addr, byte servo1, byte servo2, byte servo3, byte servo4, byte servo5, byte servo6, byte step_time = 0, byte pwm = TETRIX_SERVO_TIME_RESET){
  TetrixServoControlData[0]=step_time;
  TetrixServoControlData[1]=servo1;
  TetrixServoControlData[2]=servo2;
  TetrixServoControlData[3]=servo3;
  TetrixServoControlData[4]=servo4;
  TetrixServoControlData[5]=servo5;
  TetrixServoControlData[6]=servo6;
  TetrixServoControlData[7]=pwm;
  TetrixI2CWrite(port, addr, 0x41, TetrixServoControlData);
}
Try this example:

Code: Select all

#include "MyTetrix lib.nxc"

#define TETRIX_PORT S1

char Motor1;
char Motor2;
long Motor1Enc;
long Motor2Enc;

int i=-100;

task main(){
  TetrixSetup(TETRIX_PORT);
  Wait(15);
  SetLongAbort(true);
  while(true){

    Motor1=i;
    Motor2=i;

    TetrixMotors(TETRIX_PORT, TETRIX_ADDRESS_1, Motor1, Motor2, 0x00, TETRIX_MOTOR_REV);
    Wait(25);

    TetrixEncoders(TETRIX_PORT, TETRIX_ADDRESS_1, Motor1Enc, Motor2Enc);

    ClearScreen();

    NumOut(0, LCD_LINE1, Motor1);
    NumOut(50, LCD_LINE1, Motor2);

    NumOut(0, LCD_LINE2, Motor1Enc);
    NumOut(50, LCD_LINE2, Motor2Enc);

    Wait(25);

    if(ButtonPressed(BTNEXIT, false)){                               //Exit the program safely
      TetrixMotors(TETRIX_PORT, TETRIX_ADDRESS_1, -128, -128, 0, 0);
      break;                                       //Jump out of the while(true) loop so the program ends
    }
    i+=5;
    if(i>100)i=-100;
  }
}
Unfortunately, it does hang up some for me, but see if the encoder function works properly now.
doc-helmut wrote:...And no: no way for mutexes. All i2c functions on the related sensor port must have access to all devices in the moment the bus is ready (as usual for i2c buses).
Indeed, that is exactly what the mutex would allow. It would allow the task to have access to the I2C bus just as soon as it is available.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Tetrix and NXC

Post by HaWe »

Now it shows some values on the 2nd line.
Attachments
mattTetrix.jpg
mattTetrix.jpg (4.33 KiB) Viewed 4504 times
mattallen37
Posts: 1818
Joined: 02 Oct 2010, 02:19
Location: Michigan USA
Contact:

Re: Tetrix and NXC

Post by mattallen37 »

doc-helmut wrote:If the Tetrix i2c controller worked corectly no mutex will or would be needed. Forget about the mutex, the Tetrix controller must support multithreaded access.
Also motor command must be possible by multitasking (1 tasks may start both motors while another task intermediately stops 1 of them - or both - to avoid an obstacle).

For a first approach: Can you try to make simultaneous encoder readings by a seperate task - without mutexes - like in my own source code?
I don't think you understand what a mutex is. It's isn't this "great evil". It something that you most definitely should have implemented if you want control of the motors from multiple tasks. The Tetrix controller can NOT support multiple writes at the same time, it's electronically impossible. Believe it or not, the NXC I2C functions rely heavily on mutex's in the asm code.

Sure, I can try that. Again, it automatically uses mutex's; I have no control over that.

I think you need to review your sequential logic. However, I'm not attempting to help you with your robot programming; just the Tetrix drivers.
Matt
http://mattallen37.wordpress.com/

I'm all for gun control... that's why I use both hands when shooting ;)
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: Tetrix and NXC

Post by HaWe »

of course!
But it's not multiple writing what my code does, every i2c function waits until the bus is ready before it sends a new command.

The program is simple and just uses 2 tasks:
One task (main task) does the following in an endless loop:
- it starts the motors, runs them for 2sec by a Wait(2000), then stops them, starts them again reverse, again runs them for 2sec by a Wait(2000), then stops them.

The 2nd task just reads the encoders in an endless loop about every 20 ms and shows them on the screen, nothing else.

I'm curious if your functions have anyhow different internal functionalities and won't crash like mine.
ps: I'll try it also by my own in one of the next days...
Post Reply

Who is online

Users browsing this forum: No registered users and 0 guests