最近一段时间在做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 high 51 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 //初始化IIC
14 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 I2C1 21 22 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // enable clock for SCL and SDA pins 23 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 interrupts 39 I2C_ITConfig(I2C1, I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF, ENABLE); 40 41 42 // Configure the I2C event priority 43 NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; 44 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1 45 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级0 46 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 47 NVIC_Init(&NVIC_InitStructure); 48 49 // enable I2C1 50 I2C_Cmd(I2C1, ENABLE); 51 } 52 53 54 //Clear ADDR by reading SR1, then SR2 55 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 CR1 62 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 end 79 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 state 84 switch(I2C_GetLastEvent(I2C1)) 85 { 86 //SLAVE 87 //Receive 88 case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED: //EV1 89 I2C_clear_ADDR(I2C1); 90 break; 91 case I2C_EVENT_SLAVE_BYTE_RECEIVED: //EV2 92 //Read it, so no one is waiting, clears BTF if necessary 93 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 }