STM32F4XX中断方式通过IO模拟I2C总线Master模式
STM32的I2C硬核为了规避NXP的知识产权,使得I2C用起来经常出问题,因此ST公司推出了CPAL库,CPAL库在中断方式工作下仅支持无子地址
的器件,无法做到中断方式完成读写大部分I2C器件。同时CPAL库在多个I2C同时使用时,经测试也有问题,因此我们项目中放弃了使用ST公司的CPAL库以及标准外设库
访问I2c器件,用IO模拟I2c总线,同时也是支持中断方式完成I2C读写。
目前网上绝大部分IO模拟I2c总线的程序都是查询方式,浪费大量CPU周期用于循环等待,本文的程序使用定时器中断推动状态机来模拟I2C总线的操作,
中断方式使用,请定义回调函数,本程序将在读写完成或出错时自动调用回调函数
当然此程序也可以通过查询方式读写I2c总线,仅需查询IIC_BUSY.
本程序仅模拟主模式(Master)
i2c_sim.h
#ifndef __I2C_SIM_H__ #define __I2C_SIM_H__ #include <stm32f4xx.h> #define MAXFREQ 500000 extern uint8_t I2C_Read7(uint8_t IIC, uint8_t device, uint8_t Addr, uint8_t *Buf, uint8_t Count); extern uint8_t I2C_Read16(uint8_t IIC, uint8_t device, uint16_t Addr, uint8_t *Buf, uint8_t Count); extern uint8_t I2C_WriteByte7(uint8_t IIC, uint8_t device, uint8_t Addr, uint8_t Data); extern uint8_t I2C_Write16(uint8_t IIC, uint8_t device, uint16_t Addr, uint8_t *Buf, uint8_t Count); extern void IIC_Init(uint8_t IIC, uint16_t MicroSecond); extern void IIC_DeInit(uint8_t IIC); extern void IIC_SetCallback(uint8_t IIC, void(*OnTx)(void), void(*OnRx)(void) ,void(*OnErr)(void)); #endif
i2c_sim.c
#include "stm32f4xx_conf.h" #include <string.h> #define IIC_COUNT 2 #if (IIC_COUNT>3) Error! To many IIC #endif /*----------- I2C1 Device -----------*/ #define I2C1_SCL_GPIO_PORT GPIOB #define I2C1_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB #define I2C1_SCL_GPIO_PIN GPIO_Pin_6 #define I2C1_SCL_GPIO_PINSOURCE GPIO_PinSource6 #define I2C1_SDA_GPIO_PORT GPIOB #define I2C1_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB #define I2C1_SDA_GPIO_PIN GPIO_Pin_7 #define I2C1_SDA_GPIO_PINSOURCE GPIO_PinSource7 /*-----------I2C2 Device -----------*/ #define I2C2_SCL_GPIO_PORT GPIOA #define I2C2_SCL_GPIO_CLK RCC_AHB1Periph_GPIOA #define I2C2_SCL_GPIO_PIN GPIO_Pin_8 #define I2C2_SCL_GPIO_PINSOURCE GPIO_PinSource8 #define I2C2_SDA_GPIO_PORT GPIOC #define I2C2_SDA_GPIO_CLK RCC_AHB1Periph_GPIOC #define I2C2_SDA_GPIO_PIN GPIO_Pin_9 #define I2C2_SDA_GPIO_PINSOURCE GPIO_PinSource9 /*-----------I2C3 Device -----------*/ #define I2C3_SCL_GPIO_PORT GPIOH #define I2C3_SCL_GPIO_CLK RCC_AHB1Periph_GPIOH #define I2C3_SCL_GPIO_PIN GPIO_Pin_7 #define I2C3_SCL_GPIO_PINSOURCE GPIO_PinSource7 #define I2C3_SDA_GPIO_PORT GPIOH #define I2C3_SDA_GPIO_CLK RCC_AHB1Periph_GPIOH #define I2C3_SDA_GPIO_PIN GPIO_Pin_8 #define I2C3_SDA_GPIO_PINSOURCE GPIO_PinSource8 GPIO_TypeDef* I2C_SCL_GPIO_PORT[3] = {I2C1_SCL_GPIO_PORT, I2C2_SCL_GPIO_PORT, I2C3_SCL_GPIO_PORT}; const uint16_t I2C_SCL_GPIO_PIN[3] = {I2C1_SCL_GPIO_PIN, I2C2_SCL_GPIO_PIN, I2C3_SCL_GPIO_PIN}; const uint32_t I2C_SCL_GPIO_CLK[3] = {I2C1_SCL_GPIO_CLK, I2C2_SCL_GPIO_CLK, I2C3_SCL_GPIO_CLK}; const uint16_t I2C_SCL_GPIO_PINSOURCE[3] = {I2C1_SCL_GPIO_PINSOURCE, I2C2_SCL_GPIO_PINSOURCE, I2C3_SCL_GPIO_PINSOURCE}; GPIO_TypeDef* I2C_SDA_GPIO_PORT[3] = {I2C1_SDA_GPIO_PORT,I2C2_SDA_GPIO_PORT,I2C3_SDA_GPIO_PORT}; const uint16_t I2C_SDA_GPIO_PIN[3] = {I2C1_SDA_GPIO_PIN,I2C2_SDA_GPIO_PIN,I2C3_SDA_GPIO_PIN}; const uint32_t I2C_SDA_GPIO_CLK[3] = {I2C1_SDA_GPIO_CLK,I2C2_SDA_GPIO_CLK,I2C3_SDA_GPIO_CLK}; const uint16_t I2C_SDA_GPIO_PINSOURCE[3] = {I2C1_SDA_GPIO_PINSOURCE,I2C2_SDA_GPIO_PINSOURCE,I2C3_SDA_GPIO_PINSOURCE}; TIM_TypeDef* Timer[3] = {TIM5, TIM6, TIM7}; const IRQn_Type TimerIRQ[3] = {TIM5_IRQn, TIM6_DAC_IRQn, TIM7_IRQn}; const uint32_t RCC_APB1Periph_TIM[3] ={RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7}; #define SDA_Clear(IIC) I2C_SDA_GPIO_PORT[IIC]->BSRRH=I2C_SDA_GPIO_PIN[IIC] #define SDA_Set(IIC) I2C_SDA_GPIO_PORT[IIC]->BSRRL=I2C_SDA_GPIO_PIN[IIC] #define SCL_Clear(IIC) I2C_SCL_GPIO_PORT[IIC]->BSRRH=I2C_SCL_GPIO_PIN[IIC] #define SCL_Set(IIC) I2C_SCL_GPIO_PORT[IIC]->BSRRL=I2C_SCL_GPIO_PIN[IIC] #define En_SDA_Input(IIC) I2C_SDA_GPIO_PORT[IIC]->MODER&=~(I2C_SDA_GPIO_PIN[IIC]<<I2C_SDA_GPIO_PINSOURCE[IIC]) #define En_SDA_Output(IIC) I2C_SDA_GPIO_PORT[IIC]->MODER|=(I2C_SDA_GPIO_PIN[IIC]<<I2C_SDA_GPIO_PINSOURCE[IIC]) #define SDA_Read(IIC) ((I2C_SDA_GPIO_PORT[IIC]->IDR&I2C_SDA_GPIO_PIN[IIC])!=0)?1:0 typedef struct { __IO uint8_t StartState; __IO uint8_t StopState; __IO int8_t ReadByteState; __IO uint8_t TransferByte; __IO uint8_t ReadStop; __IO uint8_t WriteByteState; __IO uint8_t WriteACK; __IO uint8_t Command; //1-Read, 0=Write; __IO uint8_t Device; __IO uint32_t SubAddr; __IO uint8_t SubAddrLen; __IO uint8_t *TransferBuf; __IO uint16_t TransferCount; __IO uint8_t ReadState; __IO uint8_t WriteState; __IO uint8_t dat; __IO uint8_t bit; __IO uint8_t IIC_BUSY; __IO uint8_t ERROR; } IIC_State; static IIC_State iic_state[IIC_COUNT]; typedef struct { void(*OnTx)(void); void(*OnRx)(void); void(*OnErr)(void); } IIC_Callback; __IO IIC_Callback iic_callback[IIC_COUNT]; #define IN 1 #define OUT 0 void __INLINE SetIicSdaDir(uint8_t IIC, uint8_t x) { if (x) En_SDA_Input(IIC); else En_SDA_Output(IIC); } void IIC_GPIOInit(uint8_t IIC) { GPIO_InitTypeDef GPIO_InitStructure; /* Enable I2Cx SCL and SDA Pin Clock */ RCC_AHB1PeriphClockCmd((I2C_SCL_GPIO_CLK[IIC] | I2C_SDA_GPIO_CLK[IIC]), ENABLE); /* Set GPIO frequency to 50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* Select Alternate function mode */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//????? /* Select output Open Drain type */ GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* Disable internal Pull-up */ GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; /* Initialize I2Cx SCL Pin */ GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN[IIC]; GPIO_Init((GPIO_TypeDef*)I2C_SCL_GPIO_PORT[IIC], &GPIO_InitStructure); /* Initialize I2Cx SDA Pin */ GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PIN[IIC]; GPIO_Init((GPIO_TypeDef*)I2C_SDA_GPIO_PORT[IIC], &GPIO_InitStructure); } static void IIC_DelayTimer_Init(uint8_t IIC) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TimerIRQ[IIC]; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0 ; NVIC_Init(&NVIC_InitStructure); memset((void *)&iic_state[IIC], 0, sizeof(IIC_State)); memset((void *)&iic_callback[IIC], 0, sizeof(IIC_Callback)); } static void IIC_DelayTimer_DeInit(uint8_t IIC) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitStructure.NVIC_IRQChannel = TimerIRQ[IIC]; NVIC_InitStructure.NVIC_IRQChannelCmd = DISABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0 ; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(Timer[IIC], DISABLE); memset(&iic_state[IIC], 0, sizeof(IIC_State)); } static void IIC_SetDelay(uint8_t IIC, uint16_t MicroSecond) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_ClocksTypeDef rccClocks; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM[IIC],ENABLE); RCC_GetClocksFreq(&rccClocks); TIM_DeInit(Timer[IIC]); TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; if (Timer[IIC]==TIM2||Timer[IIC]==TIM3||Timer[IIC]==TIM4||Timer[IIC]==TIM5||Timer[IIC]==TIM6||Timer[IIC]==TIM7|| Timer[IIC]==TIM12||Timer[IIC]==TIM13||Timer[IIC]==TIM14) TIM_TimeBaseStructure.TIM_Prescaler=rccClocks.PCLK1_Frequency*2/1000000; else TIM_TimeBaseStructure.TIM_Prescaler=rccClocks.PCLK2_Frequency*2/1000000; TIM_TimeBaseStructure.TIM_ClockDivision=0; TIM_TimeBaseStructure.TIM_Period=MicroSecond; TIM_TimeBaseInit(Timer[IIC], &TIM_TimeBaseStructure); TIM_ClearFlag(Timer[IIC], TIM_FLAG_Update); TIM_ITConfig(Timer[IIC],TIM_FLAG_Update, ENABLE); } void IIC_Init(uint8_t IIC, uint16_t MicroSecond) { IIC_GPIOInit(IIC); SDA_Set(IIC); SCL_Set(IIC); IIC_DelayTimer_Init(IIC); IIC_SetDelay(IIC, MicroSecond); } #define p iic_state[IIC] #define q iic_callback[IIC] void IIC_SetCallback(uint8_t IIC, void(*OnTx)(void), void(*OnRx)(void) ,void(*OnErr)(void)) { q.OnErr=OnErr; q.OnTx=OnTx; q.OnRx=OnRx; } void IIC_DeInit(uint8_t IIC) { IIC_DelayTimer_DeInit(IIC); } static uint8_t IIC_StartStateMachine(uint8_t IIC) { switch(p.StartState) { case 0: SDA_Set(IIC); SCL_Set(IIC); p.StartState++; break; case 1: SDA_Clear(IIC); //SoftDelay(0); p.StartState++; break; case 2: SCL_Clear(IIC); p.StartState=0; break; } return p.StartState; } static uint8_t IIC_StopStateMachine(uint8_t IIC) { switch(p.StopState) { case 0: SCL_Set(IIC); SDA_Clear(IIC); //SoftDelay(1); p.StopState++; break; case 1: SDA_Set(IIC); p.StopState=0; break; } return p.StopState; } static uint8_t IIC_ReadByteStateMachine(uint8_t IIC) { switch(p.ReadByteState) { case 0: SetIicSdaDir(IIC, IN); p.bit=0; p.ReadByteState++; break; case 1: p.dat <<= 1; SCL_Set(IIC); p.ReadByteState++; break; case 2: if(SDA_Read(IIC)) { p.dat |= 1; } SCL_Clear(IIC); p.bit++; if (p.bit==8) p.ReadByteState++; else { p.ReadByteState--; break; } case 3: p.TransferByte=p.dat; SetIicSdaDir(IIC, OUT); if (p.ReadStop) SDA_Set(IIC); else SDA_Clear(IIC); // ReadStop = 0; ask, ReadStop = 1,stop p.ReadByteState++; break; case 4: SCL_Set(IIC); p.ReadByteState++; break; case 5: SCL_Clear(IIC); p.ReadByteState++; case 6: p.ReadByteState=0; break; } return p.ReadByteState; } static uint8_t IIC_WriteByteStateMachine(uint8_t IIC) { switch(p.WriteByteState) { case 0: p.dat=p.TransferByte; p.bit=8; p.WriteByteState++; case 1: if(p.dat & 0x80) { SDA_Set(IIC); } else { SDA_Clear(IIC); } p.WriteByteState++; break; case 2: SCL_Set(IIC); p.WriteByteState++; break; case 3: p.dat <<= 1; SCL_Clear(IIC); p.bit--; if (p.bit) { p.WriteByteState=1; break; } else p.WriteByteState++; case 4: SetIicSdaDir(IIC, IN); p.WriteByteState++; break; case 5: SCL_Set(IIC); p.WriteByteState++; break; case 6: p.WriteACK = SDA_Read(IIC); SCL_Clear(IIC); SetIicSdaDir(IIC, OUT); p.WriteByteState++; break; case 7: p.WriteByteState=0; break; } return p.WriteByteState; } static uint8_t IIC_ReadStateMachine(uint8_t IIC) { switch(p.ReadState) { case 0: p.ReadState++; case 1: if (IIC_StartStateMachine(IIC)==0) p.ReadState++; break; case 2: p.TransferByte=p.Device; p.ReadState++; case 3: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.ReadState=14; //Stop } else { if (p.SubAddrLen) p.ReadState++; //Send Access Address else p.ReadState+=3; //No Address } } break; case 4: //Send Address switch(p.SubAddrLen) { case 4: p.TransferByte=(p.SubAddr >> 24)&0x000000FF; break; case 3: p.TransferByte=(p.SubAddr >> 16)&0x000000FF; break; case 2: p.TransferByte=(p.SubAddr >> 8)&0x000000FF; break; case 1: p.TransferByte=p.SubAddr&0x000000FF; break; } p.SubAddrLen--; p.ReadState++; case 5: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.ReadState=14; //Stop } else { if (p.SubAddrLen==0) p.ReadState++; else p.ReadState--; } } break; case 6: if (IIC_StartStateMachine(IIC)==0) p.ReadState++; break; case 7: //Send Device Read p.TransferByte=p.Device|0x01; p.ReadState++; case 8: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.ReadState=14; } else { if (p.TransferCount==1) p.ReadState+=3; else p.ReadState++; } } break; case 9: //Read Bytes p.ReadStop=0; p.ReadState++; case 10: if (IIC_ReadByteStateMachine(IIC)==0) { *p.TransferBuf=p.TransferByte; p.TransferBuf++; p.TransferCount--; if (p.TransferCount==1) p.ReadState++; } break; case 11: //Read Last Byte p.ReadStop=1; p.ReadState++; case 12: //Read Last Byte if (IIC_ReadByteStateMachine(IIC)==0) { *p.TransferBuf=p.TransferByte; p.TransferCount=0; p.ReadState++; } break; case 13: if (IIC_StopStateMachine(IIC)==0) { p.ReadState=0; p.IIC_BUSY=0; p.ERROR=0; if (q.OnRx) q.OnRx(); } break; case 14: if (IIC_StopStateMachine(IIC)==0) { p.ReadState=0; p.IIC_BUSY=0; p.ERROR=1; if (q.OnErr) q.OnErr(); } break; } return p.ReadState; } static uint8_t IIC_WriteStateMachine(uint8_t IIC) { switch(p.WriteState) { case 0: p.WriteState++; case 1: if (IIC_StartStateMachine(IIC)==0) p.WriteState++; break; case 2: p.TransferByte=p.Device; p.WriteState++; case 3: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.WriteState=11; //Stop } else { if (p.SubAddrLen) p.WriteState++; //Send Access Address else { if (p.TransferCount) p.WriteState+=5; //Multi-Bytes; else p.WriteState+=3; //Single Byte } } } break; case 4: //Send Address switch(p.SubAddrLen) { case 4: p.TransferByte=(p.SubAddr >> 24)&0x000000FF; break; case 3: p.TransferByte=(p.SubAddr >> 16)&0x000000FF; break; case 2: p.TransferByte=(p.SubAddr >> 8)&0x000000FF; break; case 1: p.TransferByte=p.SubAddr&0x000000FF; break; } p.SubAddrLen--; p.WriteState++; case 5: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.WriteState=11; //Stop } else { if (p.SubAddrLen==0) { if (p.TransferCount) p.WriteState+=3; //Multi-Bytes; else p.WriteState++; //Single Byte } else p.WriteState--; } } break; case 6: //Send Only One Byte p.TransferByte=(uint32_t)p.TransferBuf; p.WriteState++; case 7: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.WriteState=11; //Stop } else { p.WriteState+=3; } } break; case 8: //Send Multi-Bytes Data p.TransferByte=*p.TransferBuf; p.TransferBuf++; p.TransferCount--; p.WriteState++; case 9: if (IIC_WriteByteStateMachine(IIC)==0) { if (p.WriteACK==1) { p.WriteState=11; //Stop } else { if (p.TransferCount==0) p.WriteState++; else p.WriteState--; } } break; case 10: if (IIC_StopStateMachine(IIC)==0) { p.WriteState=0; p.IIC_BUSY=0; p.ERROR=0; if (q.OnTx) q.OnTx(); } break; case 11: if (IIC_StopStateMachine(IIC)==0) { p.WriteState=0; p.IIC_BUSY=0; p.ERROR=1; if (q.OnErr) q.OnErr(); } break; } return p.WriteState; } static uint8_t IIC_StateMachine(uint8_t IIC) { if (p.Command) return IIC_ReadStateMachine(IIC); return IIC_WriteStateMachine(IIC); } uint8_t I2C_Read7(uint8_t IIC, uint8_t device, uint8_t Addr, uint8_t *Buf, uint8_t Count) { if (p.IIC_BUSY==0) { memset(&p, 0, sizeof(IIC_State)); p.Command=1; //1-Read, 0=Write; p.Device=device; p.SubAddr=Addr; p.SubAddrLen=1; p.TransferBuf=Buf; p.TransferCount=Count; p.IIC_BUSY=1; TIM_Cmd(Timer[IIC], ENABLE); return 1; } else return 0; } uint8_t I2C_Read16(uint8_t IIC, uint8_t device, uint16_t Addr, uint8_t *Buf, uint8_t Count) { if (p.IIC_BUSY==0) { memset(&p, 0, sizeof(IIC_State)); p.Command=1; //1-Read, 0=Write; p.Device=device; p.SubAddr=Addr; p.SubAddrLen=2; p.TransferBuf=Buf; p.TransferCount=Count; p.IIC_BUSY=1; TIM_Cmd(Timer[IIC], ENABLE); return 1; } else return 0; } uint8_t I2C_WriteByte7(uint8_t IIC, uint8_t device, uint8_t Addr, uint8_t Data) { if (p.IIC_BUSY==0) { memset(&p, 0, sizeof(IIC_State)); p.Command=0; //1-Read, 0=Write; p.Device=device; p.SubAddr=Addr; p.SubAddrLen=1; p.TransferBuf=(uint8_t *)Data; p.TransferCount=0; p.IIC_BUSY=1; TIM_Cmd(Timer[IIC], ENABLE); return 1; } else return 0; } uint8_t I2C_Write16(uint8_t IIC, uint8_t device, uint16_t Addr, uint8_t *Buf, uint8_t Count) { if (p.IIC_BUSY==0) { memset(&p, 0, sizeof(IIC_State)); p.Command=0; //1-Read, 0=Write; p.Device=device; p.SubAddr=Addr; p.SubAddrLen=2; p.TransferBuf=Buf; p.TransferCount=Count; p.IIC_BUSY=1; TIM_Cmd(Timer[IIC], ENABLE); return 1; } else return 0; } #if (IIC_COUNT>=1) void TIM5_IRQHandler(void) { if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM5, TIM_IT_Update); if (IIC_StateMachine(0)==0) { if (iic_state[0].IIC_BUSY==0) TIM_Cmd(TIM5, DISABLE); } } } #endif #if (IIC_COUNT>=2) void TIM6_DAC_IRQHandler(void) { if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM6, TIM_IT_Update); if (IIC_StateMachine(1)==0) { if (iic_state[1].IIC_BUSY==0) TIM_Cmd(TIM6, DISABLE); } } } #endif #if (IIC_COUNT>=3) void TIM7_IRQHandler(void) { if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM7, TIM_IT_Update); if (IIC_StateMachine(2)==0) { if (iic_state[2].IIC_BUSY==0) TIM_Cmd(TIM7, DISABLE); } } } #endif