Overview
In this blog we will be discussing another special functionality of GPIO pins I2C or Inter-Integrated Circuit or I2c functionality . I2C is a two wire interface or TWI which was developed by the Philips corporation for use in consumer products . It is a bidirectional bus that can be easily implemented in an IC process.I2C combines best features of SPI as well as UART it lets the user control multiple slaves via multiple masters which is useful when logging data to sd cards or displaying on LCD. Just like SPI the output data bits are synchronized to sampling of the clock shared by both the parties involved in the communication . The master always generates the clock.The protocol finds applications in hardware sensors or displays , reading memory ICs , communicating with microcontrollers , ADC or DACs.
I2C Theory
I2C consists of 2 lines SCL and SDA
1.SCL (Serial Clock ) – For synchronizing data transfer between master and slave
2, SDA (Serial Data ) – The data transmission or receiving line
Multiple Master and Slave lines are connected to the SDA and SCL lines and both these lines are pulled up using resistors to Vdd (5v)
Operation Modes In I2C
- Master Transmitter
- Master Receiver
- Slave Transmitter
- Slave Receiver
I2C Clock Speed
The speed of the I2C bus should be in reference to the one provided in the datasheet
The modes of I2C clock speed are as follows:-
- Standard-mode : 100 KHZ max
- Fast-mode: 400 KHz max
- Fast-mode Plus:1MHz
- High-speed mode: 3.4 MHz
I2C duty cycle specifies the ratio between Tlow and Thigh of the I2C SCL line
The values being:
I2C_DUTYCYCLE_2=2:1
I2C_DUTYCYCLE_16_9= 16:9
The desired clock speed can be achieved using the appropriate duty cycle to prescale
How data is transmitted in I2C Protocol
- Transactions are initiated and completed by master
- All the messages have an address frame and data frame
- Data is placed on SDA when SCL goes low and is sampled after SCL goes HIGH
- All transactions begin with START and are terminated by STOP
- A START is defined when SDA goes low from high and SCL is still HIGH
- A STOP is define when SDA goes HIGH from LOW while SCL is still HIGH
- Both START and STOP conditions are generated by the master itself
- Both START and STOP conditions are generated by the master itself
- Any information on the SDA lines should be 8 Bits long.
- Each byte must be followed by Acknowledge(ACK) bit.
- Data is transferred with the MSB first
- The address frame is sent out first
- The 7 bit address frame is sent out with the MSB first followed by R/W indicating a read(1) or write(0) operation.
- The data frame begins transmission after the address frame is sent
- The SCL will keep on generating clock pulses at regular interval and data will be placed at SDA at regular interval by either master or slave depending it is a write operation or read operation
I2C Features in STM32F103
I2C Instances in STM32F103
The I2C instances vary from microcontroller to microcontroller i.e the stm32f103c676A has one I2c instance with stm32 f411 RE having I2C1 and I2C2 . The pins for I2C1 are
- PB7 – This is used as SDA
- PB6 – This is used as SCL
The pins for I2C2 are:-
- PB3 – This is used as SDA
- PB10 – This is used as SCL
I2C configuration Parameters in STM32F103
This parameter defines the clock speed which as mentioned previously can be 100000 in standard mode and 400000 fast mode.
This parameter helps in configuring the HIGH and LOW ratio of the clock and has value 2:1 and 16:9
This parameter takes in the address of the first device which can be 7 bit or 10 bit long
This parameter suggests the type of the address size being chosen which can be 7 bit or 10 bit
This parameter is used to disable or enable the dual addressing mode of I2C
This parameter is used to disable or enable the general call addressing mode.
This parameters checks the nostrech mode
Applications of I2C
- It is used to scan sensors such as - MPU6050 , BMP280 , PCA 9685 PWM controller, TSL2561 luminosity measurements
- ADXL345 3 axis accelerometer
- Ssoled display , 16 x 2 led display
- CAT24C512 EEPROM 64KB
How to configure the I2C peripheral in STM32F103
We would be using STM32 HAL and STM32CubeIDE for using the I2C peripheral in STM32F103 in this blog tutorial series.
CONFIGURATION IN STM32CUBEIDE
FIG 1- Selecting the SCL and SDA pin
FIG 2 – Configuring the i2c parameters
- Initialize the GPIO init type def structure and enable the clock for both port for which alternate mapping is to be done in this case that is PORT B and enable the clock for I2C peripheral
- Initialize the peripheral using the init API
- Configure the GPIO parameters such as:-
- PINS: It includes the pins that is to be set for I2C
- MODE: This suggests that alternate mode is being the GPIO pin
- PULL UP : This suggests if the GPIO pin is being pulled up by default
- ALTERNATE: Selects the alternate functionality of the register in our case this will be AF4
- Next the select how the device needs to be operated as either Master or Slave
- Configure the Features of Master
- Speed Mode: This helps configuring the speed at which the I2C peripheral is to be operated - Fast Mode or Standard Mode
- Clock Speed : Selecting the speed mode selects the clock speed of the peripheral which 100000 in case of standard mode and 400000 in case of Fast Mode
- Duty Cycle : The duty cycle selects the HIGH or LOW duration of clock which can have values 16/9 or 2/1
- Own address : This is used to specify the address of the first device that can have the address 7 bit or 10 bit
- Addressing mode : This is used to specify in which mode the address is being used 7 bit or 10 bit
- Dualaddressing mode: This is used to specify if the dual addressing mode is enabled or disabled
I2C data handling API types
In polling mode the CPU’s normal operation is stopped or blocked until the data is transmitted or received . The microcontroller has to be turned on for the whole operation
For eg – HAL_StatusTypeDef HAL_I2C_Master_Transmit (I2C_HandleTypeDef * hi2c,uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout)
In polling mode the CPU’s normal operation is stopped or blocked until the data is transmitted or received . The microcontroller has to be turned on for the whole operation
For eg – HAL_StatusTypeDef HAL_I2C_Master_Transmit (I2C_HandleTypeDef * hi2c,uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout)
The DMA mode is the most efficient way or data transmission in which the data is received in the preprogrammed memory location . Hence in this mode there is no intervention from the CPU and once the data transmission is done the CPU notifies
For eg – HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA (I2C_HandleTypeDef * hi2c,uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout)
SDK files
- Stm32f1xx_hal.c
- Stm32f1xx_hal_i2c.c
- Stm32f1xx_hal_i2c.h
The hal.c contains all the macros , function declarations , alternate function mapping of GPIO peripheral and Clock Configurations .The i2c.c file contains initialization and declaration of various low level registers , macros , static void functions , callback functions , configuration of various parameters of both master as well as slave . The i2c.h file contains all the enums , structure parameters such as clock speed , ownaddress declarations etc.It also contains the declaration of macros of the values that will be filled by the structure parameters such as duty cycle value .
HAL APIs Involved
- HAL_StatusTypeDef HAL_I2C_Init (I2C_HandleTypeDef * hi2c)
- void HAL_I2C_MspInit (I2C_HandleTypeDef * hi2c)
- 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)
- HAL_StatusTypeDef HAL_I2C_Slave_Transmit (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout)
- HAL_StatusTypeDef HAL_I2C_Slave_Receive (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout)
- HAL_StatusTypeDef HAL_I2C_Mem_Write (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout)
- .HAL_StatusTypeDef HAL_I2C_Mem_Read (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint1 6_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Init (I2C_HandleTypeDef * hi2c)
FUNCTION DESCRIPTION
Initializes the I2C according to the specified parameters in the I2C_InitTypeDef and initialize the associated handle.
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C
RETURN VALUES
- HAL: status
HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c)
{
uint32_t freqrange;
uint32_t pclk1;
/* Check the I2C handle allocation */
if (hi2c == NULL)
{
return HAL_ERROR;
}
FUNCTION NAME
void HAL_I2C_MspInit (I2C_HandleTypeDef * hi2c)
FUNCTION DESCRIPTION
Initialize the I2C MSP.
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C
RETURN VALUES
- HAL: status
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Master_Transmit (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Transmits in master mode an amount of data in blocking mode according to the values in the parameters
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- DevAddress: Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
RETURN VALUES
- HAL: status
while (1)
{
ret = HAL_I2C_Master_Transmit(&hi2c1, (4 << 1),(uint8_t *)dataBuffer, 11, 100);
;
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Master_Receive (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Receives in master mode an amount of data in blocking mode according to the values in the parameters
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- DevAddress: Target device address: The device 7 bits address value in datasheet must be shifted to the left before calling the interface
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
RETURN VALUES
- HAL: status
while (1)
{
ret = HAL_I2C_Master_Receive(&hi2c1, (6 << 1),(uint8_t *)dataBuffer2, 11, 100);
if ( ret != HAL_OK ) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Slave_Transmit (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Transmits in slave mode an amount of data in blocking mode according to the values in the parameters
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
RETURN VALUES
- HAL: status
while (1)
{
HAL_I2C_Slave_Transmit(&hi2c1,(uint8_t *)dataBuffer3, 16, 80);
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Slave_Receive (I2C_HandleTypeDef * hi2c, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Receive in slave mode an amount of data in blocking mode according to the values in the parameters
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
RETURN VALUES
- HAL: status
while (1)
{
HAL_I2C_Slave_Receive(&hi2c1,(uint8_t *)dataBufferrr, 10, 100);
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Mem_Write (I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Write an amount of data in blocking mode to a specific memory address.
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
- MemAddress:The address of the memory that has to be written
- MemAddSize: The size of memory address
- DevAddress:The device 7 bits address value in datasheet must be shifted to the left before calling the interface
RETURN VALUES
- HAL: status
FUNCTION NAME
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef * hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t * pData, uint16_t Size, uint32_t Timeout)
FUNCTION DESCRIPTION
Read an amount of data in blocking mode to a specific memory address.
PARAMETERS
- hi2c: Pointer to a I2C_HandleTypeDef structure that contains the configuration information for the specified I2C.
- pData: Pointer to data buffer
- Size: Amount of data to be sent
- Timeout: Timeout duration
- MemAddress:The address of the memory that has to be read
- MemAddSize: The size of memory address
- DevAddress:The device 7 bits address value in datasheet must be shifted to the left before calling the interface
RETURN VALUES
- HAL: status
FUNCTION NAME
static void MX_I2C1_Init(void)
FUNCTION DESCRIPTION
This function is used to intialize the I2C1 along with the parameters
PARAMETERS
NONE
RETURN VALUES
NONE
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 = 400000;
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;
CODE
Sending code to arduino using I2C via STM32
#include "main.h"
/* Private includes ----------------------------------------------------------*/
int ret;
char dataBuffer[] = "Hello world!";
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
ret = HAL_I2C_Master_Transmit(&hi2c1, (4 << 1),(uint8_t *)dataBuffer, 11, 100);
if ( ret != HAL_OK ) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, 1);
}
if ( ret == HAL_OK ) {
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
HAL_Delay(200);
}
HAL_Delay(1000); }
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
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_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
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_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
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 = 400000;
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 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
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,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
Author: Kunal Gupta
Author