【STM32 IIC详解】stm32 IIC从机模式(中断方式收发数据)

您所在的位置:网站首页 i2c的地址模式 【STM32 IIC详解】stm32 IIC从机模式(中断方式收发数据)

【STM32 IIC详解】stm32 IIC从机模式(中断方式收发数据)

2024-07-16 14:12| 来源: 网络整理| 查看: 265

https://blog.csdn.net/liwei16611/article/details/75258222

1、IIC简介

 

第二节代码会用到该部分内容,对于IIC来说,从机是不能主动发送数据的,开始条件都是由主机生成。

 

 

 

  1.1、主机发送数据流程

 

  1) 主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始   2) 主机接着发送一个命令字节。该字节由 7 位的外围器件地址和 1 位读写控制位 R/W组成(此时 R/W=0)   3) 相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0)   4) 主机收到从机的应答信号后开始发送第一个字节的数据   5) 从机收到数据后返回一个应答信号 ACK   6) 主机收到应答信号后再发送下一个数据字节   7) 当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信

 

 

 

 

 

  1.2、主机接收数据流程

 

  1) 主机发送启动信号后,接着发送命令字节(其中 R/W=1)   2) 对应的从机收到地址字节后,返回一个应答信号并向主机发送数据   3) 主机收到数据后向从机反馈一个应答信号   4) 从机收到应答信号后再向主机发送下一个数据   5) 当主机完成接收数据后,向从机发送一个“非应答信号(ACK=1)”,从机收到ASK=1 的非应答信号后便停止发送   6) 主机发送非应答信号后,再发送一个停止信号,释放总线结束通信

 

 

 

 

 

  1.3、处理器的I2C模块会在如下所述的情况产生中断信号

 

  RX_UNDER   当处理器通过IC_DATA_CMD寄存器读取接收缓冲器为空时置位   RX_OVER    当接收缓冲器被填满,而且还有数据从外设发送过来时被置位;缓冲器被填满后接收的数据将会丢失   RX_FULL    当接收缓冲器达到或者超过IC_RX_TL寄存器中规定的阈值时被置位;当数据低于阈值时标志位将被自动清除   TX_OVER    当发送缓冲器被填满,而且处理器试图发送另外的命令写IC_DATA_CMD寄存器时被置位   TX_EMPTY   当发送缓冲器等于或者低于IC_TX_TL寄存器中规定的阈值时被置位;当数据高于阈值时标志位将被自动清除   TX_ABRT    当i2c模块无法完成处理器下达的命令时被置位,有如下几种原因:                           * 发送地址字节后没有从机应答                           * 地址识别成功后主机发送的数据从机没有应答                           * 当i2c模块只能作为从机时试图发送主机命令                           * 当模块的RESTART功能被关闭,而处理试图完成的功能必须要RESTART功能开启才能完成                           * 高速模块主机代码被应答                           * START BYTE被应答                           * 模块仲裁失败                           无论标志位什么时候被置位,发送缓冲器和接收缓冲器的内容都会被刷新   ACTIVITY   表明i2c模块正在活动,这个标志位将会一直保持直到用以下4种方式清除:                           * 关闭i2c                           * 读取IC_CLR_ACTIVITY寄存器                           * 读取IC_CLR_INTR寄存器                           * 系统重启                           即使i2c模块是空闲的,这个标志仍然需要被置位直到被清除,因为这表明i2c总线上有数据正在传输   需要用到的:     RD_REQ     当i2c模块作为从机时并且另外的主机试图从本模块读取数据时被置位     RX_DONE    当i2c模块作为从机发送数据时,如果主机没有应答则置位;这种情况发生在i2c模块发送最后一个字节数据时,表明传输结束   STOP_DET   表明i2c总线上产生了STOP信号,无论模块作为主机还是从机   START_DET  表明i2c总线上产生了START信号,无论模块作为主机还是从机

 

 

 

 

 

  2、IIC从机中断收发函数

 

// 从机收发函数处理 void I2C1_EV_IRQHandler(void) {   __IO uint16_t SR1Register =0;   __IO uint16_t SR2Register =0;

  SR1Register = I2C1->SR1;           // 通过读取 SR1/2 获取 IIC 状态   SR2Register = I2C1->SR2;

  // 从机发送   // 判断IIC是从机模式 - 最低位(MSL = 0)   if((SR2Register & 0x0001) != 0x0001)   {     // ADDR:根据状态判断获取从机IIC地址成功     if((SR1Register & 0x0002) == 0x0002)     {       // 清除标志,准备接收数据       SR1Register = 0;       SR2Register = 0;  TrCount= 0;     }

//TxE:根据状态标志可以发送数据 if((SR1Register & 0x0080) == 0x0080) {  SR1Register = 0;       SR2Register = 0;  I2C1->DR =TrCount ++; }

//STOPF:监测停止标志 if((SR1Register & 0x0010) == 0x0010) {  I2C1->CR1 |= CR1_PE_Set;       SR1Register = 0;       SR2Register = 0;  TrCount= 5; }

//TIME_OUT if((SR1Register & 0x4000) == 0x4000) {  I2C1->CR1 |= CR1_PE_Set;  SR1Register = 0;       SR2Register = 0; }   }       // IIC从机接收   // 判断IIC是从机模式 - 最低位(MSL = 0)   if((SR2Register &0x0001) != 0x0001)   {     // 收到主机发送的地址:(ADDR = 1: EV1)     if((SR1Register & 0x0002) == 0x0002)     {       // 清除标志,准备接受数据       SR1Register = 0;       SR2Register = 0;       Rx_Idx = 0;     }          // 接收数据 (RXNE = 1: EV2)     if((SR1Register & 0x0040) == 0x0040)     {       Buffer_Rx[Rx_Idx++] = I2C1->DR;       SR1Register = 0;       SR2Register = 0;     }          // 监测停止条件 (STOPF =1: EV4)     if(( SR1Register & 0x0010) == 0x0010)     {       I2C1->CR1 |= CR1_PE_Set;       SR1Register = 0;       SR2Register = 0;       Flag_RcvOK = 1;                              }   } }

 

 

 

 

 

  3、代码参考实例

 

//stm32f10x_it.c

#include "stm32f10x_it.h" #include "stdio.h"

extern u32 BufferSize ; extern u8 I2C1_ADDRESS ; extern u8 I2C2_ADDRESS ; extern vu8 I2C1_Buffer_Tx[]; extern vu8 I2C2_Buffer_Rx[]; vu32 Tx_Counter = 0; vu32 Rx_Counter = 0; vu32 show_counter1 = 0; vu32 show_counter2 = 0;

// I2C1 作为主机,用于中断接收从机数据 void I2C1_EV_IRQHandler(void) {   show_counter1++;   if(show_counter1 > 1000000)   {     show_counter1 = 0;     printf("\r\n The I2C1 LastEvent is %x \r\n", I2C_GetLastEvent(I2C1));   }   switch(I2C_GetLastEvent(I2C1))   {     case I2C_EVENT_MASTER_MODE_SELECT: // 已发送启始条件     {  // 七位地址发送       I2C_Send7bitAddress(I2C1, I2C2_ADDRESS, I2C_Direction_Receiver);       printf("\r\n The I2C1 is ready \r\n");       break;     }     case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: // 已发送从机地址     {       printf("\r\n The slave address is %x \r\n", I2C_ReceiveData(I2C1));       break;     }     case (I2C_EVENT_MASTER_BYTE_RECEIVED | (I2C_FLAG_BTF & 0x0f)): // 第一个数据已接收     {       // 要接收最后一个字节前先关总线,不然总线锁死       I2C_GenerateSTOP(I2C1,ENABLE);       printf("\r\n The I2C1 has received data2 %x \r\n", I2C_ReceiveData(I2C1));       printf("\r\n The I2C1 is finish \r\n");       break;     }     case 0x40:     {       // 接收了两个同样的数据,没有这个释放不了 RXNE       I2C_ReceiveData(I2C1);     } default: {break;}   } }

// I2C2 用于从机发送数据到主机 void I2C2_EV_IRQHandler(void) {   show_counter2++;   if(show_counter2 > 100000)   {     show_counter2 = 0;     printf("\r\n The I2C2 LastEvent is %x \r\n", I2C_GetLastEvent(I2C2));   }   switch(I2C_GetLastEvent(I2C2))   {     // 收到匹配的地址数据     case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:     {       printf("\r\n The I2C2 is ready \r\n");       I2C_GenerateSTOP(I2C2, DISABLE);       break;     }     case I2C_EVENT_SLAVE_BYTE_TRANSMITTING: //发送数据     {       printf("\r\n The I2C2 transmits is transmitting \r\n");       I2C_SendData(I2C2, 0xb6 + Rx_Counter);       break;

 

 

 

 

    }

 

   // 发送数据,要发送,不然锁死,不过 master 没收到

    case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:     {

      printf("\r\n The I2C2 transmits one byte \r\n");

     I2C_SendData(I2C2, 0xb6 + (Rx_Counter++));

      break;     }     case I2C_EVENT_SLAVE_STOP_DETECTED: //收到结束条件     {       printf("\r\n The I2C2 is finish \r\n");       I2C_ClearFlag(I2C2,I2C_FLAG_STOPF);       I2C_GenerateSTOP(I2C2, ENABLE);       break;     }     default: {break;}   } }

/*---------------------------------------------------------------------------------------------------- 名称: I2C 测试 24C02 测试 编写: mingzhang.zhao 内容:测试 stm32f103vct6 的硬件 I2C 实现中断收发数据 注意事项: 1.USART1: PA9 为 TX, PA10 为 RX I2C1: PB6 为 SCL, PB7 为 SDA I2C2: PB10 为 SCL, PB11 为 SDA ----------------------------------------------------------------------------------------------------*/ #include "stm32f10x.h" #include "stdio.h" #define PRINTF_ON 1 void RCC_Configuration(void);void GPIO_Configuration(void); void USART_Configuration(void); void I2C_Configuration(void); void NVIC_Configuration(void); void Delay(__IO uint32_t t); u8 I2C1_ADDRESS = 0x30; //7 位 I2C 地址 u8 I2C2_ADDRESS = 0x31; #define Size 4 vu8 I2C1_Buffer_Tx[Size] = {1,2,3,4}; vu8 I2C2_Buffer_Rx[Size] = {0}; u32 BufferSize = Size ;

int main(void) {   RCC_Configuration();   GPIO_Configuration();   USART_Configuration();   I2C_Configuration();   NVIC_Configuration();   I2C_GenerateSTART(I2C1,ENABLE);   while(1)   {     Delay(1000);     I2C_GenerateSTART(I2C1,ENABLE); //I2C1 循环读取数据   } }

// 初始化和配置相关 void I2C_Configuration(void) {   I2C_InitTypeDef I2C_InitStructure;   I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;   I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;   I2C_InitStructure.I2C_OwnAddress1 = I2C1_ADDRESS;   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;   I2C_InitStructure.I2C_ClockSpeed = 200000;I2C_Init(I2C1,&I2C_InitStructure);   I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;   I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;   I2C_InitStructure.I2C_OwnAddress1 = I2C2_ADDRESS;   I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;   I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;   I2C_InitStructure.I2C_ClockSpeed = 200000;   I2C_Init(I2C2,&I2C_InitStructure);   I2C_ITConfig(I2C1,I2C_IT_EVT|I2C_IT_BUF,ENABLE);   I2C_ITConfig(I2C2,I2C_IT_EVT|I2C_IT_BUF,ENABLE);   I2C_Cmd(I2C1,ENABLE);   I2C_Cmd(I2C2,ENABLE); }

void NVIC_Configuration(void) {   NVIC_InitTypeDef NVIC_InitStructure;   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);   NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   NVIC_Init(&NVIC_InitStructure);   NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   NVIC_Init(&NVIC_InitStructure); }

void GPIO_Configuration(void) {   GPIO_InitTypeDef GPIO_InitStructure;   //初始化 I2C1GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;   GPIO_Init(GPIOB , &GPIO_InitStructure);   //初始化 I2C2   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;   GPIO_Init(GPIOB , &GPIO_InitStructure);   //初始化 USART1   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   GPIO_Init(GPIOA , &GPIO_InitStructure);   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   GPIO_Init(GPIOA , &GPIO_InitStructure); }

void RCC_Configuration(void) {   /* 定义枚举类型变量 HSEStartUpStatus */   ErrorStatus HSEStartUpStatus;   /* 复位系统时钟设置*/   RCC_DeInit();   /* 开启 HSE*/   RCC_HSEConfig(RCC_HSE_ON);   /* 等待 HSE 起振并稳定*/   HSEStartUpStatus = RCC_WaitForHSEStartUp();   /* 判断 HSE 起是否振成功,是则进入 if()内部 */   if(HSEStartUpStatus == SUCCESS)   {   /* 选择 HCLK(AHB)时钟源为 SYSCLK 1 分频 */   RCC_HCLKConfig(RCC_SYSCLK_Div1);   /* 选择 PCLK2 时钟源为 HCLK(AHB) 1 分频 */   RCC_PCLK2Config(RCC_HCLK_Div1);   /* 选择 PCLK1 时钟源为 HCLK(AHB) 2 分频 */RCC_PCLK1Config(RCC_HCLK_Div2);   /* 设置 FLASH 延时周期数为 2 */   FLASH_SetLatency(FLASH_Latency_2);   /* 使能 FLASH 预取缓存 */   FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);   /* 选择锁相环(PLL)时钟源为 HSE 1 分频, 倍频数为 9,则 PLL 输出频率为 8MHz   * 9 = 72MHz */   RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);   /* 使能 PLL */   RCC_PLLCmd(ENABLE);   /* 等待 PLL 输出稳定 */   while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);   /* 选择 SYSCLK 时钟源为 PLL */   RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);   /* 等待 PLL 成为 SYSCLK 时钟源 */   while(RCC_GetSYSCLKSource() != 0x08);   }   /* 打开 APB2 总线上的 GPIOA 时钟*/   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB   2Periph_USART1, ENABLE);   //RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);   RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2,ENABLE);   //RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Pe   riph_WWDG|RCC_APB1Periph_SPI2, ENABLE); }

void USART_Configuration(void) {   USART_InitTypeDef USART_InitStructure;   USART_ClockInitTypeDef USART_ClockInitStructure;USART_ClockInitStructure.USART_Clock = USART_Clock_Disable;   USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low;   USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge;   USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable;   USART_ClockInit(USART1 , &USART_ClockInitStructure);   USART_InitStructure.USART_BaudRate = 9600;   USART_InitStructure.USART_WordLength = USART_WordLength_8b;   USART_InitStructure.USART_StopBits = USART_StopBits_1;   USART_InitStructure.USART_Parity = USART_Parity_No;   USART_InitStructure.USART_HardwareFlowControl =   USART_HardwareFlowControl_None;   USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;   USART_Init(USART1,&USART_InitStructure);   USART_Cmd(USART1,ENABLE); }

void Delay(__IO uint32_t t) {   while(t--); } #if PRINTF_ON int fputc(int ch,FILE *f) {   USART_SendData(USART1,(u8) ch);   while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);   return ch; } #endif

 

refer:http://blog.csdn.net/g_salamander/article/details/8016698 --------------------- 作者:liwei16611 来源:CSDN 原文:https://blog.csdn.net/liwei16611/article/details/75258222 版权声明:本文为博主原创文章,转载请附上博文链接!



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3