5.STM32CUBEMX+CLION+OV2640



引脚信息:

stm32f407OV2640
3.3VVCC
PG15RST
PG9PWDN
PA4HREF
PD7SCCB_SDA (开漏输出,上拉,开漏输出的时候(=1), 也可以读取外部信号的高低电平)
PD6SCCB_SCL (推挽输出)
PC6D0(DCMI)
PC7D1(DCMI)
PC8D2(DCMI)
PC9D3(DCMI)
PC11D4(DCMI)
PB6D5(DCMI)
PE5D6(DCMI)
PE6D7(DCMI)
PA6CLK
PG9PWDN
PB7VSYNC
PA8XCLK(NC)

一、STM32CUBEMX的配置

使能RCC、SYS常规配置
在这里插入图片描述
TIM9用做微秒延时定时器
在这里插入图片描述
串口1用做打印输出
在这里插入图片描述
串口3用做DCMI数据传输到上位机
在这里插入图片描述
DCMI的配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
时钟树:

在这里插入图片描述

选择SW4STM32用CLION打开
在这里插入图片描述

二、使用步骤

1.移植OV2640驱动

驱动代码几乎来自正点原子例程:
ov2640.c:


#include "main.h"
#include "sccb.h"
#include "ov2640.h"
#include "ov2640cfg.h"
#include "usart.h"
#include "tim.h"

/**
 * @brief       OV2640 读寄存器
 * @param       reg :  寄存器地址
 * @retval      读到的寄存器值
 */
uint8_t ov2640_read_reg(uint16_t reg) {
    uint8_t data = 0;

    sccb_start();                       /* 启动SCCB传输 */
    sccb_send_byte(OV2640_ADDR);        /* 写器件ADDR */
    delay_us(100);
    sccb_send_byte(reg);                /* 写寄存器地址 */
    delay_us(100);
    sccb_stop();
    delay_us(100);

    /* 设置寄存器地址后,才是读 */
    sccb_start();
    sccb_send_byte(OV2640_ADDR | 0x01); /* 发送读命令 */
    delay_us(100);
    data = sccb_read_byte();            /* 读取数据 */
    sccb_nack();
    sccb_stop();

    return data;
}

/**
 * @brief       OV2640 写寄存器
 * @param       reg : 寄存器地址
 * @param       data: 要写入寄存器的值
 * @retval      0, 成功; 1, 失败;
 */
uint8_t ov2640_write_reg(uint16_t reg, uint8_t data) {
    uint8_t res = 0;

    sccb_start();                           /* 启动SCCB传输 */
    delay_us(100);
    if (sccb_send_byte(OV2640_ADDR))res = 1;/* 写器件ID */
    delay_us(100);
    if (sccb_send_byte(reg))res = 1;        /* 写寄存器地址 */
    delay_us(100);
    if (sccb_send_byte(data))res = 1;       /* 写数据 */
    delay_us(100);
    sccb_stop();

    return res;
}

/**
 * @brief       初始化 OV2640
 * @param       无
 * @retval      0, 成功; 1, 失败;
 */
uint8_t ov2640_init(void) {
    uint16_t i = 0;
    uint16_t reg;


    OV2640_PWDN(0);

    HAL_Delay(10);
    OV2640_RST(0);
    /* 必须先拉低OV2640的RST脚,再上电 */
    HAL_Delay(20);
    OV2640_RST(1);     /* 结束复位 */
    HAL_Delay(20);

    sccb_init();        /* 初始化SCCB 的IO口 */
    HAL_Delay(5);
    ov2640_write_reg(OV2640_DSP_RA_DLMT, 0x01);     /* 操作sensor寄存器 */
    ov2640_write_reg(OV2640_SENSOR_COM7, 0x80);     /* 软复位OV2640 */
    HAL_Delay(50);
    reg = ov2640_read_reg(OV2640_SENSOR_MIDH);      /* 读取厂家ID 高八位 */
    reg <<= 8;
    reg |= ov2640_read_reg(OV2640_SENSOR_MIDL);     /* 读取厂家ID 低八位 */

    if (reg != OV2640_MID)      /* ID 是否正常 */
    {
        printf("MID:%d\r\n", reg);
        return 1;               /* 失败 */
    }

    reg = ov2640_read_reg(OV2640_SENSOR_PIDH);  /* 读取厂家ID 高八位 */
    reg <<= 8;
    reg |= ov2640_read_reg(OV2640_SENSOR_PIDL); /* 读取厂家ID 低八位 */

    if (reg != OV2640_PID)      /* ID是否正常 */
    {
        printf("HID:%d\r\n", reg);
        return 1;   /* 失败 */
    }

    /* 初始化 OV2640 */
    for (i = 0; i < sizeof(ov2640_uxga_init_reg_tbl) / 2; i++) {
        ov2640_write_reg(ov2640_uxga_init_reg_tbl[i][0], ov2640_uxga_init_reg_tbl[i][1]);
    }
    printf("OV2640初始化完成\r\n");
    return 0;       /* OV2640初始化完成 */
}

/**
 * @brief       OV2640 切换为JPEG模式
 * @param       无
 * @retval      无
 */
void ov2640_jpeg_mode(void) {
    uint16_t i = 0;

    /* 设置:YUV422格式 */
    for (i = 0; i < (sizeof(ov2640_yuv422_reg_tbl) / 2); i++) {
        ov2640_write_reg(ov2640_yuv422_reg_tbl[i][0], ov2640_yuv422_reg_tbl[i][1]); /* 发送配置数组 */
    }

    /* 设置:输出JPEG数据 */
    for (i = 0; i < (sizeof(ov2640_jpeg_reg_tbl) / 2); i++) {
        ov2640_write_reg(ov2640_jpeg_reg_tbl[i][0], ov2640_jpeg_reg_tbl[i][1]);     /* 发送配置数组 */
    }
}

/**
 * @brief       OV2640 切换为RGB565模式
 * @param       无
 * @retval      无
 */
void ov2640_rgb565_mode(void) {
    uint16_t i = 0;

    /* 设置:RGB565输出 */
    for (i = 0; i < (sizeof(ov2640_rgb565_reg_tbl) / 4); i++) {
        ov2640_write_reg(ov2640_rgb565_reg_tbl[i][0], ov2640_rgb565_reg_tbl[i][1]); /* 发送配置数组 */
    }
}

/* 自动曝光设置参数表,支持5个等级 */
const static uint8_t OV2640_AUTOEXPOSURE_LEVEL[5][8] =
        {
                {
                        0xFF, 0x01,
                        0x24, 0x20,
                        0x25, 0x18,
                        0x26, 0x60,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x34,
                        0x25, 0x1c,
                        0x26, 0x00,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x3e,
                        0x25, 0x38,
                        0x26, 0x81,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x48,
                        0x25, 0x40,
                        0x26, 0x81,
                },
                {
                        0xFF, 0x01,
                        0x24, 0x58,
                        0x25, 0x50,
                        0x26, 0x92,
                },
        };

/**
 * @brief       OV2640 EV曝光补偿
 * @param       level : 0~4
 * @retval      无
 */
void ov2640_auto_exposure(uint8_t level) {
    uint8_t i;
    uint8_t *p = (uint8_t *) OV2640_AUTOEXPOSURE_LEVEL[level];

    for (i = 0; i < 4; i++) {
        ov2640_write_reg(p[i * 2], p[i * 2 + 1]);
    }
}

/**
 * @brief       OV2640 白平衡设置
 * @param       mode : 0~4, 白平衡模式
 *   @arg       0: 自动   auto
 *   @arg       1: 日光   sunny
 *   @arg       2: 办公室 office
 *   @arg       3: 阴天   cloudy
 *   @arg       4: 家里   home
 * @retval      无
 */
void ov2640_light_mode(uint8_t mode) {
    uint8_t regccval = 0x5E;/* Sunny */
    uint8_t regcdval = 0x41;
    uint8_t regceval = 0x54;

    switch (mode) {
        case 0:             /* auto */
            ov2640_write_reg(0xFF, 0x00);
            ov2640_write_reg(0xC7, 0x00);    /* AWB ON  */
            return;
        case 2:             /* cloudy */
            regccval = 0x65;
            regcdval = 0x41;
            regceval = 0x4F;
            break;
        case 3:             /* office */
            regccval = 0x52;
            regcdval = 0x41;
            regceval = 0x66;
            break;
        case 4:             /* home */
            regccval = 0x42;
            regcdval = 0x3F;
            regceval = 0x71;
            break;
        default :
            break;
    }

    ov2640_write_reg(0xFF, 0x00);
    ov2640_write_reg(0xC7, 0x40);            /* AWB OFF  */
    ov2640_write_reg(0xCC, regccval);
    ov2640_write_reg(0xCD, regcdval);
    ov2640_write_reg(0xCE, regceval);
}

/**
 * @brief       OV2640 色彩饱和度设置
 * @param       set : 0~4, 代表色彩饱和度 -2 ~ 2.
 * @retval      无
 */
void ov2640_color_saturation(uint8_t sat) {
    uint8_t reg7dval = ((sat + 2) << 4) | 0x08;

    ov2640_write_reg(0xFF, 0x00);
    ov2640_write_reg(0x7C, 0x00);
    ov2640_write_reg(0x7D, 0x02);
    ov2640_write_reg(0x7C, 0x03);
    ov2640_write_reg(0x7D, reg7dval);
    ov2640_write_reg(0x7D, reg7dval);
}

/**
 * @brief       OV2640 亮度设置
 * @param       bright : 0~5, 代表亮度 -2 ~ 2.
 * @retval      无
 */
void ov2640_brightness(uint8_t bright) {
    ov2640_write_reg(0xff, 0x00);
    ov2640_write_reg(0x7c, 0x00);
    ov2640_write_reg(0x7d, 0x04);
    ov2640_write_reg(0x7c, 0x09);
    ov2640_write_reg(0x7d, bright << 4);
    ov2640_write_reg(0x7d, 0x00);
}

/**
 * @brief       OV2640 对比度设置
 * @param       contrast : 0~4, 代表对比度 -2 ~ 2.
 * @retval      无
 */
void ov2640_contrast(uint8_t contrast) {
    uint8_t reg7d0val = 0x20;       /* 默认为普通模式 */
    uint8_t reg7d1val = 0x20;

    switch (contrast) {
        case 0:
            reg7d0val = 0x18;       /* -2 */
            reg7d1val = 0x34;
            break;
        case 1:
            reg7d0val = 0x1C;       /* -1 */
            reg7d1val = 0x2A;
            break;
        case 3:
            reg7d0val = 0x24;       /* 1 */
            reg7d1val = 0x16;
            break;
        case 4:
            reg7d0val = 0x28;       /* 2 */
            reg7d1val = 0x0C;
            break;
        default :
            break;
    }

    ov2640_write_reg(0xff, 0x00);
    ov2640_write_reg(0x7c, 0x00);
    ov2640_write_reg(0x7d, 0x04);
    ov2640_write_reg(0x7c, 0x07);
    ov2640_write_reg(0x7d, 0x20);
    ov2640_write_reg(0x7d, reg7d0val);
    ov2640_write_reg(0x7d, reg7d1val);
    ov2640_write_reg(0x7d, 0x06);
}

/**
 * @brief       OV2640 特效设置
 * @param       eft : 0~6, 特效效果
 *   @arg       0: 正常
 *   @arg       1: 负片
 *   @arg       2: 黑白
 *   @arg       3: 偏红色
 *   @arg       4: 偏绿色
 *   @arg       5: 偏蓝色
 *   @arg       6: 复古
 * @retval      无
 */
void ov2640_special_effects(uint8_t eft) {
    uint8_t reg7d0val = 0x00;   /* 默认为普通模式 */
    uint8_t reg7d1val = 0x80;
    uint8_t reg7d2val = 0x80;

    switch (eft) {
        case 1:     /* 负片 */
            reg7d0val = 0x40;
            break;
        case 2:     /* 黑白 */
            reg7d0val = 0x18;
            break;
        case 3:     /* 偏红色 */
            reg7d0val = 0x18;
            reg7d1val = 0x40;
            reg7d2val = 0xC0;
            break;
        case 4:     /* 偏绿色 */
            reg7d0val = 0x18;
            reg7d1val = 0x40;
            reg7d2val = 0x40;
            break;
        case 5:     /* 偏蓝色 */
            reg7d0val = 0x18;
            reg7d1val = 0xA0;
            reg7d2val = 0x40;
            break;
        case 6:     /* 复古 */
            reg7d0val = 0x18;
            reg7d1val = 0x40;
            reg7d2val = 0xA6;
            break;
    }

    ov2640_write_reg(0xff, 0x00);
    ov2640_write_reg(0x7c, 0x00);
    ov2640_write_reg(0x7d, reg7d0val);
    ov2640_write_reg(0x7c, 0x05);
    ov2640_write_reg(0x7d, reg7d1val);
    ov2640_write_reg(0x7d, reg7d2val);
}

/**
 * @brief       OV2640 彩条测试
 * @param       mode : 0~2
 *   @arg       0: 关闭 
 *   @arg       1: 彩条
 * @retval      无
 */
void ov2640_color_bar(uint8_t mode) {
    uint8_t reg;

    ov2640_write_reg(0xFF, 0x01);
    reg = ov2640_read_reg(0x12);
    reg &= ~(1 << 1);
    if (mode)reg |= 1 << 1;
    ov2640_write_reg(0x12, reg);
}

/**
 * @bref        设置传感器输出窗口
 * @param       sx    : 起始地址x
 * @param       sy    : 起始地址y
 * @param       width : 宽度(对应:horizontal)
 * @param       hight : 高度(对应:vertical)
 * @retval      无
 */
void ov2640_window_set(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height) {
    uint16_t endx;
    uint16_t endy;
    uint8_t temp;

    endx = sx + width / 2;
    endy = sy + height / 2;

    ov2640_write_reg(0xFF, 0x01);
    temp = ov2640_read_reg(0x03);       /* 读取Vref之前的值 */
    temp &= 0xF0;
    temp |= ((endy & 0x03) << 2) | (sy & 0x03);
    ov2640_write_reg(0x03, temp);       /* 设置Vref的start和end的最低2位 */
    ov2640_write_reg(0x19, sy >> 2);    /* 设置Vref的start高8位 */
    ov2640_write_reg(0x1A, endy >> 2);  /* 设置Vref的end的高8位 */

    temp = ov2640_read_reg(0x32);       /* 读取Href之前的值 */
    temp &= 0xC0;
    temp |= ((endx & 0x07) << 3) | (sx & 0x07);
    ov2640_write_reg(0x32, temp);       /* 设置Href的start和end的最低3位 */
    ov2640_write_reg(0x17, sx >> 3);    /* 设置Href的start高8位 */
    ov2640_write_reg(0x18, endx >> 3);  /* 设置Href的end的高8位 */
}

/** 
 * @bref        设置图像输出大小
 * @@param      width : 宽度(对应:horizontal)
 * @param       height: 高度(对应:vertical)
 * @note        OV2640输出图像的大小(分辨率),完全由该函数确定
 *              width和height必须是4的倍数
 * @retval      0     : 设置成功
 *              其他  : 设置失败
 */
uint8_t ov2640_outsize_set(uint16_t width, uint16_t height) {
    uint16_t outh;
    uint16_t outw;
    uint8_t temp;

    if (width % 4) return 1;
    if (height % 4) return 2;

    outw = width / 4;
    outh = height / 4;
    ov2640_write_reg(0xFF, 0x00);
    ov2640_write_reg(0xE0, 0x04);
    ov2640_write_reg(0x5A, outw & 0xFF);    /* 设置OUTW的低八位 */
    ov2640_write_reg(0x5B, outh & 0xFF);    /* 设置OUTH的低八位 */

    temp = (outw >> 8) & 0x03;
    temp |= (outh >> 6) & 0x04;
    ov2640_write_reg(0x5C, temp);           /* 设置OUTH/OUTW的高位 */
    ov2640_write_reg(0xE0, 0x00);

    return 0;
}

/**
 * @brief       设置图像开窗大小
 *   @note      OV2640输出图像的大小(分辨率),完全由该函数确定
 *              由于开启了scale功能,用于输出的图像窗口为: xsize - 2 * offx, ysize - 2 * offy
 *              实际输出(width,height), 是在: xsize - 2 * offx, ysize - 2 * offy的基础上进行缩放处理.
 *              一般设置offx和offy的值为16和4,更小也是可以,不过默认是16和4
 * @param       offx,offy    : 输出图像在ov2640_image_window_set设定窗口(假设长宽为xsize和ysize)上的偏移
 * @param       width,height : 实际输出图像的宽度和高度,必须是4的倍数
 * @retval      0, 成功; 其他, 失败;
 */
uint8_t ov2640_image_win_set(uint16_t offx, uint16_t offy, uint16_t width, uint16_t height) {
    uint16_t hsize;
    uint16_t vsize;
    uint8_t temp;

    if (width % 4) return 1;
    if (height % 4) return 2;
    hsize = width / 4;
    vsize = height / 4;
    ov2640_write_reg(0xFF, 0x00);
    ov2640_write_reg(0xE0, 0x04);
    ov2640_write_reg(0x51, hsize & 0xFF);           /* 设置H_SIZE的低八位 */
    ov2640_write_reg(0x52, vsize & 0xFF);           /* 设置V_SIZE的低八位 */
    ov2640_write_reg(0x53, offx & 0xFF);            /* 设置offx的低八位 */
    ov2640_write_reg(0x54, offy & 0xFF);            /* 设置offy的低八位 */
    temp = (vsize >> 1) & 0x80;
    temp |= (offy >> 4) & 0x70;
    temp |= (hsize >> 5) & 0x08;
    temp |= (offx >> 8) & 0x07;
    ov2640_write_reg(0x55, temp);                   /* 设置H_SIZE/V_SIZE/OFFX,OFFY的高位 */
    ov2640_write_reg(0x57, (hsize >> 2) & 0x80);    /* 设置H_SIZE/V_SIZE/OFFX,OFFY的高位 */
    ov2640_write_reg(0xE0, 0x00);

    return 0;
}

/**
 * @bref    该函数设置图像尺寸大小,也就是所选格式的输出分辨率
 * @note    UXGA:1600*1200,SVGA:800*600,CIF:352*288
 * @param   width  : 图像宽度
 * @param   height : 图像高度
 *
 * @retval  0      : 设置成功
 *          其他   : 设置失败
 */
uint8_t ov2640_imagesize_set(uint16_t width, uint16_t height) {
    uint8_t temp;

    ov2640_write_reg(0xFF, 0x00);
    ov2640_write_reg(0xE0, 0x04);
    ov2640_write_reg(0xC0, (width) >> 3 & 0xFF);    /* 设置HSIZE的10:3位 */
    ov2640_write_reg(0xC1, (height) >> 3 & 0xFF);   /* 设置VSIZE的10:3位 */

    temp = (width & 0x07) << 3;
    temp |= height & 0x07;
    temp |= (width >> 4) & 0x80;

    ov2640_write_reg(0x8C, temp);
    ov2640_write_reg(0xE0, 0x00);

    return 0;
}

/**
 * @bref        闪光灯外部信号控制
 * @param       sw : 0,关闭
 *                   1,打开
 * @retval      无
 */
void ov2640_flash_extctrl(uint8_t sw) {
    if (sw) OV2640_FLASH(1);
    else
        OV2640_FLASH(0);
}

/**
 * @bref        闪光灯内部信号控制
 * @note        控制信号从OV2640的STROBE引脚自动输出
 * @retval      无
 */
void ov2640_flash_intctrl(void) {
    ov2640_write_reg(0xFF, 0x01);
    ov2640_write_reg(0x4B, 0x81);
    ov2640_write_reg(0x4B, 0x01);
}

ov2640.h:



#ifndef _OV2640_H
#define _OV2640_H

#include "main.h"

 
/******************************************************************************************/
/* PWDN 引脚定义 */
#define OV_PWDN_GPIO_PORT               GPIOG
#define OV_PWDN_GPIO_PIN                GPIO_PIN_9
#define OV_PWDN_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)   /* PG口时钟使能 */

/* RESET 引脚定义 */
#define OV_RESET_GPIO_PORT              GPIOG
#define OV_RESET_GPIO_PIN               GPIO_PIN_15
#define OV_RESET_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)   /* PG口时钟使能 */

/* FLASH 引脚定义 */
#define OV_FLASH_GPIO_PORT              GPIOA
#define OV_FLASH_GPIO_PIN               GPIO_PIN_8
#define OV_FLASH_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

/******************************************************************************************/

/* IO控制函数 */
#define OV2640_PWDN(x)      do{ x ? \
                                HAL_GPIO_WritePin(OV_PWDN_GPIO_PORT, OV_PWDN_GPIO_PIN, GPIO_PIN_SET) : \
                                HAL_GPIO_WritePin(OV_PWDN_GPIO_PORT, OV_PWDN_GPIO_PIN, GPIO_PIN_RESET); \
                            }while(0)       /* POWER DOWN控制信号 */

#define OV2640_RST(x)       do{ x ? \
                                HAL_GPIO_WritePin(OV_RESET_GPIO_PORT, OV_RESET_GPIO_PIN, GPIO_PIN_SET) : \
                                HAL_GPIO_WritePin(OV_RESET_GPIO_PORT, OV_RESET_GPIO_PIN, GPIO_PIN_RESET); \
                            }while(0)       /* 复位控制信号 */

#define OV2640_FLASH(x)     do{ x ? \
                                HAL_GPIO_WritePin(OV_FLASH_GPIO_PORT, OV_FLASH_GPIO_PIN, GPIO_PIN_SET) : \
                                HAL_GPIO_WritePin(OV_FLASH_GPIO_PORT, OV_FLASH_GPIO_PIN, GPIO_PIN_RESET); \
                            }while(0)       /* 闪光灯控制信号 */

#define Image_FlipVer       1               /* 图像垂直翻转使能 1;使能 0:失能(使用ATK-OV2640模组版本必须使能) */

/* OV2640的ID和访问地址 */
#define OV2640_MID          0x7FA2
#define OV2640_PID          0x2642
#define OV2640_ADDR         0x60            /* OV2640的IIC地址 */


/* 当选择DSP地址(0xFF=0x00)时,OV2640的DSP寄存器地址映射表 */
#define OV2640_DSP_R_BYPASS     0x05
#define OV2640_DSP_Qs           0x44
#define OV2640_DSP_CTRL         0x50
#define OV2640_DSP_HSIZE1       0x51
#define OV2640_DSP_VSIZE1       0x52
#define OV2640_DSP_XOFFL        0x53
#define OV2640_DSP_YOFFL        0x54
#define OV2640_DSP_VHYX         0x55
#define OV2640_DSP_DPRP         0x56
#define OV2640_DSP_TEST         0x57
#define OV2640_DSP_ZMOW         0x5A
#define OV2640_DSP_ZMOH         0x5B
#define OV2640_DSP_ZMHH         0x5C
#define OV2640_DSP_BPADDR       0x7C
#define OV2640_DSP_BPDATA       0x7D
#define OV2640_DSP_CTRL2        0x86
#define OV2640_DSP_CTRL3        0x87
#define OV2640_DSP_SIZEL        0x8C
#define OV2640_DSP_HSIZE2       0xC0
#define OV2640_DSP_VSIZE2       0xC1
#define OV2640_DSP_CTRL0        0xC2
#define OV2640_DSP_CTRL1        0xC3
#define OV2640_DSP_R_DVP_SP     0xD3
#define OV2640_DSP_IMAGE_MODE   0xDA
#define OV2640_DSP_RESET        0xE0
#define OV2640_DSP_MS_SP        0xF0
#define OV2640_DSP_SS_ID        0x7F
#define OV2640_DSP_SS_CTRL      0xF8
#define OV2640_DSP_MC_BIST      0xF9
#define OV2640_DSP_MC_AL        0xFA
#define OV2640_DSP_MC_AH        0xFB
#define OV2640_DSP_MC_D         0xFC
#define OV2640_DSP_P_STATUS     0xFE
#define OV2640_DSP_RA_DLMT      0xFF

/* 当选择传感器地址(0xFF=0x01)时,OV2640的DSP寄存器地址映射表 */
#define OV2640_SENSOR_GAIN       0x00
#define OV2640_SENSOR_COM1       0x03
#define OV2640_SENSOR_REG04      0x04
#define OV2640_SENSOR_REG08      0x08
#define OV2640_SENSOR_COM2       0x09
#define OV2640_SENSOR_PIDH       0x0A
#define OV2640_SENSOR_PIDL       0x0B
#define OV2640_SENSOR_COM3       0x0C
#define OV2640_SENSOR_COM4       0x0D
#define OV2640_SENSOR_AEC        0x10
#define OV2640_SENSOR_CLKRC      0x11
#define OV2640_SENSOR_COM7       0x12
#define OV2640_SENSOR_COM8       0x13
#define OV2640_SENSOR_COM9       0x14
#define OV2640_SENSOR_COM10      0x15
#define OV2640_SENSOR_HREFST     0x17
#define OV2640_SENSOR_HREFEND    0x18
#define OV2640_SENSOR_VSTART     0x19
#define OV2640_SENSOR_VEND       0x1A
#define OV2640_SENSOR_MIDH       0x1C
#define OV2640_SENSOR_MIDL       0x1D
#define OV2640_SENSOR_AEW        0x24
#define OV2640_SENSOR_AEB        0x25
#define OV2640_SENSOR_W          0x26
#define OV2640_SENSOR_REG2A      0x2A
#define OV2640_SENSOR_FRARL      0x2B
#define OV2640_SENSOR_ADDVSL     0x2D
#define OV2640_SENSOR_ADDVHS     0x2E
#define OV2640_SENSOR_YAVG       0x2F
#define OV2640_SENSOR_REG32      0x32
#define OV2640_SENSOR_ARCOM2     0x34
#define OV2640_SENSOR_REG45      0x45
#define OV2640_SENSOR_FLL        0x46
#define OV2640_SENSOR_FLH        0x47
#define OV2640_SENSOR_COM19      0x48
#define OV2640_SENSOR_ZOOMS      0x49
#define OV2640_SENSOR_COM22      0x4B
#define OV2640_SENSOR_COM25      0x4E
#define OV2640_SENSOR_BD50       0x4F
#define OV2640_SENSOR_BD60       0x50
#define OV2640_SENSOR_REG5D      0x5D
#define OV2640_SENSOR_REG5E      0x5E
#define OV2640_SENSOR_REG5F      0x5F
#define OV2640_SENSOR_REG60      0x60
#define OV2640_SENSOR_HISTO_LOW  0x61
#define OV2640_SENSOR_HISTO_HIGH 0x62


/* 对外接口函数 */
uint8_t ov2640_read_reg(uint16_t reg);

uint8_t ov2640_write_reg(uint16_t reg, uint8_t data);

uint8_t ov2640_init(void);

void ov2640_jpeg_mode(void);

void ov2640_rgb565_mode(void);

void ov2640_auto_exposure(uint8_t level);

void ov2640_light_mode(uint8_t mode);

void ov2640_color_saturation(uint8_t sat);

void ov2640_brightness(uint8_t bright);

void ov2640_contrast(uint8_t contrast);

void ov2640_special_effects(uint8_t eft);

void ov2640_window_set(uint16_t sx, uint16_t sy, uint16_t width, uint16_t height);

uint8_t ov2640_outsize_set(uint16_t width, uint16_t height);

uint8_t ov2640_image_win_set(uint16_t offx, uint16_t offy, uint16_t width, uint16_t height);

uint8_t ov2640_image_window_set(uint16_t offx, uint16_t offy, uint16_t width, uint16_t height);

uint8_t ov2640_imagesize_set(uint16_t width, uint16_t height);

void ov2640_flash_extctrl(uint8_t sw);

void ov2640_flash_intctrl(void);

#endif

sccb.c



#include "sccb.h"
#include "tim.h"


/**
 * @brief       初始化SCCB
 * @param        SCCB_SCL-PD6(推挽输出)
 * @retval       SCCB_SDA-PD7(SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平)
 */
void sccb_init(void) {

    sccb_stop();    /* 停止总线上所有设备 */
}

/**
 * @brief       SCCB延时函数,用于控制IIC读写速度
 * @param       无
 * @retval      无
 */
static void sccb_delay(void) {
    delay_us(50);
}

/**
 * @brief       产生SCCB起始信号
 * @param       无
 * @retval      无
 */
void sccb_start(void) {
    SCCB_SDA(1);
    SCCB_SCL(1);
    sccb_delay();
    SCCB_SDA(0);    /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    sccb_delay();
    SCCB_SCL(0);    /* 钳住SCCB总线,准备发送或接收数据 */
}

/**
 * @brief       产生SCCB停止信号
 * @param       无
 * @retval      无
 */
void sccb_stop(void) {
    SCCB_SDA(0);    /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    sccb_delay();
    SCCB_SCL(1);
    sccb_delay();
    SCCB_SDA(1);    /* 发送SCCB总线结束信号 */
    sccb_delay();
}

/**
 * @brief       不产生ACK应答
 * @param       无
 * @retval      无
 */
void sccb_nack(void) {
    sccb_delay();
    SCCB_SDA(1);    /* SCL 0 -> 1  时 SDA = 1, 表示不应答 */
    SCCB_SCL(1);
    sccb_delay();
    SCCB_SCL(0);    /* 产生一个时钟 */
    sccb_delay();
    SCCB_SDA(0);
    sccb_delay();
}

/**
 * @brief       SCCB  发送一个字节
 * @param       data: 要发送的数据
 * @retval      res
 */
uint8_t sccb_send_byte(uint8_t data) {
    uint8_t t, res;

    for (t = 0; t < 8; t++) {
        SCCB_SDA((data & 0x80) >> 7);   /* 高位先发送 */
        sccb_delay();
        SCCB_SCL(1);
        sccb_delay();
        SCCB_SCL(0);
        data <<= 1;     /* 左移1位,用于下一次发送 */
    }
#if OV_SCCB_TYPE_NOD
    SCCB_SDA_IN();      /* 设置SDA为输入 */
    sccb_delay();
#endif
    SCCB_SDA(1);        /* 发送完成, 主机释放SDA线 */
    sccb_delay();
    SCCB_SCL(1);        /* 接收第九位,以判断是否发送成功 */
    sccb_delay();

    if (SCCB_READ_SDA) {
        res = 1;        /* SDA=1发送失败,返回1 */
    } else {
        res = 0;        /* SDA=0发送成功,返回0 */
    }

    SCCB_SCL(0);
#if OV_SCCB_TYPE_NOD
    SCCB_SDA_OUT();      /* 设置SDA为输出 */
    delay_us(50);
#endif
    return res;
}

/**
 * @brief       SCCB 读取一个字节
 * @param       无
 * @retval      读取到的数据
 */
uint8_t sccb_read_byte(void) {
    uint8_t i, receive = 0;
#if OV_SCCB_TYPE_NOD
    SCCB_SDA_IN();          /* 设置SDA为输入 */
    delay_us(50);
#endif
    for (i = 0; i < 8; i++) /* 接收1个字节数据 */
    {
        sccb_delay();
        receive <<= 1;      /* 高位先输出,所以先收到的数据位要左移 */
        SCCB_SCL(1);

        if (SCCB_READ_SDA) {
            receive++;
        }
        sccb_delay();
        SCCB_SCL(0);
    }
#if OV_SCCB_TYPE_NOD
    SCCB_SDA_OUT();         /* 设置SDA为输出 */
    delay_us(50);
#endif
    return receive;
}

sccb.h



#ifndef __SCCB_H
#define __SCCB_H

#include "main.h"


/******************************************************************************************/
/* 引脚 定义 */

#define SCCB_SCL_GPIO_PORT               GPIOD
#define SCCB_SCL_GPIO_PIN                GPIO_PIN_6
#define SCCB_SCL_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)        /* PD口时钟使能 */

#define SCCB_SDA_GPIO_PORT               GPIOD
#define SCCB_SDA_GPIO_PIN                GPIO_PIN_7
#define SCCB_SDA_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)        /* PG口时钟使能 */


#define OV_SCCB_TYPE_NOD     0          /* 如果不用开漏模式或SCCB上无上拉电阻,我们需要推挽和输入切换的方式 */
#if OV_SCCB_TYPE_NOD
#define SCCB_SDA_IN()  { GPIOD->MODER &= ~(3 << (7 * 2)); GPIOD->MODER |= 0 << (7 * 2); }   /* PD7 输入 */
#define SCCB_SDA_OUT() { GPIOD->MODER &= ~(3 << (7 * 2)); GPIOD->MODER |= 1 << (7 * 2); }   /* PD7 输出 */
#endif

/******************************************************************************************/

/* IO操作函数 */
#define SCCB_SCL(x)   do{ x ? \
                          HAL_GPIO_WritePin(SCCB_SCL_GPIO_PORT, SCCB_SCL_GPIO_PIN, GPIO_PIN_SET) : \
                          HAL_GPIO_WritePin(SCCB_SCL_GPIO_PORT, SCCB_SCL_GPIO_PIN, GPIO_PIN_RESET); \
                      }while(0)  /* SCL */

#define SCCB_SDA(x)   do{ x ? \
                          HAL_GPIO_WritePin(SCCB_SDA_GPIO_PORT, SCCB_SDA_GPIO_PIN, GPIO_PIN_SET) : \
                          HAL_GPIO_WritePin(SCCB_SDA_GPIO_PORT, SCCB_SDA_GPIO_PIN, GPIO_PIN_RESET); \
                      }while(0)  /* SDA */


#define SCCB_READ_SDA       HAL_GPIO_ReadPin(SCCB_SDA_GPIO_PORT, SCCB_SDA_GPIO_PIN)         /* 读取SDA */


/* 对外接口函数 */
void sccb_init(void);

void sccb_stop(void);

void sccb_start(void);

void sccb_nack(void);

uint8_t sccb_read_byte(void);

uint8_t sccb_send_byte(uint8_t data);

#endif

tim.c

/**
  ******************************************************************************
  * @file    tim.c
  * @brief   This file provides code for the configuration
  *          of the TIM instances.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "tim.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim9;

/* TIM1 init function */
void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 6720-1;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 250-1;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_DISABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_1);
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
  sBreakDeadTimeConfig.DeadTime = 0;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  HAL_TIM_MspPostInit(&htim1);

}
/* TIM9 init function */
void MX_TIM9_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};

  htim9.Instance = TIM9;
  htim9.Init.Prescaler = 168-1;
  htim9.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim9.Init.Period = 65535;
  htim9.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim9.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim9) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim9, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */

  /* USER CODE END TIM1_MspInit 0 */
    /* TIM1 clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();
  /* USER CODE BEGIN TIM1_MspInit 1 */

  /* USER CODE END TIM1_MspInit 1 */
  }
  else if(tim_baseHandle->Instance==TIM9)
  {
  /* USER CODE BEGIN TIM9_MspInit 0 */

  /* USER CODE END TIM9_MspInit 0 */
    /* TIM9 clock enable */
    __HAL_RCC_TIM9_CLK_ENABLE();
  /* USER CODE BEGIN TIM9_MspInit 1 */

  /* USER CODE END TIM9_MspInit 1 */
  }
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(timHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspPostInit 0 */

  /* USER CODE END TIM1_MspPostInit 0 */

    __HAL_RCC_GPIOE_CLK_ENABLE();
    /**TIM1 GPIO Configuration
    PE9     ------> TIM1_CH1
    PE11     ------> TIM1_CH2
    PE13     ------> TIM1_CH3
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_11|GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /* USER CODE BEGIN TIM1_MspPostInit 1 */

  /* USER CODE END TIM1_MspPostInit 1 */
  }

}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspDeInit 0 */

  /* USER CODE END TIM1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM1_CLK_DISABLE();
  /* USER CODE BEGIN TIM1_MspDeInit 1 */

  /* USER CODE END TIM1_MspDeInit 1 */
  }
  else if(tim_baseHandle->Instance==TIM9)
  {
  /* USER CODE BEGIN TIM9_MspDeInit 0 */

  /* USER CODE END TIM9_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM9_CLK_DISABLE();
  /* USER CODE BEGIN TIM9_MspDeInit 1 */

  /* USER CODE END TIM9_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
void delay_us(uint16_t us) {
    uint16_t differ = 0xffff - us - 5;
//设定TIM9计数器起始值
    __HAL_TIM_SET_COUNTER(&htim9, differ);
//启动定时器
    HAL_TIM_Base_Start(&htim9);
    while (differ < 0xffff - 5) {
        //查询计数器的计数值
        differ = __HAL_TIM_GET_COUNTER(&htim9);
    }
    HAL_TIM_Base_Stop(&htim9);
}
/* USER CODE END 1 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

tim.h

/**
  ******************************************************************************
  * @file    tim.h
  * @brief   This file contains all the function prototypes for
  *          the tim.c file
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __TIM_H__
#define __TIM_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim9;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_TIM1_Init(void);
void MX_TIM9_Init(void);

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);

/* USER CODE BEGIN Prototypes */
void delay_us(uint16_t us);
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* __TIM_H__ */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

dcmi.c

/* USER CODE BEGIN 1 */
void dcmi_start(void) {
    __HAL_DMA_ENABLE(&hdma_dcmi);   /* 使能DMA */
    DCMI->CR |= DCMI_CR_CAPTURE;            /* DCMI捕获使能 */
}

void dcmi_stop(void) {
    DCMI->CR &= ~(DCMI_CR_CAPTURE);         /* DCMI捕获关闭 */
    while (DCMI->CR & 0X01);                /* 等待传输结束 */
    __HAL_DMA_DISABLE(&hdma_dcmi);  /* 关闭DMA */
}
/* USER CODE END 1 */

stm32f4_it.c

#include <stdio.h>
#include <example.h>
void DCMI_IRQHandler(void)
{
  /* USER CODE BEGIN DCMI_IRQn 0 */
    __HAL_DCMI_CLEAR_FLAG(&hdcmi, DCMI_FLAG_FRAMERI);//清除帧中断
    jpeg_data_process();//jpeg数据处理
  /* USER CODE END DCMI_IRQn 0 */
  HAL_DCMI_IRQHandler(&hdcmi);
  /* USER CODE BEGIN DCMI_IRQn 1 */

  /* USER CODE END DCMI_IRQn 1 */
}

example.c

#include "example.h"
#include "ov2640.h"
#include "dcmi.h"


uint8_t ov2640_mode = 1;        //工作模式:0,RGB565模式;1,JPEG模式
#define jpeg_buf_size 30*1024            //定义JPEG数据缓存jpeg_buf的大小(*4字节)
__ALIGNED(4) uint32_t jpeg_buf[jpeg_buf_size];    //JPEG数据缓存buf
volatile uint32_t jpeg_data_len = 0;            //buf中的JPEG有效数据长度
volatile uint8_t jpeg_data_ok = 0;                //JPEG数据采集完成标志
const char *EFFECTS_TBL[7] = {"Normal", "Negative", "B&W", "Redish", "Greenish", "Bluish", "Antique"};     /* 7种特效 */
const char *JPEG_SIZE_TBL[13] = {"QQVGA", "QCIF", "QVGA", "WGVGA", "CIF", "VGA", "SVGA", "XGA", "WXGA", "SVGA", "WXGA+",
                                 "SXGA", "UXGA"}; /* JPEG图片 13种尺寸 */

const uint16_t jpeg_img_size_tbl[][2] =
        {
                160, 120,       /* QQVGA */
                176, 144,       /* QCIF */
                320, 240,       /* QVGA */
                400, 240,        /* WGVGA */
                352, 288,        /* CIF */
                640, 480,       /* VGA */
                800, 600,       /* SVGA */
                1024, 768,      /* XGA */
                1280, 800,      /* WXGA */
                1280, 960,      /* XVGA */
                1440, 900,      /* WXGA+ */
                1280, 1024,     /* SXGA */
                1600, 1200,     /* UXGA */
        };

void jpeg_data_process(void) {
    if (ov2640_mode)//只有在JPEG格式下,才需要做处理.
    {
        if (jpeg_data_ok == 0)    //jpeg数据还未采集完?
        {
            __HAL_DMA_DISABLE(&hdma_dcmi);//关闭DMA
            while (DMA2_Stream1->CR & 0X01);    //等待DMA2_Stream1可配置
            jpeg_data_len = jpeg_buf_size - __HAL_DMA_GET_COUNTER(&hdma_dcmi);//得到剩余数据长度
            jpeg_data_ok = 1;                //标记JPEG数据采集完按成,等待其他函数处理
        }
        if (jpeg_data_ok == 2)    //上一次的jpeg数据已经被处理了
        {
            __HAL_DMA_SET_COUNTER(&hdma_dcmi, jpeg_buf_size);//传输长度为jpeg_buf_size*4字节
            __HAL_DMA_ENABLE(&hdma_dcmi); //打开DMA
            jpeg_data_ok = 0;                        //标记数据未采集
        }
    }

}

extern void dcmi_start(void);


void jpeg_test(void) {
    uint32_t i;
    uint8_t *p;
    uint8_t effect = 0, saturation = 2, contrast = 2;
    uint8_t size = 2;        //默认是QVGA 320*240尺寸
    uint8_t msgbuf[15];    //消息缓存区
//    delay_init(168);
    while (ov2640_init());       /* 初始化OV2640 */

    ov2640_jpeg_mode(); /* JPEG模式 */
    ov2640_contrast(contrast);//对比度
    ov2640_color_saturation(saturation);//饱和度
    ov2640_special_effects(effect);//设置特效
    ov2640_outsize_set(jpeg_img_size_tbl[size][0], jpeg_img_size_tbl[size][1]);//设置输出尺寸
    HAL_DMA_Start(&hdma_dcmi, (uint32_t) &DCMI->DR, (uint32_t) &jpeg_buf, jpeg_buf_size);
    dcmi_start();


    while (1) {
        if (jpeg_data_ok == 1)    //已经采集完一帧图像了
        {
            p = (uint8_t *) jpeg_buf;

            for (i = 0; i < jpeg_data_len * 4; i++)        //dma传输1次等于4字节,所以乘以4.
            {
                while ((USART3->SR & 0X40) == 0);    //循环发送,直到发送完毕
                USART3->DR = p[i];
            }
            jpeg_data_ok = 2;    //标记jpeg数据处理完了,可以让DMA去采集下一帧了.
        }

    }
}

example.h

#ifndef F407_OV2640_EXAMPLE_H
#define F407_OV2640_EXAMPLE_H
void jpeg_data_process(void);

void jpeg_test(void);

#endif

usart.c


/* USER CODE BEGIN 1 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE {
    HAL_UART_Transmit(&huart1, (uint8_t *) &ch, 1, 0xFFFF);
    return ch;
}

/* USER CODE END 1 */

usart.h

#include "stdio.h"

main.c

/* USER CODE BEGIN Header */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dcmi.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "example.h"
#include "ov2640.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_USART1_UART_Init();
  MX_DCMI_Init();
  MX_USART3_UART_Init();
  MX_TIM9_Init();
  /* USER CODE BEGIN 2 */
    DCMI->IER = 0x0;//函数HAL_DCMI_Init()会默认打开很多中断,不处理会有各种问题,因此关闭所有,只打开需要的
    __HAL_DCMI_ENABLE_IT(&hdcmi, DCMI_IT_FRAME);        //开启帧中断
    __HAL_DCMI_ENABLE(&hdcmi);

    jpeg_test();
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


三、效果

对应串口1:
在这里插入图片描述
串口3
在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值