0


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

标签: pycharm stm32 照相机

本文转载自: https://blog.csdn.net/m0_71523511/article/details/136309226
版权归原作者 geeoni 所有, 如有侵权,请联系我们删除。

“pycharm控制STM32F103ZET6拍照并上位机接收显示(OV7670、照相机、STM32、TFTLCD)”的评论:

还没有评论