STM32 IIC双机通信—— HAL库硬件IIC版

1,stm32的I2C总线既可以作为主模式也可以作为从模式

作为主模式和作为从模式在CUBE中的配置是一样的,只是i2c设备的地址不一样

 

 

Timing是时序,是在配置的时候生成的,不需要修改

当总线的频率设置为100KHZ的时候,rise time 和fall time 要设置为100ns

ownAddress就是指的i2c总线的设备地址,该地址可以选择7bit 或者10bit

2,测试的时候使用两块开发板,一块作为master另一块作为slave,主机向从机发送"I2C",当从机收到之后然后发给主机,主机收到之后才能向下运行,然后循环打印收到的内容。

 

主机代码如下:

/**
******************************************************************************
* File Name : main.c
* Description : Main program body
******************************************************************************
*
* COPYRIGHT(c) 2017 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f0xx_hal.h"
//#include "stm32f1xx_hal_usart.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
uint8_t buffer_receive[20] = {0};
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();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
while(HAL_I2C_Master_Transmit(&hi2c1, 0x0B, (uint8_t *)&"I2C", 3, 100) != HAL_OK)
{
}
while(HAL_I2C_Master_Receive(&hi2c1, 0x0B, &buffer_receive[0], 3, 100) != HAL_OK)
{
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
if(buffer_receive[0] == 'I' && buffer_receive[1] == '2' && buffer_receive[2] == 'C')
{
HAL_UART_Transmit(&huart1, buffer_receive, 3, 100);
HAL_UART_Transmit(&huart1, (uint8_t *)&"\n", 1, 100);
}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* I2C1 init function */
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00201D2B;
hi2c1.Init.OwnAddress1 = 0x0A;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0xFF;
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 pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler */
}
#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
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
从机代码如下:
/**
******************************************************************************
* File Name : main.c
* Description : Main program body
******************************************************************************
*
* COPYRIGHT(c) 2017 STMicroelectronics
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f0xx_hal.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
static uint8_t buffer[20] = {0};
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();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
while(HAL_I2C_Slave_Receive(&hi2c1, buffer, 3, 100) != HAL_OK)
{
}
while(HAL_I2C_Slave_Transmit(&hi2c1, &buffer[0], 3, 100) != HAL_OK)
{
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* I2C1 init function */
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00201D2B;
hi2c1.Init.OwnAddress1 = 0x0B;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0xFF;
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 pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler */
}
#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
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


 


STM32 IIC双机通信—— HAL库硬件IIC版

参考传送门

  关于IIC的原理这里我就不多说了,网上有很多很好的解析,如果要看我个人对IIC的理解的话,可以点击查看,这里主要讲一下怎样利用STM32CubeMx实现IIC的通讯,经过个人实践,感觉HAL库的硬件IIC要比标准库的稳定。好了,下面就从STM32CubeMx 配置开始一步步实现IIC通讯。

  STM32CubeMx的配置,这里关于新建工程的步骤我就不细说了,如果还不会操作STM32CubeMx 的可以点击查看, 这里主要对IIC的配置进行说明。

  

  了解IIC的都知道,IIC通信有主从机之分,用两片STM32进行IIC通信当然也不例外,不过使用STM32CubeMx 配置有一个好处,就是不用分别配置主从机,在STM32CubeMx 配置里面,主从机的配置是一样,唯一不同的就是IIC的地址如上图,这个地址很重要,只要配置好了,基本就成功了

  还有一个要注意的,就是IIC的SDA、SCK引脚要配置成NPP模式,不然容易出现信号线忙,检测不到从机的情况。

  

  配置配好后我们生成代码,就可以进行通信了,主从机核心代码如下:

 

  下面是主机的重要代码:

/* I2C2 init function  IIC配置*/
static void MX_I2C2_Init(void)
{

  hi2c2.Instance = I2C2;
  hi2c2.Init.Timing = 0x10805D88;
  hi2c2.Init.OwnAddress1 = 20;        //用户自己配置的地址
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;        
  hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Analogue filter 
    */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Digital filter 
    */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}


while(HAL_I2C_Master_Transmit_IT(&hi2c2 ,0x0b,&BUFF[0], 1)!= HAL_OK){}   
//IIC主机发送函数,主要IIC配置好了,这个可以添加到main函数里面测试

 

  关于STM32CubeMx的HAL库IIC收发有几种函数,用户可以根据自己不同的需求进行选择,以下就是主要的几个HAL库IIC收发函数:

/* 第1个参数为I2C操作句柄
   第2个参数为从机设备地址
   第3个参数为从机寄存器地址
   第4个参数为从机寄存器地址长度
   第5个参数为发送的数据的起始地址
   第6个参数为传输数据的大小
   第7个参数为操作超时时间   */
HAL_I2C_Mem_Write(&hi2c2,salve_add,0,0,PA_BUFF,sizeof(PA_BUFF),0x10);

HAL_I2C_Mem_Write_IT();

HAL_I2C_Mem_Read();

HAL_I2C_Mem_Read_IT();

HAL_I2C_Mem_Read_DMA();

HAL_I2C_Mem_Write_DMA();


/*    不需要用到寄存器地址的主机HAL库IIC收发函数   */
HAL_I2C_Master_Receive();     //STM32 主机接收,不需要用到寄存器地址
HAL_I2C_Master_Transmit();

HAL_I2C_Master_Receive_IT();   //中断IIC接收 
HAL_I2C_Master_Receive_DMA();  //DMA 方式的IIC接收  

HAL_I2C_Master_Transmit_IT();   //中断IIC发送  

HAL_I2C_Master_Transmit_DMA();   //DMA 方式的IIC发送  

HAL_I2C_Master_Transmit(&hi2c2,0x0B,PA_BUFF,sizeof(PA_BUFF),0x10); //STM32 主机发送 

/* 不需要用到寄存器地址的从机HAL库IIC收发函数   */ 
HAL_I2C_Slave_Receive();    //STM32 从机机接收,不需要用到寄存器地址 
 
HAL_I2C_Slave_Transmit();   //STM32 从机机发送,不需要用到寄存器地址  

HAL_I2C_Slave_Receive_IT(); 

HAL_I2C_Slave_Receive_DMA(); 

HAL_I2C_Slave_Transmit_IT();

HAL_I2C_Slave_Transmit_DMA();

 

  我这里因为只是做两个STM32间的单向通行而已,不需要对寄存器进行写数据。

  所以主机发送函数选择了 HAL_I2C_Master_Transmit( ); 函数,而我从机则选择HAL_I2C_Slave_Receive( );函数,从机代码如下:

/*   I2C2 init function 从机IIC初始化配置   */
static void MX_I2C2_Init(void)
{

  hi2c2.Instance = I2C2;
  hi2c2.Init.Timing = 0x10805D88;
  hi2c2.Init.OwnAddress1 = 0x0A;
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;
  hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Analogue filter 
    */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Digital filter 
    */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

while(HAL_I2C_Slave_Receive(&hi2c2, RE_BUFF, 1, 100)!= HAL_OK) {} 
// 在配置好IIC后,可直接把该函数放到main函数测试,第一个参数是IIC通道选择,第二个参数是接收缓存,第三个数据的是接收长度,第四个参数是超时时间

 

  经过测试,发现如果发送数据过多,用硬件I2C收发的话,使用中断会比较稳定

  作为从机,要与主机完成通信,有一个特别要注意的事情,就是IIC配置的地址要与主机发送的地址一致,否则无法完成应答。我一开始就是直接发送自己在软件配置的IIC地址,可是没有通信成功,检查才发现,软件配置好IIC后,生成代码时,地址会最后一位补0,自动补够8位;而我从机发送出来的地址,也会把你发送的地址最后一位改为0再发送,这个我查了一下函数地层,好像是因为这是是发送函数,所以直接帮你把R/W位改为0了。所以导致我一开始怎么都调不通,使用逻辑分析仪后,分析采集到的数据才发现这个问题,于是我,直接手动改从机的IIC地址,改成逻辑分析仪发出来的地址一样,这样就通了。

 

  通信结果如图所示:

  

 

补充2点要注意的:

  1. IIC硬件连接的时候SDA跟SCL总线注意都要上拉一个4.7K的电阻,否则IIC无法工作;

  2. 使用主从收发函数时,发送的地址要注意。如果是发送函数,发送出的地址最后一位 R/W 位如果不为0,则HAL库函数会地址把最后一位修改为0,再把地址发送出去,但是如果地址最后一位 R/W 位为0,函数就不会对地址进行修改,直接把地址发送出去;如果是接收函数,发送出去的地址最后一位 R/W位 不为1,则HAL库函数会把地址最后一位修改为1,再把地址发送出去,但是如果地址最后一位 R/W 位为1,函数就不会对地址进行修改,直接把地址发送出去.。所以从机地址设置,一定要与主机发出的地址一致才能正确应答。

  例如:

  (1) HAL_I2C_Master_Receive(&hi2c2,0x0a,(uint8_t *)test,sizeof(test),1000); 因为这个是读出数据函数,这里发送的地址位第8位地址位 R/W 位应该为 1,发送地址 0x0a 硬件会把最后一位改为0,把地址变成0xb 再把 0x0b这个地址发送出去 ;如果地址最后一位R/W是 1,发送地址是 0x0b ,因为最后一位为 1,那么该函数发送的地址就不会对发送的地址最后一位R/W为做出修改,直接把 0x0b 发送出去,发出去的地址依然为0x0b。

  (2) HAL_I2C_Master_Transmit(&hi2c2,0x0f,(uint8_t *)test,sizeof(test),1000); 因为这个是写入数据函数,R/W 位为 0,发送地址 0x0f 硬件会把最后一位改为 0 变成 0xe,如果发送的地址是 0xa ,则不会对地址进行修改,直接把地址 0x0a 发送出去。

  

  现在我以F40X系列的STM32 的 IIC 读函数为例子来翻一下函数地层分析造成这个问题的原因:

  

  来到地层我们找到发送地址的函数;

  

  

  

  上面跳转到这里我们就接近真相了,上图1是跳转下去的 IIC 写函数的地层,2 是 IIC 跳转下去的读函数的地层,这里 ADDRESS 就是用户写进IIC发送读取函数里的寻址地址了,接下来到重点了,对于I2C_OAR1_ADD0 我们再次跳转分析下去看看;

  

  从这里接收函数我们可以看到,I2C_OAR1_ADD0 最终的值应该为 0x00000001,我们依旧以地址 0x 0b 为例: 0x 0b = 0000 1011 , 由或运算的定义我们可知,数据再做或运算 (|)时, 参加运算的两个对象只要有一个为1,其值为1 , 所以 (00000001) | (0000 1011)= 0000 1011= 0x0b 。这时如果地址为 0x0xa(0000 1010),则会出现地址位最后一位R/W位被改为1的情况,运算过程为(00000001) | (0000 1010)= 0000 1011 = 0x0b ,结合这个地层运算,就不难解析为什么使用HAL库的 IIC 接收函数,发送出去的地址会出现变化的情况了。

  至于发送函数同理,I2C_OAR1_ADD0 最终的值应该为 0x00000001取反后就变成了 0x 1111 1110 ,由与运算(&)的定义可知两位同时为“1”,结果才为“1”,否则为0。所以这里我们依旧以地址0x0b = 0000 1011 为例,(1111 1110) &(0000 1011)= 0000 1010 = 0x0b,所以发送函数把地址最后一位的R/W位改为了 0 ,而当地址为 0x0a = 0000 1010 时,我们再进行运算一下可得(1111 1110) &(0000 1010)= 0000 1010  = 0x0a , 地址没有改变,所以这也就解析了上面我说的地址变化的问题了。

 

  • 12
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值