基于stm32的自定义HID设备开发与上位机通讯实现(附源代码)

现在主流的安卓手机数据连接线,Mini-usb、Micro-usb,Type-c,产品追随主流,摒弃ST-LINK、JLINK,直接用usb数据传输升级。主要实现与HID设备的通信即人机交互。本文主要介绍了HID设备的下位机通信连接与上位机设备识别。

下位机:

1.准备工作,所需文件,如下图所示:

2.环境搭建:建立keil开发环境文件,并添加相应的源文件,不作详细解释。    

3、在platform_config.h文件中修改相应的gpio口,只需要修改UDBDP,因为这个gpio口接了一个上拉电阻。

#define USB_DISCONNECT GPIOA 
#define USB_DISCONNECT_PIN GPIO_Pin_12 
#define RCC_APB2Periph_GPIO_DISCONNECT RCC_APB2Periph_GPIOB

4、加入usb设备初始化

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIO_DISCONNECT, ENABLE); 
GPIO_InitStructure.GPIO_Pin = USB_DISCONNECT_PIN; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 
GPIO_Init(USB_DISCONNECT, &GPIO_InitStructure);

5.在usb_desc.c中修改设备描述符(PID和VID):

6、设置USB时钟

void Set_USBClock(void) {
RCC_OTGFSCLKConfig(RCC_OTGFSCLKSource_PLLVCO_Div3); 
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS,ENABLE); 
RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5); 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE); }

上位机:

    主要调用了windows系统库user32.dll、hid.dll和setupapi.dll

        以下是调用windows的API的函数 ,下面关于API接口部分调用举例:

        // 获得GUID
        [DllImport("hid.dll")]
        private static extern void HidD_GetHidGuid(ref Guid HidGuid);

        //过滤设备,获取需要的设备
        [DllImport("setupapi.dll", SetLastError = true)]
        private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, uint Enumerator, IntPtr HwndParent, DIGCF Flags);

        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern Boolean SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet);

         //获取设备,true获取到
        [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, IntPtr deviceInfoData, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

   上位机关键处理:非UI线程访问界面线程以处理及数据发送。

           示例①接收数据处理:

          myHid.DataReceived += new EventHandler<HID.Report>(myhid_DataReceived); /// <summary>
        /// 事件:数据到达,处理此事件以接收输入数据
        /// </summary>
        public event EventHandler<Report> DataReceived;
        protected virtual void OnDataReceived(Report e)
        {
            DataReceived.Invoke(this, e);
        }
        /// <summary>
        /// 创建一个非UI线程访问界面线程
        /// </summary>
        string receiveData = "";
        //数据到达事件
        protected void myhid_DataReceived(object sender, report e)
        {
            RecDataBuffer = e.reportBuff;
            receiveData = "";
            receiveData = new ASCIIEncoding().GetString(RecDataBuffer);
            //MessageBox.Show(receiveData);
            SetShootRecoordText(receiveData);
        }
        private delegate void SetShootRecoordTextCallback(string text);
        //在给textBox1.text赋值的地方调用以下方法即可
        private void SetShootRecoordText(string text)
        {
            // InvokeRequired需要比较调用线程ID和创建线程ID
            // 如果它们不相同则返回true
            if (this.msg.InvokeRequired)
            {
                SetShootRecoordTextCallback d = new SetShootRecoordTextCallback(SetShootRecoordText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.msg.Text += text;
                msg.SelectionStart = msg.Text.Length;
                msg.ScrollToCaret();
            }

        }
        /// <summary>
        /// 异步读取结束,发出有数据到达事件
        /// </summary>
        /// <param name="iResult">这里是输入报告的数组</param>
        private void ReadCompleted(IAsyncResult iResult)
        {
            byte[] readBuff = (byte[])(iResult.AsyncState);
            try
            {
                if (deviceOpened == true)
                {
                    hidDevice.EndRead(iResult);//读取结束,如果读取错误就会产生一个异常
                    byte[] reportData = new byte[readBuff.Length - 1];
                    for (int i = 1; i < readBuff.Length; i++)
                        reportData[i - 1] = readBuff[i];
                    Report e = new Report(readBuff[0], reportData);
                    OnDataReceived(e); //发出数据到达消息
                    BeginAsyncRead();//启动下一次读操作
                }
            }
            catch (IOException e)//读写错误,设备可能已经被移除
            {
                EventArgs ex = new EventArgs();
                OnDeviceRemoved(ex);//发出设备移除消息
            }
        }

            示例②数据发送处理:

        public readonly byte reportID;
        public readonly byte[] reportBuff;
        public Report(byte id, byte[] arrayBuff)
        {
            reportID = id;
            reportBuff = arrayBuff;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public HID_RETURN Write(Report r)
        {
            if (deviceOpened) 
            {
                try
                {
                    byte[] buffer = new byte[outputReportLength];
                    buffer[0] = r.reportID;
                    int maxBufferLength = 0;
                    if (r.reportBuff.Length < outputReportLength - 1)
                        maxBufferLength = r.reportBuff.Length;
                    else
                        maxBufferLength = outputReportLength - 1;
                    for (int i = 1; i <= maxBufferLength; i++)
                        buffer[i] = r.reportBuff[i - 1];
                    hidDevice.Write(buffer, 0, OutputReportLength);
                }
                catch
                {
                    EventArgs ex = new EventArgs();
                    OnDeviceRemoved(ex);//发出设备移除消息                  
                }
            }
            return HID_RETURN.WRITE_FAILD;
        }

        上位机避免应用程序运行卡死,主要方式是定义委托,用非UI线程控制去访问控件。                                    

测试结果:

 /// <summary>
        /// 向下位机发送数据,下位机收到应答返回应答。
        /// </summary> 关键点:实现Encoding.Default.GetBytes(str)
        /// <param name="sender"></param>
        /// <param name="e"></param>

        private void LED_CheckedChanged(object sender, EventArgs e)
        {
            Byte[] data = new Byte[64];
            string str = "hello stm32!";
            if (LedCheck.CheckState == CheckState.Checked)
            {
                //data[1] = Convert.ToByte(0x01););
                LedCheck.BackColor = Color.Green;
            }
            else
            {
                LedCheck.BackColor = GroupBox.DefaultBackColor;
            }
            data = Encoding.Default.GetBytes(str);
            Report r = new Report(0, data);
            myHid.Write(r);
        }
    }
int main(void)
{
	static int flag=0;
	uint8_t ret=-1;
	uint8_t Rcvdata[64];
  uint8_t Sendata[64]="hello hid !";
	Set_System();//系统时钟初始化
	USB_Interrupts_Config();
	Set_USBClock();
	USB_Init();
	LED_Initializes();
	LED_ON;
	
	while(1)
	{
		ret=USB_GetData(Rcvdata,sizeof(Rcvdata));
		if(ret==(sizeof(Rcvdata))&& strcmp(Rcvdata,"hello stm32!")==0)
		    {
			flag=1;
		    }
		
                if(flag==1)
		    {
		    USB_SendData(Rcvdata,sizeof(Rcvdata));
		    delay(10000000);
		    USB_SendData(Sendata,sizeof(Sendata));
		    delay(10000000);
	            FreeUserBuffer(ENDP2, EP_DBUF_OUT);
		    ClearDTOG_TX(ENDP2);
		    memset(Rcvdata,sizeof(uint8_t),sizeof(Rcvdata));
		    flag=0;
		}
		    FreeUserBuffer(ENDP1,EP_DBUF_IN);
		    ClearDTOG_RX(ENDP1);
		    LED_OFF;
		    delay(1000000);
		    LED_ON;
		    delay(1000000);
	}
		
}

上位机(c#,vs2017):点击下载

下位机(stm32f103,keil5):点击下载

猜你喜欢

转载自blog.csdn.net/github_37687123/article/details/80998607
今日推荐