×
单片机 > 单片机程序设计 > 详情

stm32之USB应用实例(自制简易鼠标设备,详细源码)

发布时间:2020-06-09 发布时间:
|

前言: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(ConvertedValu


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM中断源之定时器中断