Librerie per il calcolo del CRC16 e CRC32

Sono stato una buona mezz'ora a studiare il disassemblato della funzione. A me pare che sui vettori con dimensione fissa non ci siano problemi perché nel codice il compilatore prima di chiamare la funzione carica l'indirizzo di fine del vettore e nella routine di calcolo del CRC semplicemente confronta il valore corrente del puntatore con la fine precedentemente memorizzata per cui quando arriva in fondo al vettore, lui esce.

leo72:

gpb01:
... ok ... quindi, per stare tranquilli al 100%, ho si inserisce a manina un ultimo valore 0x00 alla fine o si modifica la routine per accettare un parametro in più ... il numero di caratteri (così inoltre diventa congrua con le altre due) ... ]:smiley: ]:smiley: ]:smiley: ]:smiley:

Guglielmo

La precedente versione di quella routine funzionava proprio così, ossia passando 2 parametri: il vettore e la sua lunghezza.
Però giorni fa mi era venuta in mente questa cosa del byte/unsigned char, l'ho voluta provare e pare che funzioni alla perfezione :wink:

non può funzinare alla perfezione: se io ho un array di byte in cui chessò, ho inviato il gezzo dell'intero 126, avrò inviato 2 byte: 0x00 0x7E però con questo "trucco" il valore 0x7E NON verrà mai controllato. In pratica state dando per scontato che il valore 0x00 NON sia mai contenuto nell'array di byte, cosa che non è assolutamente vera.

Un trucco che si può usare è usare 2 0x00 di fila, e se la stringa di byte contiene due 0x00 di fila nel suo contenuto allora dividerli con un valore noto; in pratica come fa il C con i caratteri di escape. (ha un nome questo sistema, si usa per le comunicazioni, ma non ricordo)

A me pare che sui vettori con dimensione fissa

se ti riferisci a me, sappi che la funzione prende un PUNTATORE all'array di dimensione fissa. quindi un test veritiero deve tenerne conto.

In oltre, lato ricezione, spesso si usa un array a dimensione fissa bello grosso, e lo si riempie finchè ci sono dati; con il sistema dimensione dell'array automatico con sizeof, sei costretto a ridimensionare l'array (o usare un array mallocato sul momento), antrambi i sistemi giocano col l'allocazioe dinamica, che come disse anche astro più volte, è spesso pura follia su un micro come gli atmega.

Non mi riferivo a nessuno. :wink:
Comunque disassemblando il codice la prima analisi, cioè quella del carattere \0 finale, è errata. Come detto, il compilatore carica l'indirizzo della cella finale e con un semplice confronto controlla quando è arrivato ad essa.

Quindi l'array potrebbe contenere tutti 0x00 ma verrebbe elaborato ugualmente?

lesto:
...
non può funzinare alla perfezione: se io ho un array di byte in cui chessò, ho inviato il gezzo dell'intero 126, avrò inviato 2 byte: 0x00 0x7E però con questo "trucco" il valore 0x7E NON verrà mai controllato. In pratica state dando per scontato che il valore 0x00 NON sia mai contenuto nell'array di byte, cosa che non è assolutamente vera.
...

Concordo al 100% ...
... se si deve fare una routine la si deve fare indipendente dal blocco di bytes che deve elaborare e l'unico modo è ... puntatore al blocco, lunghezza del blocco ]:smiley: ]:smiley: ]:smiley:

Guglielmo

leo72:
Non mi riferivo a nessuno. :wink:
Comunque disassemblando il codice la prima analisi, cioè quella del carattere \0 finale, è errata. Come detto, il compilatore carica l'indirizzo della cella finale e con un semplice confronto controlla quando è arrivato ad essa.

Cioè tu vuoi dire che

while (*data) {

}

... viene compilato tramutandolo in un controllo sul fatto che si sia o meno all'interno del blocco puntato ??? E se il pointer che io passo è di una struttura ? O di un elemento di una union ? ... mi sembra azzardato ... :roll_eyes:

Guglielmo

uhmm forse possibile, però a me sembra più voler dire "il valore attualmente puntato da data" (e quindi 0x00 ci frega), se il compilatore fa qualcosa è davvero "sporco", imho

però

while (*data) {
        byte extract = *data++;

a me SEMBRA errato, come sarabbe tradotto?

byte extract = *(data++);

non legge il primo byte e va in overflow di 1

byte extract = (*data)++;

prende il valore puntato e gli somma 1 (e in teoria va in loop infinito)

questa è la versione originale, con la lunghezza:

//CRC-8 - algoritmo basato sulle formule di CRC-8 di Dallas/Maxim 
byte CRC8(const byte *data, byte len) {
    byte crc = 0x00;

    while (len--) {
        byte extract = *data++;
        for (byte tempI = 8; tempI; tempI--) {
            byte sum = (crc ^ extract) & 0x01;
            crc >>= 1;
            if (sum) {
                crc ^= 0x8C;
            }
            extract >>= 1;
        }
    }
    return crc;
}

Se provate con diversi vettori, anche che contengono 0 nei loro elementi, avrete gli stessi risultati fra questa e quella che non vuole la lunghezza dell'array. Poi se trovate un errore a me fa piacere:

  1. imparo e la prox volta non o commetto
  2. sistemiamo insieme questa funzione
    Entrambe sono nello spirito dell'open source, non prendo queste critiche male, anzi :wink:

Bene. La funzione è errata. :wink:
Ho provato ora inserendo un elemento "0" e la somma è diversa. Mi sa che ho fatto prove del cavolo :stuck_out_tongue_closed_eyes:

La funzione senza lunghezza al primo 0 mi sa che si ferma. Ho da imparare ancora molto sull'assembly Atmel e sul C in generale :sweat_smile: :sweat_smile:

Ok. Prendo la pala e vado a fare una buchetta in qualche aiola... ci vediamo tra un mesetto o due.. :*

... era difatti come supponevo ... nella while il compilatore mette il valore puntato in quel momento dal pointer (*data) e quindi ... se il valore è 0x00 lo considera FALSE e esce dalla while :wink:

Comunque è stato un bell'esercizio XD

Guglielmo

allego il crc16 portato in java, mi è servito per comunicare con alcune schedine :smiley:

public class CRC {

	private static final int table[]={
   		 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
   		 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
   		 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
   		 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
   		 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
   		 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
   		 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
   		 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
   		 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
   		 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
   		 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
   		 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
   		 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
   		 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
   		 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
   		 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
   		 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
   		 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
   		 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
   		 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
   		 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
   		 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
   		 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
   		 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
   		 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
   		 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
   		 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
   		 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
   		 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
   		 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
   		 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
   		 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
   		};
	
	public static int crc16(byte[] bytes) { 

		int crc = 0x0000;
		for (byte b : bytes) {
			crc = ((crc >>> 8) & 0xFF) ^ table[(crc ^ b) & 0xff];
			crc = crc & 0xFFFF;//because int in java is 32bit, but we want only 16bit
		}
		//
		return crc;
	}
	
	public static void main(String args[]){
		int crc = CRC.crc16("waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".getBytes());
		System.out.println("CRC16 = " + Integer.toHexString(crc));
	}

}

Emmm ... sarò stanco e non riesco a vederlo .... ma perché diavolo questo codice da errore ???

  1. crc8.h :
#ifdef __cplusplus
 extern "C" {
#endif

byte calc_crc8(byte *, int);

#ifdef __cplusplus
 }
#endif
  1. crc8.c :
byte calc_crc8 (byte *data, int len)
{
  byte crc = 0;
  while (len--) {
    byte extract = *data++;
    for (byte tempI = 8; tempI; tempI--) {
      byte sum = (crc ^extract) & 0x01;
      crc >>= 1;
      if (sum) {
        crc ^= 0x8C;
      }
      extract >>= 1;
    }  
  }
  return (crc);
}
  1. il programmino di prova :
#include "crc8.h"

byte myCrc8;

void setup() {

   delay(3000);
   
   Serial.begin(9600);
   
   myCrc8  = calc_crc8("123456789", 9);
   
      Serial.println("Calculate CRC on string 123456789");
      Serial.print("CRC8 = 0x");
      Serial.println(myCrc8, HEX);
}

void loop() {
}

L'errore che ricevo :

crc8.c:1: error: expected '=', ',', ';', 'asm' or 'attribute' before 'calc_crc8'

GRAZIE :slight_smile:

Guglielmo

a naso direi da

byte *

a

byte*

e forse manca il

#include "crc8.h"

nel .c (o mi confondo con i cpp?)

  1. byte* o byte * sono indifferenti, come lo è char* o char * (... anche io ero abituato a scrivere con l'asterisco attaccato, ma ho visto che spesso invece usano la seconda forma)

  2. non c'è neanche in crc16.c e crc32.c , eppure ... non danno problemi ...


Inserito da Leo :

Il simbolo di puntatore, l'asterisco, è un operatore prefisso, ossia influenza ciò che viene dopo e non ciò che viene prima.
Cioè quando si scrive char*(spazio)mario in realtà il compilatore vede char(spazio)*mario.

il .h lo hai lnel .ino, ma NON nel .c

.... si, esattamente come per il crc16.c ed il crc32.c che non danno alcun problema.

Non solo, se ce lo metti ... salta fuori lo stesso identico errore anche nel .h ... :astonished: :astonished: :astonished:

Sembra che NON gli piaccia il tipo "byte" come tipo della funzione calc_crc8 ...

Ti dico, deve essere una banalità, ma non la vedo ...

Guglielmo

gpb01:

  1. byte* o byte * sono indifferenti, come lo è char* o char * (... anche io ero abituato a scrivere con l'asterisco attaccato, ma ho visto che spesso invece usano la seconda forma)

Il simbolo di puntatore, l'asterisco, è un operatore prefisso, ossia influenza ciò che viene dopo e non ciò che viene prima.
Cioè quando si scrive char*(spazio)mario in realtà il compilatore vede char(spazio)*mario.

SCUSAMI GUGLIELMO.... ho premuto per sbaglio su Modify invece che su Quote ed ho cambiato il tuo post
Oggi non è giornata... :astonished: :astonished:

Se l'errore è sul tipo byte, devi includere il file Arduino.h perché byte è un tipo definito dall'Arduino.
Puoi sostituirlo con uint8_t, che è l'equivalente. Guarda se funziona.

OK, e questo è risolto ... mancava :

#include <Arduino.h>

... purtroppo con uint8_t, senza l'include, da lo stesso problema :wink:

Altro errore, il compilatore NON digerisce la forma

for (byte tempI = 8; tempI; tempI--)

... che in effetti neanche io avevo mai usato ... cosa si intende ? tempI > 0 ???

Guglielmo

più precisamente

tempI != 0

edit: credo che in generale gli errori di prima li hai avuiti perchè hai forzato l'uso del C