最近一段时间在做I2C通信协议,需要在两块STM32之间做I2C通信,定的是主机用IO口模拟,从机用的是STM32的硬件I2C,我的项目要求是需要主从机之间能够进行一串数据收发而不出错,实验时在主机方面,利用IO口模拟主机,只需要理解时序就够了,同时将速度能够控制在100K(标准)左右,基本的时序理解网上大把的资料,所以主机这一块几个小时就搞定了,而在做从机时,遇到了困难,本来从机也想用IO口模拟的,但是速度达不到那么快,因此只能选择硬件做从机,现就从机用中断方式开说,总结过程中遇到的几点问题:
1、由于STM32的硬件问题,建议在使用I2C时,将其优先级设为最高。
2、针对程序中除了I2C数据收发,还有别的中断程序或者指令要执行而导致I2C数据传输堵塞时,可以在执行完该段程序后重新初始化I2C。
主机程序如下:
1 #include "Hal_IIC/I2C.h"2 #include "Hal_delay/delay.h"3 #include "common.h"4 #include "gizwits_product.h"5 6 extern void delayUs(uint32_t nus);7 uint8_t b[5];8 extern uint8_t Cookr[5];9 extern uint8_t WR_flag;10 uint8_t Wifi_SET; //WIFI状态脚11 extern uint8_t Power_flag; //电磁炉开启关闭标志位12 uint8_t Give_Up;13 /*--------------------------------------------------------------------------------14 调用方式:void IIC_Init(void) 15 函数说明:私有函数,I2C专用,函数初始化16 ---------------------------------------------------------------------------------*/ 17 void IIC_Init(void)18 { 19 GPIO_InitTypeDef GPIO_InitStructure;20 RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); //使能GPIOA时钟21 22 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;23 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出24 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;25 GPIO_Init(GPIOA, &GPIO_InitStructure);26 GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12); //PA11,PA12 输出高27 }28 /*--------------------------------------------------------------------------------29 调用方式:void I2CStart(void) 30 函数说明:私有函数,I2C专用,开始信号31 ---------------------------------------------------------------------------------*/32 void IIC_Start(void)33 {34 SDA_OUT(); //sda线输出35 IIC_SDA=1; 36 IIC_SCL=1;37 delayUs(4);38 IIC_SDA=0;//START:when CLK is high,DATA change form high to low 39 delayUs(4);40 IIC_SCL=0; //钳住I2C总线,准备发送或接收数据 41 } 42 /*--------------------------------------------------------------------------------43 调用方式:void I2CStop(void) 44 函数说明:私有函数,I2C专用,停止信号45 ---------------------------------------------------------------------------------*/46 void IIC_Stop(void)47 {48 SDA_OUT();//sda线输出49 IIC_SCL=0;50 IIC_SDA=0;//STOP:when CLK is high DATA change form low to high51 delayUs(4);52 IIC_SCL=1; 53 IIC_SDA=1;//发送I2C总线结束信号54 delayUs(4); 55 }56 /*--------------------------------------------------------------------------------57 调用方式:I2CAck(void) 58 函数说明:私有函数,I2C专用,等待从器件接收方的应答,0表示接受成功,1表示失败59 ---------------------------------------------------------------------------------*/60 uint8_t IIC_Wait_Ack(void)61 {62 uint8_t ucErrTime=0;63 SDA_IN(); //SDA设置为输入 64 IIC_SDA=1;delayUs(1); 65 IIC_SCL=1;delayUs(1); 66 while(READ_SDA)67 {68 ucErrTime++;69 if(ucErrTime>250)70 {71 IIC_Stop();72 return 1;73 }74 }75 IIC_SCL=0;//时钟输出0 76 return 0; 77 } 78 /*--------------------------------------------------------------------------------79 调用方式:void SendAck(void) 80 函数说明:私有函数,I2C专用,主器件为接收方,从器件为发送方时,应答信号。81 ---------------------------------------------------------------------------------*/82 void IIC_Ack(void)83 {84 IIC_SCL=0;85 SDA_OUT();86 IIC_SDA=0;87 delayUs(2);88 IIC_SCL=1;89 delayUs(2);90 IIC_SCL=0;91 }92 /*--------------------------------------------------------------------------------93 调用方式:void SendAck(void) 94 函数说明:私有函数,I2C专用,主器件为接收方,从器件为发送方时,非应答信号。95 ---------------------------------------------------------------------------------*/ 96 void IIC_NAck(void)97 {98 IIC_SCL=0;99 SDA_OUT(); 100 IIC_SDA=1; 101 delayUs(2); 102 IIC_SCL=1; 103 delayUs(2); 104 IIC_SCL=0; 105 } 106 /*-------------------------------------------------------------------------------- 107 调用方式:void IIC_Send_Byte(unsigned char ch) 108 函数说明:私有函数,I2C专用 109 ---------------------------------------------------------------------------------*/ 110 void IIC_Send_Byte(uint8_t txd) 111 { 112 uint8_t t; 113 SDA_OUT(); 114 IIC_SCL=0;//拉低时钟开始数据传输 115 for(t=0;t<8;t++) 116 { 117 //IIC_SDA=(txd&0x80)>>7; 118 if((txd&0x80)>>7) 119 IIC_SDA=1; 120 else 121 IIC_SDA=0; 122 txd<<=1; 123 delayUs(2); //对TEA5767这三个延时都是必须的 124 IIC_SCL=1; 125 delayUs(2); 126 IIC_SCL=0; 127 delayUs(2); 128 } 129 } 130 /*-------------------------------------------------------------------------------- 131 调用方式:unsigned char IIC_Read_Byte(void) 132 函数说明:私有函数,I2C专用 133 ---------------------------------------------------------------------------------*/ 134 //读1个字节,ack=1时,发送ACK,ack=0,发送nACK 135 uint8_t IIC_Read_Byte(unsigned char ack) 136 { 137 unsigned char i,receive=0; 138 SDA_IN();//SDA设置为输入 139 for(i=0;i<8;i++ ) 140 { 141 receive<<=1; 142 IIC_SCL=0; 143 delayUs(5); 144 IIC_SCL=1; 145 delayUs(5); 146 147 if(READ_SDA)receive++; 148 149 } 150 if (!ack) 151 IIC_NAck();//发送nACK 152 else 153 IIC_Ack(); //发送ACK 154 return receive; 155 } 156 157 158 //读温度传感器,温度值是由h的高字节和低字节的高四位组成,总共12位,其中负温度值是由补码形式 159 void T_Read(void) 160 { 161 162 /***************read start*******************/ 163 if(WR_flag==0x02) 164 { 165 166 IIC_Start(); 167 IIC_Send_Byte( 0x30|0x01); //读操作 168 while(IIC_Wait_Ack()); 169 // delayMs(500); //等待从机处理一个字节地址位 170 Give_Up = IIC_Read_Byte(1); 171 for(uint8_t i=0;i<4;i++) 172 { 173 b[i] = IIC_Read_Byte(1); 174 printf("%c",b[i]); 175 } 176 b[4] = IIC_Read_Byte(0); 177 printf("%c",b[4]); 178 179 if((b[0]==0xFA)&&(b[4]==0xFB)) 180 { 181 for(uint8_t i=1;i<6;i++) 182 { 183 Cookr[i] = b[i]; 184 } 185 186 } 187 } 188 189 /****************read end********************/ 190 /****************write start*****************/ 191 if(WR_flag==0x01) 192 { 193 IIC_Start(); 194 IIC_Send_Byte(0x30); //写操作 195 while(IIC_Wait_Ack()); 196 IIC_Send_Byte(0xFA); 197 while(IIC_Wait_Ack()); 198 delayMs(3); //延时太低传输数据会出错,因为从机还没处理完数据 199 IIC_Send_Byte(Cookr[1]); 200 while(IIC_Wait_Ack()); 201 delayMs(3); 202 IIC_Send_Byte(0x03); 203 while(IIC_Wait_Ack()); 204 delayMs(3); 205 IIC_Send_Byte(Power_flag); 206 while(IIC_Wait_Ack()); 207 delayMs(3); 208 IIC_Send_Byte(0xFB); 209 while(IIC_Wait_Ack()); 210 delayMs(3); 211 IIC_Stop(); 212 WR_flag=0x02; 213 } 214 /***************write end*****************/ 215 216 }
从机使用中断方式
1 #include "myiic.h"2 #include "delay.h"3 #include "led.h"4 #include "key.h"5 #include "usart.h"6 7 8 #define MY_I2C_ADDRESS 0x30 //模拟从机地址9 unsigned char b[5]={0x00,0x00,0x00,0x00,0x00}; //从机接收操作10 uint8_t Wifi_Set=0x00;11 extern u8 flag; //电磁炉开关中断位12 unsigned char a[5]={0xFA,0x00,0x00,0x00,0xFB}; 13 //初始化IIC14 void I2C1_Init(void)15 {16 GPIO_InitTypeDef GPIO_InitStructure;17 I2C_InitTypeDef I2C_InitStructure;18 NVIC_InitTypeDef NVIC_InitStructure;19 20 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // enable APB1 peripheral clock for I2C121 22 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // enable clock for SCL and SDA pins23 24 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;25 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;26 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //I2C必须开漏输出,实现线与逻辑27 GPIO_Init(GPIOB, &GPIO_InitStructure);28 29 30 I2C_InitStructure.I2C_ClockSpeed = 100000; // configure I2C1 31 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;32 I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;33 I2C_InitStructure.I2C_OwnAddress1 = MY_I2C_ADDRESS;34 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;35 I2C_InitStructure.I2C_AcknowledgedAddress= I2C_AcknowledgedAddress_7bit;36 I2C_Init(I2C1, &I2C_InitStructure);37 38 //setup interrupts39 I2C_ITConfig(I2C1, I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF, ENABLE); 40 41 42 // Configure the I2C event priority43 NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;44 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级145 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级046 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;47 NVIC_Init(&NVIC_InitStructure);48 49 // enable I2C150 I2C_Cmd(I2C1, ENABLE);51 }52 53 54 //Clear ADDR by reading SR1, then SR255 56 void I2C_clear_ADDR(I2C_TypeDef* I2Cx) {57 I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR);58 ((void)(I2Cx->SR2));59 }60 61 //Clear STOPF by reading SR1, then writing CR162 63 void I2C_clear_STOPF(I2C_TypeDef* I2Cx) {64 I2C_GetFlagStatus(I2Cx, I2C_FLAG_STOPF);65 I2C_Cmd(I2Cx, ENABLE);66 }67 68 /*--------------------------------------------------------------------------------69 调用方式:void I2C1_EV_IRQHandler(void) 70 函数说明:私有函数,I2C专用,中断按键处理函数,从机中断都在这里面执行71 ---------------------------------------------------------------------------------*/72 73 uint8_t data = 0;74 uint8_t S_data=0;75 void I2C1_EV_IRQHandler(void) 76 {77 // KV1=0; //只是一个测试灯78 //Clear AF from slave-transmission end79 if(I2C_GetITStatus(I2C1, I2C_IT_AF)) 80 {81 I2C_ClearITPendingBit(I2C1, I2C_IT_AF);82 }83 //Big state machine response, since doesn't actually keep state84 switch(I2C_GetLastEvent(I2C1)) 85 {86 //SLAVE87 //Receive88 case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: //EV189 I2C_clear_ADDR(I2C1);90 break;91 case I2C_EVENT_SLAVE_BYTE_RECEIVED: //EV292 //Read it, so no one is waiting, clears BTF if necessary93 b[data] = I2C_ReceiveData(I2C1);94 // printf("%c",b[data]);95 data++;96 if(data>=5)97 {98 data=0;99 if((b[0]==0xFA)&&(b[4]==0xFB)) 100 { 101 a[1]=b[1]; 102 Wifi_Set=b[2]; 103 flag=b[3]; 104 // printf("%c",a[1]); 105 } 106 107 } 108 if(I2C_GetFlagStatus(I2C1, I2C_FLAG_DUALF)) 109 {//Secondary Receive 110 } 111 else if(I2C_GetFlagStatus(I2C1, I2C_FLAG_GENCALL)) 112 {//General Receive 113 } 114 else 115 {//Normal 116 } 117 break; 118 case I2C_EVENT_SLAVE_STOP_DETECTED: //End of receive, EV4 119 I2C_clear_STOPF(I2C1); 120 break; 121 122 //Transmit 123 case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED: //EV1 124 I2C_clear_ADDR(I2C1); 125 //Send first byte 126 I2C_SendData(I2C1, 0x00); 127 128 break; 129 case I2C_EVENT_SLAVE_BYTE_TRANSMITTED: //EV3 130 //Determine what you want to send 131 //data = 5; 132 if(I2C_GetFlagStatus(I2C1, I2C_FLAG_DUALF)) 133 {//Secondary Transmit 134 } 135 else if(I2C_GetFlagStatus(I2C1, I2C_FLAG_GENCALL)) 136 {//General Transmit 137 } 138 else 139 {//Normal 140 } 141 //Read flag and write next byte to clear BTF if present 142 I2C_GetFlagStatus(I2C1, I2C_FLAG_BTF); 143 I2C_SendData(I2C1, a[S_data]); 144 S_data++; 145 if(S_data>=5) 146 S_data=0; 147 break; 148 case I2C_EVENT_SLAVE_ACK_FAILURE://End of transmission EV3_2 149 //TODO: Doesn't seem to be getting reached, so just 150 //check at top-level 151 I2C_ClearITPendingBit(I2C1, I2C_IT_AF); 152 break; 153 //Alternative Cases for address match 154 case I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED: //EV1 155 break; 156 case I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED: //EV1 157 break; 158 case I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED: //EV1 159 break; 160 161 162 //MASTER 163 case I2C_EVENT_MASTER_MODE_SELECT: //EV5, just sent start bit 164 break; 165 //Receive 166 case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: //EV6, just sent addr 167 break; 168 case I2C_EVENT_MASTER_BYTE_RECEIVED: //EV7 169 break; 170 //Transmit 171 case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: //EV6, just sent addr 172 break; 173 case I2C_EVENT_MASTER_BYTE_TRANSMITTING: //EV8, about to send data 174 break; 175 case I2C_EVENT_MASTER_BYTE_TRANSMITTED: //EV8_2, just sent data 176 break; 177 178 //Alternative addressing stuff, not going to worry about 179 case I2C_EVENT_MASTER_MODE_ADDRESS10: //EV9 180 break; 181 default: 182 //How the FUCK did you get here? 183 //I should probably raise some error, but fuck it, 184 //it's late 185 break; 186 187 } 188 189 190 } 191 192 void I2C1_ER_IRQHandler(void) { 193 // GPIO_SetBits(GPIOD, RED); 194 // LED3=0; 195 //Can't use nice switch statement, because no fxn available 196 if(I2C_GetITStatus(I2C1, I2C_IT_SMBALERT)) { 197 } else if(I2C_GetITStatus(I2C1, I2C_IT_TIMEOUT)) { 198 } else if(I2C_GetITStatus(I2C1, I2C_IT_PECERR)) { 199 } else if(I2C_GetITStatus(I2C1, I2C_IT_OVR)) { 200 //Overrun 201 //CLK stretch disabled and receiving 202 //DR has not been read, b4 next byte comes in 203 //effect: lose byte 204 //should:clear RxNE and transmitter should retransmit 205 206 //Underrun 207 //CLK stretch disabled and I2C transmitting 208 //haven't updated DR since new clock 209 //effect: same byte resent 210 //should: make sure discarded, and write next 211 } else if(I2C_GetITStatus(I2C1, I2C_IT_AF)) { 212 //Detected NACK 213 //Transmitter must reset com 214 //Slave: lines released 215 //Master: Stop or repeated Start must must be generated 216 //Master = MSL bit 217 //Fixup 218 I2C_ClearITPendingBit(I2C1, I2C_IT_AF); 219 } else if(I2C_GetITStatus(I2C1, I2C_IT_ARLO)) { 220 //Arbitration Lost 221 //Goes to slave mode, but can't ack slave address in same transfer 222 //Can after repeat Start though 223 } else if(I2C_GetITStatus(I2C1, I2C_IT_BERR)) { 224 //Bus Error 225 //In slave mode: data discarded, lines released, acts like restart 226 //In master mode: current transmission continues 227 } 228 }