px-fwlib 0.10.0
Cross-platform embedded library and documentation for 8/16/32-bit microcontrollers generated with Doxygen 1.9.2
px_i2c.c
1/* =============================================================================
2 ____ ___ ____ ___ _ _ ___ __ __ ___ __ __ TM
3 | _ \ |_ _| / ___| / _ \ | \ | | / _ \ | \/ | |_ _| \ \/ /
4 | |_) | | | | | | | | | | \| | | | | | | |\/| | | | \ /
5 | __/ | | | |___ | |_| | | |\ | | |_| | | | | | | | / \
6 |_| |___| \____| \___/ |_| \_| \___/ |_| |_| |___| /_/\_\
7
8 Copyright (c) 2018 Pieter Conradie <https://piconomix.com>
9
10 License: MIT
11 https://github.com/piconomix/px-fwlib/blob/master/LICENSE.md
12
13 Title: px_i2c.h : I2C peripheral driver
14 Author(s): Pieter Conradie
15 Creation Date: 2018-03-15
16
17============================================================================= */
18
19/* _____STANDARD INCLUDES____________________________________________________ */
20
21/* _____PROJECT INCLUDES_____________________________________________________ */
22#include "px_i2c.h"
23#include "px_board.h"
24#include "px_stm32cube.h"
25#include "px_log.h"
26
27/* _____LOCAL DEFINITIONS____________________________________________________ */
28PX_LOG_NAME("px_i2c");
29
30/// Definition of data for each I2C peripheral
31typedef struct px_i2c_per_s
32{
33 I2C_TypeDef * i2c_base_adr; ///< I2C peripheral base register address
34 px_i2c_nr_t i2c_nr; ///< Peripheral
35 uint8_t open_counter; ///< Number of open handles referencing peripheral
37
38/* _____LOCAL VARIABLES______________________________________________________ */
39/// Allocate data for each enabled I2C peripheral
40#if PX_I2C_CFG_I2C1_EN
41static px_i2c_per_t px_i2c_per_1;
42#endif
43#if PX_I2C_CFG_I2C2_EN
44static px_i2c_per_t px_i2c_per_2;
45#endif
46
47/* _____LOCAL FUNCTIONS______________________________________________________ */
48static bool px_i2c_init_per(I2C_TypeDef * i2c_base_adr,
49 px_i2c_nr_t i2c_nr)
50{
51 // Enable peripheral clock
52 switch(i2c_nr)
53 {
54#if PX_I2C_CFG_I2C1_EN
55 case PX_I2C_NR_1:
56 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
57 break;
58#endif
59#if PX_I2C_CFG_I2C2_EN
60 case PX_I2C_NR_2:
61 LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C2);
62 break;
63#endif
64 default:
65 PX_LOG_E("Invalid peripheral");
66 return false;
67 }
68 // Set Control register 1
69 i2c_base_adr->CR1 = 0;
70 // Set Control register 2
71 i2c_base_adr->CR2 = 0;
72 // Set Timing register
73 LL_I2C_SetTiming(i2c_base_adr, PX_I2C_CFG_TIMINGR);
74 // Enable I2C
75 LL_I2C_Enable(i2c_base_adr);
76 // Success
77 return true;
78}
79
80static void px_i2c_init_per_data(px_i2c_nr_t i2c_nr,
81 px_i2c_per_t * i2c_per)
82{
83 // Set peripheral
84 i2c_per->i2c_nr = i2c_nr;
85 // Set peripheral base address
86 switch(i2c_nr)
87 {
88#if PX_I2C_CFG_I2C1_EN
89 case PX_I2C_NR_1:
90 i2c_per->i2c_base_adr = I2C1;
91 break;
92#endif
93#if PX_I2C_CFG_I2C2_EN
94 case PX_I2C_NR_2:
95 i2c_per->i2c_base_adr = I2C2;
96 break;
97#endif
98 default:
99 PX_LOG_E("Invalid peripheral");
100 return;
101 }
102 // Clear open counter
103 i2c_per->open_counter = 0;
104}
105
106/* _____GLOBAL FUNCTIONS_____________________________________________________ */
107void px_i2c_init(void)
108{
109 // Initialize peripheral data for each enabled peripheral
110#if PX_I2C_CFG_I2C1_EN
111 px_i2c_init_per_data(PX_I2C_NR_1, &px_i2c_per_1);
112#endif
113#if PX_I2C_CFG_I2C2_EN
114 px_i2c_init_per_data(PX_I2C_NR_2, &px_i2c_per_2);
115#endif
116}
117
119 px_i2c_nr_t i2c_nr,
120 uint8_t slave_adr)
121{
122 px_i2c_per_t * i2c_per;
123
124#if PX_LOG
125 // Verify that pointer to handle is not NULL
126 if(handle == NULL)
127 {
128 PX_LOG_ASSERT(false);
129 return false;
130 }
131 if(handle->i2c_per != NULL)
132 {
133 PX_LOG_W("Handle already open?");
134 }
135#endif
136
137 // Handle not initialised
138 handle->i2c_per = NULL;
139 // Set pointer to peripheral data
140 switch(i2c_nr)
141 {
142#if PX_I2C_CFG_I2C1_EN
143 case PX_I2C_NR_1:
144 i2c_per = &px_i2c_per_1;
145 break;
146#endif
147#if PX_I2C_CFG_I2C2_EN
148 case PX_I2C_NR_2:
149 i2c_per = &px_i2c_per_2;
150 break;
151#endif
152 default:
153 PX_LOG_E("Invalid peripheral specified");
154 return false;
155 }
156#if PX_LOG
157 // Check that px_i2c_init() has been called
158 if(i2c_per->i2c_base_adr == NULL)
159 {
160 PX_LOG_ASSERT(false);
161 return false;
162 }
163#endif
164 // Save 7-bit slave address
165 handle->slave_adr = slave_adr;
166 // Peripheral closed?
167 if(i2c_per->open_counter == 0)
168 {
169 // Initialise peripheral
170 if(!px_i2c_init_per(i2c_per->i2c_base_adr, i2c_nr))
171 {
172 // Invalid parameter specified
173 return false;
174 }
175 }
176 // Point handle to peripheral
177 handle->i2c_per = i2c_per;
178 // Success
179 PX_LOG_ASSERT(i2c_per->open_counter < 255);
180 i2c_per->open_counter++;
181 return true;
182}
183
185{
186 px_i2c_per_t * i2c_per;
187 I2C_TypeDef * i2c_base_adr;
188
189#if PX_LOG
190 // Check handle
191 if( (handle == NULL)
192 ||(handle->i2c_per == NULL)
193 ||(handle->i2c_per->i2c_base_adr == NULL)
194 ||(handle->i2c_per->open_counter == 0 ) )
195 {
196 PX_LOG_ASSERT(false);
197 return false;
198 }
199#endif
200
201 // Set pointer to peripheral
202 i2c_per = handle->i2c_per;
203 // Get I2C peripheral base register address
204 i2c_base_adr = i2c_per->i2c_base_adr;
205 // Decrement open count
206 PX_LOG_ASSERT(i2c_per->open_counter > 0);
207 i2c_per->open_counter--;
208 // More open handles referencing peripheral?
209 if(i2c_per->open_counter != 0)
210 {
211 // Close handle
212 handle->i2c_per = NULL;
213 // Success
214 return true;
215 }
216 // Disable peripheral
217 i2c_base_adr->CR1 = 0;
218 // Disable peripheral clock
219 switch(i2c_per->i2c_nr)
220 {
221#if PX_I2C_CFG_I2C1_EN
222 case PX_I2C_NR_1:
223 LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C1);
224 break;
225#endif
226#if PX_I2C_CFG_I2C2_EN
227 case PX_I2C_NR_2:
228 LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_I2C2);
229 break;
230#endif
231 default:
232 break;
233 }
234 // Close handle
235 handle->i2c_per = NULL;
236 // Success
237 return true;
238}
239
241 const void * data,
242 size_t nr_of_bytes,
243 uint8_t flags)
244{
245 px_i2c_per_t * i2c_per;
246 I2C_TypeDef * i2c_base_adr;
247 const uint8_t * data_u8 = (const uint8_t *)data;
248 uint32_t i2c_isr;
249
250#if PX_LOG
251 // Check handle
252 if( (handle == NULL)
253 ||(handle->i2c_per == NULL)
254 ||(handle->i2c_per->i2c_base_adr == NULL)
255 ||(handle->i2c_per->open_counter == 0 ) )
256 {
257 PX_LOG_ASSERT(false);
258 return false;
259 }
260#endif
261 // Check that slave address is 7 bits
262 PX_LOG_ASSERT(handle->slave_adr < 0x80);
263
264 // Set pointer to peripheral
265 i2c_per = handle->i2c_per;
266 // Get I2C peripheral base register address
267 i2c_base_adr = i2c_per->i2c_base_adr;
268 // Must RELOAD bit be cleared?
269 if( (nr_of_bytes <= 1)
270 &&( (flags & PX_I2C_FLAG_STOP) || (flags & PX_I2C_FLAG_END) ) )
271 {
272 LL_I2C_DisableReloadMode(i2c_base_adr);
273 }
274 else
275 {
276 LL_I2C_EnableReloadMode(i2c_base_adr);
277 }
278 // Set the number of bytes to transfer
279 if(nr_of_bytes > 0)
280 {
281 LL_I2C_SetTransferSize(i2c_base_adr, 1);
282 }
283 else
284 {
285 LL_I2C_SetTransferSize(i2c_base_adr, 0);
286 }
287 // Generate START or REPEATED START condition?
288 if( (flags & PX_I2C_FLAG_START) || (flags & PX_I2C_FLAG_REP_START) )
289 {
290 if(flags & PX_I2C_FLAG_START)
291 {
292 // I2C bus must be idle for a START condition
293 if(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
294 {
295 PX_LOG_E("I2C bus is busy with a previous transaction");
296 return false;
297 }
298 }
299 else
300 {
301 // I2C bus must be busy for a REPEATED START condition
302 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
303 {
304 PX_LOG_E("I2C bus is idle");
305 return false;
306 }
307 }
308 // Set slave address
309 LL_I2C_SetSlaveAddr(i2c_base_adr, (handle->slave_adr)<<1);
310 // Set transfer direction to write
311 LL_I2C_SetTransferRequest(i2c_base_adr, LL_I2C_REQUEST_WRITE);
312 // Generate START condition
313 LL_I2C_GenerateStartCondition(i2c_base_adr);
314 if(flags & PX_I2C_FLAG_START)
315 {
316 // Wait until I2C bus is busy
317 while(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
318 }
319 // Wait until START condition has ended
320 while(i2c_base_adr->CR2 & I2C_CR2_START) {;}
321 // Received NAK?
322 if(LL_I2C_IsActiveFlag_NACK(i2c_base_adr))
323 {
324 // Clear flag
325 LL_I2C_ClearFlag_NACK(i2c_base_adr);
326 // I2C slave not present. STOP condition automatically generated
327 PX_LOG_W("I2C Slave did not ACK SLA+W");
328 // Wait until bus is free
329 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
330 return false;
331 }
332 }
333
334 // Write byte(s)
335 while(true)
336 {
337 // I2C Bus must be busy
338 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
339 {
340 PX_LOG_E("I2C bus is idle");
341 return false;
342 }
343 // Wait for a flag to be set
344 do
345 {
346 i2c_isr = i2c_base_adr->ISR;
347 }
348 while((i2c_isr & (I2C_ISR_TXIS | I2C_ISR_NACKF | I2C_ISR_TC | I2C_ISR_TCR | I2C_ISR_BERR)) == 0);
349
350 // Received Bus Error?
351 if(i2c_isr & I2C_ISR_BERR)
352 {
353 // Clear flag
354 LL_I2C_ClearFlag_BERR(i2c_base_adr);
355 // I2C Bus Error
356 PX_LOG_E("I2C Bus Error");
357 // Wait until bus is free
358 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
359 return false;
360 }
361 // Received NAK?
362 if(i2c_isr & I2C_ISR_NACKF)
363 {
364 // Clear flag
365 LL_I2C_ClearFlag_NACK(i2c_base_adr);
366 // I2C slave NACKed byte. STOP condition automatically generated
367 PX_LOG_E("I2C Slave did not ACK byte");
368 // Wait until bus is free
369 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
370 return false;
371 }
372 // Transmit buffer ready to accept next byte?
373 if(i2c_isr & I2C_ISR_TXIS)
374 {
375 if(nr_of_bytes != 0)
376 {
377 // Load one byte to be transmitted
378 LL_I2C_TransmitData8(i2c_base_adr, *data_u8++);
379 // Decrement number of bytes
380 nr_of_bytes--;
381 }
382 }
383 // Received TC?
384 if(i2c_isr & I2C_ISR_TC)
385 {
386 // Finished transmitting bytes?
387 if(nr_of_bytes == 0)
388 {
389 // Yes
390 break;
391 }
392 }
393 // Received TCR?
394 if(i2c_isr & I2C_ISR_TCR)
395 {
396 // Finished transmitting bytes?
397 if(nr_of_bytes == 0)
398 {
399 // Yes
400 break;
401 }
402 // Set the number of bytes to transfer
403 LL_I2C_SetTransferSize(i2c_base_adr, 1);
404 }
405 }
406
407 // Generate STOP condition?
408 if(flags & PX_I2C_FLAG_STOP)
409 {
410 // I2C Bus must be busy
411 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
412 {
413 PX_LOG_E("I2C bus is idle");
414 return false;
415 }
416 // Generate STOP condition
417 LL_I2C_GenerateStopCondition(i2c_base_adr);
418 // Wait until stop condition has ended
419 while(!LL_I2C_IsActiveFlag_STOP(i2c_base_adr)) {;}
420 // Clear flag
421 LL_I2C_ClearFlag_STOP(i2c_base_adr);
422 // Wait until bus is free
423 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
424 }
425
426 // Success
427 return true;
428}
429
431 void * data,
432 size_t nr_of_bytes,
433 uint8_t flags)
434{
435 px_i2c_per_t * i2c_per;
436 I2C_TypeDef * i2c_base_adr;
437 uint8_t * data_u8 = (uint8_t *)data;
438 uint32_t i2c_isr;
439
440#if PX_LOG
441 // Check handle
442 if( (handle == NULL)
443 ||(handle->i2c_per == NULL)
444 ||(handle->i2c_per->i2c_base_adr == NULL)
445 ||(handle->i2c_per->open_counter == 0 ) )
446 {
447 PX_LOG_ASSERT(false);
448 return false;
449 }
450#endif
451 // Check that slave address is 7 bits
452 PX_LOG_ASSERT(handle->slave_adr < 0x80);
453
454 // Set pointer to peripheral
455 i2c_per = handle->i2c_per;
456 // Get I2C peripheral base register address
457 i2c_base_adr = i2c_per->i2c_base_adr;
458 // Must RELOAD bit be cleared?
459 if( (nr_of_bytes <= 1)
460 &&( (flags & PX_I2C_FLAG_STOP) || (flags & PX_I2C_FLAG_END) ) )
461 {
462 LL_I2C_DisableReloadMode(i2c_base_adr);
463 }
464 else
465 {
466 LL_I2C_EnableReloadMode(i2c_base_adr);
467 }
468 // Set the number of bytes to transfer
469 if(nr_of_bytes > 0)
470 {
471 LL_I2C_SetTransferSize(i2c_base_adr, 1);
472 }
473 else
474 {
475 LL_I2C_SetTransferSize(i2c_base_adr, 0);
476 }
477 // Generate START or REPEATED START condition?
478 if( (flags & PX_I2C_FLAG_START) || (flags & PX_I2C_FLAG_REP_START) )
479 {
480 if(flags & PX_I2C_FLAG_START)
481 {
482 // I2C bus must be idle for a START condition
483 if(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
484 {
485 PX_LOG_E("I2C bus is busy with a previous transaction");
486 return false;
487 }
488 }
489 else
490 {
491 // I2C bus must be busy for a REPEATED START condition
492 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
493 {
494 PX_LOG_E("I2C bus is idle");
495 return false;
496 }
497 }
498 // Set slave address
499 LL_I2C_SetSlaveAddr(i2c_base_adr, (handle->slave_adr)<<1);
500 // Set transfer direction to read
501 LL_I2C_SetTransferRequest(i2c_base_adr, LL_I2C_REQUEST_READ);
502 // Generate START condition
503 LL_I2C_GenerateStartCondition(i2c_base_adr);
504 if(flags & PX_I2C_FLAG_START)
505 {
506 while(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
507 }
508 // Wait until START condition has ended
509 while(i2c_base_adr->CR2 & I2C_CR2_START) {;}
510 // Received NAK?
511 if(LL_I2C_IsActiveFlag_NACK(i2c_base_adr))
512 {
513 // Clear flag
514 LL_I2C_ClearFlag_NACK(i2c_base_adr);
515 // I2C slave not present. STOP condition automatically generated
516 PX_LOG_W("I2C Slave did not ACK SLA+R");
517 // Wait until bus is free
518 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
519 return false;
520 }
521 }
522
523 // Read byte(s)
524 while(true)
525 {
526 // I2C Bus must be busy
527 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
528 {
529 PX_LOG_E("I2C bus is idle");
530 return false;
531 }
532 // Wait for a flag to be set
533 do
534 {
535 i2c_isr = i2c_base_adr->ISR;
536 }
537 while((i2c_isr & (I2C_ISR_RXNE | I2C_ISR_TC | I2C_ISR_TCR | I2C_ISR_NACKF)) == 0);
538
539 // Received byte in buffer?
540 if(i2c_isr & I2C_ISR_RXNE)
541 {
542 if(nr_of_bytes != 0)
543 {
544 // Fetch byte from receive buffer
545 *data_u8++ = LL_I2C_ReceiveData8(i2c_base_adr);
546 // Decrement number of bytes
547 nr_of_bytes--;
548 }
549 }
550 // Received NAK?
551 if(i2c_isr & I2C_ISR_NACKF)
552 {
553 // Clear flag
554 LL_I2C_ClearFlag_NACK(i2c_base_adr);
555 // Received NACKF, but dit not expect it
556 PX_LOG_E("Received NACKF");
557 // Wait until bus is free
558 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
559 return false;
560 }
561 // Received TC?
562 if(i2c_isr & I2C_ISR_TC)
563 {
564 // Finished receiving bytes?
565 if(nr_of_bytes == 0)
566 {
567 // Yes
568 break;
569 }
570 }
571 // Received TCR?
572 if(i2c_isr & I2C_ISR_TCR)
573 {
574 // Must RELOAD bit be cleared?
575 if( (nr_of_bytes == 1)
576 &&( (flags & PX_I2C_FLAG_STOP) || (flags & PX_I2C_FLAG_END) ) )
577 {
578 LL_I2C_DisableReloadMode(i2c_base_adr);
579 }
580
581 // Finished receiving bytes?
582 if(nr_of_bytes == 0)
583 {
584 // Yes
585 break;
586 }
587 // Set the number of bytes to transfer
588 LL_I2C_SetTransferSize(i2c_base_adr, 1);
589 }
590 }
591
592 // Generate STOP condition?
593 if(flags & PX_I2C_FLAG_STOP)
594 {
595 // I2C Bus must be busy
596 if(!LL_I2C_IsActiveFlag_BUSY(i2c_base_adr))
597 {
598 PX_LOG_E("I2C bus is idle");
599 return false;
600 }
601 // Generate STOP condition
602 LL_I2C_GenerateStopCondition(i2c_base_adr);
603 // Wait until stop condition has ended
604 while(!LL_I2C_IsActiveFlag_STOP(i2c_base_adr)) {;}
605 // Clear flag
606 LL_I2C_ClearFlag_STOP(i2c_base_adr);
607 // Wait until bus is free
608 while(LL_I2C_IsActiveFlag_BUSY(i2c_base_adr)) {;}
609 }
610
611 // Success
612 return true;
613}
614
616 uint8_t slave_adr)
617{
618#if PX_LOG
619 // Check handle
620 if( (handle == NULL)
621 ||(handle->i2c_per == NULL)
622 ||(handle->i2c_per->i2c_base_adr == NULL)
623 ||(handle->i2c_per->open_counter == 0 ) )
624 {
625 PX_LOG_ASSERT(false);
626 return;
627 }
628#endif
629 // Check that slave address is 7 bits
630 PX_LOG_ASSERT(slave_adr < 0x80);
631
632 // Set new slave address
633 handle->slave_adr = slave_adr;
634}
#define NULL
NULL pointer.
Definition: px_defs.h:49
#define PX_LOG_NAME(name)
Macro to declare a log name string once for each file to reduce code size.
Definition: px_log.h:349
#define PX_LOG_W(format,...)
Macro to display a formatted WARNING message.
Definition: px_log.h:388
#define PX_LOG_E(format,...)
Macro to display a formatted ERROR message.
Definition: px_log.h:377
#define PX_LOG_ASSERT(expression)
Macro that will test an expression, and block indefinitely if false.
Definition: px_log.h:440
uint8_t slave_adr
7-bit I2C slave address
Definition: px_i2c.h:106
struct px_i2c_per_s * i2c_per
I2C peripheral data.
Definition: px_i2c.h:105
#define PX_I2C_FLAG_START
Begin I2C transaction with a START condition.
Definition: px_i2c.h:85
#define PX_I2C_CFG_TIMINGR
I2C peripheral timing register value Calculated using STM32CubeMX graphical tool:
bool px_i2c_close(px_i2c_handle_t *handle)
Close specified handle.
Definition: px_i2c.c:184
void px_i2c_init(void)
Initialise I2C driver.
Definition: px_i2c.c:107
bool px_i2c_open(px_i2c_handle_t *handle, px_i2c_nr_t i2c_nr, uint8_t slave_adr)
Open I2C slave handle.
Definition: px_i2c.c:118
px_i2c_nr_t
Specify I2C peripheral.
Definition: px_i2c.h:76
bool px_i2c_rd(px_i2c_handle_t *handle, void *data, size_t nr_of_bytes, uint8_t flags)
Perform an I2C read transaction with an I2C slave.
Definition: px_i2c.c:430
#define PX_I2C_FLAG_END
Signal END of current I2C transaction for REP_START to follow.
Definition: px_i2c.h:89
#define PX_I2C_FLAG_REP_START
Begin I2C transaction with a REPEATED START condition.
Definition: px_i2c.h:91
#define PX_I2C_FLAG_STOP
Finish I2C transaction with a STOP condition.
Definition: px_i2c.h:87
void px_i2c_change_slave_adr(px_i2c_handle_t *handle, uint8_t slave_adr)
Change I2C slave address.
Definition: px_i2c.c:615
bool px_i2c_wr(px_i2c_handle_t *handle, const void *data, size_t nr_of_bytes, uint8_t flags)
Perform an I2C write transaction with an I2C slave.
Definition: px_i2c.c:240
Define I2C handle for a slave.
Definition: px_i2c.h:104
Definition of data for each I2C peripheral.
Definition: px_i2c.c:32
I2C_TypeDef * i2c_base_adr
I2C peripheral base register address.
Definition: px_i2c.c:33
px_i2c_nr_t i2c_nr
Peripheral.
Definition: px_i2c.c:34
uint8_t open_counter
Number of open handles referencing peripheral.
Definition: px_i2c.c:35