update
This commit is contained in:
@@ -2,23 +2,25 @@
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/i2c_slave.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/uart.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
// I2C Slave Configuration
|
||||
#define I2C_SLAVE_SDA_IO 21
|
||||
#define I2C_SLAVE_SCL_IO 22
|
||||
#define I2C_SLAVE_FREQ_HZ 100000
|
||||
#define I2C_SLAVE_ADDR 0x61
|
||||
#define I2C_SLAVE_RX_BUF_LEN 128
|
||||
#define I2C_SLAVE_TX_BUF_LEN 128
|
||||
#define I2C_SLAVE_RX_BUF_LEN 256
|
||||
#define I2C_SLAVE_TX_BUF_LEN 256
|
||||
|
||||
// UART Configuration
|
||||
#define UART_NUM UART_NUM_0
|
||||
#define UART_BAUD_RATE 921600
|
||||
#define UART_BUF_SIZE 256
|
||||
#define TAG "I2C2SERIAL"
|
||||
#define UART_BUF_SIZE 512
|
||||
|
||||
static i2c_slave_dev_handle_t i2c_slave = NULL;
|
||||
static TaskHandle_t s_i2c_task_handle = NULL;
|
||||
static const char *TAG = "I2C_SLAVE";
|
||||
|
||||
static void uart_init(void) {
|
||||
uart_config_t uart_config = {
|
||||
@@ -33,117 +35,94 @@ static void uart_init(void) {
|
||||
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
|
||||
}
|
||||
|
||||
// Callback for I2C Receive Done
|
||||
static bool i2c_rx_done_callback(i2c_slave_dev_handle_t channel, const i2c_slave_rx_done_event_data_t *edata, void *user_data) {
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
if (s_i2c_task_handle) {
|
||||
vTaskNotifyGiveFromISR(s_i2c_task_handle, &high_task_wakeup);
|
||||
}
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static void i2c_slave_init(void) {
|
||||
i2c_slave_config_t conf = {
|
||||
static esp_err_t i2c_slave_init(void) {
|
||||
int i2c_slave_port = I2C_NUM_0;
|
||||
i2c_config_t conf_slave = {
|
||||
.sda_io_num = I2C_SLAVE_SDA_IO,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_io_num = I2C_SLAVE_SCL_IO,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.send_buf_depth = 256,
|
||||
.slave_addr = I2C_SLAVE_ADDR,
|
||||
.addr_bit_len = I2C_ADDR_BIT_LEN_7,
|
||||
.intr_priority = 0,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.mode = I2C_MODE_SLAVE,
|
||||
.slave.addr_10bit_en = 0,
|
||||
.slave.slave_addr = I2C_SLAVE_ADDR,
|
||||
.clk_flags = 0,
|
||||
};
|
||||
#if SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE
|
||||
conf.flags.stretch_en = true;
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(i2c_new_slave_device(&conf, &i2c_slave));
|
||||
|
||||
// Enable Internal Pullups manually
|
||||
gpio_set_pull_mode(I2C_SLAVE_SDA_IO, GPIO_PULLUP_ONLY);
|
||||
gpio_set_pull_mode(I2C_SLAVE_SCL_IO, GPIO_PULLUP_ONLY);
|
||||
|
||||
i2c_slave_event_callbacks_t cbs = {
|
||||
.on_recv_done = i2c_rx_done_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_slave_register_event_callbacks(i2c_slave, &cbs, NULL));
|
||||
esp_err_t err = i2c_param_config(i2c_slave_port, &conf_slave);
|
||||
if (err != ESP_OK) return err;
|
||||
return i2c_driver_install(i2c_slave_port, conf_slave.mode, I2C_SLAVE_RX_BUF_LEN, I2C_SLAVE_TX_BUF_LEN, 0);
|
||||
}
|
||||
|
||||
static void i2c2serial_task(void *arg) {
|
||||
uint8_t i2c_rx[I2C_SLAVE_RX_BUF_LEN];
|
||||
uint8_t uart_rx[UART_BUF_SIZE];
|
||||
uint8_t i2c_tx[I2C_SLAVE_TX_BUF_LEN];
|
||||
size_t out_res = 0; // Added for ESP-IDF v5.4 compatibility
|
||||
void app_main(void) {
|
||||
uint8_t i2c_rx_buffer[I2C_SLAVE_RX_BUF_LEN];
|
||||
uint8_t uart_rx_buffer[UART_BUF_SIZE];
|
||||
uint8_t i2c_tx_buffer[I2C_SLAVE_TX_BUF_LEN];
|
||||
|
||||
s_i2c_task_handle = xTaskGetCurrentTaskHandle();
|
||||
uart_init();
|
||||
ESP_ERROR_CHECK(i2c_slave_init());
|
||||
ESP_LOGI(TAG, "I2C Slave initialized successfully (Legacy Driver)");
|
||||
|
||||
while (1) {
|
||||
// 1. Prepare to Receive (Non-blocking queue)
|
||||
memset(i2c_rx, 0, sizeof(i2c_rx));
|
||||
// 1. Wait for data from Master (Blocking with timeout)
|
||||
int len = i2c_slave_read_buffer(I2C_NUM_0, i2c_rx_buffer, I2C_SLAVE_RX_BUF_LEN, 50 / portTICK_PERIOD_MS);
|
||||
|
||||
// This queues the receive request
|
||||
ESP_ERROR_CHECK(i2c_slave_receive(i2c_slave, i2c_rx, sizeof(i2c_rx)));
|
||||
if (len > 0) {
|
||||
ESP_LOGI(TAG, "Received %d bytes from Master", len);
|
||||
|
||||
// 2. Send CMD to Python
|
||||
uart_write_bytes(UART_NUM, "CMD:", 4);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char hex[3];
|
||||
sprintf(hex, "%02X", i2c_rx_buffer[i]);
|
||||
uart_write_bytes(UART_NUM, hex, 2);
|
||||
}
|
||||
uart_write_bytes(UART_NUM, "\n", 1);
|
||||
|
||||
// 3. Wait for DATA from Python (Blocking with timeout)
|
||||
int total_rx = 0;
|
||||
bool data_received = false;
|
||||
// Simple timeout for Python response
|
||||
for (int i=0; i<100; i++) { // ~1 second timeout
|
||||
int ulen = uart_read_bytes(UART_NUM, uart_rx_buffer + total_rx, UART_BUF_SIZE - total_rx, 10 / portTICK_PERIOD_MS);
|
||||
if (ulen > 0) {
|
||||
total_rx += ulen;
|
||||
// Check for newline
|
||||
char *newline = memchr(uart_rx_buffer, '\n', total_rx);
|
||||
if (newline) {
|
||||
// Parse DATA:
|
||||
if (strncmp((char*)uart_rx_buffer, "DATA:", 5) == 0) {
|
||||
int tx_len = 0;
|
||||
char *p = (char*)uart_rx_buffer + 5;
|
||||
while (p < newline) {
|
||||
unsigned int val;
|
||||
if (sscanf(p, "%2x", &val) == 1) {
|
||||
i2c_tx_buffer[tx_len++] = (uint8_t)val;
|
||||
p += 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tx_len > 0) {
|
||||
i2c_slave_write_buffer(I2C_NUM_0, i2c_tx_buffer, tx_len, 100 / portTICK_PERIOD_MS);
|
||||
ESP_LOGI(TAG, "Loaded %d bytes to TX buffer", tx_len);
|
||||
} else {
|
||||
// Even if empty, we might need to clear or write 0 if Master expects read?
|
||||
// If Python sends empty DATA:, it means just ack/nothing to send.
|
||||
}
|
||||
data_received = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Wait for Receive Complete (Callback triggers notification)
|
||||
// This is where we wait for the Master to finish writing the command
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
// Calculate actual length received (SCD30 commands are usually 2-5 bytes)
|
||||
// We look for the first 0 if the master didn't send a full 128 bytes
|
||||
size_t actual_rx_len = 2; // SCD30 commands are at least 2 bytes
|
||||
|
||||
// 3. Fast Send to PC
|
||||
uart_write_bytes(UART_NUM, "CMD:", 4);
|
||||
for (size_t i = 0; i < actual_rx_len; ++i) {
|
||||
char hex[3];
|
||||
sprintf(hex, "%02X", i2c_rx[i]);
|
||||
uart_write_bytes(UART_NUM, hex, 2);
|
||||
}
|
||||
uart_write_bytes(UART_NUM, "\n", 1);
|
||||
|
||||
// 4. Wait for PC response
|
||||
int total_len = 0;
|
||||
TickType_t start_tick = xTaskGetTickCount();
|
||||
bool got_data = false;
|
||||
|
||||
// Wait up to 200ms for "DATA:[hex]\n" from PC
|
||||
while ((xTaskGetTickCount() - start_tick) < pdMS_TO_TICKS(200)) {
|
||||
int len = uart_read_bytes(UART_NUM, uart_rx + total_len, UART_BUF_SIZE - total_len, pdMS_TO_TICKS(5));
|
||||
if (len > 0) {
|
||||
total_len += len;
|
||||
char *newline = memchr(uart_rx, '\n', total_len);
|
||||
if (newline) {
|
||||
if (strncmp((char *)uart_rx, "DATA:", 5) == 0) {
|
||||
// 5. Fast Hex Parse
|
||||
int tx_len = 0;
|
||||
for (int i = 5; i < (newline - (char*)uart_rx) - 1; i += 2) {
|
||||
unsigned int val;
|
||||
if (sscanf((char*)&uart_rx[i], "%2x", &val) == 1) {
|
||||
i2c_tx[tx_len++] = (uint8_t)val;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Load Buffer & Release Clock Stretch
|
||||
// Added &out_res parameter for v5.4 compatibility
|
||||
if (tx_len > 0) {
|
||||
i2c_slave_transmit(i2c_slave, i2c_tx, tx_len, &out_res, pdMS_TO_TICKS(50));
|
||||
}
|
||||
got_data = true;
|
||||
break;
|
||||
}
|
||||
total_len = 0; // Clear buffer if line didn't match DATA:
|
||||
}
|
||||
if (!data_received) {
|
||||
ESP_LOGW(TAG, "Timeout waiting for Python response");
|
||||
}
|
||||
}
|
||||
|
||||
if (!got_data) {
|
||||
// If PC fails, send a dummy byte so the Master's read doesn't hang forever
|
||||
uint8_t dummy = 0xFF;
|
||||
i2c_slave_transmit(i2c_slave, &dummy, 1, &out_res, pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
// Clear RX buffer not needed as read_buffer consumes it, but good practice if using ring buffer?
|
||||
// i2c_reset_rx_fifo(I2C_NUM_0); // This might be too aggressive if data came in during processing?
|
||||
// Legacy driver handles fifo logic.
|
||||
}
|
||||
}
|
||||
void app_main(void) {
|
||||
uart_init();
|
||||
i2c_slave_init();
|
||||
xTaskCreatePinnedToCore(i2c2serial_task, "i2c2serial_task", 4096, NULL, 10, NULL, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user