基于STM32F407与OV2640摄像头模块实现图像采集并存储到SD卡
一、软件实现流程
1.1 硬件初始化(HAL库)
// I2C初始化(OV2640配置)
void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000; // 400kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
HAL_I2C_Init(&hi2c1);
}
// DCMI初始化
void MX_DCMI_Init(void)
{
DCMI_HandleTypeDef hdcmi;
hdcmi.Instance = DCMI;
hdcmi.Init.CaptureMode = DCMI_CaptureMode_Continuous;
hdcmi.Init.SynchroMode = DCMI_SynchroMode_Hardware;
hdcmi.Init.PCKPolarity = DCMI_PCKPolarity_Rising;
hdcmi.Init.VSPolarity = DCMI_VSPolarity_Low;
hdcmi.Init.HSPolarity = DCMI_HSPolarity_Low;
HAL_DCMI_Init(&hdcmi);
}
// SDIO初始化
void MX_SDIO_Init(void)
{
SD_HandleTypeDef hsd;
hsd.Instance = SDIO;
hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
hsd.Init.BusWide = SDIO_BUS_WIDE_4B;
HAL_SD_Init(&hsd);
}
1.2 OV2640驱动代码
// OV2640复位
void OV2640_Reset(void)
{
HAL_I2C_Mem_Write(&hi2c1, 0x21<<1, 0x12, I2C_MEMADD_SIZE_8BIT, (uint8_t*)0x80, 1, 100);
HAL_Delay(100);
}
// 设置JPEG模式
void OV2640_JPEGConfig(void)
{
uint8_t config[] = {
0x12, 0x80, // 软复位
0x17, 0x20, // 设置时钟分频
0x30, 0x60, // 调整时钟频率
0x12, 0x04, // 启用JPEG模式
0x70, 0xA0 // 设置JPEG质量
};
HAL_I2C_Master_Transmit(&hi2c1, 0x21<<1, config, sizeof(config), 100);
}
// DCMI DMA传输回调
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
uint32_t *buffer = (uint32_t*)0x20001000; // 1MB DMA缓冲区
HAL_DCMI_Stop_DMA(hdcmi);
// 保存到SD卡
FRESULT res = f_open(&sdFile, "IMG_001.jpg", FA_CREATE_ALWAYS | FA_WRITE);
if(res == FR_OK) {
UINT bytes_written;
f_write(&sdFile, buffer, 1024 * 33, &bytes_written); // 320x240 JPEG约65KB
f_close(&sdFile);
}
}
1.3 SD卡文件系统
// FATFS初始化
FRESULT SD_Init(void)
{
FRESULT res;
res = f_mount(&SDFatFS, "", 1); // 挂载SD卡
if(res != FR_OK) return res;
// 创建文件
res = f_open(&sdFile, "IMG_001.jpg", FA_CREATE_ALWAYS | FA_WRITE);
return res;
}
// JPEG数据写入
void Save_JPEG(uint8_t *data, uint32_t size)
{
UINT bytes_written;
f_write(&sdFile, data, size, &bytes_written);
}
二、主程序流程
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_I2C1_Init();
MX_DCMI_Init();
MX_SDIO_Init();
MX_FATFS_Init();
// 初始化摄像头
OV2640_Reset();
OV2640_JPEGConfig();
// 启动DMA传输
HAL_DCMI_Start_DMA(&hdcmiHandle, DCMI_MODE_SNAPSHOT, (uint32_t)DMA_Buffer, 1024);
while(1)
{
if(FrameReadyFlag) {
FrameReadyFlag = 0;
Save_JPEG(DMA_Buffer, 1024 * 33); // 320x240 JPEG
}
}
}
三、关键参数配置
| 参数 | 配置值 | 说明 |
|---|---|---|
| DCMI时钟 | 24MHz | 通过外部晶振提供 |
| JPEG分辨率 | 320x240 | 设置寄存器0x5A=0x91 |
| DMA缓冲区 | 1MB | 位于内部SRAM |
| SD卡文件系统 | FAT32 | 兼容性最佳 |
| JPEG压缩质量 | 80% | 寄存器0x70=0xA0 |
四、性能优化方案
-
双缓冲机制
volatile uint8_t *dma_buffer[2] = {0x20001000, 0x20002000}; volatile uint8_t buffer_index = 0; void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { buffer_index ^= 1; HAL_DCMI_Stop_DMA(hdcmi); HAL_DCMI_Start_DMA(hdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)dma_buffer[buffer_index], 1024); } -
SD卡写入优化
#define SD_BLOCK_SIZE 512 void Write_SD_Block(uint32_t addr, uint8_t *data) { HAL_SD_WriteBlocks(&hsd, data, addr, SD_BLOCK_SIZE, 1000); } -
DMA配置
static DMA_HandleTypeDef hdma_dcmi_rx; void MX_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); hdma_dcmi_rx.Instance = DMA2_Stream1; hdma_dcmi_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_dcmi_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_dcmi_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_dcmi_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_dcmi_rx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; HAL_DMA_Init(&hdma_dcmi_rx); __HAL_LINKDMA(&hdcmiHandle, hdmarx, hdma_dcmi_rx); }
五、调试与验证
-
逻辑分析仪检查时序
- 验证PCLK频率是否稳定在24MHz
- 检查VSYNC/HREF信号与数据对齐
-
SD卡测试命令
# 查看文件大小 ls -l IMG_001.jpg # 查看文件内容 hexdump -C IMG_001.jpg | head -n 32 -
典型问题解决
问题现象 解决方法 无图像数据 检查DCMI_CROPCmd是否启用 JPEG文件损坏 验证文件头是否包含0xFFD8和0xFFD9 SD卡写入失败 检查FATFS挂载状态和文件权限
六、扩展功能实现
-
实时预览(通过SPI屏幕)
void Display_JPEG(uint8_t *data) { // 使用ST7735驱动显示JPEG缩略图 SPI_SendCommand(0x11); // 退出睡眠 SPI_SendData(data, 1024); } -
无线传输(ESP8266模块)
#include "esp8266.h" void Send_Image() { char cmd[64]; sprintf(cmd, "AT+CIPSEND=%d\r\n", jpeg_size); ESP8266_Send(cmd); ESP8266_Transmit(jpeg_data, jpeg_size); }
七、完整工程资源
- STM32CubeMX配置
- 启用DCMI、I2C1、SDIO外设
- 配置DMA2_Stream1用于DCMI
- 设置系统时钟为168MHz
- 参考代码库
- OV2640驱动:
ov2640.c/h - SD卡操作:
sdio_sdcard.c/h - 代码 : stm32f407使用OV2640摄像头进行拍照存储到sd卡中 www.youwenfan.com/contentcnl/57192.html
- JPEG处理:
jpeg_encoder.c/h
- OV2640驱动:
浙公网安备 33010602011771号