前言:大多数stm32产品都带有USB2.0全速外设并提供USB开发库。我们可以使用开发库来开发一些USB设备,例如音频设备,大容量存储设备,打印机和人机界面设备等。PC之所以可以识别不同的插件设备,是因为USB已经开发了一套标准协议。插入USB设备后,主机将查询设备信息。查询设备信息后,主机本身查询匹配的驱动程序并加载该驱动程序。计算机中的应用程序可以使用该设备。下面将使用由st官方网站提供的usb库的例程来重写该例程以制作一个usb鼠标设备,并通过连接到stm32开发板的操纵杆控制鼠标光标的移动。
1.硬件设计:

接线图如上图所示。翘板传感器和MCU必须连接到同一电源3.3v。
2.软件设计:1.编程要点:
1.使用stm32标准库执行AD收集;通过检测操纵杆引脚的电压来获得操纵杆的运动。游戏杆的原理是在摇动时改变电位的电阻,从而改变电压。
2.使用USB开发库来了解官方项目例程。
首先,简要说明官方例程的项目文件。从官方网站下载资源并配置项目。您可以参考链接并选择JoyStickMouse项目以打开。尽管您看到许多文件,但其中许多文件都无法参与编译。结构如下:


2.代码设计:
可以使用官方代码编译,但是硬件配置不是我们想要的,因此我们需要进行一些更改;首先,此官方例程是通过4个按钮模拟鼠标的上下,左右移动。固定移动一定距离;在MCU侧,鼠标的移动信息(例如x轴已移动了多少等)通过4个字节发送给主机,然后主机通过分析接收到的数据做出响应;实际上,每次数据传输时,主机发起查询时,单片机都会做出响应。
我们需要删除官方按钮部分,通过操纵杆传感器模拟鼠标的运动信息,然后首先通过ad电压检测操纵杆传感器,以检测4个方向的运动,然后发送响应数据给主机。
#ifndef __ADC_H
#define __ADC_H
#include "platform_config.h"
#include
/*
#define ADC_CH0 0 //通道0
#define ADC_CH1 1 //通道1
#define ADC_CH2 2 //通道2
#define ADC_CH3 3 //通道3
*/
void Adc_GPIO_Config(void);
void Adc_Config(void);
#endif #include "adc.h"
volatile u16 ADC_ConvertedValue[10][3];
void Adc_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*使能GPIO和ADC1通道时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1 , ENABLE );
/*将PA0设置为模拟输入*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
/*将GPIO设置为模拟输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
/*将GPIO设置为模拟输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
/*将GPIO设置为模拟输入*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Adc_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
Adc_GPIO_Config();
/*72M/6=12,ADC最大时间不能超过14M*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
/*将外设 ADC1 的全部寄存器重设为默认值*/
ADC_DeInit(ADC1);
/*ADC工作模式:ADC1和ADC2工作在独立模式*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/*模数转换工作在单通道模式*/
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
/*模数转换工作在单次转换模式*/
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
/*ADC转换由软件而不是外部触发启动*/
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
/*ADC数据右对齐*/
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/*顺序进行规则转换的ADC通道的数目*/
ADC_InitStructure.ADC_NbrOfChannel = 3;
/*根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器*/
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 );
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 );
/*使能指定的ADC1*/
ADC_Cmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
/*重置指定的ADC1的校准寄存器*/
ADC_ResetCalibration(ADC1);
/*获取ADC1重置校准寄存器的状态,设置状态则等待*/
while(ADC_GetResetCalibrationStatus(ADC1));
/*开始指定ADC1的校准*/
ADC_StartCalibration(ADC1);
/*获取指定ADC1的校准程序,设置状态则等待*/
while(ADC_GetCalibrationStatus(ADC1));
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR);
DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 30;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}写完上面两个后,将它们复制到JoyStickMouse目录下的inc和src文件夹中。将adc.c文件和stm32f10x_adc.c添加到项目中,后者位于STM32_USB-FS-Device_Lib_V4.1. 0 \ Libraries \ STM32F10x_StdPeriph_Driver \ src下,因为官方例程不使用adc,因此它们不是在正式项目中添加了该文件。将c文件添加到项目中的操作如图所示,单击并找到要添加的文件:

Adc_Config();
ADC_SoftwareStartConvCmd(ADC1, ENABLE);到目前为止,已经配置了广告收集,PA。0、PA。1、PA.2的电压值将自动收集到ADC_ConvertedValue [10] [3]数组中,并收集10个值将一次为每个频道收集一次。取平均值。然后,下一步是删除官方例程的按钮配置,并根据ADC_ConvertedValue [10] [3]的值判断操纵杆的摇摆方向。

/*
STM_EVAL_PBInit(Button_RIGHT, Mode_GPIO);
STM_EVAL_PBInit(Button_LEFT, Mode_GPIO);
STM_EVAL_PBInit(Button_UP, Mode_GPIO);
STM_EVAL_PBInit(Button_DOWN, Mode_GPIO);
*/ if ((JoyState() != 0) && (PrevXferComplete))
{
Joystick_Send(JoyState());
}
替换为:
if(PrevXferComplete){
JoyState();//继续用原来已有的函数名,等一下找到该函数,更改里面的内容就行
} PrevXferComplete是最后一次交互完成的标志。等到最后一次交互结束。将更改JoyState()函数,以检测操纵杆传感器的状态并发送状态信息,如下所示。
#if 0
uint8_t JoyState(void)
{
//原来的代码 略...
}
#else //重新写JoyState函数
extern volatile u16 ADC_ConvertedValue[10][3];
u16 ConvertedValue[3];
void get_Average(void)//取平均值
{
u8 i,j;
int sum;
for(i=0;i<3;i++){
sum=0;
for(j=0;j<10;j++){
sum+=ADC_ConvertedValue[j][i];
}
ConvertedValue[i]=sum/10;
}
}
#define middle_x 0x07e2
#define middle_y 0x0813
#define idle_press 0x07cd
/*******************************************************************
说明:采用的12位adc转换器,那么最大的转换值是0xFFF,对应的电压最大3.3v;
最小转换值是0,对应的电压为0v;所以电压值跟转换值的关系就很明显了,当然这里不
需要算出电压值。middle_x、middle_y、idle_press的值是摇杆静态的检测值,我特意
打印出来,可能不同的原件静态值有区别。它们的值大概是在0~0xFFF中间,上下摇动
控制一个电位器,左右摇动控制一个电位器,所以在静态的时候是检测到中间值是合理
的,然后判断检测值相对于静态检测值的的变化趋势可得知摇动的方向。
*********************************************************************/
uint8_t JoyState(void)
{
uint8_t Mouse_Buffer[4] = {0, 0, 0, 0}; //Mouse_Buffer[4] 数据格式说明看下一段
int8_t X = 0, Y = 0;
uint8_t temp1,temp2;
get_Average();
/*计算X坐标的偏移*/
temp1=(ConvertedValue[0]&0xF00)>>8;
if(temp1<7){ //向左
X=temp1-7;
}else if(temp1>8){ //向右
X=temp1-8;
}
/*计算Y坐标的偏移*/
temp2=(ConvertedValue[1]&0xF00)>>8;
if(temp2<7){ //向下
Y=temp2-7;
}else if(temp2>8){ //向上
Y=temp2-8;
}
/*判断SW按键的状态*/
if(ConvertedValue[2]<0x200){
Mouse_Buffer[0]=0x01; //鼠标的左键点击
}
/* prepare buffer to send */
Mouse_Buffer[1] = X;
Mouse_Buffer[2] = Y;
/* Reset the control token to inform upper layer that a transfer is ongoing */
PrevXferComplete = 0;
/* Copy mouse position info in ENDP1 Tx Packet Memory Area*/
USB_SIL_Write(EP1_IN, Mouse_Buffer, 4);
/* Enable endpoint for transmission */
SetEPTxValid(ENDP1);
return 0;
}
#endif设备发送到主机的信息存储在Mouse_Buffer [4]的四个字节中:
Mouse_Buffer [0]-
| --bit7〜bit3:
| --bit2:1表示按下了中间按钮
| --bit1:1表示右键单击并按
| --bit0:1表示按下了左按钮
Mouse_Buffer [1]-X坐标更改量,负数表示向左移动,正数表示向右移动。使用补码来表示变化量
Mouse_Buffer [2]-Y坐标更改量,负数表示向下移动,正数表示向上移动。使用补码来表示变化量
Mouse_Buffer [3]-滚轮发生变化,负数表示向上滚动,负数表示向下滚动
3.下载验证:
编译修改后的项目,连接J-LINK下载器,然后将程序刻录到开发板中;通过USB-mrico将开发板连接到计算机,计算机将自动识别设备并加载相应的设备。在等待一段时间后,摇动操纵杆以观察鼠标光标在移动,摇动越大,光标移动得越快;否则,速度较慢;摇操纵杆时无响应,检查线路连接是否良好;此外,操纵杆的静态广告转换值可以通过串行端口发送。当然,您需要配置usart外设,然后使用串行端口助手在计算机端接收该值。
遇到的问题:当未拔下J-LINK时,将USB-mrico连接到开发板时,无法识别开发板设备;我必须先拔掉J-LINK。原因暂时未知,可能是我的计算机原因。
到目前为止,自制鼠标设备已经完成;如果您想了解有关官方例程的更多信息,可以参考百度图书馆。
级别是有限的,仅供参考,希望您能就错误和不足之处向我提出建议。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/bofangqi/article-352289-1.html
打倒美帝国主义
我就放心了
1后一天蓝屏接近10次气死老子了