现在主流的安卓手机数据连接线,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(myhid_DataReceived); ///
/// 事件:数据到达,处理此事件以接收输入数据
///
public event EventHandler DataReceived;
protected virtual void OnDataReceived(Report e)
{
DataReceived.Invoke(this, e);
}
///
/// 创建一个非UI线程访问界面线程
///
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();
}
}
///
/// 异步读取结束,发出有数据到达事件
///
/// 这里是输入报告的数组
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;
}
///
///
///
///
///
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线程控制去访问控件。
///
/// 向下位机发送数据,下位机收到应答返回应答。
/// 关键点:实现Encoding.Default.GetBytes(str)
///
///
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);
}
}