简体   繁体   English

ESP8266尝试从字节数组读取浮点数时异常

[英]ESP8266 Exception while trying to read float from byte array

I have a strange problem here with my ESP8266 (Wemos D1 mini). 我的ESP8266(Wemos D1 mini)在这里遇到一个奇怪的问题。 I'm wrting a library for building a network with the nRF24L01 transmitter. 我正在编写一个用于使用nRF24L01发送器构建网络的库。

I have defined a gloabl uint8_t arry which represents my receive buffer. 我定义了一个gloubl uint8_t arry,它表示我的接收缓冲区。 When receiving a transmission from an other device, the array gets filled. 当从其他设备接收到传输时,该阵列将被填充。 After filling the array, I start to parse the buffer, to identify if there is a valid Header/Frame. 填充数组后,我开始解析缓冲区,以识别是否存在有效的Header / Frame。 The header contains some typical data like sender, checksum... If the header is valid the data of frame gets parsed. 标头包含一些典型数据,例如发件人,校验和...。如果标头有效,则将解析帧数据。 The structure of the data is always the same. 数据的结构始终相同。 There can be x so called DataTriples. 可以有x个所谓的DataTriples。 A DataTriple is a combination of Key (cstring), Type of Value (uint8_t) and the Value itself. DataTriple是键(cstring),值的类型(uint8_t)和值本身的组合。 The type of value can be basic types like INT8, UINT8 or FLOAT or STRING. 值的类型可以是基本类型,例如INT8,UINT8或FLOAT或STRING。

And now the stange thing. 现在,这是坚固的东西。

To save memory I do not copy the values from the buffer into dedicated variables. 为了节省内存,我不将缓冲区中的值复制到专用变量中。 I alway point to the positions in the buffer. 我总是指向缓冲区中的位置。 And when I want to read a value, I do a simple pointer cast and read out the values. 当我想读取一个值时,我做了一个简单的指针转换并读取了这些值。 This is perfectly working if the value is a string or a simple 1 byte long uint8_t. 如果值是字符串或简单的1字节长的uint8_t,则此方法非常有用。 But if it is a float or an uint32_t I get a LoadStoreAlignmentCause Excetion that's telling me, that I want to access not alligned memory. 但是,如果它是浮点数或uint32_t,我会得到一个告诉我的LoadStoreAlignmentCause Excetion,我想访问的不是指定的内存。 How can this be? 怎么会这样? The pointers are definitly pointing to the correct buffer positon. 指针明确地指向正确的缓冲区位置。 I checked this, because if I create a new float variable and do a memcpy from the buffer to the variables address, everything is good. 我检查了一下,因为如果创建一个新的float变量并从缓冲区到变量地址执行memcpy,那么一切都很好。 But I a create a float pointer and point to the position in the buffer, I get a this exception. 但是我创建了一个浮点指针并指向缓冲区中的位置,却得到了这个异常。

Can somebody tell me the reason or tell me what I'm doing wrong? 有人可以告诉我原因还是我做错了什么?

Here are some code fragment, to get a better understanding. 以下是一些代码片段,以使您更好地理解。

bool SBSmartHomeBasicDevice::parseDataTriples() {
if (_IncommingTransmission) {
    uint8_t* iCurrentPos;
    uint8_t iDataTripleCount = 0;
    // _LastReceivedFrame.Payload points to the receive buffer
    for (iCurrentPos = _LastReceivedFrame.Payload; iCurrentPos < (_LastReceivedFrame.Payload + _LastReceivedFrame.Header.PayloadSize); iCurrentPos) {
        // Catch the type of the triple
        uint8_t type = *((uint8_t*)iCurrentPos);
        // increase the pointer about uint8_t size
        iCurrentPos += sizeof(uint8_t);
        // Catch the key of the triple
        char* key;
        key = (char*)(iCurrentPos);
        // increase the pointer about strlen of key + 1 
        iCurrentPos += strlen((char*)iCurrentPos) + 1;
        // catch the value
        void* value = (void*)(iCurrentPos);
        _LastReceivedTriples[iDataTripleCount].setType(type);
        _LastReceivedTriples[iDataTripleCount].setKey(key);


        // ***
        // Here starts the interesting part 
        // ***
        float* fTmp = (float*)iCurrentPos;
        // The following works perfect
        // ****
        float f;
        memcpy(&f, fTmp, sizeof(float));
        Serial.println(f);
        // ****
        // The following causes an exception
        Serial.println(*fTmp);
        // *** EXCEPTION ***


        _LastReceivedTriples[iDataTripleCount].setValue(iCurrentPos);
        // now increase the pointer
        switch (type) {
        case SMART_HOME_VALUE_TYPE_STRING:
            iCurrentPos += strlen((char*)iCurrentPos) + 1;
            break;
        case SMART_HOME_VALUE_TYPE_INT8:
        case SMART_HOME_VALUE_TYPE_UINT8:
            iCurrentPos += sizeof(int8_t);
            break;
        case SMART_HOME_VALUE_TYPE_INT16:
        case SMART_HOME_VALUE_TYPE_UINT16:
            iCurrentPos += sizeof(int16_t);
            break;
        case SMART_HOME_VALUE_TYPE_FLOAT:
            iCurrentPos += sizeof(float);
            break;
        case SMART_HOME_VALUE_TYPE_INT32:
        case SMART_HOME_VALUE_TYPE_UINT32:
            iCurrentPos += sizeof(int32_t);
            break;
        default:
            Serial.println("parseDataTriples(): Unknown ValueType");
        }
        _LastReceivedTriples[iDataTripleCount].print();
        iDataTripleCount++;
        _LastReceivedTriplesCount = iDataTripleCount;
    } 
    return true;
}
return false;

} }

An Thanks a lot for taking the time and helping me. 非常感谢您抽出宝贵的时间为我提供帮助。

Greets Schullebernd 迎接舒勒伯恩德

The ESP8266 can only read float s (and 16 bit int s, 32 bit int s, double s) from properly aligned addresses, ie addresses that are a multiple of the data types. ESP8266只能从正确对齐的地址(即多个数据类型的地址)中读取float (以及16位int ,32位intdouble )。 As float is 4 bytes long, it can only be read from an address that is a multiple of 4. Otherwise you get the mentioned exception. 由于float为4个字节长,因此只能从4的倍数的地址中读取它。否则,将遇到上述异常。

You are working with a uint8_t array. 您正在使用uint8_t数组。 uint8_t is 1 byte long and therefore does not need and probably is not aligned in any way. uint8_t长度为1个字节,因此不需要并且可能未以任何方式对齐。 The array might start at address 170001. If you now have a float value at position 40 within the array, your code tries to read a float value from address 170041, which is not properly aligned and causes an exception. 该数组可能从地址170001开始。如果现在数组中位置40处有一个float值,则您的代码将尝试从地址170041中读取一个float值,该值未正确对齐并导致异常。

The solution is to first copy the bytes for the float value to a local, properly aligned variable. 解决方案是首先将float值的字节复制到正确对齐的局部变量。 That's what you do but then you access it again in the non-aligned location and get an exception. 这就是您要做的,但是随后您在未对齐的位置再次访问它并获得异常。

    float* fTmp = (float*)iCurrentPos; // does not copy; takes a pointer to the orignal location

    float f;
    memcpy(&f, fTmp, sizeof(float)); // copies data to aligned variable

    Serial.println(f); // prints value from aligned variable

    Serial.println(*fTmp); // prints value for original, non-aligned location

Just get rid of the last line. 只是摆脱最后一行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM