前言: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
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』