基于RISC-V CH32V103的鼠标键盘摇杆手柄Joystick学习开发板–实例Eg1_GamePad
第三部分、实战训练
3.1 实例Eg1_GamePad
本节我们目标是实现GamePad的功能,枚举成XY轴的平面坐标和8个按键的USB HID类设备。
3.1.1硬件设计
如上图是Joystick原理图,其中VRX1与VRY1是摇杆的电位器输出的电压信号(ADC检测);SW1则是按键,右侧H1是外接的Joystick口,供接joystick模块使用;
如上图是KEY原理图,我们只要配置8个GPIO作为输入去检测按键信号;
3.1.2 软件设计
首先是工程树,我们打开工程,可以看到Project Explorer下Gamepad目录如下图
其中
- Binaries: 二进制文件;
- Includes: 包含的头文件;
- __Core:__内核文件,存放core_riscv内核文件;
- Debug: 存放串口打印和延迟函数相关的文件;
- myBSP: 我们自己编写的驱动文件;
- obj: 编译的生成的obj文件;
- Peripheral: 这是MCU厂商提供外设相关驱动;
- Startup: ch32v103的启动文件;
- __User: __ch32v103的配置文件,中断相关文件,main函数等;
工程目录这里只做一次介绍,后面的样例目录大同小异。
我们先打开startup_ch32v10x.S启动文件,我们看到如下代码
jal SystemInit
la t0, main
定位到SystemInit
void SystemInit (void)
{
RCC->CTLR |= (uint32_t)0x00000001;
RCC->CFGR0 &= (uint32_t)0xF8FF0000;
RCC->CTLR &= (uint32_t)0xFEF6FFFF;
RCC->CTLR &= (uint32_t)0xFFFBFFFF;
RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
RCC->INTR = 0x009F0000;
SetSysClock();
}
关于RCC寄存器的配置,请各位自行查阅用户手册;我们接着打开SetSysClock函数
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
SetSysClockTo24();
#elif defined SYSCLK_FREQ_48MHz
SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
SetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHz
SetSysClockTo72();
#endif
/* If none of the define above is enabled, the HSI is used as System clock
* source (default after reset)
*/
}
由于我们定义了SYSCLK_FREQ_72MHz,SetSysClockTo72这个函数设置了系统时钟为72M;
接下来我们来看看main函数,如下
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf("USBHD Device Test\r\n");
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
USB_DeviceInit();
NVIC_EnableIRQ( USBHD_IRQn );
ADC_DMA_CONF();
KEY_INIT();
while(1)
{
printf("X=%d,Y=%d\r\n",ADC_ConvertedValue[0],ADC_ConvertedValue[1]);
if(Ready)
{
Gp_SendReport();
}
}
}
NVIC_PriorityGroupConfig是配置优先级分组的,Delay_Init初始化延迟函数;USART2_Printf_Init初始化串口打印,
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
主要配置端点0~2的缓存Ram;
USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
配置系统时钟1.5分频,即48M;
USB_DeviceInit(),是对usb设备进行初始化;
ADC_DMA_CONF主要对ADC DMA进行配置;
KEY_INIT是对按键所对应的GPIO进行初始化;
接着是Gp_SendReport,主要是讲处理并上报坐标和按键数据;
void Gp_SendReport(void)
{
memset(Joystick_Buf,0,3);
Ytemp=ADC_ConvertedValue[0];
Xtemp=ADC_ConvertedValue[1];
if(Xtemp>Xmax)
Xtemp=Xmax;
if(Xtemp<Xmin)
Xtemp=Xmin;
if(Ytemp>=Ymax)
Ytemp=Ymax;
if(Ytemp<=Ymin)
Ytemp=Ymin;
printf("Xmax=%x,Xcen=%x,Xmin=%x\r\n",Xmax,Xtemp,Xmin);
printf("Ymax=%x,Ycen=%x,Ymin=%x\r\n",Ymax,Ytemp,Ymin);
//根据坐标极点确定坐标(两点直线方程)
X=((Xtemp-Xmin)*255)/(Xmax-Xmin);
Y=((Ytemp-Ymin)*255)/(Ymax-Ymin);
Joystick_Buf[0]=X;
Joystick_Buf[1]=Y;
Joystick_Buf[2]=Key_Scan();
Delay_Ms(10);
while( Endp1Busy )//如果忙(上一包数据没有传上去),则等待。
{
; }
Endp1Busy = 1; //设置为忙状态
memcpy(pEP1_IN_DataBuf, Joystick_Buf, 3);
DevEP1_IN_Deal(3);
}
最后我们再来看看USBHD_IRQHandler,我们在这个函数值调用了USB_DevTransProcess。
3.1.3 下载验证
我们把固件程序下载进去可以,打开“设备与打印机”可以看到USB设备枚举成了一个Gamepad,如下图。
右键打开游戏控制器后,点击属性得到下图所示界面
我们可以摇Joystick和按按键可以发现上图游戏控制器界面也跟着响应。