問題原因
出現該問題的主要原因在於 USB 庫中使用了 malloc() 和 free() 函數。在“usbd_conf.h”文件中有類似如下內容:
/* Memory management macros */
#define USBD_malloc malloc
#define USBD_free free
這兩個函數需要使用“target specific functions” ,另外,如 printf()、scanf() 等函數都要用到“target specific functions”。
問題解決
當遇到類似問題,你可以:
- Provide real implementations for the functions. See the syscalls.c files.
- Use a different library.
如果使用了實時操作系統,那麼還可以將malloc和free等替換成實時系統提供的函數(等同於“Use a different library”)。
我使用了FreeRTOS,該 RTOS 提供了 pvPortMalloc() 和 vPortFree() 用於替代 malloc() 和 free() 。但是,在 USB 驅動程序中無法直接使用這兩個內存管理函數,因爲它們內部調用了 vTaskSuspendAll() 和 xTaskResumeAll() 來掛起和恢復任務,而任務掛起和恢複函數在中斷中是無法使用的。USB 驅動程序恰恰是在中斷中調用的 USBD_malloc() 和 USBD_free()。因此,需要使用中斷服務版本的內存管理程序或者使用內存池才真正可行。中斷服務版本的內存分配/釋放算法需要被 portDISABLE_INTERRUPTS()/portENABLE_INTERRUPTS() 對包圍起來。具體可以參考http://www.freertos.org/FreeRTOS_Support_Forum_Archive/April_2014/freertos_about_malloc_buffer_in_interrupt_ISR_ffdab88aj.html中的討論。
注:非常建議使用實時系統提供的內存管理函數,因爲使用標準的 malloc() 與 free() 庫函數,必須承擔以下若干問題:
- 這兩個函數在小型嵌入式系統中可能不可用。
- 這兩個函數的具體實現可能會相對較大,會佔用較多寶貴的代碼空間。
- 這兩個函數通常不具備線程安全特性。
- 這兩個函數具有不確定性。每次調用時的時間開銷都可能不同。
- 這兩個函數會產生內存碎片。
- 這兩個函數會使得鏈接器配置得複雜。
SysCalls.cpp參考文件
Add this file to your project (e.g. syscalls.cpp) and paste the following code into it:
#include <stdio.h>
#include <sys/stat.h>
#include <stm32f10x_usart.h>
extern "C"
{
int _fstat (int fd, struct stat *pStat)
{
pStat->st_mode = S_IFCHR;
return 0;
}
int _close(int)
{
return -1;
}
int _write (int fd, char *pBuffer, int size)
{
for (int i = 0; i < size; i++)
{
while (!(USART1->SR & USART_SR_TXE))
{
}
USART_SendData(USART1, pBuffer[i]);
}
return size;
}
int _isatty (int fd)
{
return 1;
}
int _lseek(int, int, int)
{
return -1;
}
int _read (int fd, char *pBuffer, int size)
{
for (int i = 0; i < size; i++)
{
while ((USART1->SR & USART_SR_RXNE) == 0)
{
}
pBuffer[i] = USART_ReceiveData(USART1);
}
return size;
}
caddr_t _sbrk(int increment)
{
extern char end asm("end");
register char *pStack asm("sp");
static char *s_pHeapEnd;
if (!s_pHeapEnd)
s_pHeapEnd = &end;
if (s_pHeapEnd + increment > pStack)
return (caddr_t)-1;
char *pOldHeapEnd = s_pHeapEnd;
s_pHeapEnd += increment;
return (caddr_t)pOldHeapEnd;
}
}