STM32 HAL库实现硬件IIC通信

news/2024/7/8 4:56:37 标签: stm32, 嵌入式硬件, 单片机

文章目录

  • 一. 前言
  • 二. 关于IIC通信
  • 三. IIC通信过程
  • 四. STM32实现硬件IIC通信
  • 五. 关于硬件IIC的Bug

一. 前言

最近正在DIY一款智能电池,需要使用STM32F030F4P6和TI的电池管理芯片BQ40Z50进行SMBUS通信。SMBUS本质上就是IIC通信,项目用到STM32CubeMX+HAL库,使用硬件IIC完成MCU对芯片的寄存器数据的读取和改写。

整篇博客主要是梳理一下IIC的通信流程,并记录一下项目实现的过程,如有错的地方,欢迎各位读者批评指正。

二. 关于IIC通信

总线又两条信号线构成一条是数据线SDA ,用于数据传输。另一条则是时钟线SCL ,用于传输CLK信号,始终由主设备控制,主机(Master) 通过控制时钟信号可以实现对从机(Slave) 的控制与读写操作。各设备通过SDA、SCL接入总线,每个连接到IIC总线上的器件都有一个唯一的地址,如上图所示。

SDA和SCL需通过上拉电阻 接至VCC。由于连接到总线上的器件输出级必须是漏极开路或者集电极开路的,因此只要有一个器件任意时刻输出低电平,都将总线上的信号变低;当IIC总线空闲时,SDA和SCL两条线均为高电平,即各器件的SDA和SCL都是线与的关系。

三. IIC通信过程

IIC通信过程由一系列的操作组成:

  1. 起始信号 :通信开始时,主设备发送一个低电平的SCL时钟脉冲,然后再发送一个低电平的SDA数据线脉冲。这个SDA的下降沿表示I2C总线上的一个开始信号。

  2. 从设备地址 :主设备发送从设备地址到I2C总线。I2C地址由7Bit或10Bit组成,取决于使用的设备。

  3. 读/写位传输 :读写位占据 1bit 数据,指定了数据传输的方向:

    a. 主设备发送数据,从设备接受数据,为 0 
    b. 主设备接收数据,从设备发送数据,为 1 
    
  4. 仲裁机制和应答 :应答位由1个Bit的数据组成。主设备通过检测应答位,以确定从设备是否存在。若主设备收到的是NACK,这会让主设备发生重启或停止流程:

     a. 如果从设备存在并正确收到地址,从设备将SDA下拉,产生应答信号 0 (ACK)。
     b. 如果从设备不存在或未正确收到地址,导致SDA空闲,产生非应答信号1(NACK)。
    
  5. 数据传输 :在确认通信目标后,主设备将发送或接收数据。数据传输是在主机产生的每个时钟周期的上升沿或下降沿进行的。

    a. 主设备发送数据: 上图所示,主设备 将要发送的数据位(8位或更多)依次发送到SDA线上,并在每个时钟周期上升沿时更新数据。从设备在每个时钟周期下降沿时接收数据,并在接收之后发送应答位来确认是否接收正确。

    b. 主设备接收数据: 上图所示,当读/写传输位为1时,到了数据传输时,主机从发送变成接收,从机从接收变成发送。从设备 将要发送的数据位(8位或更多)依次发送到SDA线上,并在每个时钟周期上升沿时更新数据。主设备在每个CLK下降沿时接收数据,并在接收之后发送应答位来确认是否接收正确。

  6. 停止信号(Stop Signal) :通信完成后,主设备发送一个停止信号,由一个高电平的SCL时钟脉冲和一个高电平的SDA数据线脉冲组成。这个SDA的上升沿表示I2C总线上的一个停止信号。

四. STM32实现硬件IIC通信

本项目用的是STM32F030F4P6通过Smbus通信和TI的电池管理芯片BQ40Z50通信,读取电池内部的电压、电流及电量信息,并且通过改写电池内部寄存器控制输入输出的Mos管。Smbus本质上就是IIC通信,下面通过访问BQ40Z50内部的电压寄存器的例子来说明模拟IIC的实现过程:

STM32CubeMX配置如下:


1. 主设备: STM32F030F4P6
2. 从设备: BQ40Z50
3. 通信频率: 10-100KHz
4. 从设备地址: 0x16
5. 电压寄存器地址: 0x09

读取寄存器数据:

  1. 主机(STM32)首先产生Start信号。
  2. 然后紧跟着发送从机设备地址(0x16),此时读写位为0,表明是向从机写命令;
  3. 这时候主机等待从机(BQ40Z50)的应答信号(ACK)。
  4. 当主机收到应答信号时,发送要访问的寄存器地址(0x09),继续等待从机的应答信号;
  5. 当主机收到应答信号后,主机要改变通信模式,主机将由发送变为接收,从机将由接收变为发送。接着主机重新发送一个开始Start信号,然后紧跟着发送从机地址(0x16),注意此时读写位为1,表明将主机设置成接收模式开始读取数据。
  6. 这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收从机发送来的寄存器数据。
  7. 主机产生停止信号,结束传送过程。

虽然通信过程比较复杂,但是实现非常简单。因为大部分步骤HAL库已经封装好。直接调用现成的接口即可:

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)
                                   /**
* @brief  Read an amount of data in blocking mode from a specific memory address
* @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 status
*/

比如,读取BQ40Z50的电压:

Uint8_t Rxbuffer[2];//用于接受数据的数组
			 
HAL_I2C_Mem_Read(hi2cx,0x16,0x09,I2C_MEMADD_SIZE_8BIT,Rxbuffer,2,OxFF);//IIC接受从机数据

voltage = Rxbuffer[1]<<8|Rxbuffer[0];//对接受数据进行解析

同样的,写入寄存器数据:

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)
/**
  * @brief  Write an amount of data in blocking mode to a specific memory address
  * @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 status
  */

比如:向0x00写入0x270C和0x430D可以关闭BQ40Z50的Mos管:

uint8_t Mos_off [2];
Mos_off [0] = 0x0C;
Mos_off [1] = 0x27;
if(HAL_I2C_Mem_Write(hi2cx,0x16,0x00,I2C_MEMADD_SIZE_8BIT,Mos_off ,2,0xFF)==HAL_OK)
{
	Mos_off [0] = 0x3D;
	Mos_off [1] = 0x04;
	HAL_I2C_Mem_Write(hi2cx,0x16,0x00,I2C_MEMADD_SIZE_8BIT,Mos_off ,2,0xFF);
}

五. 关于硬件IIC的Bug

在开发过程中,IIC经常会遇到很多莫名其妙的Bug,比如反复超时,陷入I2C_WaitOnFlagUntilTimeout()死循环中,或者是一直处于HAL_I2C_STATE_BUSY_RX直接卡死,看了网络上很多的帖子,但是都没有找到好的解决办法。

这里的解决方式非常简单粗暴,就在卡死的地方直接对硬件IIC进行重置,重新初始化,亲测有效。

各位有什么更好的方法,欢迎在评论区留言,一起探讨。

void User_I2C_ErrorInit(I2C_HandleTypeDef *hi2c)
{
	HAL_I2C_DeInit(hi2c);
	
	hi2c->Instance = I2C1;
	hi2c->Init.Timing = 0x2000090E;
	hi2c->Init.OwnAddress1 = 0;
	hi2c->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
	hi2c->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
	hi2c->Init.OwnAddress2 = 0;
	hi2c->Init.OwnAddress2Masks = I2C_OA2_NOMASK;
	hi2c->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
	hi2c->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;

    hi2c->Instance->CR1 |= I2C_CR1_SWRST;
    hi2c->Instance->CR1 &= ~I2C_CR1_SWRST;

	if (HAL_I2C_Init(hi2c) != HAL_OK)
	{
		User_Error_Handler();
	}

	/** Configure Analogue filter
	*/
	if (HAL_I2CEx_ConfigAnalogFilter(hi2c, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
	{
		User_Error_Handler();
	}

	/** Configure Digital filter
	*/
	if (HAL_I2CEx_ConfigDigitalFilter(hi2c, 0) != HAL_OK)
	{
		User_Error_Handler();
	}
}

static void User_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 */
}

各位有什么更好的方法,欢迎在评论区留言,一起探讨。


http://www.niftyadmin.cn/n/5536519.html

相关文章

ListView 的简单使用及 ArrayAdapter 中参数详解

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD&#xff0c;日常还会涉及Android开发工作。 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起…

记录一次Apache Tomcat 处理返回自定义的404页面

记录工作中遇到处理访问tomcat 不存在的资源&#xff0c;返回自定义的404页面 删除webapps目录下的example、docs、manager、hta-manager目录&#xff0c;只保留 ROOT目录&#xff0c;应用部署在了这个目录 删除 manager、hta-manager 我没有发现有什么异常 制作404.jsp 或者 4…

windows USB 设备驱动开发-USB设备描述符

USB的描述符是USB设备向主机报告状态的重要数据结构&#xff0c;在USB通电后&#xff0c;端点(也称为终结点)0始终处于可用状态&#xff0c;这个默认的端点就是用于主机从设备中读取描述符的。 讨论USB通讯&#xff0c;需要从软件和硬件两方面说起&#xff0c;在软件上&#x…

车牌号查车辆信息-车牌号查车辆信息接口-汽车API接口

接口简介&#xff1a;输入车牌号&#xff0c;返回车辆相关信息&#xff08;无车主信息&#xff09;。初始登记日期、上险日期、保险到期时间、车架号、品牌这些数据会返回&#xff0c;其他数据不一定全部返回&#xff0c;,详细参数请查看返回接口文档 一般在新车上险或过户后第…

机器学习复习总结

目录 第一章 机器学习的概念及其应用 1.1机器学习的特点&#xff1a; **1.2机器学习的分类&#xff1a; 1.2.1监督学习&#xff1a; 1.2.2无监督学习&#xff1a; 1.2.3强化学习&#xff1a; 1.2.4三种机器学习的区别 *1.3深度学习 1.3.1深度学习的实质&#xff1a; …

首家!腾讯云数据万象通过中国信通院智能存储专项测试

2024年6月19日&#xff0c;由中国通信标准化协会主办&#xff0c;中国通信标准化协会大数据技术标准推进委员会(CCSA TC601)承办的首届“数据智能大会”在京隆重召开。腾讯云存储受邀出席了活动&#xff0c;大会中“可信数据智能”系列评估测试结果正式颁布&#xff0c;经过严苛…

Ubuntu 22.04.1 LTS 离线安装Docker

确定linux版本 cat /etc/lsb-release DISTRIB_IDUbuntuDISTRIB_RELEASE22.04DISTRIB_CODENAMEjammyDISTRIB_DESCRIPTION"Ubuntu 22.04.1 LTS"确定dpkg版本 sudo dpkg --print-architecture amd64下载地址 https://download.docker.com/linux/ubuntu/dists/jamm…

算法力扣刷题 三十一【150. 逆波兰表达式求值】

前言 栈和队列篇。 记录 三十一【150. 逆波兰表达式求值】 一、题目阅读 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意&#xff1a; 有效的算符为 、-、* 和 / 。 每个操作…