16
\$\begingroup\$

I'm very new to using STM's CUBE and HAL_libraries. I'm using an STM32F0 microcontroller with 32 pins. The schematic for I2C is correct. So I need a little help here.

I have a capacitive sensor (FDC1004) that uses I2C communication. I have to write these registers to read the data.

How could I correctly send START request form master to slave (slave address is A0)?

How to set pointer to 0x0C register?

  • Datasheet sees (Register 0x0C:bit[7:4]) to 1.) I don't know, how to do that? And finally how to READ from the same register?
  • Plus I have to wait DONE_x field (Register 0x0C:bits[3:0]) before I read it?

But I don't know if I am addressing the right registers! Because I don't get any data back from the sensor!

Here is my code:

int I2Ccomm ()
{

    HAL_I2C_Master_Transmit(&hi2c1,0xA1,0x0C, 10, 100); //start bit and pointer to register
    HAL_Delay(50);
    HAL_I2C_Master_Transmit(&hi2c1,0xA1,0x054, 10, 100); // setting the register
    HAL_Delay(50);

    HAL_I2C_Master_Receive(&hi2c1, 0xA0, 0x0C, 10, 100); //read from this register
    HAL_Delay(50);
    HAL_I2C_Master_Receive(&hi2c1, 0xA0, 0x02, 10, 100); //read data from register

    return ReadREG[1];
}
\$\endgroup\$
3
  • \$\begingroup\$ Please ask a more specific question(s). like How do I address X register properly? This is a badly worded question. For further guidelines see electronics.stackexchange.com/help/how-to-ask \$\endgroup\$
    – Voltage Spike
    Jul 5, 2016 at 18:19
  • \$\begingroup\$ Then is it better to write a new question or just edit this one? \$\endgroup\$
    – yest111
    Jul 5, 2016 at 18:44
  • \$\begingroup\$ Better to edit, less questions and we won't have to close this one. \$\endgroup\$
    – Voltage Spike
    Jul 5, 2016 at 18:53

1 Answer 1

22
\$\begingroup\$

Let's start with the HAL_I2C_Master_Transmit() function. If you check its declaration:

 HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  1. Minor problem with 2nd parameter, the slave device address. The slave device address is b1010000 if we complete it to 8bit format, it will be 0xA0, just as you said. Now when passing this to HAL_I2C_Master_Transmit() you do not have to set the R/W bit manually, HAL will do it for you. So when you call HAL_I2C_Master_Transmit() the transmitted R/W bit will be automatically 0 indicating write operation and when you call HAL_I2C_Master_Receive() the the transmitted R/W bit will be automatically 1 indicating write operation. You have mixed the R/W values but I think it is a don't care bit for the function, so it is not an actual error in your code.

  2. The 3rd parameter (uint8_t *pData) is a pointer to a buffer which contains the data to be sent. Now, in your call the 3rd parameter is 0x0C which is your actual data, the register address. The problem is, it will be interpreted as a pointer (by the HAL_I2C_Master_Transmit()) to a memory location, where some undefined data can be found.

  3. The 4th parameter is the size of the buffer, the number of bytes to be sent. If you want to send a single byte then this parameter should be 1 and not 10.

When working with \$ \small I^2C \$ the best thing to do is to get the datasheet of the slave device and look up the documentation of write and read operations.

Write registers

Here is the corresponding diagram from the datasheet.

enter image description here

So after sending the slave address to the bus, three more bytes should be transmitted: register pointer, MSB byte, LSB byte. A general implementation with HAL writing 16bit registers:

void write_register(uint8_t register_pointer, uint16_t register_value)
{
    uint8_t data[3];

    data[0] = register_pointer;     // 0x0C in your example
    data[1] = register_value>>8;    // MSB byte of 16bit data
    data[2] = register_value;       // LSB byte of 16bit data

    HAL_I2C_Master_Transmit(&hi2c1, 0xA0, data, 3, 100);  // data is the start pointer of our array
}

Example with your values: write_register(0x0C, 0x0054);

Alternatively HAL defined register write function can be used as well, which has additional parameters for passing register address and address size.

void write_register(uint8_t register_pointer, uint16_t register_value)
{
    HAL_StatusTypeDef status = HAL_OK;

    status = HAL_I2C_Mem_Write(&hi2c1, 0xA0, (uint16_t)register_pointer, I2C_MEMADD_SIZE_8BIT, (uint8_t*)(&register_value), 2, 100); 

    /* Check the communication status */
    if(status != HAL_OK)
    {
        // Error handling, for example re-initialization of the I2C peripheral
    }
}

Now, the HAL_I2C_Master_Receive() function is almost the same as the other.

HAL_StatusTypeDef HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);

Only difference is that the 3rd parameter is a pointer to the buffer where the received data will be stored. It is 0x02 in your code and I do not know what was your purpose with it, but it will be interpreted as a pointer (unfortunately to a random memory location).

Read registers

enter image description here

To read a register, it must be selected with an \$ \small I^2C \$ write operation by sending the appropriate register pointer (Note that if you have written this register right before the read then you do not have to send again its address to the pointer register, as you have already set it during write). Then with an \$ \small I^2C \$ read operation, read back the 16bit data.

void read_register(uint8_t register_pointer, uint8_t* receive_buffer)
{
    // first set the register pointer to the register wanted to be read
    HAL_I2C_Master_Transmit(&hi2c1, 0xA0, &register_pointer, 1, 100);  // note the & operator which gives us the address of the register_pointer variable

    // receive the 2 x 8bit data into the receive buffer
    HAL_I2C_Master_Receive(&hi2c1, 0xA0, receive_buffer, 2, 100);   
}

Example:

uint8_t reg_ptr = 0x0C;
uint8_t buffer[2];

read_register(reg_ptr, buffer);

// the register content available in the buffer

There is also a HAL defined register read function as well, which has.

uint16_t read_register(uint8_t register_pointer)
{
    HAL_StatusTypeDef status = HAL_OK;
    uint16_t return_value = 0;

    status = HAL_I2C_Mem_Read(&hi2c1, 0xA0, (uint16_t)register_pointer, I2C_MEMADD_SIZE_8BIT, &return_value, 2, 100);

    /* Check the communication status */
    if(status != HAL_OK)
    {

    }

    return return_value;
}

Read through 8.5 Programming section of the datasheet for more details.

\$\endgroup\$
4
  • \$\begingroup\$ Thank you for your answer, now it is finally working. But one more question? Do I have to wait a few mili secundes for reading or could I read without any delay? \$\endgroup\$
    – yest111
    Jul 8, 2016 at 14:07
  • \$\begingroup\$ I do not think that any delay is necessary, you could try without any first. \$\endgroup\$ Jul 8, 2016 at 14:23
  • 1
    \$\begingroup\$ You do not need to wait, I2C involves looping on the clock signal until the slave has completed. \$\endgroup\$ Jul 11, 2016 at 20:14
  • \$\begingroup\$ In case anybody else has my issue, 7bit device address must be shifted left 1 bit. ie my target was 0x60, but used 0xC0 on HAL_I2C_Mem_Read \$\endgroup\$
    – xvan
    Oct 26 at 19:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.