Implemented RX on Node
This commit is contained in:
@@ -63,6 +63,7 @@ target_link_libraries(i2c-hub-firmware
|
||||
# Add the standard include files to the build
|
||||
target_include_directories(i2c-hub-firmware PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/include
|
||||
)
|
||||
|
||||
# Add any user requested libraries
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,10 @@ add_library(GobotRPC STATIC
|
||||
|
||||
target_include_directories(GobotRPC PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include/util
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include/ti
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../..
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../../include
|
||||
)
|
||||
|
||||
target_link_libraries(GobotRPC
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
add_library(GobotRPC_Node_RP2040_I2C STATIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/../node_interface/rp2040/i2c/init.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../node_interface/rp2040/i2c/rx_tx.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../crc16.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../protocol_base.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/../node_interface/init.cpp
|
||||
)
|
||||
target_include_directories(GobotRPC_Node_RP2040_I2C PUBLIC
|
||||
${CMAKE_CURRENT_LIST_DIR}/../node_interface/include
|
||||
${CMAKE_CURRENT_LIST_DIR}/../include/util
|
||||
${CMAKE_CURRENT_LIST_DIR}/../../..
|
||||
)
|
||||
|
||||
target_link_libraries(GobotRPC_Node_RP2040_I2C
|
||||
FreeRTOS-Kernel-Heap4
|
||||
pico_stdlib
|
||||
hardware_i2c
|
||||
hardware_irq
|
||||
)
|
||||
@@ -1,3 +1,5 @@
|
||||
#include "crc16.hpp"
|
||||
|
||||
#define POLY 0x8408
|
||||
/*
|
||||
// 16 12 5
|
||||
|
||||
@@ -12,6 +12,10 @@ enum GobotRPCTypes {
|
||||
ERROR = 0b10
|
||||
};
|
||||
|
||||
struct GobotRPCPackage_Req_Vacum {
|
||||
uint8_t enable : 8;
|
||||
};
|
||||
|
||||
#define GobotRPC_Package_DATA_OFFSET 2
|
||||
#define CALC_SIZE_GobotRPC_PACKAGE(data_len) (data_len + GobotRPC_Package_DATA_OFFSET + 2)
|
||||
|
||||
@@ -24,5 +28,6 @@ public:
|
||||
|
||||
void assembleGobotRPCHeader(char * buffer, GobotRPCNumber number, GobotRPCTypes data_size, size_t);
|
||||
void assembleCRC(char * buffer, size_t data_len);
|
||||
bool checkCRC(char * buffer, size_t data_len);
|
||||
|
||||
GobotRPCHeaderInfo extractGobotRPCHeader(char * buffer);
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "protocol.hpp"
|
||||
#include "node_interface_hardware.hpp"
|
||||
|
||||
#include "FreeRTOSConfig.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "queue.h"
|
||||
|
||||
typedef void (*onPackageRxCallback)(void * args, char *data, uint16_t len, GobotRPCTypes type, GobotRPCNumber number);
|
||||
|
||||
class GobotRPC_NI {
|
||||
private:
|
||||
onPackageRxCallback onPackageRx;
|
||||
void *onPackageRxArgs;
|
||||
|
||||
QueueHandle_t txQueue;
|
||||
QueueHandle_t rxQueue;
|
||||
TaskHandle_t rxTaskHandle;
|
||||
|
||||
unsigned int core;
|
||||
|
||||
public:
|
||||
GobotRPC_NI(unsigned int core, QueueHandle_t txQueue, QueueHandle_t rxQueue);
|
||||
|
||||
void registerOnPackageRxCallback(onPackageRxCallback callback, void *context);
|
||||
void sendPackage(char *data, size_t len, GobotRPCTypes type, GobotRPCNumber number);
|
||||
|
||||
void rxTask();
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "FreeRTOSConfig.h"
|
||||
#include "FreeRTOS.h"
|
||||
#include "queue.h"
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
struct GobotRPC_NI_Package_Transport {
|
||||
char data[32];
|
||||
uint length;
|
||||
uint index;
|
||||
};
|
||||
|
||||
enum I2C_WRITE_STATE {
|
||||
I2C_WRITE_STATE_FIRST,
|
||||
I2C_WRITE_STATE_LENGTH,
|
||||
I2C_WRITE_STATE_DATA,
|
||||
};
|
||||
|
||||
enum I2C_READ_STATE {
|
||||
I2C_READ_STAGE_FIRST,
|
||||
I2C_READ_STAGE_DONE,
|
||||
I2C_READ_STAGE_WIP,
|
||||
I2C_READ_STAGE_INVALID
|
||||
};
|
||||
|
||||
enum I2C_SLAVE_EVENT {
|
||||
I2C_SLAVE_RECEIVE,
|
||||
I2C_SLAVE_REQUEST,
|
||||
I2C_SLAVE_FINISH
|
||||
};
|
||||
|
||||
class GobotRPC_NI_Hardware {
|
||||
|
||||
};
|
||||
|
||||
struct GobotRPC_NI_Hardware_RP2040_I2C_PreTxQueues {
|
||||
QueueHandle_t isrPreTxQueue;
|
||||
QueueHandle_t isrTXQueue;
|
||||
};
|
||||
|
||||
class GobotRPC_NI_Hardware_RP2040_I2C : public GobotRPC_NI_Hardware {
|
||||
uint core;
|
||||
|
||||
QueueHandle_t isrPreTxQueue;
|
||||
QueueHandle_t isrTXQueue;
|
||||
QueueHandle_t isrRXQueue;
|
||||
TaskHandle_t preTxTaskHandle;
|
||||
|
||||
uint i2cSDA_PIN;
|
||||
uint i2cSCL_PIN;
|
||||
uint int_PIN;
|
||||
uint8_t i2cAddress;
|
||||
|
||||
I2C_WRITE_STATE writeState = I2C_WRITE_STATE_FIRST;
|
||||
I2C_READ_STATE readState = I2C_READ_STAGE_INVALID;
|
||||
|
||||
GobotRPC_NI_Package_Transport rxPackage;
|
||||
GobotRPC_NI_Package_Transport txPackage;
|
||||
|
||||
public:
|
||||
i2c_inst_t * i2c_inst;
|
||||
|
||||
GobotRPC_NI_Hardware_RP2040_I2C(
|
||||
xQueueHandle TXQueue, xQueueHandle RXQueue, uint core,
|
||||
i2c_inst_t * i2c_inst,
|
||||
uint8_t i2cAddress,
|
||||
uint i2cSDA_PIN, uint i2cSCL_PIN, uint int_PIN
|
||||
);
|
||||
|
||||
void onI2CIRQ(I2C_SLAVE_EVENT event, BaseType_t * xHigherPriorityTaskWoken);
|
||||
void preTxTask();
|
||||
};
|
||||
|
||||
extern GobotRPC_NI_Hardware_RP2040_I2C * g_gobotrpc_ni_hardware_rp2040_i2c;
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "node_interface.hpp"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include <string.h>
|
||||
|
||||
static void rxTaskFn(void *pvParameters) {
|
||||
GobotRPC_NI *ni = (GobotRPC_NI *)pvParameters;
|
||||
ni->rxTask();
|
||||
}
|
||||
|
||||
GobotRPC_NI::GobotRPC_NI(unsigned int core, QueueHandle_t txQueue, QueueHandle_t rxQueue) {
|
||||
this->core = core;
|
||||
|
||||
this->txQueue = txQueue;
|
||||
this->rxQueue = rxQueue;
|
||||
|
||||
this->onPackageRx = NULL;
|
||||
this->onPackageRxArgs = NULL;
|
||||
|
||||
xTaskCreateAffinitySet(
|
||||
rxTaskFn,
|
||||
"GobotRPC_NI RX Task",
|
||||
2048,
|
||||
this,
|
||||
4,
|
||||
this->core,
|
||||
&this->rxTaskHandle
|
||||
);
|
||||
}
|
||||
|
||||
void GobotRPC_NI::registerOnPackageRxCallback(onPackageRxCallback callback, void *context) {
|
||||
this->onPackageRx = callback;
|
||||
this->onPackageRxArgs = context;
|
||||
}
|
||||
|
||||
void GobotRPC_NI::rxTask() {
|
||||
while (true) {
|
||||
GobotRPC_NI_Package_Transport package;
|
||||
|
||||
if (xQueueReceive(this->rxQueue, &package, portMAX_DELAY) == pdTRUE) {
|
||||
GobotRPCHeaderInfo header = extractGobotRPCHeader(package.data);
|
||||
|
||||
if(this->onPackageRx != NULL) {
|
||||
if (!checkCRC(package.data, header.len)) {
|
||||
volatile int a = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
this->onPackageRx(
|
||||
onPackageRxArgs,
|
||||
package.data + GobotRPC_Package_DATA_OFFSET,
|
||||
header.len, header.type, header.number
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GobotRPC_NI::sendPackage(char *data, size_t len, GobotRPCTypes type, GobotRPCNumber number) {
|
||||
GobotRPC_NI_Package_Transport package;
|
||||
memcpy(package.data + GobotRPC_Package_DATA_OFFSET, data, len);
|
||||
assembleGobotRPCHeader(package.data, number, type, len);
|
||||
assembleCRC(package.data, len);
|
||||
xQueueSend(this->txQueue, &package, portMAX_DELAY);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
#include "node_interface_hardware.hpp"
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include <string.h>
|
||||
|
||||
GobotRPC_NI_Hardware_RP2040_I2C * g_gobotrpc_ni_hardware_rp2040_i2c;
|
||||
i2c_inst_t * g_i2c_inst;
|
||||
bool g_transfer_in_progress = false;
|
||||
|
||||
static void finish_transfer(BaseType_t * xHigherPriorityTaskWoken) {
|
||||
if(g_transfer_in_progress) {
|
||||
GobotRPC_NI_Hardware_RP2040_I2C * gobotrpc_ni = g_gobotrpc_ni_hardware_rp2040_i2c;
|
||||
g_transfer_in_progress = false;
|
||||
gobotrpc_ni->onI2CIRQ(I2C_SLAVE_FINISH, xHigherPriorityTaskWoken);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_isr() {
|
||||
GobotRPC_NI_Hardware_RP2040_I2C * gobotrpc_ni = g_gobotrpc_ni_hardware_rp2040_i2c;
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
|
||||
i2c_hw_t * hw = i2c_get_hw(g_i2c_inst);
|
||||
uint32_t intr_stat = hw->intr_stat;
|
||||
|
||||
if (intr_stat == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (intr_stat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) {
|
||||
hw->clr_tx_abrt;
|
||||
finish_transfer(&xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (intr_stat & I2C_IC_INTR_STAT_R_START_DET_BITS) {
|
||||
hw->clr_start_det;
|
||||
finish_transfer(&xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) {
|
||||
hw->clr_stop_det;
|
||||
finish_transfer(&xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) {
|
||||
g_transfer_in_progress = true;
|
||||
gobotrpc_ni->onI2CIRQ(I2C_SLAVE_RECEIVE, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
if (intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) {
|
||||
hw->clr_rd_req;
|
||||
g_transfer_in_progress = true;
|
||||
gobotrpc_ni->onI2CIRQ(I2C_SLAVE_REQUEST, &xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
static void preTxTaskFn(void *pvParameters) {
|
||||
GobotRPC_NI_Hardware_RP2040_I2C * hw = (GobotRPC_NI_Hardware_RP2040_I2C *)(pvParameters);
|
||||
hw->preTxTask();
|
||||
};
|
||||
|
||||
GobotRPC_NI_Hardware_RP2040_I2C::GobotRPC_NI_Hardware_RP2040_I2C(
|
||||
xQueueHandle TXQueue, xQueueHandle RXQueue, uint core,
|
||||
i2c_inst_t * i2c_inst,
|
||||
uint8_t i2cAddress,
|
||||
uint i2cSDA_PIN, uint i2cSCL_PIN, uint int_PIN
|
||||
) {
|
||||
g_gobotrpc_ni_hardware_rp2040_i2c = this;
|
||||
g_i2c_inst = i2c_inst;
|
||||
|
||||
this->core = core;
|
||||
|
||||
this->isrRXQueue = RXQueue;
|
||||
this->isrPreTxQueue = TXQueue;
|
||||
this->isrTXQueue = xQueueCreate(3, sizeof(GobotRPC_NI_Package_Transport));
|
||||
|
||||
xTaskCreateAffinitySet(
|
||||
preTxTaskFn,
|
||||
"GobotRPC_NI Hardware RP2040 I2C PreTx Task",
|
||||
2048,
|
||||
this,
|
||||
4,
|
||||
this->core,
|
||||
&this->preTxTaskHandle
|
||||
);
|
||||
|
||||
this->i2cSDA_PIN = i2cSDA_PIN;
|
||||
this->i2cSCL_PIN = i2cSCL_PIN;
|
||||
this->int_PIN = int_PIN;
|
||||
|
||||
this->i2cAddress = i2cAddress;
|
||||
this->i2c_inst = i2c_inst;
|
||||
|
||||
memset(&rxPackage.data, 0, sizeof(GobotRPC_NI_Package_Transport::data));
|
||||
memset(&txPackage.data, 0, sizeof(GobotRPC_NI_Package_Transport::data));
|
||||
|
||||
gpio_init(i2cSDA_PIN);
|
||||
gpio_init(i2cSCL_PIN);
|
||||
gpio_set_function(i2cSDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(i2cSCL_PIN, GPIO_FUNC_I2C);
|
||||
|
||||
gpio_init(int_PIN);
|
||||
gpio_set_dir(int_PIN, GPIO_OUT);
|
||||
gpio_put(int_PIN, 0);
|
||||
|
||||
// Note: The I2C slave does clock stretching implicitly after a RD_REQ, while the Tx FIFO is empty.
|
||||
// There is also an option to enable clock stretching while the Rx FIFO is full, but we leave it
|
||||
// disabled since the Rx FIFO should never fill up (unless slave->handler() is way too slow).
|
||||
i2c_set_slave_mode(i2c_inst, true, i2cAddress);
|
||||
|
||||
i2c_hw_t *hw = i2c_get_hw(i2c_inst);
|
||||
// unmask necessary interrupts
|
||||
hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS |\
|
||||
I2C_IC_INTR_MASK_M_RD_REQ_BITS |\
|
||||
I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS |\
|
||||
I2C_IC_INTR_MASK_M_STOP_DET_BITS |\
|
||||
I2C_IC_INTR_MASK_M_START_DET_BITS;
|
||||
|
||||
// enable interrupt for current core
|
||||
unsigned int intNum = i2c_inst == i2c0 ? I2C0_IRQ : I2C1_IRQ;
|
||||
irq_set_exclusive_handler(intNum, i2c_isr);
|
||||
irq_set_enabled(intNum, true);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "node_interface_hardware.hpp"
|
||||
#include "hardware/irq.h"
|
||||
|
||||
void GobotRPC_NI_Hardware_RP2040_I2C::onI2CIRQ(I2C_SLAVE_EVENT event, BaseType_t * xHigherPriorityTaskWoken) {
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_RECEIVE: { // I2C Write from Master
|
||||
char data = i2c_read_byte_raw(i2c_inst);
|
||||
|
||||
switch (writeState) {
|
||||
case I2C_WRITE_STATE_FIRST:
|
||||
writeState = I2C_WRITE_STATE_LENGTH;
|
||||
rxPackage.index = 0;
|
||||
rxPackage.length = 0;
|
||||
rxPackage.data[rxPackage.index++] = data;
|
||||
break;
|
||||
|
||||
case I2C_WRITE_STATE_LENGTH:
|
||||
rxPackage.data[rxPackage.index++] = data;
|
||||
rxPackage.length = data;
|
||||
writeState = I2C_WRITE_STATE_DATA;
|
||||
break;
|
||||
|
||||
case I2C_WRITE_STATE_DATA:
|
||||
rxPackage.data[rxPackage.index++] = data;
|
||||
|
||||
if(rxPackage.index == rxPackage.length) {
|
||||
xQueueSendFromISR(isrRXQueue, &rxPackage, xHigherPriorityTaskWoken);
|
||||
writeState = I2C_WRITE_STATE_FIRST;
|
||||
} else {
|
||||
writeState = I2C_WRITE_STATE_DATA;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case I2C_SLAVE_REQUEST: { // I2C Read from Master
|
||||
if(xQueueIsQueueEmptyFromISR(isrTXQueue) == pdFALSE) {
|
||||
readState = I2C_READ_STAGE_INVALID;
|
||||
}
|
||||
|
||||
switch (readState) {
|
||||
case I2C_READ_STAGE_INVALID:
|
||||
i2c_write_byte_raw(i2c_inst, 0x00);
|
||||
break;
|
||||
|
||||
case I2C_READ_STAGE_FIRST:
|
||||
xQueueReceiveFromISR(isrTXQueue, &txPackage, xHigherPriorityTaskWoken);
|
||||
txPackage.index = 0;
|
||||
i2c_write_byte_raw(i2c_inst, txPackage.data[txPackage.index++]);
|
||||
readState = I2C_READ_STAGE_WIP;
|
||||
break;
|
||||
|
||||
case I2C_READ_STAGE_WIP:
|
||||
if(txPackage.index == txPackage.length - 1) {
|
||||
readState = I2C_READ_STAGE_DONE;
|
||||
} else {
|
||||
i2c_write_byte_raw(i2c_inst, txPackage.data[txPackage.index++]);
|
||||
}
|
||||
break;
|
||||
|
||||
case I2C_READ_STAGE_DONE:
|
||||
i2c_write_byte_raw(i2c_inst, 0x00);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case I2C_SLAVE_FINISH:
|
||||
writeState = I2C_WRITE_STATE_FIRST;
|
||||
readState = I2C_READ_STAGE_FIRST;
|
||||
rxPackage.index = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GobotRPC_NI_Hardware_RP2040_I2C::preTxTask() {
|
||||
GobotRPC_NI_Package_Transport pkg;
|
||||
|
||||
while (1) {
|
||||
xQueueReceive(isrPreTxQueue, &pkg, portMAX_DELAY);
|
||||
xQueueSend(isrTXQueue, &pkg, portMAX_DELAY);
|
||||
gpio_put(int_PIN, 1);
|
||||
}
|
||||
}
|
||||
@@ -22,4 +22,10 @@ void assembleCRC(char * buffer, size_t data_len) {
|
||||
unsigned short crc = crc16(buffer, data_len + GobotRPC_Package_DATA_OFFSET);
|
||||
buffer[GobotRPC_Package_DATA_OFFSET + data_len + 1] = crc & 0xff;
|
||||
buffer[GobotRPC_Package_DATA_OFFSET + data_len + 0] = (crc >> 8) & 0xff;
|
||||
}
|
||||
|
||||
bool checkCRC(char * buffer, size_t data_len) {
|
||||
unsigned short crc = crc16(buffer, data_len - 2);
|
||||
unsigned short crc_received = buffer[data_len - GobotRPC_Package_DATA_OFFSET + 1] | (buffer[data_len - GobotRPC_Package_DATA_OFFSET + 0] << 8);
|
||||
return crc == crc_received;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ void GobotRPC_TI_Hardware_RP2040_I2C::i2cTxTask() {
|
||||
pullPackageCB(&pkg, pullPackageCBArgs);
|
||||
|
||||
xSemaphoreTake(i2cMutex, portMAX_DELAY);
|
||||
unsigned int res = i2c_write_burst_blocking(i2c, pkg.addr & 0x7f, (uint8_t *)(&pkg.data), pkg.len);
|
||||
unsigned int res = i2c_write_blocking(i2c, pkg.addr & 0x7f, (uint8_t * )(pkg.data), pkg.len, false);
|
||||
xSemaphoreGive(i2cMutex);
|
||||
|
||||
if(res == PICO_ERROR_GENERIC) {
|
||||
|
||||
BIN
i2c-hub/pulseview-i2c-session
Normal file
BIN
i2c-hub/pulseview-i2c-session
Normal file
Binary file not shown.
Reference in New Issue
Block a user