STM32的I2C主从机通信

最近一段时间在做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 }

 

posted @ 2017-11-10 11:02  熊总熊  阅读(10776)  评论(2编辑  收藏  举报