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 }