Forum: Mikrocontroller und Digitale Elektronik Einzelnes Bit im RAM schreiben oder lesen


von ArduStemmi (Gast)


Lesenswert?

Guten Abend!
Ich möchte möglichst viele 5 Bit breite Datensätze speichern. Bisher 
habe ich im RAM ein Byte Array (uint8_t Daten[1024]) angelegt und in 
jedem Byte einen 5 bit breiten Datensatz gespeichert. Damit verschenke 
ich 3 * 1024 potentielle Speicherstellen. Wenn ich einen Zeiger hätte, 
der 5 Bit weit zeigt, wäre das Problem zu lösen. Leider weiß ich nicht, 
wie ich einen solchen Zeiger erstellen kann. Könnt Ihr helfen? Eine 
Nebenbedingung ist aber, es darf nicht allzuviel Rechenzeit dauern, weil 
die 5 bit breiten Datensätze möglichst schnell von einem Port gelesen 
werden müssen. Es soll ein Logic Analyzer werden. Ich benutze C auf 
einem Atmega 328P.
Danke!

von holger (Gast)


Lesenswert?

>Wenn ich einen Zeiger hätte,
>der 5 Bit weit zeigt, wäre das Problem zu lösen.

Sowas gibt es nicht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

ArduStemmi schrieb:
> nicht allzuviel Rechenzeit dauern, weil die 5 bit breiten Datensätze
> möglichst schnell von einem Port gelesen werden müssen.
Vergiss es. Dein RAM ist byteorientiert, alles was davon abweicht, 
kostet Rechenzeit. Oder andersrum: für 5 von 8 Kombinationen fallen dann 
gleich 2 der aufwändigen Speicheroperationen zusätzlich zusammen mit den 
nötigen Schiebe- und Maskieroperationen an...

von Falk B. (falk)


Lesenswert?

@ArduStemmi (Gast)

>jedem Byte einen 5 bit breiten Datensatz gespeichert. Damit verschenke
>ich 3 * 1024 potentielle Speicherstellen. Wenn ich einen Zeiger hätte,
>der 5 Bit weit zeigt, wäre das Problem zu lösen. Leider weiß ich nicht,
>wie ich einen solchen Zeiger erstellen kann. Könnt Ihr helfen?

Du musst dir eine Funktion schreiben, welche deine 5 Bit Datensatz in 
das 8 Bit Array einfügt. Dazu brauchst du Bitmanipulation, in dem 
Artikel sind auch Beispiele drin.
1
void write_5bit(uint8_t data, uint16_t index) {
2
  // selber ausfüllen
3
}
4
5
uint8_t read_5bit(uint16_t index) {
6
  uint8_t data;
7
  // selber ausfüllen
8
  return data;
9
}

> Eine
>Nebenbedingung ist aber, es darf nicht allzuviel Rechenzeit dauern, weil
>die 5 bit breiten Datensätze möglichst schnell von einem Port gelesen
>werden müssen. Es soll ein Logic Analyzer werden.

Alles relativ. Welche maximale Samplerate strebst du an? 100kHz? 
Vielleicht reichen ja auch 4 Bit, dann kannst du einfach 2 Samples in 1 
Byte packen.

von Tom Thomsen (Gast)


Lesenswert?

ArduStemmi schrieb:
> Ich möchte möglichst viele 5 Bit breite Datensätze speichern. Bisher
> habe ich im RAM ein Byte Array (uint8_t Daten[1024]) angelegt und in
> jedem Byte einen 5 bit breiten Datensatz gespeichert.

Pack deine Datenbits in 40Bit große Datenblöcke. Dann wird alles gut.

von Maik (Gast)


Lesenswert?

das klingt doch nicht wirklich kompliziert.
Dafür würde ich einfach mal in einem C Forum fragen oder Pascal!
Ja Pascal :-) klingt jetzt komisch ist aber so, die Leute dort sind 
meist extrem Hilfreich, und können Dir den richtigen Denkanstoß geben
Ich hätte da schon eine Idee, weiß aber auch nicht wie das geschickt 
umgesetzt wird.
Hier wirst Du nur hören, geht nicht, gibts nicht oder kauf Dir ein Buch 
:-)

von Maik (Gast)


Lesenswert?

Das mit den 4 Bit klingt schonmal gut :-) Ich dachte eher an fortlaufend 
sammeln und sobald 8 Vollständig sind schreiben oder so
Gibt sicher noch etliche andere schnelle Lösungen

von Maik (Gast)


Lesenswert?

bzw 10 bit splitten in 5 und dann warten bis das nächste 8er Paket 
kommt..jeder der hier jetzt wieder rummaulen will, soll erstmal 
überlegen, dann versteht er wie ich es meine

von Maik (Gast)


Lesenswert?

meine 8 Bit splitten.egal :-)

von (prx) A. K. (prx)


Lesenswert?

Tom Thomsen schrieb:
> Pack deine Datenbits in 40Bit große Datenblöcke. Dann wird alles gut.

Wobei man bei AVRs mangels effizienter Shifts etwas kreativ vorgehen 
muss. Also erst 8 4-Bit Blöcke speichert und fehlenden Bits in einem 
Byte sammelt.

von MSP430 (Gast)


Lesenswert?

Versuch einmal
http://www.c-howto.de/tutorial-strukturierte-datentypen-bitfelder.html
mit 8 Member a 5 Bit als packed und schau was der Compiler daraus macht. 
Besser wird das von Hand auch nicht. Dann kannst du entscheiden: 
Speicher vs. Geschwindigkeit.

von holger (Gast)


Lesenswert?

>Dann kannst du entscheiden: Speicher vs. Geschwindigkeit.

Wenn man dann noch einen ATMeg1284 mit 16kB RAM nimmt
fällt die Wshl nicht mehr ganz so schwer. Oder gleich
einen Cortex-M4;) Der wäre mit per Timer getriggertem
DMA sowieso besser geeignet für einen LA.

von (prx) A. K. (prx)


Lesenswert?

MSP430 schrieb im Beitrag #4470101:
> Besser wird das von Hand auch nicht.

Doch. Bei einem AVR wird es schneller, wenn man die 5 Bits in 4+1 
aufspaltet und getrennt speichert. Das macht aber kein Compiler von sich 
aus. Darf er nicht.

: Bearbeitet durch User
von ArduStemmi (Gast)


Lesenswert?

Tom Thomsen schrieb:
> Pack deine Datenbits in 40Bit große Datenblöcke. Dann wird alles gut.

Das klingt gut, aber wie mache ich 40 bit große Datenblöcke?

von (prx) A. K. (prx)


Lesenswert?

ArduStemmi schrieb:
> Das klingt gut, aber wie mache ich 40 bit große Datenblöcke?

Aus 5 Bytes.

von ArduStemmi (Gast)


Lesenswert?

Ja! Aber wie greife ich auf Bit 0, 5, 10, ... 35 zu?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

ArduStemmi schrieb:
> Ja! Aber wie greife ich auf Bit 0, 5, 10, ... 35 zu?
Du rechnest dir erst mal mit viel Aufwand die passende Adresse aus und 
maskierst dann am besten über eine Tabelle das passende Bit heraus.

Langer Rede kurzer Sinn: es ist unsinnig. Nimm einen grösseren 
Controller und/oder lies 8 Bits ein. Nicht umsonst haben übliche LA 
genau solche Podbreiten...

von Basti B. (basti195) Benutzerseite


Lesenswert?

Ich würde es in 8 bit Variablen lassen.
Du kannst über Bitmanipulation 2 5-bit zahlen in 2 8-bit Variablen 
Speichern
Würde dann so ausschauen
var1         var2
--------    --------
12345123    45.....

die Zahlen beschreiben die Wertigkeit der 5 bit zahl.
Aufwendig wird es wenn du genau den x-ten Wert adressieren möchtest.
Hier würde ich
Bytenummer = (Anzahl_5-bit_var * 5)/8   // Berechnet dir in welchen Byte 
deine 5 bit anfangen
start nummer = (anzahle_5-bit_var * 5)%8 // Berechnet die stelle des 
ersten Bytes der 5-bit zahl

bsp
bytenummer:   1     2         3
byte:         01234567  01234567 01234567  .....
              --------  -------- -------
5-bit:        12345123  45123451 23451234  5.....
gesucht                        x
Startadresse der 3. 5bit Wertes (x)
3.var
Bytenummer 3*5/8   = 2
start nummer 2*5%8 = 7

Nun musst du nur einen Start wert bestimmen und den Speicher Reservieren
Grüße
basti195

: Bearbeitet durch User
von Sascha (Gast)


Lesenswert?

Du verschenkst 3072 Bit wenn du 8 Bit Variablen verwendest.

Ist ja beeindruckend.

Bist du dir vollkommen sicher, dass sich dafür der Thread überhaupt 
gelohnt hat, geschweige denn das Programmieren und der 
Performanceverlust durch die Bitoperationen?

von ArduStemmi (Gast)


Lesenswert?

Das ist richtig!

Einer der wichtigsten Gründe, warum ich mich mit Mikrocontrollern 
beschäftige, ist das damit verbundene Lernen! In Wirklichkeit brauche 
ich gar keinen Logicanalyzer! Aber ich wollte mich mal mit der Sache 
umfassend beschäftigen. Also das komplette Programm: Hardwareentwurf und 
Basteln, Mikrocontroller programmieren und PC-Programm mit allerlei 
Schnickschnack schreiben!

In diesem Zusammenhang kam mir die Frage, ob man den RAM nicht Bit für 
Bit beschreiben und lesen kann!

Das geht scheinbar nicht ohne aufwendiges Bitgeschiebe (also viel Zeit, 
naja im Mikrosekundenbreich viel) Wieder was gelernt!

Ich werde von fünf auf vier Kanäle wechseln und kann dann 2048 Samples 
speichern!

Ich habe übrigens bei 16 MHz auf 800 kHz  samplingrate (entspricht 1,25 
Mikrosekunden Samplingtime)  gebracht! Passt ganz genau, weil es muss ja 
ein ganzzahliges Vielfaches von 0,0625 Mikrosekunden sein! Ach ja! 
Geeicht ist es nicht! Es ist so genau wie der Quartz taktet! Für meine 
Anwendung ausreichend!

Und Spaß macht das!

von Fabian O. (xfr)


Lesenswert?

Hier ein Beispiel, wie Du es implementieren könntest:
1
#include <assert.h>
2
#include <stdint.h>
3
#include <stdio.h>
4
#include <stdlib.h>
5
6
void write_value(uint8_t* data, uint16_t pos, uint8_t value)
7
{
8
  uint16_t start_byte = pos * 5 / 8;
9
  uint8_t  start_bit  = pos * 5 % 8;
10
  
11
  uint8_t low_mask  =   0x1F << start_bit;
12
  uint8_t low_value = (value << start_bit) & low_mask;
13
  data[start_byte]  = (data[start_byte] & ~low_mask) | low_value;
14
 
15
  if (start_bit > 3)
16
  {
17
    uint8_t high_mask    =   (0x1F << start_bit) >> 8;
18
    uint8_t high_value   = ((value << start_bit) >> 8) & high_mask;
19
    data[start_byte + 1] = (data[start_byte + 1] & ~high_mask) | high_value;
20
  }
21
}
22
23
uint8_t read_value(const uint8_t* data, uint16_t pos)
24
{  
25
  uint16_t start_byte = pos * 5 / 8;
26
  uint8_t  start_bit  = pos * 5 % 8;
27
  
28
  uint8_t value = (data[start_byte] >> start_bit) & 0x1F;
29
  if (start_bit > 3)
30
  {
31
    value |= ((data[start_byte + 1] << 8) >> start_bit) & 0x1F;
32
  }
33
34
  return value;
35
}
36
37
uint8_t raw_data[1024];
38
uint8_t compressed_data[640];
39
40
int main(void)
41
{
42
  for (uint16_t i = 0; i < 1024; ++i)
43
  {
44
    uint8_t value = rand() % 32;
45
    raw_data[i] = value;
46
    write_value(compressed_data, i, value);
47
  }
48
  
49
  for (uint16_t i = 0; i < 1024; ++i)
50
  {
51
    assert(read_value(compressed_data, i) == raw_data[i]);
52
  }
53
}

Auf einem AVR dürfte das allerdings ziemlich Rechenzeit kosten, 
hauptsächlich wegen den Bitshifts um variable Länge.

: Bearbeitet durch User
von ArduStemmi (Gast)


Lesenswert?

Danke für Deine Bemühungen! Du hast natürlich Recht! Für 800 kHz 
ungeeignet! Aber für Samplingrates kleiner 100 kHz könnte es gehen! Ich 
werde das mal testen!

von ... (Gast)


Lesenswert?

Fuer etwas aehnliches: 4 10 bit AD-gewandelte Werte in 5 Bytes
zu packen habe ich mir fuer die Midrange PICs (12F675/683, 16F684)
mal eine Assemblerroutine programmiert.
Die kann ich dann bequem vom Compiler aus aufrufen und
brauch das widerliche Geschiebe nicht in C zu formulieren.

Die Routine zum "Einpacken" und zum "Auspacken" ist lustigerweise
dieselbe. (Die Speicherbereiche auf denen gearbeitet wird,
natuerlich nicht.)

> Einer der wichtigsten Gründe, warum ich mich mit Mikrocontrollern
> beschäftige, ist das damit verbundene Lernen!

Dann mach mal.

Mehr als ein wenig Zeit und "Brain" braucht es nicht.

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Ich habe auch mal ne Beispielimplementierung gemacht, bin aber wohl 
schon zu spät...

von MSP430 (Gast)


Lesenswert?

ein anderes Beispiel
1
struct set_of_eight          // 5 bytes
2
{
3
    uint8_t first :5;
4
    uint8_t second :5;
5
    uint8_t third :5;
6
    uint8_t fourth :5;
7
    uint8_t fifth :5;
8
    uint8_t sixth :5;
9
    uint8_t seventh :5;
10
    uint8_t eigth :5;
11
}__attribute__((packed));

von Daniel A. (daniel-a)


Angehängte Dateien:

Lesenswert?

Nocheine Beispielimplementierung, diesmal mit 4 bit Blöcken am Anfang 
und das 5te am ende

von Sven B. (scummos)


Lesenswert?

Wie schon gesagt wurde, vergiss es, es wird einfach deutlich langsamer 
sein. Wenn du die 30% extra RAM nicht hast, nimm einen größeren 
Controller.

von Falk B. (falk)


Lesenswert?

Eben. Man sollte nicht den Schinken nach der Wurst werfen.

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

> Eben. Man sollte nicht den Schinken nach der Wurst werfen.

Das sagen die Hochsprachler immer.

Und genau deswegen merkt man von dem Leistungszuwachs der Hardware in 
den letzten Jahrzehnten auch so gut wie garnix. Alles wurde in immer 
tieferen Schichten von in Hochsprachen geschriebenen Abstraktionen 
versenkt...

Das klappte in den 80ern, 90ern und auch in den 00er Jahren noch 
ziemlich gut. Aber seitdem etwa ab 2008 das Moore'sche Gesetz doch ein 
wenig in's Hinken gekommen ist, merkt man doch sehr deutlich, dass 
dieser Weg letztlich in den Abgrund führen wird, ja führen muss.

Das Lustige ist, dass ausgerechnet eine Sprache, die das Prädikat 
"Hochsprache" eigentlich garnicht verdient, den Weg in den Abgrund 
geebnet hat. Und diese Sprache heißt "C". Und sie wird wohl auch bis zum 
bitteren Ende signifikant beteiligt sein...

von n^2 (Gast)


Lesenswert?

Es gibt verschiedene Arten es zu realisieren, und es spricht auch nichts 
dagegen mehrere parallel zu implementieren.
 z.B.
 -  Einfach die bits verschwenden, dafür maximale Samplerate
 -  den mode nur 4 Bits zu speichern, die dann in ein Byte zu packen, 
fehlt ein bit, aber doppelte länge, evt etwas langsammer
 - 8 Bytes auf einmal einlesen, einlesen b1 und b2, 4+4 bit zusammen 
packen wegschreiben, das 5 bit in ein anderes byte reinschieben, nach 8 
spamples auch das wegschreiben.
 - Einlesen, gucken ob der Wert identisch mit dem vorherigen, dann einen 
Counter zählen, bis 3 Bit, Die drei Bit werden dann in den freien Bits 
abgelegt.
 - wie vorher nur das die Wiederholung im Extrabyte gespeichert wird 
oder die 3 Bit noch mitbenutzt, damit kann man je nach Signal lange 
Aufzeichungsdauer erreichen.

 Später beim Benutzen sucht man sich den Mode aus, den man braucht hohe 
Samplingrate oder lange Aufzeichung. Beim überprüfen von seriellen 
Bussen reicht oft 2 oder 3 Bit.

 Wichtig ist nur das beim Einlesen jedes Sample den gleichen Abstand zum 
nächsten hat, da sonst das Timing nachher nicht stimmt. Bei Auswerten 
hat man Zeit und muss nicht so tricksen. Wenn man eine sehr effektive 
Einleseroutine haben will, kann man da schon einiges an Zeit 
reinstecken.

von Zeno (Gast)


Lesenswert?

@ArduStemmi

Das könnte man doch mit einer Hard-/Softwarekombination lösen.

Beim LA willst Du ja nur wissen ob am Messpunkt zum Zeitpunkt x L oder H 
Pegel anliegt. Du brauchst also einen Puffer in welchen der Pegel 
entsprechend Deiner gewünschten Samplingrate eingeschrieben wird. Für 
sowas verwendet man normalerweise ein Schieberegister. Die Daten werden 
hierbei bei jedem Sampleimpuls in das Schieberegister seriell 
eingeschrieben und stehen nach 8 Samples an den Ausgängen des Registers. 
Jetzt kann man das Register auslesen und damit ein 8 Bit breites 
Datenwort, welches man nun als Byte speichern könnte. Dann werden wieder 
8 Samples eingelesen und das Register ausgelesen. Das Auslesen des 
Registers muß natürlich zwischen 2 Samples passieren.
Du brauchst natürlich für jeden Kanal ein Register. Das Einschreiben in 
die Register wird mit dem gleichen Impuls für alle Register ausgelöst, 
also für alle Kanäle synchron.

So würde ich das realisieren. Nachteil es braucht natürlich zusätzliche 
Hardware. Vorteil die Anzahl der Kanäle kann sehr groß gewählt werden, 
allerdings geht das auf Kosten der Samplerate, da ja alle Register nach 
jedem 8. Sample zwischen 2 Samples gelesen werden müssen.

Ist nur mal so als Denkanstoß gedacht und erhebt keinen Anspruch auf 
Perfektion.


Zeno

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.