CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

Problem about IMU sensor (ICM-20948) with I2C
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

Problem about IMU sensor (ICM-20948) with I2C
PostPosted: Thu Aug 15, 2019 7:36 am     Reply with quote

Hello, I am new to embedded system and I am trying out with the PIC18f4550 and a ICM 20948 modules. So in order to get the sensor reading, I think the single- byte write/read sequence would be used. However, I do not know what actually need to be write on the code in order to get the reading. The below code is my attempt on the single - byte read sequence, hope someone could give me some advice.
Code:

main()
{
i2c_start();  // Start condition
i2c_write(b1101000); // Problem 1 : not sure if this is the right salve address
i2c_write(cmd); //Problem 2: Is that I should the register in the user bank register map?
i2c_start();
i2c_write(b1101001)
//Problem 3: How should I do the NACK ?
i2c_stop(); // Stop condition


while()
{}
}


Here is the datasheet for the IMU module :http://www.invensense.com/wp-content/uploads/2016/06/DS-000189-ICM-20948-v1.3.pdf
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 8:58 am     Reply with quote

First , download and confirm you can run PCM P's 'I2C Scanner' program. It's in the code library.
That PIC is a 5 volt device, the peripheral is a 3 volt device, so unless you have 'logic level 'conversion betwen the devices, it won't operate properly. It 'might' kinda work depending on the I/O pins you select.

You can use the 'L' version of that PIC as it will run on 3 volts.

Jay
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 9:36 am     Reply with quote

I had looked at the I2C Scanner program. However, I could not run debug and print the message. Also my board don't have any LCD / any display, would you have any suggest such that I could use the program to check the slave address ?
temtronic



Joined: 01 Jul 2010
Posts: 9081
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 10:31 am     Reply with quote

Since that PIC has USB, is it connected to a PC ? If not, do you have ONE spare output pin ? That could be connected to a TTL<>USB module and then you could send information from PIC to a terminal program running on the PC.
If that's not possible, do you have a pin that could have an LED-resistor attached? You could modify the I2C scanner program to 'pulse' the LED in a similar fashion that old P.O.S.T. cards did for PCs.
For any kind of sucessful programming ,you really need either serial connection to PC or a 'local' display on the PIC PCB.

Jay
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 10:46 am     Reply with quote

Here is the data sheet:
https://www.invensense.com/wp-content/uploads/2016/06/DS-000189-ICM-20948-v1.3.pdf
On page 14, it lists the i2c slave address of the device. It's given in 7-bit
format. CCS uses 8-bit format, so left-shift it by 1. This gives possible
i2c addresses of 0xD0 or 0xD2, depending on the state of the AD0 pin.
The AD0 pin is on pin 9 of the ICM chip. It's probably tied to ground, but
that's the kind of thing you can find out by using the i2c scanner program.

Let's do hardware. I assume the ICM-20948 module is running at +3.3v.

Method 1 - Using the hardware i2c module in the 18F4550:

1. Connect SDA on the ICM to SDA on the PIC (PIN_B0).
Connect SCL on the ICM to SCL on the PIC (PIN_B1).

2. Use 3.3K pull-up resistors on SDA and SCL. Connect the pull-ups to 3.3v.

3. Enable the SMBus mode in the hardware i2c controller in the 18F4550
with the following #use i2c() statement:
Code:
#use i2c(Master, I2C1, SMBUS, Slow)



Method 2 - Use software i2c on the 18F4550:

1. Same as above, but it could use any pins on PortB, not just B0 & B1.
But pins B6 and B7 should be kept for the ICSP programmer's use.

2. Same as above.

3. Use the following #use i2c() statement:
Code:
#use i2c(Master, sda=PIN_B0, scl=PIN_B1, Force_sw, Slow)

sda and scl could be other pins on PortB (only).

Once you have method 1 or 2 set up, then use the i2c scanner program
to see if it detects the ICM device.
http://www.ccsinfo.com/forum/viewtopic.php?t=49713
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 11:36 am     Reply with quote

Thanks for the help I will try it and update in few days. But I would like to ask after using the I2C scanner to check the modules, what would be the procedure to get the sensor reading? is it using the read/write sequence that on page 30 on the datasheet

Datasheet: https://www.invensense.com/wp-content/uploads/2016/06/DS-000189-ICM-20948-v1.3.pdf
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 12:43 pm     Reply with quote

It tells you exactly in the Burst Read Sequence diagram. The master sends:

S
AD+W
RA
S
AD+R
Data
Data + NACK // Do NACK by master on last data byte read
P

Translate that to CCS, but only read one byte in this example:
Code:

#define ICM_WR_ADDR  0xD0
#define ICM_RD_ADDR  0xD1
#define ICM_REG_WHO_AM_I  0x00

int8 result;

i2c_start();
i2c_write(ICM_WR_ADDR);
i2c_write(ICM_REG_WHO_AM_I);
i2c_start();
i2c_write(ICM_RD_ADDR);
result = i2c_read(0);   // Do a NACK on the last read operation
i2c_stop();

The result from reading the WHO_AM_I register should be 0xEA.
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Thu Aug 15, 2019 10:24 pm     Reply with quote

Thanks PCM programmer, it is clear and I understand how it works now.
But if i want to do a self test for gyro module, first it need to be enable the self test at address 02 like stated on page 60 from the datasheet.
However, how do I change particular bits [5:3] without modifying the reserved bit[7:6]? And how do I check if the value is within the correct range ?
Code:

#define ICM_WR_ADDR  0xD0
#define ICM_RD_ADDR  0xD1
#define ICM_REG_WHO_AM_I  0x00

int8 result;

i2c_start();                        //S
i2c_write(ICM_WR_ADDR);            //AD+W
i2c_write(0x02);                   //RA
i2c_write(DATA);                  //Question stated above
i2c_stop();                           //P
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Aug 16, 2019 2:47 am     Reply with quote

Toast wrote:

How do I change paticular bits [5:3] without modifing the reserved
bit[7:6]? And how do I check if the value is within the correct range ?

Read the register in the ICM chip. Put the result into a temporary
variable in your program. Then use the CCS function bit_set() to set
a bit that you want set. Then write the modified temporary variable
back to the register in the ICM chip.


Toast wrote:

How do I check if the value is within the correct range ?

According to the ICM data sheet on page 11, it says:
Quote:

The self-test response for each gyroscope axis is defined in the
gyroscope specification table:

3.1 GYROSCOPE SPECIFICATIONS

Full-Scale Range: GYRO_FS_SEL=0 ±250 dps

When the value of the self-test response is within the specified min/max
limits, the part has passed self-test. When the self-test response exceeds
the min/max values, the part is deemed to have failed self-test.

You can set GYRO_FS_SEL to other values and get a wider range, but
your first test can be with it set = 0. You will need a 'signed int16' variable
to hold values from -250 to +250.
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Fri Aug 16, 2019 4:03 am     Reply with quote

In the process, I found that there are some register sharing the same address with different user bank register.
Does it mean that I need to write the command to change the user bank before I write the RA?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Aug 16, 2019 2:21 pm     Reply with quote

Toast wrote:

Does it mean that I need to write the command to change the user bank
before I write the RA?

There is a sample driver for the ICM20648, which is apparently a similar
chip to the ICM20948, available on Github:
https://os.mbed.com/teams/SiliconLabs/code/ICM20648/file/296308a935f5/ICM20648.cpp/

Search for these lines:
Quote:

void ICM20648::read_register

void ICM20648::write_register

These are SPI routines, but they do call the select_bank() routine
before they talk to any register. It's very simple to calculate the bank.
You can put the same thing in your driver.

You should write the read_register() and write_register() routines,
except use i2c, as shown in earlier posts in this thread. Incorporate
the bank select code at the start of each routine.
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Wed Aug 28, 2019 11:34 pm     Reply with quote

PCM programmer wrote:
It tells you exactly in the Burst Read Sequence diagram. The master sends:

S
AD+W
RA
S
AD+R
Data
Data + NACK // Do NACK by master on last data byte read
P

Translate that to CCS, but only read one byte in this example:
Code:

#define ICM_WR_ADDR  0xD0
#define ICM_RD_ADDR  0xD1
#define ICM_REG_WHO_AM_I  0x00

int8 result;

i2c_start();
i2c_write(ICM_WR_ADDR);
i2c_write(ICM_REG_WHO_AM_I);
i2c_start();
i2c_write(ICM_RD_ADDR);
result = i2c_read(0);   // Do a NACK on the last read operation
i2c_stop();

The result from reading the WHO_AM_I register should be 0xEA.


I have tested the code, however the result is not 0xEA. The below is my code
Code:

#use i2c(Master,sda=PIN_B2,scl=PIN_B1,force_hw)
#define LED pin_a2

#define ICM_WR_ADDR 0xD0
#define ICM_RD_ADDR 0xD1
#define ICM_REG_WHO_AM_I 0x00

/* Register common for all banks */
#define ICM_REG_BANK_SEL            0x7F 


/**************************************************************************//**
* @name ICM20648 register banks
* @{
******************************************************************************/
#define ICM_BANK_0                  (0 << 7)     /**< Register bank 0 */
#define ICM_BANK_1                  (1 << 7)     /**< Register bank 1 */
#define ICM_BANK_2                  (2 << 7)     /**< Register bank 2 */
#define ICM_BANK_3                  (3 << 7)     /**< Register bank 3 */
/**@}*/


void select_bank(unsigned int8 bank)
{

    /* clear R/W bit - write, send the address */
    i2c_start();                             //S
    i2c_write(ICM_WR_ADDR);                  //AD+W
    i2c_write(ICM_REG_BANK_SEL);        //RA
    i2c_write((unsigned int8)(bank<<4));
    i2c_stop();
   
    return;
}
void who_am_i()
{
int8 result;
// get the who am i register value

select_bank(0);
i2c_start();
i2c_write(ICM_WR_ADDR);
i2c_write(ICM_REG_WHO_AM_I);
i2c_start();
i2c_write(ICM_RD_ADDR);
result = i2c_read(0);
i2c_stop();

if(result != 0xEA)
{
output_high(LED);
}
else
{
output_low(LED);
}

////////////////////
}

void main()
{

who_am_i();



   while(TRUE)
   {
   }

}

I got the LED turned on so the result is not 0xEA, however i dont have any display hardware so i cannot tell the exact value of the result. I also checked the i2c device with i2c scanner program.
Also, I would like to ask why ICM_WR_ADDR would be 0xD0 and ICM_RD_ADDR would be 0xD1. I am new to pic and not quite understand how it works. My thought is that the slave address of the ICM-20948 is b110100X which is 7bit long, but in the code the address write to i2c is 8bit long, since i do not long which whether the extra bit is append to first or last, i considered both cases. First, bX110 100X which the last four bit will not be 0 or 1. Second, b 1101 00X0 the last four bit will not be 0 or 1 as well. So i am a bit confused and hoping for some helping hands.
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 29, 2019 12:10 am     Reply with quote

CCS uses 8-bit i2c address format. To convert 7-bit to 8-bit format,
shift the 7-bit format left by 1. Then make bit 0 = 0 for an i2c write
and make bit 0 = 1 for an i2c read.
Toast



Joined: 15 Aug 2019
Posts: 16

View user's profile Send private message

PostPosted: Thu Aug 29, 2019 1:08 am     Reply with quote

PCM programmer wrote:
CCS uses 8-bit i2c address format. To convert 7-bit to 8-bit format,
shift the 7-bit format left by 1. Then make bit 0 = 0 for an i2c write
and make bit 0 = 1 for an i2c read.

Does it mean that from my code , the i2c_write(ICM_RD_ADDR) should be using i2c_read instead? I am quite confused when should use write and when should use read
Code:
select_bank(0);
i2c_start();
i2c_write(ICM_WR_ADDR);
i2c_write(ICM_REG_WHO_AM_I);
i2c_start();
i2c_write(ICM_RD_ADDR);
result = i2c_read(0);
i2c_stop();


Last edited by Toast on Thu Aug 29, 2019 1:13 am; edited 1 time in total
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Thu Aug 29, 2019 1:10 am     Reply with quote

You send the write address before you write to a register, and you send
the read address before you read a register, just as the code shows.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group