Tuesday, 21 March 2017

microcontroller - Addressing registers with I2C STM32F0 HAL libraries


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];
}

Answer



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*)(®ister_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, ®ister_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.


No comments:

Post a Comment

arduino - Can I use TI's cc2541 BLE as micro controller to perform operations/ processing instead of ATmega328P AU to save cost?

I am using arduino pro mini (which contains Atmega328p AU ) along with cc2541(HM-10) to process and transfer data over BLE to smartphone. I...