通过HAL库实现STM32F103C8T6两块板之间的IIC主从相互通信,串口打印接收数据。
/**I2C1 GPIO Configuration
PB6 ------> I2C1_SCL
PB7 ------> I2C1_SDA
注意通信线记得接上拉电阻4.7k。
配置sys 串行调试
配置外部高速时钟
配置串口,默认值
配置IIC,默认值,从机地址0x01,左移1位=0x02
开启IIC的事件中断与错误中断
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
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了。
抓出来也可以看到,当主机写时该字节为0x02:
主机读时该字节为0x03:
2、在进行双机通信时,优先从机先上电,避免主机先上电后导致iic报错。最好上电前有个延时,然后再收发。
3、主机只发送测试:尽量带延时
4、主机只接收:尽量带延时
5、400k通信
感觉400k时不是很稳定,
1、在时序波形中会观察到这个高电平,虽然不影响,但觉得很奇怪。不知哪里有问题。
2、当发送的间隔时间小于3ms时:
I2C Acknowledge failure error interrupt occurred;
时序上会怎么样呢?
所有的读字节从机都开小差了,why??? 只有写入正常
甚至会导致死机。
可以看到主机回调函数呈现发送后无应答错误。
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
/* 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;
那么问题来了,
,具体有啥影响我也还不知道,只不过觉得↓↓↓↓↓↓↓↓
链接:https://pan.baidu.com/s/1EH16ZvEZNzmFBbUddxjHPQ
提取码:f4zt
复制这段内容后打开百度网盘手机App,操作更方便哦
当发现通信时没反应,别抓狂,我也浑浑噩噩了搞了很久,双机cubeMx自带的例程坑,莫名让人抓狂,再抓狂时有可能是需要你复位从机-》紧接着复位主机,然后奇迹出现!