2024寒假练 —基于SAMD21G17D MCU的综合开发平台板子上的触摸滑条与USB通信,实现对电脑的控制
该项目使用了SAMD21G17D MCU的综合开发平台板子上的触摸滑条与USB通信,实现了电脑的控制的设计,它的主要功能为:诸如台式机与笔记本电脑,键盘上会集成各种软件的控制快捷键,使用本板卡实现一个滑动控制器,在播放视频时可以用手指左右滑动控制视频进度条等变化。。
标签
开发板
USB
2024年寒假练
小月月
更新2024-04-02
168

2024年寒假练

—基于SAMD21G17D MCU的综合开发平台板子上的触摸滑条与USB通信,实现对电脑的控制

项目功能介绍

诸如台式机与笔记本电脑,键盘上会集成各种软件的控制快捷键,使用本板卡实现一个滑动控制器,在播放视频时可以用手指左右滑动控制视频进度条等变化。

一:环境配置

1. MPLAB X IDE v6.15 

Microchip Technology(微芯科技)公司提供的一个集成开发环境(IDE),主要用于微控制器的编程和开发。它支持多种微控制器系列,包括 PIC、AVR 和 SAM 等,并提供了一个全面的工具集,包括编辑器、编译器、调试器和模拟器等。

学习花了一点时间,这里推荐一下教学视频:https://www.bilibili.com/video/BV14v421y7SS/

https://www.bilibili.com/video/BV1uF4m1g7mn/

MPLAB X IDE 具有直观的用户界面和易于使用的功能,例如代码编辑器具有语法高亮和自动完成功能,以帮助开发者更高效地编写代码。此外,它还提供了调试工具,如断点设置、单步执行和变量观察,以便开发者可以在开发过程中调试和测试代码

2. 硬禾学堂电子森林平台

硬禾学堂为“寒假在家一起练”制作了一个平台,这平台正是我视频中演示用到的板子他的原理图如下,具体可以参考https://www.eetree.cn/project/2586

二:程序实现

程序均是使用C语言写SAM D21的。如果我的代码写得不是特别简洁干练,那……我就先不改啦~我相信,随着我的不断学习和成长,我的代码会变得越来越简洁、高效的!

设计思路:

传感器选择:板块自带电容触摸

滑动检测算法:可以通过检测传感器的电容变化来确定手指的滑动方向。

通信接口:确定SAMD21G17D MCU的综合开发平台有与电脑进行通信的接口。本次选择USB hid通信协议。

视频控制软件:电脑端视频控制软件一般都支持键盘左右键来控制视频播放进度,通过软件将接收来自滑动控制器的输入,并根据滑动方向控制视频进度条的移动。

 

MCU 编程:在 SAMD21G17D MCU 上编写代码,实现滑动检测算法、通信协议和与电脑视频控制软件的交互。

 

系统集成与测试:将硬件和软件进行集成,并进行系统测试,确保滑动控制器能够准确检测手指滑动,并将滑动动作转换为视频进度条的控制信号。

 

 

1. 模块介绍:

按键、电位器输入--------------以模拟信号的方式读取输入参数

8路RGB灯珠显示------------通过发送数字信号,实现对每个LED的精确控制。

0.96寸128*64OLED------------IIC接口通信,可直观显示各类传感器数据

蜂鸣器输出-------------------以PWM信号驱动,可作为警报源

电容触摸按键-------------------基于触摸函数库的电容触摸应用,并通过Data Visualizer 获取可视化触摸调试数据,可针对不同的触摸需求进行参数调整。

MCP9808数字温度传感器------可检测-40°C和+125°C之间的温度,具有用户可编程寄存器,可以为温度检测应用提供灵活性。这些寄存器支持用户可选的设置,例如关断或低功耗模式,以及温度报警窗口限制的规范和临界输出限制。

MCP7940N实时时钟/日历(RTCC)-----使用内部计数器跟踪小时、分钟、秒、天、数月、月年和数周的时间。警报可以在所有计数器上配置,包括数月。对于使用和配置,MCP7940N支持高达400 kHz的I2C通信。

与SAMD21G17D MCU开发板的接口

1. 触摸滑条的使用:

软件要求

MPLAB IDE V6.15

XC32 Compiler 4.3.0

MCC 5.3.7 with Harmony 3

需要安装的插件如下:

点击mcc图标,定义芯片引脚

接下来要定义绑定触摸相关设置如下:1. 完成的功能及完成的任务

结合板子上的触摸滑条与USB通信,实现对电脑播放视频进度条变化的控制;点按SAMD21G17D MCU的综合开发平台的电容触摸,实现对板子上LED灯光的亮灭的控制。

软件程序原理图示意图如下:

软件项目关键部分代码如下:

使用方式如下:

初始化配置

void SYS_Initialize ( void* data )

{
    /* MISRAC 2012 deviation block start */
    /* MISRA C-2012 Rule 2.2 deviated in this file.  Deviation record ID -  H3_MISRAC_2012_R_2_2_DR_1 */
    //控制寄存器,启用了读写访问
    NVMCTRL_REGS->NVMCTRL_CTRLB = NVMCTRL_CTRLB_RWS(3UL);
  //初始化端口相关的设置
    PORT_Initialize();
//初始化时钟系统
    CLOCK_Initialize();
    NVMCTRL_Initialize( );
    EVSYS_Initialize();
    SERCOM5_USART_Initialize();
    RTC_Initialize();
    /* MISRAC 2012 deviation block start */
    /* Following MISRA-C rules deviated in this block  */
    /* MISRA C-2012 Rule 11.3 - Deviation record ID - H3_MISRAC_2012_R_11_3_DR_1 */
    /* MISRA C-2012 Rule 11.8 - Deviation record ID - H3_MISRAC_2012_R_11_8_DR_1 */
    /* Initialize the USB device layer 初始化了 USB 设备*/
    sysObj.usbDevObject0 = USB_DEVICE_Initialize (USB_DEVICE_INDEX_0 , ( SYS_MODULE_INIT* ) & usbDevInitData);
    //初始化触摸相关的功能
touch_init();
    /* Initialize USB Driver */
    sysObj.drvUSBFSV1Object = DRV_USBFSV1_Initialize(DRV_USBFSV1_INDEX_0, (SYS_MODULE_INIT *) &drvUSBInit);
    /* MISRAC 2012 deviation block end 初始化触摸滑块相关的功能*/
    APP_TOUCH_SLIDER_Initialize();
    //初始化 USB 应用相关的功能
    APP_USB_Initialize();
    NVIC_Initialize();
    /* MISRAC 2012 deviation block end */
}

设置触摸进程:

APP_TOUCH_SLIDER_Tasks函数定义,它在C 语言中用于执行与触摸滑块应用程序相关的任务。这个函数使用一个switch语句来根据应用程序的当前状态执行不同的操作。

void APP_TOUCH_SLIDER_Tasks(void) {
    /* Check the application's current state. */
    switch (app_touch_sliderData.state) {
            /* Application's initial state. */
        case APP_TOUCH_SLIDER_STATE_INIT:
        {
            //应用程序初始化
            bool appInitialized = true;

            if (appInitialized) {

                app_touch_sliderData.state = APP_TOUCH_SLIDER_STATE_SERVICE_TASKS;
            }
            break;
        }
        case APP_TOUCH_SLIDER_STATE_SERVICE_TASKS:
        {
            /* call touch process function */
            //----------------------------------------

            touch_process(); //touch触摸库函数,处理触摸事件

            //如果为1,表示触摸测量已完成。将measurement_done_touch变量设置为0以清除完成标志,并调用touch_task_self函数处理触摸数据

            if (measurement_done_touch == 1u) { //measurement done flag
                measurement_done_touch = 0u;
                // process touch data
                touch_task_self(); //新增用户任务处理函数
            }
            break;
        }

检查变量time_to_measure_touch_var的值,如果为1,表示需要进行触摸采集。然后调用qtm_ptc_start_measurement_seq函数启动触摸采集序列,同时指定了一个测量完成回调函数qtm_measure_complete_callback。最后,函数返回触摸采集的结果touch_ret。

void APP_TOUCH_SLIDER_Tasks(void) {
    /* Check the application's current state. */
    switch (app_touch_sliderData.state) {
            /* Application's initial state. */
        case APP_TOUCH_SLIDER_STATE_INIT:
        {
            //应用程序初始化
            bool appInitialized = true;
            if (appInitialized) {
                app_touch_sliderData.state = APP_TOUCH_SLIDER_STATE_SERVICE_TASKS;
            }
            break;
        }
        case APP_TOUCH_SLIDER_STATE_SERVICE_TASKS:
        {
            /* call touch process function */
            //----------------------------------------
            touch_process(); //touch触摸库函数,处理触摸事件
            //如果为1,表示触摸测量已完成。将measurement_done_touch变量设置为0以清除完成标志,并调用touch_task_self函数处理触摸数据
            if (measurement_done_touch == 1u) { //measurement done flag
                measurement_done_touch = 0u;
                // process touch data
                touch_task_self(); //新增用户任务处理函数
            }
            break;
        }
/*

 * 这是一个名为touch_task_self的函数定义,它在 C 语言中用于执行与触摸任务相关的操作。这个函数会根据触摸事件执行不同的操作。
首先,声明了三个无符号整型变量key_status、scroller_status和scroller_position,并将它们初始化为0。
然后,通过调用get_sensor_state函数获取传感器状态,并使用KEY_TOUCHED_MASK掩码提取与触摸键相关的位,将结果存储在key_status变量中。接着,根据key_status的值决定是否点亮或熄灭 LED0 以指示触摸检测的状态。
接下来,保存上一时刻滑动条的位置到app_touch_sliderData.scroller_position_last变量中,并获取当前滑动条的状态。
然后,根据滑动条的状态,通过调用get_scroller_position函数获取滑动条的位置,并将结果存储在scroller_position变量中。然后,将scroller_position右移5位,以适应示例中的8位滑动条分辨率(可以根据实际需求修改)。然后,将当前滑动条位置存储在app_touch_sliderData.scroller_position_current变量中,并设置app_touch_sliderData.isTouchSliderActivated变量为true以指示滑动条已被激活。

最后,根据scroller_position的值执行不同的操作,以控制不同 LED 的亮灭状态。在示例中,当scroller_position为0时,点亮 LED0;为1时,熄灭 LED0;为2时,切换 LED0 的状态;为3时,点亮 LED3;为4时,熄灭 LED4;为5时,切换 LED5 的状态;为6时,点亮 LED6;为7时,熄灭 LED7;为其他值时,熄灭所有 LED。

 */

void touch_task_self(void) {
    uint8_t key_status = 0u;
    uint8_t scroller_status = 0u;
    uint16_t scroller_position = 0u;

    key_status = get_sensor_state(0) & KEY_TOUCHED_MASK;

    if (0u != key_status) {
        LED0_Clear(); //Touch detect
    }
else {
        LED0_Set(); //Touch No detect
    }
    app_touch_sliderData.scroller_position_last = app_touch_sliderData.scroller_position_current;
//保存上一时刻滑动条位置
    scroller_status = get_scroller_state(0);
    if (0u != scroller_status) {
//If slider is activated
        //Parse slider position
        scroller_position = get_scroller_position(0);
        scroller_position = scroller_position >> 5u; //Example: 8 bit scroller resolution. Modify as per requirement.
        app_touch_sliderData.scroller_position_current = scroller_position; //保存当前触摸按键值到全局变量中,便于其他函数/任务使用
        app_touch_sliderData.isTouchSliderActivated = true; //触摸按键触发

;        //        if (scroller_position1 > 43)
        //        if (scroller_position1 > 85)
        switch (scroller_position) {
            case 0:
                LED0_Set(); //LED0_OFF
                break;
            case 1:
                LED0_Clear(); //LED0_ON
                break;
            case 2:
                LED0_Toggle(); //LED0_Toggle
                break;
            case 3:
                LED0_Set(); //LED3_ON
                break;
            case 4:
                LED0_Clear(); //LED4_ON
                break;
            case 5:
                LED0_Toggle(); //LED5_ON
                break;
            case 6:
                LED0_Set(); //LED6_ON
                break;
            case 7:
                LED0_Clear(); //LED7_ON
                break;
            default:
                //LED_OFF
                break;
        }
    }
}

以下代码用于模拟USB键盘输入。它的主要功能是根据触摸滑块(滑动条上的元素)的当前位置和上一次的位置来发送键盘的左右箭头键事件。如果触摸滑块向左滑动,则发送左箭头键事件;如果向右滑动,则发送右箭头键事件。如果没有任何操作,将发送一个无事件消息

void APP_USB_EmulateKeyboard(void) {
    //检查开关是否被按下
    if (app_usbData.isSwitchPressed) {
        /* Clear the switch pressed flag */
        app_usbData.isSwitchPressed = false;
        /* If the switch was pressed, update the key counter and then add the key to the key code array. */
        app_usbData.key++;      

//根据当前触摸滑块的位置和上一次的位置来更新keyCodeArray的第一个键码。如果当前位置小于上一次位置,则发送左箭头键;如果当前位置大于上一次位置,则发送右箭头键。
        if (app_touch_sliderData.scroller_position_current < app_touch_sliderData.scroller_position_last) {
            app_usbData.keyCodeArray.keyCode[0] = USB_HID_KEYBOARD_KEYPAD_KEYBOARD_LEFT_ARROW;
        } else if (app_touch_sliderData.scroller_position_current > app_touch_sliderData.scroller_position_last){
            app_usbData.keyCodeArray.keyCode[0] = USB_HID_KEYBOARD_KEYPAD_KEYBOARD_RIGHT_ARROW;
        }
        /* Start a switch press ignore counter */
    } else {

        /* Indicate no event */
        app_usbData.keyCodeArray.keyCode[0] = USB_HID_KEYBOARD_KEYPAD_RESERVED_NO_EVENT_INDICATED;
    }

//创建键盘输入,发送消息
    KEYBOARD_InputReportCreate(&app_usbData.keyCodeArray, &app_usbData.keyboardModifierKeys, &keyboardInputReport);

}

这样就ok啦,一路下来,还是感谢官方大大的活动,感谢帮助我的朋友

遇到的主要难题及解决方法;

一开始是环境搭建问题,编译有问题。搞了几天,解决从mac换到了Windows平台。切换到了v6.15版本。

触摸点灯问题,解决参考pdf文档

Usb hid使用,识别 数据传输异常,解决 找到了HID Keyboard Example的例子,把左右划当作键盘的两个按键,例如控制音量的快捷键,F2/F3,这样控制pc音量https://github.com/Microchip-MPLAB-Harmony/usb_apps_device/blob/master/docs/docs_md/GUID-659FAABC-7E33-4359-B1D8-66E784DF9E43.md

未来计划:继续学习嵌入式开发

建议:本次还发现扩展板typec是区分正反的,希望提前告知。另外希望可以多多给到技术支持。

附件下载
D21_Touch_USB_HID_Device.7z
团队介绍
我就是我,一样的烟火
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号