C Memory Buffer破坏微控制器内存分配 - Atmel ATxmega

时间:2017-06-06 04:35:55

标签: c memory memory-management atmel

我正在使用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

2 个答案:

答案 0 :(得分: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。例如,我可以看到VersionMonthVersionMajor可能是不变的。

答案 1 :(得分:0)

没有。只是没有。

你的观念是错误的。使用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的目的。单写点,单读点。

希望它有所帮助。