Запись 9-битных значений в байтовый массив (или EEPROM) без потери оставшегося бита в следующем байте - PullRequest
2 голосов
/ 03 октября 2019

Я не смог найти ответ с Google, поэтому я пошел на него и запрограммировал довольно много часов.

Я хочу сохранить 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байт)

Ответы [ 2 ]

1 голос
/ 04 октября 2019

Эта функция должна принимать число битов, заданное bitsPerVal от каждого значения во входном массиве pVals, и упаковывать их в байтовый массив, на который указывает 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 после.

Надеюсь, это хорошо работает, хотя я провел много испытаний на нем.

0 голосов
/ 04 октября 2019

Вот пример того, как 10 бит (фактически 16 бит при записи ...) из 2 разных полей могут записывать в 16 бит вывода.

struct EEPROM_Output 
{
 uint16_t a   : 9;  // 0 - 511 can be stored here
 uint16_t b   : 1;  // 0 or 1 here.
 uint16_t pad : 6;  // Future use - we place this here to make it obvious that there are bits remaining.
};

void foo()
{
    struct EEPROM_Output save;
    save.a = 100;
    save.b = 1;

    WriteToEEPROM(&save, sizeof(save));
}
...