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

时间:2022-09-21 17:33:21

参考传送门

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

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

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

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

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

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

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

  下面是主机的重要代码:

/* I2C2 init function  IIC配置*/
static void MX_I2C2_Init(void)
{ hi2c2.Instance = I2C2;
hi2c2.Init.Timing = 0x10805D88;
hi2c2.Init.OwnAddress1 = ; //用户自己配置的地址
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c2.Init.OwnAddress2 = ;
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, ) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
} } while(HAL_I2C_Master_Transmit_IT(&hi2c2 ,0x0b,&BUFF[], )!= 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,,,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 = ;
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, ) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
} } while(HAL_I2C_Slave_Receive(&hi2c2, RE_BUFF, , )!= HAL_OK) {}
// 在配置好IIC后,可直接把该函数放到main函数测试,第一个参数是IIC通道选择,第二个参数是接收缓存,第三个数据的是接收长度,第四个参数是超时时间

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

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

  通信结果如图所示:

  STM32  IIC双机通信—— HAL库硬件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 读函数为例子来翻一下函数地层分析造成这个问题的原因:

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

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

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

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

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

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

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

  从这里接收函数我们可以看到,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 , 地址没有改变,所以这也就解析了上面我说的地址变化的问题了。

STM32 IIC双机通信—— HAL库硬件IIC版的更多相关文章

  1. STM32 关于HAL库硬件SPI要注意的问题总结

    利用STM32CUbeMx编写程序,大大方便了开发,最近做的项目利用到了 STM32CUbeMx的硬件SP,这里对SPI的使用做一个总结. HAL库里的硬件SPI主要有以下几个库函数: /* hspi ...

  2. STM32串口接收中断——基于HAL库

    写在前面 最近需要使用一款STM32L4系列的芯片进行开发,需要学习使用HAL库.在进行串口中断使用的时候遇到了一些小麻烦,写下解决方案供大家参考. 1.UART相关的头文件引用错误 由于本人直接使用 ...

  3. STM32L4R9使用HAL库调试IIC注意事项

    STM32使用Cubemx生成的代码中,用到IIC的驱动,但是始终不能读写,因此使用逻辑分析仪,发现原本地址为0x58的写成了0x20,因此肯定是地址错了.因此,总结如下: 1.需要逻辑分析仪分析II ...

  4. webview页面和壳通信的库(精简版)

    // PG精简版 (function() { var PG = { iosBridge: null, callbackId: 0, callbacks: [], commandQueue: [], c ...

  5. STM32硬件IIC操作

    Stm32具有IIC接口,接口有以下主要特性 多主机功能:该模块既可做主设备也可做从设备 主设备功能 C地址检测 位/10位地址和广播呼叫 支持不同的通讯速度 状态标志: 发送器/接收器模式标志 字节 ...

  6. 【春节歌曲回味 | STM32小音乐盒 】PWM+定时器驱动无源蜂鸣器(STM32 HAL库)

    l  STM32通过PWM与定时器方式控制无源蜂鸣器鸣响 l  STM32小音乐盒,歌曲进度条图形显示与百分比显示,歌曲切换 l  编程使用STM32 HAL库 l  IIC OLED界面编程,动画实 ...

  7. STM32标准外设库、 HAL库、LL库

    工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库.到目前为止,有标准外设库(STD库).HAL库.LL库 三种.前两者都是常用的库,后面的LL库是ST最近才添加,目前支持的 ...

  8. STM32之HAL库、标准外设库、LL库(STM32 Embedded Software)-(转载)

    STM32 Embedded Software  工作以来一直使用ST的STM32系列芯片,ST为开发者提供了非常方便的开发库.到目前为止,有标准外设库(STD库).HAL库.LL库 三种.前两者都是 ...

  9. STM32 HAL库 UART 串口读写功能笔记

    https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...

随机推荐

  1. Oracle开发专题之:%TYPE 和 %ROWTYPE

    1. 使用%TYPE 在许多情况下,PL/SQL变量可以用来存储在数据库表中的数据.在这种情况下,变量应该拥有与表列相同的类型.例如,students表的first_name列的类型为VARCHAR2 ...

  2. 怎么让一个非窗口组件可以接受来自Windows的消息

    为什么要这样做? 有时候我们需要一个非窗口组件(比如一个非继承自TWinContrl的组件)可以接受Windows消息.要接受消息就需要一个窗口句柄,但是非窗口组件却没有句柄.这篇文章将讲述怎么让一个 ...

  3. C#实现MD5字符串加密

    public string Md5Encrypt(string str, string str2) { byte[] result = Encoding.Default.GetBytes((str+s ...

  4. 基于 Webpack 引入 jquery 插件的笔记

    如果都是基于 webpack(npm 上有包),那就非常顺利: import $ from 'jquery' import 'jquery-modal/jquery.modal.min.css' im ...

  5. Oracle 备份与恢复

    在进行生产服务器升级.或更换数据库服务器.搭建测试环境时,需要对生产数据库进行备份以及将来可能的还原. 1.expdp导出 expdp DMS version directory=DATA_PUMP_ ...

  6. ajax方式下载文件

    在web项目中需要下载文件,由于传递的参数比较多(通过参数在服务器端动态下载指定文件),所以希望使用post方式传递参数.通常,在web前端需要下载文件,都是通过指定<a>标签的href属 ...

  7. &lpar;七)STL适配器

    1.适配器是稍微修改某些功能,比如三个参数改为两个参数,函数的名称改一下等等,可以出现在容器.迭代器和仿函数中. 2.适配器相当于对某个东西进行封装,例如A是B的适配器,则真正的功能实现是在B中,可以 ...

  8. Java学习笔记(十五):import关键字

  9. selenium&lowbar;unittest基本框架

    from selenium import webdriver import unittest import time #创建类引入unitest.testcase用例库 class BaiDu_tes ...

  10. uva-10487-枚举

    题意:给你一个集合,每俩个数相加得到一个和s,输入s1,问离s1最近的s是多少 二分,注意如果二分出相等,那一定是最近的数,要不然就比较最后mid和mid-1的数 #include <strin ...