forked from LonelyWolf/stm32
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathi2c.c
370 lines (311 loc) · 10.9 KB
/
i2c.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#include <stm32l1xx_gpio.h>
#include <stm32l1xx_rcc.h>
#include <i2c.h>
// Wait for specified I2C event (combination of I2C flags)
// input:
// I2Cx - I2C port
// I2C_Event - I2C event (one of I2C_EVENT_XXX values)
// return:
// I2C_SUCCESS if event happens or I2C_ERROR in case of timeout
I2C_Status I2Cx_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_Event) {
volatile uint32_t wait = I2C_WAIT_TIMEOUT;
uint32_t reg;
while (wait--) {
reg = I2Cx->SR1;
reg |= I2Cx->SR2 << 16;
reg &= I2C_FLAG_MASK;
if ((reg & I2C_Event) == I2C_Event) return I2C_SUCCESS;
}
return I2C_ERROR;
}
// Wait until I2C flag set
// input:
// I2Cx - I2C port
// I2C_Flag - I2C flag (one of I2C_F_XXX values)
// return:
// I2C_SUCCESS if flag set or I2C_ERROR in case of timeout
I2C_Status I2Cx_WaitFlagSet(I2C_TypeDef* I2Cx, uint32_t I2C_Flag) {
volatile uint32_t wait = I2C_WAIT_TIMEOUT;
volatile uint16_t *preg;
// Determine which I2C register to be read
preg = (I2C_Flag & 0x80000000) ? &(I2Cx->SR1) : &(I2Cx->SR2);
I2C_Flag &= 0xFFFF;
// Wait for flag to be set
while (wait--) {
if (*preg & I2C_Flag) return I2C_SUCCESS;
}
return I2C_ERROR;
}
// Wait until I2C flag cleared
// input:
// I2Cx - I2C port
// I2C_Flag - I2C flag (one of I2C_F_XXX values)
// return:
// I2C_SUCCESS if flag cleared or I2C_ERROR in case of timeout
I2C_Status I2Cx_WaitFlagReset(I2C_TypeDef* I2Cx, uint32_t I2C_Flag) {
volatile uint32_t wait = I2C_WAIT_TIMEOUT;
volatile uint16_t *preg;
// Determine which I2C register to be read
preg = (I2C_Flag & 0x80000000) ? &(I2Cx->SR1) : &(I2Cx->SR2);
I2C_Flag &= 0xFFFF;
// Wait until flag cleared
while (wait--) {
if (!(*preg & I2C_Flag)) return I2C_SUCCESS;
}
return I2C_ERROR;
}
// Initialize specified I2C peripheral
// input:
// I2Cx - I2C port
// Clock - I2C speed (Hz)
// return:
// I2C_ERROR if there was a timeout during I2C initialization, I2C_SUCCESS otherwise
// note: minimum APB1 frequency for I2C work is 2MHz
I2C_Status I2Cx_Init(I2C_TypeDef* I2Cx, uint32_t Clock) {
GPIO_InitTypeDef PORT;
RCC_ClocksTypeDef RCC_Clocks; // To compute I2C speed depending on current MCU clocking
uint16_t reg, spd, freq;
// Initialize I2C GPIO peripherals
PORT.GPIO_Speed = GPIO_Speed_40MHz;
PORT.GPIO_OType = GPIO_OType_OD;
PORT.GPIO_Mode = GPIO_Mode_AF;
PORT.GPIO_PuPd = GPIO_PuPd_UP;
if (I2Cx == I2C1) {
// I2C1
RCC->AHBENR |= I2C1_PERIPH;
RCC->APB1ENR |= I2C1_CLOCK;
// Reset the I2C1 peripheral to initial state
RCC->APB1RSTR |= I2C1_CLOCK;
RCC->APB1RSTR &= ~I2C1_CLOCK;
// Change GPIO pins mapping to I2C
GPIO_PinAFConfig(I2C1_GPIO_PORT,I2C1_SCL_PIN_SRC,GPIO_AF_I2C1);
GPIO_PinAFConfig(I2C1_GPIO_PORT,I2C1_SDA_PIN_SRC,GPIO_AF_I2C1);
// Initialize I2C1 GPIO peripheral
PORT.GPIO_Pin = I2C1_SCL_PIN | I2C1_SDA_PIN;
GPIO_Init(I2C1_GPIO_PORT,&PORT);
} else {
// I2C2
RCC->AHBENR |= I2C2_PERIPH;
RCC->APB1ENR |= I2C2_CLOCK;
// Reset the I2C2 peripheral to initial state
RCC->APB1RSTR |= I2C2_CLOCK;
RCC->APB1RSTR &= ~I2C2_CLOCK;
// Change GPIO pins mapping to I2C
GPIO_PinAFConfig(I2C2_GPIO_PORT,I2C2_SCL_PIN_SRC,GPIO_AF_I2C2);
GPIO_PinAFConfig(I2C2_GPIO_PORT,I2C2_SDA_PIN_SRC,GPIO_AF_I2C2);
// Initialize I2C2 GPIO peripheral
PORT.GPIO_Pin = I2C2_SCL_PIN | I2C2_SDA_PIN;
GPIO_Init(I2C2_GPIO_PORT,&PORT);
}
// Configure the I2C peripheral
// Get CR2 register value and clear FREQ[5:0] bits
reg = I2Cx->CR2 & ~I2C_CR2_FREQ;
// Get current RCC clocks
RCC_GetClocksFreq(&RCC_Clocks);
// Set FREQ bits depending on PCLK1 value
freq = (uint16_t)(RCC_Clocks.PCLK1_Frequency / 1000000);
I2Cx->CR2 |= freq;
// TRISE can be configured only when I2C peripheral disabled
I2Cx->CR1 &= ~I2C_CR1_PE;
// Configure I2C speed
if (Clock <= 100000) {
// I2C standard speed (Clock <= 100kHz)
spd = (uint16_t)(RCC_Clocks.PCLK1_Frequency / (Clock << 1)); // Duty cycle 50%/50%
// I2C CCR value: Standard mode
reg = (spd < 0x04) ? 0x04 : spd;
// Maximum rise time for standard mode
I2Cx->TRISE = freq + 1;
} else {
// I2C fast speed (100kHz > Clock <= 400kHz)
// PCLK1 frequency must be a multiple of 10MHz
spd = (uint16_t)(RCC_Clocks.PCLK1_Frequency / (Clock * 3)); // Duty cycle 66%/33% (Tlow/Thigh = 2)
// spd = (uint16_t)(RCC_Clocks.PCLK1_Frequency / (Clock * 25)); // Duty cycle 64%/33% (Tlow/Thigh = 16/9)
// reg |= I2C_CCR_DUTY; // I2C fast mode mode duty cycle = 16/9
// I2C CCR value: Fast mode
reg = (spd == 0) ? 1 : spd;
reg |= I2C_CCR_FS;
// Maximum rise time for fast mode
I2Cx->TRISE = (uint16_t)(((freq * 300) / 1000) + 1);
}
// Write to I2C CCR register
I2Cx->CCR = reg;
// Enable acknowledge, I2C mode, peripheral enabled
I2Cx->CR1 = I2C_CR1_ACK | I2C_CR1_PE;
// Set I2C own address: 0x00, 7-bit
I2Cx->OAR1 = (1 << 14); // Bit 14 should be kept as 1
// Wait until I2C bus is free
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;
return I2C_SUCCESS;
}
// Send data to I2C port
// input:
// I2Cx - I2C port
// buf - pointer to the data buffer
// nbytes - number of bytes to transmit
// SlaveAddress - address of slave device
// stop - generate or not STOP condition (I2C_STOP/I2C_NOSTOP)
// return:
// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
I2C_Status I2Cx_Write(I2C_TypeDef* I2Cx, const uint8_t* buf, uint32_t nbytes,
uint8_t SlaveAddress, I2C_STOP_TypeDef stop) {
// Initiate a START sequence
I2Cx->CR1 |= I2C_CR1_START;
// Wait for EV5
if (I2Cx_WaitEvent(I2Cx,I2C_EVENT_EV5) == I2C_ERROR) return I2C_ERROR;
// Send the slave address (EV5)
I2Cx->DR = SlaveAddress & ~I2C_OAR1_ADD0; // Last bit be reset (transmitter mode)
// Wait for EV6
if (I2Cx_WaitEvent(I2Cx,I2C_EVENT_EV6) == I2C_ERROR) return I2C_ERROR;
// Send first byte (EV8)
I2Cx->DR = *buf++;
// Send rest of data (if present)
while (--nbytes) {
// Wait for BTF flag set
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BTF) == I2C_ERROR) return I2C_ERROR;
// Transmit byte via I2C
I2Cx->DR = *buf++;
}
// Wait for BTF flag set
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BTF) == I2C_ERROR) return I2C_ERROR;
// Transmission end
if (stop == I2C_STOP) {
// Generate a STOP condition
I2Cx->CR1 |= I2C_CR1_STOP;
// Wait for a STOP flag
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_STOPF) == I2C_ERROR) return I2C_ERROR;
}
return I2C_SUCCESS;
}
// Read data from I2C port
// input:
// I2Cx - I2C port
// buf - pointer to data buffer
// nbytes - number of bytes to receive
// SlaveAddress - address of slave device
// return:
// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
I2C_Status I2Cx_Read(I2C_TypeDef* I2Cx, uint8_t *buf, uint32_t nbytes,
uint8_t SlaveAddress) {
// Enable Acknowledgment
I2Cx->CR1 |= I2C_CR1_ACK;
// Clear POS flag
I2Cx->CR1 &= ~I2C_CR1_POS; // NACK position current
// Initiate START sequence
I2Cx->CR1 |= I2C_CR1_START;
// Wait for EV5
if (I2Cx_WaitEvent(I2Cx,I2C_EVENT_EV5) == I2C_ERROR) return I2C_ERROR;
// Send the slave address (EV5)
I2Cx->DR = SlaveAddress | I2C_OAR1_ADD0; // Last bit set (receiver mode)
// Wait for EV6
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_ADDR) == I2C_ERROR) return I2C_ERROR;
// There are can be three cases:
// read 1 byte
// read 2 bytes
// read more than 2 bytes
if (nbytes == 1) {
// Receive 1 byte (AN2824 figure 2)
I2Cx->CR1 &= (uint16_t)~((uint16_t)I2C_CR1_ACK); // Disable I2C acknowledgment
// EV6_1 must be atomic operation (AN2824)
__disable_irq();
(void)I2Cx->SR1; // Clear ADDR bit
(void)I2Cx->SR2;
I2Cx->CR1 |= I2C_CR1_STOP; // Generate a STOP condition
__enable_irq();
// Wait for RxNE flag (receive buffer not empty) EV7
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_RXNE) == I2C_ERROR) return I2C_ERROR;
// Read received byte
*buf = (uint8_t)I2Cx->DR;
} else if (nbytes == 2) {
// Receive 2 bytes (AN2824 figure 2)
I2Cx->CR1 |= I2C_CR1_POS; // Set POS flag (NACK position next)
// EV6_1 must be atomic operation (AN2824)
__disable_irq();
(void)I2Cx->SR2; // Clear ADDR bit
I2Cx->CR1 &= (uint16_t)~((uint16_t)I2C_CR1_ACK); // Disable I2C acknowledgment
__enable_irq();
// Wait for BTF flag set (byte transfer finished) EV7_3
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BTF) == I2C_ERROR) return I2C_ERROR;
// This should be atomic operation
__disable_irq();
// Generate a STOP condition
I2Cx->CR1 |= I2C_CR1_STOP;
// Read first received byte
*buf++ = (uint8_t)I2Cx->DR;
__enable_irq();
// Read second received byte
*buf = (uint8_t)I2Cx->DR;
} else {
// Receive more than 2 bytes (AN2824 figure 1)
(void)I2Cx->SR2; // Clear ADDR bit
// Read received bytes into buffer
while (nbytes-- != 3) {
// Wait for BTF (cannot guarantee 1 transfer completion time)
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BTF) == I2C_ERROR) return I2C_ERROR;
*buf++ = (uint8_t)I2Cx->DR;
}
// Wait for BTF flag set (byte transfer finished) EV7_2
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BTF) == I2C_ERROR) return I2C_ERROR;
// Disable the I2C acknowledgment
I2Cx->CR1 &= (uint16_t)~((uint16_t)I2C_CR1_ACK);
__disable_irq();
// Read received byte N-2
*buf++ = (uint8_t)I2Cx->DR;
// Generate a STOP condition
I2Cx->CR1 |= I2C_CR1_STOP;
__enable_irq();
// Read received byte N-1
*buf++ = I2Cx->DR;
// Wait for last byte received
if (I2Cx_WaitEvent(I2Cx,I2C_EVENT_EV7) == I2C_ERROR) return I2C_ERROR;
// Read last received byte
*buf = (uint8_t)I2Cx->DR;
}
// Wait for a STOP flag
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_STOPF) == I2C_ERROR) return I2C_ERROR;
return I2C_SUCCESS;
}
// Check if target device is ready for communication
// input:
// I2Cx - I2C port
// SlaveAddress - address of slave device
// Trials - number of trials
// return:
// I2C_ERROR if there was a timeout during I2C operations, I2C_SUCCESS otherwise
I2C_Status I2Cx_IsDeviceReady(I2C_TypeDef* I2Cx, uint8_t SlaveAddress, uint32_t Trials) {
volatile uint32_t wait;
uint16_t reg;
do {
// Initiate a START sequence
I2Cx->CR1 |= I2C_CR1_START;
// Wait for EV5
if (I2Cx_WaitFlagSet(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;
// Send the slave address (EV5)
I2Cx->DR = SlaveAddress & ~I2C_OAR1_ADD0; // Last bit be reset (transmitter mode)
// Wait until ADDR or AF bit set
wait = I2C_WAIT_TIMEOUT;
do {
reg = I2Cx->SR1;
} while (!(reg & I2C_SR1_ADDR) && !(reg & I2C_SR1_AF) && --wait);
// Check if device responded
if (reg & I2C_SR1_ADDR) {
// Generate a STOP condition
I2Cx->CR1 |= I2C_CR1_STOP;
// Clear the ADDR flag
(void)I2Cx->SR1;
(void)I2Cx->SR2;
// Wait for a STOP flag
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_STOPF) == I2C_ERROR) return I2C_ERROR;
// Wait until I2C bus is free
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;
return I2C_SUCCESS;
} else {
// Generate a STOP condition
I2Cx->CR1 |= I2C_CR1_STOP;
// Clear the AF flag
I2Cx->SR1 &= ~I2C_SR1_AF;
// Wait until I2C bus is free
if (I2Cx_WaitFlagReset(I2Cx,I2C_F_BUSY) == I2C_ERROR) return I2C_ERROR;
}
} while (--Trials);
return I2C_ERROR;
}