c - 将 9 位值写入字节数组(或 EEPROM),而不会浪费后续字节中的剩余位

标签 c arduino eeprom

我无法通过谷歌找到答案,所以我去找它并编写了好几个小时。

我想将 9 位值保存到 eeprom 而不会浪费其他 7 位。 我保存的值最多为 500,而且我的 EEPROM 所剩无几。

同样的原理可以应用于数组,我这样做只是为了不降低 EEPROM 的容量。

所以我做了这个小程序:

/*
 * Write only a certain number of bits to EEPROM.
 *
 * keeps the other bit in the byte of the eeprom as they are.
 *
 *  Working version with 9 bits:
 *          2019-10-03 15:57
 *          2019-10-03 22:09 tested with chars too
 *          2019-10-04 08:25 works with 7 bit chars also!
 *          2019-10-04 12:27 fixed the combining of oldByte and new values in writeBitsToEEPROM(), because chars like 'ö' altered previous bit (left side) that should not have been altered.
 *
 */


#include "arduino.h"
#include "EEPROM.h"
#include "math.h"


#define BIT_BLOCKS_COUNT 15
#define BLOCK_BYTE_COUNT 17



#define ARRAY_SIZE BLOCK_BYTE_COUNT+2

//TODO: change back to original value
#define EEPROM_SIZE ARRAY_SIZE

byte fakeEEPROM[ARRAY_SIZE] = {0};


String byteToString(byte value){
  char byteChar[9];
  byteChar[8] = '\0'; //we need a terminator

  for(int i=7; i>=0; i--){
    byteChar[7-i] = (value & (1 << i)) ? '1' : '0';
  }

  return String(byteChar);
}


String byteToString(unsigned long value, byte bytesToRead){
    String str1 = byteToString(value >> 8);
    String str2 = byteToString(value & 0xFF);

    return str1 + " " + str2;
}


int globBlockStartAdress = 0;
byte globNumberOfBits = 0;
int globBlockSizeBytes = 0;

bool initBitBlock(int blockStartAdress, int blockCount, byte numberOfBits) {

    globBlockStartAdress = blockStartAdress;
    globNumberOfBits = numberOfBits;

    // calc needed number of bytes and roud up
    int tempBlockSize = blockCount * numberOfBits / 8;
    if(blockCount * numberOfBits % 8)
        tempBlockSize++;

    // make number of bytes even
    if(tempBlockSize % 2)
        tempBlockSize++;

    globBlockSizeBytes = tempBlockSize;

    if(blockStartAdress + globBlockSizeBytes > EEPROM_SIZE)
        return false;

    return true;
}

/*
 * Writes 1 to 9 bits to "internalAdress" within a designated block in eeprom
 */
void writeBitsToEEPROM(unsigned int bitsToBeWritten, int internalAdress){

    //TODO: check if value is not higher than what can be stored
//  if(bitsToBeWritten){
//
//  }

    int trueEEPROMAdress = globBlockStartAdress + internalAdress * globNumberOfBits / 8;

    if(trueEEPROMAdress + 1 >= ARRAY_SIZE || internalAdress * globNumberOfBits / 8 >= globBlockSizeBytes){
        Serial.print("globBlockSizeBytes: ");
        Serial.println(globBlockSizeBytes);

        Serial.println("FEHLER writeBitsToEEPROMWTF: ");
        Serial.println(trueEEPROMAdress + 1);
        Serial.println(internalAdress * globNumberOfBits / 8 );

    }

    byte startBitOfEEPROMByte = (internalAdress * globNumberOfBits) % 8;

    unsigned int oldIntFromEEPROM = (fakeEEPROM[trueEEPROMAdress] << 8) | fakeEEPROM[trueEEPROMAdress + 1];

    //Todo: change to eeprom
    //filter out only the bits that need to be kept.
    //EEPROM.get(trueEEPROMAdress, oldEEPROMByteBits);

    // there might be bits in the byte that we dont want to change. left side and right side
    unsigned int mask1KeepFromEEPROM = (0xFFFF <<  (16 - startBitOfEEPROMByte));
    unsigned int mask2KeepFromEEPROM = (0xFFFF >>  (startBitOfEEPROMByte + globNumberOfBits));

    //if(16 - startBitOfEEPROMByte - numberOfBits > 0)
    //mask2KeepFromEEPROM= (0xFFFF >>  (startBitOfEEPROMByte + numberOfBits));

    // masks combined
    unsigned int maskIntToKeepFromEEPROM = mask1KeepFromEEPROM | mask2KeepFromEEPROM;


    int newEEPROMInt = (oldIntFromEEPROM & maskIntToKeepFromEEPROM) | ((bitsToBeWritten << (16 - globNumberOfBits - startBitOfEEPROMByte) & ~maskIntToKeepFromEEPROM));


    //Todo: change to eeprom
    //write
    //EEPROM.update(trueEEPROMAdress, newEEPROMByteBitsA);
    fakeEEPROM[trueEEPROMAdress] =      (newEEPROMInt >> 8);
    fakeEEPROM[trueEEPROMAdress + 1] =  (byte) newEEPROMInt;

    if(trueEEPROMAdress + 1 > BLOCK_BYTE_COUNT){
        Serial.println("FEHLER writeBitsToEEPROM");
        Serial.println(trueEEPROMAdress + 1);

        Serial.println("blockStartAdress");
        Serial.println(globBlockStartAdress);

        Serial.println("internalAdress");
        Serial.println(internalAdress);

        Serial.println("numberOfBits");
        Serial.println(globNumberOfBits);
    }


//  Serial.print("trueEEPROMAdress: ");
//  Serial.println(trueEEPROMAdress);
//
//  Serial.print("internalAdress: ");
//  Serial.println(internalAdress);
//
//  Serial.print("globNumberOfBits: ");
//  Serial.println(globNumberOfBits);
//
//  Serial.print("bitsToBeWritten:         ");
//  Serial.println(byteToString(bitsToBeWritten,2));
//
//  Serial.print(" mask1KeepFromEEPROM:    ");
//  Serial.println(byteToString(mask1KeepFromEEPROM,2));
//
//  Serial.print("mask2KeepFromEEPROM:     ");
//  Serial.println(byteToString(mask2KeepFromEEPROM,2));
//
//  Serial.print("maskIntToKeepFromEEPROM: ");
//  Serial.println(byteToString(maskIntToKeepFromEEPROM,2));
//
//  Serial.print("oldIntFromEEPROM:        ");
//  Serial.println(byteToString(oldIntFromEEPROM,2));
//
//  Serial.print("newEEPROMInt:            ");
//  Serial.println(byteToString(newEEPROMInt,2));
//
//  Serial.print("512:                     ");
//  Serial.println(byteToString(512, 2));
//
//  Serial.print("65535:                   ");
//  Serial.println(byteToString(65535, 2));
}


unsigned int ReadBitsFromEEPROM(int internalAdress){

    int trueEEPROMAdress = globBlockStartAdress + internalAdress * globNumberOfBits / 8;
    byte startBitOfEEPROMByte = (internalAdress * globNumberOfBits) % 8;

    if(trueEEPROMAdress + 1 > BLOCK_BYTE_COUNT)
        Serial.println("FEHLER readBits");
    unsigned int oldIntFromEEPROM = (fakeEEPROM[trueEEPROMAdress] << 8) | fakeEEPROM[trueEEPROMAdress + 1];

    //Todo: change to eeprom
    //filter out only the bits that need to be kept.
    //EEPROM.get(trueEEPROMAdress, oldEEPROMByteBits);

    unsigned int mask1KeepFromEEPROM = (0xFFFF <<  (16 - startBitOfEEPROMByte));
    unsigned int mask2KeepFromEEPROM = (0xFFFF >>  (startBitOfEEPROMByte + globNumberOfBits));

    unsigned int maskIntToKeepFromEEPROM = mask1KeepFromEEPROM | mask2KeepFromEEPROM;

    unsigned int valueFromEEPROM = ~maskIntToKeepFromEEPROM & oldIntFromEEPROM;


//  Serial.print("trueEEPROMAdress: ");
//  Serial.println(trueEEPROMAdress);
//
//  Serial.print("internalAdress: ");
//  Serial.println(internalAdress);
//
//  Serial.print("numberOfBits: ");
//  Serial.println(numberOfBits);
//
//  Serial.print(" mask1KeepFromEEPROM:    ");
//  Serial.println(byteToString(mask1KeepFromEEPROM,2));
//
//  Serial.print("mask2KeepFromEEPROM:     ");
//  Serial.println(byteToString(mask2KeepFromEEPROM,2));
////
//  Serial.print("maskIntToKeepFromEEPROM: ");
//  Serial.println(byteToString(maskIntToKeepFromEEPROM,2));
////
//  Serial.print("oldIntFromEEPROM:        ");
//  Serial.println(byteToString(oldIntFromEEPROM,2));

    return (valueFromEEPROM >> (16 - globNumberOfBits - startBitOfEEPROMByte));
}



void setup() {
    Serial.begin(57600);
    Serial.print(F("\n# Programversion: "));

    Serial.print(__TIME__);
    Serial.print(" ");
    Serial.println(__DATE__);



    Serial.println("Setup finished");
    delay(1000);
}


void printEEPROM(){

    for(int i = 0; i < ARRAY_SIZE; i++){
        byte b;

        //Todo: change to eeprom
        //EEPROM.get(i, b);
        b = fakeEEPROM[i];

        Serial.print(byteToString(b));
        Serial.print(" ");
    }
    Serial.println();
}


void testNumbers() {

    Serial.println("bits?");
    while( ! Serial.available());
    String input = Serial.readString();

    unsigned int value = input.toInt();


    initBitBlock(1, 15, 9);

    //  Serial.print("value:              ");
    //  Serial.println(byteToString(value));



    for(int i = 0; i < BIT_BLOCKS_COUNT;i++){

        for(int j = 0; j < BLOCK_BYTE_COUNT; j++){
            fakeEEPROM[j] = 0xFF;
            if(j > BLOCK_BYTE_COUNT)
                Serial.println("FEHLER testNumbers");
        }

//      Serial.print("EEPROM before: ");
//      printEEPROM();

        writeBitsToEEPROM(value, i);
        Serial.print("Returned: ");
        Serial.println(ReadBitsFromEEPROM(i));

//      Serial.print("EEPROM after:  ");
//      printEEPROM();
//      Serial.println();
    }
    delay(1000);
}


#define CHAR_COUNT 16


void testChars() {

//  Serial.println("bits?");
//  while( ! Serial.available());
//  String input = Serial.readString();
//
//  unsigned int value = input.toInt();

    initBitBlock(1, CHAR_COUNT, 7);

    Serial.println("string?");
    while( ! Serial.available());
    String input = Serial.readString();

    Serial.println(input);

    char testString[CHAR_COUNT] = {'\0'};

    input.toCharArray(testString, CHAR_COUNT, 0);

    for(int j = 0; j < ARRAY_SIZE; j++){
            fakeEEPROM[j] = 0;//xFF;
        }


    for(int i = 0; i < CHAR_COUNT; i++){



        Serial.print("EEPROM before: ");
        printEEPROM();

        writeBitsToEEPROM(testString[i], i);


        Serial.print("EEPROM after:  ");
        printEEPROM();
        Serial.println();
    }


    Serial.println("Returned: ");
    for(int i = 0; i < CHAR_COUNT; i++){


        Serial.print((char) ReadBitsFromEEPROM(i));

    }

    Serial.println();


    delay(1000);

}

void loop(){
    testChars();
    testNumbers();

}


当然这还不完整。它只是为了保存那些 9 位值。

我的问题是:有没有其他人编写过这样的函数 - 或者知道在哪里可以找到它 - 不限于 9 位(10 位将跨越 3 个字节)?

最佳答案

此函数应从输入数组pVals 中的每个值中获取bitsPerVal 给定的位数,并将它们打包到pOutBytes 指向的字节数组中:

#include <stdint.h>

void pack_bits(uint32_t *pVals, size_t numVals, int bitsPerVal, uint8_t *pOutBytes)
{
    uint32_t mask = ~(UINT32_MAX << bitsPerVal);
    int outBitsLeft = 8;
    int inBitsLeft = bitsPerVal;

    while(numVals > 0)
    {
        if(inBitsLeft > outBitsLeft)
        {
            inBitsLeft -= outBitsLeft;
            *pOutBytes |= (*pVals & mask) >> inBitsLeft; 
            mask >>= outBitsLeft;
            outBitsLeft = 0;
        }
        else
        {
            outBitsLeft -= inBitsLeft;
            *pOutBytes |= (*pVals & mask) << outBitsLeft;
            mask = ~(UINT32_MAX << bitsPerVal);
            inBitsLeft = bitsPerVal;
            --numVals;
            ++pVals;
        }

        if(0 == outBitsLeft)
        {
            outBitsLeft = 8;
            ++pOutBytes;
        }
    }
}

pOutBytes 指向的数组必须大小合适(即 ((numVals*bitsPerVal) + 7)/8)并在调用前初始化为零。之后您可以将其写入您的 EEPROM。

希望它能正常工作,不过我已经对其进行了大量测试。

关于c - 将 9 位值写入字节数组(或 EEPROM),而不会浪费后续字节中的剩余位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58226500/

相关文章:

c++ - Linux API 列出正在运行的进程?

C代码编码器! (数组)(嵌套循环)(fgets)(语法?)

检查c中的链接列表中是否已存在元素

c++ - Arduino Sketch 编译然后在上传时卡住

c - GCC 以相反的顺序编译 EEPROM 地址

c - 如何删除m25p40闪存的单个页面而不删除其他页面?

c++ - 用简单的话表示序列点

php - Arduino 和网络服务器之间的连接监控

c++ - Arduino 从单个数字输入读取脉宽频率和占空比

memory - 在 Arduino 的 EEPROM 中读取和写入结构