STM32: I2C with HAL what am I doing wrong? - Page 1
Forum section: electronics / general

author: Watth
author_rank: Frequent Contributor
timestamp: January 26, 2022, 06:00:33 pm
content: 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:/* 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;// data arrayint8_t dataLength;// size that has to be transmitteduint32_t tickMem;// last recorded tickuint16_t delay = 200;// time delay (ms)HAL_I2C_StateTypeDef stateI2C_1;// state of the I2C busHAL_I2C_StateTypeDef stateI2C_2;// state of the I2C bus tooHAL_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 drivesdata = 0x0A;data = 0x0;data = 0x0;data = 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 = 0x07;// display test mode command addressdata = 0x00;// display test mode disabled (normal mode)dataLength = 2;stateI2C_1 = HAL_I2C_GetState(&hi2c1);// state before transmitretI2C = HAL_I2C_Master_Transmit(&hi2c1, devAddW, data, dataLength, 100);stateI2C_2 = HAL_I2C_GetState(&hi2c1);// state after transmitHAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);// blink LED to show at least *something* is happening.tickMem = HAL_GetTick();while (HAL_GetTick() < tickMem + delay){}data = 0x07;// display test mode command addressdata = 0xFF;// display test mode enabled : should light LEDs at half currentstateI2C_1 = HAL_I2C_GetState(&hi2c1);// state before transmitretI2C = HAL_I2C_Master_Transmit(&hi2c1, devAddW, data, dataLength, 100);stateI2C_2 = HAL_I2C_GetState(&hi2c1);// state after transmitHAL_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: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?

author: thm_w
author_rank: Super Contributor
timestamp: January 26, 2022, 10:52:28 pm
content: Quotehttps://datasheets.maximintegrated.com/en/ds/MAX6956.pdfQuoteHow 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)

author: Robotec
author_rank: Contributor
timestamp: January 27, 2022, 08:06:02 am
content: 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.

author: Watth
author_rank: Frequent Contributor
timestamp: January 27, 2022, 10:43:24 am
content: Quote from: thm_w on January 26, 2022, 10:52:28 pm

author: Watth
author_rank: Frequent Contributor
timestamp: January 27, 2022, 10:45:30 am
content: Quote from: Robotec on January 27, 2022, 08:06:02 amI didn't find anything except init functions (that MX added automatically)QuoteI did, and there's nothing there. Which isn't surprising as I I checked with the debug that the transmit functions always return errors :/

author: Robotec
author_rank: Contributor
timestamp: January 27, 2022, 11:32:08 am
content: 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.

author: Watth
author_rank: Frequent Contributor
timestamp: January 27, 2022, 11:44:42 am
content: 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 27, 2022, 06:23:38 pm
content: Quote from: Watth on January 27, 2022, 10:43:24 amNo, 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:* @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 statusHAL_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 thenHAL_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:* @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 statusHAL_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.

author: Ground_Loop
author_rank: Frequent Contributor
timestamp: January 28, 2022, 02:51:52 am
content: Are you using pull up resistors?

author: Watth
author_rank: Frequent Contributor
timestamp: January 28, 2022, 10:21:30 am
content: Oh for frog's snacks it was the pull-up resistors.Iknewit 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'sPCA9535PWand its datasheet also says it needs pull-up resistors on clock and data. RTFDS!

author: Watth
author_rank: Frequent Contributor
timestamp: January 28, 2022, 10:24:20 am
content: Quote from: DavidAlfa on January 27, 2022, 06:23:38 pmNow 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.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 28, 2022, 05:42:55 pm
content: 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

author: Watth
author_rank: Frequent Contributor
timestamp: January 29, 2022, 11:17:35 am
content: Quote from: DavidAlfa on January 28, 2022, 05:42:55 pmOh 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 andthis application noteto control a pin individually as a LED driver and assign it a current fraction.