So I've come back to this after leaving it for some time.
I have written my own driver that is eerily similar to the arduino low level i2c driver.
Note: these problems happen when one uses the i2c fifo or not.
Here is the i2c driver I wrote (based on arduino esp32 i2c driver). One can run in fifo mode or nonfifo mode based on value of NON_FIFO_ENABLE.
Code: Select all
// i2c_master_esp32.c
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "soc/i2c_struct.h"
#include "soc/i2c_reg.h"
#include "soc/gpio_sig_map.h"
#include "rom/ets_sys.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_err.h"
#include "esp_intr.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/xtensa_api.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "i2c_master.h"
#define NON_FIFO_ENABLE 1
#define I2C_TIMEOUT_MS 13
#define I2C_WAIT_MS 500
#define TRACE(x) (void)( printf x, fflush(stdout) )
#define xTRACE(x) ((void)0)
enum
{
i2c_esp32Frequency = 400000,
peripheralClkFreq = 80000000, //APB_CLK_FREQ
};
enum
{
//Measured in apb clock cycles
i2c_scl_half_cycle = (peripheralClkFreq / i2c_esp32Frequency) /2,
i2c_scl_low_period = i2c_scl_half_cycle,
i2c_scl_high_period = i2c_scl_half_cycle,
i2c_scl_rstart_setup_time = i2c_scl_half_cycle / 2, //(0.6 us minimum) == (peripheralClkFreq *(6)) / (10000000)
i2c_scl_start_hold_time = i2c_scl_half_cycle / 2, //"
i2c_scl_stop_hold_time = i2c_scl_half_cycle / 2, //"
i2c_scl_stop_setup_time = i2c_scl_half_cycle / 2, //(0.6 us minimum)
i2c_sda_hold_time = i2c_scl_half_cycle / 4, //(0 us minimum)
i2c_sda_sample_time = i2c_scl_half_cycle / 4, //(0.3 us minimum)
i2c_timeout_clockcycles = (peripheralClkFreq * I2C_TIMEOUT_MS)/1000, //(13.107 ms maximum)
i2c_scl_filter_thresh = (peripheralClkFreq *(5)) / (100000000), //(50 ns maximum) == (peripheralClkFreq *(5)) / (100000000)
i2c_sda_filter_thresh = (peripheralClkFreq *(5)) / (100000000), //(50 ns maximum)
};
enum
{
maxSendSize = 30,
maxReadSize = 30,
};
enum
{
writeMask = 0xFE,
readMask = 0x01,
intClrMask = 0xFFFFFFFF,
};
typedef enum
{
rstart = 0,
writeCmd = 1,
readCmd = 2,
stop = 3,
end = 4,
} i2c_command_t;
static const bool ACK = false;
static const bool NAK = true;
static bool init;
void i2c_reset(void)
{
CLEAR_PERI_REG_MASK(I2C_INT_ENA_REG(0), intClrMask);
periph_module_disable(PERIPH_I2C0_MODULE);
ets_delay_us(1000);
i2c_master_init();
}
static void i2cResetFifo()
{
I2C0.fifo_conf.rx_fifo_rst = 1; //Reset the rx fifo
I2C0.fifo_conf.rx_fifo_rst = 0;
I2C0.fifo_conf.tx_fifo_rst = 1; //Reset the tx fifo
I2C0.fifo_conf.tx_fifo_rst = 0;
}
static void i2cResetCmd()
{
int i;
for(i=0; i<16; i++)
{
I2C0.command[i].val = 0;
}
}
static void i2cSetCmd(uint8_t index, uint8_t op_code, uint8_t byte_num, bool ack_val, bool ack_exp, bool ack_check)
{
I2C0.command[index].val = 0; //This covers all fields, setting all to 0
I2C0.command[index].ack_en = ack_check;
I2C0.command[index].ack_exp = ack_exp;
I2C0.command[index].ack_val = ack_val;
I2C0.command[index].byte_num = byte_num;
I2C0.command[index].op_code = op_code;
}
static void i2cInitFix(void)
{
i2cResetFifo();
i2cResetCmd();
I2C0.int_clr.val = 0xFFFFFFFF;
i2cSetCmd(0, rstart, 0, false, false, false);
I2C0.fifo_data.data = 0;
i2cSetCmd(1, writeCmd, 1, false, false, false);
i2cSetCmd(2, stop, 0, false, false, false);
I2C0.ctr.trans_start = 1;
while(!I2C0.command[2].done);
}
size_t i2c_master_transmit(uint8_t slaveAddress, const uint8_t* txBuf, size_t size, bool sendStopBit)
{
static uint32_t errCountTx = 0;
int txBufIndex = 0;
size_t currentSendSize;
uint32_t totalSendSize = size;
uint32_t currentEndCode;
uint32_t finalEndCode = (sendStopBit ? stop: end);
xTRACE(("i2c write started: %x, %x, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, size));
while(size > 0)
{
if( size > maxSendSize )
{
currentSendSize = maxSendSize;
size -= maxSendSize;
currentEndCode = end;
}
else
{
currentSendSize = size;
size = 0;
currentEndCode = finalEndCode;
}
uint32_t commandIndex = 0;
i2cResetFifo();
i2cResetCmd();
I2C0.int_clr.val |= intClrMask;
bool sendAddress = true; //Should be sendAddress = (txBufIndex == 0); but doesn't work
if( sendAddress )
{
#if NON_FIFO_ENABLE
I2C0.ram_data[0] = slaveAddress & writeMask;
#else
I2C0.fifo_data.val = slaveAddress & writeMask;
#endif
i2cSetCmd(commandIndex++, rstart, 0, false, false, false);
}
#if NON_FIFO_ENABLE
int fifoIndex = sendAddress ? 1 : 0;
while( fifoIndex < currentSendSize+sendAddress )
{
I2C0.ram_data[fifoIndex] = (uint32_t)txBuf[txBufIndex++];
++fifoIndex;
}
currentSendSize = I2C0.status_reg.tx_fifo_cnt;
#else
int j = 0;
while( j < currentSendSize )
{
++j;
I2C0.fifo_data.val = (uint32_t)txBuf[txBufIndex++];
while( I2C0.status_reg.tx_fifo_cnt < j );
}
currentSendSize = I2C0.status_reg.tx_fifo_cnt;
#endif
i2cSetCmd(commandIndex++, writeCmd, currentSendSize, ACK, ACK, true);
//write data
i2cSetCmd(commandIndex, currentEndCode, 0, false, false, false);
//Stop bit if there is no more data to send, otherwise end bit
I2C0.ctr.trans_start = 1; //Start command execution
uint32_t count = 0;
while(1)
{
ets_delay_us(1000);
if( I2C0.int_raw.arbitration_lost
|| I2C0.int_raw.time_out
|| I2C0.int_raw.ack_err
|| count >= I2C_WAIT_MS
)
{
TRACE(("I2C TX Error: %08X, %04X, %d, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, I2C0.status_reg.tx_fifo_cnt, count));
if( errCountTx++ > 5 )
{
i2c_reset();
}
size = 0;
totalSendSize = 0;
break;
}
if( (I2C0.command[commandIndex].done && I2C0.int_raw.trans_complete)
|| (/*I2C0.command[commandIndex].done &&*/ I2C0.int_raw.end_detect) //Commented because end command's done bit never gets set
)
{
errCountTx = 0;
break;
}
++count;
}
}
xTRACE(("Write complete: %x, %x, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, I2C0.status_reg.tx_fifo_cnt));
return totalSendSize;
}
size_t i2c_master_receive(uint8_t slaveAddress, uint8_t* rxBuf, size_t size)
{
static uint32_t errCountRx = 0;
int rxBufIndex = 0;
size_t currentReadSize;
uint32_t endCode;
uint32_t commandIndex = 0;
uint8_t dummyRx;
xTRACE(("Read start: %x, %x, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, size));
while(size > 0)
{
if( size > maxReadSize )
{
currentReadSize = maxReadSize;
endCode = end;
size -= maxReadSize;
}
else
{
currentReadSize = size;
endCode = stop;
size = 0;
}
commandIndex = 0;
i2cResetFifo();
i2cResetCmd();
I2C0.int_clr.val |= intClrMask;
bool sendAddress = true; //rxBufIndex == 0;
if( sendAddress )
{
#if NON_FIFO_ENABLE
I2C0.ram_data[0] = slaveAddress | readMask;
#else
I2C0.fifo_data.val = slaveAddress | readMask;
#endif
i2cSetCmd(commandIndex++, rstart, 0, false, false, false);
i2cSetCmd(commandIndex++, writeCmd, 1, ACK, ACK, true);
}
if( endCode == stop && size == 0 )
{
i2cSetCmd(commandIndex++, readCmd, currentReadSize-1, ACK, false, false);
i2cSetCmd(commandIndex++, readCmd, 1, NAK, false, false);
//i2c protocol specifies no ack before stop bit on last byte of read
}
else
{
i2cSetCmd(commandIndex++, readCmd, currentReadSize, ACK, false, false);
}
i2cSetCmd(commandIndex, endCode, 0, false, false, false);
I2C0.ctr.trans_start = 1;
uint32_t count = 0;
while(1)
{
ets_delay_us(1000);
if( I2C0.int_raw.arbitration_lost
|| I2C0.int_raw.time_out
|| I2C0.int_raw.ack_err
|| count >= I2C_WAIT_MS
)
{
TRACE(("I2C RX Error: %08X, %04X, %d, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, I2C0.status_reg.rx_fifo_cnt, count));
if( errCountRx++ > 5 )
{
i2c_reset();
}
size = 0;
break;
}
if( (I2C0.command[commandIndex].done && I2C0.int_raw.trans_complete)
|| (/*I2C0.command[commandIndex].done &&*/ I2C0.int_raw.end_detect) //Commented because end command's done bit never gets set
)
{
errCountRx = 0;
break;
}
++count;
}
for(int j = 0; j < currentReadSize; ++j)
{
//Read data received on the fifo
#if NON_FIFO_ENABLE
rxBuf[rxBufIndex] = (uint8_t)I2C0.ram_data[j] ;
#else
rxBuf[rxBufIndex] = (uint8_t)I2C0.fifo_data.val;
#endif
ets_delay_us(1000);
++rxBufIndex;
}
}
xTRACE(("Read complete: %x, %x, %d\n", I2C0.status_reg.val, I2C0.int_raw.val, size));
return rxBufIndex;
}
void i2c_master_close(void)
{
i2cResetCmd();
i2cSetCmd(0, rstart, 0, false, false, false);
i2cSetCmd(1, stop, 0, false, false, false);
I2C0.int_clr.val = 0xFFFFFFFF;
I2C0.ctr.trans_start = 1;
}
void i2c_master_init(void)
{
//Initialize peripheral
periph_module_enable(PERIPH_I2C0_MODULE); //Sets dport perph clock to enabled for i2c
//Set control characteristics of i2c
I2C0.ctr.val = 0 ;
I2C0.ctr.ms_mode = 1;
I2C0.ctr.sda_force_out = 1 ;
I2C0.ctr.scl_force_out = 1 ;
I2C0.ctr.clk_en = 1;
I2C0.ctr.sample_scl_level = 0 ;
I2C0.ctr.rx_lsb_first = 0 ;
I2C0.ctr.tx_lsb_first = 0 ;
I2C0.timeout.val = i2c_timeout_clockcycles;
#if NON_FIFO_ENABLE
I2C0.fifo_conf.nonfifo_en = 1;
#else
I2C0.fifo_conf.nonfifo_en = 0;
#endif
I2C0.fifo_conf.tx_fifo_empty_thrhd = 0;
I2C0.fifo_conf.rx_fifo_full_thrhd = 30;
I2C0.fifo_conf.nonfifo_rx_thres = 32;
I2C0.fifo_conf.nonfifo_tx_thres = 32;
//Set the i2c frequency and clock characteristics
I2C0.scl_low_period.period = i2c_scl_low_period;
I2C0.scl_high_period.period = i2c_scl_high_period;
I2C0.scl_start_hold.time = i2c_scl_start_hold_time;
I2C0.scl_rstart_setup.time = i2c_scl_rstart_setup_time;
I2C0.scl_stop_hold.time = i2c_scl_stop_hold_time;
I2C0.scl_stop_setup.time = i2c_scl_stop_setup_time;
I2C0.sda_hold.time = i2c_sda_hold_time;
I2C0.sda_sample.time = i2c_sda_sample_time;
I2C0.scl_filter_cfg.en = 0; //disable scl filter
I2C0.sda_filter_cfg.en = 0; //disable sda filter
//I2C0.scl_filter_cfg.thres = i2c_scl_filter_thresh;
//I2C0.sda_filter_cfg.thres = i2c_sda_filter_thresh;
I2C0.int_ena.val = 0; //Disable all I2C interrupts
//Attach scl to gpio pin 16
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode
io_conf.pin_bit_mask = GPIO_SEL_16; //bit mask of the pins that you want to set,e.g.GPIO21/22
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = 0; //enable pull-up mode
gpio_config(&io_conf); //configure GPIO with the given settings
gpio_matrix_out(16, I2CEXT0_SCL_OUT_IDX, 0, 0); //set output signal for io_matrix
gpio_matrix_in( 16, I2CEXT0_SCL_IN_IDX, 0); //set input signal for io_matrix
//Attach sda to gpio pin 17
io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
io_conf.mode = GPIO_MODE_INPUT_OUTPUT_OD; //set as output mode
io_conf.pin_bit_mask = GPIO_SEL_17; //bit mask of the pins that you want to set,e.g.GPIO21/22
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = 0; //enable pull-up mode
gpio_config(&io_conf); //configure GPIO with the given settings
gpio_matrix_out(17, I2CEXT0_SDA_OUT_IDX, 0, 0); //set output signal for io_matrix
gpio_matrix_in( 17, I2CEXT0_SDA_IN_IDX, 0); //set input signal for io_matrix
init = true;
}
// EOF
My i2c errors only seem to occur while mbedtls is configuring/connecting. I did lots of checking to see if memory related to my i2c was getting corrupted but couldn't seem to find any corruptions or leaks. I also checked i2c peripheral registers. All the registers had the expected values but the i2c ram may have been corrupted (I'm not sure on that because I only checked this in fifo mode so far).
Code: Select all
static mbedtls_ssl_context sslContext;
static mbedtls_entropy_context entropy;
static mbedtls_ctr_drbg_context ctr_drbg;
static mbedtls_ssl_config conf;
static mbedtls_x509_crt caCert;
static bool startTls(mbedtls_net_context* tlsSocket, const char* ipAddressStr, const char* caRootCertificate)
{
int error;
// Configure TLS
mbedtls_net_init(tlsSocket);
mbedtls_ssl_init(&sslContext);
mbedtls_ssl_config_init(&conf);
mbedtls_x509_crt_init(&caCert);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
if( ( error = mbedtls_entropy_self_test(1)) != 0)
{
TRACE(("Error: mbedtls_entropy_self_test failed!\n"));
}
if( error == 0
&& (error = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0)) != 0
)
{
TRACE(( "Error: mbedtls_ctr_drbg_seed returned -0x%X\n", -error));
}
if( error == 0
&& (error = mbedtls_x509_crt_parse(&caCert, (uint8_t*)caRootCertificate, strlen(caRootCertificate) + 1))
)
{
TRACE(("mbedtls_x509_crt_parse returned -0x%X\n", -error));
}
if( error == 0
&& (error = mbedtls_ssl_set_hostname(&sslContext, "openSSL TLS Server 1")) != 0
)
{
TRACE(( "Error: mbedtls_ssl_set_hostname returned -0x%X\n", -error));
}
char ipString[5];
itoa(serverPortNum, ipString, 10);
if( error == 0
&& (error = mbedtls_net_connect(tlsSocket, ipAddressStr, ipString, MBEDTLS_NET_PROTO_TCP)) != 0 )
{
TRACE(( " failed\n ! mbedtls_net_connect returned -0x%X\n", -error));
}
if( error == 0 )
{
error = mbedtls_ssl_config_defaults( &conf
, MBEDTLS_SSL_IS_CLIENT
, MBEDTLS_SSL_TRANSPORT_STREAM
, MBEDTLS_SSL_PRESET_DEFAULT
);
if( error != 0 )
{
TRACE(( "Error: mbedtls_ssl_config_defaults returned -0x%X\n", -error));
}
}
if( error == 0 )
{
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL); // TODO: enable verify
mbedtls_ssl_conf_ca_chain(&conf, &caCert, NULL);
mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg );
error = mbedtls_ssl_setup(&sslContext, &conf);
if( error != 0 )
{
TRACE(("mbedtls_ssl_setup returned -0x%X\n", -error));
}
}
TRACE_LINE;
if( error == 0 )
{
mbedtls_ssl_set_bio(&sslContext, &tlsSocket->fd, mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);
if( (error = mbedtls_ssl_handshake(&sslContext)) != 0 )
{
TRACE(( "Error: mbedtls_ssl_handshake returned -0x%X\n", -error));
}
}
return error == 0;
}
static bool stopTls(mbedtls_net_context *tlsSocket)
{
mbedtls_ssl_close_notify(&sslContext);
// Clean up
mbedtls_net_free( tlsSocket ); //Calls shutdown and close for the tcp socket
mbedtls_x509_crt_free( &caCert );
mbedtls_ssl_free( &sslContext );
mbedtls_ssl_config_free( &conf );
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );
return true;
}
I've checked the i2c signals with a scope and the lines have stable transitions.