【问题标题】:C Memory Buffer corrupting micro-controller memory allocation - Atmel ATxmegaC 内存缓冲区破坏微控制器内存分配 - Atmel ATxmega
【发布时间】:2017-06-06 04:35:55
【问题描述】:

我正在使用 ATxmega128A1 8 位微控制器,我试图找出为什么我的内存缓冲区行为异常。

我有一个如下结构的 FIFO 缓冲区:

typedef struct
{
  FIFOid ID;                        //ID value to determine the type of FIFO being used
  BOOL LOCK;                        //A lock to prevent multiple FIFO accessing (i.e. data corruption)
  UINT16 Start, End;                //Value of the current start and end FIFO index
  UINT16 volatile NbBytes;          //Number of bytes currently inside the FIFO
  UINT8 Buffer[FIFO_SIZE];          //Memory buffer for the FIFO buffer
} TFIFO;

我定义了 2 个 FIFO:

//FIFO Buffers for USART
TFIFO RxFIFO;           //Creates the Receive FIFO
TFIFO TxFIFO;           //Creates the Transmit FIFO

FIFO_SIZE 在哪里

#define FIFO_SIZE 256       

当我想从 Buffer[] 中获取一个字节时,我通过这个函数将一个指针传递给 FIFO:

BOOL FIFO_Get(TFIFO * const FIFO, UINT8 * const dataPtr);

这很好用,但是,一旦我将 FIFO_SIZE 增加到 ~510 字节以上 - 通过这个函数传递这个 FIFO 指针会导致 另一个 我的 FIFO 缓冲区之一被更改。

我在我的调试器上看到了,这一行没有问题:

if(FIFO_Get(&TxFIFO, &data)){       //Get a byte from TxFIFO

一旦我越过,它被传递给函数,RxFIFO->Buffer[]数组被修改,即更大的地址值。 FIFO_Get 函数:

// ----------------------------------------
// FIFO_Get
// ----------------------------------------
// Remove one character from the FIFO
// Input: 
//   FIFO is a  pointer to a FIFO struct with data to be retrieved
//   dataPtr is a pointer to a memory location to place the retrieved byte
// Output:
//   TRUE if the operation was successful and the data is valid
// Conditions:
//   Assumes that FIFO_Init has been called

BOOL FIFO_Get(TFIFO * const FIFO, UINT8 * const dataPtr)
{
    BOOL FIFOGetSuccess;
    FIFOGetSuccess = bFALSE;

    //disable interrupts
    //if we're GETTING from the USART receive FIFO
    //we need to disable the receive interrupt so
    //there is no data being 'Put' during a 'Get'
    if(FIFO->ID == URX){
        USARTD0.CTRLA = USART_RXCINTLVL_OFF_gc; //Rx interrupts off
        USARTD1.CTRLA = USART_RXCINTLVL_OFF_gc; //Rx interrupts off
    }

    //Attempt to get a byte from the FIFO
    if(FIFO->LOCK == bFALSE){                           //Check that the FIFO is not locked
        FIFO->LOCK = bTRUE;                             //lock the FIFO
        if (FIFO->NbBytes == 0){                        //Checks whether FIFO is empty; number of bytes in FIFO = 0
            FIFOGetSuccess = bFALSE;                    //If empty, false is Returned
        }else{
            *dataPtr = FIFO->Buffer[FIFO->Start];       //Reads the byte in the FIFO start position, and saves it to the location of the data pointer
            if (FIFO->Start == (FIFO_SIZE - 1)){        //Checks whether the start position is equal to FIFO size, the last valid 8 bit number
                FIFO->Start = 0;                        //If so, the position is set to 0
            }else{                                      //If not fifo size;
                FIFO->Start++;                          //Increments the start position by 1
            }
            FIFO->NbBytes--;                            //Decrements the number of bytes in the FIFO by 1
            FIFOGetSuccess =  bTRUE;                    //Returns true if this is successful
        }
        FIFO->LOCK = bFALSE;                            //unlock the FIFO
    }

    //re-enable interrupts
    if(FIFO->ID == URX){
        USARTD0.CTRLA = USART_RXCINTLVL_LO_gc;  //low level interrupts on Rx
        USARTD1.CTRLA = USART_RXCINTLVL_LO_gc;  //low level interrupts on Rx        
    }

    return FIFOGetSuccess;
}

我的猜测是我没有安全地在微控制器中分配内存,但是在构建之后,芯片中似乎还有很多内存:

        Program Memory Usage    :   21460 bytes   15.4 % Full
        Data Memory Usage       :   6967 bytes   12.1 % Full

我该如何解决这个问题?我做错了什么?

编辑:

我发现增加 FIFO SIZE 将 FIFO 缓冲区映射到 SRAM 地址范围(ATxmega128A1 的 SRAM 从 0x2000 到 0x3FFF)

FIFO address mapping

当另一个 FIFO 通过函数时,地址 0x3FF7 到 0x3FFF 正在改变

编辑 2: Elf Files

【问题讨论】:

  • 请提供更多上下文,创建@​​987654323@。
  • 结构有点奇怪。 FIFO(环形缓冲区)一般应该只有一个写点和一个读点。有多个它们有点奇怪。在这种情况下,您不需要“LOCK”机制。 FIFO_Get 的代码很容易看到
  • 你至少应该发布FIFO_Get功能代码
  • -添加fifo_get函数
  • 510 字节几乎是 0x200(正好是 0x1FE)。你如何分配你的FIFO?作为全局变量,在堆中,在函数栈中?您可能会遇到一些内存限制。

标签: c memory memory-management atmel


【解决方案1】:

从 ELF 文件中,我可以看到这个内存映射:

00802000 B __bss_start
00802000 D __data_end
00802000 D __data_start
00802000 D _edata
00802000 00000002 b n.4418
00802002 00000001 b packetEnd.1686
00802003 00000002 b byteCount.1684
00802005 00000002 b StepsCounted.4253
00802007 00000001 b ErrorCode.4252
00802008 00000001 b SuccessNb.4251
00802009 00000001 b ReturnNb.4250
0080200a 00000004 b stepsOutOpto.4211
0080200e 00000004 b HomingError.4212
00802012 00000008 b stepscounted.4210
0080201a 00000004 b CntHomeState.4209
0080201e 00000001 b byteCount.4166
0080201f 00000002 B EjectMsgID
00802021 00000001 B VersionMinor
00802022 00000002 B QueueEnd
00802024 00000001 B VersionMonth
00802025 00000001 B ACC1_IN
00802026 0000001f B PacketQRx
00802045 0000001f B PacketTx
00802064 00000001 B VersionMajor
00802065 00000001 B DeviceTypeId
00802066 00000001 B debugCycler_bytes
00802067 00000001 B debugAXIS_ACK
00802068 000000d4 B AXIS
0080213c 00000001 B LVL_SENSE_IN
0080213d 00000001 B LID_DETECTION_ON
0080213e 00000001 B readyimmediateTx
0080213f 0000001f B PacketQTx
0080215e 00000004 B RobotSerialNumber
00802162 00000001 B USBIN
00802163 00000001 B startupComplete
00802164 000007c0 B PacketQueue
00802924 0000001f B PacketRx
00802943 00000001 B ACC2_IN
00802944 00000002 B QueueNb
00802946 00000001 B QueuedCommandCalled
00802947 0000001f B dummyPkt
00802966 00000001 B CYCLER_IN
00802967 00000002 B QueueStart
00802969 00000001 B HardwareRevision
0080296a 00000001 B VersionYear
0080296b 00000980 B AXISRxFIFO
008032eb 00000004 B RTS
008032ef 00000980 B AXISTxFIFO
00803c6f 00000068 B AXIS_SPI_Tx_Packet
00803cd7 00000004 B ReturnPacketMissed
00803cdb 00000068 B AXIS_SPI_Rx_Packet
00803d43 00000004 B NbPacketsInAxisFIFO
00803d47 00000100 B CyclerEEPROM
00803e47 00000260 B TxFIFO
008040a7 00000260 B RxFIFO
00804307 B __bss_end

注意:我获得这张地图的方法是使用nm 和命令nm -S -n MotorBoard\ Xmega\ Large\ FIFO.elf。第一列是地址,第二列是变量的大小。

ATMEL ATxmega128A1 Datasheet 表示您在 0x20000x3FFF 之间有 8KB 的 SRAM。如您所见,您的两个 FIFO 缓冲区都与 0x3FFF 限制重叠(0x3E47 用于 TxFIFO0x40A7 用于 RxFIFO)。

解决问题的一种方法是通过将变量声明为const 将它们移动到ROM 中。例如,我可以看到VersionMonthVersionMajor 可能是不变的。

【讨论】:

  • 谢谢!可以清楚地看到它重叠的地方。如何获得该内存映射,以便将来避免这种情况?
  • 我编辑了我的消息以添加有关我如何获得映射的更多信息。 nm 对嵌入式工程师来说是一个非常有用的工具 ;-)
【解决方案2】:

没有。只是没有。

你的概念是错误的。使用 FIFO 缓冲区,您应该在程序中只使用一个写入点和一个读取点。在您的情况下,写入将在 UART RX 中断内,而读取将在程序的 while (1) 循环中。

足够的结构信息是这样的:

#define FIFO_SIZE       100
typedef struct {
    uint16_t Write, Read;
    uint8_t Buffer[FIFO_SIZE];
} FIFO;

然后你需要 2 个(更好的 3 个)函数:

  • FIFO_Write(FIFO* f, uint8_t ch)
  • FIFO_Get(FIFO* f, uint8_t* ch)
  • FIFO_Init(FIFO* f);

而实现类似于:

void FIFO_Init(FIFO* f) {
    f->Write = 0;
    f->Read = 0;
}

//This function needs check if buffer is full, will update it.
void FIFO_Write(FIFO* f, uint8_t ch) {
    //Check here if buffer is full first
    f->Buffer[f->Write++] = ch;
    if (f->Write >= FIFO_SIZE) {
        f->Write = 0;
    }
}

uint8_t FIFO_Read(FIFO* f, uint8_t* ch) {
    volatile uint16_t write = f->Write, read = f->Read;
    if (write == read) {
        return 0;  //FIFO empty, return 0
    }
    *ch = f->Buffer[f->Read++];
    if (f->Read >= FIFO_SIZE) {
        f->Read = 0;
    }
    return 1;      //character is valid
}

现在写入和读取的函数是相互安全的。您可以从 IRQ 或任何地方写入并同时读取,完全不用担心。

您可以轻松地在中断中使用 FIFO_Write,当您从 FIFO 读取时,您不需要禁用中断。这就是先进先出的目的。单写点,单读点。

希望对你有帮助。

【讨论】:

  • 这实际上是我的代码中的内容。我只向您展示了 FIFO_Get(您的 fifo 读取)而不是我的 FIFO_Put(您的 fifo 写入)我还有一个 Init 函数。
  • 问题不是我的函数而是缓冲区的内存映射。
  • 懒得检查了,但是如果我把我的函数/结构改成和你一样的,并且仍然有同样大的fifo大小,它仍然会产生我的错误
  • 但你不能确定内存是个问题。我相信您的实现是一个问题,而不是 MCU 硬件。
  • 我所做的只是将指针传递给函数,然后内存就乱套了。
猜你喜欢
  • 2013-02-04
  • 1970-01-01
  • 2012-08-26
  • 1970-01-01
  • 2013-03-21
  • 1970-01-01
  • 2017-04-17
  • 2014-11-11
  • 1970-01-01
相关资源
最近更新 更多