1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2023-03-24     spaceman          the first version
9  */
10 
11 #include "board.h"
12 
13 #ifdef BSP_USING_OV2640
14 
15 #include <dfs_file.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <sys/stat.h>
19 #include <sys/statfs.h>
20 #include "drv_dcmi.h"
21 #include "drv_ov2640.h"
22 #include "drv_ov2640_cfg.h"
23 
24 #define DRV_DEBUG
25 //#define CAMERA_DUMP
26 #define LOG_TAG     "drv.ov2640"
27 #include <drv_log.h>
28 
29 #define CHIP_ADDRESS    0x30 /* OV2640 address */
30 // #define CHIP_ADDRESS    0x3C /* OV5640 address */
31 #define I2C_NAME        "i2c1"
32 #define PWDN_PIN    GET_PIN(D, 14)
33 
34 struct rt_i2c_bus_device *i2c_bus  = RT_NULL;
35 
36 #if defined(CAMERA_DUMP)
37 #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
dump_hex(const rt_uint8_t * ptr,rt_size_t buflen)38 static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
39 {
40     unsigned char *buf = (unsigned char *)ptr;
41     int i, j;
42 
43     for (i = 0; i < buflen; i += 16)
44     {
45         rt_kprintf("%08x:", i);
46 
47         for (j = 0; j < 16; j++)
48         {
49             if (i + j < buflen)
50             {
51                 rt_kprintf("%02x", buf[i + j]);
52             }
53             else
54             {
55                 rt_kprintf(" ");
56             }
57         }
58         rt_kprintf(" ");
59 
60         for (j = 0; j < 16; j++)
61         {
62             if (i + j < buflen)
63             {
64                 rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
65             }
66         }
67         rt_kprintf("\n");
68     }
69 }
70 #endif
71 
72 /* i2c read reg */
read_reg(struct rt_i2c_bus_device * bus,rt_uint8_t reg,rt_uint8_t len,rt_uint8_t * buf)73 static rt_err_t read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf)
74 {
75     struct rt_i2c_msg msg[2] = {0, 0};
76 
77     RT_ASSERT(bus != RT_NULL);
78 
79     msg[0].addr  = CHIP_ADDRESS;
80     msg[0].flags = RT_I2C_WR;
81     msg[0].buf   = &reg;
82     msg[0].len   = 1;
83 
84     msg[1].addr  = CHIP_ADDRESS;
85     msg[1].flags = RT_I2C_RD;
86     msg[1].len   = len;
87     msg[1].buf   = buf;
88 
89     if (rt_i2c_transfer(bus, msg, 2) == 2)
90     {
91         return RT_EOK;
92     }
93 
94     return -RT_ERROR;
95 }
96 
97 /* i2c write reg */
write_reg(struct rt_i2c_bus_device * bus,rt_uint8_t reg,rt_uint8_t data)98 static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t data)
99 {
100     rt_uint8_t buf[2];
101     struct rt_i2c_msg msgs;
102 
103     RT_ASSERT(bus != RT_NULL);
104 
105     buf[0] = reg;
106     buf[1] = data;
107 
108     msgs.addr = CHIP_ADDRESS;
109     msgs.flags = RT_I2C_WR;
110     msgs.buf = buf;
111     msgs.len = 2;
112 
113     if (rt_i2c_transfer(bus, &msgs, 1) == 1)
114     {
115         return RT_EOK;
116     }
117 
118     return -RT_ERROR;
119 }
120 
ov2640_read_id(struct rt_i2c_bus_device * bus,rt_uint16_t * id)121 static rt_err_t ov2640_read_id(struct rt_i2c_bus_device *bus, rt_uint16_t *id)
122 {
123     rt_uint8_t read_value[2];
124 
125     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 SENSOR 寄存器组
126 
127     read_reg(bus, OV2640_SENSOR_PIDH, 1, &read_value[0]); // 读取ID高字节
128     read_reg(bus, OV2640_SENSOR_PIDL, 1, &read_value[1]); // 读取ID低字节
129 
130     *id = ((rt_uint16_t)(read_value[0] << 8) & 0xFF00);
131     *id |= ((rt_uint16_t)(read_value[1]) & 0x00FF);
132 
133     if ((*id != OV2640_ID1) && (*id != OV2640_ID2)) {
134         LOG_E("ov2640 init error, id: 0x%04x", *id);
135         return -RT_ERROR;
136     }
137 
138     LOG_I("ov2640 init success, id: 0x%04x", *id);
139 
140     return RT_EOK;
141 }
142 
ov2640_reset(struct rt_i2c_bus_device * bus)143 static rt_err_t ov2640_reset(struct rt_i2c_bus_device *bus)
144 {
145     rt_pin_mode(PWDN_PIN, PIN_MODE_OUTPUT);
146 
147     rt_thread_mdelay(5);  // 等待模块上电稳定,最少5ms,然后拉低PWDN
148     rt_pin_write(PWDN_PIN, PIN_LOW);  // PWDN 引脚输出低电平,不开启掉电模式,摄像头正常工作,此时摄像头模块的白色LED会点亮
149 
150     // 根据OV2640的上电时序,硬件复位的持续时间要>=3ms,反客的OV2640采用硬件RC复位,持续时间大概在6ms左右
151     // 因此加入延时,等待硬件复位完成并稳定下来
152     rt_thread_mdelay(5);
153 
154     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR);   // 选择 SENSOR 寄存器组
155     write_reg(bus, OV2640_SENSOR_COM7, 0x80);                  // 启动软件复位
156 
157     // 根据OV2640的软件复位时序,软件复位执行后,要>=2ms方可执行SCCB配置,此处采用保守一点的参数,延时10ms
158     rt_thread_mdelay(10);
159     return RT_EOK;
160 }
161 
ov2640_config(struct rt_i2c_bus_device * bus,const rt_uint8_t (* configdata)[2])162 static rt_err_t ov2640_config(struct rt_i2c_bus_device *bus, const rt_uint8_t (*configdata)[2])
163 {
164     rt_uint32_t i    = 0;
165 
166     for (i = 0; configdata[i][0]; i++) {
167         write_reg(bus, configdata[i][0], configdata[i][1]); // 进行参数配置
168     }
169 
170     return RT_EOK;
171 }
172 
ov2640_set_pixformat(struct rt_i2c_bus_device * bus,rt_uint8_t pixformat)173 void ov2640_set_pixformat(struct rt_i2c_bus_device *bus, rt_uint8_t pixformat)
174 {
175     const rt_uint8_t(*configdata)[2];
176     uint32_t i; // 计数变量
177 
178     switch (pixformat) {
179         case Pixformat_RGB565:
180             configdata = OV2640_RGB565_Config;
181             break;
182         case Pixformat_JPEG:
183             configdata = OV2640_JPEG_Config;
184             break;
185         default:
186             break;
187     }
188 
189     for (i = 0; configdata[i][0]; i++) {
190         write_reg(bus, configdata[i][0], configdata[i][1]); // 进行参数配置
191     }
192 }
193 
ov2640_set_framesize(struct rt_i2c_bus_device * bus,rt_uint16_t width,rt_uint16_t height)194 rt_err_t ov2640_set_framesize(struct rt_i2c_bus_device *bus, rt_uint16_t width, rt_uint16_t height)
195 {
196     if ((width % 4) || (height % 4)) // 输出图像的大小一定要能被4整除
197     {
198         return -RT_ERROR; // 返回错误标志
199     }
200 
201     write_reg(bus, OV2640_SEL_Registers,OV2640_SEL_DSP); // 选择 dsp寄存器组
202 
203     write_reg(bus, 0x5a, width / 4 & 0xff);                                   // 实际图像输出的宽度(outw),7~0 bit,寄存器的值等于实际值/4
204     write_reg(bus, 0x5b, height / 4 & 0xff);                                  // 实际图像输出的高度(outh),7~0 bit,寄存器的值等于实际值/4
205     write_reg(bus, 0x5c, (width / 4 >> 8 & 0x03) | (height / 4 >> 6 & 0x04)); // 设置zmhh的bit[2:0],也就是outh 的第 8 bit,outw 的第 9~8 bit,
206 
207     write_reg(bus, OV2640_DSP_RESET, 0x00); // 复位
208 
209     return RT_EOK; // 成功
210 }
211 
ov2640_set_horizontal_mirror(struct rt_i2c_bus_device * bus,rt_uint8_t configstate)212 rt_err_t ov2640_set_horizontal_mirror(struct rt_i2c_bus_device *bus, rt_uint8_t configstate)
213 {
214     rt_uint8_t ov2640_reg; // 寄存器的值
215 
216     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 sensor 寄存器组
217     read_reg(bus, OV2640_SENSOR_REG04, 1, &ov2640_reg);      // 读取 0x04 的寄存器值
218 
219     // reg04,寄存器组4,寄存器地址为 0x04,该寄存器的bit[7],用于设置水平是否镜像
220     if (configstate == OV2640_Enable) // 如果使能镜像
221     {
222         ov2640_reg |= 0x80; // bit[7]置1则镜像
223     } else                  // 取消镜像
224     {
225         ov2640_reg &= ~0x80; // bit[7]置0则是正常模式
226     }
227     return write_reg(bus, OV2640_SENSOR_REG04, ov2640_reg); // 写入寄存器
228 }
229 
ov2640_set_vertical_flip(struct rt_i2c_bus_device * bus,rt_uint8_t configstate)230 rt_err_t ov2640_set_vertical_flip(struct rt_i2c_bus_device *bus, rt_uint8_t configstate)
231 {
232     rt_uint8_t ov2640_reg; // 寄存器的值
233 
234     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_SENSOR); // 选择 sensor 寄存器组
235     read_reg(bus, OV2640_SENSOR_REG04, 1, &ov2640_reg);      // 读取 0x04 的寄存器值
236 
237     // reg04,寄存器组4,寄存器地址为 0x04,该寄存器的第bit[6],用于设置水平是垂直翻转
238     if (configstate == OV2640_Enable) {
239         // 此处设置参考openmv的驱动
240         // bit[4]具体的作用是什么手册没有说,如果垂直翻转之后,该位不置1的话,颜色会不对
241         ov2640_reg |= 0x40 | 0x10; // bit[6]置1时,图像会垂直翻转
242     } else                         // 取消翻转
243     {
244         ov2640_reg &= ~(0x40 | 0x10); // 将bit[6]和bit[4]都写0
245     }
246     return write_reg(bus, OV2640_SENSOR_REG04, ov2640_reg); // 写入寄存器
247 }
248 
ov2640_set_saturation(struct rt_i2c_bus_device * bus,rt_int8_t saturation)249 void ov2640_set_saturation(struct rt_i2c_bus_device *bus, rt_int8_t saturation)
250 {
251     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组
252 
253     switch (saturation) {
254         case 2:
255             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
256             write_reg(bus, OV2640_DSP_BPDATA, 0x02);
257             write_reg(bus, OV2640_DSP_BPADDR, 0x03);
258             write_reg(bus, OV2640_DSP_BPDATA, 0x68);
259             write_reg(bus, OV2640_DSP_BPDATA, 0x68);
260             break;
261 
262         case 1:
263             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
264             write_reg(bus, OV2640_DSP_BPDATA, 0x02);
265             write_reg(bus, OV2640_DSP_BPADDR, 0x03);
266             write_reg(bus, OV2640_DSP_BPDATA, 0x58);
267             write_reg(bus, OV2640_DSP_BPDATA, 0x58);
268             break;
269 
270         case 0:
271             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
272             write_reg(bus, OV2640_DSP_BPDATA, 0x02);
273             write_reg(bus, OV2640_DSP_BPADDR, 0x03);
274             write_reg(bus, OV2640_DSP_BPDATA, 0x48);
275             write_reg(bus, OV2640_DSP_BPDATA, 0x48);
276             break;
277 
278         case -1:
279             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
280             write_reg(bus, OV2640_DSP_BPDATA, 0x02);
281             write_reg(bus, OV2640_DSP_BPADDR, 0x03);
282             write_reg(bus, OV2640_DSP_BPDATA, 0x38);
283             write_reg(bus, OV2640_DSP_BPDATA, 0x38);
284             break;
285 
286         case -2:
287             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
288             write_reg(bus, OV2640_DSP_BPDATA, 0x02);
289             write_reg(bus, OV2640_DSP_BPADDR, 0x03);
290             write_reg(bus, OV2640_DSP_BPDATA, 0x28);
291             write_reg(bus, OV2640_DSP_BPDATA, 0x28);
292             break;
293 
294         default:
295             break;
296     }
297 }
298 
ov2640_set_brightness(struct rt_i2c_bus_device * bus,rt_int8_t brightness)299 void ov2640_set_brightness(struct rt_i2c_bus_device *bus, rt_int8_t brightness)
300 {
301     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组
302 
303     switch (brightness) {
304         case 2:
305             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
306             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
307             write_reg(bus, OV2640_DSP_BPADDR, 0x09);
308             write_reg(bus, OV2640_DSP_BPDATA, 0x40);
309             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
310             break;
311 
312         case 1:
313             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
314             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
315             write_reg(bus, OV2640_DSP_BPADDR, 0x09);
316             write_reg(bus, OV2640_DSP_BPDATA, 0x30);
317             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
318             break;
319 
320         case 0:
321             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
322             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
323             write_reg(bus, OV2640_DSP_BPADDR, 0x09);
324             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
325             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
326             break;
327 
328         case -1:
329             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
330             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
331             write_reg(bus, OV2640_DSP_BPADDR, 0x09);
332             write_reg(bus, OV2640_DSP_BPDATA, 0x10);
333             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
334             break;
335 
336         case -2:
337             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
338             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
339             write_reg(bus, OV2640_DSP_BPADDR, 0x09);
340             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
341             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
342             break;
343 
344         default:
345             break;
346     }
347 }
348 
ov2640_set_contrast(struct rt_i2c_bus_device * bus,rt_int8_t contrast)349 void ov2640_set_contrast(struct rt_i2c_bus_device *bus, rt_int8_t contrast)
350 {
351     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组
352 
353     switch (contrast) {
354         case 2:
355             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
356             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
357             write_reg(bus, OV2640_DSP_BPADDR, 0x07);
358             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
359             write_reg(bus, OV2640_DSP_BPADDR, 0x28);
360             write_reg(bus, OV2640_DSP_BPDATA, 0x0c);
361             write_reg(bus, OV2640_DSP_BPDATA, 0x06);
362             break;
363 
364         case 1:
365             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
366             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
367             write_reg(bus, OV2640_DSP_BPADDR, 0x07);
368             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
369             write_reg(bus, OV2640_DSP_BPADDR, 0x24);
370             write_reg(bus, OV2640_DSP_BPDATA, 0x16);
371             write_reg(bus, OV2640_DSP_BPDATA, 0x06);
372             break;
373 
374         case 0:
375             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
376             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
377             write_reg(bus, OV2640_DSP_BPADDR, 0x07);
378             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
379             write_reg(bus, OV2640_DSP_BPADDR, 0x20);
380             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
381             write_reg(bus, OV2640_DSP_BPDATA, 0x06);
382             break;
383 
384         case -1:
385             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
386             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
387             write_reg(bus, OV2640_DSP_BPADDR, 0x07);
388             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
389             write_reg(bus, OV2640_DSP_BPADDR, 0x1c);
390             write_reg(bus, OV2640_DSP_BPDATA, 0x2a);
391             write_reg(bus, OV2640_DSP_BPDATA, 0x06);
392             break;
393 
394         case -2:
395             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
396             write_reg(bus, OV2640_DSP_BPDATA, 0x04);
397             write_reg(bus, OV2640_DSP_BPADDR, 0x07);
398             write_reg(bus, OV2640_DSP_BPDATA, 0x20);
399             write_reg(bus, OV2640_DSP_BPADDR, 0x18);
400             write_reg(bus, OV2640_DSP_BPDATA, 0x34);
401             write_reg(bus, OV2640_DSP_BPDATA, 0x06);
402             break;
403 
404         default:
405             break;
406     }
407 }
408 
ov2640_set_effect(struct rt_i2c_bus_device * bus,rt_uint8_t effect_mode)409 void ov2640_set_effect(struct rt_i2c_bus_device *bus, rt_uint8_t effect_mode)
410 {
411     write_reg(bus, OV2640_SEL_Registers, OV2640_SEL_DSP); // 选择 dsp寄存器组
412 
413     switch (effect_mode) {
414         case OV2640_Effect_Normal: // 正常模式
415             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
416             write_reg(bus, OV2640_DSP_BPDATA, 0x00);
417             write_reg(bus, OV2640_DSP_BPADDR, 0x05);
418             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
419             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
420             break;
421 
422         case OV2640_Effect_Negative: // 负片模式,也就是颜色全部取反
423             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
424             write_reg(bus, OV2640_DSP_BPDATA, 0x40);
425             write_reg(bus, OV2640_DSP_BPADDR, 0x05);
426             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
427             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
428             break;
429 
430         case OV2640_Effect_BW: // 黑白模式
431             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
432             write_reg(bus, OV2640_DSP_BPDATA, 0x18);
433             write_reg(bus, OV2640_DSP_BPADDR, 0x05);
434             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
435             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
436             break;
437 
438         case OV2640_Effect_BW_Negative: // 黑白+负片模式
439             write_reg(bus, OV2640_DSP_BPADDR, 0x00);
440             write_reg(bus, OV2640_DSP_BPDATA, 0x58);
441             write_reg(bus, OV2640_DSP_BPADDR, 0x05);
442             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
443             write_reg(bus, OV2640_DSP_BPDATA, 0x80);
444             break;
445 
446         default:
447             break;
448     }
449 }
450 
rt_hw_ov2640_init(void)451 int rt_hw_ov2640_init(void)
452 {
453     extern rt_err_t ov2640_dcmi_crop(uint16_t displey_xsize, uint16_t displey_ysize, uint16_t sensor_xsize, uint16_t sensor_ysize);
454 
455     static rt_uint16_t id = 0;
456     rt_device_t dcmi_dev = RT_NULL;
457 
458     i2c_bus = rt_i2c_bus_device_find(I2C_NAME);
459     if (i2c_bus == RT_NULL)
460     {
461         LOG_E("can't find %c deivce", I2C_NAME);
462         return -RT_ERROR;
463     }
464 
465     /* dcmi init */
466     dcmi_dev = rt_device_find("dcmi");
467     if (dcmi_dev == RT_NULL)
468     {
469         LOG_E("can't find dcmi device!");
470         return -RT_ERROR;
471     }
472     rt_device_open(dcmi_dev, RT_DEVICE_FLAG_RDWR);
473 
474     ov2640_reset(i2c_bus);
475     ov2640_read_id(i2c_bus, &id);
476     ov2640_config(i2c_bus, OV2640_SVGA_Config); // 配置 SVGA模式  ------>  800*600,  最大帧率30帧
477     // ov2640_config(i2c_bus, OV2640_UXGA_Config);  // 配置 UXGA模式  ------>  1600*1200,最大帧率15帧
478     ov2640_set_framesize(i2c_bus, OV2640_Width, OV2640_Height);                   // 设置OV2640输出的图像大小
479 
480     // 将OV2640输出图像裁剪成适应屏幕的大小
481     struct stm32_dcmi_cropsize cropsize = {Display_Width, Display_Height, OV2640_Width, OV2640_Height};
482     rt_device_control(dcmi_dev, DCMI_CTRL_CROP, &cropsize);
483 
484     ov2640_set_pixformat(i2c_bus, Pixformat_RGB565);
485     // ov2640_set_pixformat(i2c_bus, Pixformat_JPEG);
486 
487     ov2640_set_saturation(i2c_bus, 0);
488     ov2640_set_brightness(i2c_bus, 0);
489     ov2640_set_contrast(i2c_bus, 0);
490     ov2640_set_effect(i2c_bus, OV2640_Effect_Normal);
491 
492     return RT_EOK;
493 }
494 INIT_APP_EXPORT(rt_hw_ov2640_init);
495 
496 #ifdef DRV_DEBUG
497 #ifdef FINSH_USING_MSH
498 #ifdef BSP_USING_LCD_SPI
499 #include "drv_lcd_spi.h"
camera_sample(int argc,char ** argv)500 int camera_sample(int argc, char **argv)
501 {
502     rt_device_t dcmi_dev = RT_NULL;
503     rt_uint8_t fps = 0;
504     dcmi_dev = rt_device_find("dcmi");
505     if (dcmi_dev == RT_NULL)
506     {
507         LOG_E("can't find dcmi device!");
508         return -RT_ERROR;
509     }
510     struct stm32_dcmi* stm32_dcmi_dev = DCMI_DEVICE(dcmi_dev);
511 
512     // malloc dma memory
513     struct rt_memheap* axi_sram = (struct rt_memheap*)rt_object_find("axi_sram", RT_Object_Class_MemHeap);
514     void* buff_ptr = rt_memheap_alloc(axi_sram, OV2640_BufferSize);
515 
516     // 启动DMA连续传输
517     struct stm32_dcmi_dma_transmitbuffer transmitbuffer = {(uint32_t)buff_ptr, OV2640_BufferSize};
518     rt_device_control(dcmi_dev, DCMI_CTRL_TRANSMIT_CONTINUOUS, &transmitbuffer);
519 
520     while (1) {
521         rt_sem_take(&stm32_dcmi_dev->cam_semaphore, RT_WAITING_FOREVER);
522         // rt_device_control(dcmi_dev, DCMI_CTRL_SUSPEND, RT_NULL);
523 
524         // 将图像数据复制到屏幕
525         lcd_copybuffer(0, 0, Display_Width, Display_Height, (uint16_t *)buff_ptr);
526         // rt_device_control(dcmi_dev, DCMI_CTRL_RESUME, RT_NULL);
527         rt_device_control(dcmi_dev, DCMI_CTRL_GET_FPS, &fps);
528         LOG_D("fps: %d", fps);
529     }
530     rt_memheap_free(buff_ptr);
531 }
532 MSH_CMD_EXPORT(camera_sample, record picture to lcd);
533 
534 #endif /* BSP_USING_LCD_SPI */
535 #endif /* FINSH_USING_MSH */
536 #endif /* DRV_DEBUG */
537 
538 #endif
539