Files
arcs/arcs-sdk/drivers/lisa_camera/bus/lisa_camera_bus_spi.c
2026-08-13 16:50:52 +08:00

340 lines
11 KiB
C

#include <stdint.h>
#include <string.h>
#include "lisa_device.h"
#include "lisa_spi.h"
#include <lisa_semaphore.h>
#include "Driver_SPI.h"
#include "Driver_GPIO.h"
#include "lisa_gpio.h"
#include "lisa_camera_bus.h"
#include "dma.h"
#define TAG "camera_bus_spi"
#include "lisa_log.h"
/* SPI 驱动上下文结构体 */
typedef struct {
void *spi_dev; /* SPI 设备句柄 */
lisa_device_t *cs_gpio_dev; /* CS GPIO lisa 设备 */
uint8_t cs_pin; /* CS 引脚 */
uint8_t dma_channel; /* DMA 通道 */
lisa_semaphore_t *stop_sem; /* 停止信号量 */
lisa_camera_frame_callback_t callback; /* 帧完成回调 */
lisa_camera_get_free_fb_t get_free_fb; /* 获取空闲帧回调 */
lisa_camera_get_free_fb_from_isr_t get_free_fb_isr; /* 获取空闲帧回调(ISR) */
void *user_data; /* 用户数据 */
lisa_camera_fb_t *current_fb; /* 当前帧缓冲区 */
volatile uint8_t stop_flag; /* 停止标志 */
} caemra_bus_spi_priv_t;
static caemra_bus_spi_priv_t camera_bus_spi_priv = {
.spi_dev = NULL,
.cs_gpio_dev = NULL,
.cs_pin = 0,
.dma_channel = 0,
.stop_sem = NULL,
.callback = NULL,
.get_free_fb = NULL,
.get_free_fb_isr = NULL,
.user_data = NULL,
.current_fb = NULL,
.stop_flag = 1,
};
static void _spi_drv_event(uint32_t event, uint32_t usr_param)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)usr_param;
lisa_camera_fb_t *completed_fb = NULL;
lisa_camera_fb_t *next_fb = NULL;
if (event != CSK_SPI_EVENT_TRANSFER_COMPLETE) {
LOGW("spi event: %x", (unsigned int)event);
}
if ((event & CSK_SPI_EVENT_TRANSFER_COMPLETE) && !priv->cs_gpio_dev) {
/* 有 CS 模式: SPI 传输完成即表示一帧结束 */
if (priv->stop_flag) {
lisa_semaphore_give(priv->stop_sem);
return;
}
completed_fb = priv->current_fb;
/* 通过回调获取下一个空闲帧缓冲区并继续接收 */
if (priv->get_free_fb_isr) {
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区继续接收 */
priv->current_fb = next_fb;
SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
} else {
/* 没有空闲缓冲区,复用当前缓冲区继续接收,丢弃本帧数据 */
LOGW("No free frame buffer available, reuse current buffer and drop frame");
SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
/* 通知上层帧已完成 (仅当成功获取到新缓冲区时才交付数据) */
if (priv->callback && completed_fb) {
// completed_fb->len = SPI_GetDataCount(priv->spi_dev);
priv->callback(completed_fb, priv->user_data);
}
}
}
/* NOCS 模式: CS 引脚控制回调 */
_FAST_TEXT static void _spi_cs_set_cb(void *spi_dev, uint8_t level)
{
SPI_Pull_CS(spi_dev, level);
}
/* NOCS 模式: GPIO 中断处理函数,检测 CS 上升沿表示帧结束 */
_FAST_TEXT static void _gpio_drv_event(uint32_t pin, void *user_data)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)user_data;
lisa_camera_fb_t *completed_fb = NULL;
lisa_camera_fb_t *next_fb = NULL;
if (priv->stop_flag) {
lisa_gpio_disable_irq(priv->cs_gpio_dev, priv->cs_pin);
lisa_semaphore_give(priv->stop_sem);
return;
}
/* 获取实际接收的数据长度 */
if (priv->current_fb) {
// priv->current_fb->len = SPI_GetDataCount(priv->spi_dev);
}
SPI_Control(priv->spi_dev, CSK_SPI_ABORT_TRANSFER, 0);
completed_fb = priv->current_fb;
/* 通过回调获取下一个空闲帧缓冲区 */
if (priv->get_free_fb_isr) {
int ret;
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区继续接收 */
priv->current_fb = next_fb;
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
}
} else {
/* 没有空闲缓冲区,复用当前缓冲区继续接收,丢弃本帧数据 */
LOGW("No free frame buffer available, reuse current buffer and drop frame");
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
}
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
/* 通知上层帧已完成 (仅当成功获取到新缓冲区时才交付数据) */
if (priv->callback && completed_fb) {
priv->callback(completed_fb, priv->user_data);
}
}
static int lisa_camera_bus_spi_init(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config)
{
int ret = 0;
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
const lisa_camera_bus_spi_config_t *spi_config = &bus_config->config.spi;
if (bus_config->bus_type != LISA_CAMERA_BUS_SPI) {
LOGE("Invalid bus type %d", bus_config->bus_type);
return LISA_DEVICE_ERR_INVALID;
}
if (strcmp(spi_config->spi_dev->name, "spi0") == 0) {
priv->spi_dev = SPI0();
}
else if (strcmp(spi_config->spi_dev->name, "spi1") == 0) {
priv->spi_dev = SPI1();
}
else if (strcmp(spi_config->spi_dev->name, "spi2") == 0) {
priv->spi_dev = SPI2();
}
priv->dma_channel = bus_config->dma_channel;
priv->stop_sem = lisa_semaphore_create(1);
SPI_Uninitialize(priv->spi_dev);
if (bus_config->config.spi.cs_gpio) {
ret = SPI_Initialize_NCS(priv->spi_dev, _spi_drv_event, priv, _spi_cs_set_cb);
}
else {
ret = SPI_Initialize(priv->spi_dev, _spi_drv_event, priv);
}
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Initialize failed %d", ret);
return ret;
}
ret = SPI_PowerControl(priv->spi_dev, CSK_POWER_FULL);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_PowerControl failed %d", ret);
return ret;
}
uint32_t control = CSK_SPI_MODE_SLAVE | CSK_SPI_DATA_BITS(8);
if (spi_config->spi_bit_order == 0) {
control |= CSK_SPI_MSB_LSB; /* MSB first */
}
else {
control |= CSK_SPI_LSB_MSB; /* LSB first */
}
switch (spi_config->spi_mode) {
case 0:
control |= CSK_SPI_CPOL0_CPHA0;
break;
case 1:
control |= CSK_SPI_CPOL0_CPHA1;
break;
case 2:
control |= CSK_SPI_CPOL1_CPHA0;
break;
case 3:
control |= CSK_SPI_CPOL1_CPHA1;
break;
default:
LOGE("Invalid SPI mode %d", spi_config->spi_mode);
return LISA_DEVICE_ERR_INVALID;
}
ret = SPI_Control(priv->spi_dev, control, spi_config->spi_freq);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Control failed %d", ret);
return ret;
}
SPI_ADV_ATTR attr = {0};
attr.flags = SPI_ATTR_RX_NSYNCA | SPI_ATTR_RX_DMACH_PRIO | SPI_ATTR_RX_DMACH_RSVD;
attr.rx_nsynca = 1;
attr.rx_dmach_prio = 6;
attr.rx_dmach_rsvd = priv->dma_channel;
ret = SPI_Control(priv->spi_dev, CSK_SPI_SET_ADV_ATTR, (uint32_t)&attr);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Control ADV_ATTR failed %d", ret);
return ret;
}
dma_channel_unreserve(attr.rx_dmach_rsvd);
if (spi_config->cs_gpio) {
SPI_Enable_Pull_CS(priv->spi_dev, spi_config->cs_pin);
priv->cs_gpio_dev = spi_config->cs_gpio;
priv->cs_pin = spi_config->cs_pin;
lisa_gpio_configure(spi_config->cs_gpio, spi_config->cs_pin, LISA_GPIO_CONFIG_INPUT_PULLUP);
lisa_gpio_configure_irq(spi_config->cs_gpio, spi_config->cs_pin, LISA_GPIO_IRQ_EDGE_RISING,
_gpio_drv_event, priv);
}
LOGI("SPI camera bus init success (NOCS=%d)", spi_config->cs_pin);
return ret;
}
static int lisa_camera_bus_spi_start_capture(lisa_device_t *dev, lisa_camera_frame_callback_t callback,
lisa_camera_get_free_fb_t get_free_fb,
lisa_camera_get_free_fb_from_isr_t get_free_fb_from_isr,
void *data)
{
int ret = 0;
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
if (!priv->stop_flag) {
LOGW("camera spi already start.");
return 0;
}
/* 保存回调函数 */
priv->callback = callback;
priv->get_free_fb = get_free_fb;
priv->get_free_fb_isr = get_free_fb_from_isr;
priv->user_data = data;
/* 通过回调获取第一个空闲帧缓冲区 */
priv->current_fb = get_free_fb ? get_free_fb(data) : NULL;
if (!priv->current_fb) {
LOGE("No free frame buffer available");
return -1;
}
priv->stop_flag = 0;
SPI_ADV_ATTR attr = {
.flags = SPI_ATTR_RX_DMACH_RSVD,
.rx_dmach_rsvd = priv->dma_channel,
};
SPI_Control(priv->spi_dev, CSK_SPI_SET_ADV_ATTR, (uint32_t)&attr);
if (priv->cs_gpio_dev) {
/* NOCS 模式: 使用 SPI_Receive_NEnd 并启用 GPIO 中断 */
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
priv->stop_flag = 1;
return ret;
}
if (priv->cs_gpio_dev) {
lisa_gpio_enable_irq(priv->cs_gpio_dev, priv->cs_pin);
}
}
else {
/* 有 CS 模式: 使用 SPI_Receive */
ret = SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive failed %d", ret);
priv->stop_flag = 1;
}
}
return ret;
}
static int lisa_camera_bus_spi_stop_capture(lisa_device_t *dev)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
if (priv->stop_flag) {
LOGW("camera spi already stop.");
return 0;
}
priv->stop_flag = 1;
if (priv->cs_gpio_dev) {
/* NOCS 模式: 等待 GPIO 中断处理完成 */
lisa_semaphore_take(priv->stop_sem, LISA_WAIT_FOREVER);
}
SPI_Control(priv->spi_dev, CSK_SPI_ABORT_TRANSFER, 0);
if (priv->current_fb != NULL) {
lisa_camera_release_fb(dev, priv->current_fb);
priv->current_fb = NULL;
}
dma_channel_unreserve(priv->dma_channel);
return 0;
}
const lisa_camera_bus_if_t lisa_camera_bus_spi_if = {
.init = lisa_camera_bus_spi_init,
.start_capture = lisa_camera_bus_spi_start_capture,
.stop_capture = lisa_camera_bus_spi_stop_capture,
};
static int camera_bus_spi_init(void)
{
return 0;
}
LISA_DEVICE_REGISTER(camera_bus, &lisa_camera_bus_spi_if, &camera_bus_spi_priv, NULL, camera_bus_spi_init, LISA_DEVICE_PRIORITY_HIGH);