asp.net 4.0网站开发与项目实战木制家具东莞网站建设
asp.net 4.0网站开发与项目实战,木制家具东莞网站建设,wordpress 发邮件 php,怎么在手机上设计网站1. 为什么选择STM32F103做USB HID设备#xff1f;从“芯”开始
如果你手头正好有一块STM32F103的开发板#xff0c;比如常见的“蓝桥杯”板、正点原子的Mini板或者野火的指南者#xff0c;那你已经拥有了开启USB世界大门的钥匙。STM32F103这颗经典的Cortex-M3内核芯片#…1. 为什么选择STM32F103做USB HID设备从“芯”开始如果你手头正好有一块STM32F103的开发板比如常见的“蓝桥杯”板、正点原子的Mini板或者野火的指南者那你已经拥有了开启USB世界大门的钥匙。STM32F103这颗经典的Cortex-M3内核芯片内置了全速USB 2.0控制器这意味着你不需要额外购买任何USB PHY芯片直接用芯片的USB引脚PA11和PA12就能和电脑“对话”。对于很多想学习USB通信的新手来说这简直是天大的福音硬件成本极低学习门槛一下子就降下来了。那么USB HID又是什么你可以把它想象成电脑最熟悉、最“自来熟”的一类USB设备。键盘、鼠标、游戏手柄这些都属于HID人机接口设备大类。为什么从HID开始学因为它最“省事”。当你把一个HID设备插上电脑操作系统Windows, Linux, macOS几乎瞬间就能识别并加载好通用驱动不需要你额外去写一个复杂的驱动安装程序。这对于我们做原型验证、快速实现一个自定义的数据采集器或者控制器来说实在是太方便了。想象一下你做了一个环境传感器通过USB插上电脑电脑直接把它认成一个自带特殊按键的“键盘”或“手柄”你写的上位机软件就能直接读取数据多酷而Custom HID则是HID协议中留给开发者的“自留地”。标准HID设备如键盘的报告描述符是固定的规定了第一字节是修饰键第二字节保留第三字节开始才是按键码。但Custom HID允许我们自定义这份“数据说明书”也就是报告描述符告诉电脑“我发送的数据包第一个字节代表温度第二个字节代表湿度第三个字节是个状态标志……” 这样我们就获得了一个完全按自己心意定义数据格式、同时又享受系统免驱便利的高速数据通道。STM32官方库里的Custom_HID例程就是一个绝佳的起点它演示了如何用自定义的报告格式控制4个LED灯我们今天要做的就是吃透这个例程并把它变成你自己的项目基石。2. 动手之前理清硬件连接与开发环境在兴奋地打开代码之前我们先得把“战场”布置好。硬件部分其实很简单核心就是STM32F103的USB接口。找到你板子上的USB接口它通常通过一个Micro-USB或Mini-USB座子引出来内部已经连接到了芯片的PA11D-和PA12D引脚。你需要确保开发板的USB供电跳线帽设置正确很多板子可以选择从USB口取电或者从外部电源取电为了调试方便我建议先用USB供电。然后是LED部分原例程是针对ST官方评估板设计的用了GPIOC的0-3脚。你的板子LED连接可能完全不同比如正点原子Mini板是接在PA8野火指南者接在PB0和PB1。这没关系我们后面改代码就行但心里要先有数知道你的灯具体接在哪个GPIO引脚上。软件环境是重头戏。你需要准备三样东西集成开发环境IDE、STM32标准外设库和USB设备库。IDE首推Keil MDK-ARM虽然现在有免费的STM32CubeIDE但老牌的Keil在调试STM32F1系列时资源丰富社区问题解答也多。去Keil官网下载安装记得顺便安装STM32F1系列的器件支持包Pack。然后是STM32F10x标准外设库这个库封装了对GPIO、定时器、USB等外设的底层操作ST官网可以找到通常叫“STM32F10x Standard Peripherals Library”。最后也是本文的主角——STM32 USB-FS-Device库版本号比如V4.1.0。这个库包含了USB协议栈的实现以及各种USB设备类HID、CDC虚拟串口、MSC大容量存储等的例程。你需要把这个库下载到本地。我建议你在电脑上建立一个清晰的工作目录。比如D:\STM32_Projects\USB_Dev然后把下载的USB设备库整个解压进去。别急着在原始目录里修改先拷贝一份出来再操作这是避免把原始例程搞乱的好习惯。准备好这些就像厨师备好了菜和锅接下来就可以开始烹饪我们的固件大餐了。3. 第一步裁剪与移植Custom_HID官方例程拿到ST的USB库你会发现里面例子非常多CDC、MSC、HID等等我们要像修剪盆景一样把不需要的枝叶剪掉只留下我们需要的Custom_HID主干。首先找到库中的Project目录里面应该有一个Custom_HID的文件夹。把它单独拷贝到你的工作区。更稳妥的做法是把整个STM32_USB-FS-Device_Lib_V4.1.0\Libraries和Project\Custom_HID都拷贝到一个新文件夹比如我命名为MyCustomHID_Project。接下来用Keil打开Projects\Custom_HID\MDK-ARM下的工程文件。打开后在左侧的“Project”栏里你会看到很多分组和文件。原工程为了兼容多种ST评估板包含了很多你可能用不上的板级支持文件。我们的目标是让工程尽可能简洁。你可以安全地删除User组以外的其他组比如Libraries\STM32_USB-FS-Device_Driver和Libraries\CMSIS这些核心USB和内核文件要保留但Utilities\STM32_EVAL这个目录下的文件如果你用的不是ST官方评估板就需要进行大改甚至用你自己的板级支持文件替换。然后进入“魔术棒”选项Options for Target。在Device标签页确认芯片型号选对了比如STM32F103ZE如果你用的是ZET6芯片。在C/C标签页找到Define符号定义。这里非常关键原工程可能定义的是STM32F10X_MD中等密度但对于F103ZE这类拥有256KB以上Flash的芯片属于高密度High-density产品必须改为STM32F10X_HD。同时确保USE_STDPERIPH_DRIVER这个宏存在它告诉编译器我们要使用标准外设库。USE_STM3210B_EVAL这个宏如果你用的不是这块官方板可以先留着但后面在代码里我们会通过条件编译避开它或者直接修改对应的板级文件。完成这些设置后点击编译F7。这是第一个里程碑。如果顺利通过生成了.axf或.hex文件恭喜你移植的骨架已经搭好了。此时你甚至可以先不管LED直接把程序烧录到板子里用USB线连接电脑。打开设备管理器在“人体学输入设备”或“通用串行总线控制器”下面你应该能看到一个新出现的“HID-compliant device”或者“USB Input Device”。这说明芯片内部的USB核心和我们的基础协议栈已经正常工作能和电脑握手成功了。这一步的成功会给你巨大的信心。4. 核心改造让代码适配你的硬件电路电脑能识别设备了但设备还不会听我们的话控制LED。现在进入最核心的代码修改环节目标是把例程中对LED的操作映射到我们实际开发板的GPIO引脚上。这里主要修改两个地方LED引脚定义和USB数据回调处理逻辑。首先找到LED引脚定义文件。在官方例程里通常位于Utilities\STM32_EVAL\STM3210B_EVAL\下的stm3210b_eval.h和.c文件。我们直接打开.h文件找到LED定义的部分。你会看到类似这样的代码#define LEDn 4 #define LED1_PIN GPIO_Pin_0 #define LED1_GPIO_PORT GPIOC #define LED1_GPIO_CLK RCC_APB2Periph_GPIOC这里定义了4个LED都挂在GPIOC的0-3脚上。如果你的板子LED接在PA8且是低电平点亮那么你就需要修改为#define LEDn 1 // 假设你只用一个LED做测试 #define LED1_PIN GPIO_Pin_8 #define LED1_GPIO_PORT GPIOA #define LED1_GPIO_CLK RCC_APB2Periph_GPIOA同时别忘了打开对应的.c文件stm3210b_eval.c找到STM_EVAL_LEDInit、STM_EVAL_LEDOn、STM_EVAL_LEDOff这些函数。仔细看LEDOn和LEDOff的内部实现它们是通过GPIO_SetBits置高和GPIO_ResetBits置低来控制引脚电平的。你需要确认你的板子LED电路是高电平点亮还是低电平点亮也就是常说的“共阴极”还是“共阳极”。大多数开发板为了省事和直观采用“阴极驱动”即LED阳极接3.3V阴极接GPIO引脚GPIO输出低电平时点亮。如果例程的逻辑和你的硬件相反就会出现“发送点亮命令灯却灭了”的情况。这时就需要修改USB数据处理的回调函数。关键文件是src目录下的usb_endp.c。找到EP1_OUT_Callback函数这个函数会在电脑通过端点1Endpoint 1发送数据给设备时自动被USB中断调用。函数里会读取Receive_Buffer根据缓冲区里的数据来决定点亮或熄灭哪个LED。原代码的逻辑可能是Receive_Buffer[0]是LED编号Receive_Buffer[1]是状态0点亮1熄灭。但如果你的硬件是低电平点亮而原LEDOn函数是做GPIO_ResetBits输出低那么这里的逻辑就是对的。如果反了你就需要交换STM_EVAL_LEDOn和STM_EVAL_LEDOff的调用。同理usb_prop.c文件中的CustomHID_Status_In函数也可能需要类似的调整。这个过程就像在解一个逻辑谜题务必静下心来对照原理图理清“电脑命令-数据字节-函数调用-GPIO电平-LED亮灭”这条完整的链条。5. 理解与修改报告描述符定义你的数据协议如果说前面的步骤是让设备“能动”那么理解报告描述符就是让设备“能说会道”告诉电脑它传输的数据究竟代表什么。报告描述符是一种用特定语法描述数据格式的“语言”它定义在usb_desc.c文件中。对于Custom_HID例程我们需要关注的是HID_ReportDescriptor这个数组。这是一串看起来像天书的十六进制数。别怕我们一点点拆解。以控制LED1的那一段为例0x85, 0x01, // REPORT_ID (1) // 报告ID为1 0x09, 0x01, // USAGE (LED 1) // 用途页内用法为LED 1 0x15, 0x00, // LOGICAL_MINIMUM (0) // 逻辑最小值是0 0x25, 0x01, // LOGICAL_MAXIMUM (1) // 逻辑最大值是1 0x75, 0x08, // REPORT_SIZE (8) // 每个字段占8位1字节 0x95, 0x01, // REPORT_COUNT (1) // 有1个这样的字段 0xB1, 0x82, // FEATURE (Data,Var,Abs,Vol) // 这是一个特征报告主机-设备 0x85, 0x01, // REPORT_ID (1) // 再次指定报告ID 0x09, 0x01, // USAGE (LED 1) // 用途同上 0x91, 0x82, // OUTPUT (Data,Var,Abs,Vol) // 这是一个输出报告主机-设备这段描述符定义了两件事第一一个报告ID为1的特征报告Feature Report主机可以用来查询或设置设备的一些特性第二一个报告ID同样为1的输出报告Output Report主机通过发送这种报告来控制设备。我们的例程主要用的是输出报告。它告诉电脑“当我发送一个报告ID为1的数据包时里面包含1个字节8位的数据这个数据的合法范围是0到1它对应控制的是LED1。” 所以在测试工具里我们发送Report ID0x01, Data0x01就能点亮LED1发送Data0x00则熄灭。如果你想增加一个控制蜂鸣器的功能可以定义一个新的报告ID比如0x05并指定其USAGE为自定义的用途逻辑最大值可以设为1开关或更大的值控制鸣叫频率。修改报告描述符后电脑需要重新枚举设备才能识别新的格式最直接的方法就是重新插拔USB。6. 实战测试用工具验证通信与控制代码改好了也编译烧录进去了怎么知道它真的在工作呢我们需要一个能直接和HID设备“对话”的上位机工具。这里强烈推荐SimpleHIDWrite或者功能更强大的HIDAPI测试工具、Bus Hound。以SimpleHIDWrite为例它界面简洁直击要害。打开软件你应该能在设备列表里找到你的“USB Input Device”。选中它软件会显示该设备的供应商IDVID、产品IDPID以及输入/输出报告长度。重点看输出报告长度我们的例程里定义的是2个字节报告ID数据。在发送数据区域你需要按照报告描述符的约定来填充。比如要点亮LED1就发送01 01。第一个字节01是报告ID第二个字节01是数据代表点亮。点击发送观察你的开发板上的LED1是否亮起。再发送01 00LED1应该熄灭。如果灯没反应别慌这是调试的常态。我们可以按以下顺序排查第一确认设备是否被正确识别设备管理器有无感叹号。第二确认烧录的程序是否最新重新编译烧录一次。第三用调试器单步运行在EP1_OUT_Callback函数里设断点看电脑发送数据时程序是否执行到这里以及Receive_Buffer里的值是否正确。第四检查GPIO初始化是否正确可以用一个简单的跑马灯程序先测试一下硬件上的LED和GPIO驱动是否正常。这个过程虽然繁琐但每一步都能让你对USB通信和嵌入式调试的理解加深一层。当你第一次通过自己编写的Custom HID设备控制一个硬件动作时那种成就感是无与伦比的。7. 超越例程打造你自己的Custom HID应用成功点亮LED只是一个开始Custom HID的真正威力在于传输任意你定义的数据。假设我们要做一个简单的双通道电压采集器通过USB把数据实时上传给电脑。该怎么做呢首先我们要修改报告描述符。我们需要定义一个输入报告Input Report设备-主机来上传数据。在HID_ReportDescriptor数组中添加一段新的描述。例如定义报告ID 0x10用于上传数据包含两个16位的电压值假设0-4095对应0-3.3V。// 报告ID 0x10: 上传ADC数据 0x85, 0x10, // REPORT_ID (16) 0x09, 0x01, // USAGE (Generic Desktop) - X轴可自定义用途 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x26, 0xFF, 0x0F, // LOGICAL_MAXIMUM (4095) // 2字节最大4095 0x75, 0x10, // REPORT_SIZE (16) // 每个字段16位 0x95, 0x02, // REPORT_COUNT (2) // 2个这样的字段通道1和2 0x81, 0x02, // INPUT (Data,Var,Abs) // 这是一个输入报告然后在固件中你需要定期比如用定时器中断读取两个ADC通道的值填充到一个缓冲区并通过USB_SIL_Write函数和SetEPTxValid函数主动将数据通过指定的端点发送给主机。同时主机端的上位机程序可以用Python的hidapi库、C#的HidLibrary等需要定期读取这个输入报告解析出两个16位整数再换算成电压值显示出来。反过来你也可以定义更复杂的输出报告让电脑下发命令比如设置采集频率、启动或停止采集、控制其他外设等。这样一来一个功能完整、双向通信的数据采集设备就初具雏形了。在这个过程中你可能会遇到USB传输的稳定性问题比如数据丢失。这时就需要了解USB的传输类型HID默认使用中断传输Interrupt Transfer它保证了一定延迟内的带宽但对于高速大数据量可能不够。如果需要更高速率可以考虑使用CDC虚拟串口或WinUSB但那需要安装特定的驱动失去了HID即插即用的便利性。对于大多数中低速每秒几千字节的控制和数据采集场景Custom HID是完全够用且非常优雅的方案。8. 调试心法与常见问题避坑指南玩转USB开发一半时间在写代码另一半时间在调试。分享几个我踩过坑后总结的心法。首先善用工具。除了SimpleHIDWriteBus Hound是USB协议分析的神器它能捕获USB总线上所有的数据包让你清晰地看到枚举过程、配置描述符的获取、以及每一次数据交互的细节。当设备无法识别时看Bus Hound的捕获记录经常能一眼看出问题所在比如描述符返回错误、请求超时等。其次理解枚举过程。USB设备插入后主机会发起一系列标准请求获取设备描述符、配置描述符、字符串描述符等。我们的固件必须在usb_core.c和usb_desc.c中正确响应这些请求。一个常见的错误是描述符的长度或内容不对导致主机在获取某个描述符时失败整个枚举过程中断。确保你的DeviceDescriptor、ConfigDescriptor、ReportDescriptor等数组定义正确并且usb_desc.c中返回这些描述符长度的函数如GetReportDescriptor返回了正确的值。第三端点缓冲区管理。STM32的USB外设有固定的端点缓冲区。在usb_conf.h文件中我们定义了每个端点使用的缓冲区大小。对于Custom HID我们通常使用端点1EP1作为中断输出端点主机到设备端点2EP2作为中断输入端点设备到主机。务必确保你定义的缓冲区大小比如EP1_OUT_FRAME_INTERVAL大于或等于你的报告长度。如果报告是64字节缓冲区至少也要64字节否则会导致数据被截断。最后电源与复位。USB通信对时序和电源稳定性很敏感。如果程序跑飞或者USB通信突然中断尝试给芯片一个完整的硬件复位按复位键而不是仅仅重新插拔USB线。有时软件跑飞后USB外设可能处于一个奇怪的状态只有硬件复位才能彻底恢复。另外确保你的USB线是数据线而不仅仅是充电线。我就曾因为一根劣质的充电线调试了一下午始终无法识别设备换根线立马解决。这些细节看似微不足道却往往是阻碍项目成功的“最后一公里”。