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/8fac_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;
}
#endifu8 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复用为USART1GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); // GPIOA10复用为USART1// USART1端口配置GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // GPIOA9与GPIOA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; // 速度提高到100MHzGPIO_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; // 抢占优先级提高到1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级0NVIC_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 //�����������ֽ��� 200extern 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; //??IOGPIO_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; // ??PA8GPIO_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: //sunnyreg13val=0xE5;reg01val=0x5A;reg02val=0x5C;break; case 2: //cloudyreg13val=0xE5;reg01val=0x58;reg02val=0x60;break; case 3: //officereg13val=0xE5;reg01val=0x84;reg02val=0x4C;break; case 4: //homereg13val=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,2void OV7670_Color_Saturation(u8 sat)
{u8 reg4f5054val = 0x80; //????sat=2,?????????u8 reg52val = 0x22;u8 reg53val = 0x5E;switch(sat){case 0: //-2reg4f5054val = 0x40; reg52val = 0x11;reg53val = 0x2F; break;case 1: //-1reg4f5054val = 0x66; reg52val = 0x1B;reg53val = 0x4B; break;case 3: //1reg4f5054val = 0x99; reg52val = 0x28;reg53val = 0x71; break;case 4: //2reg4f5054val = 0xC0; reg52val = 0x33;reg53val = 0x8D; break;}// ??OV7670?????SCCB_WR_Reg(0x4F, reg4f5054val); //??????1SCCB_WR_Reg(0x50, reg4f5054val); //??????2SCCB_WR_Reg(0x51, 0x00); //??????3SCCB_WR_Reg(0x52, reg52val); //??????4SCCB_WR_Reg(0x53, reg53val); //??????5SCCB_WR_Reg(0x54, reg4f5054val); //??????6SCCB_WR_Reg(0x58, 0x9E); //MTXS
}// ????
// 0:-2
// 1:-1
// 2,0
// 3,1
// 4,2
void OV7670_Brightness(u8 bright)
{u8 reg55val = 0x00; // ??bright=2switch(bright){case 0: // -2reg55val = 0xB0;break;case 1: // -1reg55val = 0x98;break;case 3: // 1reg55val = 0x18;break;case 4: // 2reg55val = 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=2switch(contrast){case 0: // -2reg56val = 0x30;break;case 1: // -1reg56val = 0x38;break;case 3: // 1reg56val = 0x50;break;case 4: // 2reg56val = 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*2endy = 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,配置系统时钟为72MHzdelay_init(72); // 延时初始化SPI2_Init(); // 配置SPI2接口类型LCD_Init(); // 初始化液晶屏// 配置串口,使用适中波特率以提高稳定性uart_init(460800); // 使用460800波特率初始化串口// 初始化OV7670while (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 = 240class OV7670Viewer:def __init__(self, port, baudrate=921600):"""初始化OV7670图像查看器参数:port: 串口端口名baudrate: 波特率,默认为921600 (可根据实际STM32设置调整)"""self.port = portself.baudrate = baudrateself.ser = Noneself.running = Falseself.frame = np.zeros((IMG_HEIGHT, IMG_WIDTH, 3), dtype=np.uint8)self.new_frame = Falsedef 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 Trueexcept serial.SerialException as e:print(f"无法连接串口: {e}")return Falsedef close(self):"""关闭串口连接"""if self.ser and self.ser.is_open:self.ser.close()print("串口连接已关闭")def start_capture(self):"""开始捕获图像"""self.running = Trueself.capture_thread = Thread(target=self._capture_loop)self.capture_thread.daemon = Trueself.capture_thread.start()def stop_capture(self):"""停止捕获图像"""self.running = Falseif 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) | byte2r = (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 = Falseframe_end_detected = Falseframe_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 = Trueframe_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 = Falseelse:# 还没找到帧结束标记,但已经有足够多的数据,可能是因为缓冲区中有部分帧if len(buffer) > IMG_WIDTH * IMG_HEIGHT * 2 + 100: # 预留空间用于查找结束标记# 丢弃过多的数据,避免缓冲区过大buffer = buffer[-100:]frame_start_detected = Falseelif 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) * 2if idx + 1 < len(data):# 从RGB565格式转换为RGB888r, g, b = self._rgb565_to_rgb888(data[idx], data[idx+1])new_frame[y, x] = [b, g, r] # OpenCV使用BGR顺序# 更新图像self.frame = new_frameself.new_frame = Trueexcept Exception as e:print(f"处理帧出错: {e}")def display(self):"""显示摄像头图像"""cv2.namedWindow("OV7670 Camera", cv2.WINDOW_NORMAL)try:last_time = time.time()frame_count = 0while True:if self.new_frame:cv2.imshow("OV7670 Camera", self.frame)self.new_frame = False# 计算帧率frame_count += 1current_time = time.time()if current_time - last_time >= 1.0:fps = frame_count / (current_time - last_time)print(f"FPS: {fps:.2f}")frame_count = 0last_time = current_timekey = cv2.waitKey(1) & 0xFFif key == ord('q') or key == 27: # 'q' 或 ESC 键退出breakelif 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:passfinally: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()