5
使用STM32CubeMX生成初始化代码 在我的单片机上有一个8MHz的晶振,所以我选择HSE为时钟信号源。PLLMul配置为x9,得到72MHz的PLLCLK,提供给CPU和AHB/APB2总线,提供给APB1总线的PCLK1配置为36MHz。USB预分频配置为1.5分频,得到48MHz的USB时钟。SPI1配置为Full-Duplex Master,舍去NSS信号,USART1配置为Asynchronous。USB设备进一步配置为Custom HID Class,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE设置为64 Bytes。
注意:
我没有修改设备的VID和PID。但我猜测有些上位机软件会检测这两个ID
如果你发现你的软件不能识别我这个CMSIS-DAP,或许你需要恰当的VID和PID。可以试试示例代码中的VID/PID,它在一个叫做USBD_Config_0.c的文件中,我的工程中没有这个文件。
有STM32CubeMX生成的代码需要一些修改。在u***d_customhid.h中,CUSTOM_HID_EPIN_SIZE和CUSTOM_HID_EPOUT_SIZE必须设置为0x40U。我把CUSTOM_HID_FS_BINTERVAL改为0x01来尝试提升HID的通信速度。
在_USBD_CUSTOM_HID_Itf结构体中,我新增了一个成员:
typedef struct _USBD_CUSTOM_HID_Itf
{
uint8_t *pReport;
int8_t (* Init)(void);
int8_t (* DeInit)(void);
int8_t (* OutEvent)(uint8_t event_idx, uint8_t state);
/* I add an extra interface func below. Zach Lee */
int8_t (* InEvent)(uint8_t event_idx, uint8_t state);
} USBD_CUSTOM_HID_ItfTypeDef;
当INPUT REPORT报文成功发给上位机时,InEvent函数应当被调用,所以u***d_customhid.c中的USBD_CUSTOM_HID_DataIn函数需要修改如下:
static uint8_t USBD_CUSTOM_HID_DataIn(USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
/* Ensure that the FIFO is empty before a new transfer, this condition could
be caused by a new transfer before the end of the previous transfer */
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;
hhid->state = CUSTOM_HID_IDLE;
/* I add a new interface func in the structure USBD_CUSTOM_HID_ItfTypeDef. Zach Lee */
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->InEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
return USBD_OK;
}
设备描述符CUSTOM_HID_ReportDesc_FS被定义在u***d_suctom_hid_if.c中,我定义了一个简单的描述符:
/** U*** HID report descriptor. */
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
/* USER CODE BEGIN 0 */ /* A minimal Report Desc with INPUT/OUTPUT/FEATURE report. Zach Lee */
0x06,0x00,0xFF, /* Usage Page (vendor defined) ($FF00) global */
0x09,0x01, /* Usage (vendor defined) ($01) local */
0xA1,0x01, /* Collection (Application) */
0x15,0x00, /* LOGICAL_MINIMUM (0) */
0x25,0xFF, /* LOGICAL_MAXIMUM (255) */
0x75,0x08, /* REPORT_SIZE (8bit) */
// Input Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0x81,0x02, /* Input(data,var,absolute) */
// Output Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0x91,0x02, /* Output(data,var,absolute) */
// Feature Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0xB1,0x02, /* Feature(data,var,absolute) */
/* USER CODE END 0 */
0xC0 /* END_COLLECTION */
};
可能Feature Report在CMSIS-DAP中不是必要的,就留着它吧。
我在这个C文件中还实现了一个新的接口函数CUSTOM_HID_InEvent_FS:
static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state); /* An extra interface func. */
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS,
/* I add an extra interface func below. Zach Lee */
CUSTOM_HID_InEvent_FS
};
extern void USBD_OutEvent(void); /* Implemented in file "device.h" */
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 */
USBD_OutEvent(); /* OUTPUT REPORT was received. Zach Lee */
return (USBD_OK);
/* USER CODE END 6 */
}
extern void USBD_InEvent(void); /* Implemented in file "device.h" */
static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN extra */
USBD_InEvent(); /* INPUT REPORT has been sent. Zach Lee */
return (USBD_OK);
/* USER CODE END extra */
}
CUSTOM_HID_Init_FS和CUSTOM_HID_DeInit_FS两个函数被实现为使能/失能DAP功能:
extern void USBD_HID0_Initialize (void);
static int8_t CUSTOM_HID_Init_FS(void)
{
/* USER CODE BEGIN 4 */
USBD_HID0_Initialize(); /* Initialize USB communication of DAP. Zach Lee */
return (USBD_OK);
/* USER CODE END 4 */
}
extern void USBD_HID0_Uninitialize (void);
static int8_t CUSTOM_HID_DeInit_FS(void)
{
/* USER CODE BEGIN 5 */
USBD_HID0_Uninitialize(); /* Uninitialize. Zach Lee */
return (USBD_OK);
/* USER CODE END 5 */
}
6
编写将所有源代码关联起来的桥梁 为了移除CMSIS RTOS,我写了一些函数来模拟RTOS:
/**
* Replace CMSIS RTOS api
*/
static volatile int osFlags; /* Use "volatile" to prevent GCC optimizing the code. */
void osKernelInitialize(void)
{
osFlags = 0;
return;
}
int osThreadFlagsWait(int mask, int b, int c)
{
(void)b;
(void)c;
int ret;
while((osFlags&mask) == 0)
{
;
}
ret = osFlags; osFlags &= ~mask;
return ret;
}
void osThreadFlagsSet(int tid, int f)
{
(void)tid;
osFlags |= f;
return;
}
函数USBD_Configured和USBD_HID_GetReportTrigger实现如下:
intUSBD_Configured(int n){
(void)n;
return(hU***DeviceFS.dev_state == USBD_STATE_CONFIGURED ?1:0);}
void USBD_HID_GetReportTrigger(int a, int b, void * report, int len)
{
(void)a;
(void)b;
if (USBD_OK != USBD_CUSTOM_HID_SendReport(&hU***DeviceFS, report, len))
{
;
}
return;
}
函数USBD_CUSTOM_HID_SendReport是由STM32CubeMX生成的,它被定义在u***d_customhid.c中,我自己的事件句柄如下:
bool USBD_HID0_SetReport (uint8_t rtype, uint8_t req, uint8_t rid, const uint8_t *buf, int32_t len);
void USBD_OutEvent(void)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hU***DeviceFS.pClassData;
USBD_HID0_SetReport(HID_REPORT_OUTPUT, 0, 0, hhid->Report_buf, USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
}
int32_t USBD_HID0_GetReport (uint8_t rtype, uint8_t req, uint8_t rid, uint8_t *buf);
void USBD_InEvent(void)
{
int32_t len;
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hU***DeviceFS.pClassData;
if ((len=USBD_HID0_GetReport(HID_REPORT_INPUT, USBD_HID_REQ_EP_INT, 0, hhid->Report_buf)) > 0)
{
USBD_HID_GetReportTrigger(0, 0, hhid->Report_buf, len);
}
}
注意:
在DAP.h中,这里有个名为PIN_DELAY_SLOW的函数,它原本的实现是这样的:
__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) {
uint32_t count;
count = delay;
while (--count);
}
这里的空循环while (–count);会被GCC优化。我在StackOverflow中找到了一个好点子,它能正常工作但不是太合适,你有更好的方法吗?
__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) {
uint32_t count;
count = delay;
while (--count) {
/**
* Empty loop will be totally omitted by GCC.
* Search "How to prevent GCC from optimizing out a busy wait loop?" @ StackOverflow.
* This solution isn't portable. Zach Lee
*/
__ASM("");
}
}
5
使用STM32CubeMX生成初始化代码 在我的单片机上有一个8MHz的晶振,所以我选择HSE为时钟信号源。PLLMul配置为x9,得到72MHz的PLLCLK,提供给CPU和AHB/APB2总线,提供给APB1总线的PCLK1配置为36MHz。USB预分频配置为1.5分频,得到48MHz的USB时钟。SPI1配置为Full-Duplex Master,舍去NSS信号,USART1配置为Asynchronous。USB设备进一步配置为Custom HID Class,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE设置为64 Bytes。
注意:
我没有修改设备的VID和PID。但我猜测有些上位机软件会检测这两个ID
如果你发现你的软件不能识别我这个CMSIS-DAP,或许你需要恰当的VID和PID。可以试试示例代码中的VID/PID,它在一个叫做USBD_Config_0.c的文件中,我的工程中没有这个文件。
有STM32CubeMX生成的代码需要一些修改。在u***d_customhid.h中,CUSTOM_HID_EPIN_SIZE和CUSTOM_HID_EPOUT_SIZE必须设置为0x40U。我把CUSTOM_HID_FS_BINTERVAL改为0x01来尝试提升HID的通信速度。
在_USBD_CUSTOM_HID_Itf结构体中,我新增了一个成员:
typedef struct _USBD_CUSTOM_HID_Itf
{
uint8_t *pReport;
int8_t (* Init)(void);
int8_t (* DeInit)(void);
int8_t (* OutEvent)(uint8_t event_idx, uint8_t state);
/* I add an extra interface func below. Zach Lee */
int8_t (* InEvent)(uint8_t event_idx, uint8_t state);
} USBD_CUSTOM_HID_ItfTypeDef;
当INPUT REPORT报文成功发给上位机时,InEvent函数应当被调用,所以u***d_customhid.c中的USBD_CUSTOM_HID_DataIn函数需要修改如下:
static uint8_t USBD_CUSTOM_HID_DataIn(USBD_HandleTypeDef *pdev,
uint8_t epnum)
{
/* Ensure that the FIFO is empty before a new transfer, this condition could
be caused by a new transfer before the end of the previous transfer */
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;
hhid->state = CUSTOM_HID_IDLE;
/* I add a new interface func in the structure USBD_CUSTOM_HID_ItfTypeDef. Zach Lee */
((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->InEvent(hhid->Report_buf[0],
hhid->Report_buf[1]);
return USBD_OK;
}
设备描述符CUSTOM_HID_ReportDesc_FS被定义在u***d_suctom_hid_if.c中,我定义了一个简单的描述符:
/** U*** HID report descriptor. */
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
/* USER CODE BEGIN 0 */ /* A minimal Report Desc with INPUT/OUTPUT/FEATURE report. Zach Lee */
0x06,0x00,0xFF, /* Usage Page (vendor defined) ($FF00) global */
0x09,0x01, /* Usage (vendor defined) ($01) local */
0xA1,0x01, /* Collection (Application) */
0x15,0x00, /* LOGICAL_MINIMUM (0) */
0x25,0xFF, /* LOGICAL_MAXIMUM (255) */
0x75,0x08, /* REPORT_SIZE (8bit) */
// Input Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0x81,0x02, /* Input(data,var,absolute) */
// Output Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0x91,0x02, /* Output(data,var,absolute) */
// Feature Report
0x95,64, /* Report Length (64 REPORT_SIZE) */
0x09,0x01, /* USAGE (Vendor Usage 1) */
0xB1,0x02, /* Feature(data,var,absolute) */
/* USER CODE END 0 */
0xC0 /* END_COLLECTION */
};
可能Feature Report在CMSIS-DAP中不是必要的,就留着它吧。
我在这个C文件中还实现了一个新的接口函数CUSTOM_HID_InEvent_FS:
static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state); /* An extra interface func. */
USBD_CUSTOM_HID_ItfTypeDef USBD_CustomHID_fops_FS =
{
CUSTOM_HID_ReportDesc_FS,
CUSTOM_HID_Init_FS,
CUSTOM_HID_DeInit_FS,
CUSTOM_HID_OutEvent_FS,
/* I add an extra interface func below. Zach Lee */
CUSTOM_HID_InEvent_FS
};
extern void USBD_OutEvent(void); /* Implemented in file "device.h" */
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN 6 */
USBD_OutEvent(); /* OUTPUT REPORT was received. Zach Lee */
return (USBD_OK);
/* USER CODE END 6 */
}
extern void USBD_InEvent(void); /* Implemented in file "device.h" */
static int8_t CUSTOM_HID_InEvent_FS(uint8_t event_idx, uint8_t state)
{
/* USER CODE BEGIN extra */
USBD_InEvent(); /* INPUT REPORT has been sent. Zach Lee */
return (USBD_OK);
/* USER CODE END extra */
}
CUSTOM_HID_Init_FS和CUSTOM_HID_DeInit_FS两个函数被实现为使能/失能DAP功能:
extern void USBD_HID0_Initialize (void);
static int8_t CUSTOM_HID_Init_FS(void)
{
/* USER CODE BEGIN 4 */
USBD_HID0_Initialize(); /* Initialize USB communication of DAP. Zach Lee */
return (USBD_OK);
/* USER CODE END 4 */
}
extern void USBD_HID0_Uninitialize (void);
static int8_t CUSTOM_HID_DeInit_FS(void)
{
/* USER CODE BEGIN 5 */
USBD_HID0_Uninitialize(); /* Uninitialize. Zach Lee */
return (USBD_OK);
/* USER CODE END 5 */
}
6
编写将所有源代码关联起来的桥梁 为了移除CMSIS RTOS,我写了一些函数来模拟RTOS:
/**
* Replace CMSIS RTOS api
*/
static volatile int osFlags; /* Use "volatile" to prevent GCC optimizing the code. */
void osKernelInitialize(void)
{
osFlags = 0;
return;
}
int osThreadFlagsWait(int mask, int b, int c)
{
(void)b;
(void)c;
int ret;
while((osFlags&mask) == 0)
{
;
}
ret = osFlags; osFlags &= ~mask;
return ret;
}
void osThreadFlagsSet(int tid, int f)
{
(void)tid;
osFlags |= f;
return;
}
函数USBD_Configured和USBD_HID_GetReportTrigger实现如下:
intUSBD_Configured(int n){
(void)n;
return(hU***DeviceFS.dev_state == USBD_STATE_CONFIGURED ?1:0);}
void USBD_HID_GetReportTrigger(int a, int b, void * report, int len)
{
(void)a;
(void)b;
if (USBD_OK != USBD_CUSTOM_HID_SendReport(&hU***DeviceFS, report, len))
{
;
}
return;
}
函数USBD_CUSTOM_HID_SendReport是由STM32CubeMX生成的,它被定义在u***d_customhid.c中,我自己的事件句柄如下:
bool USBD_HID0_SetReport (uint8_t rtype, uint8_t req, uint8_t rid, const uint8_t *buf, int32_t len);
void USBD_OutEvent(void)
{
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hU***DeviceFS.pClassData;
USBD_HID0_SetReport(HID_REPORT_OUTPUT, 0, 0, hhid->Report_buf, USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
}
int32_t USBD_HID0_GetReport (uint8_t rtype, uint8_t req, uint8_t rid, uint8_t *buf);
void USBD_InEvent(void)
{
int32_t len;
USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hU***DeviceFS.pClassData;
if ((len=USBD_HID0_GetReport(HID_REPORT_INPUT, USBD_HID_REQ_EP_INT, 0, hhid->Report_buf)) > 0)
{
USBD_HID_GetReportTrigger(0, 0, hhid->Report_buf, len);
}
}
注意:
在DAP.h中,这里有个名为PIN_DELAY_SLOW的函数,它原本的实现是这样的:
__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) {
uint32_t count;
count = delay;
while (--count);
}
这里的空循环while (–count);会被GCC优化。我在StackOverflow中找到了一个好点子,它能正常工作但不是太合适,你有更好的方法吗?
__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) {
uint32_t count;
count = delay;
while (--count) {
/**
* Empty loop will be totally omitted by GCC.
* Search "How to prevent GCC from optimizing out a busy wait loop?" @ StackOverflow.
* This solution isn't portable. Zach Lee
*/
__ASM("");
}
}
举报