需求

实习工作,老板要求用单片机读取驾驶模拟器(Joystick)返回的数据,驾驶模拟器usb输出,输出信息包括:方向盘转角、左右拨杆、按键等。

硬件

采用正点原子探索者开发板,即插即用,硬件不需要改动。开发板做主设备,因此必须选用f4系列,f103只能做从设备,其他能做主设备的f1,没就用过,不做尝试,直接用f407zgt6。驾驶模拟器上是一个stm32单片机,商家已经擦除芯片型号,盲猜是f1系列,因为模拟驾驶器是USB从设备,只需要f1系列即可。

探索的思路

最初不知道驾驶模拟器是用什么协议,于是开始漫长的探索过程。

usb HID设备

首先,将驾驶模拟器插入windows电脑,再任务管理器中发现,多出两行,表明该设备为USB HID设备。USB HID是Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏杆等。通过window的系统调用,可以接收到模拟量和数字量。协议讲解 在这里。
使用Windows读取数据程序如下,也可以参考这里。

#include<stdio.h>
#include <iostream>
#include<stdlib.h>
#include<conio.h>
#include <iostream>
#include<string>
#include<Windows.h>
//添加joystick操作api的支持库
#include<MMSystem.h>
#pragma comment(lib, "Winmm.lib")
using namespace std;
int main()
{UINT joyNums;joyNums = joyGetNumDevs();//读取手柄信息printf("当前手柄数量:%d \n", joyNums);//采集手柄数量;JOYINFO joyinfo;//定义joystick信息结构体JOYINFOEX joyinfoex;joyinfoex.dwSize = sizeof(JOYINFOEX);joyinfoex.dwFlags = JOY_RETURNALL;while (1){MMRESULT joyreturn = joyGetPosEx(JOYSTICKID1, &joyinfoex);cout << joyreturn<< endl;cout << joyinfoex.dwXpos << endl;Sleep(1000);}Sleep(3000);return 0;
}

Windows下读取到的数据写在Joyinfoex结构体中,这里不再赘述。

stm32官方usb库移植

官方usb库下载教程。

STM32官方USB例程JoyStick详解。
移植过程可以参考正点原子教程,正点原子将回调函数写好了,但仅限打印相关提示话语,具体如何处理数据还需要自行修改。

修改为读取驾驶模拟器程序

官方库中有Joystick从机,也就是驾驶模拟器那边的例程,还有鼠标键盘读取的例程,因为鼠标键盘也是USB HID设备,我这里在正点原子提供的,移植好的鼠标键盘例程的基础上,修改为可以读取Joystick的程序。

直接用鼠标的程序,把驾驶器插进去,会有如下输出信息,说明两边USB协议层已经识别,而且驾驶模拟器这边方向盘自行转动做初始化操作,说明从机设备有响应。但显示无法识别的USB设备,推测应该是HID层的问题。
用文本搜索功能,找到回调函数中显示的内容

//usbh_usr.c文件
//无法识别的USB设备
void USBH_USR_DeviceNotSupported(void)
{ printf("无法识别的USB设备!\r\n\r\n");
}

继续搜索该回调函数,发现他是一个结构体的成员。

//usbh_usr.c文件
//USB HOST 用户回调函数.
USBH_Usr_cb_TypeDef USR_Callbacks =
{USBH_USR_Init,USBH_USR_DeInit,USBH_USR_DeviceAttached,USBH_USR_ResetDevice,USBH_USR_DeviceDisconnected,USBH_USR_OverCurrentDetected,USBH_USR_DeviceSpeedDetected,USBH_USR_Device_DescAvailable,USBH_USR_DeviceAddressAssigned,USBH_USR_Configuration_DescAvailable,USBH_USR_Manufacturer_String,USBH_USR_Product_String,USBH_USR_SerialNum_String,USBH_USR_EnumerationDone,USBH_USR_UserInput,NULL,USBH_USR_DeviceNotSupported,USBH_USR_UnrecoveredError
};
//usbh_core.h文件中
typedef struct _USBH_USR_PROP
{void (*Init)(void);       /* HostLibInitialized */void (*DeInit)(void);       /* HostLibInitialized */  void (*DeviceAttached)(void);           /* DeviceAttached */void (*ResetDevice)(void);void (*DeviceDisconnected)(void); void (*OverCurrentDetected)(void);  void (*DeviceSpeedDetected)(uint8_t DeviceSpeed);          /* DeviceSpeed */void (*DeviceDescAvailable)(void *);    /* DeviceDescriptor is available */void (*DeviceAddressAssigned)(void);  /* Address is assigned to USB Device */void (*ConfigurationDescAvailable)(USBH_CfgDesc_TypeDef *,USBH_InterfaceDesc_TypeDef *,USBH_EpDesc_TypeDef *); /* Configuration Descriptor available */void (*ManufacturerString)(void *);     /* ManufacturerString*/void (*ProductString)(void *);          /* ProductString*/void (*SerialNumString)(void *);        /* SerialNubString*/void (*EnumerationDone)(void);           /* Enumeration finished */USBH_USR_Status (*UserInput)(void);int  (*UserApplication) (void);void (*DeviceNotSupported)(void); /* Device is not supported*/void (*UnrecoveredError)(void);
}

继续搜索USR_Callbacks结构体,发现在主函数中,以指针形式传入USB初始化函数。

//main,c
int main(void)
{ u32 t; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2delay_init(168);  //初始化延时函数uart_init(115200);     //初始化串口波特率为115200LED_Init();                    //初始化LED    //定时器时钟84M,分频系数8400,所以84M/8400=10Khz(0.1ms)的计数频率TIM3_Int_Init(TIMER_TIME*10-1,8400-1); uart2_init(9600);//DMA1,STEAM6,CH4,外设为串口2,存储器为SendBuff,长度为:SEND_BUF_SIZE.MYDMA_Config(DMA1_Stream6,DMA_Channel_4,(u32)&USART2->DR,(u32)SendBuff,SEND_BUF_SIZE);//初始化USB主机USBH_Init(&USB_OTG_Core_dev,USB_OTG_FS_CORE_ID,&USB_Host,&HID_cb,&USR_Callbacks);  while(1) {USBH_Process(&USB_OTG_Core_dev, &USB_Host); //if(bDeviceState==1)//连接建立了{ if(USBH_Check_HIDCommDead(&USB_OTG_Core_dev,&HID_Machine))//检测USB HID通信,是否还正常? {       USBH_HID_Reconnect();//重连}              }else   //连接未建立的时候,检测{if(USBH_Check_EnumeDead(&USB_Host))   //检测USB HOST 枚举是否死机了?死机了,则重新初始化 {       USBH_HID_Reconnect();//重连}          }t++;if(t==200000){LED0=!LED0;t=0;}}
}

按经验应该是向USB驱动程序注册回调函数,而且调用的是DeviceNotSupported这个结构体成员名。

//usbh_core.c文件
void USBH_Init(USB_OTG_CORE_HANDLE *pdev,USB_OTG_CORE_ID_TypeDef coreID,USBH_HOST *phost,               USBH_Class_cb_TypeDef *class_cb, USBH_Usr_cb_TypeDef *usr_cb)
{/* Hardware Init */USB_OTG_BSP_Init(pdev);  /* configure GPIO pin used for switching VBUS power */USB_OTG_BSP_ConfigVBUS(0);  /* Host de-initializations */USBH_DeInit(pdev, phost);/*Register class and user callbacks */phost->class_cb = class_cb;phost->usr_cb = usr_cb;  /* Start the USB OTG core */     HCD_Init(pdev , coreID);/* Upon Init call usr call back */phost->usr_cb->Init();/* Enable Interrupts */USB_OTG_BSP_EnableInterrupt(pdev);
}

注册回调函数时,将USR_Callbacks指针赋值给phost->usr_cb,继续搜索->DeviceNotSupported,发现在。

//usb_hid_core.c
static USBH_Status USBH_HID_InterfaceInit ( USB_OTG_CORE_HANDLE *pdev, void *phost)
{   uint8_t maxEP;USBH_HOST *pphost = phost;uint8_t num =0;USBH_Status status = USBH_BUSY ;HID_Machine.state = HID_ERROR;if(pphost->device_prop.Itf_Desc[0].bInterfaceSubClass  == HID_JOTSTICK)//方向盘的话修改为0{/*识别设备标识*/if(pphost->device_prop.Itf_Desc[0].bInterfaceProtocol  == HID_JOTSTICK)       {HID_Machine.cb = &HID_MOUSE_cb;//将HID设备确定为鼠标设备,cb应该是call back}HID_Machine.state     = HID_IDLE;HID_Machine.ctl_state = HID_REQ_IDLE; HID_Machine.ep_addr   = pphost->device_prop.Ep_Desc[0][0].bEndpointAddress;HID_Machine.length    = pphost->device_prop.Ep_Desc[0][0].wMaxPacketSize;HID_Machine.poll      = pphost->device_prop.Ep_Desc[0][0].bInterval ;if (HID_Machine.poll  < HID_MIN_POLL) {HID_Machine.poll = HID_MIN_POLL;}/* Check fo available number of endpoints *//* Find the number of EPs in the Interface Descriptor */      /* Choose the lower number in order not to overrun the buffer allocated */maxEP = ( (pphost->device_prop.Itf_Desc[0].bNumEndpoints <= USBH_MAX_NUM_ENDPOINTS) ? pphost->device_prop.Itf_Desc[0].bNumEndpoints :USBH_MAX_NUM_ENDPOINTS); /* Decode endpoint IN and OUT address from interface descriptor */for (num=0; num < maxEP; num++){if(pphost->device_prop.Ep_Desc[0][num].bEndpointAddress & 0x80){HID_Machine.HIDIntInEp = (pphost->device_prop.Ep_Desc[0][num].bEndpointAddress);HID_Machine.hc_num_in  =\USBH_Alloc_Channel(pdev, pphost->device_prop.Ep_Desc[0][num].bEndpointAddress);/* Open channel for IN endpoint */USBH_Open_Channel  (pdev,HID_Machine.hc_num_in,pphost->device_prop.address,pphost->device_prop.speed,EP_TYPE_INTR,HID_Machine.length); }else{HID_Machine.HIDIntOutEp = (pphost->device_prop.Ep_Desc[0][num].bEndpointAddress);HID_Machine.hc_num_out  =\USBH_Alloc_Channel(pdev, pphost->device_prop.Ep_Desc[0][num].bEndpointAddress);/* Open channel for OUT endpoint */USBH_Open_Channel  (pdev,HID_Machine.hc_num_out,pphost->device_prop.address,pphost->device_prop.speed,EP_TYPE_INTR,HID_Machine.length); } }   start_toggle =0;status = USBH_OK; }else{pphost->usr_cb->DeviceNotSupported();   }return status;
}

(以上代码我已改过)由此推断是因为设备号不同导致大的无法识别,修改最开始两个if中的编号后,便可以识别Joystick设备。

返回的数据长度

此时仍不能读取到数据,从获取获取鼠标数据的路线从后向前反推,逆向找数据来源。

//usb_hid_mouce.c
static void  MOUSE_Decode(uint8_t *data)
{   //修改过了USR_MOUSE_ProcessData(data);
}

搜索MOUSE_Decode(),发现HID_MOUSE_cb是鼠标设备的回调函数结构体,其中包含MOUSE_Init()和MOUSE_Decode()。HID设备初始化函数USBH_HID_InterfaceInit()中,将HID设备绑定为为鼠标设备,将HID_MOUSE_cb指针,赋值给HID_Machine.cb。查找发现在USBH_HID_Handle(),有调用MOUSE_Decode()。

//usb_hid_core.c
static USBH_Status USBH_HID_Handle(USB_OTG_CORE_HANDLE *pdev , void   *phost)
{USBH_HOST *pphost = phost;USBH_Status status = USBH_OK;switch (HID_Machine.state){ case HID_IDLE:HID_Machine.cb->Init();HID_Machine.state = HID_SYNC;case HID_SYNC:/* Sync with start of Even Frame */if(USB_OTG_IsEvenFrame(pdev) == TRUE){HID_Machine.state = HID_GET_DATA;  }break;case HID_GET_DATA:USBH_InterruptReceiveData(pdev, HID_Machine.buff,HID_Machine.length,HID_Machine.hc_num_in);start_toggle = 1;HID_Machine.state = HID_POLL;HID_Machine.timer = HCD_GetCurrentFrame(pdev);break;case HID_POLL: if(( HCD_GetCurrentFrame(pdev) - HID_Machine.timer) >= HID_Machine.poll){//printf("goto USBH_HID_Handle() 5.1\r\n");HID_Machine.state = HID_GET_DATA;}else if(HCD_GetURB_State(pdev , HID_Machine.hc_num_in) == URB_DONE){//printf("goto USBH_HID_Handle() 5.2\r\n");if(start_toggle == 1) /* handle data once */{//printf("goto USBH_HID_Handle() 5.3\r\n");start_toggle = 0;HID_Machine.cb->Decode(HID_Machine.buff);}}else if(HCD_GetURB_State(pdev, HID_Machine.hc_num_in) == URB_STALL) /* IN Endpoint Stalled */{//printf("goto USBH_HID_Handle() 5.4\r\n");/* Issue Clear Feature on interrupt IN endpoint */ if( (USBH_ClrFeature(pdev, pphost,HID_Machine.ep_addr,HID_Machine.hc_num_in)) == USBH_OK){//printf("goto USBH_HID_Handle() 5.5\r\n");/* Change state to issue next IN token */HID_Machine.state = HID_GET_DATA;}}      break;default:break;}return status;
}

USBH_HID_Handle()中HID_Machine.state在3和5之间跳,在status是5时,执行数据处理函数。MOUSE_Decode()中需要用到HID_Machine.length,匹配收到数据的长度,返回的数据长度是8字节,直接取消长度限制,最终获得目标数据,返回的是64个8位数。

附:几种即使模拟器各个数据位的含义

  1. 安路迪
数据位 含义
Data[0] 方向盘低八位
Data[1] 方向盘高八位
Data[2] 随着油门增大128->0;刹车踩下128->255
Data[3] 离合踩下128->255
Data[4] 右转向灯、左转向灯、鸣笛、空、空、手刹、右按键、下按键
Data[5] 上、雨刷二、雨刷一、近光灯、空、远光晃、近光、钥匙
Data[6] 空、倒挡、五档、四档、三档、二档、一档、左按键
Data[7] 校验
  1. 罗技

完整代码传送门

https://download.csdn.net/download/renzemingcsdn/15726059

stm32读取驾驶模拟器数据 stm32F407读取joystick数据相关推荐

  1. STM32 Cubemax(十一) ——JY901陀螺仪数据的读取与简单数据处理

    STM32 Cubemax(十一) --JY901陀螺仪数据的读取与简单数据处理 文章目录 STM32 Cubemax(十一) --JY901陀螺仪数据的读取与简单数据处理 前言 JY901简单介绍 ...

  2. 绘制STM32最小系统电路原理图、STM32F103读取SD卡的数据

    绘制STM32最小系统电路原理图.STM32F103读取SD卡的数据 文章目录 绘制STM32最小系统电路原理图.STM32F103读取SD卡的数据 1 AltiumDesigner 软件配置 2 A ...

  3. 【自动驾驶模拟器AirSim快速入门 | 02】数据预处理:数据挖掘与准备

    这是机器未来的第16篇文章 原文首发地址:https://blog.csdn.net/RobotFutures/article/details/125321082 开源项目: 项目地址:https:/ ...

  4. ADC芯片ADS1258采集数据的读取

    分享一个5年前做过的通过ADS1258芯片读取霍尔传感器ACS758检测的电流数据,原理图较简单,补贴了,直接贴.h与.c文件,几年前的代码了,凑活看,实际可用,因为用在了车厂的一个非标检测项目上,已 ...

  5. pandas读取csv文件的前几行数据(nrows参数)、pandas读取csv文件的中间几行数据(skiprows=range(a,b))

    pandas读取csv文件的前几行数据(nrows参数).pandas读取csv文件的中间几行数据(skiprows=range(a,b)) 目录 pandas读取csv文件的前几行数据.pandas ...

  6. RStudio启动后修改文件(数据)读取默认目录

    RStudio启动后修改文件(数据)读取默认目录 # 初始的路径是C盘下的用户目录: # 将路径修改到我们需要加载的数据所在的路径下: # 参考:Rstudio

  7. python使用openpyxl读取数据_Python-openpyxl读取和写入数据1

    1.读取单元格保存到文件,注意python中文件报错或者不能写入时改文件后缀:.xls from openpyxl import Workbook from openpyxl import load_ ...

  8. python各种文件数据的读取

    (持续更新中-) 文章目录 (持续更新中...) 0 常规方法open 0.1 读取时存在中文无法识别 0.2 写入,写入中文 1.读取excel文件 一)python读取: 2.读取csv文件 一) ...

  9. java读取欧姆龙plc_欧姆龙CJ2M系列PLC与PLC之间的数据相互读取设定

    分享一下欧姆龙CJ2M系列的plc与PLC之间的数据相互读取设定,因为CJ2M系列PLC自带EIP协议,所以硬件连接只需要将多个PLC通过网线用交换机连起来,并将所有的PLC的IP地址设置在同一个网段 ...

最新文章

  1. 浅显易懂 Makefile 入门 (07)— 其它函数(foreach 、if、call、origin )
  2. dedecms模板中首页实现分页的方法
  3. 转载:一致性 hash 算法( consistent hashing )
  4. Java正则表达式简单用法
  5. golang:Linux下安装go环境
  6. python使用pip安装_Python | 如何用pip安装模块和包
  7. 7.26 1004度度熊的午饭时光 百度之星题解
  8. Fedora 14 yum 报错: Cannot retrieve repository metadata (repomd.xml) for repository
  9. 小型计算机和Pc,超小型台式电脑:重量相当于两个新iPhone
  10. vue3+vant开发微信公众号网页爬坑不完全指北
  11. Excel文本日期格式转为日期格式的方法
  12. 珠穆朗玛币王:11月22日是谁丢了136亿美元
  13. V3D/Vaa3D installation procedures in Windows(VS2013) V3D安装教程
  14. 转:华为网络工程师离职总结:资质平庸的人该怎么办?震惊了无数人!
  15. 通过自适应UM(非锐化掩膜)算法进行图像增强(Image Enhancement via Adaptive Unsharp Masking)
  16. Python转换秒为时间
  17. 再见 Teamviewer!这款国产轻量级远程桌面软件超牛逼
  18. 数仓建设目标-统计口径
  19. 计算机毕业设计Android高校图书馆推荐书目系统APP(源码+系统+mysql数据库+Lw文档)
  20. 无监督学习中的目标检测

热门文章

  1. 初级篇第三期:初识UI
  2. 戴尔服务器安装centos7修改网卡名,Centos7.6修改网卡名称为之前的eth形式
  3. mysql跳脱字符 单引号_MYSQL特殊字符(单引号,行尾斜杠)的处理
  4. 蓝牙天线的一点小资料
  5. vc 6.0添加c文件 fatal error C1010解决办法
  6. Android开发网络连接超时
  7. myeclipse中的项目如何切换svn地址
  8. Silverlight 2.5D RPG游戏技巧与特“.NET技术”效处理:(十一)AI系统
  9. Undefined reference to ...
  10. mysql decimal被四舍五入_MySQL之ROUND函数四舍五入的陷阱