CH571F蓝牙orUSB摇杆鼠标

news2024/10/2 10:42:19

演示视频:

短视频刷个爽

程序基本上是基于官方的例程上改的,用到的例程有:蓝牙的HID_Mouse,USB的CompoundDev,还有ADC,按键中断。

主要原理

就是ADC采集采集摇杆电压,通过蓝牙HID或者USB的HID发送给电脑或者手机,实现鼠标功能。

软硬件开源链接

:opencaneve: 开源STM32HAL ESP8266 ESP32 python Android Windows把我学习到的以及找到的可以用的代码分享出来python记录打卡信息 ESP32蓝牙鼠标 开源windows串口助手 - Gitee.com

简单讲一下程序逻辑

首先是初始化

int main(void)
{

#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE)
    PWR_DCDCCfg(ENABLE);
#endif
    SetSysClock(CLK_SOURCE_PLL_60MHz);
#if(defined(HAL_SLEEP)) && (HAL_SLEEP == TRUE)
    GPIOA_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
    GPIOB_ModeCfg(GPIO_Pin_All, GPIO_ModeIN_PU);
#endif
#ifdef DEBUG
    GPIOA_SetBits(bTXD1);
    GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
    UART1_DefInit();
#endif

    pEP0_RAM_Addr = EP0_Databuf;
    pEP1_RAM_Addr = EP1_Databuf;
    pEP2_RAM_Addr = EP2_Databuf;
    pEP3_RAM_Addr = EP3_Databuf;
    USB_DeviceInit();
    PFIC_EnableIRQ(USB_IRQn);

    PRINT("%s\n", VER_LIB);
    CH57X_BLEInit();
    HAL_Init();
    GAPRole_PeripheralInit();
    HidDev_Init();
    HidEmu_Init();

    GPIOB_SetBits(GPIO_Pin_4);
    GPIOB_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA);
    TMR0_TimerInit(FREQ_SYS/100);         // 设置定时时间 100ms10
	TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); // 开启中断
	PFIC_EnableIRQ(TMR0_IRQn);

    PRINT("\n2.Single channel sampling...\n");
    GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_Floating);
    GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeIN_Floating);
    ADC_ExtSingleChSampInit(SampleFreq_3_2, ADC_PGA_1_2);
//    RoughCalib_Value = ADC_DataCalib_Rough(); // 用于计算ADC内部偏差,记录到全局变量 RoughCalib_Value中
//    PRINT("RoughCalib_Value =%d \n", RoughCalib_Value);
	ADC_ChannelCfg(0);
	start_lr_adc = ADC_ExcutSingleConver(); //
    ADC_ChannelCfg(1);
	start_ud_adc = ADC_ExcutSingleConver(); //
	PRINT("%d %d \n", adcBuff[0],adcBuff[1]); // 注意:由于ADC内部偏差的存在,当采样电压在所选增益范围极限附近的时候,可能会出现数据溢出的现象

	GPIOB_ModeCfg(GPIO_Pin_7, GPIO_ModeIN_PU);
	GPIOB_ITModeCfg(GPIO_Pin_7, GPIO_ITMode_FallEdge); // 下降沿唤醒
	GPIOB_ModeCfg(GPIO_Pin_13, GPIO_ModeIN_PU);
	GPIOB_ITModeCfg(GPIO_Pin_13, GPIO_ITMode_FallEdge); // 下降沿唤醒
	GPIOB_ModeCfg(GPIO_Pin_12, GPIO_ModeIN_PU);
	GPIOB_ITModeCfg(GPIO_Pin_12, GPIO_ITMode_FallEdge); // 下降沿唤醒
	PFIC_EnableIRQ(GPIO_B_IRQn);


    Main_Circulation();


}

 这部分基本上直接从例程中copy过来融合在一起

然后是定时器中断,

虽然好像蓝牙也有个类似于任务排序的函数,但不是很会用,所以并没有用自带的调度程序,而蓝牙程序中不能长时间被其他程序占用,不然会丢失蓝牙连接,所以我没有用延时,使用的定时器计时

int led_cnt=0;
__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void TMR0_IRQHandler(void) // TMR0 定时中断
{
    if(TMR0_GetITFlag(TMR0_3_IT_CYC_END))
    {
        TMR0_ClearITFlag(TMR0_3_IT_CYC_END); // 清除中断标志

        if(delay_cnt[0]>0){
        	delay_cnt[0]--;
        	if(delay_cnt[0]==0){
        		run_flag[0]=1;
        	}
        }
        if(delay_cnt[1]>0){
        	delay_cnt[1]--;
        	if(delay_cnt[1]==0){
        		run_flag[1]=1;
        	}
        }
    	led_cnt++;
    	if(led_cnt%15==0 && stick_func==0) GPIOB_InverseBits(GPIO_Pin_4);//反转
    	else if(led_cnt%50==0 && stick_func==1) GPIOB_InverseBits(GPIO_Pin_4);//反转
//    	if(led_cnt==150){
//    		usb_flag =0;
//    	}
    }
}

定时器中断中有两个计时变量,主要负责USB数据发送和ADC采集

最后是主循环,

里面就是蓝牙、USB和ADC的调用程序

__attribute__((section(".highcode")))
__attribute__((noinline))
void Main_Circulation()
{
	memset(run_flag,1,10);
	uint8_t i=0;
    while(1)
    {
//    	mDelaymS(100);
//    	if(usb_flag == 0){
//    		TMR0_ITCfg(DISABLE, TMR0_3_IT_CYC_END);
    		TMOS_SystemProcess();//处理蓝牙
//    	}else{
    		TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END);
//    	}
    	if(run_flag[0]&&usb_flag!=0){//处理usb
    		run_flag[0]=0;
    		switch(run_index[0]){
    		case 0:
    			if(stick_func == 0)
    			DevHIDMouseReport(key2_down<<1|key1_down,(int)(abs(lr_value)<=15 ? lr_value/2 : lr_value > 0 ? lr_value*1.57-17.14:lr_value*1.57+17.14),(int)(abs(ud_value<=15) ? ud_value/2 : ud_value >0 ?ud_value*1.57-17.14 : ud_value*1.8+13));
    			else DevHIDMouseReport(key2_down<<1|key1_down,0,0);
    			if(abs(ud_value)==0){
    				delay_cnt[0]=1;//10ms
    				run_index[0]=0;//摇杆没有值就只反馈鼠标按键
    			}else{
    				delay_cnt[0]=1;//10ms
    				run_index[0]++;//摇杆有值再进行下一步
    			}
    			break;
    		case 1:
    			DevHIDMouseReport(0x00,0,0);
    			delay_cnt[0]=1;//10ms
    			run_index[0]++;
    			break;
    		case 2:
    			if(stick_func == 1){
    				DevHIDKeyReport(ud_value<0 ? 0x52 : ud_value > 0 ? 0x51 : 0);//上下方向键,鼠标滚轮在手机抖音横屏翻页时会有问题
    				if(abs(ud_value)<15)delay_cnt[0]=20;
    				else delay_cnt[0]=20-abs(ud_value)/2;
    				run_index[0]++;
    			}else{
    				delay_cnt[0]=1;//10ms
    				run_index[0]=0;//结束
    			}
    			break;
    		case 3:
    			DevHIDKeyReport(0x00);
    			delay_cnt[0]=1;
    			run_index[0]=0;
    			break;
    		case 4:
    			delay_cnt[0]=1;
    			run_index[0]=0;
    			break;
    		default:
    			run_index[0]=0;
    			delay_cnt[0]=10;
    			break;
    		}
    	}
    	if(run_flag[1]){//处理ADC
    		run_flag[1]=0;
    		switch(run_index[1]){
    		case 0:

    			delay_cnt[1]=10;//100ms
    			run_index[1]++;
    			ADC_ChannelCfg(0);
				adcBuff[0] = ADC_ExcutSingleConver(); //
			    ADC_ChannelCfg(1);
				adcBuff[1] = ADC_ExcutSingleConver(); //
				if(abs(start_lr_adc-adcBuff[0])>death_value){
					lr_value = (adcBuff[0]-start_lr_adc)/div_times;
				}else
					lr_value = 0;
				if (abs(start_ud_adc - adcBuff[1]) > death_value) {
					ud_value = (start_ud_adc - adcBuff[1])/div_times;
				} else
					ud_value = 0;
				if(switch_flag==1){
					if(!key3_down){
						if(stick_func==0)
							stick_func = 1;
						else stick_func = 0;
						switch_flag = 0;
					}
				}
				PRINT("%d %d stick_func %d\n", lr_value,ud_value,stick_func); // 注意:由于ADC内部偏差的存在,当采样电压在所选增益范围极限附近的时候,可能会出现数据溢出的现象
    			break;
    		case 1:

    			delay_cnt[1]=2;//20ms
    			run_index[1]=0;
    			break;
    		default:
    			run_index[1]=0;
    			break;
    		}
    	}

    }
}

蓝牙数据发送时对数据稍稍做了处理,摇杆幅度小时放慢速度,幅度大时加快速度

hidEmuSendMouseReport(mouse_button ,(int)(abs(lr_value)<=15 ? lr_value/2 : lr_value > 0 ? lr_value*1.57-17.14:lr_value*1.57+17.14), (int)(abs(ud_value<=15) ? ud_value/2 : ud_value >0 ?ud_value*1.57-17.14 : ud_value*1.8+13));//根据摇杆上下限调整系数,主要是小幅度时减半,大幅度时增大

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1979270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文心一言 VS 讯飞星火 VS chatgpt (317)-- 算法导论22.3 9题

九、请给出如下猜想的一个反例&#xff1a;如果有向图G包含一条从结点u到结点v的路径&#xff0c;则任何对图G的深度优先搜索都将导致v.d⩽u.f。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 首先&#xff0c;我们需要澄清问题中的几个关键点。在图的深度优先…

想做抖音短视频,视频素材去哪里找啊?

各位抖音上的短视频创作者们&#xff0c;是否曾幻想过自己的作品能够在全网爆火&#xff0c;却常因为缺少那些能够让视频更加生动的素材而感到困扰&#xff1f;不用担心&#xff0c;今天我要为大家介绍几个优秀的视频素材网站&#xff0c;让你的抖音之路顺风顺水&#xff01; …

Linux系统中的高级用户空间与内核空间交互技术

Linux作为一种开源操作系统&#xff0c;具有良好的稳定性、安全性和自定制性&#xff0c;因而在各种设备和场景中得到广泛应用。作为Linux系统的核心组成部分&#xff0c;内核空间与用户空间交互技术对系统性能和功能扩展起着关键作用。本文将深入探讨Linux系统中的高级用户空间…

Vue Vine:带给你全新的 Vue 书写体验!

你好&#xff0c;我是 Kagol&#xff0c;个人公众号&#xff1a;前端开源星球。 上个月和 TinyVue 的小伙伴们一起参加了 VueConf 24 大会&#xff0c;有幸认识沈青川大佬&#xff0c;并了解了他的 Vue Vine 项目&#xff0c;Vue Vine 让你可以在一个文件中通过函数方式定义多…

系统化学习 H264视频编码(05)码流数据及相关概念解读

说明&#xff1a;我们参考黄金圈学习法&#xff08;什么是黄金圈法则?->模型 黄金圈法则&#xff0c;本文使用&#xff1a;why-what&#xff09;来学习音H264视频编码。本系列文章侧重于理解视频编码的知识体系和实践方法&#xff0c;理论方面会更多地讲清楚 音视频中概念的…

Nginx进阶-常见配置(二)

一、nginx 日志配置 nginx 日志介绍 nginx 有一个非常灵活的日志记录模式,每个级别的配置可以有各自独立的访问日志, 所需日志模块 ngx_http_log_module 的支持&#xff0c;日志格式通过 log_format 命令来定义&#xff0c;日志对于统计和排错是非常有利的&#xff0c;下面总…

【TwinCAT3教程】TwinCAT3 PLC 简单程序编写与调试

一、PLC 简单程序编写 1.1 新建TwinCAT3项目 (1)打开 TwinCAT 3,点击 New TwinCAT Project 新建 TC3 项目。 (2)选择 TwinCAT Project,输入项目名称和项目保存路径,然后点击确定。 1.2 添加PLC项目 1.2.1 步骤 (1)在树形资源管理器右键点击 PLC,选择 添加新项 新…

STM32F28335实验:继电器

继电器控制电机&#xff1a; 5s启动 5s停止 循环 管脚图&#xff1a; 管脚用的是GPIO15 驱动&#xff1a; beep.c /** leds.c** Created on: 2024年8月2日* Author: Administrator*/#include<relay.h>/***************************************************…

【算法设计题】查找给定结点的双亲结点(二叉树),第3题(C/C++)

目录 第3题 查找给定结点的双亲结点&#xff08;二叉树&#xff09; 得分点&#xff08;必背&#xff09; 题解 定义函数和初始化变量&#xff1a; 处理特殊情况&#xff1a; 遍历树&#xff1a; 中序遍历左子树&#xff1a; 处理右子树&#xff1a; 返回结果&#x…

LSTM实战之预测股票

&#x1f4c8; 用PyTorch搭建LSTM模型&#xff0c;轻松预测股票价格&#xff01;&#x1f680; Hey小伙伴们&#xff0c;今天给大家带来一个超级实用的项目教程——如何用PyTorch和LSTM模型来预测股票价格&#xff01;&#x1f31f; &#x1f50d; 项目背景 我们都知道股市是…

《Unity3D网络游戏实战》学习与实践--制作一款大乱斗游戏

角色类 基类Base Human是基础的角色类&#xff0c;它处理“操控角色”和“同步角色”的一些共有功能&#xff1b;CtrlHuman类代表“操控角色”​&#xff0c;它在BaseHuman类的基础上处理鼠标操控功能&#xff1b;SyncHuman类是“同步角色”类&#xff0c;它也继承自BaseHuman&…

MySQL的数据结构B+tree以及SQL优化

首先呢&#xff0c;我们知道MySQL的数据结构为Btree,那么其结构究竟是什么样的&#xff0c;为什么选择Btree&#xff0c;而不选择Btree。下面我们从其结构分析 1.Btree平衡多路查找树 B-tree结构的数据可以让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录…

入门mem0.NET

入门mem0.NET 安装包 如果你的项目使用了EntityFrameworkCore,那么你可以跟随这个教程走 <ItemGroup><PackageReference Include"mem0.NET" Version"0.1.7" /><PackageReference Include"mem0.NET.Qdrant" Version"0.1.7…

云动态摘要 2024-08-04

给您带来云厂商的最新动态&#xff0c;最新产品资讯和最新优惠更新。 最新优惠与活动 数据库上云优选 阿里云 2024-07-04 RDS、PolarDB、Redis、MongoDB 全系产品新用户低至首年6折起&#xff01; [免费体验]智能助手ChatBI上线 腾讯云 2024-07-02 基于混元大模型打造&…

java之IO篇——File、字节流、字符流

前言 IO流是用于读写文件中的数据&#xff0c;要读写文件之前可以创建文件获取文件对象再创建IO流&#xff0c;正文会先介绍File类&#xff0c;通过File类的构造方法获取文件的对象&#xff0c;创建文件或目录以及File类的一些方法获取文件对象的属性。后面还介绍了相关的IO流体…

Radxa ROCK 3C开发板编译Opencv,支持调用树莓派摄像头模块V2

目录 1、ROCK 3C和树莓派摄像头模块V2介绍2、ROCK 3C在rsetup开启支持3、测试指令4、编译Opencv4.1 增加swap&#xff0c;确保内存够用4.2 安装依赖和下载opencv4.3 编译参考链接 5、使用opencv调用树莓派摄像头模块V2 1、ROCK 3C和树莓派摄像头模块V2介绍 ROCK 3C 是一款基于…

刷题篇 - 01

目录 题目一&#xff1a; 题目二&#xff1a; 题目三&#xff1a; 题目四&#xff1a; 题目五&#xff1a; 题目六&#xff1a; 题目七&#xff1a; 题目一&#xff1a; 387. 字符串中的第一个唯一字符 - 力扣&#xff08;LeetCode&#xff09; public int firstUniqC…

订单定时状态处理业务(SpringTask)

文章目录 概要整体架构流程技术细节小结 概要 订单定时状态处理通常涉及到对订单状态进行定期检查&#xff0c;并根据订单的状态自动执行某些操作&#xff0c;比如关闭未支付的订单、自动确认收货等. 需求分析以及接口设计 需求分析 用户下单后可能存在的情况&#xff1a; …

鸿蒙(API 12 Beta2版)NDK开发【内存管理purgeable内存开发指导】

场景介绍 HarmonyOS提供Purgeable Memory内存管理机制&#xff0c;开发者可以使用相关接口创建PurgeableMemory对象&#xff0c;从而管理purgeable内存。 开发者可以通过本指导了解在HarmonyOS应用中&#xff0c;如何使用Native层相关接口操作purgeable内存。功能包括purgeab…

Jupyter-Notebook常用操作看这一篇就够啦

来源&#xff1a; “码农不会写诗”公众号 链接&#xff1a;Jupyter-Notebook常用操作看这一篇就够啦 文章目录 01 概括02 快捷键总结03 运行外部python文件04 魔法命令4.1 运行计时4.2 查看变量与函数4.3 其它常用指令 书接上文 Jupyter-Notebook是一个基于 Web 的交互式开发环…