1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html##
第五十三章 照相机实验
本章将介绍使用APM32F407模拟照相机将摄像头采集到的图像数据以BMP或JPEG的文件格式保存至SD中,是前面章节中SD卡驱动、摄像头驱动、FATFS、字库管理库、图片编解码库等的综合应用。通过本章的学习,读者将学习到编码BMP文件以及保存摄像头采集到图像数据的保存。
本章分为如下几个小节:
53.1 硬件设计
53.2 程序设计
53.3 下载验证
53.1 硬件设计
53.1.1 例程功能
- 程序运行后会先检查SD卡,若检测不到SD卡则无法进行本实验,若SD无法访问则后续不能保存图片文件
- 初始化完成后,LCD实时显示摄像头采集到的数据
- 按下KEY0或KEY_UP按键,可分别进行拍摄并保存BMP或JPG格式的图片文件至SD卡
- LED1闪烁,提示DCI捕获到一帧数据
- LED0闪烁,指示程序正在运行
53.1.2 硬件资源 - LED
LED0 - PF9
LED1 - PF10 - 按键
KEY0 - PE4
KEY_UP - PA0 - 正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
- ATK-MC2640摄像头模块
OV_D0~D7 - PC6/PC7/PC8/PC9/PC11/PB6/PE5/PE6
OV_SCL - PD6
OV_SDA - PD7
OV_VSYNC - PB7
OV_HREF - PA4
OV_PCLK - PA6
OV_PWDN - PG9
OV_RESET - PG15
OV_XCLK - PA8 - SD卡(SDIO驱动)
- NOR Flash(SPI驱动)
53.1.3 原理图
本章实验涉及的SD卡、摄像头、LCD等的连接原理图,请读者自行查看前面对应章节中的连接原理图。
53.2 程序设计
53.2.1 实验应用代码
本章实验的应用代码,如下所示:
c
int main(void)
{
uint8_t t = 0;
uint8_t key;
uint8_t res;
uint8_t sd_ok = 1;
uint8_t *pname;
NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); /* 设置中断优先级分组为组3 */
sys_apm32_clock_init(336, 8, 2, 7); /* 配置系统时钟 */
delay_init(168); /* 初始化延时功能 */
usart_init(115200); /* 初始化串口 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
lcd_init(); /* 初始化LCD */
piclib_init(); /* 初始化画图 */
sram_init(); /* 初始化外部SRAM */
my_mem_init(SRAMIN); /* 初始化内部SRAM内存池 */
my_mem_init(SRAMCCM); /* 初始化CCM内存池 */
my_mem_init(SRAMEX); /* 初始化外部SRAM内存池 */
exfuns_init(); /* 为exfuns申请内存 */
f_mount(fs[0], "0:", 1); /* 挂载SD卡 */
f_mount(fs[1], "1:", 1); /* 挂载NOR Flash */
while (ov2640_init() != 0) /* 初始化OV2640 */
{
/* 摄像头错误,代码省略 */
}
sw_sdcard_mode();
while (sd_init() != SD_OK) /* 初始化SD卡 */
{
/* SD卡错误,代码省略 */
}
while (fonts_init() != 0) /* 检查字库 */
{
/* 字库错误,代码省略 */
}
text_show_string(30, 50, 200, 16, "正点原子APM32开发板", 16, 0, RED);
text_show_string(30, 70, 200, 16, "照相机实验", 16, 0, RED);
text_show_string(30, 90, 200, 16, "KEY0:拍照(bmp格式)", 16, 0, RED);
text_show_string(30, 110, 200, 16, "KEY_UP:拍照(jpg格式)", 16, 0, RED);
/* 创建PHOTO文件夹 */
res = (uint8_t)f_mkdir("0:/PHOTO");
if ((res != (uint8_t)FR_EXIST) && (res != 0))
{
/* SD卡错误,代码省略 */
}
/* 申请内存 */
g_dci_line_buf[0] = (uint32_t *)mymalloc(SRAMIN,
JPEG_LINE_SIZE * sizeof(uint32_t));
g_dci_line_buf[1] = (uint32_t *)mymalloc(SRAMIN,
JPEG_LINE_SIZE * sizeof(uint32_t));
g_jpeg_data_buf = (uint32_t *)mymalloc(SRAMEX, JPEG_BUF_SIZE);
pname = (uint8_t *)mymalloc(SRAMIN, 30);
while ( (g_dci_line_buf[0] == NULL) ||
(g_dci_line_buf[1] == NULL) ||
(g_jpeg_data_buf == NULL) ||
(pname == NULL))
{
/* 内存分配错误,代码省略 */
}
sw_ov2640_mode();
ov2640_rgb565_mode(); /* 配置OV2640为RGB565模式 */
dci_init(); /* 初始化DCI */
dci_dma_init( (uint32_t)&(LCD->LCD_RAM), /* 配置DCI DMA */
0,
1,
DMA_MEMORY_DATA_SIZE_HALFWORD,
DMA_MEMORY_INC_DISABLE);
ov2640_outsize_set(lcddev.width, lcddev.height); /* 全屏缩放 */
dci_start(); /* 启动DCI传输 */
ov2640_flash_intctrl(); /* 内部控制闪光灯 */
while (1)
{
t++;
key = key_scan(0);
if (key != 0) /* 有按键按下 */
{
dci_stop(); /* 先禁止DCI传输 */
if (sd_ok == 1) /* SD卡正常 */
{
sw_sdcard_mode(); /* 切换为SD卡模式 */
switch (key)
{
case KEY0_PRES:
{
/* 得到BMP格式文件名 */
camera_new_pathname(pname, 0);
/* 编码并保存BMP文件 */
res = bmp_encode( pname,
0,
0,
lcddev.width,
lcddev.height,
0);
break;
}
case WKUP_PRES:
{
/* 得到JPG格式文件名 */
camera_new_pathname(pname, 1);
/* 保存OV2640输出的JPEG数据到JPG文件 */
res = ov2640_jpg_photo(pname);
ov2640_outsize_set(lcddev.width, lcddev.height);
break;
}
default:
{
break;
}
}
sw_ov2640_mode(); /* 切换为OV2640模式 */
if (res != 0)
{
text_show_string(30, 130, 240, 16,
"写入文件错误!", 16, 0, RED);
}
else
{
text_show_string(10, 130, 240, 16, "拍照成功!", 16, 0, RED);
text_show_string(10, 150, 240, 16, "保存为:", 16, 0, RED);
text_show_string(10 + 56, 150, 240, 16,
(char*)pname, 16, 0, RED);
}
delay_ms(1000);
}
else/* SD卡不可用 */
{
text_show_string(30, 130, 240, 16, "SD卡错误!", 16, 0, RED);
text_show_string(30, 150, 240, 16, "拍照功能不可用!", 16, 0, RED);
}
delay_ms(2000);
dci_start(); /* 恢复DCI传输 */
}
if (t == 20)
{
LED0_TOGGLE();
t = 0;
}
delay_ms(10);
}
}
可以看到,本章实验实际上是前面章节中SD卡驱动、摄像头驱动、FATFS、字库管理库、图片编解码库等的综合应用,并没有涉及新的内容。要注意的是,对于正点原子APM32F407最小系统板,其SD卡和摄像头是无法同时工作的,因为它们使用到了部分共同的GPIO引脚,因此在本章实验中,需要对GPIO引脚的复用模式进行切换,以正常使用SD卡或摄像头,用于切换GPIO引脚复用模式的函数,如下所示:
c
/**
* @bref 切换为OV2640模式
* @note 切换PC8/PC9/PC11为DCI复用功能(AF13)
* @param 无
* @retval 无
*/
static void sw_ov2640_mode(void)
{
OV2640_PWDN(0);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_8, GPIO_AF_DCMI);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_9, GPIO_AF_DCMI);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_11, GPIO_AF_DCMI);
}
/**
* @bref 切换为SD卡模式
* @note 切换PC8/PC9/PC11为SDIO复用功能(AF12)
* @param 无
* @retval 无
*/
static void sw_sdcard_mode(void)
{
OV2640_PWDN(1);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_8, GPIO_AF_SDIO);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_9, GPIO_AF_SDIO);
GPIO_ConfigPinAF(GPIOC, GPIO_PIN_SOURCE_11, GPIO_AF_SDIO);
}
从应用代码的初始化函数中可以看到,在初始化过程中,配置了DCI和DMA将摄像头返回的图像数据传输至LCD上进行显示,初始化完成后,便检测按键输入,若无按键输入,则一直在LCD上显示摄像头采集到的图像数据,若检测到KEY0按键被按下,则调用函数bmp_encode()读取LCD上整个屏幕显示的数据并编码保存到SD卡上,若检测到KEY_UP按键,则调用函数ov2640_jpg_photo()将摄像头配置为JPEG格式输出图像数据,然后获取摄像头输出的一帧JPEG数据,并将JPEG数据通过FATFS写入SD的指定文件中。
53.3 下载验证
在完成编译和烧录操作后,可以看到LCD上实时显示这摄像头采集到的图像数据,此时若按下KEY0按键,可以看到LCD上显示了拍照成功和BMP图片保存路径的提示信息,若按下KEY_UP按键,可以看到LCD上显示了拍照成功和JPEG图片保存路径的提示信息。拍照完成后,可通过PC或图片显示实验例程(需修改获取图片的文件夹路径为"0:PHOTO")查看所拍摄的BMP或JPEG格式的照片。