OV7670连接STM32F407VET

目录

常见问题

delay.c

delay.h

sys.c

sys.h

usart.c

usart.h

ov7670.c

ov7670.h

main.c

本地接收脚本


Github源码仓:ceilf6/SmartFruits · GitHub

我们老师让我们别用 HAL 库写,于是我们用的是官方库

常见问题

无法进行烧录:

        1.串口被占用:关闭其他占用串口的软件,然后拔掉串口重新插入

        2.烧录时波特率不对,一般都是115200

        3.先拔掉接到单片机上的外设,因为外设的接收输出串口会对烧录代码有影响

delay.c

#include "delay.h"

static u8  fac_us=0;//us��ʱ������
static u16 fac_ms=0;//ms��ʱ������
//��ʼ���ӳٺ���
//SYSTICK��ʱ�ӹ̶�ΪHCLKʱ�ӵ�1/8
//SYSCLK:ϵͳʱ��
void delay_init(u8 SYSCLK)
{
	SysTick->CTRL&=0xfffffffb;//bit2���,ѡ���ⲿʱ��  HCLK/8
	fac_us=SYSCLK/8;		    
	fac_ms=(u16)fac_us*1000;
}								    
//��ʱnms
//ע��nms�ķ�Χ
//SysTick->LOADΪ24λ�Ĵ���,����,�����ʱΪ:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK��λΪHz,nms��λΪms
//��72M������,nms<=1864 
void delay_ms(u16 nms)
{	 		  	  
	u32 temp;		   
	SysTick->LOAD=(u32)nms*fac_ms;//ʱ�����(SysTick->LOADΪ24bit)
	SysTick->VAL =0x00;           //��ռ�����
	SysTick->CTRL=0x01 ;          //��ʼ����  
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//�ȴ�ʱ�䵽��   
	SysTick->CTRL=0x00;       //�رռ�����
	SysTick->VAL =0X00;       //��ռ�����	  	    
}   
//��ʱnus
//nusΪҪ��ʱ��us��.		    								   
void delay_us(u32 nus)
{		
	u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; //ʱ�����	  		 
	SysTick->VAL=0x00;        //��ռ�����
	SysTick->CTRL=0x01 ;      //��ʼ���� 	 
	do
	{
		temp=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));//�ȴ�ʱ�䵽��   
	SysTick->CTRL=0x00;       //�رռ�����
	SysTick->VAL =0X00;       //��ռ�����	 
}

delay.h

#ifndef __DELAY_H
#define __DELAY_H 			   
#include <sys.h>	  
	 
void delay_init(u8 SYSCLK);
void delay_ms(u16 nms);
void delay_us(u32 nus);

#endif

sys.c

#include "sys.h"  


//THUMBָ�֧�ֻ������
//�������·���ʵ��ִ�л��ָ��WFI  
//__asm void WFI_SET(void)
//{
//	WFI;		  
//}
�ر������ж�(���Dz�����fault��NMI�ж�)
//__asm void INTX_DISABLE(void)
//{
//	CPSID   I
//	BX      LR	  
//}
���������ж�
//__asm void INTX_ENABLE(void)
//{
//	CPSIE   I
//	BX      LR  
//}
����ջ����ַ
addr:ջ����ַ
//__asm void MSR_MSP(u32 addr) 
//{
//	MSR MSP, r0 			//set Main Stack value
//	BX r14
//}
void NVIC_Configuration()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	NVIC_InitStructure.NVIC_IRQChannel=EXTI1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

sys.h

#ifndef __SYS_H
#define __SYS_H	 
#include "stm32f4xx.h" 
#include "delay.h"
#include "usart.h"	
																    
	 
//λ������,ʵ��51���Ƶ�GPIO���ƹ���

#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO�ڵ�ַӳ��
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 
//IO�ڲ���,ֻ�Ե�һ��IO��!
//ȷ��n��ֵС��16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //��� 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //���� 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //��� 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //���� 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //��� 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //���� 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //��� 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //���� 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //��� 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //����

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //��� 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //����

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //��� 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //����

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  //��� 
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n)  //����

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  //��� 
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  //����

//����Ϊ��ຯ��
void WFI_SET(void);		//ִ��WFIָ��
void INTX_DISABLE(void);//�ر������ж�
void INTX_ENABLE(void);	//���������ж�
void MSR_MSP(u32 addr);	//���ö�ջ��ַ 
 
void NVIC_Configuration(void);
#endif

usart.c

#include "sys.h"
#include "usart.h"	
#include "stm32f4xx.h"


//
// 支持printf函数重定向
#if 1
#pragma import(__use_no_semihosting)             
// 标准库需要支持的函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
// 定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
// 重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0); // 循环发送,直到发送完成   
	USART1->DR = (u8) ch;      
	return ch;
}
#endif
 

u8 USART_RX_BUF[USART_REC_LEN];     // 接收缓冲,最大USART_REC_LEN个字节.

// 初始化IO 串口1 
// bound: 波特率
void uart_init(u32 bound){
   // GPIO端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); // 使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // 使能USART1时钟
 
	// 串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); // GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); // GPIOA10复用为USART1
	
	// USART1端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 速度提高到100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); // 初始化PA9,PA10

   // USART1 初始化设置
	USART_InitStructure.USART_BaudRate = bound; // 波特率设置
	USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
	USART_Init(USART1, &USART_InitStructure); // 初始化串口1
	
	// 配置USART1 DMA发送和接收
    // 启用DMA时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    
    // 配置DMA用于USART传输(可选,如果您想进一步提高传输效率)
    // 此处省略DMA配置,如需使用请添加相应代码
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2
	// Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级提高到1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级0
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
	NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器

	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启相关中断

	USART_Cmd(USART1, ENABLE);  // 使能串口1
    
    // 使能发送和接收
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_ITConfig(USART1, USART_IT_TXE, DISABLE); // 发送中断先禁用,需要时再开启
}


void USART1_IRQHandler(void)                	// 串口1中断服务程序
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        // 接收中断处理
        uint8_t data = USART_ReceiveData(USART1);
        // 清除中断标志
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "stm32f4xx_conf.h"
#include "sys.h" 


//USART_REC_LEN,���ڶ��崮������������յ��ֽ���(������2��14�η�)
//EN_USART1_RX��ʹ�ܷ�ʽ
// 	
#define USART_REC_LEN  			200  	//�����������ֽ��� 200

	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //���ջ���,���USART_REC_LEN���ֽ�.ĩ�ֽ�Ϊ���з� 


void uart_init(u32 bound);
#endif

ov7670.c

#include "sys.h"
#include "ov7670.h"
#include "ov7670cfg.h"
#include "timer.h"	  
#include "delay.h"
#include "usart.h"			 
#include "sccb.h"	
#include "exti.h"
#include "stm32f4xx.h"
u8 OV7670_Init(void)
{
    u8 temp;
    u16 i=0;      
    //??IO
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    // ??GPIO??
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB, ENABLE);
	
		// PC9-HREF???(????)
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;       // ??PA8
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
		GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;    // ????
		GPIO_Init(GPIOC, &GPIO_InitStructure);
        // PA9 - VSY ?? ??
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // PB12 - WR ????
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB,GPIO_Pin_12);
    
    // PA10 - RCK ????
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_11);    
    
    // PA0~7 ?? ??
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|
                                  GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
     
    // PA14 - OE ????
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_14;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_15);
    
    // PB0 - RRST, PB1 - WRST ????
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0|GPIO_Pin_1;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1);
    
    // ?F4???,SWD??????,???????
    
    SCCB_Init();                //???SCCB?IO?          
    if(SCCB_WR_Reg(0x12,0x80))return 1;    //??SCCB      
    delay_ms(50);  
    //??????
    temp=SCCB_RD_Reg(0x0b);  
    if(temp!=0x73)return 2;  
    temp=SCCB_RD_Reg(0x0a);  
    if(temp!=0x76)return 2;
    //?????      
    for(i=0;i<sizeof(ov7670_init_reg_tbl)/sizeof(ov7670_init_reg_tbl[0]);i++)
    {
        SCCB_WR_Reg(ov7670_init_reg_tbl[i][0],ov7670_init_reg_tbl[i][1]);
    }
    
    return 0x00;     //ok
}



void OV7670_Light_Mode(u8 mode)
{
    u8 reg13val=0xE7;  //????????????
    u8 reg01val=0;
    u8 reg02val=0;
    
    switch(mode)
    {
        case 1:  //sunny
            reg13val=0xE5;
            reg01val=0x5A;
            reg02val=0x5C;
            break;    
        case 2:  //cloudy
            reg13val=0xE5;
            reg01val=0x58;
            reg02val=0x60;
            break;    
        case 3:  //office
            reg13val=0xE5;
            reg01val=0x84;
            reg02val=0x4C;
            break;    
        case 4:  //home
            reg13val=0xE5;
            reg01val=0x96;
            reg02val=0x40;
            break;    
    }
    
    SCCB_WR_Reg(0x13, reg13val);  //COM8?? 
    SCCB_WR_Reg(0x01, reg01val);  //AWB?????? 
    SCCB_WR_Reg(0x02, reg02val);  //AWB?????? 
}

//????
//0:-2
//1:-1
//2,0
//3,1
//4,2



void OV7670_Color_Saturation(u8 sat)
{
    u8 reg4f5054val = 0x80;  //????sat=2,?????????
    u8 reg52val = 0x22;
    u8 reg53val = 0x5E;
    
    switch(sat)
    {
        case 0:  //-2
            reg4f5054val = 0x40;     
            reg52val = 0x11;
            reg53val = 0x2F;     
            break;
        case 1:  //-1
            reg4f5054val = 0x66;    
            reg52val = 0x1B;
            reg53val = 0x4B;  
            break;
        case 3:  //1
            reg4f5054val = 0x99;   
            reg52val = 0x28;
            reg53val = 0x71;   
            break;
        case 4:  //2
            reg4f5054val = 0xC0;   
            reg52val = 0x33;
            reg53val = 0x8D;   
            break;
    }
    
    // ??OV7670?????
    SCCB_WR_Reg(0x4F, reg4f5054val);  //??????1
    SCCB_WR_Reg(0x50, reg4f5054val);  //??????2
    SCCB_WR_Reg(0x51, 0x00);          //??????3
    SCCB_WR_Reg(0x52, reg52val);      //??????4
    SCCB_WR_Reg(0x53, reg53val);      //??????5
    SCCB_WR_Reg(0x54, reg4f5054val);  //??????6
    SCCB_WR_Reg(0x58, 0x9E);          //MTXS
}





// ????
// 0:-2
// 1:-1
// 2,0
// 3,1
// 4,2
void OV7670_Brightness(u8 bright)
{
    u8 reg55val = 0x00;  // ??bright=2
    switch(bright)
    {
        case 0:  // -2
            reg55val = 0xB0;
            break;
        case 1:  // -1
            reg55val = 0x98;
            break;
        case 3:  // 1
            reg55val = 0x18;
            break;
        case 4:  // 2
            reg55val = 0x30;
            break;
    }
    SCCB_WR_Reg(0x55, reg55val);  // ????
}

// ?????
// 0:-2
// 1:-1
// 2,0
// 3,1
// 4,2
void OV7670_Contrast(u8 contrast)
{
    u8 reg56val = 0x40;  // ??contrast=2
    switch(contrast)
    {
        case 0:  // -2
            reg56val = 0x30;
            break;
        case 1:  // -1
            reg56val = 0x38;
            break;
        case 3:  // 1
            reg56val = 0x50;
            break;
        case 4:  // 2
            reg56val = 0x60;
            break;
    }
    SCCB_WR_Reg(0x56, reg56val);  // ?????
}

// ????
// 0:????
// 1:??
// 2:??
// 3:???
// 4:???
// 5:???
// 6:??
void OV7670_Special_Effects(u8 eft)
{
    u8 reg3aval = 0x04;  // ???????
    u8 reg67val = 0xC0;
    u8 reg68val = 0x80;
    
    switch(eft)
    {
        case 1:  // ??
            reg3aval = 0x24;
            reg67val = 0x80;
            reg68val = 0x80;
            break;
        case 2:  // ??
            reg3aval = 0x14;
            reg67val = 0x80;
            reg68val = 0x80;
            break;
        case 3:  // ???
            reg3aval = 0x14;
            reg67val = 0xC0;
            reg68val = 0x80;
            break;
        case 4:  // ???
            reg3aval = 0x14;
            reg67val = 0x40;
            reg68val = 0x40;
            break;
        case 5:  // ???
            reg3aval = 0x14;
            reg67val = 0x80;
            reg68val = 0xC0;
            break;
        case 6:  // ??
            reg3aval = 0x14;
            reg67val = 0xA0;
            reg68val = 0x40;
            break;
    }
    
    SCCB_WR_Reg(0x3A, reg3aval);  // TSLB??
    SCCB_WR_Reg(0x68, reg67val);  // MANU,??U?
    SCCB_WR_Reg(0x67, reg68val);  // MANV,??V?
}

// ????????
// ?QVGA??
void OV7670_Window_Set(u16 sx, u16 sy, u16 width, u16 height)
{
    u16 endx;
    u16 endy;
    u8 temp;
    
    endx = sx + width * 2;  // V*2
    endy = sy + height * 2;
    if(endy > 784) endy -= 784;
    
    temp = SCCB_RD_Reg(0x03);  // ??Vref????
    temp &= 0xF0;
    temp |= ((endx & 0x03) << 2) | (sx & 0x03);
    SCCB_WR_Reg(0x03, temp);   // ??Vref?start?end???2?
    SCCB_WR_Reg(0x19, sx >> 2);  // ??Vref?start?8?
    SCCB_WR_Reg(0x1A, endx >> 2); // ??Vref?end??8?

    temp = SCCB_RD_Reg(0x32);  // ??Href????
    temp &= 0xC0;
    temp |= ((endy & 0x07) << 3) | (sy & 0x07);
    SCCB_WR_Reg(0x17, sy >> 3);  // ??Href?start?8?
    SCCB_WR_Reg(0x18, endy >> 3); // ??Href?end??8?
}

// ??????
void OV7670_Effects_Set(void)
{
    u8 lightmode = 0, effect = 0;
    s8 saturation = 4, brightness = 0, contrast = 0;

    OV7670_Light_Mode(lightmode);
    OV7670_Color_Saturation(saturation);
    OV7670_Brightness(brightness);
    OV7670_Contrast(contrast);
    OV7670_Special_Effects(effect);
    // OV7670_Window_Set(184,10,320,240); // ???? 320*240 ?x?
    OV7670_Window_Set(12, 176, 240, 320); // ????
}

ov7670.h

#ifndef _OV7670_H
#define _OV7670_H
#include "stm32f4xx.h"
#include "sys.h"
#include "sccb.h"

#include <stdint.h>  // ???????????

typedef uint8_t  u8;   // ???8???
typedef uint16_t u16;  // ???16???
typedef uint32_t u32;  // ???32???
//     
//????????guanfu_wang??
//ALIENTEK??STM32???V3
//OV7670 ????       
//????@ALIENTEK
//????:www.openedv.com
//????:2015/1/18
//??:V1.0                                          
//

// ???????F407?????
#define OV7670_VSYNC    PAin(8)         //??????IO
#define OV7670_WRST     PBout(0)        //????? 
#define OV7670_WREN     PBout(12)       //??FIFO??

// F4????BSRR?BRR??????F1??
#define OV7670_RCK_H    GPIOA->BSRRH=1<<11 //??????????
#define OV7670_RCK_L    GPIOA->BSRRL=1<<11  //??????????

#define OV7670_RRST     PBout(1)        //?????
#define OV7670_CS       PAout(15)       //????(OE)

// ??????????????
#define OV7670_DATA     (GPIOA->IDR & 0x00FF)  //??????
                                    
// ????                                 
u8   OV7670_Init(void);                  
void OV7670_Light_Mode(u8 mode);
void OV7670_Color_Saturation(u8 sat);
void OV7670_Brightness(u8 bright);
void OV7670_Contrast(u8 contrast);
void OV7670_Special_Effects(u8 eft);
void OV7670_Window_Set(u16 sx, u16 sy, u16 width, u16 height);
void OV7670_Effects_Set(void);

#endif

main.c

#include "stm32f4xx.h"
#include "delay.h"
#include "lcd.h"
#include "spi.h"
#include "test.h"
#include "GUI.h"
#include "ov7670.h"
#include "sccb.h"
#include "usart.h"
#include "timer.h"
#include "exti.h"
#include "stm32f4xx_gpio.h"

extern u8 ov_sta;  // 在 exit.c 中定义
extern u8 ov_frame;  // 在 time.c 中定义

// 更新LCD显示
void camera_refresh(void) {
    u32 j;
    u16 color;

    if (ov_sta) {  // 有新的一帧图像捕获成功
        LCD_direction(1);
        LCD_SetWindows(0, 0, 319, 239);

        OV7670_RRST = 0;  // 开始复位读指针
        OV7670_RCK_L;
        OV7670_RCK_H;
        OV7670_RCK_L;
        OV7670_RRST = 1;  // 复位读指针结束
        OV7670_RCK_H;

        for (j = 0; j < 76800; j++) {  // 读取数据
            OV7670_RCK_L;
            color = GPIOA->IDR & 0xFF;  // 读数据
            OV7670_RCK_H;
            color <<= 8;

            OV7670_RCK_L;
            color |= GPIOA->IDR & 0xFF;  // 读数据
            OV7670_RCK_H;
            Lcd_WriteData_16Bit(color);
        }

        EXTI_ClearITPendingBit(EXTI_Line8);  // 清除中断标志位
        ov_sta = 0;  // 开始下一次采集
        ov_frame++;
    }
}

// 通过串口发送图像
void camera_refresh_1(void) {
    u32 j;
    u8 data1, data2;

    if (ov_sta) {  // 有新的一帧图像捕获成功
        OV7670_RRST = 0;  // 开始复位读指针
        OV7670_RCK_L;
        OV7670_RCK_H;
        OV7670_RCK_L;
        OV7670_RRST = 1;  // 复位读指针结束
        OV7670_RCK_H;

        printf("%c", 0x01);  // 帧起始标记
        printf("%c", 0xFE);

        for (j = 0; j < 76800; j++) {  // 读取数据
            OV7670_RCK_L;
            data1 = GPIOA->IDR & 0xFF;  // 读数据
            OV7670_RCK_H;

            OV7670_RCK_L;
            data2 = GPIOA->IDR & 0xFF;  // 读数据
            OV7670_RCK_H;

            // 使用printf可能会导致数据发送速度较慢,这里改用直接发送
            while(!(USART1->SR & USART_SR_TXE));
            USART1->DR = data1;
            
            while(!(USART1->SR & USART_SR_TXE));
            USART1->DR = data2;
        }

        printf("%c", 0xFE);  // 帧结束标记
        printf("%c", 0x01);

        EXTI_ClearITPendingBit(EXTI_Line8);  // 清除中断标志位
        ov_sta = 0;  // 开始下一次采集
        ov_frame++;
    }
}

int main(void) {
    // 系统初始化
    SystemInit();  // 配置RCC,配置系统时钟为72MHz
    delay_init(72);  // 延时初始化
    SPI2_Init();  // 配置SPI2接口类型
    LCD_Init();  // 初始化液晶屏
      // 配置串口,使用适中波特率以提高稳定性
    uart_init(460800);  // 使用460800波特率初始化串口

    // 初始化OV7670
    while (OV7670_Init()) {
        LCD_ShowString(100, 20, 16, "ERROR", 1);
        delay_ms(200);
        delay_ms(200);
    }

    LCD_ShowString(200, 200, 16, "ok", 1);
    delay_ms(1500);

    LCD_Clear(BLACK);
    EXTI8_Init();  // 使能帧中断
    OV7670_Effects_Set();  // 设置OV7670效果
    OV7670_CS = 0;  // 使能摄像头

    LCD_Clear(BLACK);

    // 主循环
    while (1) {
        // 通过串口传输图像数据到电脑端
        camera_refresh_1();  
        
        // 也可以在LCD上显示图像(可选)
        // camera_refresh();
    }
}

本地接收脚本

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
OV7670摄像头图像接收显示脚本
用于接收STM32通过串口发送的OV7670摄像头图像数据并显示
"""

import serial
import numpy as np
import cv2
import time
import argparse
from threading import Thread

# 图像参数 - OV7670默认为RGB565格式
IMG_WIDTH = 320
IMG_HEIGHT = 240

class OV7670Viewer:
    def __init__(self, port, baudrate=921600):
        """
        初始化OV7670图像查看器
        
        参数:
            port: 串口端口名
            baudrate: 波特率,默认为921600 (可根据实际STM32设置调整)
        """
        self.port = port
        self.baudrate = baudrate
        self.ser = None
        self.running = False
        self.frame = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
        self.new_frame = False
        
    def connect(self):
        """连接到串口设备"""
        try:
            self.ser = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE,
                timeout=1
            )
            print(f"已连接到 {self.port} (波特率: {self.baudrate})")
            return True
        except serial.SerialException as e:
            print(f"无法连接串口: {e}")
            return False
    
    def close(self):
        """关闭串口连接"""
        if self.ser and self.ser.is_open:
            self.ser.close()
            print("串口连接已关闭")
    
    def start_capture(self):
        """开始捕获图像"""
        self.running = True
        self.capture_thread = Thread(target=self._capture_loop)
        self.capture_thread.daemon = True
        self.capture_thread.start()
        
    def stop_capture(self):
        """停止捕获图像"""
        self.running = False
        if hasattr(self, 'capture_thread'):
            self.capture_thread.join(timeout=1.0)
    
    def _rgb565_to_rgb888(self, byte1, byte2):
        """
        将RGB565格式转换为RGB888
        
        参数:
            byte1, byte2: RGB565的两个字节
            
        返回:
            (r, g, b): RGB888元组
        """
        rgb = (byte1 << 8) | byte2
        
        r = (rgb & 0xF800) >> 8  # 取高5位作为红色
        g = (rgb & 0x07E0) >> 3  # 取中间6位作为绿色
        b = (rgb & 0x001F) << 3  # 取低5位作为蓝色
        
        return (r, g, b)
    
    def _capture_loop(self):
        """捕获图像循环"""
        buffer = bytearray()
        frame_start_detected = False
        frame_end_detected = False
        frame_data = bytearray()
        
        while self.running:
            try:
                if self.ser.in_waiting:
                    # 读取可用数据
                    data = self.ser.read(self.ser.in_waiting)
                    buffer.extend(data)
                    
                    # 查找帧开始标记 (0x01 0xFE)
                    if not frame_start_detected:
                        start_index = buffer.find(b'\x01\xFE')
                        if start_index >= 0:
                            buffer = buffer[start_index + 2:]  # 跳过开始标记
                            frame_start_detected = True
                            frame_data = bytearray()
                    
                    # 如果已经找到帧开始,查找帧结束标记 (0xFE 0x01)
                    if frame_start_detected:
                        end_index = buffer.find(b'\xFE\x01')
                        if end_index >= 0:
                            # 添加结束标记前的所有数据
                            frame_data.extend(buffer[:end_index])
                            
                            # 处理完整帧
                            self._process_frame(frame_data)
                            
                            # 重置为下一帧做准备
                            buffer = buffer[end_index + 2:]  # 跳过结束标记
                            frame_start_detected = False
                        else:
                            # 还没找到帧结束标记,但已经有足够多的数据,可能是因为缓冲区中有部分帧
                            if len(buffer) > IMG_WIDTH * IMG_HEIGHT * 2 + 100:  # 预留空间用于查找结束标记
                                # 丢弃过多的数据,避免缓冲区过大
                                buffer = buffer[-100:]
                                frame_start_detected = False
                            elif frame_start_detected:
                                # 继续收集此帧的数据
                                frame_data.extend(buffer)
                                buffer = bytearray()
                
                time.sleep(0.001)  # 短暂休眠,避免CPU占用过高
                
            except Exception as e:
                print(f"捕获过程出错: {e}")
                time.sleep(0.5)  # 出错后暂停一下,避免连续报错
    
    def _process_frame(self, data):
        """
        处理完整帧的数据
        
        参数:
            data: 图像数据字节数组
        """
        try:
            # 检查数据是否足够
            if len(data) < IMG_WIDTH * IMG_HEIGHT * 2:
                print(f"帧数据不完整: {len(data)}/{IMG_WIDTH * IMG_HEIGHT * 2}")
                return
            
            # 创建新的图像帧
            new_frame = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)
            
            # 逐像素填充图像
            for y in range(IMG_HEIGHT):
                for x in range(IMG_WIDTH):
                    idx = (y * IMG_WIDTH + x) * 2
                    if idx + 1 < len(data):
                        # 从RGB565格式转换为RGB888
                        r, g, b = self._rgb565_to_rgb888(data[idx], data[idx+1])
                        new_frame[y, x] = [b, g, r]  # OpenCV使用BGR顺序
            
            # 更新图像
            self.frame = new_frame
            self.new_frame = True
            
        except Exception as e:
            print(f"处理帧出错: {e}")
            
    def display(self):
        """显示摄像头图像"""
        cv2.namedWindow("OV7670 Camera", cv2.WINDOW_NORMAL)
        
        try:
            last_time = time.time()
            frame_count = 0
            
            while True:
                if self.new_frame:
                    cv2.imshow("OV7670 Camera", self.frame)
                    self.new_frame = False
                    
                    # 计算帧率
                    frame_count += 1
                    current_time = time.time()
                    if current_time - last_time >= 1.0:
                        fps = frame_count / (current_time - last_time)
                        print(f"FPS: {fps:.2f}")
                        frame_count = 0
                        last_time = current_time
                
                key = cv2.waitKey(1) & 0xFF
                if key == ord('q') or key == 27:  # 'q' 或 ESC 键退出
                    break
                elif key == ord('s'):  # 's' 键保存图像
                    timestamp = time.strftime("%Y%m%d_%H%M%S")
                    filename = f"ov7670_image_{timestamp}.png"
                    cv2.imwrite(filename, self.frame)
                    print(f"图像保存为 {filename}")
                
        except KeyboardInterrupt:
            pass
        finally:
            cv2.destroyAllWindows()

def main():
    parser = argparse.ArgumentParser(description='OV7670摄像头图像显示程序')
    parser.add_argument('-p', '--port', required=True, help='串口端口名')
    parser.add_argument('-b', '--baudrate', type=int, default=921600, 
                        help='波特率 (默认: 921600)')
    args = parser.parse_args()
    
    viewer = OV7670Viewer(port=args.port, baudrate=args.baudrate)
    
    if viewer.connect():
        viewer.start_capture()
        try:
            viewer.display()
        finally:
            viewer.stop_capture()
            viewer.close()

if __name__ == "__main__":
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值