pycharm控制STM32F103ZET6拍照并上位机接收显示(OV7670、照相机、STM32、TFTLCD)

基于STM32的照相机

准备工作

一、硬件及片上资源:

1,串口1(波特率:921600,PA9/PA10通过usb转ttl连接电脑,或者其他方法)上传图片数据至上位机

2,串口2(波特率:115200,PA2/PA3通过usb转ttl连接电脑,或者其他方法)控制拍照

3,2.8寸TFTLCD模块

4,按键KEY1(PE3)

5,SD卡

6,外部中断8(PA8,用于检测OV7670的帧信号)

7,定时器6(用于打印摄像头帧率)

8,带FIFO的OV7670摄像头模块

9、STM32F103ET6

10、USB转TTL模块两个

11、STLINK(其他下载器也可以:DSP、JTAG...)

二、软件:

1、pycharm

2、keil5-MDK

3、串口调试助手(XCOM)

三、连线:

在代码中都有。

最终效果

开机的时候先检测字库,然后检测SD卡根目录是否存在PHOTO文件夹,如果不存在则创建,如果创建失败,则报错(提示拍照功能不可用)。在找到SD卡的PHOTO文件夹后,开始初始化OV7670,在初始化成功之后,就一直在TFTLCD上显示OV7670拍到的内容。当上位机按下拍照时,进行拍照,此时DS1亮,照片通过串口发送至上位机,当DS1灭之后,拍照成功。(也可以自己改一改用板子的按键控制拍照)

1、实物图:

2、上位机效果:

一、下位机

代码过多过长这里只展示重要的:

1、主函数

复制代码
 int main(void)
 {	 
						 
	u8 res;	
	u8 *pname;				//带路径的文件名		   
	u16 i;	
    
	delay_init();	    	 //延时函数初始化	  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init1(921600);	 	//串口初始化为115200
    uart_init2(115200);	
 	usmart_dev.init(72);		//初始化USMART		
 	LED_Init();		  			//初始化与LED连接的硬件接口
	KEY_Init();					//初始化按键
	LCD_Init();			   		//初始化LCD    
	BEEP_Init();        		//蜂鸣器初始化	 
	W25QXX_Init();				//初始化W25Q128
 	my_mem_init(SRAMIN);		//初始化内部内存池
	exfuns_init();				//为fatfs相关变量申请内存  
 	f_mount(fs[0],"0:",1); 		//挂载SD卡 
 	f_mount(fs[1],"1:",1); 		//挂载FLASH. 
	EXTI8_Init();				//使能定时器捕获
	EXTIX_Init();
	POINT_COLOR=RED;      

	USART_SendData(USART2,0x31);
    while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET);//等待发送结束								   						    
 	pname=mymalloc(SRAMIN,30);	//为带路径的文件名分配30个字节的内存		    
 	while(pname==NULL)			//内存分配出错
 	{	    
		Show_Str(30,190,240,16,"内存分配失败!",16,0);
		delay_ms(200);				  
		LCD_Fill(30,190,240,146,WHITE);//清除显示	     
		delay_ms(200);				  
	}   											  
	while(OV7670_Init())//初始化OV7670
	{
		Show_Str(30,190,240,16,"OV7670 错误!",16,0);
		delay_ms(2000);
	    LCD_Fill(30,190,239,206,WHITE);
		delay_ms(2000);
	}
	delay_ms(10000);
 	Show_Str(30,190,200,16,"OV7670 normal",16,0);
	delay_ms(14444);	
    delay_ms(14444);										  
	
	OV7670_Light_Mode(0);//0
	OV7670_Color_Saturation(0);
	OV7670_Brightness(2);//0
	OV7670_Contrast(2);//0
	OV7670_Special_Effects(0);
	OV7670_Window_Set(12,176,240,320);	//设置窗口	  
  	OV7670_CS=0;				    		    
	LCD_Clear(BLACK);
 	while(1)
	{	
		if(Res_com2 == 0x31)
		{
		   delay_ms(1800);
           Res_com2 = 0;
           //Res_com = 0;
		   LED1=0;	//点亮DS1,提示正在拍照
		 
		   res=bmp_encode(pname,(lcddev.width-240)/2,(lcddev.height-320)/2,240,320,0);
		   Show_Str(40,130,240,12,"picture_capture_finish!",12,0);	
			LED1=1;//关闭DS1
			delay_ms(1800);//等待1.8秒钟
			LCD_Clear(BLACK);
		    //jjj = 0;
		}
		else 
		    delay_ms(5);
			camera_refresh();//更新显示
			i++;
			if(i==10000)//DS0闪烁.
			{
				i=0;
				LED0=!LED0;
			}
	}	   										    
}

2、OV7670初始化

复制代码
u8 OV7670_Init(void)
{
	u8 temp;
	u16 i=0;	  
	//设置IO
 	GPIO_InitTypeDef  GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO, ENABLE);	 //使能相关端口时钟
 
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_8; 	//PA8 输入 上拉
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_8);
		
 	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4;				 // 端口配置
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
 	GPIO_Init(GPIOB, &GPIO_InitStructure);
 	GPIO_SetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4);	

	
	GPIO_InitStructure.GPIO_Pin  = 0xff; //PC0~7 输入 上拉
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
 	GPIO_Init(GPIOC, &GPIO_InitStructure);
	 
	
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_6;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOD, &GPIO_InitStructure);
	GPIO_SetBits(GPIOD,GPIO_Pin_6);
	
	GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_14|GPIO_Pin_15;  
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 	GPIO_Init(GPIOG, &GPIO_InitStructure);
	GPIO_SetBits(GPIOG,GPIO_Pin_14|GPIO_Pin_15);
	
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);	//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
} 

二、上位机

1、控制拍照

复制代码
# 和另一个.py文件一起运行,点击可视化界面的拍照即可拍照
import serial
import time
import tkinter as tk

def send_command():
    command_to_send = b'\x31\r\n'
    ser.write(command_to_send)
    # You can add any additional actions or updates here

# Create the serial connection
ser = serial.Serial('COM13', 115200)
time.sleep(2)

# Create the Tkinter window
window = tk.Tk()
window.title("Serial control take photos")

# Create a button to send the command
send_button = tk.Button(window, text="拍照", command=send_command)
send_button.pack(pady=20)

# Run the Tkinter main loop
window.mainloop()

# Close the serial connection when the window is closed
ser.close()

2、接收图片数据

复制代码
# 用波特率为921600的串口接收下位机上传的图片数据,接受的图片会有一点色彩问题,怀疑是传输出现的问题,用高斯滤波就可以基本滤除。
# 注意要连接好串口,板子上好电,这个代码才能运行不然报错找不到串口
import serial
import struct
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import cv2
import os

ser = serial.Serial('COM5', 921600)

# 初始化一个空的二维数组,用于存储接收到的数据
received_data = np.zeros((320, 240), dtype=np.uint16)

# 初始化图像计数器
image_counter = 1
# 全局变量,保存当前索引
image_index = 0
# 设置图像保存目录
save_dir = "pic_receive"

# 如果目录不存在,则创建目录
if not os.path.exists(save_dir):
    os.makedirs(save_dir)

while True:
    # 初始化索引和计数器
    received_index = 0
    row = 0
    col = 0

    # 接收数据直到收到足够的数据
    print("可以发送数据")
    while received_index < 240 * 320:
        # 读取两个字节的数据
        data = ser.read(2)

        # 解析uint16数据
        color_value = struct.unpack('>H', data)[0]  # '>H'表示大端字节序的uint16

        # 将数据存入二维数组
        received_data[row, col] = color_value
        col += 1
        received_index += 1

        # 判断是否接收完一行数据
        if col >= 240:
            col = 0
            row += 1

            # 如果接收完一帧数据,进行解析和显示
            if row >= 320:
                # 解析RGB565格式的数据为RGB888格式
                # 不知道什么原因发上来列发生错误,进行重组
                selected_columns1 = received_data[:, 0:47]
                selected_columns2 = received_data[:, 47:240]
                merged_array = np.concatenate((selected_columns2, selected_columns1), axis=1)

                rgb888_data = []
                for i in range(320):
                    for j in range(240):
                        color_value = merged_array[i, j]
                        r = (color_value & 0xF800) >> 8
                        g = (color_value & 0x07E0) >> 3
                        b = (color_value & 0x001F) << 3
                        rgb888_data.append((r, g, b))

                # 创建RGB888格式的图像对象
                image = Image.new('RGB', (240, 320))

                # 将RGB888格式的数据填充到图像对象中
                image.putdata(rgb888_data)

                # 保存图像到文件夹
                image_filename = os.path.join(save_dir, f"{image_counter:010d}.png")
                image.save(image_filename)

                # 增加图像计数器
                image_counter += 1

                # 显示图像
                #plt.imshow(image)
                #plt.show()
                # 重置二维数组,准备接收下一帧数据
                received_data = np.zeros((320, 240), dtype=np.uint16)
                row = 0
                col = 0
                # 清空串口接收缓冲区
                ser.flushInput()

三、资源获取

我用夸克网盘分享了「照相机+双串口+上位机接收并显示.rar」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。

链接:https://pan.quark.cn/s/125911f5def1

提取码:Za2E

相关推荐
秀秀更健康38 分钟前
stm32: 系统时钟如何配置为72Mhz
stm32·单片机·嵌入式硬件
X1A0RAN4 小时前
解决Pycharm中部分文件或文件夹被隐藏不展示问题
ide·python·pycharm
归零鸟6 小时前
WD Elements移动硬盘能识别出盘但不能出盘的修复记录
stm32·单片机·嵌入式硬件
追兮兮7 小时前
MCUQuickStart v1.1.0发布,一键生成Keil工程+RTOS模板
stm32·单片机·嵌入式硬件·freertos·gd32·keil5
rit84324997 小时前
STM32移植NES模拟器指南
stm32·单片机·嵌入式硬件
都在酒里7 小时前
STM32 I2C通信协议详解——标准库函数实现(通讯协议总结一)
stm32·嵌入式硬件·i2c
fengfuyao9858 小时前
STM32 HAL库实现串口DMA接收不定长数据
stm32·单片机·嵌入式硬件
yuan199978 小时前
STM32直流无刷电机六拍方波控制器程序
stm32·单片机·嵌入式硬件
番茄灭世神9 小时前
PN学堂GD32教程第21篇——WiFiIOT
c语言·stm32·单片机·嵌入式·gd32
数智工坊11 小时前
PyCharm 运行 Python 脚本总自动进 Test 模式?附 RT-DETRv2 依赖缺失终极排坑
开发语言·ide·人工智能·python·pycharm