STM32之I2C模块调试总结

发布者:lidong4069最新更新时间:2019-04-01 来源: eefocus关键字:STM32  I2C模块  调试总结 手机看文章 扫描二维码
随时随地手机看文章

 前一段时间对STM32的I2C模块进行了调试,今天做一个总结。关于I2C协议的知识,这里就不再赘述,网上有很多介绍I2C协议的文章。目前实现I2C协议的方式有两种,一是采用GPIO口来模拟I2C协议,另外一种是使用STM32自带的I2C模块。虽说使用GPIO口模拟I2C协议较为复杂,需要详细了解I2C协议的内容,但是实现这种方式的资料也非常多,网上都有对应的源码实现,只需要简单修改,就可以实现功能。而针对使用STM32自带的I2C模块,网络上贬斥的声音较多,说是模块本身自带bug,容易出问题,甚至还有人说是史上最难调的I2C模块。当然了,这些问题我自己目前还没有遇到,可能需要以后来验证了。好了,言归正传,今天主要记录一下调试过程以及需要注意的地方。      


//功能:初始化IIC接口

void SSX1207_Init(void)

{

 GPIO_InitTypeDef  GPIO_InitStructure;

 I2C_InitTypeDef   I2C_InitStructure;

 

 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟

 //GPIOB6,B7初始化设置,PB6=SCL,PB7=SDA

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用模式

 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏模式

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz

 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//

 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化


 GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_I2C1);//将PB6连接到SCL

 GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_I2C1);//将PB7连接到SDA


 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//使能I2C1时钟

 //配置I2C参数

 I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;

 I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;

 I2C_InitStructure.I2C_ClockSpeed=I2C_Standard_Speed;

 I2C_InitStructure.I2C_OwnAddress1 = 0x00;

 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;

 I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

 I2C_Init(I2C1,&I2C_InitStructure);//初始化

 I2C_Cmd(I2C1,ENABLE);//使能I2C

}


该函数完成对I2C模块的初始化,首先要确定使用的GPIO口,然后对GPIO口的参数进行配置,这一部分也是参数网上的一下资料进行配置的,需要注意的是GPIO_Mode要配置成复用模式,GPIO_OType要配置成开漏模式,如果使用别的库可能对应的参数有差异,但基本功能应该是一样的,需要将GPIO口配置成复用开漏模式。上面只是对要使用到的GPIO口的配置,还需要配置I2C的参数,这部分只需要注意一下OwnAddress1即可,该参数只需要跟总线上的其他I2C设备的地址不一样就行了,是用户自己定义的。


    初始化函数记录完成之后,下面记录主I2C设备对从I2C设备写数据的过程。


//功能:将指定数量的数据写入到IIC设备中

//参数:

//  pBuffer--要写入的数据数组

//  NumToWrite--写入数据的个数

void SSX1207_WriteByteArray(u8 *pBuffer,u16 NumToWrite)

{

 uint32_t flag=0;

 I2C_AcknowledgeConfig(I2C1,ENABLE);//ACK置1

 I2C_GenerateSTART(I2C1,ENABLE);//产生一个启动信号

 timeout=0x1000;

 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))//EV5事件

 {

  if((--timeout)==0)

  {

   printf("EV5 fail\r\n");

   break;

  } 

 }

 flag = I2C_GetLastEvent(I2C1);

 printf("flag=%x\r\n",flag);

 if(timeout!=0)

 printf("EV5 sucess\r\n");

 I2C_Send7bitAddress(I2C1,0X50,I2C_Direction_Transmitter);//发送安全芯片地址和写命令

 timeout=0x1000;

 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))//EV6事件

 {

  if((--timeout)==0)

  {

   printf("EV6 fail\r\n");

   break;

  } 

 }

 flag = I2C_GetLastEvent(I2C1);

 printf("flag=%x\r\n",flag);

 if(timeout!=0)

 printf("EV6 sucess\r\n");

 while(NumToWrite--)

 {

  timeout=0x1000;

  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING))//EV8事件

  {

   if((--timeout)==0)

   {

   printf("EV8 fail\r\n");

   break;

   } 

  }

  flag = I2C_GetLastEvent(I2C1);

  printf("flag=%x\r\n",flag);

  if(timeout!=0)

  printf("EV8 sucess\r\n");

  I2C_SendData(I2C1,*pBuffer);

  pBuffer++;

 }

 timeout=0x1000;

 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))//EV8_2事件

 {

  if((--timeout)==0)

  {

   printf("EV8_2 fail\r\n");

   break;

  } 

 }

 flag = I2C_GetLastEvent(I2C1);

 printf("flag=%x\r\n",flag);

 if(timeout!=0)

 printf("EV8_2 sucess\r\n");

 I2C_GenerateSTOP(I2C1,ENABLE);//产生一个停止信号

}


该函数是将指定数量的数据写入到I2C从设备中,整个流程是参照手册的上的流程来编码的。


//功能:从IIC设备读取指定长度的数据

//参数:

//  pBuffer:要读取数据的存放数组

//  NumToRead:读取数据的数量

void SSX1207_ReadByteArray(u8 *pBuffer,u16 NumToRead)

{

 uint32_t flag=0;

 u16 NumToRead_FLAG=NumToRead;

 

 if(NumToRead_FLAG==8)

 {

  I2C_GenerateSTART(I2C1,ENABLE);//产生一个启动信号

  timeout=0x1000;

  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))//EV5事件

  {

   if((--timeout)==0)

   {

    printf("EV5 fail!\r\n");

    break;

   } 

  }

  flag = I2C_GetLastEvent(I2C1);

  printf("flag=%x\r\n",flag);

  if(timeout!=0)

  printf("EV5 sucess!\r\n");

  I2C_Send7bitAddress(I2C1,0X50,I2C_Direction_Receiver);//发送安全芯片地址和读命令

  timeout=0x1000;

  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))//EV6事件

  {

   if((--timeout)==0)

   {

    printf("EV6 fail!\r\n");

    break;

   } 

  }

  flag = I2C_GetLastEvent(I2C1);

  printf("flag=%x\r\n",flag);

  if(timeout!=0)

  printf("EV6 sucess!\r\n");

 }

 while(NumToRead)

 {

  printf("len=%d\r\n",NumToRead);

  NumToRead--;

  if((NumToRead==2)&&(NumToRead_FLAG!=8))

  {

   I2C_AcknowledgeConfig(I2C1,DISABLE);//ACK位清零 

  }

  if((NumToRead==1)&&(NumToRead_FLAG!=8))

  {

   I2C_GenerateSTOP(I2C1,ENABLE);//产生一个停止信号

  }

  timeout=0x1000;

  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED))//EV7事件

  {

   if((--timeout)==0)

   {

    printf("EV7 fail!\r\n");

    break;

   } 

  }

  *pBuffer=I2C_ReceiveData(I2C1);

  flag = I2C_GetLastEvent(I2C1);

  printf("flag=%x\r\n",flag);

  if(timeout!=0)

  printf("EV7 sucess! *pBuffer=%02x\r\n",*pBuffer);

  pBuffer++; 

 }

 printf("NumToRead=%d\r\n",NumToRead);

}


该函数是主设备向从I2C设备读取指定长度的数据。整个流程也是参考手册的流程来实现的。




读取数据的过程主要是注意要读取的数据还剩三个字节的时候CR1寄存器中ACK位和STOP位的变化情况,这里所说的情况是要读取的数据大于两个字节的情况。




如代码中红色标注的所示,此时倒数第三个字节在DR寄存器中,需要将ACK复位,然后读取该字节,但STOP位不能置1(务必要注意这一点,网上有在此时就将STOP位置1的,导致后续的操作失败)。当要读取倒数第二个字节时,将STOP位置1,如代码中紫色标注的部分。另外,为了后续正常读写数据,需要将ACK位再次置1,本次是在写数据函数内实现的。以上是操作I2C设备的三个重要的函数,针对测试的主函数就不再记录了。

关键字:STM32  I2C模块  调试总结 引用地址:STM32之I2C模块调试总结

上一篇:STM32的HAL库的 I2C和UART使用函数
下一篇:STM32 编译指令 #pragma pack 的配对使用

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

ARM开发(3)基于STM32的矩阵键盘控制蜂鸣器
一 矩阵键盘控制蜂鸣器原理: 1.1 本实验实现8*7矩阵键盘上按键控制蜂鸣器响。 1.2 实验思路:根据电路图原理,找出矩阵键盘行列所对应的引脚,赋予对应的按键值,然后控制蜂鸣器响。 1.3 开发环境 : MDK5 库函数版本开发 JLINK仿真 二 实验步骤: 2.1 key.h代码: #ifndef __KEY_H #define __KEY_H #include”sys.h” #define ROWPINS GPIO_Pin_6|GPIO_Pin_5|GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_2 |GPIO_Pin_1|GPIO_Pin_0//矩阵键盘行引脚 #defi
[单片机]
零基础入门STM32定时器配置及其中断设置
  我们大家都知道STM32定时器比较多,但调试都是一样的,寄存器都是一一对应的。就拿tiM2举例说明。在网上搜了好多关于定时器的设置,但大多数都是一个版本,而且都是针对库函数操作的,让人看起来一头雾水,对于初学者很是不利(我也是初学者)。下面我将自己的定时器设置过程一一记录下来,以供大家参考,我们共同学习……   首先定义定时器头文件,也就是定义寄存器以供操作:   #define TIM2_CR1 (*((volatile unsigned long *)0x40000000))   #define TIM2_CR2 (*((volatile unsigned long *)0x40000004))   #define
[单片机]
DSP进行浮点快速傅立叶变换剖析
前言 本文目的是演示如何使用STM32F30x 内部的DSP 进行浮点快速傅立叶变换(FFT),为联系实际应用,使用ADC 对波形发生器进行ADC 采样,然后对ADC 采样结果进行FFT, 与 Matlab 仿真结果进行比较察看最终结果的准确性。会使用到ARMDSP 库文件,以及STM32F30x 的浮点运算单元以及DSP指令等。 模拟ADC采样数据实现FFT 使用Matlab生成AM调制波形 波形公式为:AM_50= sin(2πfc)*(1+50%*sin(2πfm)), 其中fc 为载波频率,fm 为调制波频率,调制比50%。为了使用ADC 采样,将波形进行偏移处理,叠加1.5V 电压,最终波形展开公式如下: AM_50
[单片机]
DSP进行浮点快速傅立叶变换剖析
STM32接口FSMC/FMC难点详解
STM32F767的FMC将外部存储器划分为6个固定大小的256M的存储区域,如下图 STM32F767的FMC 存储块 1(Bank1 )被分为 4个区,每个区管理64M 字节空间,每个 区都有独立的寄存器对所连接储进行配置。Bank1 的 256M 字节空间由 28 根地址线 根地址线(HADDR )寻址。 这里 HADDR 是内部AHB地址总线,其中地址总线HADDR 来自外部存储器地址FMC_A (FMC_A 会接到外部存储器的地址线上,也就是HADDR内部总线的 来自外部存储器地址线), 而 HADDR 对4个区进行寻址。如表 18.1.2.1所示: 比如外部存储器接到FMC_
[单片机]
<font color='red'>STM32</font>接口FSMC/FMC难点详解
STM32—DAC配置
一.DAC介绍 ADC是模数转换器,可以将模拟电压转换位数字信号;DAC是数模转换器,可以将数字信号转换为模拟电压。 STM32F103ZET6内部DAC有2个通道,12位数字输入(也可以配置为8位),可以按要求输出不同的信号波形,其主要特点如下: 2个DAC转换通道 每个通道都有DMA功能 2个通道可以同时转换或者分别转换 输入信号可以是12位或8位 12位输入模式分为:右对齐、左对齐 有同步更新功能 可以生成噪声波形 可以生成三角波形 DAC框图如下: 二.主要寄存器说明 一般使用DAC情况不多,而且使用也就是单纯输出电压,基本用不到STM32输出一些特殊的波形,所以了解一下基本的寄存器就可以配饰DAC了。 D
[单片机]
<font color='red'>STM32</font>—DAC配置
STM32系统芯片,加快LoRa IoT智能设备开发
单片集成STM32微控制器 IP和增强版Semtech射频模块 支持LoRa®等全球低功耗广域网接入 意法半导体工业产品10年生命周期滚动保证 通过智能基础设施及物流、智能工业和智能生活促进世界可持续发展,横跨多重电子应用领域的全球领先的半导体供应商意法半导体(STMicroelectronics,简称ST; 纽约证券交易所代码:STM)展示了全球首款通过长距离无线技术将智能设备连接到物联网(IoT)的LoRa®系统芯片(SoC)。 STM32WLE5 系统芯片使产品开发人员能够创建远程环境传感器、仪表、跟踪器和过程控制器等设备,帮助企业有效地管理能源和资源的使用情况。 该系统芯片在一个易于使用的单片产品内整合
[单片机]
<font color='red'>STM32</font>系统芯片,加快LoRa IoT智能设备开发
基于STM32的解魔方机器人设计方案
方案设计 采用舵机作为魔方的驱动电机,从舵机的驱动原理可知:舵机运行的速度和的主频没有关系,所以采用和采用更高主频的相比在控制效果上没有什么差别。过程简单,非常容易上手,而且不需要进行的移植,非常适合对魔方机器人的舵机进行控制。 2.复原时间是魔方机器人的一个非常重要,可以说是最为重要的一个参数,本文的软件设计中涉及到了大量的,如 Kocemba 复原算法和 KNN 分类算法等,而控制器主频对于算法运行时间的长短起着决定性的作用。 所以在本文的方案设计中,我们把核心算法全部交给 Allwinner A20 运行的 APP。 设计原理 1、Kociemba算法 Kociemba算法,又称为二阶段
[机器人]
STM32串口通信过程详解
按照数据传送方向分类: 单工:数据传输只支持数据在一个方向上传输; 半双工:允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口; 全双工:允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。 分别如下图中的a、b、c所示: 按照通信方式分类: 同步通信:带时钟同步信号传输。比如:SPI,IIC通信接口; 异步通信:不带时钟同步信号。比如:UART(通用异步收发器),单总线; 在同步通讯中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数
[单片机]
<font color='red'>STM32</font>串口通信过程详解
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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