forked from simondlevy/BreezySTM32
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathdrv_i2c.c
628 lines (539 loc) · 22.3 KB
/
drv_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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
/*
drv_i2c.c : I^2C support for STM32F103
Adapted from https://github.com/multiwii/baseflight/blob/master/src/drv_i2c.c
This file is part of BreezySTM32.
BreezySTM32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
BreezySTM32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with BreezySTM32. If not, see <http://www.gnu.org/licenses/>.
*/
#define I2C_DEVICE (I2CDEV_2)
#include <stdbool.h>
#include <stdio.h>
#include <string.h> // memset
#include "stm32f10x_conf.h"
#include "drv_system.h" // timers, delays, etc
#include "drv_gpio.h"
#include "drv_i2c.h"
// I2C Interrupt Handlers
static void i2c_er_handler(void);
static void i2c_ev_handler(void);
static void i2cUnstick(void);
// I2C Circular Buffer Variables
static bool i2c_buffer_lock = false;
static i2cJob_t i2c_buffer[I2C_BUFFER_SIZE + 10];
static volatile uint8_t i2c_buffer_head;
static volatile uint8_t i2c_buffer_tail;
static volatile uint8_t i2c_buffer_count;
static void i2c_init_buffer(void);
static void i2c_job_handler(void);
typedef struct i2cDevice_t {
I2C_TypeDef *dev;
GPIO_TypeDef *gpio;
uint16_t scl;
uint16_t sda;
uint8_t ev_irq;
uint8_t er_irq;
uint32_t peripheral;
} i2cDevice_t;
static const i2cDevice_t i2cHardwareMap[] = {
{ I2C1, GPIOB, Pin_6, Pin_7, I2C1_EV_IRQn, I2C1_ER_IRQn, RCC_APB1Periph_I2C1 },
{ I2C2, GPIOB, Pin_10, Pin_11, I2C2_EV_IRQn, I2C2_ER_IRQn, RCC_APB1Periph_I2C2 },
};
// Copy of peripheral address for IRQ routines
static I2C_TypeDef *I2Cx = NULL;
// Copy of device index for reinit, etc purposes
static I2CDevice I2Cx_index;
void I2C1_ER_IRQHandler(void)
{
i2c_er_handler();
}
void I2C1_EV_IRQHandler(void)
{
i2c_ev_handler();
}
void I2C2_ER_IRQHandler(void)
{
i2c_er_handler();
}
void I2C2_EV_IRQHandler(void)
{
i2c_ev_handler();
}
#define I2C_DEFAULT_TIMEOUT 30000
static volatile uint16_t i2cErrorCount = 0;
static volatile bool error = false;
static volatile bool busy;
static volatile uint8_t addr;
static volatile uint8_t reg;
static volatile uint8_t bytes;
static volatile uint8_t writing;
static volatile uint8_t reading;
static volatile uint8_t *write_p;
static volatile uint8_t *read_p;
static volatile uint8_t *status;
static void (*complete_CB)(uint8_t);
static bool i2cHandleHardwareFailure(void)
{
i2cErrorCount++;
// reinit peripheral + clock out garbage
i2cInit(I2Cx_index);
return false;
}
bool i2cWriteBuffer(uint8_t addr_, uint8_t reg_, uint8_t len_, uint8_t *data)
{
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
addr = addr_ << 1;
reg = reg_;
writing = 1;
reading = 0;
write_p = data;
read_p = data;
bytes = len_;
busy = 1;
error = false;
if (!I2Cx)
return false;
if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
while (I2Cx->CR1 & 0x0200 && --timeout > 0) {
; // wait for any stop to finish sending
}
if (timeout == 0)
return i2cHandleHardwareFailure();
I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
}
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
}
timeout = I2C_DEFAULT_TIMEOUT;
while (busy && --timeout > 0) {
;
}
if (timeout == 0)
return i2cHandleHardwareFailure();
return !error;
}
bool i2cWrite(uint8_t addr_, uint8_t reg_, uint8_t data)
{
return i2cWriteBuffer(addr_, reg_, 1, &data);
}
bool i2cRead(uint8_t addr_, uint8_t reg_, uint8_t len, uint8_t *buf)
{
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
addr = addr_ << 1;
reg = reg_;
writing = 0;
reading = 1;
read_p = buf;
write_p = buf;
bytes = len;
busy = 1;
error = false;
if (!I2Cx)
return false;
if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
while (I2Cx->CR1 & 0x0200 && --timeout > 0) {
; // wait for any stop to finish sending
}
if (timeout == 0)
return i2cHandleHardwareFailure();
I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
}
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
}
timeout = I2C_DEFAULT_TIMEOUT;
while (busy && --timeout > 0) {
;
}
if (timeout == 0)
return i2cHandleHardwareFailure();
return !error;
}
bool i2cReadAsync(uint8_t addr_, uint8_t reg_, uint8_t len, uint8_t *buf, volatile uint8_t* status_, void (*CB)(uint8_t))
{
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
addr = addr_ << 1;
reg = reg_;
writing = 0;
reading = 1;
read_p = buf;
write_p = buf;
bytes = len;
busy = 1;
error = false;
status = status_;
complete_CB = CB;
if(!I2Cx)
return false;
if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
while (I2Cx->CR1 & 0x0200 && --timeout > 0) { // This is blocking, but happens only
; // wait for any stop to finish sending // if we are stomping on the port (try to avoid)
}
if (timeout == 0)
return i2cHandleHardwareFailure();
I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
}
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
}
return true;
}
bool i2cWriteAsync(uint8_t addr_, uint8_t reg_, uint8_t len_, uint8_t *buf_, volatile uint8_t* status_, void (*CB)(uint8_t))
{
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
addr = addr_ << 1;
reg = reg_;
writing = 1;
reading = 0;
write_p = buf_;
read_p = buf_;
bytes = len_;
busy = 1;
error = false;
status = status_;
complete_CB = CB;
if (!I2Cx)
return false;
if (!(I2Cx->CR2 & I2C_IT_EVT)) { // if we are restarting the driver
if (!(I2Cx->CR1 & 0x0100)) { // ensure sending a start
while (I2Cx->CR1 & 0x0200 && --timeout > 0) {
; // wait for any stop to finish sending
}
if (timeout == 0)
return i2cHandleHardwareFailure();
I2C_GenerateSTART(I2Cx, ENABLE); // send the start for the new job
}
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, ENABLE); // allow the interrupts to fire off again
}
return true;
}
static void i2c_er_handler(void)
{
// Read the I2C1 status register
volatile uint32_t SR1Register = I2Cx->SR1;
if (SR1Register & 0x0F00) // an error
{
i2cErrorCount++;
error = true;
}
// If AF, BERR or ARLO, abandon the current job and commence new if there are jobs
if (SR1Register & 0x0700) {
(void)I2Cx->SR2; // read second status register to clear ADDR if it is set (note that BTF will not be set after a NACK)
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable the RXNE/TXE interrupt - prevent the ISR tailchaining onto the ER (hopefully)
if (!(SR1Register & 0x0200) && !(I2Cx->CR1 & 0x0200)) { // if we dont have an ARLO error, ensure sending of a stop
if (I2Cx->CR1 & 0x0100) { // We are currently trying to send a start, this is very bad as start, stop will hang the peripheral
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
while (I2Cx->CR1 & 0x0100 && --timeout > 0) {
; // wait for any start to finish sending
}
I2C_GenerateSTOP(I2Cx, ENABLE); // send stop to finalise bus transaction
timeout = I2C_DEFAULT_TIMEOUT;
while (I2Cx->CR1 & 0x0200 && --timeout > 0) {
; // wait for stop to finish sending
}
i2cInit(I2Cx_index); // reset and configure the hardware
} else {
I2C_GenerateSTOP(I2Cx, ENABLE); // stop to free up the bus
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Disable EVT and ERR interrupts while bus inactive
}
}
}
I2Cx->SR1 &= ~0x0F00; // reset all the error bits to clear the interrupt
if (status)
(*status) = I2C_JOB_ERROR; // Update job status
if (complete_CB != NULL)
complete_CB(I2C_JOB_ERROR);
busy = 0;
i2c_job_handler();
}
void i2c_ev_handler(void)
{
static uint8_t subaddress_sent, final_stop; // flag to indicate if subaddess sent, flag to indicate final bus condition
static int8_t index; // index is signed -1 == send the subaddress
uint8_t SReg_1 = I2Cx->SR1; // read the status register here
if (SReg_1 & 0x0001) { // we just sent a start - EV5 in ref manual
I2Cx->CR1 &= ~0x0800; // reset the POS bit so ACK/NACK applied to the current byte
I2C_AcknowledgeConfig(I2Cx, ENABLE); // make sure ACK is on
index = 0; // reset the index
if (reading && (subaddress_sent || 0xFF == reg)) { // we have sent the subaddr
subaddress_sent = 1; // make sure this is set in case of no subaddress, so following code runs correctly
if (bytes == 2)
I2Cx->CR1 |= 0x0800; // set the POS bit so NACK applied to the final byte in the two byte read
I2C_Send7bitAddress(I2Cx, addr, I2C_Direction_Receiver); // send the address and set hardware mode
} else { // direction is Tx, or we havent sent the sub and rep start
I2C_Send7bitAddress(I2Cx, addr, I2C_Direction_Transmitter); // send the address and set hardware mode
if (reg != 0xFF) // 0xFF as subaddress means it will be ignored, in Tx or Rx mode
index = -1; // send a subaddress
}
} else if (SReg_1 & 0x0002) { // we just sent the address - EV6 in ref manual
// Read SR1,2 to clear ADDR
__DMB(); // memory fence to control hardware
if (bytes == 1 && reading && subaddress_sent) { // we are receiving 1 byte - EV6_3
I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
__DMB();
(void)I2Cx->SR2; // clear ADDR after ACK is turned off
I2C_GenerateSTOP(I2Cx, ENABLE); // program the stop
final_stop = 1;
I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE); // allow us to have an EV7
} else { // EV6 and EV6_1
(void)I2Cx->SR2; // clear the ADDR here
__DMB();
if (bytes == 2 && reading && subaddress_sent) { // rx 2 bytes - EV6_1
I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to fill
} else if (bytes == 3 && reading && subaddress_sent) // rx 3 bytes
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // make sure RXNE disabled so we get a BTF in two bytes time
else // receiving greater than three bytes, sending subaddress, or transmitting
I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE);
}
} else if (SReg_1 & 0x004) { // Byte transfer finished - EV7_2, EV7_3 or EV8_2
final_stop = 1;
if (reading && subaddress_sent) { // EV7_2, EV7_3
if (bytes > 2) { // EV7_2
I2C_AcknowledgeConfig(I2Cx, DISABLE); // turn off ACK
read_p[index++] = (uint8_t)I2Cx->DR; // read data N-2
I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
final_stop = 1; // required to fix hardware
read_p[index++] = (uint8_t)I2Cx->DR; // read data N - 1
I2C_ITConfig(I2Cx, I2C_IT_BUF, ENABLE); // enable TXE to allow the final EV7
} else { // EV7_3
if (final_stop){
I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
}
else
I2C_GenerateSTART(I2Cx, ENABLE); // program a rep start
read_p[index++] = (uint8_t)I2Cx->DR; // read data N - 1
read_p[index++] = (uint8_t)I2Cx->DR; // read data N
index++; // to show job completed
}
} else { // EV8_2, which may be due to a subaddress sent or a write completion
if (subaddress_sent || (writing)) {
if (final_stop)
I2C_GenerateSTOP(I2Cx, ENABLE); // program the Stop
else
I2C_GenerateSTART(I2Cx, ENABLE); // program a rep start
index++; // to show that the job is complete
} else { // We need to send a subaddress
I2C_GenerateSTART(I2Cx, ENABLE); // program the repeated Start
subaddress_sent = 1; // this is set back to zero upon completion of the current task
}
}
// we must wait for the start to clear, otherwise we get constant BTF
uint32_t timeout = I2C_DEFAULT_TIMEOUT;
while (I2Cx->CR1 & 0x0100 && --timeout > 0) {
;
}
} else if (SReg_1 & 0x0040) { // Byte received - EV7
read_p[index++] = (uint8_t)I2Cx->DR;
if (bytes == (index + 3))
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush so we can get an EV7_2
if (bytes == index) // We have completed a final EV7
index++; // to show job is complete
} else if (SReg_1 & 0x0080) { // Byte transmitted EV8 / EV8_1
if (index != -1) { // we dont have a subaddress to send
I2Cx->DR = write_p[index++];
if (bytes == index) // we have sent all the data
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush
} else {
index++;
I2Cx->DR = reg; // send the subaddress
if (reading || !bytes) // if receiving or sending 0 bytes, flush now
I2C_ITConfig(I2Cx, I2C_IT_BUF, DISABLE); // disable TXE to allow the buffer to flush
}
}
if (index == bytes + 1) { // we have completed the current job
subaddress_sent = 0; // reset this here
if (final_stop) { // If there is a final stop and no more jobs, bus is inactive, disable interrupts to prevent BTF
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Disable EVT and ERR interrupts while bus inactive
}
if (status){
(*status) = I2C_JOB_COMPLETE; // Update status
}
if (complete_CB != NULL){
complete_CB(I2C_JOB_COMPLETE); // Call the custom callback (we are finished)
}
busy = 0;
i2c_job_handler(); // Start the next job (if there is one on the queue)
}
}
void i2cInit(I2CDevice index)
{
NVIC_InitTypeDef nvic;
I2C_InitTypeDef i2c;
if (index > I2CDEV_MAX)
index = I2CDEV_MAX;
// Turn on peripheral clock, save device and index
I2Cx = i2cHardwareMap[index].dev;
I2Cx_index = index;
RCC_APB1PeriphClockCmd(i2cHardwareMap[index].peripheral, ENABLE);
// clock out stuff to make sure slaves arent stuck
// This will also configure GPIO as AF_OD at the end
i2cUnstick();
// Init I2C peripheral
I2C_DeInit(I2Cx);
I2C_StructInit(&i2c);
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE); // Enable EVT and ERR interrupts - they are enabled by the first request
i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_DutyCycle = I2C_DutyCycle_2;
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
i2c.I2C_ClockSpeed = 400000;
I2C_Cmd(I2Cx, ENABLE);
I2C_Init(I2Cx, &i2c);
// I2C ER Interrupt
nvic.NVIC_IRQChannel = i2cHardwareMap[index].er_irq;
// Set the priority to just below the systick interrupt
nvic.NVIC_IRQChannelPreemptionPriority = 2;
nvic.NVIC_IRQChannelSubPriority = 1;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
// I2C EV Interrupt
nvic.NVIC_IRQChannel = i2cHardwareMap[index].ev_irq;
// Set the priority to just below the systick interrupt
nvic.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Init(&nvic);
// Initialize buffer
i2c_init_buffer();
}
uint16_t i2cGetErrorCounter(void)
{
return i2cErrorCount;
}
static void i2cUnstick(void)
{
GPIO_TypeDef *gpio;
gpio_config_t cfg;
uint16_t scl, sda;
int i;
// disable any I2C interrupts
I2C_ITConfig(I2Cx, I2C_IT_EVT | I2C_IT_ERR, DISABLE);
// prepare pins
gpio = i2cHardwareMap[I2Cx_index].gpio;
scl = i2cHardwareMap[I2Cx_index].scl;
sda = i2cHardwareMap[I2Cx_index].sda;
digitalHi(gpio, scl | sda);
cfg.pin = scl | sda;
cfg.speed = Speed_2MHz;
cfg.mode = Mode_Out_OD;
gpioInit(gpio, &cfg);
for (i = 0; i < 8; i++) {
// Wait for any clock stretching to finish
while (!digitalIn(gpio, scl))
delayMicroseconds(10);
// Pull low
digitalLo(gpio, scl); // Set bus low
delayMicroseconds(10);
// Release high again
digitalHi(gpio, scl); // Set bus high
delayMicroseconds(10);
}
// Generate a start then stop condition
// SCL PB10
// SDA PB11
digitalLo(gpio, sda); // Set bus data low
delayMicroseconds(10);
digitalLo(gpio, scl); // Set bus scl low
delayMicroseconds(10);
digitalHi(gpio, scl); // Set bus scl high
delayMicroseconds(10);
digitalHi(gpio, sda); // Set bus sda high
// Init pins
cfg.pin = scl | sda;
cfg.speed = Speed_2MHz;
cfg.mode = Mode_AF_OD;
gpioInit(gpio, &cfg);
// clear the buffer
}
void i2c_job_handler()
{
if(i2c_buffer_count == 0)
{
// the queue is empty, stop performing i2c until
// a new job is enqueued
return;
}
if(busy)
{
// wait for the current job to finish. This function
// will get called again when the job is done
return;
}
// Perform the job on the front of the queue
i2cJob_t* job = i2c_buffer + i2c_buffer_tail;
// First, change status to BUSY
if (job->status)
(*job->status) = I2C_JOB_BUSY;
// perform the appropriate job
if(job->type == READ)
{
i2cReadAsync(job->addr,
job->reg,
job->length,
job->data,
job->status,
job->CB);
}
else
{
i2cWriteAsync(job->addr,
job->reg,
job->length,
job->data,
job->status,
job->CB);
}
// Increment the tail
i2c_buffer_tail = (i2c_buffer_tail + 1) % I2C_BUFFER_SIZE;
// Decrement the number of jobs on the buffer
i2c_buffer_count--;
return;
}
void i2c_queue_job(i2cJobType_t type, uint8_t addr_, uint8_t reg_, uint8_t *data, uint8_t length, volatile uint8_t* status_, void(*CB)(uint8_t))
{
// If this job were going to overflow the buffer, ignore it.
if (i2c_buffer_count >= I2C_BUFFER_SIZE)
{
if(status_)
*status_ = I2C_JOB_ERROR;
return;
}
// Get a pointer to the head
volatile i2cJob_t* job = i2c_buffer + i2c_buffer_head;
// Increment the buffer head for next call
i2c_buffer_head = (i2c_buffer_head + 1) % I2C_BUFFER_SIZE;
// Increment the buffer size
i2c_buffer_count++;
// save the data about the job
job->type = type;
job->data = data;
job->addr = addr_;
job->reg = reg_;
job->length = length;
job->next_job = NULL;
job->status = status_;
job->CB = CB;
// change job status
if (job->status)
(*job->status) = I2C_JOB_QUEUED;
if(i2c_buffer_count == 1)
{
// if the buffer queue was empty, restart i2c job handling
i2c_job_handler();
}
i2c_buffer_lock = false;
return;
}
void i2c_init_buffer()
{
// write zeros to the buffer, and set all the indexes to zero
memset(i2c_buffer, 0, I2C_BUFFER_SIZE*sizeof(i2cJob_t));
i2c_buffer_count = 0;
i2c_buffer_head = 0;
i2c_buffer_tail = 0;
}