ESP-IDF v5.5.2
The Parlio Tx unit working properly. But after I initialized Rx unit, and try to create a rx transaction through function: parlio_rx_unit_receive(), invalid argument error happened.
Below is relative codes, if someone from Esprissif can help to check my code and let me know what's wrong ?
Thank you in advance.
FYI, you can start from function: parlioRxReceive() (fpga.c)
Code: Select all
#include "fpga.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "driver/parlio_tx.h"
#include "driver/parlio_rx.h"
#include "driver/gpio.h"
#include "gpio.h"
#define DBUS0 3
#define DBUS1 0
#define DBUS2 1
#define DBUS3 6
#define DBUS4 7
#define DBUS5 8
#define DBUS6 9
#define DBUS7 10
#define TX_EN 24 //high active
#define RX_EN 23 //high active
#define PAD_CLK 25
extern const char *TAG; //define in softap_main.c
typedef struct {
parlio_tx_unit_handle_t unit;
volatile bool ing; //是否有并口数据正在发送中
parlio_transmit_config_t transmitConfig;
} ParlioTx;
typedef struct {
parlio_rx_unit_handle_t unit;
parlio_rx_delimiter_handle_t deli; //RX软界定符
uint8_t *buffer; //用于并口数据接收的DMA兼容缓冲区
volatile bool bufferFilled; //用于接收数据的缓冲区中存在收到但尚未处理的数据
volatile bool ing; //是否有并口数据正在接收中
volatile bool timeoutError;
parlio_receive_config_t receiveConfig;
} ParlioRx;
static ParlioTx tx = {
.unit = NULL,
.ing = false,
.transmitConfig = {
.idle_value = 0x00 // 空闲状态下所有数据线均为低电平
}
};
static ParlioRx rx = {
.unit = NULL,
.deli = NULL,
.buffer = NULL,
.bufferFilled = false,
.ing = false,
.timeoutError = false,
// 配置 RX 单元接收参数
.receiveConfig = {
.delimiter = NULL, // 使用上面创建的软帧界定符
.flags = {
.partial_rx_en = false, // 禁用部分接收模式
.indirect_mount = false,
}
}
};
static IRAM_ATTR bool parlio_tx_done_cb(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx)
{
//printf("parlioTx transaction completed.\n"); 这行代码会引起 lock error
tx.ing = false;
return false; //Whether a high priority task has been waken up by this callback function
}
static bool on_rx_partial_receive_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// 当接收到部分数据时调用(用于无限事务)。可在回调中作简单处理,如队列、任务操作,或将接受完成的数据拷贝到用户 buffer 中
return false;
}
static bool on_rx_receive_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, false); //停止软帧界定符
rx.ing = false;
rx.bufferFilled = true;
return false;
}
static bool on_rx_timeout_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// 当接收超时时调用
parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, false); //停止软帧界定符
rx.timeoutError = true; //置接收超时标志
rx.ing = false;
return false;
}
bool openParlioTx()
{
if (tx.unit!=NULL){
ESP_LOGE(TAG,"Found tx.unit is not NULL in openParlioTx().");
return false;
}
tx.ing = false;
parlio_tx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_DEFAULT, // 选择默认的时钟源
.data_width = 8, // 数据宽度为 8 位
.clk_in_gpio_num = -1, // 不使用外部时钟源
.valid_gpio_num = TX_EN, // 在TX_EN输出有效信号
.clk_out_gpio_num = PAD_CLK,
.data_gpio_nums = {
DBUS0,DBUS1,DBUS2,DBUS3,DBUS4,DBUS5,DBUS6,DBUS7,
},
.output_clk_freq_hz = 10 * 1000 * 1000, // 输出时钟频率为 10 MHz
.trans_queue_depth = 1, // 待处理事务队列深度为1
.max_transfer_size = 128, // 一次传输的最大传输大小为 128 字节
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // 在时钟下降沿采样数据
.flags = {
.invert_valid_out = false, // 有效信号默认高电平有效(不反转)
}
};
esp_err_t r = parlio_new_tx_unit(&config, &tx.unit); // 创建 TX 单元实例
ESP_ERROR_CHECK(r);
if (r!=ESP_OK) {
tx.unit = NULL;
return false;
}
parlio_tx_event_callbacks_t parlio_cbs = {
.on_trans_done = parlio_tx_done_cb,
};
r = parlio_tx_unit_register_event_callbacks(tx.unit, &parlio_cbs, NULL);
ESP_ERROR_CHECK(r);
if (r!=ESP_OK)
return false;
r = parlio_tx_unit_enable(tx.unit); // 使能 TX 单元
ESP_ERROR_CHECK(r);
if (r!=ESP_OK)
return false;
printf("Open Parlio Tx\n");
return true;
}
bool openParlioRx() {
if (rx.unit!=NULL){
ESP_LOGE(TAG,"Found rx.unit is not NULL in openParlioRx().");
return false;
}
if (rx.buffer==NULL) { //分配用于并口数据接收的缓冲区(全生命周期有效,不考虑回收问题)
rx.buffer = heap_caps_calloc(1, 512, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
if (rx.buffer == NULL) {
ESP_LOGE(TAG,"Allocation of DMA capatible bufferParlioRx failed!");
}
}
rx.ing = false;
rx.bufferFilled = false;
rx.timeoutError = false;
parlio_rx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_DEFAULT, // 选择默认的时钟源
.data_width = 8, // 数据宽度为 8 位
.clk_in_gpio_num = -1, // 不使用外部时钟源
.clk_out_gpio_num = PAD_CLK, // 输出时钟引脚
.valid_gpio_num = RX_EN, // 在RX_EN脚上输出有效信号
.data_gpio_nums = {
DBUS0,DBUS1,DBUS2,DBUS3,DBUS4,DBUS5,DBUS6,DBUS7,
},
.exp_clk_freq_hz = 1 * 1000 * 1000, // 期望时钟频率为 10 MHz
.trans_queue_depth = 10, // 事务队列深度为 1
.max_recv_size = 1024, // 最大接收大小为 512 字节
};
esp_err_t r = parlio_new_rx_unit(&config, &rx.unit); // 创建 RX 单元实例
ESP_ERROR_CHECK(r);
if (r!=ESP_OK) {
rx.unit = NULL;
return false;
}
parlio_rx_event_callbacks_t cbs = {
.on_partial_receive = on_rx_partial_receive_callback,
.on_receive_done = on_rx_receive_done_callback,
.on_timeout = on_rx_timeout_callback
};
r = parlio_rx_unit_register_event_callbacks(rx.unit, &cbs, NULL);
ESP_ERROR_CHECK(r);
if (r!=ESP_OK)
return false;
r = parlio_rx_unit_enable(rx.unit,true); // 使能RX单元
ESP_ERROR_CHECK(r);
if (r!=ESP_OK)
return false;
parlio_rx_soft_delimiter_config_t soft_config = {
.sample_edge = PARLIO_SAMPLE_EDGE_POS, // 上升沿采样
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // 从 MSB 开始打包位
.eof_data_len = 512, // 512字节后结束帧,由于没有其他结束依据,软帧界定符必须设置该字段
.timeout_ticks = 0 // 无超时
};
r = parlio_new_rx_soft_delimiter(&soft_config, &rx.deli);
ESP_ERROR_CHECK(r);
if (r!=ESP_OK)
return false;
printf("Open Parlio Rx\n");
return true;
}
void closeParlioTx() {
if (tx.unit==NULL){
ESP_LOGE(TAG,"Found tx.unit is NULL in closeParlioTx().");
return;
}
ESP_ERROR_CHECK(parlio_tx_unit_disable(tx.unit));
ESP_ERROR_CHECK(parlio_del_tx_unit(tx.unit));
tx.unit = NULL;
tx.ing = false;
printf("ParlioTx closed\n");
}
void closeParlioRx() {
if (rx.unit==NULL){
ESP_LOGE(TAG,"Found rx.unit is NULL in closeParlioRx().");
return;
}
ESP_ERROR_CHECK(parlio_rx_unit_disable(rx.unit));
ESP_ERROR_CHECK(parlio_del_rx_unit(rx.unit));
rx.unit = NULL;
rx.ing = false;
rx.bufferFilled = false;
rx.timeoutError = false;
ESP_ERROR_CHECK(parlio_del_rx_delimiter(rx.deli));
rx.deli = NULL;
printf("PalioRx closed\n");
}
bool parlioTxSend(const uint8_t* data, uint16_t sizeBytes)
{
if (tx.ing || rx.ing) //正在发送或接收数据,不能发送
return false;
if (rx.unit!=NULL)
closeParlioRx();
if (tx.unit==NULL)
openParlioTx();
tx.ing = true;
esp_err_t r = parlio_tx_unit_transmit(tx.unit,data,sizeBytes*sizeof(uint8_t)*8,&tx.transmitConfig);
ESP_ERROR_CHECK(r);
return (r==ESP_OK);
}
bool parlioRxReceive(uint8_t* buffer, const size_t sizeBuffer){
if (tx.ing || rx.ing) //正在发送或接收数据,不能发起新的接收事务
return false;
if (tx.unit!=NULL)
closeParlioTx();
if (rx.unit==NULL)
openParlioRx();
printf("parlioRxReceive execute.\n");
rx.ing = true;
// 配置 RX 单元接收参数
rx.receiveConfig.delimiter = rx.deli;
// 启动软帧界定符(仅软帧界定符需要)
esp_err_t r = parlio_rx_soft_delimiter_start_stop(rx.unit, rx.deli, true);
ESP_ERROR_CHECK(r);
if (rx.deli==NULL)
printf("Error: rx.deli==NULL\n");
// 启动接收事务
r = parlio_rx_unit_receive(rx.unit, buffer, sizeBuffer, &rx.receiveConfig);
ESP_ERROR_CHECK(r);
return (r==ESP_OK);
}
//如果有且条件允许,接收FPGA响应包
void tryReceiveResponsePacket() {
if (tx.ing || rx.ing) //正在发送或接收数据,不能发起新的接收事务
return;
if (rx.bufferFilled) //接收缓冲区不空,不能发起新的接收事务
return;
if (gpio_get_level(RDATA_RDY)==0)
return;
//RDATA_RDY为高,表示FPGA请求发送响应包
parlioRxReceive(rx.buffer,512);
}
void processResponsePacket() {
if (!rx.bufferFilled)
return;
printf("recv: %x, %x, %x, %x\n", rx.buffer[0],rx.buffer[1],rx.buffer[2],rx.buffer[15]);
rx.bufferFilled = false;
}