STM32硬件IIC 主从(一、CubeMx快速实现)

目的

通过HAL库实现STM32F103C8T6两块板之间的IIC主从相互通信,串口打印接收数据。

环境搭建

/**I2C1 GPIO Configuration    
PB6     ------> I2C1_SCL
PB7     ------> I2C1_SDA 
注意通信线记得接上拉电阻4.7k。

STM32硬件IIC 主从(一、CubeMx快速实现)_第1张图片
STM32硬件IIC 主从(一、CubeMx快速实现)_第2张图片

CubeMx配置

配置sys 串行调试
STM32硬件IIC 主从(一、CubeMx快速实现)_第3张图片
配置外部高速时钟
STM32硬件IIC 主从(一、CubeMx快速实现)_第4张图片
配置串口,默认值
STM32硬件IIC 主从(一、CubeMx快速实现)_第5张图片
配置IIC,默认值,从机地址0x01,左移1位=0x02
STM32硬件IIC 主从(一、CubeMx快速实现)_第6张图片
开启IIC的事件中断与错误中断
STM32硬件IIC 主从(一、CubeMx快速实现)_第7张图片

代码

1、实现串口通信发送:
usart.h添加

#include "stdio.h"

usart.c添加:实现打印函数

int fputc(int ch,FILE *f)
{
	uint8_t temp[1] = {ch};
	HAL_UART_Transmit(&huart1,temp,1,2);
	return(ch);
}

2、重点
main.c

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "My_i2c.h"
/* 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 ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(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();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	HAL_Delay(3000);//一定要有,双机上电需要顺序,从机先,主机后
	printf("system run\r\n");
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
#ifdef _User_i2c
	i2c_CycleRun();//i2c实现
#endif
  }
  /* USER CODE END 3 */
}

My_i2c.c

/**************************************************************
*file:i2c实现双机通信
*date:2021.6.2
*author:残梦
*version:V1.0
*note:
**************************************************************/
#include "My_i2c.h"
#include "i2c.h"
#include "usart.h"

#ifdef _User_i2c

typedef struct
{
	uint8_t Flag_Tx;		//数据发送成功
	uint8_t Flag_Rx;		//接收到数据
	uint8_t Flag_Error;		//通信错误
	uint32_t ErrorCode;		//通信错误代码
	uint8_t RxData[3];		//接收数据区域
	uint8_t TxData[3];		//发送数据区域
}i2c_StructDef;

static i2c_StructDef i2c = 
{
	.Flag_Tx = 0,
	.Flag_Rx = 0,
	.Flag_Error = 0,
	. ErrorCode = 0,
	.RxData[0] = 0,
	.TxData[0] = 0,
	.RxData[1] = 0,
	.TxData[1] = 0,	
	.RxData[2] = 0,
	.TxData[2] = 0	
};

static void i2c_Message(void);

#ifdef _i2c_Master		//主机
uint8_t I2C_Type[] = "master";

/****************************************************
 * function:主循环
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void i2c_CycleRun(void)
{
#define TransferWay 0

#if (TransferWay == 0)	//使用中断的方式
	if(HAL_I2C_Master_Transmit_IT(&hi2c1, SLAVE_ADDRESS, i2c.TxData, sizeof(i2c.TxData)) == HAL_OK)
	{
		i2c.Flag_Tx = 1;
	}
	HAL_Delay(3);//间隔时间低于3ms会导致:I2C Acknowledge failure error interrupt occurred;
	if(HAL_I2C_Master_Receive_IT(&hi2c1, SLAVE_ADDRESS, i2c.RxData, sizeof(i2c.RxData)) == HAL_OK)
	{
		i2c.Flag_Rx = 1;
	}
#endif		//使用阻塞的方式
#if (TransferWay == 1)		
	if(HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS, i2c.TxData, sizeof(i2c.TxData),500) == HAL_OK)
	{
		i2c.Flag_Tx = 1;
	}
	HAL_Delay(3);
	if(HAL_I2C_Master_Receive(&hi2c1, SLAVE_ADDRESS, i2c.RxData, sizeof(i2c.RxData),500) == HAL_OK)
	{
		i2c.Flag_Rx = 1;
	}
#endif
#if (TransferWay == 2)
	if(HAL_I2C_Master_Seq_Transmit_IT(&hi2c1, SLAVE_ADDRESS, i2c.TxData, sizeof(i2c.TxData),I2C_FIRST_FRAME) == HAL_OK)
	{
		i2c.Flag_Tx = 1;
	}
	HAL_Delay(3);
	if(HAL_I2C_Master_Seq_Receive_IT(&hi2c1, SLAVE_ADDRESS, i2c.RxData, sizeof(i2c.RxData),I2C_LAST_FRAME) == HAL_OK)
	{
		i2c.Flag_Rx = 1;
	}	
#endif
	i2c_Message();
}

#else					//从机
uint8_t I2C_Type[] = "slave";
/****************************************************
 * function:主循环
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void i2c_CycleRun(void)
{
	//Put I2C peripheral in listen mode process
	if(HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK)//一定要有,每次通信后都需要重新监听
	{

	}
	i2c_Message();	
}

/****************************************************
* function:从机发送完数据后回调
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
	i2c.Flag_Tx = 1;
}

/****************************************************
* function:从机接收完数据后回调
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *I2cHandle)
{
	i2c.Flag_Rx = 1;
}

/****************************************************
* function:Slave Address Match callback.
 * param:hi2c Pointer to a I2C_HandleTypeDef structure that contains
 *                the configuration information for the specified I2C.
 * @param  TransferDirection: Master request Transfer Direction (Write/Read), value of @ref I2C_XferDirection_definition
 * @param  AddrMatchCode: Address Match Code
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
  if(TransferDirection == I2C_DIRECTION_TRANSMIT)
  {
	if(HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, i2c.RxData, sizeof(i2c.RxData), I2C_FIRST_FRAME) != HAL_OK)
	{

	}	  
	  
  }
  else if(TransferDirection == I2C_DIRECTION_RECEIVE)
  {
	if(HAL_I2C_Slave_Seq_Transmit_IT(&hi2c1, i2c.TxData, sizeof(i2c.TxData), I2C_LAST_FRAME)!= HAL_OK)
	{
		
	}  
  }
}

/****************************************************
* function:Listen Complete callback.
* @param  hi2c Pointer to a I2C_HandleTypeDef structure that contains
*                the configuration information for the specified I2C.
* return:void
* note:
* date:2021.6.2
 * *************************************************/
void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{

}
#endif
/****************************************************
* function:i2c错误回调函数
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *I2cHandle)
{

		i2c.Flag_Error = 1;
		i2c.ErrorCode = I2cHandle->ErrorCode;
}

/****************************************************
* function:i2c通信情况信息
 * param:void
 * return:void
 * note:
 * date:2021.6.2
 * *************************************************/
static void i2c_Message(void)
{
	if(i2c.Flag_Tx)
	{
		i2c.Flag_Tx = 0;
		printf("%s Tx ok\r\n",I2C_Type);
		i2c.TxData[0]++;
	}
	if(i2c.Flag_Rx)
	{
		i2c.Flag_Rx = 0;
		printf("%s Rx:%d %d %d\r\n",I2C_Type,i2c.RxData[0],i2c.RxData[1],i2c.RxData[2]);
	}
	
	if(i2c.Flag_Error)
	{
		i2c.Flag_Error = 0;
		switch(i2c.ErrorCode)
		{
			case HAL_I2C_ERROR_BERR:printf("I2C Bus error interrupt occurred;\r\n");break;
			case HAL_I2C_ERROR_ARLO:printf("I2C Arbitration Lost error interrupt occurred;\r\n");break;
			case HAL_I2C_ERROR_AF:printf("I2C Acknowledge failure error interrupt occurred;\r\n");break;
			case HAL_I2C_ERROR_OVR:printf("I2C Over-Run/Under-Run interrupt occurred;\r\n");break;

			case HAL_I2C_ERROR_NONE:printf("HAL_I2C_ERROR_NONE;\r\n");break;
			case HAL_I2C_ERROR_DMA:printf("HAL_I2C_ERROR_DMA;\r\n");break;
			case HAL_I2C_ERROR_TIMEOUT:printf("HAL_I2C_ERROR_TIMEOUT;\r\n");break;
			case HAL_I2C_ERROR_SIZE :printf("HAL_I2C_ERROR_SIZE ;\r\n");break;
			case HAL_I2C_ERROR_DMA_PARAM :printf("HAL_I2C_ERROR_DMA_PARAM ;\r\n");break;
			
			default:printf("I2C Other error;\r\n");break;
		}
	}
}
 
#endif


My_i2c.h

#ifndef _My_i2c_H_
#define _My_i2c_H_

#include "main.h"

#define _User_i2c
#ifdef _User_i2c

#define _i2c_Master	//定义为主机,屏蔽为从机
#define SLAVE_ADDRESS ((0x01)<<1)

#endif

void i2c_CycleRun(void);
#endif

效果

STM32硬件IIC 主从(一、CubeMx快速实现)_第8张图片
吃顿饭的时间依然通信正常

关于

1、关于硬件i2c为I2C_ADDRESSINGMODE_7BIT时的从机地址
hi2c1.Init.OwnAddress1 = SLAVE_ADDRESS;

此处的SLAVE_ADDRESS 为

#define SLAVE_ADDRESS ((0x01)<<1)

这是由于HAL_I2C_Init()函数中采用使用的是这样赋值给OAR1的

  MODIFY_REG(hi2c->Instance->OAR1, (I2C_OAR1_ADDMODE | I2C_OAR1_ADD8_9 | I2C_OAR1_ADD1_7 | I2C_OAR1_ADD0), (hi2c->Init.AddressingMode | hi2c->Init.OwnAddress1));

这句话其实就是将(hi2c->Init.AddressingMode | hi2c->Init.OwnAddress1))的值赋予OAR1,由此可以看到在给i2c赋地址时记得偏移,否则就是地址0x00了。
STM32硬件IIC 主从(一、CubeMx快速实现)_第9张图片
抓出来也可以看到,当主机写时该字节为0x02:
STM32硬件IIC 主从(一、CubeMx快速实现)_第10张图片
主机读时该字节为0x03:
STM32硬件IIC 主从(一、CubeMx快速实现)_第11张图片
2、在进行双机通信时,优先从机先上电,避免主机先上电后导致iic报错。最好上电前有个延时,然后再收发。
3、主机只发送测试:尽量带延时
STM32硬件IIC 主从(一、CubeMx快速实现)_第12张图片

STM32硬件IIC 主从(一、CubeMx快速实现)_第13张图片
4、主机只接收:尽量带延时
STM32硬件IIC 主从(一、CubeMx快速实现)_第14张图片
STM32硬件IIC 主从(一、CubeMx快速实现)_第15张图片
5、400k通信
STM32硬件IIC 主从(一、CubeMx快速实现)_第16张图片
感觉400k时不是很稳定,
STM32硬件IIC 主从(一、CubeMx快速实现)_第17张图片

疑惑

1、在时序波形中会观察到这个高电平,虽然不影响,但觉得很奇怪。不知哪里有问题。
在这里插入图片描述
2、当发送的间隔时间小于3ms时:
I2C Acknowledge failure error interrupt occurred;

STM32硬件IIC 主从(一、CubeMx快速实现)_第18张图片
时序上会怎么样呢?
在这里插入图片描述
STM32硬件IIC 主从(一、CubeMx快速实现)_第19张图片
STM32硬件IIC 主从(一、CubeMx快速实现)_第20张图片
所有的读字节从机都开小差了,why??? 只有写入正常
甚至会导致死机。
STM32硬件IIC 主从(一、CubeMx快速实现)_第21张图片
可以看到主机回调函数呈现发送后无应答错误。

STM32硬件IIC 主从(一、CubeMx快速实现)_第22张图片
3、I2C_NOSTRETCH_DISABLE

  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

从这句语句看是禁止了,实际呢?
STM32硬件IIC 主从(一、CubeMx快速实现)_第23张图片

  /* Configure I2Cx: Generalcall and NoStretch mode */
  MODIFY_REG(hi2c->Instance->CR1, (I2C_CR1_ENGC | I2C_CR1_NOSTRETCH), (hi2c->Init.GeneralCallMode | hi2c->Init.NoStretchMode));

可以看到CR1的NoStretchMode被写入了0;
那么问题来了,
在这里插入图片描述
,具体有啥影响我也还不知道,只不过觉得↓↓↓↓↓↓↓↓
STM32硬件IIC 主从(一、CubeMx快速实现)_第24张图片

整体工程

链接:https://pan.baidu.com/s/1EH16ZvEZNzmFBbUddxjHPQ 
提取码:f4zt 
复制这段内容后打开百度网盘手机App,操作更方便哦

最后

当发现通信时没反应,别抓狂,我也浑浑噩噩了搞了很久,双机cubeMx自带的例程坑,莫名让人抓狂,再抓狂时有可能是需要你复位从机-》紧接着复位主机,然后奇迹出现!
STM32硬件IIC 主从(一、CubeMx快速实现)_第25张图片

你可能感兴趣的:(STM32,stm32,嵌入式,单片机)