如果不知道如何创建工程文件的可以参考我之前写的一篇文章:【STM32CubeIDE入门】(一)工程创建&工程配置
开发板型号:STM32F103VET6(野火指南者)
前言:由于 USB 协议过于复杂,这里不展开详细讲解,不过我上传了有关 USB 的一些 pdf 文档以及工具,文中用到的工具皆出自这里,并且其中包括的《圈圈教你玩USB》是一本非常好的入门书籍,想要深入可以看看。
一、基础配置
二、USB配置
注意:下一步是很多教程没有讲到的一步,但却是很重要的一步。
先从USB主机如何检测到设备插入说起。USB设备端(开发板)有两根线,分别叫做 D+ 和 D-,如果设备端是高速设备(480Mbps)或全速设备(12Mbps),上拉电阻接到 D+ 上,低速设备(1.5Mbps)的上拉电阻接到 D- 上。而 stm32 默认使用的是全速设备(12Mbps)。
对于这个上拉电阻,要查看你们板子的原理图,看上拉电阻是如何放置的。对于野火的指南者来说,它的上拉电阻是由 PD6 引脚来控制的,这样可以灵活的连接与断开 USB。
所以还要将 PD6 引脚使能,默认低电平可以让上拉电阻导通。
自动生成代码后按F11
或者点击运行将代码烧入进开发板后连接上USB后就可以发现电脑可以识别到一个鼠标设备了(STM32CubeIDE 默认生成的例子就是一个鼠标例程)。但此时还什么都干不了,还需要进一步的编写代码。
三、修改例程
想要控制鼠标就要向主机发送一串四字节的数据。(USB 传输方向是 LSB,即低字节先行,这里说的第几字节就是根据此规则划分的)
- 第 1 个字节
- D0 位:鼠标左键单击
- D1 位:鼠标中键单击
- D2 位:鼠标右键单击
- D7~D3位:保留,置0
- 第 2 个字节:鼠标X轴移动偏移量
- 第 3 个字节:鼠标Y轴移动偏移量
- 第 4 个字节:鼠标滚轮移动偏移量
如果想要了解为什么要发送这样四个字节的数据可以点击这里和这里。
main.c
/* USER CODE BEGIN Includes */
#include "usbd_hid.h"
/* USER CODE END Includes */
/* USER CODE BEGIN 0 */
extern USBD_HandleTypeDef hUsbDeviceFS;
typedef struct
{
char mouse_abs_left : 1; //鼠标左键单击
char mouse_abs_right : 1; //鼠标右键单击
char mouse_abs_wheel : 1; //鼠标中键单击
char reserve : 5; //常量0
char mouse_rel_x; //鼠标X轴移动偏移量
char mouse_rel_y; //鼠标Y轴移动偏移量
char mouse_rel_wheel; //鼠标滚轮移动偏移量
}tyMouse_buff;
tyMouse_buff tMouse_buff;
void User_Init(void)
{
tMouse_buff.mouse_abs_left = 0;
tMouse_buff.mouse_abs_right = 0;
tMouse_buff.mouse_abs_wheel = 0;
tMouse_buff.reserve = 0;
tMouse_buff.mouse_rel_x = 0;
tMouse_buff.mouse_rel_y = 0;
tMouse_buff.mouse_rel_wheel = 0;
}
#define KEY1_Press (1 << 1)
#define KEY2_Press (1 << 2)
unsigned char Get_Key_State(void)
{
unsigned char keyState = 0;
if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_SET)
{
keyState |= KEY1_Press;
}
if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_SET)
{
keyState |= KEY2_Press;
}
return keyState;
}
void Send_mouse_msg(void)
{
USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t *)&tMouse_buff, sizeof(tMouse_buff));
}
int main(void)
{
/* USER CODE BEGIN 1 */
unsigned char keyState;
unsigned char keyStateLast;
/* USER CODE END 1 */
// 省略。。。
while (1)
{
if ((keyState = Get_Key_State()) != 0)
{
HAL_Delay(10);
while ((keyState = Get_Key_State()) != 0)
{
keyStateLast = keyState;
switch(keyState)
{
case 0x02:tMouse_buff.mouse_rel_x = 1;break; // KEY1:鼠标左移
case 0x04:tMouse_buff.mouse_rel_y = 1;break; // KEY2:鼠标下移
case 0x06:tMouse_buff.mouse_rel_wheel = -1;break; // KEY1与KEY2同时按:鼠标滚轮下滑
default:break;
}
Send_mouse_msg();
HAL_Delay(10);
tMouse_buff.mouse_rel_x = 0;
tMouse_buff.mouse_rel_y = 0;
tMouse_buff.mouse_rel_wheel = 0;
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
将程序烧入进板子上按下对应键就可以观察不同的现象啦。