At present, most IO analog I2c bus programs on the Internet are query methods, which wastes a lot of CPU cycles for cyclic waiting. The program in this article uses the timer interrupt to push the state machine to simulate I2C bus operations,
The method of interruption is used. Please define the callback function. This program will automatically call the callback function when reading or writing is completed or an error occurs.
Of course, this program can also read and write the I2c bus through the query method. You only need to query IIC_BUSY.
I2c_sim.h
#ifndef __I2C_SIM_H__#define __I2C_SIM_H__#include <stm32f4xx.h>#define MAXFREQ 1000000extern 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;__IOuint8_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 0void __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,stopp.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 Addresselse p.ReadState+=3;//No Address}}break;case 4://Send Addressswitch(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 Bytesp.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 Bytep.ReadStop=1;p.ReadState++;case 12://Read Last Byteif (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 Addresselse {if (p.TransferCount) p.WriteState+=5;//Multi-Bytes;else p.WriteState+=3; //Single Byte}}}break;case 4://Send Addressswitch(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