[NXC] Adressing I2C-Device

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
rk-at-kkos
Posts: 5
Joined: 14 Feb 2013, 16:42

[NXC] Adressing I2C-Device

Post by rk-at-kkos »

Hello everyone.
I made an I2C-8-Channel-ADC (MAX127) talk to an arduino board using the Wire library for I2C communication. No problem there:

Code: Select all

Wire.beginTransmission(0x28);  // start talking to device 0x28 (7-bit-address) 
Wire.write(0x80);                  // send command byte: convert value at channel 0.
Wire.endTransmission();        // close communication

if (Wire.available() > 1) {   // 2 bytes in buffer for reading
  result = (Wire.read() << 4 | Wire.read() >> 4);  // read & shift to get 12-bit-result
}
Now I'd like to do the same with in NXC. Trouble is, most of the functions require a device address (which I set) and a device register address (which I don't have and the manufacturer claims not to exist). So it seems I should try

Code: Select all

setSensorLowspeed(IN_1);
retValue = I2CSendCommand(IN_1, 0x28, 0x80);
retValue is a long telling me -35. A further inquiry says to consult CommLSCheckStatusType which is a struct, not a long (here I suspect to be simply too inept...).
But still, trying

Code: Select all

numbytes = I2CBytesReady(IN_1);
always gives only zero, which makes me suspect the command didn't get through.

What am I doing wrong?

Ralf
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: [NXC] Adressing I2C-Device

Post by afanofosc »

I am not familiar with this device but I see Xander has a RobotC driver for it. Given his code it should be trivial to write an equivalent version for NXC. Try this program:

Code: Select all

// Maxim Integrated Products MAX127 default I2C device address
#define MIP_ADDR_MAX127 0x50

int SensorMAX127Value(byte port, byte i2cAddress, byte adChannel)
{
   int _chVal = -1;

   byte count = 2;
   byte msg[], reply[];
   // Control byte is calculated as follows:
   // start bit (7) + channel number bit-shifted into place
   byte ctrlByte = (1 << 7) + (adChannel << 4);
   ArrayBuild(msg, i2cAddress, ctrlByte);

   if (I2CBytes(port, msg, count, reply))
   {
     // Convert the bytes into ints
     // 1st byte contains bits 11-4 of the channel's value
     // 2nd byte contains bits 3-0 of the channel's value, followed by 4 0's
     // We'll need to shift the 1st byte left by 4 and the 2nd byte to the right by 4
     _chVal = (reply[0] << 4) + (reply[1] >> 4);
   }
   return _chVal;
}

task main()
{
  SetSensorLowspeed(S1);
  byte addr = MIP_ADDR_MAX127; // change this if the i2c address is not 0x50
  while (true)
  {
    byte channel = 0;
    NumOut(0, LCD_LINE1, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE2, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE3, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE4, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE5, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE6, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE7, SensorMAX127Value(S1, addr, channel++));
    NumOut(0, LCD_LINE8, SensorMAX127Value(S1, addr, channel++));
  }
}
The I2CSendCommand function is really designed for specific types of sensors which have command registers to which you can write commands. Other than I2CBytes you would normally use I2CWrite, I2CCheckStatus, and I2CRead in 3 steps to start the I2C transaction, wait for it to finish, and then read the data that was sent back to the NXT from the device. I2CBytes wraps all that in a high level API function.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: [NXC] Adressing I2C-Device

Post by HaWe »

I think I'll never understand those NXC i2c commands...
but I really wonder why the NXC code is so much longer and much more complicated than just the simple Arduino code

Code: Select all

Wire.beginTransmission(0x28);  // start talking to device 0x28 (7-bit-address)
Wire.write(0x80);                  // send command byte: convert value at channel 0.
Wire.endTransmission();        // close communication

if (Wire.available() > 1) {   // 2 bytes in buffer for reading
  result = (Wire.read() << 4 | Wire.read() >> 4);  // read & shift to get 12-bit-result
}
- and why it couldn't be just that simple also by NXC?
rk-at-kkos
Posts: 5
Joined: 14 Feb 2013, 16:42

Re: [NXC] Adressing I2C-Device

Post by rk-at-kkos »

I tried the suggested solution and it works splendidly. Besides, it is not so much more. You just have to build the array and don't have a read-Method.

The most significant difference compared to my efforts is the use of the 8-bit address (0x50) instead of the 7-bit version (0x28 = 0x50>>1) for arduino.
And the correct use of I2CBytes, of course.

Moreover, now I have a better understanding of the I2C-Communication. Whether the msg-array contains a device register address or not does not matter - it's up to the slave device what to do with the numbers. If only one byte is expected, everything's fine.

I just have to get the electronic part right, the values are going up an down wildly. Maybe a capacitor on the analog lines would help - but that is something entirely different.

Thanks a lot - problem solved, one more happy customer.

Ralf
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: [NXC] Adressing I2C-Device

Post by afanofosc »

Before you tear apart your electronics please make sure that the wildly varying number trouble is not an artifact of me not clearing the screen between LCD writes. I have seen people get confused by this sort of thing where a value like 15 gets overwritten by a value of 9 with the resulting value appearing to be 95 when it is actually just the 5 left over from 15 showing on the LCD due to not clearing the screen before drawing. You could add a call to ClearScreen() at the start of the while loop and perhaps add a Wait(MS_50) at the end of the while loop to slow down the redraw a bit.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: [NXC] Adressing I2C-Device

Post by afanofosc »

doc-helmut wrote:but I really wonder why the NXC code is so much longer and much more complicated than just the simple Arduino code
...
- and why it couldn't be just that simple also by NXC?
I2CWrite encompasses the first 3 Arduino Wire commands so it is 3 times simpler in that case. I2CCheckStatus or I2CBytesReady are both about the same as Wire.available. You would normally want to loop even in the Arduino case because if you call this too fast you may just miss the data entirely and assume something went wrong when if you had just waited a millsecond or 30 longer the data would have been available. And I2CRead is equivalent to both the Wire.read commands, returning all available bytes in a single byte array. So equivalently simple or many times simpler depending on how many bytes (up to 16 for the NXT) that you want to read, i.e., one call vs as many as 16 separate calls.

And I2CBytes wraps the entire transaction into a very simple and easy to use function that starts the transaction with the buffer to send, the count of bytes desired to read which is updated to store the count of bytes available to read, and the buffer to store the response.

How much easier do you want it to be?

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
afanofosc
Site Admin
Posts: 1256
Joined: 26 Sep 2010, 19:36
Location: Nashville, TN
Contact:

Re: [NXC] Adressing I2C-Device

Post by afanofosc »

I will add the MAX127 function and default address constant to the NXC API headers.

John Hansen
Multi-platform LEGO MINDSTORMS programming
http://bricxcc.sourceforge.net/
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: [NXC] Adressing I2C-Device

Post by HaWe »

yes, you're probably right, if one once understood the technical standpoint in it then it actually might be easy.
From a logical POV, it's more easy to
1) look if (or wait until) i2c is ready, and if, then
2) call the write or the read function.

Code: Select all

if (Wire.available() > 1) {    
  result = (Wire.read() << 4 | Wire.read() >> 4);
What also always confuses me are the lots of variables and the arraybuild functions with lots of bytes which one has to use instead plus the chunk of variants which then has to be passed altogether to I2CBytes which also returnes the error state additionally

Code: Select all

int _chVal = -1;
   byte count = 2;
   byte msg[], reply[];
   byte ctrlByte = (1 << 7) + (adChannel << 4);
   ArrayBuild(msg, i2cAddress, ctrlByte);
   if (I2CBytes(port, msg, count, reply))
   {
     _chVal = (reply[0] << 4) + (reply[1] >> 4);
   }
   return _chVal;
This shouldn't sound like any grousing, but I simpy guess that's just my personal handicap I have to handle - and the reason why I never managed to write any working i2c functions so far by myself and probably never will...
rk-at-kkos
Posts: 5
Joined: 14 Feb 2013, 16:42

Re: [NXC] Adressing I2C-Device

Post by rk-at-kkos »

If it's just for the sake of shortening, one might do it this way:

Code: Select all

#define MAX127PORT S1

byte Max127msg[] = {0x50, 0};  // Message array for the adc

int MAX127Value(byte channel)
{
   byte count = 2;
   byte reply[];
   // Control byte: start bit (7) + channel number bit-shifted into place
   Max127msg[1] = 0x80 | (channel << 4);

   if (I2CBytes(MAX127PORT, Max127msg, count, reply))
     return ( (reply[0] << 4) | (reply[1] >> 4) );

   return -1;
}
So I have some global variables, but a singe parameter is all the function needs. On the arduino I wrap it in an Object with an address attribute, so all it takes for my students is a read-Method.
I found an explanation for the varying values (which don't happen on the arduino): They are gone since I recharged the battery. So maybe the voltage for the ADC had dropped too low.
And I believe the IC is not widely in use, since you can't buy it around the corner. So adding it to the API headers might not be necessary...

Ralf
HaWe
Posts: 2500
Joined: 04 Nov 2014, 19:00

Re: [NXC] Adressing I2C-Device

Post by HaWe »

hey,
could you pls explain what these lines mean...?

Code: Select all

 byte Max127msg[] = {0x50, 0};           // why [0]=0x50 ?  why not 0x28 ?
...
Max127msg[1] = 0x80 | (channel << 4);   // 0x80=read_adc-command? why "bitwise-or"?  why (channel<<4) ?  
edit:
BTW:
why not make

Code: Select all

byte Max127msg[] = {0x50, 0};  // Message array for the adc
local to the Max127Value function?
Last edited by HaWe on 16 Feb 2013, 09:49, edited 1 time in total.
Post Reply

Who is online

Users browsing this forum: Semrush [Bot] and 1 guest