演示视频:
短视频刷个爽
程序基本上是基于官方的例程上改的,用到的例程有:蓝牙的HID_Mouse,USB的CompoundDev,还有ADC,按键中断。
主要原理
就是ADC采集采集摇杆电压,通过蓝牙HID或者USB的HID发送给电脑或者手机,实现鼠标功能。
软硬件开源链接
简单讲一下程序逻辑
首先是初始化
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));//根据摇杆上下限调整系数,主要是小幅度时减半,大幅度时增大