[作品提交] 【2024 DigiKey 创意大赛】 键鼠统一管家——作品提交

lzhan   2024-10-31 23:34 楼主
键鼠统一管家
作者:lzhan
一、作品简介
本项目为键鼠统一管家,其中使用NUCLEO-L476RG开发板作为主控板,负责与OPENMV H7、自制PCB进行通信,同时作为USB Host用于驱动用户端的键盘和鼠标;OPENMV H7用于采集图像并进行处理,来判断人脸的朝向,将结果上报给主控板;自制PCB包含两个USB Device,根据朝向判决结果,将键鼠数据转发到对应的PC主机上。
设备展示
wd_233250p6oifrgdqpztlosl.png
wd_233251xwvwjmf5vjanf66n.jpg
该产品所使用的物料如下:
物料名称 型号 功能
OPENMV模组 OPENMV CAM H7 采集图像、面部朝向判断
STM32 NUCLEO-L476RG USB Host、主控板
STM32 STM32F070 USB Device
二、系统框图
该系统的整体框图如下所示。每个PC电脑各有一个显示器,并排成一定夹角放在一起,将该系统放在两个显示器之间。OPENMV H7和NUCLEO-L476RG都是使用USB各自独立进行供电,自制PCB由NUCLEO-L476RG进行供电。
wd_233251lszh3mlh1muc83jm.jpg
三、各部分功能说明
OPENMV H7开发板用来实现人脸检测,并根据人脸中心位置与预设标定位置的比较来判断人脸朝向。
当人脸朝左时,蓝色LED亮,同时拉高GPIO P1为高电平,拉低GPIO P2为低电平。
wd_233251pheme6949zxxeui6.jpg
当人脸朝右时,红色LED亮,同时拉高GPIO P2为高电平,拉低GPIO P1为低电平。
wd_233251k4h9x4qc9xei4ztw.jpg
OPENMV H7在实现时使用到了OPENMV H7的摄像头、LED和GPIO共三种外设。其实现过程如下:
  1. import摄像头所需要的sensor库、延时所需要的time库、图像处理所需要的image库、GPIO所需要的pyb库和LED所需要的machine库。
  2. 对Sensor进行复位、设置Sensor对比度、设置Sensor增益、设置Sensor捕捉图像大小、设置Sensor输出格式
  3. 初始化标定中点midpoint为0;初始化方向状态变量stat为1,1标志头向左偏,2标志头向右偏;初始化现在中点nowpoint为0。
  4. 初始化蓝色LED灯作为头部转向为左(蓝的拼音为Lan,取L,近似为Left)的指示;初始化红色LED灯作为头部转向为右(红的英文为Red,取R,近似为Right)的指示。
  5. 初始化GPIO P1和P2,设置其为输出,并设置其电平为低电平。
  6. 加载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数据,然后对应主机从中获得对应的数据,从而完成一套键鼠控制两台电脑的功能。
wd_233251kmrdzyccpfexq61k.png
PCB顶层
wd_233251uxleqy4q6qs4rqrk.png
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 创意大赛】物料开箱 https://bbs.eeworld.com.cn/thread-1290478-1-1.html
【2024 DigiKey 创意大赛】进度分享 https://bbs.eeworld.com.cn/thread-1292099-1-1.html
【2024 DigiKey 创意大赛】 键鼠统一管家(1) 软件算法及实现 https://bbs.eeworld.com.cn/thread-1297454-1-1.html
【2024 DigiKey 创意大赛】 键鼠统一管家(2) OPENMV调试 https://bbs.eeworld.com.cn/thread-1297816-1-1.html
【2024 DigiKey 创意大赛】 键鼠统一管家(3) 扩展板制作 https://bbs.eeworld.com.cn/thread-1297918-1-1.html
七、其他
附件为word版文档

本帖最后由 lzhan 于 2024-11-1 13:40 编辑

回复评论 (1)

一套键鼠控制两个电脑,创意不错   

在爱好的道路上不断前进,在生活的迷雾中播撒光引
点赞  2024-11-1 11:22
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复