451 lines
22 KiB
C
451 lines
22 KiB
C
|
||
/********************************** (C) COPYRIGHT *******************************
|
||
* File Name :CompatibilityHID.C
|
||
* Author : WCH
|
||
* Version : V1.0
|
||
* Date : 2017/01/20
|
||
* Description : CH554模拟HID兼容设备,支持中断上下传,支持设置全速,低速
|
||
*******************************************************************************/
|
||
|
||
#include "./Public/CH554.H"
|
||
#include "./Public/Debug.H"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
#define Fullspeed 1
|
||
#ifdef Fullspeed
|
||
#define THIS_ENDP0_SIZE 64
|
||
#else
|
||
#define THIS_ENDP0_SIZE DEFAULT_ENDP0_SIZE
|
||
#endif
|
||
UINT8X Ep0Buffer[8<(THIS_ENDP0_SIZE+2)?8:(THIS_ENDP0_SIZE+2)] _at_ 0x0000; //端点0 OUT&IN缓冲区,必须是偶地址
|
||
UINT8X Ep2Buffer[128<(2*MAX_PACKET_SIZE+4)?128:(2*MAX_PACKET_SIZE+4)] _at_ 0x0044;//端点2 IN&OUT缓冲区,必须是偶地址
|
||
UINT8 SetupReq,SetupLen,Ready,Count,FLAG,UsbConfig;
|
||
PUINT8 pDescr; //USB配置标志
|
||
USB_SETUP_REQ SetupReqBuf; //暂存Setup包
|
||
#define UsbSetupBuf ((PUSB_SETUP_REQ)Ep0Buffer)
|
||
|
||
sbit Ep2InKey = P1^5; //K1按键
|
||
#pragma NOAREGS
|
||
/*设备描述符*/
|
||
UINT8C DevDesc[18] = {0x12,0x01,0x10,0x01,0x00,0x00,0x00,THIS_ENDP0_SIZE,
|
||
0x31,0x51,0x07,0x20,0x00,0x00,0x00,0x00,
|
||
0x00,0x01
|
||
};
|
||
UINT8C CfgDesc[41] =
|
||
{
|
||
0x09,0x02,0x29,0x00,0x01,0x01,0x04,0xA0,0x23, //配置描述符
|
||
0x09,0x04,0x00,0x00,0x02,0x03,0x00,0x00,0x05, //接口描述符
|
||
0x09,0x21,0x00,0x01,0x00,0x01,0x22,0x22,0x00, //HID类描述符
|
||
0x07,0x05,0x82,0x03,THIS_ENDP0_SIZE,0x00,0x18, //端点描述符
|
||
0x07,0x05,0x02,0x03,THIS_ENDP0_SIZE,0x00,0x18, //端点描述符
|
||
};
|
||
/*字符串描述符 略*/
|
||
|
||
/*HID类报表描述符*/
|
||
UINT8C HIDRepDesc[ ] =
|
||
{
|
||
0x06, 0x00,0xff,
|
||
0x09, 0x01,
|
||
0xa1, 0x01, //集合开始
|
||
0x09, 0x02, //Usage Page 用法
|
||
0x15, 0x00, //Logical Minimun
|
||
0x26, 0x00,0xff, //Logical Maximun
|
||
0x75, 0x08, //Report Size
|
||
0x95, THIS_ENDP0_SIZE, //Report Counet
|
||
0x81, 0x06, //Input
|
||
0x09, 0x02, //Usage Page 用法
|
||
0x15, 0x00, //Logical Minimun
|
||
0x26, 0x00,0xff, //Logical Maximun
|
||
0x75, 0x08, //Report Size
|
||
0x95, THIS_ENDP0_SIZE, //Report Counet
|
||
0x91, 0x06, //Output
|
||
0xC0
|
||
};
|
||
// unsigned char code LangDes[]={0x04,0x03,0x09,0x04}; //语言描述符
|
||
// unsigned char code SerDes[]={
|
||
// 0x28,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||
// 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||
// 0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x43,0x00,0x42,
|
||
// 0x00,0x43,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00
|
||
// }; //字符串描述符
|
||
|
||
UINT8X UserEp2Buf[64]; //用户数据定义
|
||
|
||
/*******************************************************************************
|
||
* Function Name : USBDeviceInit()
|
||
* Description : USB设备模式配置,设备模式启动,收发端点配置,中断开启
|
||
* Input : None
|
||
* Output : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
void USBDeviceInit()
|
||
{
|
||
IE_USB = 0;
|
||
USB_CTRL = 0x00; // 先设定USB设备模式
|
||
#ifndef Fullspeed
|
||
UDEV_CTRL |= bUD_LOW_SPEED; //选择低速1.5M模式
|
||
#else
|
||
UDEV_CTRL &= ~bUD_LOW_SPEED; //选择全速12M模式,默认方式
|
||
#endif
|
||
UEP2_DMA = Ep2Buffer; //端点2数据传输地址
|
||
UEP2_3_MOD |= bUEP2_TX_EN; //端点2发送使能
|
||
UEP2_3_MOD |= bUEP2_RX_EN; //端点2接收使能
|
||
UEP2_3_MOD &= ~bUEP2_BUF_MOD; //端点2收发各64字节缓冲区
|
||
UEP2_CTRL = bUEP_AUTO_TOG | UEP_T_RES_NAK | UEP_R_RES_ACK; //端点2自动翻转同步标志位,IN事务返回NAK,OUT返回ACK
|
||
UEP0_DMA = Ep0Buffer; //端点0数据传输地址
|
||
UEP4_1_MOD &= ~(bUEP4_RX_EN | bUEP4_TX_EN); //端点0单64字节收发缓冲区
|
||
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; //OUT事务返回ACK,IN事务返回NAK
|
||
|
||
USB_DEV_AD = 0x00;
|
||
UDEV_CTRL = bUD_PD_DIS; // 禁止DP/DM下拉电阻
|
||
USB_CTRL = bUC_DEV_PU_EN | bUC_INT_BUSY | bUC_DMA_EN; // 启动USB设备及DMA,在中断期间中断标志未清除前自动返回NAK
|
||
UDEV_CTRL |= bUD_PORT_EN; // 允许USB端口
|
||
USB_INT_FG = 0xFF; // 清中断标志
|
||
USB_INT_EN = bUIE_SUSPEND | bUIE_TRANSFER | bUIE_BUS_RST;
|
||
IE_USB = 1;
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : Enp2BlukIn()
|
||
* Description : USB设备模式端点2的批量上传
|
||
* Input : None
|
||
* Output : None
|
||
* Return : None
|
||
*******************************************************************************/
|
||
void Enp2BlukIn( )
|
||
{
|
||
memcpy( Ep2Buffer+MAX_PACKET_SIZE, UserEp2Buf, sizeof(UserEp2Buf)); //加载上传数据
|
||
UEP2_T_LEN = THIS_ENDP0_SIZE; //上传最大包长度
|
||
UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; //有数据时上传数据并应答ACK
|
||
while(UEP2_CTRL&UEP_T_RES_ACK); //等待传输完成
|
||
}
|
||
|
||
/*******************************************************************************
|
||
* Function Name : DeviceInterrupt()
|
||
* Description : CH559USB中断处理函数
|
||
*******************************************************************************/
|
||
void DeviceInterrupt( void ) interrupt INT_NO_USB using 1 //USB中断服务程序,使用寄存器组1
|
||
{
|
||
UINT8 len,i;
|
||
if(UIF_TRANSFER) //USB传输完成标志
|
||
{
|
||
switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
|
||
{
|
||
case UIS_TOKEN_IN | 2: //endpoint 2# 端点批量上传
|
||
UEP2_T_LEN = 0; //预使用发送长度一定要清空
|
||
// UEP1_CTRL ^= bUEP_T_TOG; //如果不设置自动翻转则需要手动翻转
|
||
UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK; //默认应答NAK
|
||
break;
|
||
case UIS_TOKEN_OUT | 2: //endpoint 2# 端点批量下传
|
||
if ( U_TOG_OK ) // 不同步的数据包将丢弃
|
||
{
|
||
len = USB_RX_LEN; //接收数据长度,数据从Ep2Buffer首地址开始存放
|
||
for ( i = 0; i < len; i ++ )
|
||
{
|
||
Ep2Buffer[MAX_PACKET_SIZE+i] = Ep2Buffer[i] ^ 0xFF; // OUT数据取反到IN由计算机验证
|
||
}
|
||
UEP2_T_LEN = len;
|
||
UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK; // 允许上传
|
||
}
|
||
break;
|
||
case UIS_TOKEN_SETUP | 0: //SETUP事务
|
||
len = USB_RX_LEN;
|
||
if(len == (sizeof(USB_SETUP_REQ)))
|
||
{
|
||
SetupLen = UsbSetupBuf->wLengthL;
|
||
if(UsbSetupBuf->wLengthH || SetupLen > 0x7F )
|
||
{
|
||
SetupLen = 0x7F; // 限制总长度
|
||
}
|
||
len = 0; // 默认为成功并且上传0长度
|
||
SetupReq = UsbSetupBuf->bRequest;
|
||
if ( ( UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )/*HID类命令*/
|
||
{
|
||
switch( SetupReq )
|
||
{
|
||
case 0x01: //GetReport
|
||
break;
|
||
case 0x02: //GetIdle
|
||
break;
|
||
case 0x03: //GetProtocol
|
||
break;
|
||
case 0x09: //SetReport
|
||
break;
|
||
case 0x0A: //SetIdle
|
||
break;
|
||
case 0x0B: //SetProtocol
|
||
break;
|
||
default:
|
||
len = 0xFF; /*命令不支持*/
|
||
break;
|
||
}
|
||
}
|
||
else //标准请求
|
||
{
|
||
switch(SetupReq) //请求码
|
||
{
|
||
case USB_GET_DESCRIPTOR:
|
||
switch(UsbSetupBuf->wValueH)
|
||
{
|
||
case 1: //设备描述符
|
||
pDescr = DevDesc; //把设备描述符送到要发送的缓冲区
|
||
len = sizeof(DevDesc);
|
||
break;
|
||
case 2: //配置描述符
|
||
pDescr = CfgDesc; //把设备描述符送到要发送的缓冲区
|
||
len = sizeof(CfgDesc);
|
||
break;
|
||
case 0x22: //报表描述符
|
||
pDescr = HIDRepDesc; //数据准备上传
|
||
len = sizeof(HIDRepDesc);
|
||
Ready = 1; //如果有更多接口,该标准位应该在最后一个接口配置完成后有效
|
||
break;
|
||
default:
|
||
len = 0xff; //不支持的命令或者出错
|
||
break;
|
||
}
|
||
if ( SetupLen > len )
|
||
{
|
||
SetupLen = len; //限制总长度
|
||
}
|
||
len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE : SetupLen;//本次传输长度
|
||
memcpy(Ep0Buffer,pDescr,len); //加载上传数据
|
||
SetupLen -= len;
|
||
pDescr += len;
|
||
break;
|
||
case USB_SET_ADDRESS:
|
||
SetupLen = UsbSetupBuf->wValueL; //暂存USB设备地址
|
||
break;
|
||
case USB_GET_CONFIGURATION:
|
||
Ep0Buffer[0] = UsbConfig;
|
||
if ( SetupLen >= 1 )
|
||
{
|
||
len = 1;
|
||
}
|
||
break;
|
||
case USB_SET_CONFIGURATION:
|
||
UsbConfig = UsbSetupBuf->wValueL;
|
||
break;
|
||
case 0x0A:
|
||
break;
|
||
case USB_CLEAR_FEATURE: //Clear Feature
|
||
if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )// 端点
|
||
{
|
||
switch( UsbSetupBuf->wIndexL )
|
||
{
|
||
case 0x82:
|
||
UEP2_CTRL = UEP2_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
|
||
break;
|
||
case 0x81:
|
||
UEP1_CTRL = UEP1_CTRL & ~ ( bUEP_T_TOG | MASK_UEP_T_RES ) | UEP_T_RES_NAK;
|
||
break;
|
||
case 0x02:
|
||
UEP2_CTRL = UEP2_CTRL & ~ ( bUEP_R_TOG | MASK_UEP_R_RES ) | UEP_R_RES_ACK;
|
||
break;
|
||
default:
|
||
len = 0xFF; // 不支持的端点
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
len = 0xFF; // 不是端点不支持
|
||
}
|
||
break;
|
||
case USB_SET_FEATURE: /* Set Feature */
|
||
if( ( UsbSetupBuf->bRequestType & 0x1F ) == 0x00 ) /* 设置设备 */
|
||
{
|
||
if( ( ( ( UINT16 )UsbSetupBuf->wValueH << 8 ) | UsbSetupBuf->wValueL ) == 0x01 )
|
||
{
|
||
if( CfgDesc[ 7 ] & 0x20 )
|
||
{
|
||
/* 设置唤醒使能标志 */
|
||
}
|
||
else
|
||
{
|
||
len = 0xFF; /* 操作失败 */
|
||
}
|
||
}
|
||
else
|
||
{
|
||
len = 0xFF; /* 操作失败 */
|
||
}
|
||
}
|
||
else if( ( UsbSetupBuf->bRequestType & 0x1F ) == 0x02 ) /* 设置端点 */
|
||
{
|
||
if( ( ( ( UINT16 )UsbSetupBuf->wValueH << 8 ) | UsbSetupBuf->wValueL ) == 0x00 )
|
||
{
|
||
switch( ( ( UINT16 )UsbSetupBuf->wIndexH << 8 ) | UsbSetupBuf->wIndexL )
|
||
{
|
||
case 0x82:
|
||
UEP2_CTRL = UEP2_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;/* 设置端点2 IN STALL */
|
||
break;
|
||
case 0x02:
|
||
UEP2_CTRL = UEP2_CTRL & (~bUEP_R_TOG) | UEP_R_RES_STALL;/* 设置端点2 OUT Stall */
|
||
break;
|
||
case 0x81:
|
||
UEP1_CTRL = UEP1_CTRL & (~bUEP_T_TOG) | UEP_T_RES_STALL;/* 设置端点1 IN STALL */
|
||
break;
|
||
default:
|
||
len = 0xFF; /* 操作失败 */
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
len = 0xFF; /* 操作失败 */
|
||
}
|
||
}
|
||
else
|
||
{
|
||
len = 0xFF; /* 操作失败 */
|
||
}
|
||
break;
|
||
case USB_GET_STATUS:
|
||
Ep0Buffer[0] = 0x00;
|
||
Ep0Buffer[1] = 0x00;
|
||
if ( SetupLen >= 2 )
|
||
{
|
||
len = 2;
|
||
}
|
||
else
|
||
{
|
||
len = SetupLen;
|
||
}
|
||
break;
|
||
default:
|
||
len = 0xff; //操作失败
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
len = 0xff; //包长度错误
|
||
}
|
||
if(len == 0xff)
|
||
{
|
||
SetupReq = 0xFF;
|
||
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;//STALL
|
||
}
|
||
else if(len <= THIS_ENDP0_SIZE) //上传数据或者状态阶段返回0长度包
|
||
{
|
||
UEP0_T_LEN = len;
|
||
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;//默认数据包是DATA1,返回应答ACK
|
||
}
|
||
else
|
||
{
|
||
UEP0_T_LEN = 0; //虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
|
||
UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;//默认数据包是DATA1,返回应答ACK
|
||
}
|
||
break;
|
||
case UIS_TOKEN_IN | 0: //endpoint0 IN
|
||
switch(SetupReq)
|
||
{
|
||
case USB_GET_DESCRIPTOR:
|
||
len = SetupLen >= THIS_ENDP0_SIZE ? THIS_ENDP0_SIZE : SetupLen; //本次传输长度
|
||
memcpy( Ep0Buffer, pDescr, len ); //加载上传数据
|
||
SetupLen -= len;
|
||
pDescr += len;
|
||
UEP0_T_LEN = len;
|
||
UEP0_CTRL ^= bUEP_T_TOG; //同步标志位翻转
|
||
break;
|
||
case USB_SET_ADDRESS:
|
||
USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen;
|
||
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
|
||
break;
|
||
default:
|
||
UEP0_T_LEN = 0; //状态阶段完成中断或者是强制上传0长度数据包结束控制传输
|
||
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
|
||
break;
|
||
}
|
||
break;
|
||
case UIS_TOKEN_OUT | 0: // endpoint0 OUT
|
||
len = USB_RX_LEN;
|
||
if(SetupReq == 0x09)
|
||
{
|
||
if(Ep0Buffer[0])
|
||
{
|
||
printf("Light on Num Lock LED!\n");
|
||
}
|
||
else if(Ep0Buffer[0] == 0)
|
||
{
|
||
printf("Light off Num Lock LED!\n");
|
||
}
|
||
}
|
||
UEP0_T_LEN = 0; //虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
|
||
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_ACK;//默认数据包是DATA0,返回应答ACK
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
UIF_TRANSFER = 0; //写0清空中断
|
||
}
|
||
if(UIF_BUS_RST) //设备模式USB总线复位中断
|
||
{
|
||
UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
|
||
UEP1_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK;
|
||
UEP2_CTRL = bUEP_AUTO_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
|
||
USB_DEV_AD = 0x00;
|
||
UIF_SUSPEND = 0;
|
||
UIF_TRANSFER = 0;
|
||
UIF_BUS_RST = 0; //清中断标志
|
||
}
|
||
if (UIF_SUSPEND) //USB总线挂起/唤醒完成
|
||
{
|
||
UIF_SUSPEND = 0;
|
||
if ( USB_MIS_ST & bUMS_SUSPEND ) //挂起
|
||
{
|
||
#ifdef DE_PRINTF
|
||
printf( "zz" ); //睡眠状态
|
||
#endif
|
||
while ( XBUS_AUX & bUART0_TX )
|
||
{
|
||
; //等待发送完成
|
||
}
|
||
SAFE_MOD = 0x55;
|
||
SAFE_MOD = 0xAA;
|
||
WAKE_CTRL = bWAK_BY_USB | bWAK_RXD0_LO; //USB或者RXD0有信号时可被唤醒
|
||
PCON |= PD; //睡眠
|
||
SAFE_MOD = 0x55;
|
||
SAFE_MOD = 0xAA;
|
||
WAKE_CTRL = 0x00;
|
||
}
|
||
}
|
||
else { //意外的中断,不可能发生的情况
|
||
USB_INT_FG = 0xFF; //清中断标志
|
||
// printf("UnknownInt N");
|
||
}
|
||
}
|
||
|
||
main()
|
||
{
|
||
UINT8 i;
|
||
CfgFsys( ); //CH559时钟选择配置
|
||
mDelaymS(5); //修改主频等待内部晶振稳定,必加
|
||
mInitSTDIO( ); //串口0初始化
|
||
#ifdef DE_PRINTF
|
||
printf("start ...\n");
|
||
#endif
|
||
for(i=0; i<64; i++) //准备演示数据
|
||
{
|
||
UserEp2Buf[i] = i;
|
||
}
|
||
USBDeviceInit(); //USB设备模式初始化
|
||
EA = 1; //允许单片机中断
|
||
UEP1_T_LEN = 0; //预使用发送长度一定要清空
|
||
UEP2_T_LEN = 0; //预使用发送长度一定要清空
|
||
FLAG = 0;
|
||
Ready = 0;
|
||
while(1)
|
||
{
|
||
if(Ready&& (Ep2InKey==0))
|
||
{
|
||
Enp2BlukIn( );
|
||
mDelaymS( 100 );
|
||
}
|
||
mDelaymS( 100 ); //模拟单片机做其它事
|
||
}
|
||
}
|