STM32单片机USB扫码枪开发笔记

您所在的位置:网站首页 扫码枪原理识别 STM32单片机USB扫码枪开发笔记

STM32单片机USB扫码枪开发笔记

2024-04-08 13:28| 来源: 网络整理| 查看: 265

在一次开发中偶然需要使用到扫描枪去读取条形码,在开发过程中遇到了扫码枪能枚举成功,但是获取不到数据,之后上网查询资料发现根本就没有这一类的资料,或许是我找的方式不对吧!所以在此写下我在开发过程中所遇到的问题。

一、硬件配置

1.芯片所使用的是STM32F407RE

2.管脚说明

PA12          DP(D+)

PA11          DM(D-)

PA10          ID(主机模式直接接地,从机模式需接3.3V电源,扫码枪需要为主机模式)

PA9          VCC(电源5V)

3.电路请看下图

(芯片端)

(接口端)

二、程序,先上代码再说问题

 

1.管脚、中断优先级、延时配置

#include "usb_bsp.h" #include "usb_delay.h" /**************************** * *以下函数都在USB库"usbh_core.c"中的"USBH_Init"初始化函数中调用 * ****************************/ /************************ * *功能:USB_OTG 底层IO初始化 *pdev:USB OTG内核结构体指针 *返回值:无 * ************************/ void USB_OTG_BSP_Init(USB_OTG_CORE_HANDLE *pdev) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟 RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS, ENABLE);//使能USB OTG时钟 //GPIOA11,A12设置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;//PA11/12复用功能输出 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化 //复用USB_OTG接口 GPIO_PinAFConfig(GPIOA,GPIO_PinSource11,GPIO_AF_OTG_FS);//PA11,AF10(USB) GPIO_PinAFConfig(GPIOA,GPIO_PinSource12,GPIO_AF_OTG_FS);//PA12,AF10(USB) } /************************ * *功能:USB_OTG 中断配置 *pdev:USB OTG内核结构体指针 *返回值:无 * ************************/ void USB_OTG_BSP_EnableInterrupt(USB_OTG_CORE_HANDLE *pdev) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = OTG_FS_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x000;//抢占优先级1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能通道 NVIC_Init(&NVIC_InitStructure);//配置 } /************************ * *功能:USB_OTG 端口供电设置(不需要配置) *pdev:USB OTG内核结构体指针 *state:0,断电;1,上电 *返回值:无 * ************************/ void USB_OTG_BSP_DriveVBUS(USB_OTG_CORE_HANDLE *pdev, uint8_t state) { } /************************ * *功能:USB_OTG 供电IO配置(不需要配置) *pdev:USB OTG内核结构体指针 *返回值:无 * ************************/ void USB_OTG_BSP_ConfigVBUS(USB_OTG_CORE_HANDLE *pdev) { } /************************ * *功能:USB驱动库微秒延时函数 *usec:计数时间 *返回值:无 * ************************/ void USB_OTG_BSP_uDelay (const uint32_t usec) { USB_delay_us(usec); } /************************ * *功能:USB驱动库毫秒延时函数 *msec:计数时间 *返回值:无 * ************************/ void USB_OTG_BSP_mDelay (const uint32_t msec) { USB_delay_us(msec); }

2.延时函数

#include "usb_delay.h" #include "usbh_usr.h" /****************** * *功能: 并不标准的延时函数 *time: 延时时间 *返回值: 无 * ******************/ void USB_delay_us(uint16_t time) { USB_User.USB_Time = time; //USB_User.USB_Time请在定时器中调用 while(0 != USB_User.USB_Time); }

3.USB驱动回调函数配置

#include "usbh_usr.h" #include "string.h" //USB USBH_HOST USB_Host; USB_OTG_CORE_HANDLE USB_OTG_Core; USB_Pack USB_User; //自定义的一个USB结构体,用来做断开与连接等标记的 /******************** * *功能:主设备中断服务函数 * *********************/ void OTG_FS_IRQHandler(void) { USBH_OTG_ISR_Handler(&USB_OTG_Core); } /******************** * *功能:主设备回调函数 * *********************/ 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 //未恢复错误信息 }; /*********** 以下为各回调函数实现 *************/ void USBH_USR_Init(void){} void USBH_USR_ResetDevice(void){} void USBH_USR_OverCurrentDetected(void){} void USBH_USR_DeviceAddressAssigned(void){} void USBH_USR_Manufacturer_String(void*ManufacturerString){} void USBH_USR_Product_String(void*ProductString){} void USBH_USR_SerialNum_String(void*SerialNumString){} void USBH_USR_DeviceNotSupported(void){} void USBH_USR_UnrecoveredError(void){} /******************** * *功能:检测设备接入 * *********************/ void USBH_USR_DeviceAttached(void) { USB_User.USB_Connect = 1; } /******************** * *功能:检测设备移除 * *********************/ void USBH_USR_DeviceDisconnected (void) { USB_User.USB_Connect = 0; } /******************** * *功能:检测从设备速度 *DeviceSpeed:0 高速,1 全速,2 低速,其他错误 * *********************/ void USBH_USR_DeviceSpeedDetected(uint8_t DeviceSpeed) { if(DeviceSpeed==HPRT0_PRTSPD_HIGH_SPEED) { ;//高速(HS)USB设备 } else if(DeviceSpeed==HPRT0_PRTSPD_FULL_SPEED) { ;//全速(FS)USB设备 } else if(DeviceSpeed==HPRT0_PRTSPD_LOW_SPEED) { ;//低速(LS)USB设备 } else { ;//设备错误 } } /******************** * *功能:检测从设备描述符 *DeviceDesc:设备描述符指针 * *********************/ void USBH_USR_Device_DescAvailable(void *DeviceDesc) { // USBH_DevDesc_TypeDef *desc; // desc=DeviceDesc; // printf("VID: %04Xh\r\n" , (uint32_t)(*desc).idVendor); // printf("PID: %04Xh\r\n" , (uint32_t)(*desc).idProduct); } /******************** * *功能:检测从设备ID,判别从设备类型 **cfgDesc:设备配置描述符指针 **itfDesc:接口描述符指针 **epDesc:从设备描述符指针 * *********************/ void USBH_USR_Configuration_DescAvailable(USBH_CfgDesc_TypeDef * cfgDesc, USBH_InterfaceDesc_TypeDef *itfDesc, USBH_EpDesc_TypeDef *epDesc) { USBH_InterfaceDesc_TypeDef *id; id = itfDesc; if((*id).bInterfaceClass==0x08) { ;//可移动存储器设备 }else if((*id).bInterfaceClass==0x03) { ;//HID 设备 } } /******************** * *功能:设备枚举成功 * *********************/ void USBH_USR_EnumerationDone(void) { //设备枚举完成 到了这里就说明你的USB驱动并没有问题 } /******************** * *功能:等待用户输入执行下一步操作 *返回值:此处返回成功直接跳过用户操作 * *********************/ USBH_USR_Status USBH_USR_UserInput(void) { return USBH_USR_RESP_OK; } /******************** * *功能:设备重新初始化 * *********************/ void USBH_USR_DeInit(void) { USB_User.USB_AppDate=USH_USR_FS_Init; } /*********************** *USB枚举状态死机检测,防止USB枚举失败导致的死机 *phost:USB_HOST结构体指针 *返回值:0,没有死机 * 1,死机了,外部必须重新启动USB连接. ***********************/ uint8_t USBH_Check_EnumeDead(USBH_HOST *phost) { static uint16_t error_cnt=0; //这个状态,如果持续存在,则说明USB死机了. if(phost->gState==HOST_CTRL_XFER&&(phost->EnumState==ENUM_IDLE||phost->EnumState==ENUM_GET_FULL_DEV_DESC)) { error_cnt++; if(error_cnt>2000)//死机了 { error_cnt=0; RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,ENABLE);//USB OTG FS 复位 RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_OTG_FS,DISABLE); //复位结束 return 1; } }else error_cnt=0; return 0; } /*********************** *功能:USB HID通信死机检测,防止USB通信死机(暂时仅针对:DTERR,即Data toggle error) *pcore:USB_OTG_Core_dev_HANDLE结构体指针 *phidm:HID_Machine_TypeDef结构体指针 *返回值:0,没有死机 * 1,死机了,外部必须重新启动USB连接. ***********************/ u8 USBH_Check_HIDCommDead(USB_OTG_CORE_HANDLE *pcore,HID_Machine_TypeDef *phidm) { if(pcore->host.HC_Status[phidm->hc_num_in]==HC_DATATGLERR)//检测到DTERR错误,直接重启USB. { return 1; } return 0; } //以下两个函数将在usbh_hid_keybd.c库中调用 /******************** * *功能:初始化HID设备 * * *********************/ void USR_KEYBRD_Init(void) { //枚举过后如果这里还没来,那么就说明扫码枪初始化失败了 USB_User.File_Satrt = 0; //全局变量作为一个标记而已 } /******************** * *功能:接收扫码枪数据(此函数只会在有数据的时候才会触发) *data:接收到的数据 *返回值:无 * *********************/ void USR_KEYBRD_ProcessData (uint8_t data) { barcode_data[data]; //到了这里自己定义一个数组去接收数据吧 }

以上就是STM32F407的一个USB扫码枪的配置过程,在接下来将说明为什么枚举成功后“USR_KEYBRD_ProcessData ”函数仿真还是不进的问题。

“USR_KEYBRD_ProcessData ”函数是在“usbh_hid_keybd.c”库文件中的“KEYBRD_Decode”函数中调用了的,那么“KEYBRD_Decode”函数又是在那里调用呢?我们接着往上找。

其实它是“HID_cb_TypeDef”结构体中一个成员,里面就一个初始化跟数据,而“HID_cb_TypeDef”在“usbh_hid_core.h”文件中“HID_Machine_TypeDef”结构里的结构成员。

打开“usbh_hid_core.c 跟 usbh_hid_core.h”库文件,你会发现你所遇到的那些问题都可以在这里面找出来,USR_KEYBRD_ProcessData 函数不被调用的原因可以仿真一步一步的往上找。

写到这里我就不再继续深写下去了,直接说出问题所在

在“usbh_hid_core.c”文件中找到“USBH_HID_ClassRequest”函数,在“USBH_HID_ClassRequest”函数中查看“USBH_Get_HID_Descriptor”函数的返回值是否==USBH_OK,如果不是那么直接将此函数的返回值status = USBH_OK;就这样我的问题得到了解决

上代码

//stm32 usb库代码中的USBH_HID_ClassRequest回调函数 static USBH_Status USBH_HID_ClassRequest(USB_OTG_CORE_HANDLE *pdev , void *phost) { USBH_HOST *pphost = phost; USBH_Status status = USBH_BUSY; USBH_Status classReqStatus = USBH_BUSY; /* Switch HID state machine */ switch (HID_Machine.ctl_state) { case HID_IDLE: case HID_REQ_GET_HID_DESC: /* Get HID Desc 问题就出在这个描述函数中,我是直接将这个函数的返回值变为 USBH_OK */ if (USBH_Get_HID_Descriptor (pdev, pphost, USB_HID_DESC_SIZE)== USBH_OK) { USBH_ParseHIDDesc(&HID_Desc, pdev->host.Rx_Buffer); HID_Machine.ctl_state = HID_REQ_GET_REPORT_DESC; } break; case HID_REQ_GET_REPORT_DESC: /* Get Report Desc */ if (USBH_Get_HID_ReportDescriptor(pdev , pphost, HID_Desc.wItemLength) == USBH_OK) { HID_Machine.ctl_state = HID_REQ_SET_IDLE; } break; case HID_REQ_SET_IDLE: classReqStatus = USBH_Set_Idle (pdev, pphost, 0, 0); /* set Idle */ if (classReqStatus == USBH_OK) { HID_Machine.ctl_state = HID_REQ_SET_PROTOCOL; } else if(classReqStatus == USBH_NOT_SUPPORTED) { HID_Machine.ctl_state = HID_REQ_SET_PROTOCOL; } break; case HID_REQ_SET_PROTOCOL: /* set protocol */ if (USBH_Set_Protocol (pdev ,pphost, 0) == USBH_OK) { HID_Machine.ctl_state = HID_REQ_IDLE; /* all requests performed*/ status = USBH_OK; } break; default: break; } return status; }

以上就是我所遇到的问题后一步一步的找出来去解决的,希望能帮助到有需要的人,这是我第一次发的技术博客文档,写的不好的地方还请见谅。

最后在这里说明一下如有侵权请联系本作者,转发请说明出处,谢谢。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3