【STM32】HAL库 STM32CubeMX教程十二---IIC(读取AT24C02 )

发布者:BlossomSunrise最新更新时间:2020-08-27 来源: eefocus关键字:STM32  HAL库  STM32CubeMX  IIC  读取AT24C02 手机看文章 扫描二维码
随时随地手机看文章

前言:

本系列教程将HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用


在之前的标准库中,STM32的硬件IIC非常复杂,更重要的是它并不稳定,所以都不推荐使用。


但是在我们的HAL库中,对硬件IIC做了全新的优化,使得之前软件IIC几百行代码,在HAL库中,只需要寥寥几行就可以完成 那么这篇文章将带你去感受下它的优异之处


这可能是目前关于STM32CubeMX的硬件iic 讲的最全面和详细的一篇文章之一了


所用工具:

1、芯片: STM32F103ZET6

2、STM32CubeMx软件

3、IDE: MDK-Keil软件

4、STM32F1xx/STM32F4xxHAL库

5、IIC: 使用硬件IIC1

知识概括:

通过本篇博客您将学到:

IIC的基本原理

STM32CubeMX创建IIC例程

HAL库IIC函数库

AT24C02 芯片原理


《IIC原理超详细讲解—值得一看》。
如果对IIC还不是太了解的朋友请移步到这篇文章中

IIC起始信号和终止信号:

  • 起始信号:SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。

  • 停止信号:SCL保持高电平。SDA由低电平变为高电平。

在这里插入图片描述

【STM32】HAL库 STM32CubeMX教程四—UART串口通信详解

5 时钟源设置
在这里插入图片描述
我的是 外部晶振为8MHz

  • 1选择外部时钟HSE 8MHz

  • 2PLL锁相环倍频9倍

  • 3系统时钟来源选择为PLL

  • 4设置APB1分频器为 /2

  • 5 使能CSS监视时钟

32的时钟树框图 如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》

6 项目文件设置
在这里插入图片描述

  • 1 设置项目名称

  • 2 设置存储路径

  • 3 选择所用IDE

在这里插入图片描述
7创建工程文件

然后点击GENERATE CODE 创建工程

配置下载工具
新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行

在这里插入图片描述

IIC HAL库代码部分

在i2c.c文件中可以看到IIC初始化函数。在stm32f1xx_hal_i2c.h头文件中可以看到I2C的操作函数。分别对应轮询,中断和DMA三种控制方式
在这里插入图片描述

上面的函数看起来多,但是只是发送和接收的方式改变了,函数的参数和本质功能并没有改变
比方说IIC发送函数 还是发送函数,只不过有普通发送,DMA传输,中断 的几种发送模式

这里我们仅介绍下普通发送,其他的只是改下函数名即可

IIC写函数

HAL_I2C_Master_Transmit(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);


功能:IIC写数据
参数:

  • *hi2c 设置使用的是那个IIC 例:&hi2c2

  • DevAddress 写入的地址 设置写入数据的地址 例 0xA0

  • *pData 需要写入的数据

  • Size 要发送的字节数


    • Timeout 最大传输时间,超过传输时间将自动退出传输函数

IIC读函数

HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout);


功能:IIC读一个字节
参数:

  • *hi2c: 设置使用的是那个IIC 例:&hi2c2

  • DevAddress: 写入的地址 设置写入数据的地址 例 0xA0

  • *pDat:a 存储读取到的数据

  • Size: 发送的字节数

  • Timeout: 最大读取时间,超过时间将自动退出读取函数

举例:

HAL_I2C_Master_Transmit(&hi2c1,0xA1,(uint8_t*)TxData,2,1000) ;;


发送两个字节数据

IIC写数据函数

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);

/* 第1个参数为I2C操作句柄

   第2个参数为从机设备地址

   第3个参数为从机寄存器地址

   第4个参数为从机寄存器地址长度

   第5个参数为发送的数据的起始地址

   第6个参数为传输数据的大小

   第7个参数为操作超时时间   */


功能: IIC写多个数据 该函数适用于IIC外设里面还有子地址寄存器的设备,比方说E2PROM,除了设备地址,每个存储字节都有其对应的地址

参数:

  • *hi2c: I2C设备号指针,设置使用的是那个IIC 例:&hi2c2

  • DevAddress: 从设备地址 从设备的IIC地址 例E2PROM的设备地址 0xA0

  • MemAddress: 从机寄存器地址 ,每写入一个字节数据,地址就会自动+1

  • MemAddSize: 从机寄存器地址字节长度 8位或16位


    • 写入数据的字节类型 8位还是16位


    • I2C_MEMADD_SIZE_8BIT


    • I2C_MEMADD_SIZE_16BIT

  • 在stm32f1xx_hal_i2c.h中有定义
    在这里插入图片描述

  • *pData: 需要写入的的数据的起始地址

  • Size: 传输数据的大小 多少个字节

  • Timeout: 最大读取时间,超过时间将自动退出函数

使用HAL_I2C_Mem_Write等于先使用HAL_I2C_Master_Transmit传输第一个寄存器地址,再用HAL_I2C_Master_Transmit传输写入第一个寄存器的数据。可以传输多个数据

void Single_WriteI2C(uint8_t REG_Address,uint8_t REG_data)

{

    uint8_t TxData[2] = {REG_Address,REG_data};

    while(HAL_I2C_Master_Transmit(&hi2c1,I2C1_WRITE_ADDRESS,(uint8_t*)TxData,2,1000) != HAL_OK)

    {

        if (HAL_I2C_GetError(&hi2c1) != HAL_I2C_ERROR_AF)

                {

                  Error_Handler();

                }

    }

}


在传输过程,寄存器地址和源数据地址是会自加的。

至于读函数也是如此,因此用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read,来写读指定设备的指定寄存器数据是十分方便的,让设计过程省了好多步骤。

举例:


8位:


HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);


HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_8BIT,&(I2C_Buffer_Write[i]),8, 1000);


16位:


HAL_I2C_Mem_Write(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);


HAL_I2C_Mem_Read(&hi2c2, ADDR, i, I2C_MEMADD_SIZE_16BIT,&(I2C_Buffer_Write[i]),8, 1000);


如果只往某个外设中写数据,则用Master_Transmit。 如果是外设里面还有子地址,例如我们的E2PROM,有设备地址,还有每个数据的寄存器存储地址。则用Mem_Write。

Mem_Write是2个地址,Master_Transmit只有从机地址


硬件IIC读取AT24C02

在mian.c文件前面声明,AT24C02 写地址和读地址 ,定义写数据数组,和读数据数组


/* USER CODE BEGIN PV */

#include


#define ADDR_24LCxx_Write 0xA0

#define ADDR_24LCxx_Read 0xA1

#define BufferSize 256

uint8_t WriteBuffer[BufferSize],ReadBuffer[BufferSize];

uint16_t i;

/* USER CODE END PV */


重新定义printf函数


在 stm32f4xx_hal.c中包含#include


#include "stm32f4xx_hal.h"

#include

extern UART_HandleTypeDef huart1;   //声明串口


在 stm32f4xx_hal.c 中重写fget和fput函数


/**

  * 函数功能: 重定向c库函数printf到DEBUG_USARTx

  * 输入参数: 无

  * 返 回 值: 无

  * 说    明:无

  */

int fputc(int ch, FILE *f)

{

  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);

  return ch;

}


/**

  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx

  * 输入参数: 无

  * 返 回 值: 无

  * 说    明:无

  */

int fgetc(FILE *f)

{

  uint8_t ch = 0;

  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);

  return ch;

}


在main.c中添加


  /* USER CODE BEGIN 2 */

for(i=0; i<256; i++)

    WriteBuffer[i]=i;    /* WriteBuffer init */



printf("rn***************I2C Example Z小旋测试*******************************rn");

for (int j=0; j<32; j++)

        {

                if(HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT,WriteBuffer+8*j,8, 1000) == HAL_OK)

                {

                                printf("rn EEPROM 24C02 Write Test OK rn");

                        HAL_Delay(20);

                }

                else

                {

                         HAL_Delay(20);

                                printf("rn EEPROM 24C02 Write Test False rn");

                }

}

/*

// wrinte date to EEPROM   如果要一次写一个字节,写256次,用这里的代码

for(i=0;i {

    HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入

  HAL_Delay(5);//此处延时必加,与AT24C02写时序有关

}

printf("rn EEPROM 24C02 Write Test OK rn");

*/


HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT,ReadBuffer,BufferSize, 0xff);


for(i=0; i<256; i++)

printf("0x%02X  ",ReadBuffer[i]);

  /* USER CODE END 2 */


注意事项:


AT24C02的IIC每次写之后要延时一段时间才能继续写 每次写之后要delay 5ms左右 不管硬件IIC采用何种形式(DMA,IT),都要确保两次写入的间隔大于5ms;

读写函数最后一个超时调整为1000以上 因为我们一次写8个字节,延时要久一点

AT24C02页写入只支持8个byte,所以需要分32次写入。这不是HAL库的bug,而是AT24C02的限制,其他的EEPROM可以支持更多byte的写入。

当然,你也可以每次写一个字节,分成256次写入,也是可以的 那就用注释了的代码即可


/*

// wrinte date to EEPROM   如果要一次写一个字节,写256次,用这里的代码

for(i=0;i {

    HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT,&WriteBuffer[i],1,0xff);//使用I2C块读,出错。因此采用此种方式,逐个单字节写入

  HAL_Delay(5);//此处延时必加,与AT24C02写时序有关

}

printf("rn EEPROM 24C02 Write Test OK rn");

[1] [2]
关键字:STM32  HAL库  STM32CubeMX  IIC  读取AT24C02 引用地址:【STM32】HAL库 STM32CubeMX教程十二---IIC(读取AT24C02 )

上一篇:【STM32】HAL库 STM32CubeMX教程十三---RTC时钟
下一篇:【STM32】 DMA原理,步骤超细详解,一文看懂DMA

推荐阅读最新更新时间:2024-11-06 15:27

STM32片内RTC亚秒特性以及应用演示的分享
绝大多数STM32系列里的RTC都具有亚秒【或称子秒】计数单元。为了了解亚秒特性及功能,不妨先看RTC的功能框图。本文中的有关截图若无特别说明均来自STM32L4系列参考手册。 RTC的时钟源【RTCCLK】可以是LSE、LSI或者HSE/32,由RTCCLK最终变成日历的秒脉冲驱动信号经过了2次分频。先经过上图中A处的异步分频单元,默认分频系数是128,形成ck_apre时钟,默认情况下该时钟频率为256Hz;然后该时钟脉冲来到图中B处的同步分频单元,默认分频系数为256,最终形成1Hz的秒脉冲【ck_spre】到日历单元。关于两分频单元分频系数的配置,通过对RTC_PRER寄存器的相关位编程实现。 其中异步分频系数配
[单片机]
<font color='red'>STM32</font>片内RTC亚秒特性以及应用演示的分享
STM32学习笔记:单片机按键单击、双击、长按功能实现
由于项目产品的需要,只能设置一个按键,但是需要实现短按(即单击)切换工作模式、长按开关机、双击暂停等复用功能。下图是三种情况下的按键波形。按键未按下时是高电平,按下去是低电平。按键单击时,判断时间门槛设置为50~2000ms;长按门槛为持续按下2000ms。双击可以视为时间间隔很短的俩次有效单击,从第一次单击上升沿到第二次单击上升沿延时门槛为100~500ms。 //按键按下去会出现下降沿,设置按键IO口所在的外部端口为下降沿触发中断。void EXTIX_Init(void) { EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;
[单片机]
<font color='red'>STM32</font>学习笔记:单片机按键单击、双击、长按功能实现
STM32与SD卡通信问题详解
SD的驱动和应用困扰了我很久,寒假的时候看到SD简化版物理层协议的时候就傻掉了,看到SD的驱动快3000行的代码也动摇了。这几天几种地看了一下SD卡的相关内容,总结了一些体会,感觉也没有那么恐怖了。我决定从分层上来讨论SD的驱动和应用,因为这样可以构建一个清晰的逻辑,且不知哪位计算机大师曾说过:一切计算机问题都可以用分层的方法来解决。 我自己把SD卡从驱动到应用共分为4层,从下至上依次为:驱动层、物理层、文件系统层、应用层。下面一一来介绍各层的一些重要的操作。 1)驱动层 驱动层,对应到ST的库,就是stm32f10x_sdio.c/.h这个两文件。其实使用任何一个STM32的外设,只要用库函数都离不开这一对互相对应的
[单片机]
STM32的AFIO口简介
1、STM32上有很多I/O口,也有很多的内置外设I2C,ADC,ISP,USART ,为了节省引出管脚,这些内置外设基本上是与I/O口共用管脚的,也就是I/O管脚的复用功能。 2、为了优化64脚或100脚封装的外设数目,可以把一些复用功能重新映射到其它引脚上。设置复用重映射和调试I/O配置寄存器(AFIO_MAPR)实现引脚的重新映射。这时,复用功能不再映射到它们的原始分配上。 3、只有使用了AFIO的事件控制寄存器、AFIO的重映射功能以及外部中断(EXTI)控制寄存器才需要开启AFIO的时钟。
[单片机]
STM32_IAP远程升级及C#上位机
STM32的IAP功能在一些需要升级维护的场景下显得十分的重要,当然在实际项目中,我们需要远程发送升级指令,使得主控进入升级模式,进而将固件下发升级。很多网上的资源中,只有IAP跳转至APP,并且不带有任何协议,直接将bin文件一次性下发。但是这样的话,在项目实际使用过程中,会非常不稳定。还有一些是有代码,但是上位机代码或者stm32的代码不给你,只是提供思路。也是挺麻烦的一件事情。 我先讲讲自己的思路,C#上位机方面,首先打开串口,串口的波特率使用115200(上位机中没有给出设置界面)。打开需要升级的bin文件,左侧textbox显示文件绝对路径,下侧textbox显示文件大小。此程序将bin文件分成2k字节一个包,并且在
[单片机]
【话说定时器系列】之七:STM32定时器比较输出话题
我们知道,STM32定时器除了基本计数定时功能外,还对外拓展了输入、输出通道,从而可以实现输入捕捉、比较输出功能。 比较输出【Compare Output】功能: 定时器通过对预设的 比较值 与 计数器的值 做匹配比较之后,并依据相应的输出模式从而实现各类输出。如PWM输出、电平翻转、单脉冲输出、强制输出等。一般来讲,STM32的通用定时器和高级定时器都具有比较捕获功能,不同的定时器可能通道数量上有差异。 或者,我们可以使用比较输出功能来感知或提示某个时间段已经过去了。这个不难理解,因为比较输出的基本特征是计数器CNT的值与比较寄存器CCR的值做比较,计数器数据变化意味着时间的流逝, 当计数器记到跟比较值相等或相匹配时 ,也就
[单片机]
基于STM32和DS1302设计的时钟程序
#ifndef __DS1302_H #define __DS1302_H #include "stm32f10x.h" extern u8 d ; //依次为年,周,月,日,时,分,秒 extern u8 disp ;//依次为年 【1】;周 【3】;月 【5】 //***********************日 【7】;时 【9】;分 【11】;秒 【13】 void ds1302d_convert(void); //如果用低disp ;必须在read_time();后调用此函数; void ds1302set_time(void); //对时函数 void ds1302read_time(void); //
[单片机]
STM32红牛开发板非固件库控制LED
摘要 STM32红牛开发板上的5个LED,接在GPIOF6~10脚,输出低电平时,LED亮。这样我们设置GPIOF的相关寄存器,让其输出低电平就可以让LED亮。因为没有用到ST提供的固件库,所以是直接对寄存器的内存地址读写,即对一个指向该地址的指针变量进行读写。并且该变量必须为易变型的,即用volatile定义,这样是为了告诉编译器不要去优化这个变量,导致其它一些寄存器的数据变化。GPIOx是挂载在APB2高速外设总线上的,最大频率是72MHZ,所以我们除了了打开HSE(外部高速时钟)并关闭内部高速时钟(开机默认选择了HSI)外,还要打开APB2总线的时钟。 一、寄存器地址映射 外设的基址PERI
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved