简易摄像机制作
利用STM32和openmv制作简易摄像机
单片机部分
介绍
- 单片机在该项目中作为上位机控制openmv
- 单击按钮为拍摄照片,双击按钮为开启闪光灯拍照
即按下按钮后一秒内再按下可开启闪光灯
流程图部分
代码主要集中于中断,所以主函数的流程图就一笔带过
- 主函数
![img]()
- 中断部分
10:15线外部中断
定时器中断
初始化硬件部分
- 选择自己的芯片/开发板,这里以我自己的为例
![img]()
配置时钟
- 使能RCC,选择高速外部晶振
![img]()
- 将时钟树按如下方式配置,使APB2频率为84MHz
![img]()
配置GPIO
- 将PA1, PA2设为上拉推挽输出模式
- PC13设为下降沿触发外部中断模式
- 将PA5设置为浮空态输出模式
![img]()
- 使能10:15线外部中断
![img]()
配置定时器
- 将时钟源设置为内部时钟
- 将定时器3预分频设为840 - 1,重装载值设置为13000 - 1,向上计数
![img]()
即将上位机的摄像模式保持1.3秒,防止下位机检测到摄像信号时间过短
- 使能定时器3中断
![img]()
配置中断向量控制器
- 将优先级组设为1位
- 将定时器3中断设为1抢占优先级,0子优先级
- 将10:15线外部中断设为0抢占优先级,0子优先级
![img]()
代码部分
- 在外部10:15线中断函数中写入以下内容:
/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
//判断当前摄像机状态
if(shutting_mode){
if(!flash_mode){//判断是否开启闪光灯
flash_mode = true;
HAL_GPIO_WritePin(Flash_CMD_GPIO_Port, Flash_CMD_Pin, GPIO_PIN_SET);
}
}
else{
shutting_mode = true;//将状态设为摄影中
HAL_GPIO_WritePin(Shut_CMD_GPIO_Port, Shut_CMD_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
HAL_TIM_Base_Start_IT(&htim3);
}
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(B1_Pin);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
- 在定时器中断中写入以下内容:
/**
* @brief This function handles TIM3 global interrupt.
*/
void TIM3_IRQHandler(void)
{
/* USER CODE BEGIN TIM3_IRQn 0 */
if(flash_mode){//判断是否开启闪光灯
HAL_GPIO_WritePin(Flash_CMD_GPIO_Port, Flash_CMD_Pin, GPIO_PIN_RESET);
flash_mode = false;
}
shutting_mode = false;
//置低PA1,PA2电平
HAL_GPIO_WritePin(Shut_CMD_GPIO_Port, Shut_CMD_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
/* USER CODE END TIM3_IRQn 0 */
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
HAL_TIM_Base_Stop_IT(&htim3);//停止计时,注意一定要写在HAL_TIM_IRQHandler后方!
/* USER CODE END TIM3_IRQn 1 */
}
OpenMV部分
功能介绍
- OpenMV作为下位机,负责拍摄和开机相机的闪光灯(即OpenMV内置LED灯)
- P0脚负责接收摄像信号,高电平触发
- P1脚负责接收闪光灯信号,高电平处触发
- 在接收到摄像信号一秒内,若接收到闪光灯信号则开启闪光灯
- 每次拍摄完成后,将图像保存至SD卡
代码部分
- 在编译器中写入以下内容
"""
OpenMV相机控制 - 使用内置RGB LED作为补光灯
功能:
1. P1: 外部单片机控制补光灯(输入检测,高电平时点亮内置LED白光)
2. P0: 外部单片机触发拍照(输入检测,高电平持续1秒触发)
3. 图片保存到SD卡
4. 使用内置RGB LED同时点亮作为白光补光灯
"""
import sensor
import image
import time
import os
import pyb
from machine import Pin
# ==================== 系统初始化 ====================
print("=== OpenMV相机系统(内置LED补光)===")
print("引脚配置:")
print("P0: 拍照触发输入(高电平持续1秒)")
print("P1: 补光灯控制输入(高电平开启)")
print("补光灯: 使用内置RGB LED(同时点亮)")
print("-" * 40)
# 摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565) # 彩色图片
sensor.set_framesize(sensor.QVGA) # 320x240分辨率
sensor.skip_frames(time=2000) # 等待摄像头稳定
# ==================== GPIO配置 ====================
# 输入引脚(由外部单片机控制)
trigger_pin = Pin('P0', Pin.IN, Pin.PULL_DOWN) # 拍照触发输入
light_control_pin = Pin('P1', Pin.IN, Pin.PULL_DOWN) # 补光灯控制输入
# 内置LED配置
# OpenMV H7/H7 Plus有3个内置LED:
# LED(1): 红色 (引脚P6)
# LED(2): 绿色 (引脚P7)
# LED(3): 蓝色 (引脚P8)
# 同时点亮这三个LED可模拟白光
red_led = pyb.LED(1) # 红色LED
green_led = pyb.LED(2) # 绿色LED
blue_led = pyb.LED(3) # 蓝色LED
# 初始状态:关闭所有LED
red_led.off()
green_led.off()
blue_led.off()
# 补光灯状态
light_status = False
# ==================== 存储初始化 ====================
def init_storage():
"""初始化存储设备"""
print("初始化存储设备...")
try:
# 检查SD卡
if 'sd' in os.listdir('/'):
# 创建保存目录
save_dir = "/sd/captures"
try:
os.mkdir(save_dir)
print(f"创建目录: {save_dir}")
except OSError:
print(f"使用目录: {save_dir}")
# 检查可用空间
import uos
stat = uos.statvfs('/sd')
free_kb = (stat[0] * stat[3]) / 1024
print(f"SD卡可用空间: {free_kb:.1f} KB")
return save_dir
else:
print("警告: 未检测到SD卡,使用内部Flash")
return "/"
except Exception as e:
print(f"存储初始化失败: {e}")
return "/"
# 初始化存储
save_path = init_storage()
print(f"图片保存路径: {save_path}")
# ==================== 内置LED控制函数 ====================
def set_builtin_light(state):
"""控制内置LED作为补光灯"""
global light_status
light_status = state
if state:
# 同时点亮三个LED(红+绿+蓝 ≈ 白光)
red_led.on()
green_led.on()
blue_led.on()
print("内置补光灯: 开启")
else:
# 关闭所有LED
red_led.off()
green_led.off()
blue_led.off()
print("内置补光灯: 关闭")
def update_light_status():
"""根据P1输入状态更新内置补光灯"""
# 读取P1引脚状态
new_status = (light_control_pin.value() == 1)
# 状态变化时更新
if new_status != light_status:
set_builtin_light(new_status)
# ==================== 拍照功能 ====================
photo_count = 0
last_photo_time = 0
TRIGGER_DURATION = 1000 # 触发持续时间:1秒
def take_photo():
"""拍照并保存到存储"""
global photo_count, last_photo_time
# 防重复触发检查(至少间隔0.5秒)
current_time = time.ticks_ms()
if current_time - last_photo_time < 500:
print("防重复触发,请稍候...")
return False
last_photo_time = current_time
try:
# 记录补光灯原始状态
original_light_state = light_status
# 拍照
print("正在拍照...")
img = sensor.snapshot()
# 生成带时间戳的文件名
photo_count += 1
timestamp = time.localtime()
filename = f"IMG_{timestamp[0]:04d}{timestamp[1]:02d}{timestamp[2]:02d}_"
filename += f"{timestamp[3]:02d}{timestamp[4]:02d}{timestamp[5]:02d}.jpg"
# 完整保存路径
if save_path == "/":
full_path = filename
else:
full_path = f"{save_path}/{filename}"
# 保存图片(质量85%,兼顾质量和文件大小)
img.save(full_path, quality=85)
# 获取文件大小
try:
file_size = os.stat(full_path)[6]
print(f"✓ 已保存: {filename}")
print(f" 大小: {file_size/1024:.1f} KB")
print(f" 累计照片: {photo_count}")
except:
print(f"✓ 已保存: {filename}")
# 恢复补光灯原始状态
if not original_light_state:
time.sleep_ms(100) # 拍照后保持补光灯短暂亮起
set_builtin_light(False)
return True
except Exception as e:
print(f"✗ 拍照失败: {e}")
# 确保补光灯状态恢复
if not original_light_state:
set_builtin_light(False)
return False
# ==================== 主控制逻辑 ====================
print("\n系统初始化完成")
print("等待外部控制信号...")
print("控制方式:")
print("1. 设置P1为高电平 -> 打开内置补光灯")
print("2. 设置P0为高电平并保持1秒 -> 触发拍照")
print("-" * 30)
# 触发状态变量
trigger_active = False
trigger_start_time = 0
last_trigger_progress = 0
# 主循环
try:
while True:
# 1. 更新补光灯状态
update_light_status()
# 2. 检测拍照触发
trigger_state = trigger_pin.value()
if trigger_state == 1: # 检测到高电平
if not trigger_active:
# 开始触发计时
trigger_start_time = time.ticks_ms()
trigger_active = True
last_trigger_progress = 0
print("检测到拍照触发信号,请保持高电平1秒...")
# 计算持续时间
current_time = time.ticks_ms()
duration = time.ticks_diff(current_time, trigger_start_time)
# 显示进度(每10%更新一次)
current_progress = int(min(duration / TRIGGER_DURATION * 10, 10))
if current_progress > last_trigger_progress:
last_trigger_progress = current_progress
if current_progress < 10:
print(f"触发进度: {current_progress * 10}%")
# 触发条件满足
if duration >= TRIGGER_DURATION:
print("触发条件满足,正在拍照...")
if take_photo():
print("拍照完成")
else:
print("拍照失败")
# 触发完成,等待引脚释放
trigger_active = False
time.sleep_ms(100) # 短暂延时
# 等待引脚变为低电平
while trigger_pin.value() == 1:
time.sleep_ms(10)
print("等待下一次触发...")
else: # 低电平
if trigger_active:
# 触发中断
trigger_active = False
if time.ticks_diff(time.ticks_ms(), trigger_start_time) > 100:
print("触发中断,重新等待...")
# 3. 短暂延时,降低CPU使用率
time.sleep_ms(20)
except KeyboardInterrupt:
print("\n手动停止程序")
except Exception as e:
print(f"\n程序异常: {e}")
finally:
# 确保补光灯关闭
set_builtin_light(False)
print("补光灯已关闭")
print("程序结束")
拼接线路
- 将单片机的PA1与OpenMV的P0连接
- 将单片机的PA2与OpenMV的P1连接
实验效果
- 单击单片机按钮,仅拍照(红灯为保存失败,因为没接SD卡)
![img]()
- 双击单片机按钮,开启闪光灯并拍照
![img]()
- 截获图片:
![img]()

利用STM32和openmv制作简易摄像机













浙公网安备 33010602011771号