【2024 DigiKey 创意大赛】 键鼠统一管家——作品提交
[复制链接]
本帖最后由 lzhan 于 2024-11-1 13:40 编辑
键鼠统一管家
作者:lzhan
一、作品简介
本项目为键鼠统一管家,其中使用NUCLEO-L476RG开发板作为主控板,负责与OPENMV H7、自制PCB进行通信,同时作为USB Host用于驱动用户端的键盘和鼠标;OPENMV H7用于采集图像并进行处理,来判断人脸的朝向,将结果上报给主控板;自制PCB包含两个USB Device,根据朝向判决结果,将键鼠数据转发到对应的PC主机上。
设备展示
该产品所使用的物料如下:
物料名称 |
型号 |
功能 |
OPENMV模组 |
OPENMV CAM H7 |
采集图像、面部朝向判断 |
STM32 |
NUCLEO-L476RG |
USB Host、主控板 |
STM32 |
STM32F070 |
USB Device |
二、系统框图
该系统的整体框图如下所示。每个PC电脑各有一个显示器,并排成一定夹角放在一起,将该系统放在两个显示器之间。OPENMV H7和NUCLEO-L476RG都是使用USB各自独立进行供电,自制PCB由NUCLEO-L476RG进行供电。
三、各部分功能说明
OPENMV H7开发板用来实现人脸检测,并根据人脸中心位置与预设标定位置的比较来判断人脸朝向。
当人脸朝左时,蓝色LED亮,同时拉高GPIO P1为高电平,拉低GPIO P2为低电平。
当人脸朝右时,红色LED亮,同时拉高GPIO P2为高电平,拉低GPIO P1为低电平。
OPENMV H7在实现时使用到了OPENMV H7的摄像头、LED和GPIO共三种外设。其实现过程如下:
- import摄像头所需要的sensor库、延时所需要的time库、图像处理所需要的image库、GPIO所需要的pyb库和LED所需要的machine库。
- 对Sensor进行复位、设置Sensor对比度、设置Sensor增益、设置Sensor捕捉图像大小、设置Sensor输出格式
- 初始化标定中点midpoint为0;初始化方向状态变量stat为1,1标志头向左偏,2标志头向右偏;初始化现在中点nowpoint为0。
- 初始化蓝色LED灯作为头部转向为左(蓝的拼音为Lan,取L,近似为Left)的指示;初始化红色LED灯作为头部转向为右(红的英文为Red,取R,近似为Right)的指示。
- 初始化GPIO P1和P2,设置其为输出,并设置其电平为低电平。
- 加载Haar人脸检测模型,然后进行人脸检测。当检测到人脸时,根据人脸的左坐标和宽度计算得到从此时脸的中点坐标。若此时脸的中点坐标小于等于标定中点坐标且stat等于1,说明脸向右偏,则需要通知处理模块,因此先将LED1关闭,然后打开LED2,将stat置为2,拉低GPIO P1,拉高GPIO P2;若此时脸的中点坐标大于标定中点坐标且stat等于2,说明脸向左偏,则需要通知处理模块,因此先将LED2关闭,然后打开LED1,将stat置为1,拉低GPIO P2,拉高GPIO P2。
代码如下:
import sensor
import time
import image
import pyb
from machine import LED
global calib
def callback_p0(line):
calib = 1
def main():
sensor.reset()
sensor.set_contrast(8)
sensor.set_gainceiling(16)
sensor.set_framesize(sensor.HQVGA)
sensor.set_pixformat(sensor.GRAYSCALE)
calib = 0
midpoint = 0
stat = 1
nowpoint = 0
led1 = LED("LED_BLUE")
led2 = LED("LED_RED")
led3 = LED("LED_GREEN")
p0_h = pyb.Pin("P0", pyb.Pin.IN)
p1_h = pyb.Pin("P1", pyb.Pin.OUT_PP)
p2_h = pyb.Pin("P2", pyb.Pin.OUT_PP)
p1_h.low()
p2_h.low()
p0_ext = pyb.ExtInt(p0_h, pyb.ExtInt.IRQ_FALLING,
pyb.Pin.PULL_UP, callback_p0)
face_cascade = image.HaarCascade("frontalface", stages=25)
while True:
img = sensor.snapshot()
objects = img.find_features(face_cascade, threshold=1, scale_factor=1.5)
for face in objects:
if(calib):
nowpoint = (face[0]+face[2])/2
calib = 0
led3.on()
pyb.delay(5000)
led3.off()
nowpoint = (face[0]+face[2])/2
if((nowpoint <= midpoint) and (stat == 1)):
led1.off()
led2.on()
stat = 2
p1_h.low()
p2_h.high()
elif(nowpoint>midpoint) and (stat == 2):
led2.off()
led1.on()
stat = 1
p2_h.low()
p1_h.high()
NUCLEO-L476RG开发板用于实现USB Host,操作键鼠一体USB接收器,将USB HID报文中的数据转发到SPI输出。根据OPENMV的GPIO的电平进行处理:若GPIO1的电平为高,则说明此时人脸是朝向左侧的,此时对应左侧电脑的SPI slave (STM32F070,实现左测电脑的USB输入设备)片选信号置为有效,HID报文数据被SPI slave接收后通过USB端点发送出去;若GPIO2的电平为高,则说明此时人脸是朝向右侧的,另一个SPI slave的片选信号有效,HID报文被转发到另一个USB设备发出。
如果键盘鼠标是独立的(要两个USB口),则需要增加一个USB host来处理,就需要再用一块STM32L476或类似的带USB-OTG设备的MCU,将其收到的HID报文发送给主控的STM32L476. 因为大赛物料成本的限制,手上又有无线键鼠套装,就只用了一个USB host.
部分主要代码如下:
int main(void)
{
gpio_config();
GPIOB->OSPEEDR = 2<<30|2<<28|2<<26;
FLASH->ACR = FLASH_ACR_ICEN|FLASH_ACR_DCEN|FLASH_ACR_PRFTEN
|2<<FLASH_ACR_LATENCY_Pos; // 2 wait states, 32~48MHz
clk_config(); // without this, default SYSCLK is 4MHz
uart_setup();
if((RCC->CFGR & RCC_CFGR_SWS_Msk)!=RCC_CFGR_SWS_PLL)
{
uart_wstr("Clock setting to PLL failed. ");
}
spi_setup();
usb_setup();
RCC->APB1ENR1 |= RCC_APB1ENR1_TIM6EN;
__NOP();
TIM6->PSC = 479; // prescale to 100kHz
TIM6->EGR = TIM_EGR_UG; // generate update
TIM6->SR = 0; // clear interrupt flag
TIM6->DIER = TIM_DIER_UIE; // enable Interrupt
NVIC_EnableIRQ(TIM6_DAC_IRQn);
while(1)
{
__WFI();
}
}
自制PCB是一个两层板,其主要包含两个F070,用于作对应两台PC电脑的USB Device,其通过两个SPI与NUCLEO-L476RG相连接,接收转发的对应的USB数据,然后对应主机从中获得对应的数据,从而完成一套键鼠控制两台电脑的功能。
PCB顶层
PCB底层
部分主要代码如下:
int main(void)
{
int i;
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN|RCC_APB2ENR_SPI1EN;
gpio_config();
FLASH->ACR=FLASH_ACR_PRFTBS|FLASH_ACR_PRFTBE|FLASH_ACR_LATENCY;
SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
clock_setup();
config_spi_slave();
uart_setup();
uart_whex(SPI1->CR2);
uart_wstr(" F070 test\r\n");
NVIC_EnableIRQ(SPI1_IRQn);
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
USB->CNTR &= ~1; // remove PDWN
delay_ms(1);
USB->CNTR = 0; // no reset
USB->CNTR = USB_CNTR_CTRM|USB_CNTR_WKUPM|USB_CNTR_ERRM
|USB_CNTR_SUSPM|USB_CNTR_RESETM;
uart_wstr("Configured USB_CNTR\r\n");
NVIC_EnableIRQ(USB_IRQn);
USB->BCDR = USB_BCDR_DPPU; // pull-up resistor enable
while(1)
{
static char row=0;
__WFI();
if(ep0_state & 0x80) // request data processing
{
if(ep0_state==0x80) // SETUP phase
{
if(!setup_packet_service())
{
ep0_state=0;
uart_wstr("NOT_SUPPORTED [Type ");
uart_whexb(ep0_std_req->bmRequestType);
uart_wstr(" Request ");
uart_whexb(ep0_std_req->bRequest);
uart_wstr(" Val ");
uart_whexh(ep0_std_req->wValue);
uart_wstr(" Ind ");
uart_whexh(ep0_std_req->wIndex);
uart_wstr(" Len ");
uart_whexh(ep0_std_req->wLength);
uart_wstr("] ");
}
// ep0_state should be set to 1 or 2, if processed
}
else // OUT phase
{
ep0_state=4;
ep0_reenable_tx(0); //zero data, status stage
}
}
else
{
if(usb_address && ep0_state==0)
{
USB->DADDR = USB_DADDR_EF|usb_address;
usb_address=0;
}
}
}
}
四、作品源码
源码已经上传到下载中心,
五、作品功能演示视频
以下是我的作品演示:
六、项目总结
我本人非常荣幸能够参加本次大赛,通过这次大赛,让我能够有机会使用到OPENMV H7来完成项目,我从中学习到了如何使用MicroPython方法来进行嵌入式的开发与调试。感谢 @cruelfox 为作品提供的USB部分实现的技术支持。
由于平时事情比较繁琐,因此项目做的较为仓促,图像处理算法的复杂度一降再降。目前还存在以下的问题:只允许脑袋进行转向,若身子发生较大的移动,则相对位置关系被破坏,因此就无法获得正确的判别结果,存在一定限制。
帖子分享汇总:
【2024 DigiKey 创意大赛】 键鼠统一管家(3) 扩展板制作 https://bbs.eeworld.com.cn/thread-1297918-1-1.html
七、其他
附件为word版文档
|