Author Topic: STM32: I2C with HAL what am I doing wrong?  (Read 9831 times)

0 Members and 3 Guests are viewing this topic.

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
STM32: I2C with HAL what am I doing wrong?
« on: January 26, 2022, 06:00:33 pm »
Hey everyone,
After a successful attempt with SPI, I am now having trouble with I2C.
Contect; I'm using a Nucleo-L412KB to try to command a MAX6956AAX LED driver. Using STM32CubeIDE.
The thing is that I can't get to send anything at all. Here's part of main.c (the full file is attached):
Code: [Select]
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
uint8_t devAddW = 0b10000000; // device (MAX6956) address; W because end with bit 0 (write mode)
uint8_t data[8]; // data array
int8_t dataLength; // size that has to be transmitted
uint32_t tickMem; // last recorded tick
uint16_t delay = 200; // time delay (ms)

HAL_I2C_StateTypeDef stateI2C_1; // state of the I2C bus
HAL_I2C_StateTypeDef stateI2C_2; // state of the I2C bus too
HAL_StatusTypeDef retI2C; // value returned by I2C transmit
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_I2C1_Init(void);

int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_I2C1_Init();
  /* USER CODE BEGIN 2 */
    // setting pins as LED drives
data[0] = 0x0A;
data[1] = 0x0;
data[3] = 0x0;
data[4] = 0x0;

dataLength = 4;

stateI2C_1 = HAL_I2C_GetState(&hi2c1);
retI2C = HAL_I2C_Master_Transmit(&hi2c1, devAddW, data, dataLength, 100);
stateI2C_2 = HAL_I2C_GetState(&hi2c1);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
while (1) {

data[0] = 0x07; // display test mode command address
data[1] = 0x00; // display test mode disabled (normal mode)
dataLength = 2;

stateI2C_1 = HAL_I2C_GetState(&hi2c1); // state before transmit
retI2C = HAL_I2C_Master_Transmit(&hi2c1, devAddW, data, dataLength, 100);
stateI2C_2 = HAL_I2C_GetState(&hi2c1); // state after transmit

HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3); // blink LED to show at least *something* is happening.
tickMem = HAL_GetTick();
while (HAL_GetTick() < tickMem + delay)
{
}

data[0] = 0x07; // display test mode command address
data[1] = 0xFF; // display test mode enabled : should light LEDs at half current

stateI2C_1 = HAL_I2C_GetState(&hi2c1); // state before transmit
retI2C = HAL_I2C_Master_Transmit(&hi2c1, devAddW, data, dataLength, 100);
stateI2C_2 = HAL_I2C_GetState(&hi2c1); // state after transmit

HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3); // blink LED to show at least *something* is happening.
tickMem = HAL_GetTick();
while (HAL_GetTick() < tickMem + delay)
{
}
}
}
I also add the I2C init function so you may see if my config is wrong:
Code: [Select]
static void MX_I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00707CBB;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
/** Configure Analogue filter
*/
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE)
!= HAL_OK) {
Error_Handler();
}
/** Configure Digital filter
*/
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK) {
Error_Handler();
}
}
What happens is that HAL_I2C_Master_Transmit() returns HAL_ERROR (in the retI2C variable) from the first attempt (before the infinite loop).
Is it the 3d arg I give in HAL_I2C_Master_Transmit() that isn't good?
Because "Matth" was already taken.
 

Offline thm_w

  • Super Contributor
  • ***
  • Posts: 6349
  • Country: ca
  • Non-expert
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #1 on: January 26, 2022, 10:52:28 pm »
Quote
Slave Address
The MAX6956 has a 7-bit-long slave address (Figure 6). The eighth bit following the 7-bit slave address is the R/W bit. It is low for a write command, high for a read command. The first 3 bits (MSBs) of the MAX6956 slave address are always 100. Slave address bits A3, A2, A1, and A0 are selected by address inputs, AD1 and AD0. These two input pins may be connected to GND, V+, SDA, or SCL. The MAX6956 has 16 possible slave addresses (Table 3) and therefore, a maximum of 16 MAX6956 devices may share the same interface.

https://datasheets.maximintegrated.com/en/ds/MAX6956.pdf

Quote
* @param  DevAddress Target device address: The device 7 bits address value
 *         in datasheet must be shifted to the left before calling the interface

How do you have the address pins on the MAX6956 hooked up?

The third argument looks ok, data[] array.
But you are using int for dataLength, not uint, not sure if that matters:

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
« Last Edit: January 26, 2022, 11:00:50 pm by thm_w »
Profile -> Modify profile -> Look and Layout ->  Don't show users' signatures
 
The following users thanked this post: Watth

Offline Robotec

  • Contributor
  • Posts: 44
  • Country: es
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #2 on: January 27, 2022, 08:06:02 am »
it may be included in some of your functions but in almost all CUBEMX predefined functions after init you have to call the correspondent start function.

First thing I would do is attach a oscilloscope or logic analyser and see what is happening there.
 

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #3 on: January 27, 2022, 10:43:24 am »
Quote
Slave Address
How do you have the address pins on the MAX6956 hooked up?
both to GND, therefore the adress (from A6 to A0] should be 1000000
Quote
The third argument looks ok, data[] array.
But you are using int for dataLength, not uint, not sure if that matters:

HAL_StatusTypeDef HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)
Ah, thanks, I didn't catch that. Also I didn't pay attention and should have used a u_in16 (adress shifted to the right, as per UM1785). Size should be unint_16 too. Althjough, note that the compiler didn't note that as an error nor a warning.
So here's the new declaration block:
Code: [Select]
uint16_t devAddW = 0b1000000000000000; // // device address W because (ends with bit 0 for write mode) shifted to right
uint8_t data[8]; // data array
uint16_t dataLength; // size that has to be transmitted
uint32_t tickMem; // last recorded tick
uint16_t delay = 200; // time delay (ms)
Because "Matth" was already taken.
 

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #4 on: January 27, 2022, 10:45:30 am »
it may be included in some of your functions but in almost all CUBEMX predefined functions after init you have to call the correspondent start function.
I didn't find anything except init functions (that MX added automatically)
Quote
First thing I would do is attach a oscilloscope or logic analyser and see what is happening there.
I did, and there's nothing there. Which isn't surprising as I I checked with the debug that the transmit functions always return errors :/
Because "Matth" was already taken.
 

Offline Robotec

  • Contributor
  • Posts: 44
  • Country: es
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #5 on: January 27, 2022, 11:32:08 am »
after: static void MX_I2C1_Init(void); which includes the HAL_I2C_Init(&hi2c1) but i checked and no start is required in i2c.

HAL_I2C_IsDeviceReady() should give you if the system is ready, anyway stm32f4xx_hal_i2c.c has a good read on how to use the i2c functions.

anyway I would comment everything that is non-essential like the filters for testing and later on keep adding stuff.

If that still doesn't work try some example code from STM.

Finally check that the i2c address has to be moved 1 bit to the left or not, for example my AS5600 0x36 and is defined like this:

is #define I2C_ADDRESS   0x36<<1.

in your case(example of just 1 bank)

#define I2C_ADDRESS   0x0F<<1.

the write or read is handled by the function used not by the address normally , it would be really weird.

HAL_I2C_Master_Transmit or HAL_I2C_Master_Receive.
 
The following users thanked this post: Watth

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #6 on: January 27, 2022, 11:44:42 am »
Yes, I'll start from scratch with the minimum functions to be sure I2C is set-up and running.
Note that for the config I let CubeMX do the job.
I also noticed HAL_I2C_IsDeviceReady() it should be worth a look; I'll need to be sure I2C is set-up and running.
I'm having a look at examples I find online, and will try with another I2C slave or another nucleo board I have.

I guess there could indeed be a problem with how I entered the address.
Because "Matth" was already taken.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5895
  • Country: es
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #7 on: January 27, 2022, 06:23:38 pm »
So here's the new declaration block:
Code: [Select]
uint16_t devAddW = 0b1000000000000000; // // device address W because (ends with bit 0 for write mode) shifted to right
uint8_t data[8]; // data array
uint16_t dataLength; // size that has to be transmitted
uint32_t tickMem; // last recorded tick
uint16_t delay = 200; // time delay (ms)

No, no, no. It's a int16 because you can use larger addressing.
You can call a function passing an 8-bit value, no matter if the function use 8,16,32bit input value.
It'lll copy the value, it's fine.

What you can't do, for example, is passing a 8-bit pointer to a function expecting a 16 bit pointer.

Your config and your device use 7-bit address, so keep using:
uint8_t devAddW = (0b1000000 <<1 );

That address is only set when AD0 and AD1 pins are tied to ground, have you checked that?

HAL will use only the lower byte because it's configured in 7-bit addressing mode.


To send/receive raw data:
Code: [Select]

  * @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  * @param  DevAddress Target device address: The device 7 bits address value
  *         in datasheet must be shifted to the left before calling the interface
  * @param  pData Pointer to data buffer
  * @param  Size Amount of data to be sent
  * @param  Timeout Timeout duration
  * @retval HAL status

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

However, if you want to read or write to a specific register, it will fail, because it sends Start/Stops on every call.

Ex. you can't do:
HAL_I2C_Master_Transmit(MAX_Addr, command)
And then
HAL_I2C_Master_Receive(MAX_Addr, data)
And expect you'll get the data from the first command.

The I2C device is unselected between these calls because there was a Stop condition, so every call is a new transaction.
You must send MAX6956_Addresss, cmd and transfer/receive data, all in a single transaction.

To read/write to a specific memory address (register), use:
Code: [Select]
  * @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains
  *                the configuration information for the specified I2C.
  * @param  DevAddress Target device address: The device 7 bits address value
  *         in datasheet must be shifted to the left before calling the interface
  * @param  MemAddress Internal memory address
  * @param  MemAddSize Size of internal memory address
  * @param  pData Pointer to data buffer
  * @param  Size Amount of data to be sent
  * @param  Timeout Timeout duration
  * @retval HAL status

HAL_I2C_Mem_Write(hi2c, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout)
HAL_I2C_Mem_Read(hi2c, DevAddress, MemAddress, MemAddSize, pData, Size, Timeout)

DevAddress is (MAX6956_Addresss<<1)
MemAddress is MAX6956 Command byte.
Command byte is 8-bit, so use I2C_MEMADD_SIZE_8BIT for MemAddSize.
pData is a pointer to your data.
Size is is number of pData  bytes to send.

Ex. Writing 0x0F(16/16 current, 24mA) to Global Current register (0x02)

uint8_t data=0x0F;
HAL_I2C_Mem_Write(&hi2c1,MAX_Addr, 0x02, I2C_MEMADD_SIZE_8BIT, &data, 1, 100 )

1=1 data byte to send, 100=100ms timeout (1ms should be already more than enough)

All these functions run start and stop conditions, no need to manually do them.
« Last Edit: January 27, 2022, 10:56:40 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: thm_w, Watth

Offline Ground_Loop

  • Frequent Contributor
  • **
  • Posts: 644
  • Country: us
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #8 on: January 28, 2022, 02:51:52 am »
Are you using pull up resistors?
There's no point getting old if you don't have stories.
 
The following users thanked this post: Watth

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #9 on: January 28, 2022, 10:21:30 am »
Oh for frog's snacks it was the pull-up resistors.  :palm:

I knew it could have been something like that since I didn't get anything on the oscilloscope. But also bad parameters on the I2C.
Anyway I know have the I2C functions returning HAL_OK, including HAL_I2C_IsDeviceReady() \o/
I now have something on the oscilloscope. And it works! It's alive!
By the way, I tried with another I2C device: NXP's PCA9535PW and its datasheet also says it needs pull-up resistors on clock and data. RTFDS!
Because "Matth" was already taken.
 

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #10 on: January 28, 2022, 10:24:20 am »
However, if you want to read or write to a specific register, it will fail, because it sends Start/Stops on every call.
Now that it managed to get I2C running, I tried both, and both work. So HAL_I2C_Master_Transmit() does send both bytes consecutively within the same start/stop limits.
Because "Matth" was already taken.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 5895
  • Country: es
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #11 on: January 28, 2022, 05:42:55 pm »
Yes, when the 1st byte is the command, it's equivalent, but only for writing.
The problem will come when you try to read data, as I already explained
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: Watth

Offline WatthTopic starter

  • Regular Contributor
  • *
  • Posts: 241
  • Country: fr
Re: STM32: I2C with HAL what am I doing wrong?
« Reply #12 on: January 29, 2022, 11:17:35 am »
Yes, when the 1st byte is the command, it's equivalent, but only for writing.
The problem will come when you try to read data, as I already explained
Oh OK, sorry. I'm still on the writing side for now. I managed to write the PCA9535's outputs register, but still have trouble with the MAX6956:
On the MAX6956 (in my case the 36 pins PW) I managed to set some pins as LED drivers and light them up with the test function. But I have trouble understanding the datasheet and this application note to control a pin individually as a LED driver and assign it a current fraction.
Because "Matth" was already taken.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf