历史上的今天
返回首页

历史上的今天

今天是:2025年08月09日(星期六)

2019年08月09日 | STM32 I2C从机发送数据

2019-08-09 来源:eefocus

stm32 I2C主机发送功能在网上有很多例程,调试也很容易,从机发送数据的介绍很少,鉴于本人刚开始调试单片机程序不久,花了一整天也没调试通,最后在部门领导的帮助下,通过示波器及单步调试程序,查看寄存器状态值,终于实现了。下面主要介绍下从机发送数据注意事项。(附带例程)


1、相信使用I2C的都知道,从机是不能主动发送数据的,开始条件都是由主机生成。


2、根据参考文档,I2C默认是从机模式,只有在生成起始条件后,才会从从机模式自动切换到主机模式。所以作为从机模式使用时不能生成起始条件。


3、附图1,为从机发送器的传送时序图,截图于参考手册,图中说明部分讲述了怎样能清除事件,这点很重要,每次事件是都要去清除才可以去检测下一个事件,否则很可能检测不到其他事件,我当初就是死在这上面的。


4、需要的数据发送完成了,根据附图1和2可知,主机需要发送一个NACK,从机有NACK应答,当你以为这样可以的时候就大错特错了。文档中有句话,要在读倒数第二个字节之后发送,而非所有字节发送完成再发送。仔细看附图1中说明部分[EV3:TxE=1,写DR将清除该事件;移位寄存器非空。],如果不清除该事件的话,那对不起,你根本就检测不到EV3-2事件。所以在需要传输的数据传输完时,再向DR寄存器中写入一个数据后发送NACK就行了,这个数据是我们不需要的,写什么不重要,目的是为了清除事件(EV3)罢了(也可以在最后一个数据之前发送NACK,就不需要写入无用数据了,这里没有测试其他办法就不赘述了)


附图1:从发送器的传送序列图


附图2: 主接收器传送序列图

附bsp_i2c.c源码:


/**  ******************************************************************************


  * @file   bsp_i2c.c


  * @author Eason


  * @version V1.0


  * @date   2014-09-15


  * @brief  I2C应用函数接口


 ******************************************************************************


  * @attention


  *


  * 实验平台:野火 iSO-MINISTM32 开发板


  * 固件库版本V3.5


 *****************************************************************************


  */


 


#include"bsp_i2c.h"


 


/* Private typedef-----------------------------------------------------------*/


typedef enum{FAILED = 0, PASSED = !FAILED} TestStatus;


 


/* Private define------------------------------------------------------------*/


#defineI2C1_SLAVE_ADDRESS7     0x30


#defineI2C2_SLAVE_ADDRESS7     0x24


#defineBufferSize              4


#defineClockSpeed              300000


 


/* Privatevariables ---------------------------------------------------------*/


u8I2C1_Buffer_Tx[BufferSize] = {1,20,50,100};


u8I2C2_Buffer_Rx[BufferSize] = {0};


u8 TxIdx = 0,RxIdx = 0;


volatileTestStatus TransferStatus = FAILED;


 


/******************************************************************************


* FunctionName  : I2C_GPIO_Config


* Description     : Configuresthe different GPIO ports.


* Input          : None


* Output         : None


* Return         : None


******************************************************************************/


void I2C_GPIO_Config(void)


{


       GPIO_InitTypeDefGPIO_InitStructure;


 


         /* Enable peripheral clocks--------------------------------------------------*/


       /*GPIOB Periph clock enable */


       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


       /*I2C1 and I2C2 Periph clock enable */


       RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1| RCC_APB1Periph_I2C2, ENABLE);


 


       /*Configure I2C1 pins: SCL and SDA ----------------------------------------*/


       GPIO_InitStructure.GPIO_Pin=  GPIO_Pin_6 | GPIO_Pin_7; //选择待设置的GPIO管脚


       GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;      //管脚速率50MHz


       GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_OD;  //复用开漏输出


       GPIO_Init(GPIOB,&GPIO_InitStructure);


 


       /*Configure I2C2 pins: SCL and SDA ----------------------------------------*/


       GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10 | GPIO_Pin_11;


       GPIO_Init(GPIOB,&GPIO_InitStructure);


}


 


/******************************************************************************


* FunctionName  : I2C_Master_Init


* Description    : I2C Master initialize.


* Input          : None


* Output         : None


* Return         : None


******************************************************************************/


voidI2C_Master_Init(void)


{


         I2C_InitTypeDef  I2C_InitStructure;


         /* I2C1 configuration------------------------------------------------------*/


       I2C_InitStructure.I2C_Mode= I2C_Mode_I2C;


       I2C_InitStructure.I2C_DutyCycle= I2C_DutyCycle_2;


       I2C_InitStructure.I2C_OwnAddress1= I2C1_SLAVE_ADDRESS7;


       I2C_InitStructure.I2C_Ack= I2C_Ack_Enable;


       I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;


       I2C_InitStructure.I2C_ClockSpeed= ClockSpeed;


       I2C_Init(I2C1,&I2C_InitStructure);


 


         I2C_Cmd(I2C1, ENABLE);


}


 


/******************************************************************************


* FunctionName  : I2C_Slave_Init


* Description     :I2C Slave initialize.


* Input          : None


* Output         :None


* Return         : None


******************************************************************************/


voidI2C_Slave_Init(void)


{


         I2C_InitTypeDef  I2C_InitStructure;


       /*I2C2 configuration ------------------------------------------------------*/


         I2C_InitStructure.I2C_Mode =I2C_Mode_I2C;


       I2C_InitStructure.I2C_DutyCycle= I2C_DutyCycle_2;


       I2C_InitStructure.I2C_OwnAddress1= I2C2_SLAVE_ADDRESS7;


       I2C_InitStructure.I2C_Ack= I2C_Ack_Enable;


       I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;


       I2C_InitStructure.I2C_ClockSpeed= ClockSpeed;


       I2C_Init(I2C2,&I2C_InitStructure);


        


         I2C_Cmd(I2C2, ENABLE);


}


 


voidI2C_Mode_Init(void)


{


         I2C_GPIO_Config();


         I2C_Master_Init();


         I2C_Slave_Init();


}


/******************************************************************************


* FunctionName  : Buffercmp


* Description    : Compares two buffers.


* Input          : - pBuffer1, pBuffer2: buffers to becompared.


*                : - BufferLength: buffer'slength


* Output         : None


* Return         : PASSED: pBuffer1 identical topBuffer2


*                  FAILED: pBuffer1 differs frompBuffer2


******************************************************************************/


TestStatusBuffercmp(u8* pBuffer1, u8* pBuffer2, u16 BufferLength)


{


  while(BufferLength--)


  {


    if(*pBuffer1 != *pBuffer2)


    {


      return FAILED;


    }


   


    pBuffer1++;


    pBuffer2++;


  }


 


  return PASSED; 


}


 


/*******************************************************************************


* FunctionName  : I2C_Transmission


* Description    : Data Transmission.


* Input          : None


* Output         : None


* Return         : None


******************************************************************************/


voidI2C_Transmission(void)


{


  I2C_GenerateSTART(I2C1, ENABLE);


  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));  


  /* Send I2C2 slave Address for read*/


  I2C_Send7bitAddress(I2C1,I2C2_SLAVE_ADDRESS7, I2C_Direction_Receiver);


 


    /* Test on I2C2 EV1 and clear it */


  while(!I2C_CheckEvent(I2C2,I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED));


    /* Test on I2C1 EV6 and clear it */


  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));


 


  /* Send data */


  while (RxIdx < BufferSize)


  {


    /* Send I2C2 data */


    I2C_SendData(I2C2,I2C1_Buffer_Tx[TxIdx++]);


   /* Test on I2C1 EV7 and clear it */


         while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED));


         while(!I2C_CheckEvent(I2C2,I2C_EVENT_SLAVE_BYTE_TRANSMITTED)); 


    /* Store received data on I2C1 */


    I2C2_Buffer_Rx[RxIdx++] =I2C_ReceiveData(I2C1);         


  }


  I2C_SendData(I2C2, 0);  //此处写DR寄存器目的是为了清除EV3事件


 


  RxIdx = 0;


  TxIdx = 0;


  I2C_AcknowledgeConfig(I2C1,DISABLE); //主机发送NACK 


  while(!I2C_CheckEvent(I2C2,I2C_EVENT_SLAVE_ACK_FAILURE));


  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED));


    /*Clear I2C2 AF flag */


  I2C_ClearFlag(I2C2, I2C_FLAG_AF);


 


  /* Send I2C1 STOP Condition */


  I2C_GenerateSTOP(I2C1, ENABLE);


 


  /* Check the corectness of written data */


  TransferStatus = Buffercmp(I2C1_Buffer_Tx,I2C2_Buffer_Rx,BufferSize);


}



推荐阅读

史海拾趣

DCX-CHOL Enterprises公司的发展小趣事

为了进一步拓展业务领域和提升竞争力,DCX-CHOL Enterprises开始积极探索跨界合作的可能性。公司与多家知名企业和研究机构建立了战略合作关系,共同开展技术研发和产品创新。通过跨界合作,DCX-CHOL Enterprises不仅获得了更多的技术支持和市场资源,还拓宽了视野和思路,为公司未来的发展注入了新的活力。同时,公司还积极参与行业协会和组织的活动,加强与其他企业的交流与合作,共同推动电子行业的发展和进步。

Abbatron公司的发展小趣事

在电子行业的发展过程中,Abbatron公司也面临着各种挑战和危机。某年,由于原材料价格的大幅上涨和市场竞争的加剧,公司面临着巨大的经营压力。然而,Abbatron公司凭借其强大的研发实力和敏锐的市场洞察力,及时调整战略,通过优化生产流程和降低成本等措施,成功度过了危机。

ENERGIZER公司的发展小趣事

ENERGIZER公司,作为全球领先的电池制造商,自1896年以来就以其技术革新引领着电池能源行业的发展。1896年,ENERGIZER发明了全球第一粒电池,这一创新性的产品为电子行业的发展奠定了基石。随着科技的不断进步,ENERGIZER的电池技术也在不断革新,以满足日益增长的电子设备需求。如今,ENERGIZER的电池已经广泛应用于各种电子设备中,从遥控器、手电筒到高端电子设备,其优良的品质和耐用的性能受到了全球消费者的广泛认可。

国兴(GOODSKY)公司的发展小趣事

近年来,随着智能设备的普及,消费者对电池的需求也日益增长。为了满足这一需求,ENERGIZER不断加大研发投入,推出了一系列高性能的电池产品。其中,高能量锂电池的研发成功,为智能设备的发展提供了强有力的支持。这款电池具有高能量密度、长寿命等优点,广泛应用于智能手机、平板电脑等高端电子设备中。同时,ENERGIZER还不断优化生产工艺,降低成本,为消费者提供更优质、更实惠的电池产品。

这些故事展示了ENERGIZER公司在电子行业发展中的创新力和市场洞察力,也体现了该公司对环保和消费者需求的关注与响应。作为全球领先的电池制造商,ENERGIZER始终保持着技术创新的步伐,为全球电子行业的发展做出了重要贡献。

First Silicon Co., Ltd公司的发展小趣事

在电子行业快速发展的同时,环保问题也日益受到关注。First Silicon公司积极响应国家环保政策,将环保理念融入企业发展战略之中。公司投入巨资研发环保型半导体材料和生产工艺,努力减少生产过程中的污染排放和资源浪费。同时,公司还积极推广绿色电子产品,引导消费者树立环保意识。通过这一系列的环保举措,First Silicon不仅赢得了社会各界的广泛赞誉,还为其可持续发展奠定了坚实基础。

FDK AMERICA公司的发展小趣事

随着环保意识的日益增强,FDK AMERICA公司积极响应绿色制造理念。公司投入大量资金研发环保型电子产品,采用环保材料和绿色生产工艺,减少对环境的影响。同时,公司还积极参与各类环保活动,倡导绿色消费理念。这种环保举措不仅提升了公司的社会形象,也为公司赢得了更多消费者的青睐。

问答坊 | AI 解惑

基站被盗的几大理由

  基站即公用移动通信基站,是无线电台站的一种形式,是指在一定的无线电覆盖区中,通过移动通信交换中心,与移动电话终端之间进行信息传递的无线电收发信电台. 移动通信基站的建设是我国移动通信运营商投资的重要部分,根据媒体的报道,中国移 ...…

查看全部问答>

遇到大麻烦了,PB6.0在编译wince系统的时候 在NETCFV2_MODULES= dotnetv2处失败了。

一下子就没了头绪,网上只有问的却没有解答的。google了快一天了,没有答案。 编译日志如下 配置sysgen参数 User selected the following SYSGEN variables sysgen_as_base=1 sysgen_as_file=1 sysgen_audio=1 sysgen_auth=1 ............. ...…

查看全部问答>

SDIO自动被关闭

最近移植了下个SDIO驱动, 我把SD插进去的时候没有反应,于是我就插上去才开机, 从终端查看发现内核启动后已经识别到我的SD卡了,并且可以显示出我的SD卡的大小, 但是在最后出现一句 power down 有没有知道为什么SD驱动开启后自动关闭电源啊 ...…

查看全部问答>

如何禁止系统创建新的系统服务?

在文件过滤驱动中,如何禁止系统创建新的系统服务? 原来是想将注册表HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services设为只读, 但是发现系统有时会自己修改这个位置,比如说插入一个U盘, 设为只读会导致蓝屏。 所以问问大家,如何 ...…

查看全部问答>

创造力,幻想,臆想天开 - 大家可以得出自己的结论

100件未来式设计方案,是否反映出想象力和现实的矛盾呢?作出你自己的判断吧http://www.webdesignerdepot.com/2009/04/100-amazing-futuristic-design-concepts-w-wish-were-real/…

查看全部问答>

【招聘】 嵌入式兼职招聘(限西安)

因公司业务发展,急需招聘兼职人员,要求: 1、具有DSP/FPGA/ARM/CPLD方面的研发经验,熟悉数字电路、模拟电路设计; 2、熟悉嵌入式系统设计,熟练掌握C语言; 3、有4层以上PCB板设计经验,熟悉PCB板的设计规范; 4、对电磁兼容设计有了解; 5 ...…

查看全部问答>

PIC24FJ64GA002 引脚控制

怎么让一个管脚先为高电平  延时一会  再让另一个管脚也为高?我用延时做的   为什么先置高的就是高 延时后置高的就不能置高呢?…

查看全部问答>

应琳子姐的邀请,写一个DIY成果展示文档的例子

因为只做过一个DIY 只好炒冷饭了。 这个是我觉得DIY完成后该做的一个文档的一个大致内容。 它的目的只有一个 以简洁的方式,让读者了解整个设计的目标,优缺点,以及无需太多个人创造性工作即可原样复制该DIY; 曾经的那个收官资料,只是资 ...…

查看全部问答>

DIY一个LED小应急灯电路图

这里介绍一个纽扣电池供电的LED灯电路 主要控制芯片是比较常用NE555,纽扣电池供电,其他器件图上标的相当的清楚了,,就是连接电路是有点麻烦,可以自己腐蚀一块电路板,, …

查看全部问答>