mirror of
https://gitee.com/Vancouver2017/luban-lite.git
synced 2025-12-17 09:38:55 +00:00
v1.2.2
This commit is contained in:
@@ -13,6 +13,11 @@ if GetDepend('AIC_QSPI_DRV_TEST') and GetDepend('RT_USING_FINSH'):
|
||||
if GetDepend('AIC_QSPI_DRV_V11') and GetDepend('AIC_CHIP_D13X'):
|
||||
src += Glob('test_spislave*.c')
|
||||
|
||||
if GetDepend('AIC_EXAMPLE') and GetDepend('RT_USING_FINSH'):
|
||||
src += Glob('test_spiflash_dev_example.c')
|
||||
src += Glob('spi_dev_example.c')
|
||||
src += Glob('qspi_dev_example.c')
|
||||
|
||||
group = DefineGroup('test-qspi', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
|
||||
358
bsp/examples/test-qspi/qspi_dev_example.c
Normal file
358
bsp/examples/test-qspi/qspi_dev_example.c
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (c) 2025, ArtInChip Technology Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Authors: Cui Jiawei <jiawei.cui@artinchip.com>
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <aic_core.h>
|
||||
#include <drv_qspi.h>
|
||||
#include <drivers/spi.h>
|
||||
|
||||
/* Ensure that "Board options/Using SPI1" is enabled before running this demo. */
|
||||
#define QSPI_BUS_NAME "qspi1" /* QSPI bus name */
|
||||
#define QSPI_DEV_NAME "qspidev" /* QSPI bus name */
|
||||
#define TEST_DATA_LEN 10 /* Test data length */
|
||||
#define THREAD_PREPARE_EVENT (1 << 0)
|
||||
#define THREAD_SEND_EVENT (1 << 1)
|
||||
|
||||
static struct rt_qspi_device *g_qspi_dev = RT_NULL;
|
||||
static rt_mq_t data_mq;
|
||||
static rt_event_t g_event;
|
||||
|
||||
/**
|
||||
* Initialize QSPI device with default configuration
|
||||
*
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t qspi_demo_init(void)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct rt_qspi_configuration cfg;
|
||||
|
||||
/* If already initialized, configure the default values directly */
|
||||
if (g_qspi_dev != RT_NULL) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = aic_qspi_bus_attach_device(QSPI_BUS_NAME, QSPI_DEV_NAME, 0, AIC_QSPI0_BUS_WIDTH, RT_NULL,
|
||||
RT_NULL);
|
||||
if (ret != RT_EOK) {
|
||||
pr_err("Failed to attach device in bus_name %s\n", QSPI_BUS_NAME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
g_qspi_dev = (struct rt_qspi_device *)rt_device_find(QSPI_DEV_NAME);
|
||||
if (g_qspi_dev == RT_NULL) {
|
||||
pr_err("Failed to get device in name %s\n", QSPI_BUS_NAME);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
exit:
|
||||
// /* Default configuration (Mode 0, MSB first, 8-bit, 1MHz) */
|
||||
rt_memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.parent.mode = RT_SPI_MODE_0 | RT_SPI_MSB;
|
||||
cfg.parent.max_hz = 100000000;
|
||||
cfg.parent.data_width = 8;
|
||||
cfg.qspi_dl_width = 4;
|
||||
cfg.ddr_mode = 0;
|
||||
ret = rt_qspi_configure(g_qspi_dev, &cfg);
|
||||
if (ret != RT_EOK) {
|
||||
pr_err("QSPI config failed: %d\n", ret);
|
||||
return -ret;
|
||||
}
|
||||
|
||||
rt_kprintf("[OK] QSPI initialized (dev:%s)\n", QSPI_DEV_NAME);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static void qspi_prepare_thread(void *parameter)
|
||||
{
|
||||
rt_uint8_t data_buf[TEST_DATA_LEN];
|
||||
rt_uint32_t cnt = 0;
|
||||
|
||||
while (1) {
|
||||
/* Prepare the data, here using memset as a simple substitute. */
|
||||
rt_memset(data_buf, 0x9f, sizeof(rt_uint8_t) * TEST_DATA_LEN);
|
||||
|
||||
rt_mq_send(data_mq, data_buf, TEST_DATA_LEN);
|
||||
|
||||
rt_kprintf("qspi prepare data success, cnt: %d\n", ++cnt);
|
||||
if (cnt >= 5)
|
||||
break;
|
||||
}
|
||||
rt_event_send(g_event, THREAD_PREPARE_EVENT);
|
||||
}
|
||||
|
||||
static void qspi_send_thread(void *parameter)
|
||||
{
|
||||
struct rt_spi_device *spi_dev = &(g_qspi_dev->parent);
|
||||
struct rt_qspi_message message;
|
||||
rt_uint32_t cnt = 0;
|
||||
rt_err_t ret = 0;
|
||||
rt_uint8_t *data_buf = NULL;
|
||||
|
||||
data_buf = rt_malloc_align(TEST_DATA_LEN, 8);
|
||||
if (data_buf == NULL) {
|
||||
pr_err("data_buf malloc failure.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = rt_spi_nonblock_set(spi_dev, 1);
|
||||
if (ret < 0) {
|
||||
pr_err("spi nonblock set failure. ret = %ld\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rt_memset(&message, 0, sizeof(message));
|
||||
message.qspi_data_lines = 1;
|
||||
message.parent.send_buf = data_buf;
|
||||
message.parent.recv_buf = RT_NULL;
|
||||
message.parent.length = TEST_DATA_LEN;
|
||||
message.parent.cs_take = 1;
|
||||
|
||||
/**
|
||||
* After asynchronous transmission completes,the CS pin will be released in
|
||||
* the callback function, so this configuration is invalid.
|
||||
*/
|
||||
message.parent.cs_release = 1;
|
||||
message.parent.next = RT_NULL;
|
||||
|
||||
while (1) {
|
||||
rt_mq_recv(data_mq, data_buf, TEST_DATA_LEN, RT_WAITING_FOREVER);
|
||||
ret = rt_spi_take_bus(spi_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("rt_spi_take_bus failure. ret = %ld\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
rt_qspi_transfer_message(g_qspi_dev, &message);
|
||||
ret = rt_spi_wait_completion(spi_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("rt_spi_wait_completion failure. ret = %ld\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
ret = rt_spi_release_bus(spi_dev);
|
||||
if (ret < 0) {
|
||||
pr_err("rt_spi_release_bus failure. ret = %ld\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
rt_kprintf("qspi send data success, cnt: %d\n", ++cnt);
|
||||
if (cnt >= 5)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
rt_free_align(data_buf);
|
||||
rt_spi_nonblock_set(spi_dev, 0);
|
||||
rt_event_send(g_event, THREAD_SEND_EVENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create two threads: one for asynchronous transmission and another for data preparation.
|
||||
* Using asynchronous sending allows the CPU to free up time to handle other matters
|
||||
* (such as preparing the data to be sent).
|
||||
*
|
||||
* Note: QSPI does not support asynchronous reception.
|
||||
*
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t qspi_async_send(void)
|
||||
{
|
||||
rt_thread_t send_thread = NULL, prepare_thread = NULL;
|
||||
rt_uint32_t recv_event = 0;
|
||||
|
||||
/* Create an event to manage two threads. */
|
||||
g_event = rt_event_create("qspi_manage_evt", RT_IPC_FLAG_PRIO);
|
||||
if (g_event == RT_NULL) {
|
||||
pr_err("Failed to create event g_event\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
data_mq = rt_mq_create("data_mq", TEST_DATA_LEN, 1, RT_IPC_FLAG_FIFO);
|
||||
if (data_mq == RT_NULL) {
|
||||
pr_err("Failed to create message queue\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
send_thread = rt_thread_create("qspi_send_thread", qspi_send_thread, RT_NULL, 1024 * 4, 24, 10);
|
||||
if (send_thread != RT_NULL) {
|
||||
rt_thread_startup(send_thread);
|
||||
} else {
|
||||
pr_err("Failed to create thread: qspi_send_thread\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
prepare_thread =
|
||||
rt_thread_create("qspi_prepare_thread", qspi_prepare_thread, RT_NULL, 1024 * 4, 25, 10);
|
||||
if (prepare_thread != RT_NULL) {
|
||||
rt_thread_startup(prepare_thread);
|
||||
} else {
|
||||
pr_err("Failed to create thread: qspi_prepare_thread\n");
|
||||
rt_thread_delete(send_thread);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* Wait for two threads to finish, then clean up resources. */
|
||||
rt_event_recv(g_event, THREAD_PREPARE_EVENT | THREAD_SEND_EVENT,
|
||||
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &recv_event);
|
||||
rt_mq_delete(data_mq);
|
||||
rt_event_delete(g_event);
|
||||
|
||||
rt_kprintf("[OK] QSPI async send finish\n");
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change QSPI transfer mode (0-3)
|
||||
*
|
||||
* @param mode Target QSPI mode (0-3)
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t qspi_change_mode(rt_uint8_t mode)
|
||||
{
|
||||
struct rt_qspi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_qspi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_qspi_dev->config, sizeof(cfg));
|
||||
|
||||
/* Clear existing mode settings */
|
||||
cfg.parent.mode &= ~(RT_SPI_CPHA | RT_SPI_CPOL);
|
||||
|
||||
/* Set new mode */
|
||||
switch (mode) {
|
||||
case 0:
|
||||
cfg.parent.mode |= RT_SPI_MODE_0;
|
||||
break;
|
||||
case 1:
|
||||
cfg.parent.mode |= RT_SPI_MODE_1;
|
||||
break;
|
||||
case 2:
|
||||
cfg.parent.mode |= RT_SPI_MODE_2;
|
||||
break;
|
||||
case 3:
|
||||
cfg.parent.mode |= RT_SPI_MODE_3;
|
||||
break;
|
||||
default:
|
||||
pr_err("Invalid mode %d\n", mode);
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
ret = rt_qspi_configure(g_qspi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] QSPI mode changed to %d\n", mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bit transmission order (LSB/MSB first)
|
||||
*
|
||||
* @param lsb_first RT_TRUE for LSB first, RT_FALSE for MSB first
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t qspi_set_bit_order(rt_bool_t lsb_first)
|
||||
{
|
||||
struct rt_qspi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_qspi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_qspi_dev->config, sizeof(cfg));
|
||||
|
||||
/* Set bit order */
|
||||
if (lsb_first) {
|
||||
cfg.parent.mode &= ~RT_SPI_MSB;
|
||||
|
||||
} else {
|
||||
cfg.parent.mode |= RT_SPI_MSB;
|
||||
}
|
||||
|
||||
ret = rt_qspi_configure(g_qspi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] Bit order set to %s first\n", lsb_first ? "LSB" : "MSB");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust QSPI clock frequency
|
||||
*
|
||||
* @param hz New clock frequency in Hz
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t qspi_set_freq(uint32_t hz)
|
||||
{
|
||||
struct rt_qspi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_qspi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_qspi_dev->config, sizeof(cfg));
|
||||
cfg.parent.max_hz = hz;
|
||||
|
||||
ret = rt_qspi_configure(g_qspi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] QSPI speed set to %d Hz\n", hz);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive test function demonstrating:
|
||||
* 1. Async send
|
||||
* 2. Mode switching
|
||||
* 3. Bit order change
|
||||
* 4. Speed adjustment
|
||||
*/
|
||||
static int cmd_qspi_dev_usage(void)
|
||||
{
|
||||
int err = RT_EOK;
|
||||
|
||||
/* Initialize */
|
||||
err = qspi_demo_init();
|
||||
if (err != RT_EOK) {
|
||||
pr_err("qspi_demo_init failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Perform async send */
|
||||
err = qspi_async_send();
|
||||
if (err != RT_EOK) {
|
||||
pr_err("qspi_async_send failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Change to Mode 0 */
|
||||
err = qspi_change_mode(0);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("qspi_change_mode failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set MSB first */
|
||||
err = qspi_set_bit_order(RT_FALSE);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("qspi_set_bit_order failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Adjust speed to 100MHz */
|
||||
err = qspi_set_freq(100000000);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("qspi_set_freq failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_kprintf("[OK] All QSPI tests passed!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
MSH_CMD_EXPORT_ALIAS(cmd_qspi_dev_usage, qspi_dev_usage, QSPI device example);
|
||||
307
bsp/examples/test-qspi/spi_dev_example.c
Normal file
307
bsp/examples/test-qspi/spi_dev_example.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright (c) 2025, ArtInChip Technology Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Authors: Cui Jiawei <jiawei.cui@artinchip.com>
|
||||
*/
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <aic_core.h>
|
||||
|
||||
/* Ensure that "Board options/Using SPI3" is enabled before running this demo. */
|
||||
#define SPI_BUS_NAME "spi3" /* SPI bus name */
|
||||
#define SPI_DEVICE_NAME "spidev" /* SPI device name */
|
||||
#define TEST_DATA_SIZE 4 /* Test data length */
|
||||
|
||||
static struct rt_spi_device *g_spi_dev = RT_NULL;
|
||||
|
||||
/**
|
||||
* Initialize SPI device with default configuration
|
||||
*
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_demo_init(void)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
struct rt_spi_configuration cfg;
|
||||
|
||||
/* If already initialized, configure the default values directly */
|
||||
if (g_spi_dev != RT_NULL) {
|
||||
goto config;
|
||||
}
|
||||
|
||||
g_spi_dev = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
|
||||
if (g_spi_dev == RT_NULL) {
|
||||
rt_kprintf("malloc failed.\n");
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
ret = rt_spi_bus_attach_device(g_spi_dev, SPI_DEVICE_NAME, SPI_BUS_NAME, RT_NULL);
|
||||
if (ret != RT_EOK && g_spi_dev != NULL) {
|
||||
rt_free(g_spi_dev);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
config:
|
||||
/* Default configuration (Mode 0, MSB first, 8-bit, 1MHz) */
|
||||
cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;
|
||||
cfg.max_hz = 1 * 1000 * 1000;
|
||||
cfg.data_width = 8;
|
||||
ret = rt_spi_configure(g_spi_dev, &cfg);
|
||||
if (ret != RT_EOK) {
|
||||
pr_err("SPI config failed: %d\n", ret);
|
||||
return -ret;
|
||||
}
|
||||
|
||||
rt_kprintf("[OK] SPI initialized (dev:%s)\n", SPI_DEVICE_NAME);
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform full-duplex SPI transfer
|
||||
*
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_full_duplex_transfer(void)
|
||||
{
|
||||
struct rt_spi_message msg;
|
||||
rt_uint32_t i = 0;
|
||||
rt_uint8_t *tx_buf = NULL;
|
||||
rt_uint8_t *rx_buf = NULL;
|
||||
rt_err_t err = RT_EOK;
|
||||
|
||||
if (!g_spi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
tx_buf = aicos_malloc_align(0, TEST_DATA_SIZE, CACHE_LINE_SIZE);
|
||||
rx_buf = aicos_malloc_align(0, TEST_DATA_SIZE, CACHE_LINE_SIZE);
|
||||
if (tx_buf == NULL || rx_buf == NULL) {
|
||||
printf("Low memory!\n");
|
||||
err = RT_ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
for (i = 0; i < TEST_DATA_SIZE; i++) {
|
||||
tx_buf[i] = i % 0x100;
|
||||
}
|
||||
|
||||
msg.send_buf = &tx_buf;
|
||||
msg.recv_buf = &rx_buf;
|
||||
msg.length = sizeof(tx_buf);
|
||||
msg.cs_take = 1;
|
||||
msg.cs_release = 0;
|
||||
msg.next = RT_NULL;
|
||||
|
||||
rt_kprintf("Transfer start...\n");
|
||||
rt_spi_transfer_message(g_spi_dev, &msg);
|
||||
|
||||
rt_kprintf("[OK] Transfer data success\n");
|
||||
rt_kprintf("TX: ");
|
||||
for (i = 0; i < TEST_DATA_SIZE; i++) {
|
||||
rt_kprintf("%02X ", tx_buf[i]);
|
||||
}
|
||||
rt_kprintf("\nRX: ");
|
||||
for (i = 0; i < TEST_DATA_SIZE; i++) {
|
||||
rt_kprintf("%02X ", rx_buf[i]);
|
||||
}
|
||||
rt_kprintf("\n");
|
||||
|
||||
exit:
|
||||
if (tx_buf)
|
||||
aicos_free_align(0, tx_buf);
|
||||
if (rx_buf)
|
||||
aicos_free_align(0, rx_buf);
|
||||
return -err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change SPI transfer mode (0-3)
|
||||
*
|
||||
* @param mode Target SPI mode (0-3)
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_change_mode(rt_uint8_t mode)
|
||||
{
|
||||
struct rt_spi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_spi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_spi_dev->config, sizeof(cfg));
|
||||
|
||||
/* Clear existing mode settings */
|
||||
cfg.mode &= ~(RT_SPI_CPHA | RT_SPI_CPOL);
|
||||
|
||||
/* Set new mode */
|
||||
switch (mode) {
|
||||
case 0:
|
||||
cfg.mode |= RT_SPI_MODE_0;
|
||||
break;
|
||||
case 1:
|
||||
cfg.mode |= RT_SPI_MODE_1;
|
||||
break;
|
||||
case 2:
|
||||
cfg.mode |= RT_SPI_MODE_2;
|
||||
break;
|
||||
case 3:
|
||||
cfg.mode |= RT_SPI_MODE_3;
|
||||
break;
|
||||
default:
|
||||
pr_err("Invalid mode %d\n", mode);
|
||||
return -RT_EINVAL;
|
||||
}
|
||||
|
||||
ret = rt_spi_configure(g_spi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] SPI mode changed to %d\n", mode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bit transmission order (LSB/MSB first)
|
||||
*
|
||||
* @param lsb_first RT_TRUE for LSB first, RT_FALSE for MSB first
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_set_bit_order(rt_bool_t lsb_first)
|
||||
{
|
||||
struct rt_spi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_spi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_spi_dev->config, sizeof(cfg));
|
||||
|
||||
/* Set bit order */
|
||||
if (lsb_first) {
|
||||
cfg.mode &= ~RT_SPI_MSB;
|
||||
|
||||
} else {
|
||||
cfg.mode |= RT_SPI_MSB;
|
||||
}
|
||||
|
||||
ret = rt_spi_configure(g_spi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] Bit order set to %s first\n", lsb_first ? "LSB" : "MSB");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable 3-wire half-duplex mode
|
||||
*
|
||||
* @param enable RT_TRUE to enable, RT_FALSE to disable
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_set_3wire_mode(rt_bool_t enable)
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_spi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
struct rt_spi_configuration cfg;
|
||||
rt_memcpy(&cfg, &g_spi_dev->config, sizeof(cfg));
|
||||
|
||||
/* Set 3-wire mode */
|
||||
if (enable) {
|
||||
cfg.mode |= RT_SPI_3WIRE;
|
||||
rt_kprintf("[WARN] MOSI/MISO merged to single line\n");
|
||||
} else {
|
||||
cfg.mode &= ~RT_SPI_3WIRE;
|
||||
}
|
||||
|
||||
ret = rt_spi_configure(g_spi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] 3-wire mode %s\n", enable ? "enabled" : "disabled");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust SPI clock frequency
|
||||
*
|
||||
* @param hz New clock frequency in Hz
|
||||
* @return RT_EOK on success, error code on failure
|
||||
*/
|
||||
static rt_err_t spi_set_freq(uint32_t hz)
|
||||
{
|
||||
struct rt_spi_configuration cfg;
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
if (!g_spi_dev)
|
||||
return -RT_ENOSYS;
|
||||
|
||||
rt_memcpy(&cfg, &g_spi_dev->config, sizeof(cfg));
|
||||
cfg.max_hz = hz;
|
||||
|
||||
ret = rt_spi_configure(g_spi_dev, &cfg);
|
||||
if (ret == RT_EOK) {
|
||||
rt_kprintf("[OK] SPI speed set to %d Hz\n", hz);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comprehensive test function demonstrating:
|
||||
* 1. Full-duplex transfer
|
||||
* 2. Mode switching (to Mode 2)
|
||||
* 3. Bit order change (to LSB first)
|
||||
* 4. 3-wire mode enable
|
||||
* 5. Speed adjustment (to 10MHz)
|
||||
*/
|
||||
static int cmd_spi_dev_usage(void)
|
||||
{
|
||||
int err = RT_EOK;
|
||||
|
||||
/* Initialize if needed */
|
||||
err = spi_demo_init();
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_demo_init failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Perform full-duplex transfer */
|
||||
err = spi_full_duplex_transfer();
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_full_duplex_transfer failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Change to Mode 2 */
|
||||
err = spi_change_mode(2);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_change_mode failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Set LSB first */
|
||||
err = spi_set_bit_order(RT_TRUE);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_set_bit_order failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable 3-wire mode */
|
||||
err = spi_set_3wire_mode(RT_TRUE);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_set_3wire_mode failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Adjust speed to 10MHz */
|
||||
err = spi_set_freq(10 * 1000 * 1000);
|
||||
if (err != RT_EOK) {
|
||||
pr_err("spi_set_freq failed!,err code:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
rt_kprintf("[OK] All SPI tests passed!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
MSH_CMD_EXPORT_ALIAS(cmd_spi_dev_usage, spi_dev_usage, SPI device example);
|
||||
217
bsp/examples/test-qspi/test_spiflash_dev_example.c
Normal file
217
bsp/examples/test-qspi/test_spiflash_dev_example.c
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright (c) 2025, ArtInChip Technology Co., Ltd
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Authors: Cui Jiawei <jiawei.cui@artinchip.com>
|
||||
*/
|
||||
|
||||
#include <finsh.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <rtthread.h>
|
||||
#include <rtdevice.h>
|
||||
#include <rtdef.h>
|
||||
|
||||
#define PARTITION_NAME "blk_data" /* Target block device name */
|
||||
#define TEST_READ_WRITE_SIZE \
|
||||
0x2000 /* Data length must be block-aligned since we're operating on block devices */
|
||||
#define TEST_START_BLK_ID 0 /* Starting block number */
|
||||
|
||||
static rt_device_t s_flash_t = NULL;
|
||||
static struct rt_device_blk_geometry s_flash_geometry;
|
||||
static rt_uint8_t *s_read_buf = NULL;
|
||||
static rt_uint8_t *s_write_buf = NULL;
|
||||
|
||||
static void address_map(rt_uint8_t *data, rt_uint32_t start_address, rt_uint32_t len)
|
||||
{
|
||||
rt_uint32_t i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i && (i % 16) == 0)
|
||||
printf("\n");
|
||||
if ((i % 16) == 0) {
|
||||
printf("0x%08x : ", start_address);
|
||||
start_address += 16;
|
||||
}
|
||||
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static rt_err_t verify_data(rt_uint8_t *data_exp, rt_uint8_t *data_act, rt_uint32_t offset,
|
||||
rt_uint32_t len)
|
||||
{
|
||||
if (memcmp(data_exp, data_act, len) != 0) {
|
||||
printf("expected:-----------------------\n");
|
||||
address_map(data_exp, offset, len);
|
||||
printf("actual:-------------------------\n");
|
||||
address_map(data_act, offset, len);
|
||||
return -RT_ERROR;
|
||||
} else {
|
||||
if (len > 0x200) {
|
||||
address_map(data_act, offset, 0x100);
|
||||
printf("......\n");
|
||||
address_map(data_act + len - 0x50, offset + len - 0x50, 0x50);
|
||||
} else {
|
||||
address_map(data_act, offset, len);
|
||||
}
|
||||
return RT_EOK;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the flash storage device with specified partition name.
|
||||
* Searches for the device by name, opens it if not already opened,
|
||||
* and retrieves its geometric parameters (block size, capacity etc.).
|
||||
*
|
||||
* @param part_name Partition name to locate the target flash device
|
||||
* @return Error code from RT-Thread subsystem operations
|
||||
*/
|
||||
static rt_err_t flash_device_init(const char *part_name)
|
||||
{
|
||||
rt_err_t res = RT_ERROR;
|
||||
|
||||
s_flash_t = rt_device_find(part_name);
|
||||
if (!s_flash_t) {
|
||||
rt_kprintf("find %s failed!\n", part_name);
|
||||
return -res;
|
||||
}
|
||||
|
||||
if (!s_flash_t->ref_count) {
|
||||
res = rt_device_open(s_flash_t, RT_DEVICE_OFLAG_RDWR);
|
||||
if (res != RT_EOK) {
|
||||
rt_kprintf("open flash device failed!\n");
|
||||
return -res;
|
||||
}
|
||||
}
|
||||
|
||||
return -rt_device_control(s_flash_t, RT_DEVICE_CTRL_BLK_GETGEOME, &s_flash_geometry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read data from flash memory starting at specified block ID.
|
||||
* Requires buffer length to be aligned with flash block size.
|
||||
*
|
||||
* @param block_start_id Starting physical block index for read operation
|
||||
* @param buffer Data storage pointer for received bytes
|
||||
* @param buffer_len Total byte count to read (must be aligned with block size)
|
||||
* @return Error code from RT-Thread subsystem operations
|
||||
*/
|
||||
rt_err_t flash_device_read(rt_uint32_t block_start_id, rt_uint8_t *buffer, rt_uint32_t buffer_len)
|
||||
{
|
||||
rt_uint32_t ret = 0;
|
||||
|
||||
if (buffer_len % s_flash_geometry.block_size != 0) {
|
||||
rt_kprintf("Data length must be aligned to block size (0x%x)\n",
|
||||
s_flash_geometry.block_size);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* On success, it returns the number of blocks operated; on failure, it returns 0 */
|
||||
ret =
|
||||
rt_device_read(s_flash_t, block_start_id, buffer, buffer_len / s_flash_geometry.block_size);
|
||||
|
||||
if (ret == 0)
|
||||
return -RT_ERROR;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write data to flash memory starting at specified block ID.
|
||||
* Requires buffer length to be aligned with flash block size.
|
||||
*
|
||||
* @param block_start_id Starting physical block index for write operation
|
||||
* @param buffer Source data pointer containing bytes to program
|
||||
* @param buffer_len Total byte count to write (must be aligned with block size)
|
||||
* @return Error code from RT-Thread subsystem operations
|
||||
*/
|
||||
rt_err_t flash_device_write(rt_uint32_t block_start_id, rt_uint8_t *buffer, rt_uint32_t buffer_len)
|
||||
{
|
||||
rt_uint32_t ret = 0;
|
||||
|
||||
if (buffer_len % s_flash_geometry.block_size != 0) {
|
||||
rt_kprintf("Data length must be aligned to block size (0x%x)\n",
|
||||
s_flash_geometry.block_size);
|
||||
return -RT_ERROR;
|
||||
}
|
||||
|
||||
/* On success, it returns the number of blocks operated; on failure, it returns 0 */
|
||||
ret = rt_device_write(s_flash_t, block_start_id, buffer,
|
||||
buffer_len / s_flash_geometry.block_size);
|
||||
if (ret == 0)
|
||||
return -RT_ERROR;
|
||||
return RT_EOK;
|
||||
}
|
||||
|
||||
static int cmd_spiflash_usage_example(int argc, char *argv[])
|
||||
{
|
||||
rt_err_t ret = RT_EOK;
|
||||
|
||||
/* Allocate test data buffer */
|
||||
s_write_buf = rt_malloc_align(TEST_READ_WRITE_SIZE, CACHE_LINE_SIZE);
|
||||
if (!s_write_buf) {
|
||||
printf("Error: No memory for test data!\n");
|
||||
ret = RT_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Allocate read buffer */
|
||||
s_read_buf = rt_malloc_align(TEST_READ_WRITE_SIZE, CACHE_LINE_SIZE);
|
||||
if (!s_read_buf) {
|
||||
printf("Error: No memory for read buffer!\n");
|
||||
rt_free_align(s_write_buf);
|
||||
ret = RT_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* prepare some data */
|
||||
for (int i = 0; i < TEST_READ_WRITE_SIZE; i++) {
|
||||
s_write_buf[i] = i % 256;
|
||||
}
|
||||
|
||||
ret = flash_device_init(PARTITION_NAME);
|
||||
if (ret != RT_EOK) {
|
||||
rt_kprintf("flash device init failed!\n");
|
||||
goto exit;
|
||||
} else {
|
||||
rt_kprintf("flash device init successful!\n");
|
||||
}
|
||||
|
||||
/* Can be written to directly without first erasing. */
|
||||
ret = flash_device_write(TEST_START_BLK_ID, s_write_buf, TEST_READ_WRITE_SIZE);
|
||||
if (ret != RT_EOK) {
|
||||
rt_kprintf("data write failed!\n");
|
||||
goto exit;
|
||||
} else {
|
||||
rt_kprintf("data write successful!\n");
|
||||
}
|
||||
|
||||
ret = flash_device_read(TEST_START_BLK_ID, s_read_buf, TEST_READ_WRITE_SIZE);
|
||||
if (ret != RT_EOK) {
|
||||
rt_kprintf("data read failed!\n");
|
||||
goto exit;
|
||||
} else {
|
||||
rt_kprintf("data read successful!\n");
|
||||
}
|
||||
|
||||
ret = verify_data(s_write_buf, s_read_buf, TEST_START_BLK_ID, TEST_READ_WRITE_SIZE);
|
||||
if (ret != RT_EOK)
|
||||
goto exit;
|
||||
|
||||
exit:
|
||||
printf("flash device usage end.\n");
|
||||
|
||||
if (s_flash_t)
|
||||
rt_device_close(s_flash_t);
|
||||
if (s_write_buf)
|
||||
rt_free_align(s_write_buf);
|
||||
if (s_read_buf)
|
||||
rt_free_align(s_read_buf);
|
||||
return -ret;
|
||||
}
|
||||
|
||||
MSH_CMD_EXPORT_ALIAS(cmd_spiflash_usage_example, flash_device_usage, Flash block device example);
|
||||
Reference in New Issue
Block a user