Skip to content
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

[wiring] Add I2C and SPI support #269

Draft
wants to merge 25 commits into
base: master
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
04bd58f
[libs] Use incomplete types in SerialClass data pattern
kuba2k2 Sep 21, 2023
975bb68
[libs] Declare Serial instances in common core
kuba2k2 Sep 21, 2023
66eadf3
[libs] Move Serial constructor to header, use nullptr
kuba2k2 Sep 21, 2023
e0a746d
[boards] Update boardgen to v0.10.0
kuba2k2 Sep 21, 2023
9d1be85
[wiring] Fix missing random() function
kuba2k2 Sep 22, 2023
b1d17cb
[libs] Add compile-time feature check for Serial library
kuba2k2 Sep 22, 2023
a039656
[libs] Implement Wire API library
kuba2k2 Sep 22, 2023
6ff9872
[realtek-ambz] Implement Wire library, remove old unit
kuba2k2 Sep 22, 2023
2b54491
[wiring] Replace sdk_private.h with ArduinoPrivate.h
kuba2k2 Sep 23, 2023
7af25d4
[realtek-ambz] Use ROM table for Serial address
kuba2k2 Sep 23, 2023
4f4d3a9
[wiring] Extract utilities from Wire
kuba2k2 Sep 23, 2023
8576eba
[wiring] Implement SPI API library
kuba2k2 Sep 23, 2023
89f4745
[realtek-ambz] Implement SPI library
kuba2k2 Sep 23, 2023
ca096d8
Merge remote-tracking branch 'origin/master' into feature/i2c-spi
kuba2k2 Dec 8, 2023
ca8d767
[boards] Update boardgen to add SPI support
kuba2k2 Dec 8, 2023
6a12a0d
[wiring] Use enum for endTransmission() result
kuba2k2 Dec 9, 2023
5f3c27e
[wiring] Use private methods for Serial, Wire, SPI begin/end
kuba2k2 Dec 10, 2023
b6e54f2
[beken-72xx] Implement Wire master write on I2C1
kuba2k2 Dec 10, 2023
058099a
[beken-72xx] Flatten I2C register structs, add descriptions
kuba2k2 Dec 10, 2023
1ed395b
[beken-72xx] Implement Wire master read on I2C1
kuba2k2 Dec 11, 2023
cf3f23e
[wiring] Allow creating parameterless Wire instance
kuba2k2 Dec 12, 2023
54cc794
Merge remote-tracking branch 'origin/master' into feature/i2c-spi
kuba2k2 Mar 14, 2024
d54b863
[beken-72xx] Add draft support for I2C2
kuba2k2 Mar 15, 2024
c099503
[core] Add missing SPI begin, add weak transfer() and write()
kuba2k2 Mar 26, 2024
7630a23
[beken-72xx] Add draft SPI TX support
kuba2k2 Mar 26, 2024
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
Prev Previous commit
Next Next commit
[beken-72xx] Add draft support for I2C2
kuba2k2 committed Mar 15, 2024

Verified

This commit was signed with the committer’s verified signature.
kuba2k2 Kuba Szczodrzyński
commit d54b8633856594a157a5ad9bbee2e2c0dbda95f3
281 changes: 236 additions & 45 deletions cores/beken-72xx/arduino/libraries/Wire/Wire.cpp
Original file line number Diff line number Diff line change
@@ -13,16 +13,30 @@ enum I2CISRMode {
I2C_ISR_SLAVE_READ = 4,
};

static I2CISRMode i2c1Mode = I2C_ISR_IDLE;
static bool i2c1SendStop = false;
static bool i2c1GotAck = false;

static I2CISRMode i2c1Mode = I2C_ISR_IDLE;
static bool i2c1SendStop = false;
static bool i2c1GotAck = false;
static RingBuffer *i2c1TxBuf = nullptr;

static RingBuffer *i2c1RxBuf = nullptr;
static int32_t i2c1RxLength = 0;

static I2CISRMode i2c2Mode = I2C_ISR_IDLE;
static bool i2c2SendStop = false;
static bool i2c2GotAck = false;
static RingBuffer *i2c2TxBuf = nullptr;
static RingBuffer *i2c2RxBuf = nullptr;
static int32_t i2c2RxLength = 0;

static void i2c1Handler();
static void i2c2Handler();

#define REG_I2C2_READ_ALL(x) \
x.config = REG_I2C2->config; \
x.status = REG_I2C2->status;

#define REG_I2C2_WRITE_ALL(x) \
REG_I2C2->status = x.status; \
REG_I2C2->config = x.config;

bool TwoWire::beginPrivate(uint8_t address, uint32_t frequency) {
if (!this->data)
@@ -58,11 +72,11 @@ bool TwoWire::beginPrivate(uint8_t address, uint32_t frequency) {
// i2c2_hardware_init()
REG_I2C2->config = 0;
REG_I2C2->status = 0;
// intc_service_register(IRQ_I2C2, PRI_IRQ_I2C2, i2c2Handler);
intc_service_register(IRQ_I2C2, PRI_IRQ_I2C2, i2c2Handler);
// i2c2_set_idle_cr(0x3)
REG_I2C2->IDLE_CR = 0b11;
REG_I2C2->IDLE_CR = 3;
// i2c2_set_scl_cr(0x4)
REG_I2C2->SCL_CR = 0b100;
REG_I2C2->SCL_CR = 4;
// i2c2_set_smbus_cs(0x3)
REG_I2C2->SMBCS = 0b11;
// i2c2_set_timeout_en(1)
@@ -76,6 +90,9 @@ bool TwoWire::beginPrivate(uint8_t address, uint32_t frequency) {
icu_ctrl(CMD_CONF_PCLK_26M, &param);
param = PWD_I2C2_CLK_BIT;
icu_ctrl(CMD_CLK_PWR_UP, &param);
// i2c1_enable_interrupt()
param = IRQ_I2C2_BIT;
icu_ctrl(CMD_ICU_INT_ENABLE, &param);
// i2c2_gpio_config()
param = GFUNC_MODE_I2C2;
gpio_ctrl(CMD_GPIO_ENABLE_SECOND, &param);
@@ -259,31 +276,161 @@ static void i2c1Handler() {
REG_I2C1->SI = false;
}

static void i2c2Handler() {
if (!REG_I2C2->SI) {
LT_WM(I2C, "I2C2 interrupt not triggered");
return;
}

i2c2_hw_t REG_I2C;
REG_I2C2_READ_ALL(REG_I2C);

/* LT_DM(
I2C,
"ISR IN : mode=%d, tx=%d/rx=%d, ENSMB=%d, STA=%d, STO=%d, ACK=%d, TXMODE=%d, ACKRQ=%d",
i2c2Mode,
i2c2TxBuf ? i2c2TxBuf->available() : -1,
i2c2RxLength,
REG_I2C.ENSMB,
REG_I2C.STA,
REG_I2C.STO,
REG_I2C.ACK,
REG_I2C.TXMODE,
REG_I2C.ACKRQ
); */

if (REG_I2C.TXMODE) {
// check if ACK received
i2c2GotAck = REG_I2C.ACK;
// end the transmission if NACK
if (!i2c2GotAck)
goto stop;
}

switch (i2c2Mode) {
case I2C_ISR_MASTER_WRITE:
// end the transmission if no data left to send
if (i2c2TxBuf == nullptr || i2c2TxBuf->available() == 0)
goto stop;
// otherwise write the next byte
REG_I2C2->SMB_DAT = i2c2TxBuf->read_char();
break;

case I2C_ISR_MASTER_READ:
// failsafe for no buffer
if (i2c2RxBuf == nullptr)
i2c2RxLength = 0;
// quit the first ISR call, wait for next one with data
if (REG_I2C.STA) {
// send ACK if *any* data is requested
REG_I2C.ACK = i2c2RxLength > 0 ? true : false;
goto end;
} else {
// send ACK if *more* data is requested
REG_I2C.ACK = i2c2RxLength > 1 ? true : false;
}
// receive data
if (i2c2RxLength > 0) {
i2c2RxBuf->store_char(REG_I2C2->SMB_DAT);
i2c2RxLength--;
}
// end the transmission if no data left to receive
if (i2c2RxLength <= 0)
goto stop;
break;
}
goto end;

stop:
// both methods will stop firing the ISR:
if (i2c2SendStop) {
// - send STOP condition if requested
REG_I2C.STO = true;
} else {
// - otherwise disable I2C entirely
// REG_I2C2->ENSMB = false;
}
// finish the transmission
i2c2Mode = I2C_ISR_IDLE;

end:
REG_I2C.STA = false;
REG_I2C.SI = false;
REG_I2C2_WRITE_ALL(REG_I2C);

/* LT_DM(
I2C,
"ISR OUT: mode=%d, tx=%d/rx=%d, ENSMB=%d, STA=%d, STO=%d, ACK=%d, TXMODE=%d, ACKRQ=%d",
i2c2Mode,
i2c2TxBuf ? i2c2TxBuf->available() : -1,
i2c2RxLength,
REG_I2C.ENSMB,
REG_I2C.STA,
REG_I2C.STO,
REG_I2C.ACK,
REG_I2C.TXMODE,
REG_I2C.ACKRQ
); */
}

TwoWireResult TwoWire::endTransmission(bool sendStop) {
if (!this->txBuf || this->txAddress == 0x00)
return TWOWIRE_ERROR;
RingBuffer *buf = this->txBuf;

i2c2_hw_t REG_I2C;
int dataLength;
I2CISRMode *i2cModePtr;
bool *i2cGotAckPtr;

portDISABLE_INTERRUPTS();
// disable STOP condition
REG_I2C1->STO = false;
// set slave address
REG_I2C1->SMB_DAT = (this->txAddress << 1) | 0;
// enable START condition
REG_I2C1->STA = true;
// set work state for ISR
int dataLength = buf->available();
i2c1Mode = I2C_ISR_MASTER_WRITE;
i2c1SendStop = sendStop;
i2c1TxBuf = buf;
// start the transmission and I2C peripheral
REG_I2C1->TXMODE = true;
REG_I2C1->ENSMB = true;
switch (this->port) {
case 1:
// disable STOP condition
REG_I2C1->STO = false;
// set slave address
REG_I2C1->SMB_DAT = (this->txAddress << 1) | 0;
// enable START condition
REG_I2C1->STA = true;
// set work state for ISR
dataLength = buf->available();
i2c1Mode = I2C_ISR_MASTER_WRITE;
i2c1SendStop = sendStop;
i2c1TxBuf = buf;
i2cModePtr = &i2c1Mode;
i2cGotAckPtr = &i2c1GotAck;
// start the transmission and I2C peripheral
REG_I2C1->TXMODE = true;
REG_I2C1->ENSMB = true;
break;
case 2:
REG_I2C2_READ_ALL(REG_I2C);
// disable STOP condition
REG_I2C.STO = false;
// set slave address
REG_I2C2->SMB_DAT = (this->txAddress << 1) | 0;
// enable START condition
REG_I2C.STA = true;
// set work state for ISR
dataLength = buf->available();
i2c2Mode = I2C_ISR_MASTER_WRITE;
i2c2SendStop = sendStop;
i2c2TxBuf = buf;
i2cModePtr = &i2c2Mode;
i2cGotAckPtr = &i2c2GotAck;
// start the transmission and I2C peripheral
REG_I2C.ENSMB = true;
REG_I2C2_WRITE_ALL(REG_I2C);
break;
default:
portENABLE_INTERRUPTS();
return TWOWIRE_ERROR;
}
portENABLE_INTERRUPTS();

// wait for transmission to finish
unsigned long start = millis();
while (i2c1Mode != I2C_ISR_IDLE) {
while (*i2cModePtr != I2C_ISR_IDLE) {
ps_delay(1000);
if (millis() - start > timeout) {
LT_EM(I2C, "Timeout @ 0x%02x (TX not done)", this->txAddress);
@@ -293,11 +440,18 @@ TwoWireResult TwoWire::endTransmission(bool sendStop) {

// stop the transmission (wait a bit for STOP condition)
delayMicroseconds(500);
REG_I2C1->TXMODE = false;
REG_I2C1->ENSMB = false;
switch (this->port) {
case 1:
REG_I2C1->TXMODE = false;
REG_I2C1->ENSMB = false;
break;
case 2:
REG_I2C2->ENSMB = false;
break;
}

// check if ACK received
if (!i2c1GotAck) {
if (!*i2cGotAckPtr) {
if (buf->available() == dataLength)
return TWOWIRE_NACK_ADDR;
return TWOWIRE_NACK_DATA;
@@ -310,27 +464,56 @@ size_t TwoWire::requestFrom(uint16_t address, size_t len, bool sendStop) {
return 0;
RingBuffer *buf = this->rxBuf;

I2CISRMode *i2cModePtr;
int32_t *i2cRxLengthPtr;

portDISABLE_INTERRUPTS();
// disable STOP condition
REG_I2C1->STO = false;
// set slave address
REG_I2C1->SMB_DAT = (this->txAddress << 1) | 1;
// enable START condition
REG_I2C1->STA = true;
// set work state for ISR
int dataLength = buf->available();
i2c1Mode = I2C_ISR_MASTER_READ;
i2c1SendStop = sendStop;
i2c1RxBuf = buf;
i2c1RxLength = len;
// start the transmission and I2C peripheral
REG_I2C1->TXMODE = true;
REG_I2C1->ENSMB = true;
switch (this->port) {
case 1:
// disable STOP condition
REG_I2C1->STO = false;
// set slave address
REG_I2C1->SMB_DAT = (this->txAddress << 1) | 1;
// enable START condition
REG_I2C1->STA = true;
// set work state for ISR
i2c1Mode = I2C_ISR_MASTER_READ;
i2c1SendStop = sendStop;
i2c1RxBuf = buf;
i2c1RxLength = len;
i2cModePtr = &i2c1Mode;
i2cRxLengthPtr = &i2c1RxLength;
// start the transmission and I2C peripheral
REG_I2C1->TXMODE = true;
REG_I2C1->ENSMB = true;
break;
case 2:
// disable STOP condition
REG_I2C2->STO = false;
// set slave address
REG_I2C2->SMB_DAT = (this->txAddress << 1) | 1;
// enable START condition
REG_I2C2->STA = true;
// set work state for ISR
i2c2Mode = I2C_ISR_MASTER_READ;
i2c2SendStop = sendStop;
i2c2RxBuf = buf;
i2c2RxLength = len;
i2cModePtr = &i2c2Mode;
i2cRxLengthPtr = &i2c2RxLength;
// start the transmission and I2C peripheral
REG_I2C2->TXMODE = true;
REG_I2C2->ENSMB = true;
break;
default:
portENABLE_INTERRUPTS();
return 0;
}
portENABLE_INTERRUPTS();

// wait for transmission to finish
unsigned long start = millis();
while (i2c1Mode != I2C_ISR_IDLE) {
while (*i2cModePtr != I2C_ISR_IDLE) {
ps_delay(1000);
if (millis() - start > timeout) {
LT_EM(I2C, "Timeout @ 0x%02x (RX not done)", this->txAddress);
@@ -340,9 +523,17 @@ size_t TwoWire::requestFrom(uint16_t address, size_t len, bool sendStop) {

// stop the transmission (wait a bit for STOP condition)
delayMicroseconds(500);
REG_I2C1->TXMODE = false;
REG_I2C1->ENSMB = false;
switch (this->port) {
case 1:
REG_I2C1->TXMODE = false;
REG_I2C1->ENSMB = false;
break;
case 2:
REG_I2C2->TXMODE = false;
REG_I2C2->ENSMB = false;
break;
}

end:
return len - i2c1RxLength;
return len - *i2cRxLengthPtr;
}