RDA5807SP FM Radio Receiver - i2C

I've been pounding at this all day, and I'm missing something. If somebody can point my nose I would appreciate it.

I picked up a couple of RDA5807SP I2C based FM Radio Receiver IC's off the FleaBay a few days ago. They came in the mail, and I'm anxious to hook them up to my arduino. They're I2C controlled, they have a couple of registers storing what looks like 2 bytes of data each, and we can manipulate these registers to control the radio. I'm fairly new to I2C, but starting to get my feet wet, and I just can't seem to get the hang of the appropriate read/write on this thing. I write values to the registers, but when I read them back, they're different from what I wrote. I'm not sure where to go from here.

See my attached code.

So for example, lets say I want to write the register at 0x05, and set bits 0-3 as the value 1 (forget the other bits, they're not important yet), I'm doing whats shown below. But its not working. When I read it back, its not how I set it. What am I doing wrong?

#include <Wire.h>

int device_address = 96;
int seekPin = 12;
int seekPinState = 0;

void setup(){
pinMode (seekPin,INPUT);  
  
Wire.begin();
Serial.begin(9600);

getVolume();
maxVolume();
getVolume();

}


void loop(){

Serial.print("Device addres is: ");
Serial.println(device_address);

seekPinState = digitalRead(seekPin);

if (seekPinState == 1) {
  Serial.println("High!");

  }

getVolume();
delay(5000);

}


void maxVolume(){
Serial.println("Setting Volume");
Wire.beginTransmission(device_address);
Wire.write(5);

Wire.write(136); // first byte 10001000 
Wire.write(175); // second byte 10101111  <-- This is the 1111 for the first 3 bits of the byte.

Wire.endTransmission();

}

void getVolume(){

 Wire.requestFrom(device_address, 2);

 while (Wire.available() == 0);
  
int byte1 = Wire.read();
int byte2 = Wire.read();
String binaryByte1 = String(byte1, BIN);
String binaryByte2 = String(byte2, BIN);

Serial.print("The currnet volume setting is: ");
Serial.print(binaryByte1);
Serial.print(":");
Serial.println(binaryByte2);
}
while (Wire.available() == 0);

This just hangs about until at least one byte is available.
Then:-

int byte1 = Wire.read();
int byte2 = Wire.read();

Reads two bytes. At this point the second byte almost certainly has not arrived.
So I would try:-

while (Wire.available() < 2);

I haven't looked at the rest of the code so there might be other errors as well.

It's I2C Mike, not Serial.

@op:

Don't do this:

  while (Wire.available() == 0);

Wire.requestFrom blocks until it gets the data you wanted, or it never will. Better is:

if (Wire.requestFrom(device_address, 2) != 2)
  {
  // error
  return;
  }

I don't know what this achieves:

  int byte1 = Wire.read();
  int byte2 = Wire.read();
  String binaryByte1 = String(byte1, BIN);
  String binaryByte2 = String(byte2, BIN);

Just do:

  byte byte1 = Wire.read();
  byte byte2 = Wire.read();

  Serial.print("The current volume setting is: ");
  Serial.print(byte1, BIN);
  Serial.print(":");
  Serial.println(byte2, BIN);

It's I2C Mike, not Serial.

Yes I know, but you still have to wait until the data arrives.

My timing must be lucky - I was getting both bytes in time... but your recommended modification is certainly a good idea, I've implemented it. So the fact remains, when I set the values, and then try to read them, they come back different. I think this is more than likely a learning curve issue... I've gotta be doing something wrong.

Grumpy_Mike:
Yes I know, but you still have to wait until the data arrives.

No, because requestFrom blocks. Building in the wait only makes an infinite loop possible.

KC8OZA:
I've gotta be doing something wrong.

I've looked but I can't find the programming guide. Can you link to it please? The thing that specifies the I2C commands/responses.

The chip is the same as the FM receiver that Parallax sells, so I've been using their datasheet and example code as reference.

http://www.parallax.com/portals/0/downloads/docs/prod/audiovis/27984-FM-RadioReceiver-v1.0.pdf
http://www.parallax.com/portals/0/downloads/docs/prod/audiovis/ParallaxFMRadio-spin.zip

Some additional things I don't quite 'get' .... When I send a bitstream to try and write the register, do I send it with bit 7 first, then 6, 5, etc (like you'd write it in binary on paper) or does it go LSB first (unlike how you'd write it on paper). Do I have to send it one byte at a time, or can I send the full two bytes at once? I think if I can get my arms around the proper way to read and write the registers, I'll be home free.

Thx

The registers are 16 bit wide therefore you have to send them in two parts.
That data sheet says the high byte followed by the low byte. So to send a value in a variable called val use:-

Wire.write(val >> 8); // hi byte 
Wire.write(val & 0xff); // lo byte

Values come back from the chip with the hi byte first.

Ok so we're sending it as two bytes. I get that. We're sending the high byte first - I get that. So when I create the variable that is going to be split with the code you provided, do I write it with bit 15 first or bit 0 first?

do I write it with bit 15 first or bit 0 first?

I don't understand what you mean by first.
Declare the variable as an int

int var;

if you want to set bit 0 then:-

var = var | 0x1;

or if you want to set bit 15 then:-

var = var | 0x8000

The bits are the same as in a normal value.

So lets say I wanted bits 0123 to be 1's. Would I write the variable as shown in A or B

A - var = "0000000000001111"
B - var = "1111000000000000"

I think I write it as A

I think I write it as A

Yes so do I.

Except don't put the value in quote marks, write it in hex.

So when you say var = var | 0x8000 is this simply starting at the position identified? Like I could write just the last 4 bits of the register if I really wanted to?

The | is the inclusive OR operation it is used to set those bits in the variable that are a 1 in the mask (the number you are ORing) and leave the other bits in the variable unchanged.

I could write just the last 4 bits of the register if I really wanted to?

If you want to set the variable to 0000000000001111 then you can just use:-
var = 0xf

It sounds like learning about this OR'ing operation is going to be useful to me. Where can I learn more about how this works?

Here:-
http://www.arduino.cc/playground/Code/BitMath

Ok so after reviewing the binary math stuff, I think I get it. SO let me ask you this.... I probably want to read the register, store the result, perform a binary OR against the data, adding the bits I want to change, and then push the whole string back out , correct? There's nothing that's going to let me just send selected bits over I2C is there? Just making sure, but I think I need to be sending the whole byte(s).

Josh

there's nothing that's going to let me just send selected bits over I2C is there?

Correct

I think I need to be sending the whole byte(s)

Yes.

I probably want to read the register, store the result, perform a binary OR against the data, adding the bits I want to change, and then push the whole string back out , correct?

Normally you would not read them, you would have variables set up in the processor modify the bits and write them out. You would only read them when you want to do something with the read bits that the radio can change.

KC8OZA:
So for example, lets say I want to write the register at 0x05, and set bits 0-3 as the value 1 (forget the other bits, they're not important yet), I'm doing whats shown below. But its not working. When I read it back, its not how I set it. What am I doing wrong?

Is it working yet? I'm guessing not.

Now that I read the datasheet you linked to, I have to say it is one of the more confusing documents I have read.

After staring at it for a while I think they are saying that the device address is in fact the register. So, for example, say we want to set some bits in register 2:

15: DHIZ (1)
14: DMUTE (1)
13: MONO (0)
12: BASS (1)
11: RESERVED (0)
10: CLK32 (0)
8-7: RESERVED (0)
6-4: CLK_MODE (0)
1: SOFT RESET (0)
0: ENABLE (1)

So lets turn them into bits with the example values above:

0b1101000000000010
  |      |       |
  15     8       0

So we may as well split them up ourselves:

byte highCommand = 0b11010000;
byte lowCommand  = 0b00000010;

If I'm right, we now need to send to register (address) 2 on the device:

Wire.beginTransmission (2);
Wire.write (highCommand);
Wire.write (lowCommand);
Wire.endTransmission ();

And to get them back:

if (Wire.requestFrom (2, 2) != 2)  // read from register 2, for 2 bytes
  {
  // error
  return;
  }

byte readHigh = Wire.read ();
byte readLow = Wire.read ();

Try that and see. Also try my I2C scanner on this page:

If my theory is correct you will see it reporting that addresses 2, 3, 4, 5, 0x0A, 0x0B are on the bus.