STM32-IIC 配置解说

发布者:shmilyde最新更新时间:2017-10-25 来源: eefocus关键字:STM32  IIC 手机看文章 扫描二维码
随时随地手机看文章

STM32-IIC 配置解说(原创)STM32 - I2C 简介 :I2C 总线接口连接微控制器和串行 I2C 总线。它提供多主机功能,控制所有 I2C总线特定的时序、协议、仲裁和定时。支持标准和快速两种模式,另外 STM32的 I2C 可以使用 DMA 方式操作。本文主要以一个实例来介绍 STM32-I2C 的配置方式和具体在工程中通过调用哪些库函数来实现 I2C 器件的通信。实例:写入数据到器件 AT24C02 并将存入的数据读出好,我们先来讲讲 STM32 I2C 模块的端口基本配置,由 STM32 中文参考手册可以查到在使用 I2C 时对应的引脚要配置成哪种模式。 SCL 和 SDA 引脚都配置成开漏复用输出
本人用的是 STM32F103VET6,它有 2 个 I2C 接口。 I/O 口定义为 PB6-I2C_SCL,
PCB7-I2C1_SDA; PB10-I2C_SCL, PB11-I2C_SDA,由手册可以查出对应的端口。
图文如下:
调用库函数将 I2C 端口配置好(本文使用的是 PB6、 PB7 端口):
程序代码如下:

void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //GPIO 结构体定义
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能 I2C 的 IO 口

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化结构体配置
}

void I2C_Mode_config(void)
{

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

I2C_InitTypeDef I2C_InitStructure;

I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;

I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;

I2C_InitStructure.I2C_OwnAddress1 =0x0A;

I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;

I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

I2C_InitStructure.I2C_ClockSpeed = 400000;

I2C_Cmd(I2C1, ENABLE);

I2C_Init(I2C1, &I2C_InitStructure);
}
好了, STM32 内部的 I2C 模块工作模式就这样被设好了,接下来需要完成与外部器件
AT24C02( EEPROM)进行通信。将分两部分进行代码解析,第一部分是:对 AT24C02 进
行写操作,第二部分:对 AT24C02 进行读操作。
第一部分(写):
备注: I2C_PageSize 为宏定义 #define I2C_PageSize 8 ;

void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
Addr = WriteAddr % I2C_PageSize;//查看输入的地址是不是 8 的整数倍
count = I2C_PageSize - Addr;//表示距离下一页页首地址的距离(步伐数)
NumOfPage = NumByteToWrite / I2C_PageSize;//算出一共有多少页
NumOfSingle = NumByteToWrite % I2C_PageSize;//算出不够一页的数据的余数
if(Addr == 0) //如果输入的地址是首页地址
{
if(NumOfPage == 0) //如果不足一页数据
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不
够一页的余数作为实参
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
}

else //如果数据有一页以上
{
while(NumOfPage--)//用一个 while 循环,执行页写循环操作,有多少页就写多少次
{
I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); //调用写函数,将
I2C_PageSize 变量作为实
参执行页写
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
WriteAddr += I2C_PageSize;//每执行完一次页写对应的地址也需要移 8 个位
pBuffer += I2C_PageSize;//数据指针移 8 个位
}
if(NumOfSingle!=0)//如果有不足一页的数据余数则执行
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle
不够一页的余数作为实参
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
}
}
}
else //输入的地址不是首页地址
{
if(NumOfPage== 0) //如果不足一页
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不
够一页的余数作为实参
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
}
else//如果有一页或一页以上
{
NumByteToWrite -= count;//将地址后续的缺省位置补上数据,数据的多少就是 count
的值, NumByteToWrite 变量的值就是补上数据之后
还剩下未发送的数量
NumOfPage = NumByteToWrite / I2C_PageSize;//剩余的页数
NumOfSingle = NumByteToWrite % I2C_PageSize;//不足一页的数据数量
if(count != 0)//将地址后续的缺省位置补上数据
{
I2C_EE_PageWrite(pBuffer, WriteAddr, count);//调用写函数,以 count 为实参,将地
址缺省下来的部分地址给填充
上数据
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
WriteAddr += count;//加上 count 后,地址就移位到下一页的首地址
pBuffer += count;//数据指针移 count 个位
}
while(NumOfPage--)//将剩余的页数数据写入 EEPROM
{
I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);//调用写函数,将
I2C_PageSize 变量作为实
参执行页写
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
WriteAddr += I2C_PageSize;//将地址移 8 个位
pBuffer += I2C_PageSize; //将数据指针移 8 个位
}
if(NumOfSingle != 0)//将不足一页的数据写入 EEPROM
{
I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle
不够一页的余数作为实参
I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作
}
}
}
}

在以上写操作里面我们拿经常被调用的 I2C_EE_PageWrite 函数还有
I2C_EE_WaitEepromStandbyState 函数并结合 STM32 中文参考手册图文进行对照分析
请读者在读 I2C_EE_PageWrite 函数时请结合上述时序图和下述代码联系一起看!
注: EEPROM_ADDRESS 为器件的地址,大家按照自己具体器件地址写入即可,
例: #define EEPROM_ADDRESS 0xA0

void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
I2C_GenerateSTART(I2C1, ENABLE);//产生起始位
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件地

while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
//ADDR=1,清除 EV6
I2C_SendData(I2C1, WriteAddr); //EEPROM 的具体存储地址位置
while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄
存器非空,数据寄存器已经空,产生 EV8,发送数据到 DR 既可清除该事件
while(NumByteToWrite--) //利用 while 循环 发送数据
{
I2C_SendData(I2C1, *pBuffer); //发送数据
pBuffer++; //数据指针移位
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除
EV8
}
I2C_GenerateSTOP(I2C1, ENABLE);//产生停止信号
}

I2C_EE_WaitEepromStandbyState 这个函数,在每调用完写操作函数后都调用这个函数,这
个函数是用来检测 EEPROM 器件是否已经完成内部写的操作,判断器件完成操作后在进行
下一步的操作!代码如下:

void I2C_EE_WaitEepromStandbyState(void)
{
vu16 SR1_Tmp = 0;
do
{
I2C_GenerateSTART(I2C1, ENABLE);//产生起始信号
SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);//读 SR1 寄存器
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件
地址清除事

}while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));//如果接收不到从机的应
答( NACK)则说明 EEPROM 器件还在工作,直到完成操作跳出循环体!
I2C_ClearFlag(I2C1, I2C_FLAG_AF);//清除 AF 标志位
I2C_GenerateSTOP(I2C1, ENABLE); //产生停止信号
}
第二部分(读):
由以上 AT24C02 读时序图可以知道:读部分需要产生两次起始信号
另外:主设备在从从设备接收到最后一个字节后发送一个 NACK 。接收到 NACK 后,从设备
释放对 SCL 和 SDA 线的控制;主设备就可以发送一个停止/ 重起始条件。
● 为了在收到最后一个字节后产生一个 NACK 脉冲,在读倒数第二个数据字节之后(在倒
数第二个 RxNE 事件之后)必须清除 ACK 位。
● 为了产生一个停止/ 重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二
个 RxNE 事件之后)设置 STOP/START 位。
● 只接收一个字节时,刚好在 EV6 之后(EV6_1 时,清除 ADDR 之后)要关闭应答和停止条
件的产生位。
请读者将代码和图结合在一起看!

void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//需要两个起
始信号
{
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //调用库函数检测 I2C 器件是否处
于 BUSY 状态
I2C_GenerateSTART(I2C1, ENABLE);//开启信号
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//写入器
件地址
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清
除 EV6
I2C_SendData(I2C1, ReadAddr); //发送读的地址
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除 EV8
I2C_GenerateSTART(I2C1, ENABLE);//开启信号
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);//将器件地址
传出,主机为读
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//清除
EV6
while(NumByteToRead)
{
if(NumByteToRead == 1)//只剩下最后一个数据时进入 if 语句
{
I2C_AcknowledgeConfig(I2C1, DISABLE);//最后有一个数据时关闭应答位
I2C_GenerateSTOP(I2C1, ENABLE);//最后一个数据时使能停止位
}
if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) //读取数据
{
*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBuffer
pBuffer++; //指针移位
NumByteToRead--;//字节数减 1 
}
}
I2C_AcknowledgeConfig(I2C1, ENABLE);//将应答位使能回去,等待下次通信
}
STM32-IIC 配置解说到此告一段落!
如果有不正确的地方也请各位多多指教,本人及时纠正;欢
迎大家来和我相互交流学习,谢谢大家。

关键字:STM32  IIC 引用地址:STM32-IIC 配置解说

上一篇:关于STM32利用硬件仿真串口中断处理函数应注意的问题
下一篇:STM32外部中断使用注意事项

推荐阅读最新更新时间:2024-03-16 15:41

STM32单片机串口通讯代码
在STM32开发中,串口是我们最常用的接口。通过串口,我们很方便地把数据输出到电脑,方便我们进行程序调试。下面我们来看看STM32的串口通讯代码。 要实现串口通讯,我们要进行下面几个步骤: 首先:要打开GPIO口的时钟和串口模块时钟。在圆点博士小四轴中,我们用的是GPIOA和COM1模块。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 其次:要指定GPIO口,即确定哪些IO是用于串口通讯的。记得使用GPIO_Mode_AF_PP模式 GPIO_I
[单片机]
STM32内存管理以及堆和栈的理解
今天仔细读了一下内存管理的代码,然后还有看了堆栈的相关知识,把以前不太明白的一些东西想通了,写下来,方便以后查看,也想大家看了能指出哪里不对,然后修改。 首先,先看一下stm32的存储器结构。 Flash,SRAM寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。可访问的存储器空间被分成8个主要块,每个块为512MB。 FLASH存储下载的程序。 SRAM是存储运行程序中的数据。 所以,只要你不外扩存储器,写完的程序中的所有东西也就会出现在这两个存储器中。 这是一个前提! 堆栈的认知 1. STM32中的堆栈。 这个我产生过混淆,导致了很多逻辑上的混乱。首先要说明的是单片机是
[单片机]
<font color='red'>STM32</font>内存管理以及堆和栈的理解
stm32使用usart1串口通信以及truestudio重定向printf踩的坑
刚接触stm32,这些天一直被stm32的串口通信困扰,先是收不到数据,后来收到数据了但printf函数不能通过串口发送到上位机。问题来自于多方面,有硬件上的也有软件上的,我在这里总结一下,希望能对大家有帮助避免踩坑。 我使用的是stm32f103rct6的板子,使用hal库在turestudio9.3环境下开发,主机是ubuntu18系统。 硬件问题: 1、有一个usb转ttl线有问题,短接rx和tx后,用cutecom发送和接收的数据会不一致,换成其他线则没问题。 2、usb转ttl线的rx端要接stm32板子的rx端,usb转ttl线的rx端接板子的tx端,我之前刚好是rx-rx、tx-tx,因些收不到数据。
[单片机]
<font color='red'>stm32</font>使用usart1串口通信以及truestudio重定向printf踩的坑
STM32 Keil下编程实现LED灯点亮与闪烁
前言 基于STM32F10C8T6在Keil下编程实现LED灯点亮与闪烁 一、原理图 1.STM32F103C8T6 STM32F103C8T6是ST在2007年发布的一款MCU,截止目前ST已经发布了速度高达400MHz的STM32H7。三点好处,一是封装比较大,方便初学者焊接,二是价格低廉,学习成本比较低,三是网上有大量的资料供初学者学习使用。 2.LED2 LED2控制一个绿色通讯指示灯,管脚为PB3。 3.LED3 LED3控制8个小灯点亮,管脚为PC13。 二、Keil代码 1.Led.h 代码如下: #ifndef _LED_H_ #define _LED_H_ #include stm32f10x
[单片机]
<font color='red'>STM32</font> Keil下编程实现LED灯点亮与闪烁
做单片机设计,STM32 GPIO外部中断你必须知道
一、STM32中断分组: STM32 的每一个GPIO都能配置成一个外部中断触发源,这点也是 STM32 的强大之处。STM32 通过根据引脚的序号不同将众多中断触发源分成不同的组,比如:PA0,PB0,PC0,PD0,PE0,PF0,PG0为第一组,那么依此类推,我们能得出一共有16 组,STM32 规定,每一组中同时只能有一个中断触发源工作,那么,最多工作的也就是16个外部中断。STM32F103 的中断控制器支持 19 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F103 的19 个外部中断为: 线 0~15:对应外部 IO 口的输入中断。 线 16:连接到 PVD 输出。
[单片机]
做单片机设计,<font color='red'>STM32</font> GPIO外部中断你必须知道
stm32 iap跳转isp实现上电isp下载
概述:本程序上电等3秒种,期间每隔10ms检测一次串口是否收到握手指令(0x7F),若收到则跳转到isp程序执行。可以用mcuisp软件通过串口1直接升级程序。是一种简单的iap程序。初次下载需要手动让mcu进入isp模式。 1先看看最终效果:成功! 2.实现isp跳转 stm32的isp程序在系统存储区,可以通过boot0,boot1引脚选择,也可以通过程序直接跳转,isp程序的入口地址为 0x1FFFF000 跳转函数如下: #define ISP_ADDR 0x1FFFF000 void IspProgramRun(void) { u32 IspSpInitVal; //IAP
[单片机]
一个简单逆向stm32固件程序的实例分享
本文主要跟大家分享一个简单逆向stm32固件程序的实例,为了让大家在一款成熟的产品中去考虑加密这一块的技术,不然分分钟被别人copy! 1、情景再现 咬金,你们公司固件程序有加密处理吗 ? 额~,算了吧,我们公司的单片机程序炒鸡简单的,还加啥子密。 你这想法不对,假如产品卖得很好,如果没有任何加密措施,那岂不人家随便复制售卖。 没关系吧,反正他们没源码,应该也没那么容易复制吧 一点加密都没有,盗取还是比较简单的。 我才不信~~ 那行,把你的板子给我,不用你的源码,跟你把波特率改了! 直接读取固件 这里以stm32单片机进行演示,如果MCU没有做flash读取或者熔断保护,则可以通过jlink等烧写工具直接读取其Flash上的固
[单片机]
一个简单逆向<font color='red'>stm32</font>固件程序的实例分享
史上最全STM32调试步骤!
STM32调试步骤 调试前,首先跳线J9的2-3脚短接,跳线J4的右边两个脚短接,跳线J5短接,J8短接,J2的1-2短接,3-4短接。 将Jlink与学习板,USB延长线与学习板,串口延长线与学习板(或者USB转串口线与学习板)连接起来,这时LED灯LED5,LED6都应该点亮,如果不亮,说明板子有问题。 打开串口助手,按照如下设置: 1、从桌面打开J-Flash ARM V4.02如图所示。 也可以按照如下顺序打开J-Flash ARM 。 “开始à程序àSEGGERàJ-Link ARM V4.02àJ-Flash ARM”,如下图所示。 2、打开J-Flash ARM后,先进行芯片选项设置,打开Optionsà
[单片机]
史上最全<font color='red'>STM32</font>调试步骤!
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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