CH571F蓝牙orUSB摇杆鼠标

演示视频:

短视频刷个爽

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

主要原理

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

软硬件开源链接

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

简单讲一下程序逻辑

首先是初始化

cpp 复制代码
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过来融合在一起

然后是定时器中断,

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

cpp 复制代码
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的调用程序

cpp 复制代码
__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;
    		}
    	}

    }
}

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

cpp 复制代码
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));//根据摇杆上下限调整系数,主要是小幅度时减半,大幅度时增大
相关推荐
FreakStudio6 小时前
全网最适合入门的面向对象编程教程:56 Python字符串与序列化-正则表达式和re模块应用
python·单片机·嵌入式·面向对象·电子diy
EVERSPIN9 小时前
分享国产32位单片机的电机控制方案
单片机·嵌入式硬件
每天一杯冰美式oh9 小时前
51单片机的家用煤气报警系统【proteus仿真+程序+报告+原理图+演示视频】
嵌入式硬件·51单片机·proteus
芯橦12 小时前
【瑞昱RTL8763E】音频
单片机·嵌入式硬件·mcu·物联网·音视频·visual studio code·智能手表
夜间去看海16 小时前
基于单片机的智能浇花系统
单片机·嵌入式硬件·智能浇花
VirtuousLiu17 小时前
LM74912-Q1用作电源开关
单片机·嵌入式硬件·ti·电源设计·lm74912·电源开关
打地基的小白17 小时前
软件I2C-基于江科大源码进行的原理解析和改造升级
stm32·单片机·嵌入式硬件·通信模式·i2c
Echo_cy_17 小时前
STM32 DMA+AD多通道
stm32·单片机·嵌入式硬件
朴人17 小时前
【从零开始实现stm32无刷电机FOC】【实践】【7.2/7 完整代码编写】
stm32·单片机·嵌入式硬件·foc
追梦少年时17 小时前
STM32中断——外部中断
stm32·单片机·嵌入式硬件