前言:stm32产品大多数携带了一个USB2.0全速外设,并提供了USB开发库;我们可以利用开发库开发一些USB设备,比如音频设备、大容量存储设备、打印机、人机接口设备等。PC端之所以能识别不同的插入设备是因为USB制定了一套标准协议,USB设备插入后,主机会询问设备的信息,查询到设备信息之后,主机自身查询与其匹配的驱动并加载驱动,那么计算机里的应用程序就能使用该设备。下面将利用st官网提供的usb库的例程,改写该例程,制作一个usb鼠标设备,通过一个接到stm32开发板的摇杆来控制鼠标光标的移动。
1.硬件设计:
stc32f103c8t6最小系统开发板一个
摇杆传感器一个
USB-mrico连接线,杜邦线若干,J-LINK下载器
接线如上图所示,摇杆传感器跟MCU需接到同一个电源3.3v。
2.软件设计:
1.编程要点:
1.使用stm32标准库,进行AD采集;通过检测摇杆的引脚的电压获得摇杆的动向,摇杆的原理的就是摇动的时候改变了电位的阻值,从而改变了电压。
2.使用USB开发库,理解官方的 先对官方例程的工程文件进行简单说明,从官网下载资源并配置工程,可参考链接,选择JoyStickMouse项目打开,虽然看到很多文件,但很多没用参与编译,结构如下图: 2.代码设计: 官方的代码编译就能用,但是硬件配置不是我们想要的,所以要更改一些;首先,这个官方的例程是通过4个按键模拟鼠标的上下左右移动,按键每按一下就会固定移动一定的距离;mcu这边是将鼠标的移动信息(比如x轴移动多少等)通过4字节发送到主机端,那么主机通过分析接收的数据做出响应;事实上,每一次数据都是有主机主动发起询问,mcu才作出应答的。 我们要把官方的按键部分去掉,鼠标移动的信息通过摇杆传感器来模拟,那么先对摇杆传感器进行ad电压检测,来检测4个方向的移动情况,再给主机发送响应的数据。 >>>创建adc.h #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 >>>创建adc.c #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.0LibrariesSTM32F10x_StdPeriph_Driversrc底下,因为官方例程没用到adc,所以官方工程里没有添加这个文件进去。工程加入c文件的操作如图,点击后找到要加的文件即可: >>>在main.c里面添加include "adc.h",然后在main函数里的“”USB_Init(); ”后面调用以下两个函数: Adc_Config(); ADC_SoftwareStartConvCmd(ADC1, ENABLE); 至此,ad采集已经配置完毕,PA.0、PA.1、PA.2的电压值会自动采集到ADC_ConvertedValue[10][3]数组里面,每个通道一次采集10个值,取平均数。那么接下来就是把官方例程的按键配置去掉,同时根据ADC_ConvertedValue[10][3]的值判断摇杆的摇动方向。 >>>去掉无用的按键初始化,在hw_config.c里面找到以下代码,注释掉: /* 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); */ >>>在main函数里面找到: 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将开发板连接到电脑端,电脑端会自动识别设备,并加载相应的驱动;等待一段时间完成后,摇动摇杆可观察到鼠标光标在移动,并且摇动的幅度越大,光标移动的步伐越快;反之,越慢;若摇动摇杆无反应,需检查线路是否连接良好;此外,摇杆的静态ad转换值可以通过串口发出,当然需要配置一下usart外设,那么在电脑端用串口助手接收该值。 遇到的问题:在没有拔掉J-LINK时候,插入USB-mrico连接开发板时,无法识别开发板设备;只好先拔掉J-LINK,这个原因暂时不明,可能是我电脑的原因。 至此,自制鼠标设备已完成;如果想对官方的 水平有限,仅供参考,错误之处以及不足之处还望多多指教。
上一篇:stm32之IIC应用实例(AT24C02芯片,硬件和软件方式驱动)
下一篇:stm32之USB应用实例(官方例程资料下载使用)
推荐阅读最新更新时间:2024-11-11 09:52
设计资源 培训 开发板 精华推荐
- L7885C 功率 AM 调制器的典型应用(单位电压增益,IO = 0.5)
- MPC560xBOARDS: MPC560x调试和目标接口板
- 具有短路保护功能的 MC78M15CTG 15V 电流提升的典型应用
- RT7298B 6A、18V、同步降压转换器的典型应用
- OP262GSZ-REEL7单电源直接接入调制解调器的典型应用电路
- stm32L051低功耗开发测试板
- 一转四集线器
- ADR441B 2.5 Vout 超低噪声、LDO XFET 电压基准的典型应用,具有电流吸收器和电流源
- LTC2953IDD-1 电压监控器的典型应用电路
- LTC3624EDD-2 可调输出电压、2A 同步降压稳压器、同步至 500kHz、强制连续模式的典型应用