📄 main.c
字号:
void USBCtrlTrfSetupHandler()
{
ctrl_trf_state = WAIT_SETUP; // USB控制传输状态为等待
ctrl_trf_session_owner = MUID_NULL; //此时USB EP0端点数据不归任何类所有
wCount._word = 0; //字节计数器清0,用于记录传送数据的大小
USBCheckStdRequest(); //检测USB的标准请求
if(ctrl_trf_session_owner == MUID_NULL) // 如果上边没有请求,或请求不存在,检测HID请求
{
USBCheckHIDRequest(); // USB HID请求检测
}//end if
USBCtrlEPServiceComplete(); // 控制传输完成
}//end USBCtrlTrfSetupHandler()
/*******************************
* USB控制数据输出处理函数
* 数据从主机-设备
********************************/
void USBCtrlTrfOutHandler()
{
//说明:此程序是控制传输的数主机请求数据阶段
if(ctrl_trf_state == CTRL_TRF_RX) // 如果控制状态是,数据读取(数据主机到设备)
{
USBCtrlTrfRxService(); // USB控制数据输出
if (ctrl_trf_session_owner == MUID_HID) // 如果此时的传输属于HID所有
{
HandleControlOutReport(); // HID控制输出报表(主机输出报表到设备)
}
if(ep0Bo.Stat.DTS == 0) // DATA0与DATA1 交替
ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN;
else
ep0Bo.Stat._byte = _USIE|_DAT0|_DTSEN;
}//end if
else // 否则这是控制读的状态阶段
USBPrepareForNextSetupTrf(); // 准备下一次的控制传输
}//end USBCtrlTrfOutHandler()
/********************************
* USB控制数据输入处理函数
* 数据从设备到主机
*********************************/
void USBCtrlTrfInHandler()
{
mUSBCheckAdrPendingState(); // 因为这个是控制写(假设是设定地址)传输的最后一步,可以算是状态,在这里要检测地址状态
if(ctrl_trf_state == CTRL_TRF_TX) // 数据发送
{
USBCtrlTrfTxService(); // 数据加载到到发送缓冲区
if(ep0Bi.Stat.DTS == 0)
ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN;
else
ep0Bi.Stat._byte = _USIE|_DAT0|_DTSEN;
}
else // 否则这是控制写的状态阶段
USBPrepareForNextSetupTrf();
}//end USBCtrlTrfInHandler()
/*****************************
* 控制传输完成函数
******************************/
void USBCtrlEPServiceComplete()
{
//说明:令牌和包处理位在收到一个设置传输后将置位(禁止),要在改变ep0Bi.Stat or ep0Bo.Stat前清0(使能)
//控制传输第一步:设置阶段
UCONbits.PKTDIS = 0; //使能令牌和包接收
if(ctrl_trf_session_owner == MUID_NULL) // 请求不被支持,则直接发出端点停止信号
{
/*
如果主机的请求不被支持,或不知道怎么回应,则停止端点发出信号,同时准备好EP0
接收下一次的请求
*/
ep0Bo.Cnt = EP0_BUFF_SIZE; // 设置缓冲器大小,这样可以准备接收下一次的令牌
ep0Bo.ADR = (byte*)&SetupPkt; // 设置缓冲器地址
ep0Bo.Stat._byte = _USIE|_BSTALL; // 端点归SIE,禁止端点发出禁止端点信令
ep0Bi.Stat._byte = _USIE|_BSTALL;
}
else // 否则的话请求是能被执行的
{
if(SetupPkt.DataDir == 1) // 如果请求数据设备到主机
{
if(SetupPkt.wLength < wCount._word) // wLength是主机请求长度
wCount._word = SetupPkt.wLength; // 按照主机请求字节发送数据
USBCtrlTrfTxService(); // USB控制传输数据从设备到主机服务程序
ctrl_trf_state = CTRL_TRF_TX; // 现在处于CTRL_TRF_TX设备传送数据状态
ep0Bo.Cnt = EP0_BUFF_SIZE;
ep0Bo.ADR = (byte*)&SetupPkt;
ep0Bo.Stat._byte = _USIE; // DTSEN = 0,即不使能同步,准备接收主机发送来的信号
ep0Bi.ADR = (byte*)&CtrlTrfData;
ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; // 启动发送,启动数据发送
}
else // 否则数据从主机到设备
{
ctrl_trf_state = CTRL_TRF_RX; // 控制传输接收状态
/*
1.IN 端点准备回应上一次的终止
*/
ep0Bi.Cnt = 0; //准备状态阶段的设备回应,设备在状态阶段回应0长度的数据包
ep0Bi.Stat._byte = _USIE|_DAT1|_DTSEN; //发送DATA1数据包
/*
2.准备OUT端点接收数据,这样已经设置了数据大小以及数据区
*/
ep0Bo.Cnt = EP0_BUFF_SIZE;
ep0Bo.ADR = (byte*)&CtrlTrfData;
ep0Bo.Stat._byte = _USIE|_DAT1|_DTSEN;
}
}//end if
}//end USBCtrlEPServiceComplete()
/******************************
* USB控制传输(数据设备到主机)
******************************/
//此程序只完成数据的准备工作
void USBCtrlTrfTxService()
{
WORD byte_to_send; // 定义计数字节
if(wCount._word < EP0_BUFF_SIZE) // 判断发送的字节数是否小于EP0_BUFF_SIZE
byte_to_send._word = wCount._word; // 如果小于则发送实际值
else
byte_to_send._word = EP0_BUFF_SIZE; // 否则发送EP0_BUFF_SIZE大小的字节
/*
接下来,加载发送字节数目,最多8个字节,数据区就8个字节
*/
ep0Bi.Stat.BC9 = 0; // 计数字节高两位清0
ep0Bi.Stat.BC8 = 0;
ep0Bi.Stat._byte |= MSB(byte_to_send); // 设置传输字节数目
ep0Bi.Cnt = LSB(byte_to_send);
// 从总发送字节中减掉本次发送的字节数
wCount._word = wCount._word - byte_to_send._word;
pDst.bRam = (byte*)&CtrlTrfData; // 设定数据区指针,发送数据区(在400区内)
if(usb_stat.ctrl_trf_mem == _ROM) // 如果数据存放在ROM区,取出数据
{
while(byte_to_send._word)
{
*pDst.bRam = *pSrc.bRom;
pDst.bRam++;
pSrc.bRom++;
byte_to_send._word--;
}
}
else
{
while(byte_to_send._word)
{
*pDst.bRam = *pSrc.bRam;
pDst.bRam++;
pSrc.bRam++;
byte_to_send._word--;
}//end while(byte_to_send._word)
}//end if
}//end USBCtrlTrfTxService()
/********************************
* USB控制数据输出数据主机到设备
********************************/
void USBCtrlTrfRxService()
{
WORD byte_to_read; // 接收数据字节数计数
MSB(byte_to_read) = 0x03 & ep0Bo.Stat._byte; // 保留低两位,取出接收字节数,是主机给定的
LSB(byte_to_read) = ep0Bo.Cnt;
wCount._word = wCount._word + byte_to_read._word; // 接收字节数加操作
pSrc.bRam = (byte*)&CtrlTrfData;
while(byte_to_read._word) // 数据取出
{
*pDst.bRam = *pSrc.bRam;
pDst.bRam++;
pSrc.bRam++;
byte_to_read._word--;
}//end while(byte_to_read._word)
}//end USBCtrlTrfRxService()
//---------------------------------------------------------end 控制传输函数区
//---------------------------------------------------------USB 传输协议(请求)函数定义
/*******************************
* 检测USB的标准请求
*********************************/
void USBCheckStdRequest()
{
if(SetupPkt.RequestType != STANDARD) return; // 如果不是USB标准请求返回
switch(SetupPkt.bRequest) //选择请求类型
{
case SET_ADR: //=5 主机请求地址配置
//说明: 设备不会执行此请求,除非它已经设置一个0长度数据信息包,来完成请求的状态阶段.
// 主机传送状态阶段的令牌信息包给默认地址0,所以设备必须在改变它的地址前检测并响应此信息包
// wVAlue字段内容:新设备地址
// wIndex字段内容: 0
ctrl_trf_session_owner = MUID_USB9; // MUID_USB9=1 USB9是USB驱动所有
usb_device_state = ADR_PENDING_STATE; // 只更新状态,进入到地址未定状态
break;
case GET_DSC: //6 获得描述符表
//说明:描述符有7种类型,每一设备至少有一个设备描述符,
// 以及至少一个配置描述符和一个接口描述符
// 数据来源:设备
// 数据长度:传回字节的数目主机给定
// wValue: 高字节是描述符类型,低字节是描述符数值
// wIndex: 如果是字符串描述符此字段是语言ID,其他是0
USBStdGetDscHandler(); //USB标准获得描述符表处理
break;
case SET_CFG: //9 指示设备使用所选择的配置
//说明:在完成这个请求来指定一个支持的设置配置后,设备会进入配置状态。
// 许多标准请求需要设备在设置状态
//数据长度:0
//wValue字段:低字节指示一个设置配置。如果此字段的数值符合设备所支持的一个配置,设备会选择
// 该请求的配置。如果是0表示尚未设置配置,进入地址状态。需要新的请求来设置配置
//wIndex字段:0
//支持状态: 地址、配置
USBStdSetCfgHandler(); //USB标准设置设备配置处理
break;
case GET_CFG: //8 主机请求目前设备配置的数值
//说明:如果设备没有设置配置,则传回0
//数据长度:1
//wValue字段:0
//wIndex字段:0
ctrl_trf_session_owner = MUID_USB9; // USB9是USB驱动所有
pSrc.bRam = (byte*)&usb_active_cfg; // 数据首地址
usb_stat.ctrl_trf_mem = _RAM; // 数据定义在RAM区
wCount._word = 1; // 数据字节计数=1
break;
case GET_STATUS: // 0 主机请求一个设备、接口或端点的特征状态
//说明:如果是设备请求只有两个位。位0是自身电源字段。主机不能改变此数值。
// 位1是远程唤醒字段,在重置事0。如果是接口的请求。所有位保留。如果是
// 端点请求,只有位0有意义,位0=1是一个暂停态(Halt).
//数据字节:2
//wValue字段:0
//wIndex字段:设备=0。如果是接口为接口号,如果是端点=端点号
USBStdGetStatusHandler(); // 主机获得状态处理函数
break;
case CLR_FEATURE: //1 主机请求禁用一个在设备,接口或端点上的特征
// 将继续往下执行
case SET_FEATURE: //3 主机请求启用一个在设备,接口或端点上的特征
//说明:USB规范定义两个特性:即DEVICE_REMOTE_WAKEUP(数值为1,应用在设备)
// 以及ENDPOINT_HALT(数值为0,应用在端点)
//数据:无
//wValue字段:要启用的特性 DEVICE_REMOTE_WAKEUP 1 设备
// ENDPOINT_HALT 0 端点
// TEST_MODE 2 设备
//wIndex字段:如果是设备的特性此字段为0,如果是接口此字段为接口号码
// 如果是端点特性,此字段是端点号码
USBStdFeatureReqHandler(); //主机请求启用特征值处理函数
break;
case GET_INTF: //10 如果设备的配置支持多个互不相关设置的接口,主机请求目前的状态
//说明:wIndex字段的几口号码,是指接口描述符中的bInterface字段。每一个接口的号码不同
// 数据字段的内容,是指接口描述符中的bAlternateInterface字段内容。数据字段表示目前
// 使用的接口中两个或多个互不相关的设置。
//wValue字段:0
//wIndex字段:接口号码
ctrl_trf_session_owner = MUID_USB9;
pSrc.bRam = (byte*)&usb_alt_intf+SetupPkt.bIntfID; // 取出数据,也就是接口号码,一般就一个接口0
usb_stat.ctrl_trf_mem = _RAM;
wCount._word = 1; // 计数字节
break;
case SET_INTF: //11 如果设备的配置支持多个互不相关设置的接口,主机请求设备使用一个指定的设置
ctrl_trf_session_owner = MUID_USB9;
usb_alt_intf[SetupPkt.bIntfID] = SetupPkt.bAltID; // 选择替代的接口号替代以前的
break;
case SET_DSC: //7 主机新增一个描述符,或是更新一个存在的描述符
case SYNCH_FRAME: //12 设备设置与报告一个端点的同步帧
default:
break;
}// end switch
}//end USBCheckStdRequest()
/********************************
* USB标准描述符获得处理函数
*********************************/
void USBStdGetDscHandler()
{
if(SetupPkt.bmRequestType == 0x80) // 检测是否是GET_DSC
{
switch(SetupPkt.bDscType) // 选择描述符类型
{
case DSC_DEV: // 设备描述符
ctrl_trf_session_owner = MUID_USB9; // 控制传输归SETUP所有
pSrc.bRom = (rom byte*)&device_dsc; // 取设备描述符区的首地址,bRom两字节指针1字节数据
wCount._word = sizeof(device_dsc); // 取出设备描述符大小
break;
case DSC_CFG: // 配置描述符
ctrl_trf_session_owner = MUID_USB9; // 控制传输归SETUP所有
pSrc.bRom = *(USB_CD_Ptr+SetupPkt.bDscIndex); //USB_CD_Ptr是地址指针数组,存放CFG0地址,加上偏移后取*把地址内容也就是把CFG0地址给到指针brom
wCount._word = *(pSrc.wRom+1); //pSrc.wRom是指针,他的地址与pSrc.bRom相同但是指向2个字节数据
//加1就越过两字节数据到CFG01的sizeof(cfg01),字节这样把配置的总字节数给出
break;
case DSC_STR: // 字符串描述符
ctrl_trf_session_owner = MUID_USB9;
pSrc.bRom = *(USB_SD_Ptr+SetupPkt.bDscIndex);
wCount._word = *pSrc.bRom; // Set data count
break;
} //end switch
}//end if
usb_stat.ctrl_trf_mem = _ROM;
}//end USBStdGetDscHandler()
/********************************
*USB指示设备使用所选择的配置处理
*********************************/
void USBStdSetCfgHandler()
{
ctrl_trf_session_owner = MUID_USB9; // 控制传输归USB9,设置这个以后可以返回状态
mDisableEP1to15(); // 复位端点1——15
ClearArray((byte*)&usb_alt_intf,MAX_NUM_INT); // 清交替数组(中断端点)本例使用一个其实可以不用
usb_active_cfg = SetupPkt.bCfgValue; // 取出主机给定的设置配置
if(SetupPkt.bCfgValue == 0) // 表示尚未设置配置,进入地址状态
{
usb_device_state = ADDRESS_STATE; // 设备进入地址状态
}//end if
else
{
usb_device_state = CONFIGURED_STATE; // 否则进入配置状态
HIDInitEP(); // HID端点设置
// 一旦进入配置状态后主机已确认设备将进入数据传输阶段
} //enf if
}// end USBStdSetCfgHandler()
/******************************
* USB获得状态处理函数
******************************/
void USBStdGetStatusHandler()
{
CtrlTrfData._byte0 = 0; // 准备两个字节的数据区
CtrlTrfData._byte1 = 0;
switch(SetupPkt.Recipient) // 选择是设备接口还是端点
{
case RCPT_DEV: // =0 获取设备状态
ctrl_trf_session_owner = MUID_USB9; //归传输协议(请求)所有
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -