-
Notifications
You must be signed in to change notification settings - Fork 0
Reduce code duplication for timeout handling in twi.c #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 12 commits
c87bc7b
a901b08
98c8a13
4efd686
6ba4fd3
c27aef0
5755dde
a8ef5c0
1e7fc4d
14230ea
7fcb4d4
282faf0
4040473
dd34343
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ | |
#include <avr/io.h> | ||
#include <avr/interrupt.h> | ||
#include <compat/twi.h> | ||
#include "Arduino.h" // for digitalWrite | ||
#include "Arduino.h" // for digitalWrite and millis | ||
|
||
#ifndef cbi | ||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) | ||
|
@@ -38,10 +38,38 @@ | |
#include "pins_arduino.h" | ||
#include "twi.h" | ||
|
||
#define BUSYWAIT_WITH_TIMEOUT_UNTIL(exit_condition, timedout_label) \ | ||
do { \ | ||
uint32_t startMillis = millis(); \ | ||
while (!(exit_condition)) { \ | ||
if ((twi_timeout_ms > 0) && (millis() - startMillis > twi_timeout_ms)) { \ | ||
goto timedout_label; \ | ||
} \ | ||
} \ | ||
} while (0) | ||
|
||
// Same as BUSYWAIT_WITH_TIMEOUT_UNTIL but intended to be used in code | ||
// executed in interrupt service routines where millis() cannot be used. | ||
// https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/: | ||
// "millis() relies on interrupts to count, so it will never increment inside an ISR." | ||
// TODO: Document calculations behind the 25000 upper counter limit. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this pull request
I will figure something out. |
||
// TODO: Make the upper counter limit reflect the twi_timeout_ms value. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That seems to be - more or less - to be defined by a similar macro as commented above. |
||
#define IRS_BUSYWAIT_WITH_TIMEOUT_UNTIL(exit_condition, timedout_label) \ | ||
do { \ | ||
uint32_t counter = 0; \ | ||
while (!(exit_condition)) { \ | ||
counter++; \ | ||
if ((twi_timeout_ms > 0) && (counter >= 25000)) { \ | ||
goto timedout_label; \ | ||
} \ | ||
} \ | ||
} while (0) | ||
|
||
static volatile uint8_t twi_state; | ||
static volatile uint8_t twi_slarw; | ||
static volatile uint8_t twi_sendStop; // should the transaction end with a stop | ||
static volatile uint8_t twi_inRepStart; // in the middle of a repeated start | ||
static volatile uint8_t twi_timeout_ms = 0; | ||
|
||
static void (*twi_onSlaveTransmit)(void); | ||
static void (*twi_onSlaveReceive)(uint8_t*, int); | ||
|
@@ -154,9 +182,8 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen | |
} | ||
|
||
// wait until twi is ready, become master receiver | ||
while(TWI_READY != twi_state){ | ||
continue; | ||
} | ||
BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state == TWI_READY, waiting_timedout); | ||
|
||
twi_state = TWI_MRX; | ||
twi_sendStop = sendStop; | ||
// reset error state (0xFF.. no error occured) | ||
|
@@ -193,9 +220,7 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen | |
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); | ||
|
||
// wait for read operation to complete | ||
while(TWI_MRX == twi_state){ | ||
continue; | ||
} | ||
BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state != TWI_MRX, waiting_timedout); | ||
|
||
if (twi_masterBufferIndex < length) | ||
length = twi_masterBufferIndex; | ||
|
@@ -206,6 +231,11 @@ uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sen | |
} | ||
|
||
return length; | ||
|
||
waiting_timedout: | ||
twi_disable(); | ||
twi_init(); | ||
return 0; | ||
} | ||
|
||
/* | ||
|
@@ -233,9 +263,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait | |
} | ||
|
||
// wait until twi is ready, become master transmitter | ||
while(TWI_READY != twi_state){ | ||
continue; | ||
} | ||
BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state == TWI_READY, waiting_timedout); | ||
|
||
twi_state = TWI_MTX; | ||
twi_sendStop = sendStop; | ||
// reset error state (0xFF.. no error occured) | ||
|
@@ -275,8 +304,8 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait | |
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs | ||
|
||
// wait for write operation to complete | ||
while(wait && (TWI_MTX == twi_state)){ | ||
continue; | ||
if (wait) { | ||
BUSYWAIT_WITH_TIMEOUT_UNTIL(twi_state != TWI_MTX, waiting_timedout); | ||
} | ||
|
||
if (twi_error == 0xFF) | ||
|
@@ -287,6 +316,11 @@ uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait | |
return 3; // error: data send, nack received | ||
else | ||
return 4; // other twi error | ||
|
||
waiting_timedout: | ||
twi_disable(); | ||
twi_init(); | ||
return 4; | ||
} | ||
|
||
/* | ||
|
@@ -373,12 +407,14 @@ void twi_stop(void) | |
|
||
// wait for stop condition to be exectued on bus | ||
// TWINT is not set after a stop condition! | ||
while(TWCR & _BV(TWSTO)){ | ||
continue; | ||
} | ||
IRS_BUSYWAIT_WITH_TIMEOUT_UNTIL((TWCR & _BV(TWSTO)) == 0, waiting_timedout); | ||
|
||
// update twi state | ||
twi_state = TWI_READY; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hlovdal , is here (before waiting_timedout label) a return; statement missing? Currently the TWI is always disabled and initated again, no matter if it was timedout or not. Am i rght? |
||
waiting_timedout: | ||
twi_disable(); | ||
twi_init(); | ||
} | ||
|
||
/* | ||
|
@@ -396,6 +432,11 @@ void twi_releaseBus(void) | |
twi_state = TWI_READY; | ||
} | ||
|
||
void twi_setTimeoutInMillis(uint8_t timeout) | ||
{ | ||
twi_timeout_ms = timeout; | ||
} | ||
|
||
ISR(TWI_vect) | ||
{ | ||
switch(TW_STATUS){ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hlovdal, Is this do {} while(0) a way to put the macro around a simple code block, so the BUSYWAIT_WITH_TIMEOUT_UNTIL macro can be put multiple times in the same function and the compiler will not complain about multiple startMillis variable definition?