C语言 将 9 位值写入字节数组(或 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] = ''; //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] = {''};
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个字节(?

此函数应从输入数组中的每个值中获取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。

希望这运行良好,不过我已经做了很多测试。

下面是一个示例,说明来自 2 个不同字段的 10 位(写入时实际上是 16 位...(如何写入 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));
}

相关内容

  • 没有找到相关文章

最新更新