演示视频:
短视频刷个爽
程序基本上是基于官方的例程上改的,用到的例程有:蓝牙的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);
#endifSetSysClock(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 DEBUGGPIOA_SetBits(bTXD1);GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);UART1_DefInit();
#endifpEP0_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); // 设置定时时间 100ms10TMR0_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){//处理usbrun_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;//10msrun_index[0]=0;//摇杆没有值就只反馈鼠标按键}else{delay_cnt[0]=1;//10msrun_index[0]++;//摇杆有值再进行下一步}break;case 1:DevHIDMouseReport(0x00,0,0);delay_cnt[0]=1;//10msrun_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;//10msrun_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]){//处理ADCrun_flag[1]=0;switch(run_index[1]){case 0:delay_cnt[1]=10;//100msrun_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;}elselr_value = 0;if (abs(start_ud_adc - adcBuff[1]) > death_value) {ud_value = (start_ud_adc - adcBuff[1])/div_times;} elseud_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;//20msrun_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));//根据摇杆上下限调整系数,主要是小幅度时减半,大幅度时增大