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

news2024/11/24 4:27:50

基于STM32的照相机

  • 准备工作
  • 最终效果
  • 一、下位机
    • 1、主函数
    • 2、OV7670初始化
  • 二、上位机
    • 1、控制拍照
    • 2、接收图片数据
  • 三、资源获取

准备工作

一、硬件及片上资源:
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1445678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Unity性能优化

非原创 &#xff0c;参考文章&#xff1a; 2024 腾讯游戏客户端面经 - 知乎 (zhihu.com) C#篇 1.Unity API GameObject.GetComponent Unity是基于组件的开发方式,所以GetComponent是一个高频使用的函数 每次调用GetComponent 时,Unity都要去遍历所有的组件来找到目标组件…

首次安装Mysql数据库

1、在mysql官网下载自己需要的版本 2、选择安装类型 3、 检查一下需求版本 4、 这里可能会弹出如下信息,先不用管这一步,点击Yes继续即可 5、 安装需要的环境,点击执行就可以,此过程会比较慢 如下就是全面安装完成了,点击next即可

寒假9-蓝桥杯训练

//轨道炮 #include<iostream> using namespace std; #include<algorithm> int logs[100010]; int main() {int n;cin >> n;for (int i 1;i < n;i){cin >> logs[i];}sort(logs 1, logs n 1);int ans 1000000000;for (int i 2;i < n;i){if (…

Java外卖小程序管理系统

技术架构&#xff1a; springboot ssm mysql redis 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; 商品管理&#xff1a;新增商品、所有商品 菜单管理&#xff1a;菜单管理、菜单分类 订单管理&#xff1a;订单总览&#xff08;包括未付款、已付款、已…

Python中的正则表达式(一)

在Python中&#xff0c;正则表达式是一种用于匹配和操作字符串的强大工具。正则表达式由一系列字符和特殊字符组成&#xff0c;用于定义搜索模式。 在Python中&#xff0c;我们使用内置的 re 模块来操作正则表达式。要使用正则表达式&#xff0c;我们首先需要导入 re 模块。 下…

没更新的日子也在努力呀,布局2024!

文章目录 ⭐ 没更新的日子也在努力呀⭐ 近期的一个状态 - 已圆满⭐ 又到了2024的许愿时间了⭐ 开发者要如何去 "创富" ⭐ 没更新的日子也在努力呀 感觉很久没有更新视频了&#xff0c;好吧&#xff0c;其实真的很久没有更新短视频了。最近的一两个月真的太忙了&#…

mysql经典4张表问题

1.数据库表结构关联图 2.问题&#xff1a; 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数3.查询平均成绩大于等于60分的同学的学生编号和学生姓名和平均成绩4、查询名字中含有"风"字的学生信息5、查询课程名称为"数学"&…

2024-02-08 Unity 编辑器开发之编辑器拓展1 —— 自定义菜单栏

文章目录 1 特殊文件夹 Editor2 在 Unity 菜单栏中添加自定义页签3 在 Hierarchy 窗口中添加自定义页签4 在 Project 窗口中添加自定义页签5 在菜单栏的 Component 菜单添加脚本6 在 Inspector 为脚本右键添加菜单7 加入快捷键8 小结 1 特殊文件夹 Editor ​ Editor 文件夹是 …

【网站项目】027学校运动会信息管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

C#,普洛尼克数(Pronic Number)的算法与源代码

1 普洛尼克数(pronic number) 普洛尼克数(pronic number)&#xff0c;也叫矩形数、欧波朗数(oblong number)&#xff0c;是两个连续非负整数的积&#xff0c;即mn*(n1)。第n个普洛尼克数侪是n个三角形数个两倍。 2 计算结果 3 源程序 using System; namespace Legalsoft.Tru…

VSCode python插件:找不到自定义包导致语法解析失败

众所周知&#xff0c;在python源码中&#xff0c;我们可以通过 sys.path.append("path-to-lib") 来为python解释器添加自定义包的寻找路径。 但是vscode的默认python插件可没法聪明到根据这句话去找这个包&#xff0c;这就会导致后续代码中使用了这个库的部分无法享…

人力资源智能化管理项目(day05:角色管理)

学习源码可以看我的个人前端学习笔记 (github.com):qdxzw/humanResourceIntelligentManagementProject 搭建页面结构 分页组件&#xff1a;设置layout&#xff0c;表示需要显示的内容&#xff0c;用逗号分隔&#xff0c;布局元素会依次显示。prev表示上一页&#xff0c;next为…

AI大模型学习笔记之四:生成式人工智能(AIGC)是如何工作的?

OpenAI 发布 ChatGPT 已经1年多了&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;也已经广为人知&#xff0c;我们常常津津乐道于 ChatGPT 和 Claude 这样的人工智能系统能够神奇地生成文本与我们对话&#xff0c;并且能够记忆上下文情境。 Midjunery和DALLE 这样的AI…

Qt【一】:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow&#xff1a;包含菜单栏、工具栏、状态栏 QWidget&#xff1a;普通的一个窗口&#xff0c;什么也不包括 QDialog&#xff1a;对话框&#xff0c;常用来做登录窗口、弹出窗口&#xff08;例如设置页面&#xff09; QDialog实现简易登录界面…

AI - 碰撞避免算法分析(VO/RVO)

VO/RVO VO和RVO的原理本身理解起来比较简单的&#xff0c;就是根据两个圆形的相对半径&#xff0c;相对速度&#xff0c;相对位置&#xff0c;求出碰撞区域&#xff0c;然后将速度移出碰撞区域。VO是双方都是当作对方速度不变的情况下&#xff0c;各自都将速度完整的移出了会碰…

Stream流学习笔记

Stream流 创建流中间操作1、filter2、map3、distinct4、sorted5、limit6、skip7、flatMap 终结操作1、forEach2、count3、max&min4、collect5、查找与匹配 创建流 单例集合&#xff1a;集合对象.stream() List<Integer> list new ArrayList<>(); Stream<…

Centos7安装nginx yum报错

Centos7安装nginx yum报错&#xff0c;yum源报错解决办法&#xff1a; 1、更新epel源后&#xff0c;出现yum报错 [roothacker117 ~]# yum install epel-release&#xff08;安装成功&#xff09; [roothacker117 ~]# yum install nginx&#xff08;安装失败&#xff0c;提示如…

极市平台 | 卡车货车、野外火灾、抽烟识别等开源数据集资源汇总

本文来源公众号“极市平台”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;卡车货车、野外火灾、抽烟识别等开源数据集资源汇总 最近正好在做这方面的项目。本文收集了一些卡车货车、抽烟和野外火灾等开源数据集资源&#xff0c;…

springboot182基于springboot的网上服装商城

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Stable Diffusion 模型下载:majicMIX sombre 麦橘唯美

本文收录于《AI绘画从入门到精通》专栏,专栏总目录:点这里。 文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十