私は NXP LPC1788 マイクロコントローラー用のコードを開発していますが、最近は USB の動作を改善しようとしています。USB に関する私の現在の問題は、USB がスレーブ モードで構成されていることです。通常の操作では大量のメッセージを送受信する必要があるため、CPU はほとんどの時間を USB の処理に費やしており、ボトルネックが発生しています。
スレーブモード構成から DMA モードに切り替えることで、この問題を解決しようとしています。サンプル プロジェクトを参考にしてきましたが、必要なコードのほとんどは適切に配置されていると思います。
以前と同様に、USB の初期化は正常に機能します。問題は、DMA モードで設定したエンドポイントに USB メッセージを送信しようとするとEpDMAEn
(レジスタの適切なビットを有効にすることによって)、そのエンドポイントに対して USB システム エラー割り込みが発生することです。これについて私が得ることができる唯一の情報は次のとおりです。
データの転送時、または DD のフェッチまたは更新時にシステム エラー (AHB バス エラー) が発生すると、対応するビットがこのレジスタにセットされます。SysErrIntSt は読み取り専用レジスタです。
プログラムのこの時点では、UDCA に触れたり、DMA 記述子をセットアップしたり、過去の初期化のようなものをセットアップしたりしていません。これは、USB バスで最初のメッセージが受信されるとすぐに発生するエラーであり、それを行う必要があると思います。
エンドポイント 2 の IN と OUT を使用しています。これは、最大パケット サイズが 64 バイトのダブル バッファ バルク エンドポイントです。
DMA を使用しない場合、USB は正常に動作することを確認しました。
DMA エンジンの初期化プロセスを実行し、エンドポイントを DMA モードではなくスレーブ モードに設定すると、USB が正常に動作することを確認しました。
Example Projects
-> NXP
-> LP17xx
- >の下にある USB 大容量ストレージの例177x_8x CMSIS
は、デフォルトの構成を使用すると正常に動作することを確認しました。
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000000
...
これを次のように変更すると、同じように壊れます。
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000030 /* Endpoint 2 IN and OUT */
...
USB ハードウェア ソース ファイルの先頭に、次のように記述します。
#ifdef USB_DMA
// Stores information received using DMA on OUT endpoints.
//uint8_t dataBufferOUT[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferOUT = (uint8_t*)DMA_BUF_ADR;
// Stores information transferred using DMA on IN endpoints.
//uint8_t dataBufferIN[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferIN = (uint8_t*)(DMA_BUF_ADR+DD_BUFFER_SIZE*
USB_MAX_PACKET_SIZE);
// Current dataBufferOUT index;
uint16_t dataOUT;
// Current dataBufferIN index.
uint16_t dataIN;
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
次のコードで USB 周辺機器を初期化します。
void USBInit()
{
// Configure USB pins.
PINSEL_ConfigPin(0, 29, 1); // USB_D+1
PINSEL_ConfigPin(0, 30, 1); // USB_D-1
PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
PINSEL_ConfigPin(2, 9, 1); // USB_CONNECT1
PINSEL_ConfigPin(1, 30, 2); // USB_VBUS
// Turn on power and clock
CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);
//PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);
// Set DEV_CLK_EN and AHB_CLK_EN.
LPC_USB->USBClkCtrl |= 0x12;
// Wait until change is reflected in clock status register.
while((LPC_USB->USBClkSt & 0x12) != 0x12);
// Enable NVIC USB interrupts.
NVIC_EnableIRQ(USB_IRQn);
// Reset the USB.
USBReset();
// Set device address to 0x0 and enable device & connection.
USBSetAddress(0);
// TEMP.
sendMessageFlag = 0;
#ifdef USB_DMA
dataIN = 0;
dataOUT = 0;
#endif
}
私のUSBリセットコード:
void USBReset()
{
LPC_USB->EpInd = 0;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
LPC_USB->EpInd = 1;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);
LPC_USB->EpIntClr = 0xFFFFFFFF;
#ifdef USB_DMA
LPC_USB->EpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;
#else
LPC_USB->EpIntEn = 0xFFFFFFFF;
#endif
LPC_USB->DevIntClr = 0xFFFFFFFF;
LPC_USB->DevIntEn = DEV_STAT_INT | EP_SLOW_INT /*| EP_FAST_INT*/ ;
#ifdef USB_DMA
uint32_t n;
LPC_USB->UDCAH = USB_RAM_ADR;
LPC_USB->DMARClr = 0xFFFFFFFF;
LPC_USB->EpDMADis = 0xFFFFFFFF;
LPC_USB->EpDMAEn = USB_DMA_EP;
LPC_USB->EoTIntClr = 0xFFFFFFFF;
LPC_USB->NDDRIntClr = 0xFFFFFFFF;
LPC_USB->SysErrIntClr = 0xFFFFFFFF;
LPC_USB->DMAIntEn = 0x00000007;
DDMemMap[0] = 0x00000000;
DDMemMap[1] = 0x00000000;
for (n = 0; n < USB_EP_NUM; n++) {
udca[n] = 0;
UDCA[n] = 0;
}
#endif
}
準備ができたら、これを使用して USB を実行します。
void USBRun()
{
USBSetConnection(TRUE);
}
最後に、私の USB 割り込みルーチン:
void USB_IRQHandler(void)
{
OS_EnterInterrupt();
uint32_t data, val, pIndex, lIndex, currEpisr;
uint32_t interruptData = LPC_USB->DevIntSt;
#ifdef USB_DMA
uint32_t dmaInterruptData = LPC_USB->DMAIntSt;
#endif
//printf("InterruptData: 0x%x\n", interruptData);
if (interruptData & ERR_INT)
{
writeSIECommand(CMD_RD_ERR_STAT);
data = readSIECommandData(DAT_RD_ERR_STAT);
// printf("Error data: 0x%x\n", data);
//getchar();
}
// Handle device status interrupt (reset, connection change, suspend/resume).
if(interruptData & DEV_STAT_INT)
{
LPC_USB->DevIntClr = DEV_STAT_INT;
writeSIECommand(CMD_GET_DEV_STAT);
data = readSIECommandData(DAT_GET_DEV_STAT);
//printf("Data: 0x%x\n", data);
// Device reset.
if(data & DEV_RST)
{
USBReset();
USBResetCore();
//printf("USB Reset\n");
}
// Connection change.
if(data & DEV_CON_CH)
{
//printf("Connection change\n");
/* Pass */
}
// Suspend/resume.
if(data & DEV_SUS_CH)
{
if(data & DEV_SUS)
{
//printf("USB Suspend\n");
USBSuspend();
}
else
{
//printf("USB Resume\n");
USBResume();
}
}
OS_LeaveInterrupt();
return;
}
// Handle endpoint interrupt.
if(interruptData & EP_SLOW_INT)
{
//printf("Endpoint slow\n");
data = LPC_USB->EpIntSt;
//printf("EP interrupt: 0x%x\n", data);
currEpisr = 0;
for(pIndex=0; pIndex < USB_EP_NUM; pIndex++)
{
lIndex = pIndex >> 1;
if(data == currEpisr) break;
if(data & (1 << pIndex))
{
currEpisr |= (1 << pIndex);
LPC_USB->EpIntClr = 1 << pIndex;
while((LPC_USB->DevIntSt & CDFULL_INT) == 0);
val = LPC_USB->CmdData;
// OUT endpoint.
if((pIndex & 1) == 0)
{
// Control OUT endpoint.
if(pIndex == 0)
{
// Setup Packet.
if(val & EP_SEL_STP)
{
if(USB_P_EP[0])
{
USB_P_EP[0](USB_EVT_SETUP);
continue;
}
}
}
if(USB_P_EP[lIndex])
{
USB_P_EP[lIndex](USB_EVT_OUT);
}
}
// IN endpoint.
else
{
if(USB_P_EP[lIndex])
{
if(lIndex > 0) clearSendMessageFlag(lIndex);
USB_P_EP[lIndex](USB_EVT_IN);
}
}
}
}
LPC_USB->DevIntClr = EP_SLOW_INT;
}
#ifdef USB_DMA
if (dmaInterruptData & 0x00000001) { /* End of Transfer Interrupt */
data = LPC_USB->EoTIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_EOT);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_EOT);
}
}
}
}
LPC_USB->EoTIntClr = data;
}
if (dmaInterruptData & 0x00000002) { /* New DD Request Interrupt */
data = LPC_USB->NDDRIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_NDR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_NDR);
}
}
}
}
LPC_USB->NDDRIntClr = data;
}
if (dmaInterruptData & 0x00000004) { /* System Error Interrupt */
data = LPC_USB->SysErrIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_ERR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_ERR);
}
}
}
}
LPC_USB->SysErrIntClr = data;
}
#endif /* USB_DMA */
OS_LeaveInterrupt();
}
私が理想的に探しているのは、DMA エンジンを使用した USB メッセージの送受信を示す LPC1788 で実行できる私のコードまたは実際のサンプル プログラムのいずれかにエラーを見つけることができる場合の解決策です。
また、AHB バス エラーの原因についての情報もいただければ幸いです。
編集
以下のTurbo Jの回答への回答:
UDCAのアドレスを確認してください。必要なアラインメントは非常に厳密で、256 バイトの IIRC であるため、アドレスは LDB として 0x00 で終わる必要があります。GCC では、リンカー スクリプトで USB_RAM セクションをサポートする必要があります。
私の USB ハードウェア ヘッダー ファイルには、次のものがあります。
/* USB RAM Definitions */
#define USB_RAM_ADR LPC_PERI_RAM_BASE /* USB RAM Start Address */
#define USB_RAM_SZ 0x00004000 /* USB RAM Size (4kB) */
LPC_PERI_RAM_BASE
値は 0x20000000UL です。
私のソースファイルには、次のものがあります。
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
USB_EP_NUM
32 はどこですか。
したがって、UDCA は RAM メモリ ブロックの先頭から始まる 128 バイトの配列である必要があると思います。