M0AGX / LB9MG

Amateur radio and embedded systems

Using XMEGA hardware CRC generator for CRC-16 CCITT

CRCs are useful for checking if data received from outside or read from memory is not corrupted. This is especially important in embedded systems, as it could take just a single bit-flip to drastically change the configuration of the system. I needed to protect configuration structure of my new project when it is being saved and read from EEPROM. To make sure that the data I read from EEPROM is exactly what I have written I decided to use CRC-16 CCITT across the whole structure.

The MCU I use is an Xmega A4U series. I could have used software-only routines from util/crc16.h of avr-libc, but in the end I decided to play with the hardware CRC generator of Xmega. Using dedicated hardware is faster and more power-efficient than doing it software which makes it especially useful if lots of data is being exchanged (eg. over UARTs). For storing configuration data (that changes rarely) the benefits are much smaller.

I started with code from Atmel Software Framework. I did not want to include this gigantic library in my small project, so I had to refactor the code. I found out, that it is not exactly CRC-CCITT compliant. The initial value of the checksum was wrong. It was set to zero, while it should have been 0x1D0F. This is a very good resource of test vectors for CRC-CCITT.

My code works only across data stored in RAM (the hardware CRC generator has to be configured differently to process data from flash memory). Usage is very simple. To calculate CRC of a struct in RAM: crc16( (uint8_t*)&your_struct_in_ram, sizeof(your_struct_in_ram) );

When testing it using known vectors written as strings, you have to remember, that C adds a null character at the end of every string, so the length supplied to the crc16 function has to be smaller by one.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "crc.h"
#include <avr/io.h>

uint16_t crc16(uint8_t *data, uint16_t length){

    // Reset module
    CRC_CTRL |= CRC_RESET_RESET0_gc;

    // Set initial checksum value
    CRC.CHECKSUM0 = 0x0F;
    CRC.CHECKSUM1 = 0x1D;
    CRC.CHECKSUM2 = 0xFF;
    CRC.CHECKSUM3 = 0xFF;

    CRC_CTRL &= ~CRC_SOURCE_gm;
    CRC_CTRL |= CRC_SOURCE_IO_gc; //source is IO

    // Write data to DATAIN register
    while (length--) {
        CRC_DATAIN = *data;
        data++;
    }

    // Signal CRC complete
    CRC_STATUS |= CRC_BUSY_bm;

    while (CRC_STATUS & CRC_BUSY_bm) {} //busy wait until the module is  ready

    uint16_t checksum = 0;
    checksum = ((uint16_t)CRC_CHECKSUM0 & 0x00FF);
    checksum |= (((uint16_t)CRC_CHECKSUM1 << 8) & 0xFF00);

    CRC_CTRL &= ~CRC_SOURCE_gm; //disable CRC module

    return checksum;
}

I release the code into public domain.