cancel
Showing results for 
Search instead for 
Did you mean: 

[SOLVED] STM32F103c8t6 (aka blue pill) i2c with AM2320: HAL_I2C_IsDeviceReady() always returns an error.

FGerm.1
Associate II

So I've been trying for a while to get the I2C1 peripheral working for communicating with the AM2320 humidity/temperature sensor but for some reasons I have not yet broken down, it never seems to reach it. I used CubeMX to initialize the peripherals and the main.c code is as it follows:

#include "main.h"

I2C_HandleTypeDef hi2c1;

void SystemClock_Config(void);

static void MX_GPIO_Init(void);

static void MX_I2C1_Init(void);

int main(void)

{

 HAL_Init();

 SystemClock_Config();

 MX_GPIO_Init();

 MX_I2C1_Init();

 HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, 1);

 while (1)

 {

  HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, 1);

  for(int i = 0; i < 255; i++)

  {

   if(HAL_I2C_IsDeviceReady(&hi2c1, i, 1, 10) == HAL_OK)

   {

    HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, 0);

   }

  }

 }

}

void SystemClock_Config(void)

{

 RCC_OscInitTypeDef RCC_OscInitStruct = {0};

 RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

 /** Initializes the RCC Oscillators according to the specified parameters

 * in the RCC_OscInitTypeDef structure.

 */

 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

 RCC_OscInitStruct.HSEState = RCC_HSE_ON;

 RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;

 RCC_OscInitStruct.HSIState = RCC_HSI_ON;

 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;

 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

 RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;

 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

 {

   Error_Handler();

 }

 /** Initializes the CPU, AHB and APB buses clocks

 */

 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

                             |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;

 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;

 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)

 {

   Error_Handler();

 }

}

static void MX_I2C1_Init(void)

{

 /* USER CODE BEGIN I2C1_Init 0 */

 /* USER CODE END I2C1_Init 0 */

 /* USER CODE BEGIN I2C1_Init 1 */

 /* USER CODE END I2C1_Init 1 */

 hi2c1.Instance = I2C1;

 hi2c1.Init.ClockSpeed = 100000;

 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;

 hi2c1.Init.OwnAddress1 = 0;

 hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;

 hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;

 hi2c1.Init.OwnAddress2 = 0;

 hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;

 hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

 if (HAL_I2C_Init(&hi2c1) != HAL_OK)

 {

   Error_Handler();

 }

 /* USER CODE BEGIN I2C1_Init 2 */

 /* USER CODE END I2C1_Init 2 */

}

static void MX_GPIO_Init(void)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 /* GPIO Ports Clock Enable */

 __HAL_RCC_GPIOC_CLK_ENABLE();

 __HAL_RCC_GPIOD_CLK_ENABLE();

 __HAL_RCC_GPIOA_CLK_ENABLE();

 __HAL_RCC_GPIOB_CLK_ENABLE();

 /*Configure GPIO pin Output Level */

 HAL_GPIO_WritePin(LED_BUILTIN_GPIO_Port, LED_BUILTIN_Pin, GPIO_PIN_SET);

 /*Configure GPIO pin : LED_BUILTIN_Pin */

 GPIO_InitStruct.Pin = LED_BUILTIN_Pin;

 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

 GPIO_InitStruct.Pull = GPIO_NOPULL;

 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

 HAL_GPIO_Init(LED_BUILTIN_GPIO_Port, &GPIO_InitStruct);

}

void Error_Handler(void)

{

 /* USER CODE BEGIN Error_Handler_Debug */

 /* User can add his own implementation to report the HAL error return state */

 /* USER CODE END Error_Handler_Debug */

}

#ifdef USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line)

{

 /* USER CODE BEGIN 6 */

 /* User can add his own implementation to report the file name and line number,

    tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

 /* USER CODE END 6 */

}

#endif /* USE_FULL_ASSERT */

The idea of the code in the while(1) loop is simply to scan for determining what is the address of the AM2320 (which is, according to the datasheet, 0xB8). This is a code that I found thanks to this video: https://www.youtube.com/watch?v=1COFk1M2tak

Unfortunately, it never stops at the line of code where I ask the uC to turn the on-board LED on (where I also put a breakpoint). Digging further while debugging, I noticed that it always hangs at this specific part (also happens when I tried the Master_Transmit command) before timing out.

0693W000003Q1FVQA0.png

Electrically, I have my pins connected correctly, SCL1 and SDA1 pulled up to 3,3V (and the AM2320 is powered with 3,3V) with 4,7k resistors. I wonder if this could be a HAL library issue or in the worst case, my uC that has been damaged.

For good measure, I tested the AM2320 with an Arduino Uno and I can read it with no problem, so the sensor is fine. Now I tried using the Arduino IDE to code the Blue Pill and did minor modifications (Mostly for serial port. I2C library commands are the same) and I got about the same results as with STM32CubeIDE: no response from the AM2320.

I know this issue has been reported all over the forums, but I wonder if these could be good hints for the issue.

[Edit 2020-08-24] I tried with the other I2C perihperal (I2C2) and communication still seems to not work. I am wondering if I'm better off directly using the internal registers to be able to communicate with the sensor.

5 REPLIES 5
FGerm.1
Associate II

[Update] I wrote some basic functions where I use the i2c registers, thanks to the following YouTube video https://www.youtube.com/watch?v=ZVeG25cMe58 and shuffling through the datasheet:

/*

 * i2c_functions.h

 *

 * Created on: Aug 24, 2020

 *     Author: FredG

 */

#ifndef I2C_FUNCTIONS_H_

#define I2C_FUNCTIONS_H_

void i2c1_read(uint8_t device_addr, uint8_t *data, uint8_t data_size)

{

 uint32_t temp; //temporary variable to allow clearing bits in I2C_SR2 register

 uint8_t i = 0; //loop index for data reception

 uint8_t *byte_to_read;

 I2C1->CR1 |= I2C_CR1_ACK; //enable acknowledgement after each byte received

 I2C1->CR1 |= I2C_CR1_START; //generate a start condition

 while(!(I2C1->SR1 & I2C_SR1_SB)); //make sure that the start condition has been generated

 //after being read, the bit will be reset

 I2C1->DR = device_addr + 1;

 while(!(I2C1->SR1 & I2C_SR1_ADDR)); //make sure that the address has been received by the slave

 temp = I2C1->SR2; //read from SR2 to clear some bits

 byte_to_read = data;

 for(; i < (data_size - 3); i++) //loop to read incoming data and put it in buffer

 {

  while(!(I2C1->SR1 & I2C_SR1_RXNE)); //wait for the DR register to be full before copying into buffer

  *byte_to_read = I2C1->DR;

  byte_to_read++; //increment pointer to next array case

 }

 //before reading the 2nd byte before the last byte, clear ACK bit

 I2C1->CR1 &= ~(I2C_CR1_ACK);

 *byte_to_read = I2C1->DR; //read 2nd byte before last

 I2C1->CR1 |= I2C_CR1_STOP; //write 1 to STOP bit in CR1 register

 byte_to_read++; //move on to the byte before the last one

 *byte_to_read = I2C1->DR; //read the byte before the last one

 while(!(I2C1->SR1 & I2C_SR1_RXNE)); //check if DR is not empty

 byte_to_read++; //move on to the last byte of data

 *byte_to_read = I2C1->DR; //read the last byte of data

}

void i2c1_write(uint8_t device_addr, uint8_t *data, uint8_t data_size)

{

 uint32_t temp;

 uint8_t *byte_to_send;

 I2C1->CR1 |= I2C_CR1_START; //generate a start condition

 while(!(I2C1->SR1 & I2C_SR1_SB)); //make sure that the start condition has been generated

 //after being read, the bit will be reset

 I2C1->DR = device_addr;

 while(!(I2C1->SR1 & I2C_SR1_ADDR)); //make sure that the address has been received by the slave

 temp = I2C1->SR2;

 byte_to_send = data;

 for(int i = 0; i < data_size; i++) //loop through the buffer to send the contents

 {

  I2C1->DR = *byte_to_send; //send the data in the current buffer byte

  while(!(I2C1->SR1 & I2C_SR1_TXE)); //wait for the transfer flag to be set before continuing

  byte_to_send++;

 }

 I2C1->CR1 |= I2C_CR1_STOP; //stop the transfer

}

void i2c1_init()

{

}

#endif /* I2C_FUNCTIONS_H_ */

it still failed to transfer anything but it hung up at the line

while(!(I2C1->SR1 & I2C_SR1_SB));

This leads me to believe there might be a problem during the initialization, which I haven't tried doing using low-level code. This will be my next stop.

FGerm.1
Associate II

[Update #2] I wrote a function using low-level code (again thanks to the youtube video that helped me with the Rx/Tx functions using low level code). The function looks like this:

/*

 * i2c_config.h

 *

 * Created on: Aug 25, 2020

 *   Author: FredG

 */

#ifndef I2C_CONFIG_H_

#define I2C_CONFIG_H_

void i2c1_configuration()

{

RCC->APB1ENR &= ~(RCC_APB1ENR_I2C1EN); //disable the APB1 clock for the time of configuration

I2C1->CR1 = 0x00;

I2C1->CR2 = 0x00;

RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

I2C1->CR1 |= 36; //set APB1 clock to 36 MHz

I2C1->CCR |= 180; //enter 180 in the CCr register in order to obtain a 100kHz frequency

I2C1->TRISE = 37;

I2C1->CR1 |= I2C_CR1_ACK;

GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_MODE6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE7;

I2C1->CR1 |= I2C_CR1_PE;

}

#endif /* I2C_CONFIG_H_ */

The function was then called right after the MX_Init functions.

But I used the HAL commands for transmitting and receiving and so far, still nothing (Maybe I should use both low-level codes for next try?).

For good measure, I tried to repeat the experiment with another Blue Pill board that I bought just to make sure it wasn't hardware damage, and got the exact same results.

[Edit] It seems like when writing a 1 to the START bit in the CR1 register, it remains at 1 after exiting an I2C function. According to the datasheet, this bit should be automatically reset after a start condition has been generated, so it seems like it fails to generate the start condition.

Blue pills are notorious for being populated by counterfeit (non-ST, or pullout) chips. They are cheap for a reason. This said, I don't think that's the root cause of your problem, but you may want to get a known genuine STM32 to take out one unknown from the equation. And maybe choose some different model than the "ancient" 'F1.

Observe the pins using oscilloscope/logic analyzer. Try without the target first and make sure there is no other connection on board to given pins. Read out and check/post content of the I2C and relevant GPIO/AFIO registers. Make sure they map to the required pins. Make sure they are set as open-drain and check the pullup resistors (what value do you use?). Read the errata (if you are sure you have a genuine ST chip).

JW

Thanks for the answer, warning about Blue Pills and tip on what next to eliminate.

I have a few Nucleo boards, and I repeated the experiment with the Nucleo-G431RB (I could have also tried with a F446RE). I got pretty much the same error as before, which is that it returns a HAL_ERROR state. I did not dissecate the code during debugging to see whether it's the start condition that's not coming up or if it was anything else, but I have a little overview here:

0693W000002lGt9QAE.png

The Blue Pill boards I obtained are, at least in my opinion, from some good sources. I didn't buy them from eBay or Aliexpress, and as far as my tests went, I never had any problems with UART, USB or analog peripherals.

I used 4,7kOhm resistors as external pullups for the SDA and SCL lines

I don't know if this can be a clue but I often heard that breadboards cause electrical noise and can be mitigated by sticking a metal plate on the sticking surface underneath. Like I said earlier, I don't have either an oscilloscope or a logic probe to verify that, but I'll explore this solution.

Or the last thing I can think of is that the AM2320 doesn't like being with an STM32 microcontroller but doesn't bother being with an Arduino (although in my books, I don't really understand why it wouldn't work with it since I often see it used with a raspberry pi or an esp8266). This being said, I'll do some tests with different devices and see if they cause me problems as well or work just fine.

[Update #3] I changed the pullup resistors to a different value, this time 10k. I get past the start condition generation, but then I hang up on the address acknowledgement. The resistors labelled as 4,7k were actually a faulty batch that were under 100 ohms. not sure how it survived.

[Update #4] I am actually capable of reading from the sensor and I get the correct values. I would still like to thank you for your time trying to help me out.

Jpell.1
Associate

Buenos días , estoy trabajando en un equipo para medir temperatura y humedad, y elegí el sensor am2320.

Mi problema es que noto mucha disparidad de medición de humedad entre varios sensores, por ejemplo

Arme 10 equipos y no miden todo la misma humedad una diferencia de hasta un 15% todos los sensores juntos

En el mismo ambiente.

Habrá alguna forma de calibración de dicha sonda?

Agradecería una pronta respuesta .

Atentamente Javier

Good morning, I am working on a equipment to measure temperature and humidity, and I chose the am2320 sensor.

My problem is that I notice a lot of humidity measurement disparity between various sensors for example

Assemble 10 devices and they do not measure all the same humidity a difference of up to 15% all the sensors together

In the same environment.

Will there be any way to calibrate this probe?

I would appreciate a prompt reply.

Sincerely Javier