C 内存缓冲区破坏微控制器内存分配 - Atmel ATxmega
C Memory Buffer corrupting micro-controller memory allocation - Atmel ATxmega
我正在使用 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
没有。只是没有。
你的观念是错误的。使用 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(先进先出* 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 的目的。单写点,单读点。
希望对您有所帮助。
从 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 表示您在 0x2000
和 0x3FFF
之间有 8KB 的 SRAM。正如您所看到的,您的两个 FIFO 缓冲区都与 0x3FFF
限制重叠(0x3E47
代表 TxFIFO
,0x40A7
代表 RxFIFO
)。
解决问题的一种方法是将一些变量声明为 const
,从而将它们移动到 ROM 中。例如,我可以看到 VersionMonth
、VersionMajor
可能是常量。
我正在使用 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
没有。只是没有。
你的观念是错误的。使用 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(先进先出* 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 的目的。单写点,单读点。
希望对您有所帮助。
从 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 表示您在 0x2000
和 0x3FFF
之间有 8KB 的 SRAM。正如您所看到的,您的两个 FIFO 缓冲区都与 0x3FFF
限制重叠(0x3E47
代表 TxFIFO
,0x40A7
代表 RxFIFO
)。
解决问题的一种方法是将一些变量声明为 const
,从而将它们移动到 ROM 中。例如,我可以看到 VersionMonth
、VersionMajor
可能是常量。