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是区分正反的,希望提前告知。另外希望可以多多给到技术支持。