gpt4 book ai didi

c - 如何使用 LPC1788 的 USB DMA 引擎?

转载 作者:太空宇宙 更新时间:2023-11-03 23:26:43 25 4
gpt4 key购买 nike

我正在为 NXP LPC1788 微 Controller 开发代码,最近我一直在尝试改进 USB 的工作方式。我目前的 USB 问题是它配置为从属模式,并且由于在正常操作中必须发送和接收大量消息,CPU 将大部分时间用于处理 USB,这造成了瓶颈。

我一直试图通过从从属模式配置切换到 DMA 模式来解决这个问题。我一直在使用示例项目来提供帮助,我认为我需要的大部分代码都已准备就绪。

USB 初始化工作正常,和以前一样。 问题是,一旦我尝试将 USB 消息发送到我在 DMA 模式下配置的端点(通过启用 EpDMAEn 寄存器中的适当位), 我收到该端点的 USB 系统错误中断。我能得到的唯一信息是:

If a system error (AHB bus error) occurs when transferring the data or when fetching or updating the DD the corresponding bit is set in this register. SysErrIntSt is a read-only register.

在程序的这一点上,我还没有触及 UDCA 或设置任何 DMA 描述符或类似过去初始化的任何东西。我相信,这是在我需要执行任何操作之前 USB 总线上收到第一条消息后立即发生的错误。

我正在使用端点 2 IN 和 OUT,它们是双缓冲批量端点,最大数据包大小为 64 字节。

我已经确认,如果我不使用 DMA,USB 也能正常工作。

我已经确认,如果我完成初始化 DMA 引擎的过程但将端点配置为从属模式而不是 DMA 模式,USB 工作正常。

我已经确认 Example Projects 下的 USB Mass Storage 示例 -> NXP -> LP17xx -> 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();
}

如果您能在我的任何代码或可在 LPC1788 上运行的工作示例程序中发现错误,我理想地寻找的是一个解决方案,该程序演示了使用 DMA 引擎的 USB 消息传输和接收。

如果您能提供有关导致 AHB 总线错误的原因的任何信息,我将不胜感激。

编辑

在下面回应 Turbo J 的回答:

Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.

在我的 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 字节数组。

最佳答案

检查UDCA的地址。要求对齐非常严格,256字节IIRC,所以地址必须以0x00结尾作为LDB。 GCC 需要支持链接器脚本中的 USB_RAM 部分。

关于c - 如何使用 LPC1788 的 USB DMA 引擎?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25426588/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com