Skip to content

Commit 46bf294

Browse files
committed
video: stm32_dcmi: Snapshot mode
Add the ability to run cameras in a logical snapshot mode instead of always running in video mode. In particular, the camera starts when you ask for an image to be dequeued and stops when it receives an image. There are three ways to setup to run in this mode: a) define a max of 1 buffer: CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=1 b) Tell it to do so in the overlay by giving the DCMI object the property: snapshot-mode c) At run time: I added a video control: VIDEO_CID_SNAPSHOT_MODE Which can have the values 0 - normal, 1 - snapshot As mentioned in snapshot mode, the HAL DMA is stopped after each image and restarted when the user asks for another image. I added a timeout default to 1 second, that handles the case where sometimes the HAL is silently not successful in retrieving an image. Signed-off-by: Kurt Eckhardt <[email protected]>
1 parent bbfd206 commit 46bf294

File tree

4 files changed

+179
-15
lines changed

4 files changed

+179
-15
lines changed

drivers/video/video_ctrls.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ static inline const char *video_get_ctrl_name(uint32_t id)
488488
return "Brightness, Automatic";
489489
case VIDEO_CID_BAND_STOP_FILTER:
490490
return "Band-Stop Filter";
491+
case VIDEO_CID_SNAPSHOT_MODE:
492+
return "Snapshot Mode";
491493
case VIDEO_CID_ALPHA_COMPONENT:
492494
return "Alpha Component";
493495

drivers/video/video_stm32_dcmi.c

Lines changed: 162 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <zephyr/irq.h>
1313
#include <zephyr/logging/log.h>
1414
#include <zephyr/drivers/video.h>
15+
#include <zephyr/drivers/video-controls.h>
1516
#include <zephyr/drivers/pinctrl.h>
1617
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
1718
#include <zephyr/drivers/clock_control.h>
@@ -20,12 +21,13 @@
2021

2122
#include <stm32_ll_dma.h>
2223

24+
#include "video_ctrls.h"
2325
#include "video_device.h"
2426

2527
LOG_MODULE_REGISTER(video_stm32_dcmi, CONFIG_VIDEO_LOG_LEVEL);
2628

27-
#if CONFIG_VIDEO_BUFFER_POOL_NUM_MAX < 2
28-
#error "The minimum required number of buffers for video_stm32 is 2"
29+
#if CONFIG_VIDEO_BUFFER_POOL_NUM_MAX < 1
30+
#error "The minimum required number of buffers for video_stm32 is 1"
2931
#endif
3032

3133
typedef void (*irq_config_func_t)(const struct device *dev);
@@ -37,14 +39,22 @@ struct stream {
3739
struct dma_config cfg;
3840
};
3941

42+
43+
struct video_stm32_dcmi_ctrls {
44+
struct video_ctrl snapshot;
45+
};
46+
4047
struct video_stm32_dcmi_data {
4148
const struct device *dev;
4249
DCMI_HandleTypeDef hdcmi;
4350
struct video_format fmt;
51+
struct video_stm32_dcmi_ctrls ctrls;
4452
int capture_rate;
4553
struct k_fifo fifo_in;
4654
struct k_fifo fifo_out;
4755
struct video_buffer *vbuf;
56+
uint32_t snapshot_start_time;
57+
bool snapshot_mode;
4858
};
4959

5060
struct video_stm32_dcmi_config {
@@ -53,6 +63,8 @@ struct video_stm32_dcmi_config {
5363
const struct pinctrl_dev_config *pctrl;
5464
const struct device *sensor_dev;
5565
const struct stream dma;
66+
bool snapshot_mode;
67+
int snapshot_timeout;
5668
};
5769

5870
static void stm32_dcmi_process_dma_error(DCMI_HandleTypeDef *hdcmi)
@@ -69,7 +81,7 @@ static void stm32_dcmi_process_dma_error(DCMI_HandleTypeDef *hdcmi)
6981
}
7082

7183
if (HAL_DCMI_Start_DMA(&dev_data->hdcmi,
72-
DCMI_MODE_CONTINUOUS,
84+
dev_data->snapshot_mode ? DCMI_MODE_SNAPSHOT : DCMI_MODE_CONTINUOUS,
7385
(uint32_t)dev_data->vbuf->buffer,
7486
dev_data->vbuf->size / 4) != HAL_OK) {
7587
LOG_WRN("Continuous: HAL_DCMI_Start_DMA FAILED!");
@@ -89,6 +101,17 @@ void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
89101
struct video_buffer *vbuf;
90102
HAL_StatusTypeDef __maybe_unused hal_ret;
91103

104+
if (dev_data->snapshot_mode) {
105+
/* we remove the buffer from the camera and add it to fifo_out */
106+
vbuf = dev_data->vbuf;
107+
dev_data->vbuf = NULL;
108+
vbuf->timestamp = k_uptime_get_32();
109+
k_fifo_put(&dev_data->fifo_out, vbuf);
110+
LOG_DBG("event SH put: %p", vbuf);
111+
return;
112+
}
113+
114+
/* Not in snapshot_mode */
92115
hal_ret = HAL_DCMI_Suspend(hdcmi);
93116
__ASSERT_NO_MSG(hal_ret == HAL_OK);
94117

@@ -276,22 +299,30 @@ static int video_stm32_dcmi_set_stream(const struct device *dev, bool enable,
276299
return 0;
277300
}
278301

279-
data->vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT);
302+
if (!data->snapshot_mode && (CONFIG_VIDEO_BUFFER_POOL_NUM_MAX != 1)) {
303+
data->vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT);
280304

281-
if (data->vbuf == NULL) {
282-
LOG_ERR("Failed to dequeue a DCMI buffer.");
283-
return -ENOMEM;
305+
if (data->vbuf == NULL) {
306+
data->snapshot_mode = true;
307+
LOG_WRN("No buffers, assume snapshot mode.");
308+
}
284309
}
285310

286311
/* Set the frame control */
287312
data->hdcmi.Instance->CR &= ~(DCMI_CR_FCRC_0 | DCMI_CR_FCRC_1);
288313
data->hdcmi.Instance->CR |= STM32_DCMI_GET_CAPTURE_RATE(data->capture_rate);
289314

290-
hal_ret = HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_CONTINUOUS,
291-
(uint32_t)data->vbuf->buffer, data->vbuf->bytesused / 4);
292-
if (hal_ret != HAL_OK) {
293-
LOG_ERR("Failed to start DCMI DMA");
294-
return -EIO;
315+
/* don't start the DCMI DMA if we are in Snapshot mode */
316+
if (!data->snapshot_mode) {
317+
hal_ret = HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_CONTINUOUS,
318+
(uint32_t)data->vbuf->buffer,
319+
data->vbuf->bytesused / 4);
320+
if (hal_ret != HAL_OK) {
321+
LOG_ERR("Failed to start DCMI DMA");
322+
return -EIO;
323+
}
324+
} else {
325+
LOG_DBG("Snapshot mode active");
295326
}
296327

297328
return video_stream_start(config->sensor_dev, type);
@@ -314,19 +345,88 @@ static int video_stm32_dcmi_enqueue(const struct device *dev, struct video_buffe
314345
return 0;
315346
}
316347

317-
static int video_stm32_dcmi_dequeue(const struct device *dev, struct video_buffer **vbuf,
318-
k_timeout_t timeout)
348+
static int video_stm32_dcmi_snapshot(const struct device *dev, struct video_buffer **vbuf,
349+
k_timeout_t timeout)
319350
{
320351
struct video_stm32_dcmi_data *data = dev->data;
352+
const struct video_stm32_dcmi_config *config = dev->config;
353+
354+
LOG_DBG("dequeue snapshot: %p %llu\n", data->vbuf, timeout.ticks);
355+
356+
/* See if we were already called and have an active buffer */
357+
if (data->vbuf == NULL) {
358+
/* check first to see if we already have a buffer returned */
359+
*vbuf = k_fifo_get(&data->fifo_out, K_NO_WAIT);
360+
if (*vbuf != NULL) {
361+
LOG_DBG("k_fifo_get returned: %p\n", *vbuf);
362+
if (HAL_DCMI_Stop(&data->hdcmi) != HAL_OK) {
363+
LOG_WRN("Snapshot: HAL_DCMI_Stop FAILED!");
364+
}
365+
return 0;
366+
}
367+
data->vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT);
368+
LOG_DBG("\tcamera buf: %p\n", data->vbuf);
369+
if (data->vbuf == NULL) {
370+
LOG_WRN("Snapshot: No Buffers available!");
371+
return -ENOMEM;
372+
}
373+
374+
if (HAL_DCMI_Start_DMA(&data->hdcmi, DCMI_MODE_SNAPSHOT,
375+
(uint32_t)data->vbuf->buffer,
376+
data->vbuf->size / 4) != HAL_OK) {
377+
LOG_WRN("Snapshot: HAL_DCMI_Start_DMA FAILED!");
378+
return -EIO;
379+
}
380+
381+
/* remember when we started this request */
382+
data->snapshot_start_time = k_uptime_get_32();
383+
}
321384

322385
*vbuf = k_fifo_get(&data->fifo_out, timeout);
323386
if (*vbuf == NULL) {
387+
uint32_t time_since_start_time =
388+
(uint32_t)(k_uptime_get_32() - data->snapshot_start_time);
389+
if (time_since_start_time > config->snapshot_timeout) {
390+
LOG_WRN("Snapshot: Timed out!");
391+
if (HAL_DCMI_Stop(&data->hdcmi) != HAL_OK) {
392+
LOG_WRN("Snapshot: HAL_DCMI_Stop FAILED!");
393+
}
394+
/* verify it did not come in during this procuessing */
395+
*vbuf = k_fifo_get(&data->fifo_out, K_NO_WAIT);
396+
if (*vbuf) {
397+
return 0;
398+
}
399+
400+
if (data->vbuf != NULL) {
401+
k_fifo_put(&data->fifo_in, data->vbuf);
402+
data->vbuf = NULL;
403+
}
404+
}
405+
324406
return -EAGAIN;
325407
}
326408

409+
if (HAL_DCMI_Stop(&data->hdcmi) != HAL_OK) {
410+
LOG_WRN("Snapshot: HAL_DCMI_Stop FAILED!");
411+
}
327412
return 0;
328413
}
329414

415+
static int video_stm32_dcmi_dequeue(const struct device *dev, struct video_buffer **vbuf,
416+
k_timeout_t timeout)
417+
{
418+
struct video_stm32_dcmi_data *data = dev->data;
419+
420+
if (data->snapshot_mode) {
421+
return video_stm32_dcmi_snapshot(dev, vbuf, timeout);
422+
}
423+
424+
*vbuf = k_fifo_get(&data->fifo_out, timeout);
425+
LOG_DBG("k_fifo_get returned: %p\n", *vbuf);
426+
427+
return (*vbuf == NULL) ? -EAGAIN : 0;
428+
}
429+
330430
static int video_stm32_dcmi_get_caps(const struct device *dev, struct video_caps *caps)
331431
{
332432
const struct video_stm32_dcmi_config *config = dev->config;
@@ -338,6 +438,32 @@ static int video_stm32_dcmi_get_caps(const struct device *dev, struct video_caps
338438
return video_get_caps(config->sensor_dev, caps);
339439
}
340440

441+
static int video_stm32_dcmi_set_ctrl_snapshot(const struct device *dev, int value)
442+
{
443+
struct video_stm32_dcmi_data *data = dev->data;
444+
445+
LOG_INF("Set Snapshot: %d\n", value);
446+
if ((CONFIG_VIDEO_BUFFER_POOL_NUM_MAX == 1) && (value == 0)) {
447+
LOG_DBG("Only one buffer so snapshot mode only");
448+
return -EINVAL;
449+
}
450+
451+
data->snapshot_mode = value;
452+
return 0;
453+
}
454+
455+
static int video_stm32_dcmi_set_ctrl(const struct device *dev, uint32_t id)
456+
{
457+
struct video_stm32_dcmi_data *data = dev->data;
458+
struct video_stm32_dcmi_ctrls *ctrls = &data->ctrls;
459+
460+
if (id == VIDEO_CID_SNAPSHOT_MODE) {
461+
return video_stm32_dcmi_set_ctrl_snapshot(dev, ctrls->snapshot.val);
462+
}
463+
464+
return -ENOTSUP;
465+
}
466+
341467
static int video_stm32_dcmi_enum_frmival(const struct device *dev, struct video_frmival_enum *fie)
342468
{
343469
const struct video_stm32_dcmi_config *config = dev->config;
@@ -455,13 +581,25 @@ static DEVICE_API(video, video_stm32_dcmi_driver_api) = {
455581
.enqueue = video_stm32_dcmi_enqueue,
456582
.dequeue = video_stm32_dcmi_dequeue,
457583
.get_caps = video_stm32_dcmi_get_caps,
584+
.set_ctrl = video_stm32_dcmi_set_ctrl,
458585
.enum_frmival = video_stm32_dcmi_enum_frmival,
459586
.set_frmival = video_stm32_dcmi_set_frmival,
460587
.get_frmival = video_stm32_dcmi_get_frmival,
461588
.set_selection = video_stm32_dcmi_set_selection,
462589
.get_selection = video_stm32_dcmi_get_selection,
463590
};
464591

592+
static int video_stm32_dcmi_init_controls(const struct device *dev)
593+
{
594+
struct video_stm32_dcmi_data *data = dev->data;
595+
struct video_stm32_dcmi_ctrls *ctrls = &data->ctrls;
596+
597+
return video_init_ctrl(&ctrls->snapshot, dev, VIDEO_CID_SNAPSHOT_MODE,
598+
(struct video_ctrl_range){.min = 0, .max = 1, .step = 1,
599+
.def = data->snapshot_mode ? 1 : 0});
600+
}
601+
602+
465603
static void video_stm32_dcmi_irq_config_func(const struct device *dev)
466604
{
467605
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
@@ -542,6 +680,8 @@ static const struct video_stm32_dcmi_config video_stm32_dcmi_config_0 = {
542680
.irq_config = video_stm32_dcmi_irq_config_func,
543681
.pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
544682
.sensor_dev = SOURCE_DEV(0),
683+
.snapshot_mode = DT_INST_PROP(0, snapshot_mode),
684+
.snapshot_timeout = DT_INST_PROP(0, snapshot_timeout),
545685
DCMI_DMA_CHANNEL(0, PERIPHERAL, MEMORY)
546686
};
547687

@@ -572,6 +712,12 @@ static int video_stm32_dcmi_init(const struct device *dev)
572712
return err;
573713
}
574714

715+
/* See if we should initialize to only support snapshot mode or not */
716+
data->snapshot_mode = config->snapshot_mode;
717+
if (CONFIG_VIDEO_BUFFER_POOL_NUM_MAX == 1) {
718+
LOG_DBG("Only one buffer so snapshot mode only");
719+
data->snapshot_mode = true;
720+
}
575721
data->dev = dev;
576722
k_fifo_init(&data->fifo_in);
577723
k_fifo_init(&data->fifo_out);
@@ -589,7 +735,8 @@ static int video_stm32_dcmi_init(const struct device *dev)
589735
k_sleep(K_MSEC(100));
590736
LOG_DBG("%s inited", dev->name);
591737

592-
return 0;
738+
/* Initialize controls */
739+
return video_stm32_dcmi_init_controls(dev);
593740
}
594741

595742
DEVICE_DT_INST_DEFINE(0, &video_stm32_dcmi_init,

dts/bindings/video/st,stm32-dcmi.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,15 @@ properties:
4646
STM32_DMA_MEM_INC | STM32_DMA_PERIPH_8BITS | STM32_DMA_MEM_32BITS |
4747
STM32_DMA_PRIORITY_HIGH) STM32_DMA_FIFO_1_4>;
4848
49+
snapshot-mode:
50+
type: boolean
51+
description: Set DCMI to Snapshot mode, instead of the default Capture mode.
52+
53+
snapshot-timeout:
54+
type: int
55+
description: Set capture timeout in microseconds, default is 1000.
56+
default: 1000
57+
4958
child-binding:
5059
child-binding:
5160
include: video-interfaces.yaml

include/zephyr/drivers/video-controls.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ enum video_colorfx {
152152
*/
153153
#define VIDEO_CID_BAND_STOP_FILTER (VIDEO_CID_BASE + 33)
154154

155+
/** Switch the camera between video mode and snapshot mode.
156+
* Defaults to video mode unless max buffer count is set to 1
157+
*/
158+
159+
#define VIDEO_CID_SNAPSHOT_MODE (VIDEO_CID_BASE + 34)
160+
155161
/** Sets the alpha color component.
156162
* Some devices produce data with a user-controllable alpha component. Set the value applied to
157163
* the alpha channel of every pixel produced.

0 commit comments

Comments
 (0)