目录
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()