Skip to content

Commit

Permalink
[sw] update TWI HAL
Browse files Browse the repository at this point in the history
add non-blocking functions
  • Loading branch information
stnolting committed Mar 26, 2024
1 parent 6ac8b1c commit 2f84f25
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 132 deletions.
88 changes: 51 additions & 37 deletions sw/lib/include/neorv32_twi.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// # ********************************************************************************************* #
// # BSD 3-Clause License #
// # #
// # Copyright (c) 2023, Stephan Nolting. All rights reserved. #
// # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
// # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
// # #
// # Redistribution and use in source and binary forms, with or without modification, are #
// # permitted provided that the following conditions are met: #
Expand All @@ -28,8 +29,6 @@
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
// # ********************************************************************************************* #
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
// #################################################################################################


Expand All @@ -50,57 +49,72 @@
/** TWI module prototype */
typedef volatile struct __attribute__((packed,aligned(4))) {
uint32_t CTRL; /**< offset 0: control register (#NEORV32_TWI_CTRL_enum) */
uint32_t DATA; /**< offset 4: data register (#NEORV32_TWI_DATA_enum) */
uint32_t DCMD; /**< offset 4: data/cmd register (#NEORV32_TWI_DCMD_enum) */
} neorv32_twi_t;

/** TWI module hardware access (#neorv32_twi_t) */
#define NEORV32_TWI ((neorv32_twi_t*) (NEORV32_TWI_BASE))

/** TWI control register bits */
enum NEORV32_TWI_CTRL_enum {
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */
TWI_CTRL_START = 1, /**< TWI control register(1) (-/w): Generate START condition, auto-clears */
TWI_CTRL_STOP = 2, /**< TWI control register(2) (-/w): Generate STOP condition, auto-clears */
TWI_CTRL_MACK = 3, /**< TWI control register(3) (r/w): Generate ACK by controller for each transmission */
TWI_CTRL_CSEN = 4, /**< TWI control register(4) (r/w): Allow clock stretching when set */
TWI_CTRL_PRSC0 = 5, /**< TWI control register(5) (r/w): Clock prescaler select bit 0 */
TWI_CTRL_PRSC1 = 6, /**< TWI control register(6) (r/w): Clock prescaler select bit 1 */
TWI_CTRL_PRSC2 = 7, /**< TWI control register(7) (r/w): Clock prescaler select bit 2 */
TWI_CTRL_CDIV0 = 8, /**< TWI control register(8) (r/w): Clock divider bit 0 */
TWI_CTRL_CDIV1 = 9, /**< TWI control register(9) (r/w): Clock divider bit 1 */
TWI_CTRL_CDIV2 = 10, /**< TWI control register(10) (r/w): Clock divider bit 2 */
TWI_CTRL_CDIV3 = 11, /**< TWI control register(11) (r/w): Clock divider bit 3 */

TWI_CTRL_CLAIMED = 29, /**< TWI control register(29) (r/-): Set if the TWI bus is currently claimed by any controller */
TWI_CTRL_ACK = 30, /**< TWI control register(30) (r/-): ACK received when set */
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Transfer in progress, busy flag */
TWI_CTRL_EN = 0, /**< TWI control register(0) (r/w): TWI enable */
TWI_CTRL_PRSC0 = 1, /**< TWI control register(1) (r/w): Clock prescaler select bit 0 */
TWI_CTRL_PRSC1 = 2, /**< TWI control register(2) (r/w): Clock prescaler select bit 1 */
TWI_CTRL_PRSC2 = 3, /**< TWI control register(3) (r/w): Clock prescaler select bit 2 */
TWI_CTRL_CDIV0 = 4, /**< TWI control register(4) (r/w): Clock divider bit 0 */
TWI_CTRL_CDIV1 = 5, /**< TWI control register(5) (r/w): Clock divider bit 1 */
TWI_CTRL_CDIV2 = 6, /**< TWI control register(6) (r/w): Clock divider bit 2 */
TWI_CTRL_CDIV3 = 7, /**< TWI control register(7) (r/w): Clock divider bit 3 */

TWI_CTRL_FIFO_LSB = 15, /**< SPI control register(15) (r/-): log2(FIFO size), lsb */
TWI_CTRL_FIFO_MSB = 18, /**< SPI control register(18) (r/-): log2(FIFO size), msb */

TWI_CTRL_TX_FULL = 29, /**< TWI control register(29) (r/-): TX FIFO full */
TWI_CTRL_RX_AVAIL = 30, /**< TWI control register(30) (r/-): RX FIFO data available */
TWI_CTRL_BUSY = 31 /**< TWI control register(31) (r/-): Bus engine busy or TX FIFO not empty */
};

/** TWI receive/transmit data register bits */
enum NEORV32_TWI_DATA_enum {
TWI_DATA_LSB = 0, /**< TWI data register(0) (r/w): Receive/transmit data (8-bit) LSB */
TWI_DATA_MSB = 7 /**< TWI data register(7) (r/w): Receive/transmit data (8-bit) MSB */
/** TWI command/data register bits */
enum NEORV32_TWI_DCMD_enum {
TWI_DCMD_LSB = 0, /**< TWI data register(0) (r/w): Receive/transmit data (8-bit) LSB */
TWI_DCMD_MSB = 7, /**< TWI data register(7) (r/w): Receive/transmit data (8-bit) MSB */
TWI_DCMD_ACK = 8, /**< TWI data register(8) (r/w): RX = ACK/NACK, TX = MACK */
TWI_DCMD_CMD_LO = 9, /**< TWI data register(9) (r/w): CMD lsb */
TWI_DCMD_CMD_HI = 10 /**< TWI data register(10) (r/w): CMD msb */
};
/**@}*/

/**********************************************************************//**
* @name TWI commands
**************************************************************************/
/**@{*/
#define TWI_CMD_NOP (0b00) // no operation
#define TWI_CMD_START (0b01) // generate start condition
#define TWI_CMD_STOP (0b10) // generate stop condition
#define TWI_CMD_RTX (0b11) // transmit+receive data byte
/**@}*/


/**********************************************************************//**
* @name Prototypes
**************************************************************************/
/**@{*/
int neorv32_twi_available(void);
void neorv32_twi_setup(int prsc, int cdiv, int csen);
void neorv32_twi_disable(void);
void neorv32_twi_enable(void);
void neorv32_twi_mack_enable(void);
void neorv32_twi_mack_disable(void);
int neorv32_twi_busy(void);
int neorv32_twi_start_trans(uint8_t a);
int neorv32_twi_trans(uint8_t d);
uint8_t neorv32_twi_get_data(void);
void neorv32_twi_generate_stop(void);
void neorv32_twi_generate_start(void);
int neorv32_twi_bus_claimed(void);
int neorv32_twi_available(void);
void neorv32_twi_setup(int prsc, int cdiv);
int neorv32_twi_get_fifo_depth(void);
void neorv32_twi_disable(void);
void neorv32_twi_enable(void);

int neorv32_twi_busy(void);
int neorv32_twi_get(uint8_t *data);

int neorv32_twi_trans(uint8_t *data, int mack);
void neorv32_twi_generate_stop(void);
void neorv32_twi_generate_start(void);

void neorv32_twi_send_nonblocking(uint8_t data, int mack);
void neorv32_twi_generate_stop_nonblocking(void);
void neorv32_twi_generate_start_nonblocking(void);
/**@}*/


Expand Down
141 changes: 76 additions & 65 deletions sw/lib/source/neorv32_twi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
// # ********************************************************************************************* #
// # BSD 3-Clause License #
// # #
// # Copyright (c) 2022, Stephan Nolting. All rights reserved. #
// # The NEORV32 RISC-V Processor, https://github.com/stnolting/neorv32 #
// # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
// # #
// # Redistribution and use in source and binary forms, with or without modification, are #
// # permitted provided that the following conditions are met: #
Expand All @@ -28,8 +29,6 @@
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
// # ********************************************************************************************* #
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
// #################################################################################################


Expand Down Expand Up @@ -65,59 +64,51 @@ int neorv32_twi_available(void) {
*
* @param[in] prsc Clock prescaler select (0..7). See #NEORV32_CLOCK_PRSC_enum.
* @param[in] cdiv Clock divider (0..15).
* @param[in] csen Allow clock stretching when 1.
**************************************************************************/
void neorv32_twi_setup(int prsc, int cdiv, int csen) {
void neorv32_twi_setup(int prsc, int cdiv) {

NEORV32_TWI->CTRL = 0; // reset

uint32_t ctrl = 0;
ctrl |= ((uint32_t)( 1) << TWI_CTRL_EN);
ctrl |= ((uint32_t)(prsc & 0x07) << TWI_CTRL_PRSC0);
ctrl |= ((uint32_t)(cdiv & 0x0F) << TWI_CTRL_CDIV0);
ctrl |= ((uint32_t)(csen & 0x01) << TWI_CTRL_CSEN);
ctrl |= ((uint32_t)(cdiv & 0x0f) << TWI_CTRL_CDIV0);
NEORV32_TWI->CTRL = ctrl;
}


/**********************************************************************//**
* Disable TWI controller.
**************************************************************************/
void neorv32_twi_disable(void) {

NEORV32_TWI->CTRL &= ~((uint32_t)(1 << TWI_CTRL_EN));
}


/**********************************************************************//**
* Enable TWI controller.
* Get TWI FIFO depth.
*
* @return FIFO depth (number of entries), zero if no FIFO implemented
**************************************************************************/
void neorv32_twi_enable(void) {
int neorv32_twi_get_fifo_depth(void) {

NEORV32_TWI->CTRL |= (uint32_t)(1 << TWI_CTRL_EN);
uint32_t tmp = (NEORV32_TWI->CTRL >> TWI_CTRL_FIFO_LSB) & 0x0f;
return (int)(1 << tmp);
}


/**********************************************************************//**
* Activate sending ACKs by controller (MACK).
* Disable TWI controller.
**************************************************************************/
void neorv32_twi_mack_enable(void) {
void neorv32_twi_disable(void) {

NEORV32_TWI->CTRL |= ((uint32_t)(1 << TWI_CTRL_MACK));
NEORV32_TWI->CTRL &= ~((uint32_t)(1 << TWI_CTRL_EN));
}


/**********************************************************************//**
* Deactivate sending ACKs by controller (MACK).
* Enable TWI controller.
**************************************************************************/
void neorv32_twi_mack_disable(void) {
void neorv32_twi_enable(void) {

NEORV32_TWI->CTRL &= ~((uint32_t)(1 << TWI_CTRL_MACK));
NEORV32_TWI->CTRL |= (uint32_t)(1 << TWI_CTRL_EN);
}


/**********************************************************************//**
* Check if TWI is busy.
* Check if TWI is busy (TWI bus engine busy or TX FIFO not empty).
*
* @return 0 if idle, 1 if busy
**************************************************************************/
Expand All @@ -133,52 +124,47 @@ int neorv32_twi_busy(void) {


/**********************************************************************//**
* Generate START condition and send first byte (address including R/W bit).
* Get received data + ACK/NACH from RX FIFO.
*
* @note Blocking function.
*
* @param[in] a Data byte including 7-bit address and R/W-bit (lsb).
* @return 0: ACK received, 1: NACK received.
* @param[in,out] data Pointer for returned data (uint8_t).
* @return RX FIFO access status (-1 = no data available, 0 = ACK received, 1 = NACK received).
**************************************************************************/
int neorv32_twi_start_trans(uint8_t a) {
int neorv32_twi_get(uint8_t *data) {

neorv32_twi_generate_start(); // generate START condition
if ((NEORV32_TWI->CTRL & (1<<TWI_CTRL_RX_AVAIL)) == 0) { // no data available
return -1;
}

return neorv32_twi_trans(a); // transfer address
uint32_t tmp = NEORV32_TWI->DCMD;
*data = (uint8_t)tmp;
return (int)((tmp >> TWI_DCMD_ACK) & 1);
}


/**********************************************************************//**
* Send data byte and also receive data byte (can be read via neorv32_twi_get_data()).
* TWI transfer: send data byte and also receive data byte.
*
* @note Blocking function.
*
* @param[in] d Data byte to be send.
* @param[in,out] data Pointer for TX/RX data (uint8_t).
* @param[in] mack Generate ACK by host controller when set.
* @return 0: ACK received, 1: NACK received.
**************************************************************************/
int neorv32_twi_trans(uint8_t d) {
int neorv32_twi_trans(uint8_t *data, int mack) {

NEORV32_TWI->DATA = (uint32_t)d; // send data
while (NEORV32_TWI->CTRL & (1 << TWI_CTRL_BUSY)); // wait until idle again
uint8_t rx_data;
int device_ack;

// check for ACK/NACK
if (NEORV32_TWI->CTRL & (1 << TWI_CTRL_ACK)) {
return 0; // ACK received
}
else {
return 1; // NACK received
}
}
while (NEORV32_TWI->CTRL & (1<<TWI_CTRL_TX_FULL)); // wait for free TX entry

neorv32_twi_send_nonblocking(*data, mack); // send address + R/W (+ host ACK)

/**********************************************************************//**
* Get received data from last transmission.
*
* @return 0: Last received data byte.
**************************************************************************/
uint8_t neorv32_twi_get_data(void) {
do {
device_ack = neorv32_twi_get(&rx_data);
} while (device_ack == -1); // wait until data available

return (uint8_t)NEORV32_TWI->DATA; // get RX data from previous transmission
*data = rx_data;
return device_ack;
}


Expand All @@ -189,7 +175,8 @@ uint8_t neorv32_twi_get_data(void) {
**************************************************************************/
void neorv32_twi_generate_stop(void) {

NEORV32_TWI->CTRL |= (uint32_t)(1 << TWI_CTRL_STOP); // generate STOP condition
while (NEORV32_TWI->CTRL & (1<<TWI_CTRL_TX_FULL)); // wait for free TX entry
neorv32_twi_generate_stop_nonblocking();
while (NEORV32_TWI->CTRL & (1 << TWI_CTRL_BUSY)); // wait until idle again
}

Expand All @@ -201,22 +188,46 @@ void neorv32_twi_generate_stop(void) {
**************************************************************************/
void neorv32_twi_generate_start(void) {

NEORV32_TWI->CTRL |= (1 << TWI_CTRL_START); // generate START condition
while (NEORV32_TWI->CTRL & (1<<TWI_CTRL_TX_FULL)); // wait for free TX entry
neorv32_twi_generate_start_nonblocking();
while (NEORV32_TWI->CTRL & (1 << TWI_CTRL_BUSY)); // wait until idle again
}


/**********************************************************************//**
* Check if the TWI bus is currently claimed by any controller.
* Send data byte (RX can be read via neorv32_twi_get()).
*
* @note Non-blocking function; does not check the TX FIFO.
*
* @return 0: 0 if bus is not claimed, 1 if bus is claimed.
* @param[in] data Data byte to be send.
* @param[in] mack Generate ACK by host controller when set.
**************************************************************************/
int neorv32_twi_bus_claimed(void) {
void neorv32_twi_send_nonblocking(uint8_t data, int mack) {

if (NEORV32_TWI->CTRL & (1 << TWI_CTRL_CLAIMED)) {
return 1;
}
else {
return 0;
}
uint32_t cmd = (uint32_t)data;
cmd |= (uint32_t)((mack & 1) << TWI_DCMD_ACK);
cmd |= (uint32_t)(TWI_CMD_RTX << TWI_DCMD_CMD_LO);
NEORV32_TWI->DCMD = cmd;
}


/**********************************************************************//**
* Generate STOP condition.
*
* @note Non-blocking function; does not check the TX FIFO.
**************************************************************************/
void neorv32_twi_generate_stop_nonblocking(void) {

NEORV32_TWI->DCMD = (uint32_t)(TWI_CMD_STOP << TWI_DCMD_CMD_LO);
}


/**********************************************************************//**
* Generate START (or REPEATED-START) condition.
*
* @note Non-blocking function; does not check the TX FIFO.
**************************************************************************/
void neorv32_twi_generate_start_nonblocking(void) {

NEORV32_TWI->DCMD = (uint32_t)(TWI_CMD_START << TWI_DCMD_CMD_LO);
}
Loading

0 comments on commit 2f84f25

Please sign in to comment.