Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/avarix.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#define MAX(x,y) ((x) > (y) ? (x) : (y))
/// Clamp a value to range [x;y]
#define CLAMP(v,x,y) ((v) < (x) ? (x) : (v) > (y) ? (y) : (v))

/// Get number of elements in an array
#define LENGTHOF(a) ((sizeof(a)/sizeof((a)[0]))/(sizeof(a)%sizeof((a)[0]) == 0))

#endif
//@}
1 change: 0 additions & 1 deletion modules/i2c/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,4 @@ int8_t i2cm_recv(i2cm_t *m, uint8_t addr, uint8_t *data, uint8_t n)
return i;
}


///@endcond
76 changes: 68 additions & 8 deletions modules/i2c/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,69 @@ typedef struct i2cm_struct i2cm_t;
/// I2C slave state
typedef struct i2cs_struct i2cs_t;

/// State of i2cx
extern i2cT_t *const i2cx;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ça n'est plus correct pour i2cs_t mais ça l'est toujours pour i2cm_t.
À adapter pour préserver la doc de i2cm_t et adapter pour i2cs_t.

Au passage, i2cs_t est censée être "privée" ou pas ?
Si elle est privée, le mieux serait d'exporter des i2cs_t* et cacher la définition de i2cs_t (la sortir du .h).


#else

typedef struct TWI_MASTER_struct i2cm_t;
typedef struct i2cs_struct i2cs_t;

typedef enum {
I2CS_STATE_NONE,
I2CS_STATE_READ,
I2CS_STATE_WRITE,

} i2cs_state_t;

#ifndef I2CS_RECV_BUFFER_SIZE
# define I2CS_RECV_BUFFER_SIZE 32
#endif

#ifndef I2CS_SEND_BUFFER_SIZE
# define I2CS_SEND_BUFFER_SIZE 32
#endif

/** @brief I2C slave master-write frame received
*
* @param buffer buffer containing the received bytes
* @param n number of bytes received from master
*
* This function is called when a master-write operation has completed
*/
typedef void (*i2cs_recv_callback_t)(uint8_t *buffer, uint8_t n);

/** @brief I2C slave master-read operation was requested
*
* @param buffer buffer to provision
* @param maxsz maximum number of bytes which can be written to buffer
* @return number of bytes to send, returning 0 will result in a NACK from slave.
*
* This function is called when a master-read operation was requested by master
* and ask user to provision the buffer which will be sent.
*/
typedef uint8_t (*i2cs_prepare_send_callback_t)(uint8_t *buffer, uint8_t maxsz);

/** @brief I2C slave transaction finished successfully or not
*
* This function is called when a STOP condition or any bus error has ended current transaction
*/
typedef void (*i2cs_reset_callback_t)(void);

typedef struct {

i2cs_state_t state;

uint8_t recvd_bytes;
uint8_t recv_buffer[I2CS_RECV_BUFFER_SIZE];

uint8_t sent_bytes;
uint8_t bytes_to_send;
uint8_t send_buffer[I2CS_SEND_BUFFER_SIZE];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ton avis sur la possibilité de mutualiser les variables recv/send (notamment les buffers) ?
(Sachant que tu peux utiliser une union pour garder des noms pertinents au besoin.)


i2cs_recv_callback_t recv_callback;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tu peux retirer les lignes vides entre chaque définition de callback.

i2cs_prepare_send_callback_t prepare_send_callback;

i2cs_reset_callback_t reset_callback;

} i2cs_t;

// Check for I2C enabled as both master and slave
// Define pointers to internal structures
Expand All @@ -42,31 +98,35 @@ typedef struct i2cs_struct i2cs_t;
#elif (defined I2CC_MASTER)
# define i2cC (&TWIC.MASTER)
#elif (defined I2CC_SLAVE)
extern i2cs_t *const i2cC;
# define X_(p,s) p ## C ## s
# include "slavex.inc.h"
#endif

#if (defined I2CD_MASTER) && (defined I2CD_SLAVE)
# error I2CD enabled as both master and slave
#elif (defined I2CD_MASTER)
# define i2cD (&TWID.MASTER)
#elif (defined I2CD_SLAVE)
extern i2cs_t *const i2cD;
# define X_(p,s) p ## D ## s
# include "slavex.inc.h"
#endif

#if (defined I2CE_MASTER) && (defined I2CE_SLAVE)
# error I2CE enabled as both master and slave
#elif (defined I2CE_MASTER)
# define i2cE (&TWIE.MASTER)
#elif (defined I2CE_SLAVE)
extern i2cs_t *const i2cE;
# define X_(p,s) p ## E ## s
# include "slavex.inc.h"
#endif

#if (defined I2CF_MASTER) && (defined I2CF_SLAVE)
# error I2CF enabled as both master and slave
#elif (defined I2CF_MASTER)
# define i2cF (&TWIF.MASTER)
#elif (defined I2CF_SLAVE)
extern i2cs_t *const i2cF;
# define X_(p,s) p ## F ## s
# include "slavex.inc.h"
#endif

#endif
Expand Down
188 changes: 187 additions & 1 deletion modules/i2c/slavex.inc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,193 @@
* The X_(p,s) macro must be defined before including.
* It is automatically undefined at the end of this file.
*/
#include <avarix.h>
#include <avr/interrupt.h>
#include <stddef.h>

#error Slave mode not supported yet
#define I2CX(s) X_(I2C,s)
#define i2cX(s) X_(i2c,s)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should define and use i2csX(s) X_(i2cs,s) instead, for method names and everything that is slave-specific.

#define TWIX X_(TWI,)
#define twiX(s) X_(TWI,s)

// declare i2cX singleton
i2cs_t i2cX();

void i2cX(_init)(void)
{

i2cX().state = I2CS_STATE_NONE;

i2cX().reset_callback = NULL;
i2cX().prepare_send_callback = NULL;
i2cX().recv_callback = NULL;

// initialize hardware
TWIX.SLAVE.CTRLA = TWI_SLAVE_ENABLE_bm
| TWI_SLAVE_APIEN_bm
| TWI_SLAVE_DIEN_bm
| TWI_SLAVE_PIEN_bm
| ((I2CX(_INTLVL) << TWI_SLAVE_INTLVL_gp) & TWI_SLAVE_INTLVL_gm);
TWIX.SLAVE.ADDR = I2CX(_ADDRESS) << 1;
}

void i2cX(s_register_reset_callback)(i2cs_reset_callback_t f)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Une raison pour ne pas plutôt avoir une fonction qui prend en paramètre le i2cs_t* à modifier ?
Pour l'init on peut rester avec une fonction part I2C (vu que les init sont appellées d'un bloc par le module, de mémoire). Par contre pour les accesseurs ça rend la lib plus générique (pas besoin de connaître le nom de la fonction, on a juste à fournir le bon objet).

C'est comme ça que fonctionne le module UART notamment.

{
INTLVL_DISABLE_BLOCK(I2CX(_INTLVL)) {
i2cX().reset_callback = f;
}
}

void i2cX(s_register_recv_callback)(i2cs_recv_callback_t f)
{
INTLVL_DISABLE_BLOCK(I2CX(_INTLVL)) {
i2cX().recv_callback = f;
}
}

void i2cX(s_register_prepare_send_callback)(i2cs_prepare_send_callback_t f)
{
INTLVL_DISABLE_BLOCK(I2CX(_INTLVL)) {
i2cX().prepare_send_callback = f;
}
}

// Interrupt handler
ISR(twiX(_TWIS_vect))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La routine d'interruption étant conséquente, ça serait intéressant d'ajouter une indirection vers une fonction générique qui prendrait un i2cs_t* en paramètre.

Là encore, ça suit ce qui est fait pour l'UART.

{

i2cs_t *i2cs = &i2cX();

uint8_t status = TWIX.SLAVE.STATUS;

if(status & TWI_SLAVE_BUSERR_bm || status & TWI_SLAVE_COLL_bm) {
// bus error happened
i2cs->state = I2CS_STATE_NONE;
if(i2cs->reset_callback) {
i2cs->reset_callback();
}
return;
}

else if(status & TWI_SLAVE_APIF_bm) {
// address / stop interrupt

// check previous state, call recvd callback
// if previous frame was a master-write
if(i2cs->state == I2CS_STATE_WRITE) {
if(i2cs->recv_callback) {
i2cs->recv_callback(i2cs->recv_buffer, i2cs->recvd_bytes);
}
}

if(status & TWI_SLAVE_AP_bm) {
// valid address interrupt
if(status & TWI_SLAVE_DIR_bm) {
// master read operation

i2cs->state = I2CS_STATE_READ;
if(i2cs->prepare_send_callback) {

i2cs->sent_bytes = 0;

// ask user to provision send buffer
uint8_t rsz = i2cs->prepare_send_callback(i2cs->send_buffer,
sizeof(i2cs->send_buffer));
if(rsz > 0) {
// user got some data to send
i2cs->bytes_to_send = MIN(rsz, sizeof(i2cs->send_buffer));
// ACK
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc;
return;
}
i2cs->bytes_to_send = 0;
}

// NACK, refuse read
TWIX.SLAVE.CTRLB = TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_RESPONSE_gc;
return;
}
else {
// master write operation
i2cs->state = I2CS_STATE_WRITE;

// clear recv buffer
i2cs->recvd_bytes = 0;
// confirm operation
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc;
}
return;
}
else {
// STOP condition interrupt
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc;

i2cs->state = I2CS_STATE_NONE;
if(i2cs->reset_callback) {
i2cs->reset_callback();
}
return;
}
}

else if(status & TWI_SLAVE_DIF_bm) {
// data interruption
if(status & TWI_SLAVE_DIR_bm) {
// master read operation

if(i2cs->sent_bytes > 0 && (status & TWI_SLAVE_RXACK_bm)) {
// previous byte was NACKed by master
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc;
i2cs->state = I2CS_STATE_NONE;
return;
}

if(i2cs->sent_bytes < i2cs->bytes_to_send) {
// push byte
uint8_t byte = i2cs->send_buffer[i2cs->sent_bytes++];
TWIX.SLAVE.DATA = byte;

// ACK read, continue transmission
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc;
}
else {
// ACK read, end transmission
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_COMPTRANS_gc;
}

return;
}
else {
// master write operation
uint8_t byte = TWIX.SLAVE.DATA;

if(i2cs->recvd_bytes < I2CS_RECV_BUFFER_SIZE) {
i2cs->recv_buffer[i2cs->recvd_bytes++] = byte;

// ACK write
TWIX.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc;
}
else {
// NACK write, buffer is full
TWIX.SLAVE.CTRLB = TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_COMPTRANS_gc;
}

return;
}
}

// we encounter an unmanaged case
i2cs->state = I2CS_STATE_NONE;
i2cs->recvd_bytes = 0;
i2cs->sent_bytes = 0;
// reset client-side state
if(i2cs->reset_callback) {
i2cs->reset_callback();
}
}

#undef I2CX
#undef i2cX
#undef TWIX
#undef twiX
#undef X_
25 changes: 25 additions & 0 deletions modules/i2c/slavex.inc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/** @defgroup i2c I2C
* @brief I2C module
*/
//@{
/**
* @file
* @brief I2C slave definitions
*/
#define i2cX(s) X_(i2c,s)

// I2cX singleton
extern i2cs_t i2cX();

/** @brief Register f to be called whenever a master-read operation was requested and user need
* to provision the send buffer */
void i2cX(s_register_prepare_send_callback)(i2cs_prepare_send_callback_t f);

/** @brief Register f to be called whenever a master-write operation as finished */
void i2cX(s_register_recv_callback)(i2cs_recv_callback_t f);

/** @brief Register f to be called whenever the i2c transaction was terminated (on STOP or ERROR) */
void i2cX(s_register_reset_callback)(i2cs_reset_callback_t f);

#undef i2cX
#undef X_