From 0ebe9c4fd7dbca583d87da2bfb26e54465fc9e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Babi=C4=8D?= Date: Thu, 14 May 2020 22:16:15 +0200 Subject: [PATCH] add arduino sources --- arduino-sources/Bakalarka.cpp | 546 +++++++ arduino-sources/Bakalarka.h | 88 ++ arduino-sources/DS1302/DS1302.cpp | 202 +++ arduino-sources/MFRC522/MFRC522.cpp | 1388 +++++++++++++++++ arduino-sources/MFRC522/MFRC522.h | 329 ++++ arduino-sources/PCD8544/PCD8544.cpp | 334 ++++ arduino-sources/PCD8544/PCD8544.h | 78 + arduino-sources/Release/DS1302/subdir.mk | 24 + arduino-sources/Release/MFRC522/subdir.mk | 24 + arduino-sources/Release/PCD8544/subdir.mk | 24 + arduino-sources/Release/SD/subdir.mk | 27 + arduino-sources/Release/SD/utility/subdir.mk | 30 + arduino-sources/Release/SDFAT16/subdir.mk | 27 + arduino-sources/Release/SPI/subdir.mk | 24 + .../Release/SerialCommand/subdir.mk | 24 + arduino-sources/Release/subdir.mk | 27 + arduino-sources/SDFAT16/Fat16Config.h | 38 + arduino-sources/SDFAT16/Fat16mainpage.h | 208 +++ arduino-sources/SDFAT16/FatStructs.h | 418 +++++ arduino-sources/SDFAT16/SdCard.cpp | 277 ++++ arduino-sources/SDFAT16/SdCard.h | 192 +++ arduino-sources/SDFAT16/SdInfo.h | 117 ++ arduino-sources/SPI/SPI.cpp | 66 + arduino-sources/SPI/SPI.h | 70 + arduino-sources/SerialCommand/SerialCommand.h | 94 ++ arduino-sources/prescaler.h | 118 ++ arduino-sources/rotary.cpp | 148 ++ arduino-sources/rotary.h | 37 + 28 files changed, 4979 insertions(+) create mode 100644 arduino-sources/Bakalarka.cpp create mode 100644 arduino-sources/Bakalarka.h create mode 100644 arduino-sources/DS1302/DS1302.cpp create mode 100644 arduino-sources/MFRC522/MFRC522.cpp create mode 100644 arduino-sources/MFRC522/MFRC522.h create mode 100644 arduino-sources/PCD8544/PCD8544.cpp create mode 100644 arduino-sources/PCD8544/PCD8544.h create mode 100644 arduino-sources/Release/DS1302/subdir.mk create mode 100644 arduino-sources/Release/MFRC522/subdir.mk create mode 100644 arduino-sources/Release/PCD8544/subdir.mk create mode 100644 arduino-sources/Release/SD/subdir.mk create mode 100644 arduino-sources/Release/SD/utility/subdir.mk create mode 100644 arduino-sources/Release/SDFAT16/subdir.mk create mode 100644 arduino-sources/Release/SPI/subdir.mk create mode 100644 arduino-sources/Release/SerialCommand/subdir.mk create mode 100644 arduino-sources/Release/subdir.mk create mode 100644 arduino-sources/SDFAT16/Fat16Config.h create mode 100644 arduino-sources/SDFAT16/Fat16mainpage.h create mode 100644 arduino-sources/SDFAT16/FatStructs.h create mode 100644 arduino-sources/SDFAT16/SdCard.cpp create mode 100644 arduino-sources/SDFAT16/SdCard.h create mode 100644 arduino-sources/SDFAT16/SdInfo.h create mode 100644 arduino-sources/SPI/SPI.cpp create mode 100644 arduino-sources/SPI/SPI.h create mode 100644 arduino-sources/SerialCommand/SerialCommand.h create mode 100644 arduino-sources/prescaler.h create mode 100644 arduino-sources/rotary.cpp create mode 100644 arduino-sources/rotary.h diff --git a/arduino-sources/Bakalarka.cpp b/arduino-sources/Bakalarka.cpp new file mode 100644 index 0000000..433e0b7 --- /dev/null +++ b/arduino-sources/Bakalarka.cpp @@ -0,0 +1,546 @@ +#include "Bakalarka.h" + +#include "prescaler.h" +#include +#include +#include +//#include + + +#include "rotary.h" +Rotary encoder = Rotary(ENC_A, ENC_B); + +#include "DS1302/DS1302.h" +DS1302 rtc(DS_RST, DS_IO, DS_SCK); + +#include "SPI/SPI.h" +#include "MFRC522/MFRC522.h" +MFRC522 mfrc522(RFID_SS, RFID_RST); + +#include "PCD8544/PCD8544.h" +PCD8544 lcd(LCD_DC, LCD_RST, LCD_SS); + +#include +#include +// store error strings in flash to save RAM +#define error(s) error_P(PSTR(s)) +SdCard card; +Fat16 file; + +#include "SerialCommand/SerialCommand.h" +SerialCommand SCmd; + + + +volatile uint32_t lastDebounce; +volatile int8_t currentMenuItem = 0; +volatile boolean button = false, cursor = false, reading = false; +uint16_t lastRefresh; +static uint8_t storageIndex EEMEM; + + + +Menu storage = {"Storage", list, 0}; +Menu add_storage = {"New file", addStorage, 0}; +Menu date_time = {"Date/time", frontPage, 0}; +Menu exit_menu = {"Exit", frontPage, 0}; +//Menu s1 = {"s1", list, 0}; +//Menu s2 = {"s2", list, 0}; +//Menu s3 = {"s3", list, 0}; +//Menu s4 = {"s4", list, 0}; + +Menu mainMenu = { "", NULL, 4, { &storage, &add_storage, &date_time, &exit_menu} }; + + +// bitmaps +const byte icon_usb[20] PROGMEM = { + 0x08, 0x1C, 0x3E, 0x3E, 0x1C, 0x08, 0x0C, 0x0E, 0x0A, 0x1A, + 0x3B, 0x6B, 0x4B, 0xE8, 0xE8, 0xE8, 0x08, 0x3E, 0x1C, 0x08 +}; + +const byte icon_battery_100[21] PROGMEM = { + 0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81, + 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xFF, 0x3C, 0x3C +}; + +const byte icon_battery_75[21] PROGMEM = { + 0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81, + 0xBD, 0xBD, 0xBD, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C +}; + +const byte icon_battery_50[21] PROGMEM = { + 0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0xBD, 0xBD, 0xBD, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C +}; + +const byte icon_battery_25[21] PROGMEM = { + 0xFF, 0x81, 0xBD, 0xBD, 0xBD, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, 0x3C, 0x3C +}; + +const byte icon_arrow[6] PROGMEM = { + 0x00, 0xFE, 0x7C, 0x38, 0x10, 0x00 +}; + +void setup(void) { + // There is 16HMz crystal used, halve it to reliable 8MHz@3V3 + setClockPrescaler(CLOCK_PRESCALER_2); + + // The fastest reliable baud rate is 38400 + Serial.begin(38400); + + lcd.begin(); + lcd.clear(); + + // Rotary interrupt pin change enable + PCICR |= (1 << PCIE1); + PCMSK1 |= (1 << PCINT8) | (1 << PCINT9); + + // RFID host interrupt request + attachInterrupt(0, RFIDInterrupt, FALLING); + // Encoder button interrupt handle + attachInterrupt(1, debounceInterrupt, FALLING); + // Encoder button pull-up + pinMode(BTN, INPUT_PULLUP); + + + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + + if (!card.init(0, SD_SS)) + error("card.init failed!"); + + if (!Fat16::init(&card)) + error("Fat16::init failed!"); + + pinMode(SPKR, OUTPUT); + pinMode(VBAT_ADC_EN, OUTPUT); + +// wdt_enable(WDTO_8S); + + frontPage(); + + SCmd.addCommand("ls", ls); + SCmd.addCommand("touch", touch); + SCmd.addCommand("rm", rm); +// SCmd.addCommand("HELLO", SayHello); // Echos the string argument back +// SCmd.addCommand("P", process_command); // Converts two arguments to integers and echos them back + SCmd.addDefaultHandler(unrecognized); // Handler for command that isn't matched (says "What?") + + sei(); + +} + + + +void loop(void) { +// wdt_reset(); +// tone(SPKR, 1000, 100); +// topBar(); + SCmd.readSerial(); + + if (millis() - lastRefresh > 1000) { + frontPage(); + lastRefresh = millis(); + } + + + if (button) { + button = false; + navigateMenu(&mainMenu); + return; +// uint8_t menuLenght = sizeof(menuItems) / sizeof(menuItems[0]); +// if (currentMenuItem >= menuLenght) +// currentMenuItem = 0; +// else if (currentMenuItem < 0) +// currentMenuItem = menuLenght - 1; +// lcd.setCursor(0, 0); +// uint8_t i = 0, offset = 0; +// if (currentMenuItem >= PCD8544_LINES) +// offset = currentMenuItem - PCD8544_LINES + 1; +// for (i = offset; i < menuLenght && i < PCD8544_LINES + offset; i++) { +// if (i == (uint8_t)currentMenuItem) +// lcd.bitmap(icon_arrow, 1, 6); +// else +// lcd.print(' '); +// lcd.print(menuItems[i]); +// lcd.clearRestOfLine(); +// } + + } + + +// delay(1000); +// lcd.clear(); + +// topBar(); + + + + + + + +// delay(1000); + +// if (Serial.available()) { +// Fat16::ls(LS_DATE | LS_SIZE); +// while(Serial.available()) +// Serial.read(); +// } + + if (reading && mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { +// // Look for new cards +// if ( ! mfrc522.PICC_IsNewCardPresent()) +// return; +// +// +// // Select one of the cards +// if ( ! mfrc522.PICC_ReadCardSerial()) +// return; + + tone(SPKR, 5500, 70); + + delay(1500); + reading = false; + + if (file.open(eeprom_read_byte(&storageIndex), O_APPEND | O_WRITE)) { + Time t = rtc.time(); + char buf[21]; + snprintf(buf, sizeof buf, "%04d-%02d-%02d %02d:%02d:%02d;", t.yr, t.mon, t.day, t.hr, t.min, t.sec); + file.print(buf); + for (byte i = 0; i < mfrc522.uid.size; i++) { + file.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); + file.print(mfrc522.uid.uidByte[i], HEX); + } + file.println(";"); + file.close(); + } + +// lcd.setCursor(0, 1); +// for (byte i = 0; i < mfrc522.uid.size; i++) { +// lcd.print(mfrc522.uid.uidByte[i] < 0x10 ? "0" : ""); +// lcd.print(mfrc522.uid.uidByte[i], HEX); +// } +// lcd.clearRestOfLine(); +//// Dump debug info about the card. PICC_HaltA() is automatically called. +// mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); + } + + delay(100); + +} + +ISR(PCINT1_vect) { + cursor = encoder.process(); + if (cursor == DIR_CW) + currentMenuItem++; + else if (cursor == DIR_CCW) + currentMenuItem--; + +} + +void debounceInterrupt(void) { + if ((long) (micros() - lastDebounce) >= (long)DEBOUNCE * 1000) { + button = true; + lastDebounce = micros(); + } +} + +void RFIDInterrupt(void) { + if (!reading) + reading = true; +} + +void error_P(const char* str) { + lcd.setCursor(1, 4); + lcd.print(PSTR("ERROR")); + PgmPrint("error: "); + SerialPrintln_P(str); + if (card.errorCode) { + PgmPrint("SD error: "); + Serial.println(card.errorCode, HEX); + } + while (1) + ; +} + + + + +void navigateMenu(Menu *menu) { + if (menu->numSubmenus == 0) { + /* Execute the command */ + lcd.clear(); + menu->command(); + return; + } + /* This is a menu, not a command, so show the menu and get the user's choice */ + displayMenu(menu); + if (button) { + button = false; + menu = menu->submenu[currentMenuItem]; + } +// menu = get_user_selection(menu); + navigateMenu(menu); +} + +void displayMenu(Menu *menu) { +// uint8_t menuLenght = sizeof(menuItems) / sizeof(menuItems[0]); + if (currentMenuItem >= menu->numSubmenus) + currentMenuItem = 0; + else if (currentMenuItem < 0) + currentMenuItem = menu->numSubmenus - 1; + lcd.setCursor(0, 0); + uint8_t i = 0, offset = 0; + if (currentMenuItem >= PCD8544_LINES) + offset = currentMenuItem - PCD8544_LINES + 1; + for (i = offset; i < PCD8544_LINES + offset; i++) { + // clear rest of the menu screen if there are less items than LCD lines + if (i >= menu->numSubmenus) { + lcd.print(' '); + lcd.clearRestOfLine(); + continue; + } + + if (i == (uint8_t)currentMenuItem) + lcd.bitmap(icon_arrow, 1, 6); + else + lcd.print(' '); + lcd.print(menu->submenu[i]->title); + lcd.clearRestOfLine(); + } +} + + + +void list(void) { + cursor = DIR_CW; + while (!button) { + if (cursor) { + lcd.setCursor(0, 0); + dir_t d; + uint16_t i = 0; + boolean found = false; + uint8_t offset = 0; + if (currentMenuItem >= PCD8544_LINES) + offset = currentMenuItem - PCD8544_LINES + 1; + for (i = offset; Fat16::readDir(&d, &i, DIR_ATT_VOLUME_ID); i++) { + // if we are over the display, just seek the last file index + if (i >= (uint16_t)(PCD8544_LINES + offset)) + continue; + if (i == (uint16_t) currentMenuItem) { + lcd.bitmap(icon_arrow, 1, 6); + found = true; + } + else + lcd.print(' '); + + lcd.print(i); + lcd.print(':'); + printDirName(d); + } + + Serial.print("a: "); + if (!found) { + Serial.print("NOTfound, "); + if (cursor == DIR_CW) { + currentMenuItem++; + Serial.print("++"); + } else if (cursor == DIR_CCW) { + currentMenuItem--; + Serial.print("--"); + } + } + else + // this means that the screen will be refreshed + cursor = false; + + + if (currentMenuItem < 0) { + currentMenuItem = i - 1; + cursor = DIR_CCW; + Serial.print("i-1"); + } + if ((uint16_t) currentMenuItem > i - 1) { + currentMenuItem = 0; + Serial.print("0"); + cursor = DIR_CW; + } + + Serial.print("; cmi: "); + Serial.print(currentMenuItem); + Serial.print(", li: "); + Serial.print(i); + Serial.print(", cursor: "); + Serial.print(cursor); + Serial.print(", button: "); + Serial.println(button); + + } + } + + Serial.println("Saved"); + + // save index of a file to store into to the EEPROM memory + eeprom_write_byte(&storageIndex, currentMenuItem); + button = false; + lcd.clear(); + frontPage(); +} + + +void addStorage(void) { +// file.writeError = false; + char buf[14]; + for (unsigned short int i = 1; ; i++) { + snprintf(buf, sizeof buf, "file%d.csv", i); + if (file.open(buf, O_CREAT | O_WRITE | O_EXCL)) + break; + } + file.close(); + frontPage(); +// snprintf(buf, sizeof buf, "FILE%d.CSV", 1); + +} + + +void frontPage(void) { + +// lcd.clear(); + lcd.setCursor(0, 0); +// tone(SPKR, 5000, 50); + // USB plugged in + if (analogRead(STAT) < 512) { + lcd.bitmap(icon_usb, 1, 20); + } + // battery + else { + digitalWrite(VBAT_ADC_EN, LOW); + uint8_t vbat = (analogRead(VBAT_ADC) - 523) / 5; + if (vbat < 25) + lcd.bitmap(icon_battery_25, 1, 21); + else if (vbat < 50) + lcd.bitmap(icon_battery_50, 1, 21); + else if (vbat < 75) + lcd.bitmap(icon_battery_75, 1, 21); + else + lcd.bitmap(icon_battery_100, 1, 21); + digitalWrite(VBAT_ADC_EN, HIGH); + + } + + // Get the current time and date from the chip. + Time t = rtc.time(); + + char buf[24]; + lcd.setCursor(9, 0); + snprintf(buf, sizeof(buf), "%02d:%02d", t.hr, t.min); + lcd.print(buf); + + uint16_t i = eeprom_read_byte(&storageIndex); + dir_t d; + if (Fat16::readDir(&d, &i, DIR_ATT_VOLUME_ID)) { + lcd.setCursor(0, 2); + lcd.println("Saving to: "); + printDirName(d); + } + +} + +void printDirName(const dir_t& dir) { + uint8_t w = 0; + for (uint8_t i = 0; i < 11; i++) { + if (dir.name[i] == ' ') + continue; + if (i == 8) { + lcd.print('.'); + w++; + } + lcd.print((char) dir.name[i]); + w++; + } + lcd.clearRestOfLine(); +} +// +//void SayHello() { +// char *arg; +// arg = SCmd.next(); // Get the next argument from the SerialCommand object buffer +// if (arg != NULL) // As long as it existed, take it +// { +// Serial.print("Hello "); +// Serial.println(arg); +// } else { +// Serial.println("Hello, whoever you are"); +// } +//} +// +//void process_command() { +// int aNumber; +// char *arg; +// +// Serial.println("We're in process_command"); +// arg = SCmd.next(); +// if (arg != NULL) { +// aNumber = atoi(arg); // Converts a char string to an integer +// Serial.print("First argument was: "); +// Serial.println(aNumber); +// } else { +// Serial.println("No arguments"); +// } +// +// arg = SCmd.next(); +// if (arg != NULL) { +// aNumber = atol(arg); +// Serial.print("Second argument was: "); +// Serial.println(aNumber); +// } else { +// Serial.println("No second argument"); +// } +// +//} + + + +void ls() { + Fat16::ls(); +} + +void touch() { + char *arg, fileName[14]; + arg = SCmd.next(); + snprintf(fileName, sizeof fileName, "%s.csv", arg); + PgmPrint("File '"); + Serial.print(fileName); + if (!file.open(fileName, O_CREAT | O_WRITE)) + PgmPrintln("' could not be created."); + else + PgmPrintln("' was created successfully"); + +} + +void rm() { + char *arg, fileName[14]; + arg = SCmd.next(); + snprintf(fileName, sizeof fileName, "%s.csv", arg); + PgmPrint("File '"); + Serial.print(fileName); + if (file.open(fileName, O_WRITE) && file.remove()) + PgmPrintln("' was removed successfully"); + else + PgmPrintln("' could not be removed."); + +} + +void mv() { + char *arg; +} + + +// This gets set as the default handler, and gets called when no other command matches. +void unrecognized() { + PgmPrintln("Unrecognized command. Possibilities are: "); + PgmPrintln("ls\t\t\t- display files"); + PgmPrintln("touch FILE\t\t- create file FILE.csv"); + Serial.println(); +} diff --git a/arduino-sources/Bakalarka.h b/arduino-sources/Bakalarka.h new file mode 100644 index 0000000..d4b7590 --- /dev/null +++ b/arduino-sources/Bakalarka.h @@ -0,0 +1,88 @@ +// Only modify this file to include +// - function definitions (prototypes) +// - include files +// - extern variable definitions +// In the appropriate section + +#ifndef Bakalarka_H_ +#define Bakalarka_H_ +#include "Arduino.h" +//add your includes for the project Bakalarka here +#include "PCD8544/PCD8544.h" +#include + +//end of add your includes here +#ifdef __cplusplus +extern "C" { +#endif +void loop(); +void setup(); +#ifdef __cplusplus +} // extern "C" +#endif + +//add your function definitions for the project Bakalarka here + + +// pins on ATmega328 +#define RFID_IRQ 2 +#define BTN 3 +#define RFID_RST 4 +#define DS_SCK 5 +#define DS_IO 6 +#define DS_RST 7 +#define LCD_SS 8 +#define SD_SS 9 +#define RFID_SS 10 +#define ENC_A A0 +#define ENC_B A1 +#define LCD_DC A2 +#define LCD_RST A3 +#define SPKR A4 +#define VBAT_ADC_EN A5 +#define VBAT_ADC A6 +#define STAT A7 + +// config +#define DEBOUNCE 200 + +// function macros +//#define error(s) error_P(PSTR(s)) // store error strings in flash to save RAM + + + +#define MAX_SUBMENUS 4 + +typedef struct menu { + const char title[PCD8544_COLS]; + void (*command)(); + uint8_t numSubmenus; + struct menu *submenu[MAX_SUBMENUS]; +} Menu; + +// prototypes +void debounceInterrupt(void); +void RFIDInterrupt(void); + +void error_P(const char* str); + +void frontPage(void); +void list(void); +void navigateMenu(Menu *menu); +void displayMenu(Menu *menu); +void addStorage(void); +void printDirName(const dir_t& dir); + +//void SayHello(void); +//void process_command(void); +void ls(void); +void touch(void); +void rm(void); +void unrecognized(void); + + +//Do not add code below this line +#endif /* Bakalarka_H_ */ + + + diff --git a/arduino-sources/DS1302/DS1302.cpp b/arduino-sources/DS1302/DS1302.cpp new file mode 100644 index 0000000..15926b4 --- /dev/null +++ b/arduino-sources/DS1302/DS1302.cpp @@ -0,0 +1,202 @@ + +#include "Arduino.h" + +#include "DS1302.h" + +Time::Time(const uint16_t yr, const uint8_t mon, const uint8_t date, + const uint8_t hr, const uint8_t min, const uint8_t sec, + const Day day) { + this->yr = yr; + this->mon = mon; + this->date = date; + this->hr = hr; + this->min = min; + this->sec = sec; + this->day = day; +} + + +DS1302::DS1302(const uint8_t ce_pin, const uint8_t io_pin, + const uint8_t sclk_pin) { + ce_pin_ = ce_pin; + io_pin_ = io_pin; + sclk_pin_ = sclk_pin; + + pinMode(ce_pin, OUTPUT); + pinMode(sclk_pin, OUTPUT); +} + +void DS1302::writeOut(const uint8_t value) { + pinMode(io_pin_, OUTPUT); + shiftOut(io_pin_, sclk_pin_, LSBFIRST, value); +} + +uint8_t DS1302::readIn() { + uint8_t input_value = 0; + uint8_t bit = 0; + pinMode(io_pin_, INPUT); + + for (int i = 0; i < 8; ++i) { + bit = digitalRead(io_pin_); + input_value |= (bit << i); + + digitalWrite(sclk_pin_, HIGH); + delayMicroseconds(1); + digitalWrite(sclk_pin_, LOW); + } + + return input_value; +} + +uint8_t DS1302::registerBcdToDec(const Register reg, const uint8_t high_bit) { + const uint8_t mask = (1 << (high_bit + 1)) - 1; + uint8_t val = readRegister(reg); + val &= mask; + val = (val & 15) + 10 * ((val & (15 << 4)) >> 4); + return val; +} + +uint8_t DS1302::registerBcdToDec(const Register reg) { + return registerBcdToDec(reg, 7); +} + +void DS1302::registerDecToBcd(const Register reg, uint8_t value, + const uint8_t high_bit) { + const uint8_t mask = (1 << (high_bit + 1)) - 1; + uint8_t regv = readRegister(reg); + + // Convert value to bcd in place. + uint8_t tvalue = value / 10; + value = value % 10; + value |= (tvalue << 4); + + // Replace high bits of value if needed. + value &= mask; + value |= (regv &= ~mask); + + writeRegister(reg, value); +} + +void DS1302::registerDecToBcd(const Register reg, const uint8_t value) { + registerDecToBcd(reg, value, 7); +} + +uint8_t DS1302::readRegister(const Register reg) { + uint8_t cmd_byte = 129; // 1000 0001 + uint8_t reg_value; + cmd_byte |= (reg << 1); + + digitalWrite(sclk_pin_, LOW); + digitalWrite(ce_pin_, HIGH); + + writeOut(cmd_byte); + reg_value = readIn(); + + digitalWrite(ce_pin_, LOW); + + return reg_value; +} + +void DS1302::writeRegister(const Register reg, const uint8_t value) { + uint8_t cmd_byte = (128 | (reg << 1)); + + digitalWrite(sclk_pin_, LOW); + digitalWrite(ce_pin_, HIGH); + + writeOut(cmd_byte); + writeOut(value); + + digitalWrite(ce_pin_, LOW); +} + +void DS1302::writeProtect(const bool enable) { + writeRegister(kWriteProtectReg, (enable << 7)); +} + +void DS1302::halt(const bool enable) { + uint8_t sec = readRegister(kSecondReg); + sec &= ~(1 << 7); + sec |= (enable << 7); + writeRegister(kSecondReg, sec); +} + +uint8_t DS1302::seconds() { + return registerBcdToDec(kSecondReg, 6); +} + +uint8_t DS1302::minutes() { + return registerBcdToDec(kMinuteReg); +} + +uint8_t DS1302::hour() { + uint8_t hr = readRegister(kHourReg); + uint8_t adj; + if (hr & 128) // 12-hour mode + adj = 12 * ((hr & 32) >> 5); + else // 24-hour mode + adj = 10 * ((hr & (32 + 16)) >> 4); + hr = (hr & 15) + adj; + return hr; +} + +uint8_t DS1302::date() { + return registerBcdToDec(kDateReg, 5); +} + +uint8_t DS1302::month() { + return registerBcdToDec(kMonthReg, 4); +} + +Time::Day DS1302::day() { + return static_cast(registerBcdToDec(kDayReg, 2)); +} + +uint16_t DS1302::year() { + return 2000 + registerBcdToDec(kYearReg); +} + +Time DS1302::time() { + return Time(year(), month(), date(), + hour(), minutes(), seconds(), + day()); +} + +void DS1302::seconds(const uint8_t sec) { + registerDecToBcd(kSecondReg, sec, 6); +} + +void DS1302::minutes(const uint8_t min) { + registerDecToBcd(kMinuteReg, min, 6); +} + +void DS1302::hour(const uint8_t hr) { + writeRegister(kHourReg, 0); // set 24-hour mode + registerDecToBcd(kHourReg, hr, 5); +} + +void DS1302::date(const uint8_t date) { + registerDecToBcd(kDateReg, date, 5); +} + +void DS1302::month(const uint8_t mon) { + registerDecToBcd(kMonthReg, mon, 4); +} + +void DS1302::day(const Time::Day day) { + registerDecToBcd(kDayReg, static_cast(day), 2); +} + +void DS1302::year(uint16_t yr) { + yr -= 2000; + registerDecToBcd(kYearReg, yr); +} + +void DS1302::time(const Time t) { + seconds(t.sec); + minutes(t.min); + hour(t.hr); + date(t.date); + month(t.mon); + day(t.day); + year(t.yr); +} diff --git a/arduino-sources/MFRC522/MFRC522.cpp b/arduino-sources/MFRC522/MFRC522.cpp new file mode 100644 index 0000000..183bef7 --- /dev/null +++ b/arduino-sources/MFRC522/MFRC522.cpp @@ -0,0 +1,1388 @@ +/* +* MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. +* _Please_ see the comments in MFRC522.h - they give useful hints and background. +* Released into the public domain. +*/ + +#include +#include "MFRC522.h" + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for setting up the Arduino +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + // Set the chipSelectPin as digital output, do not select the slave yet + _chipSelectPin = chipSelectPin; + pinMode(_chipSelectPin, OUTPUT); + digitalWrite(_chipSelectPin, HIGH); + + // Set the resetPowerDownPin as digital output, do not reset or power down. + _resetPowerDownPin = resetPowerDownPin; + pinMode(_resetPowerDownPin, OUTPUT); + digitalWrite(_resetPowerDownPin, LOW); + + // Set SPI bus to work with MFRC522 chip. + setSPIConfig(); +} // End constructor + +/** + * Set SPI bus to work with MFRC522 chip. + * Please call this function if you have changed the SPI config since the MFRC522 constructor was run. + */ +void MFRC522::setSPIConfig() { + SPI.setBitOrder(MSBFIRST); + SPI.setDataMode(SPI_MODE0); +} // End setSPIConfig() + +///////////////////////////////////////////////////////////////////////////////////// +// Basic interface functions for communicating with the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Writes a byte to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte value ///< The value to write. + ) { + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(reg & 0x7E); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + SPI.transfer(value); + digitalWrite(_chipSelectPin, HIGH); // Release slave again +} // End PCD_WriteRegister() + +/** + * Writes a number of bytes to the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_WriteRegister( byte reg, ///< The register to write to. One of the PCD_Register enums. + byte count, ///< The number of bytes to write to the register + byte *values ///< The values to write. Byte array. + ) { + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(reg & 0x7E); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + for (byte index = 0; index < count; index++) { + SPI.transfer(values[index]); + } + digitalWrite(_chipSelectPin, HIGH); // Release slave again +} // End PCD_WriteRegister() + +/** + * Reads a byte from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +byte MFRC522::PCD_ReadRegister( byte reg ///< The register to read from. One of the PCD_Register enums. + ) { + byte value; + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(0x80 | (reg & 0x7E)); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. + digitalWrite(_chipSelectPin, HIGH); // Release slave again + return value; +} // End PCD_ReadRegister() + +/** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_ReadRegister( byte reg, ///< The register to read from. One of the PCD_Register enums. + byte count, ///< The number of bytes to read + byte *values, ///< Byte array to store the values in. + byte rxAlign ///< Only bit positions rxAlign..7 in values[0] are updated. + ) { + if (count == 0) { + return; + } + //Serial.print("Reading "); Serial.print(count); Serial.println(" bytes from register."); + byte address = 0x80 | (reg & 0x7E); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + byte index = 0; // Index in values array. + digitalWrite(_chipSelectPin, LOW); // Select slave + count--; // One read is performed outside of the loop + SPI.transfer(address); // Tell MFRC522 which address we want to read + while (index < count) { + if (index == 0 && rxAlign) { // Only update bit positions rxAlign..7 in values[0] + // Create bit mask for bit positions rxAlign..7 + byte mask = 0; + for (byte i = rxAlign; i <= 7; i++) { + mask |= (1 << i); + } + // Read value and tell that we want to read the same address again. + byte value = SPI.transfer(address); + // Apply mask to both current value of values[0] and the new data in value. + values[0] = (values[index] & ~mask) | (value & mask); + } + else { // Normal case + values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. + } + index++; + } + values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. + digitalWrite(_chipSelectPin, HIGH); // Release slave again +} // End PCD_ReadRegister() + +/** + * Sets the bits given in mask in register reg. + */ +void MFRC522::PCD_SetRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to set. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp | mask); // set bit mask +} // End PCD_SetRegisterBitMask() + +/** + * Clears the bits given in mask from register reg. + */ +void MFRC522::PCD_ClearRegisterBitMask( byte reg, ///< The register to update. One of the PCD_Register enums. + byte mask ///< The bits to clear. + ) { + byte tmp; + tmp = PCD_ReadRegister(reg); + PCD_WriteRegister(reg, tmp & (~mask)); // clear bit mask +} // End PCD_ClearRegisterBitMask() + + +/** + * Use the CRC coprocessor in the MFRC522 to calculate a CRC_A. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CalculateCRC( byte *data, ///< In: Pointer to the data to transfer to the FIFO for CRC calculation. + byte length, ///< In: The number of bytes to transfer. + byte *result ///< Out: Pointer to result buffer. Result is written to result[0..1], low byte first. + ) { + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, length, data); // Write data to the FIFO + PCD_WriteRegister(CommandReg, PCD_CalcCRC); // Start the calculation + + // Wait for the CRC calculation to complete. Each iteration of the while-loop takes 17.73�s. + word i = 5000; + byte n; + while (1) { + n = PCD_ReadRegister(DivIrqReg); // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + if (n & 0x04) { // CRCIRq bit set - calculation done + break; + } + if (--i == 0) { // The emergency break. We will eventually terminate on this one after 89ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // Transfer the result from the registers to the result buffer + result[0] = PCD_ReadRegister(CRCResultRegL); + result[1] = PCD_ReadRegister(CRCResultRegH); + return STATUS_OK; +} // End PCD_CalculateCRC() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for manipulating the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init() { + if (digitalRead(_resetPowerDownPin) == LOW) { //The MFRC522 chip is in power down mode. + digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + delay(50); + } + else { // Perform a soft reset + PCD_Reset(); + } + + // When communicating with a PICC we need a timeout if something goes wrong. + // f_timer = 13.56 MHz / (2*TPreScaler+1) where TPreScaler = [TPrescaler_Hi:TPrescaler_Lo]. + // TPrescaler_Hi are the four low bits in TModeReg. TPrescaler_Lo is TPrescalerReg. + PCD_WriteRegister(TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds + PCD_WriteRegister(TPrescalerReg, 0xA9); // TPreScaler = TModeReg[3..0]:TPrescalerReg, ie 0x0A9 = 169 => f_timer=40kHz, ie a timer period of 25�s. + PCD_WriteRegister(TReloadRegH, 0x03); // Reload timer with 0x3E8 = 1000, ie 25ms before timeout. + PCD_WriteRegister(TReloadRegL, 0xE8); + + PCD_WriteRegister(TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + PCD_WriteRegister(ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4) + PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset) +} // End PCD_Init() + +/** + * Performs a soft reset on the MFRC522 chip and waits for it to be ready again. + */ +void MFRC522::PCD_Reset() { + PCD_WriteRegister(CommandReg, PCD_SoftReset); // Issue the SoftReset command. + // The datasheet does not mention how long the SoftRest command takes to complete. + // But the MFRC522 might have been in soft power-down mode (triggered by bit 4 of CommandReg) + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74�s. Let us be generous: 50ms. + delay(50); + // Wait for the PowerDown bit in CommandReg to be cleared + while (PCD_ReadRegister(CommandReg) & (1<<4)) { + // PCD still restarting - unlikely after waiting 50ms, but better safe than sorry. + } +} // End PCD_Reset() + +/** + * Turns the antenna on by enabling pins TX1 and TX2. + * After a reset these pins disabled. + */ +void MFRC522::PCD_AntennaOn() { + byte value = PCD_ReadRegister(TxControlReg); + if ((value & 0x03) != 0x03) { + PCD_WriteRegister(TxControlReg, value | 0x03); + } +} // End PCD_AntennaOn() + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the Transceive command. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_TransceiveData( byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. Default NULL. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte waitIRq = 0x30; // RxIRq and IdleIRq + return PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, sendData, sendLen, backData, backLen, validBits, rxAlign, checkCRC); +} // End PCD_TransceiveData() + +/** + * Transfers data to the MFRC522 FIFO, executes a commend, waits for completion and transfers data back from the FIFO. + * CRC validation can only be done if backData and backLen are specified. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_CommunicateWithPICC( byte command, ///< The command to execute. One of the PCD_Command enums. + byte waitIRq, ///< The bits in the ComIrqReg register that signals successful completion of the command. + byte *sendData, ///< Pointer to the data to transfer to the FIFO. + byte sendLen, ///< Number of bytes to transfer to the FIFO. + byte *backData, ///< NULL or pointer to buffer if data should be read back after executing the command. + byte *backLen, ///< In: Max number of bytes to write to *backData. Out: The number of bytes returned. + byte *validBits, ///< In/Out: The number of valid bits in the last byte. 0 for 8 valid bits. + byte rxAlign, ///< In: Defines the bit position in backData[0] for the first bit received. Default 0. + bool checkCRC ///< In: True => The last two bytes of the response is assumed to be a CRC_A that must be validated. + ) { + byte n, _validBits; + unsigned int i; + + // Prepare values for BitFramingReg + byte txLastBits = validBits ? *validBits : 0; + byte bitFraming = (rxAlign << 4) + txLastBits; // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop any active command. + PCD_WriteRegister(ComIrqReg, 0x7F); // Clear all seven interrupt request bits + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // FlushBuffer = 1, FIFO initialization + PCD_WriteRegister(FIFODataReg, sendLen, sendData); // Write sendData to the FIFO + PCD_WriteRegister(BitFramingReg, bitFraming); // Bit adjustments + PCD_WriteRegister(CommandReg, command); // Execute the command + if (command == PCD_Transceive) { + PCD_SetRegisterBitMask(BitFramingReg, 0x80); // StartSend=1, transmission of data starts + } + + // Wait for the command to complete. + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer automatically starts when the PCD stops transmitting. + // Each iteration of the do-while-loop takes 17.86�s. + i = 2000; + while (1) { + n = PCD_ReadRegister(ComIrqReg); // ComIrqReg[7..0] bits are: Set1 TxIRq RxIRq IdleIRq HiAlertIRq LoAlertIRq ErrIRq TimerIRq + if (n & waitIRq) { // One of the interrupts that signal success has been set. + break; + } + if (n & 0x01) { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + if (--i == 0) { // The emergency break. If all other condions fail we will eventually terminate on this one after 35.7ms. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; + } + } + + // Stop now if any errors except collisions were detected. + byte errorRegValue = PCD_ReadRegister(ErrorReg); // ErrorReg[7..0] bits are: WrErr TempErr reserved BufferOvfl CollErr CRCErr ParityErr ProtocolErr + if (errorRegValue & 0x13) { // BufferOvfl ParityErr ProtocolErr + return STATUS_ERROR; + } + + // If the caller wants data back, get it from the MFRC522. + if (backData && backLen) { + n = PCD_ReadRegister(FIFOLevelReg); // Number of bytes in the FIFO + if (n > *backLen) { + return STATUS_NO_ROOM; + } + *backLen = n; // Number of bytes returned + PCD_ReadRegister(FIFODataReg, n, backData, rxAlign); // Get received data from FIFO + _validBits = PCD_ReadRegister(ControlReg) & 0x07; // RxLastBits[2:0] indicates the number of valid bits in the last received byte. If this value is 000b, the whole byte is valid. + if (validBits) { + *validBits = _validBits; + } + } + + // Tell about collisions + if (errorRegValue & 0x08) { // CollErr + return STATUS_COLLISION; + } + + // Perform CRC_A validation if requested. + if (backData && backLen && checkCRC) { + // In this case a MIFARE Classic NAK is not OK. + if (*backLen == 1 && _validBits == 4) { + return STATUS_MIFARE_NACK; + } + // We need at least the CRC_A value and all 8 bits of the last byte must be received. + if (*backLen < 2 || _validBits != 0) { + return STATUS_CRC_WRONG; + } + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + byte controlBuffer[2]; + n = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (n != STATUS_OK) { + return n; + } + if ((backData[*backLen - 2] != controlBuffer[0]) || (backData[*backLen - 1] != controlBuffer[1])) { + return STATUS_CRC_WRONG; + } + } + + return STATUS_OK; +} // End PCD_CommunicateWithPICC() + +/** + * Transmits a REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_RequestA(byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_REQA, bufferATQA, bufferSize); +} // End PICC_RequestA() + +/** + * Transmits a Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_WakeupA( byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + return PICC_REQA_or_WUPA(PICC_CMD_WUPA, bufferATQA, bufferSize); +} // End PICC_WakeupA() + +/** + * Transmits REQA or WUPA commands. + * Beware: When two PICCs are in the field at the same time I often get STATUS_TIMEOUT - probably due do bad antenna design. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_REQA_or_WUPA( byte command, ///< The command to send - PICC_CMD_REQA or PICC_CMD_WUPA + byte *bufferATQA, ///< The buffer to store the ATQA (Answer to request) in + byte *bufferSize ///< Buffer size, at least two bytes. Also number of bytes returned if STATUS_OK. + ) { + byte validBits; + byte status; + + if (bufferATQA == NULL || *bufferSize < 2) { // The ATQA response is 2 bytes long. + return STATUS_NO_ROOM; + } + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + validBits = 7; // For REQA and WUPA we need the short frame format - transmit only 7 bits of the last (and only) byte. TxLastBits = BitFramingReg[2..0] + status = PCD_TransceiveData(&command, 1, bufferATQA, bufferSize, &validBits); + if (status != STATUS_OK) { + return status; + } + if (*bufferSize != 2 || validBits != 0) { // ATQA must be exactly 16 bits. + return STATUS_ERROR; + } + return STATUS_OK; +} // End PICC_REQA_or_WUPA() + +/** + * Transmits SELECT/ANTICOLLISION commands to select a single PICC. + * Before calling this function the PICCs must be placed in the READY(*) state by calling PICC_RequestA() or PICC_WakeupA(). + * On success: + * - The chosen PICC is in state ACTIVE(*) and all other PICCs have returned to state IDLE/HALT. (Figure 7 of the ISO/IEC 14443-3 draft.) + * - The UID size and value of the chosen PICC is returned in *uid along with the SAK. + * + * A PICC UID consists of 4, 7 or 10 bytes. + * Only 4 bytes can be specified in a SELECT command, so for the longer UIDs two or three iterations are used: + * UID size Number of UID bytes Cascade levels Example of PICC + * ======== =================== ============== =============== + * single 4 1 MIFARE Classic + * double 7 2 MIFARE Ultralight + * triple 10 3 Not currently in use? + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_Select( Uid *uid, ///< Pointer to Uid struct. Normally output, but can also be used to supply a known UID. + byte validBits ///< The number of known UID bits supplied in *uid. Normally 0. If set you must also supply uid->size. + ) { + bool uidComplete; + bool selectDone; + bool useCascadeTag; + byte cascadeLevel = 1; + byte result; + byte count; + byte index; + byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + char currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + byte buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 byte standard frame + 2 bytes CRC_A + byte bufferUsed; // The number of bytes used in the buffer, ie the number of bytes to transfer to the FIFO. + byte rxAlign; // Used in BitFramingReg. Defines the bit position for the first bit received. + byte txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted byte. + byte *responseBuffer; + byte responseLength; + + // Description of buffer structure: + // Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3 + // Byte 1: NVB Number of Valid Bits (in complete command, not just the UID): High nibble: complete bytes, Low nibble: Extra bits. + // Byte 2: UID-data or CT See explanation below. CT means Cascade Tag. + // Byte 3: UID-data + // Byte 4: UID-data + // Byte 5: UID-data + // Byte 6: BCC Block Check Character - XOR of bytes 2-5 + // Byte 7: CRC_A + // Byte 8: CRC_A + // The BCC and CRC_A is only transmitted if we know all the UID bits of the current Cascade Level. + // + // Description of bytes 2-5: (Section 6.5.4 of the ISO/IEC 14443-3 draft: UID contents and cascade levels) + // UID size Cascade level Byte2 Byte3 Byte4 Byte5 + // ======== ============= ===== ===== ===== ===== + // 4 bytes 1 uid0 uid1 uid2 uid3 + // 7 bytes 1 CT uid0 uid1 uid2 + // 2 uid3 uid4 uid5 uid6 + // 10 bytes 1 CT uid0 uid1 uid2 + // 2 CT uid3 uid4 uid5 + // 3 uid6 uid7 uid8 uid9 + + // Sanity checks + if (validBits > 80) { + return STATUS_INVALID; + } + + // Prepare MFRC522 + PCD_ClearRegisterBitMask(CollReg, 0x80); // ValuesAfterColl=1 => Bits received after collision are cleared. + + // Repeat Cascade Level loop until we have a complete UID. + uidComplete = false; + while ( ! uidComplete) { + // Set the Cascade Level in the SEL byte, find out if we need to use the Cascade Tag in byte 2. + switch (cascadeLevel) { + case 1: + buffer[0] = PICC_CMD_SEL_CL1; + uidIndex = 0; + useCascadeTag = validBits && uid->size > 4; // When we know that the UID has more than 4 bytes + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL2; + uidIndex = 3; + useCascadeTag = validBits && uid->size > 7; // When we know that the UID has more than 7 bytes + break; + + case 3: + buffer[0] = PICC_CMD_SEL_CL3; + uidIndex = 6; + useCascadeTag = false; // Never used in CL3. + break; + + default: + return STATUS_INTERNAL_ERROR; + break; + } + + // How many UID bits are known in this Cascade Level? + currentLevelKnownBits = validBits - (8 * uidIndex); + if (currentLevelKnownBits < 0) { + currentLevelKnownBits = 0; + } + // Copy the known bits from uid->uidByte[] to buffer[] + index = 2; // destination index in buffer[] + if (useCascadeTag) { + buffer[index++] = PICC_CMD_CT; + } + byte bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); // The number of bytes needed to represent the known bits for this level. + if (bytesToCopy) { + byte maxBytes = useCascadeTag ? 3 : 4; // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag + if (bytesToCopy > maxBytes) { + bytesToCopy = maxBytes; + } + for (count = 0; count < bytesToCopy; count++) { + buffer[index++] = uid->uidByte[uidIndex + count]; + } + } + // Now that the data has been copied we need to include the 8 bits in CT in currentLevelKnownBits + if (useCascadeTag) { + currentLevelKnownBits += 8; + } + + // Repeat anti collision loop until we can transmit all UID bits + BCC and receive a SAK - max 32 iterations. + selectDone = false; + while ( ! selectDone) { + // Find out how many bits and bytes to send and receive. + if (currentLevelKnownBits >= 32) { // All UID bits in this Cascade Level are known. This is a SELECT. + //Serial.print("SELECT: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes + // Calulate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 7, &buffer[7]); + if (result != STATUS_OK) { + return result; + } + txLastBits = 0; // 0 => All 8 bits are valid. + bufferUsed = 9; + // Store response in the last 3 bytes of buffer (BCC and CRC_A - not needed after tx) + responseBuffer = &buffer[6]; + responseLength = 3; + } + else { // This is an ANTICOLLISION. + //Serial.print("ANTICOLLISION: currentLevelKnownBits="); Serial.println(currentLevelKnownBits, DEC); + txLastBits = currentLevelKnownBits % 8; + count = currentLevelKnownBits / 8; // Number of whole bytes in the UID part. + index = 2 + count; // Number of whole bytes: SEL + NVB + UIDs + buffer[1] = (index << 4) + txLastBits; // NVB - Number of Valid Bits + bufferUsed = index + (txLastBits ? 1 : 0); + // Store response in the unused part of buffer + responseBuffer = &buffer[index]; + responseLength = sizeof(buffer) - index; + } + + // Set bit adjustments + rxAlign = txLastBits; // Having a seperate variable is overkill. But it makes the next line easier to read. + PCD_WriteRegister(BitFramingReg, (rxAlign << 4) + txLastBits); // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + + // Transmit the buffer and receive the response. + result = PCD_TransceiveData(buffer, bufferUsed, responseBuffer, &responseLength, &txLastBits, rxAlign); + if (result == STATUS_COLLISION) { // More than one PICC in the field => collision. + result = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (result & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + byte collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. + if (collisionPos == 0) { + collisionPos = 32; + } + if (collisionPos <= currentLevelKnownBits) { // No progress - should not happen + return STATUS_INTERNAL_ERROR; + } + // Choose the PICC with the bit set. + currentLevelKnownBits = collisionPos; + count = (currentLevelKnownBits - 1) % 8; // The bit to modify + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << count); + } + else if (result != STATUS_OK) { + return result; + } + else { // STATUS_OK + if (currentLevelKnownBits >= 32) { // This was a SELECT. + selectDone = true; // No more anticollision + // We continue below outside the while. + } + else { // This was an ANTICOLLISION. + // We now have all 32 bits of the UID in this Cascade Level + currentLevelKnownBits = 32; + // Run loop again to do the SELECT. + } + } + } // End of while ( ! selectDone) + + // We do not check the CBB - it was constructed by us above. + + // Copy the found UID bytes from buffer[] to uid->uidByte[] + index = (buffer[2] == PICC_CMD_CT) ? 3 : 2; // source index in buffer[] + bytesToCopy = (buffer[2] == PICC_CMD_CT) ? 3 : 4; + for (count = 0; count < bytesToCopy; count++) { + uid->uidByte[uidIndex + count] = buffer[index++]; + } + + // Check response SAK (Select Acknowledge) + if (responseLength != 3 || txLastBits != 0) { // SAK must be exactly 24 bits (1 byte + CRC_A). + return STATUS_ERROR; + } + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. + result = PCD_CalculateCRC(responseBuffer, 1, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) { + return STATUS_CRC_WRONG; + } + if (responseBuffer[0] & 0x04) { // Cascade bit set - UID not complete yes + cascadeLevel++; + } + else { + uidComplete = true; + uid->sak = responseBuffer[0]; + } + } // End of while ( ! uidComplete) + + // Set correct uid->size + uid->size = 3 * cascadeLevel + 1; + + return STATUS_OK; +} // End PICC_Select() + +/** + * Instructs a PICC in state ACTIVE(*) to go to state HALT. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PICC_HaltA() { + byte result; + byte buffer[4]; + + // Build command buffer + buffer[0] = PICC_CMD_HLTA; + buffer[1] = 0; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Send the command. + // The standard says: + // If the PICC responds with any modulation during a period of 1 ms after the end of the frame containing the + // HLTA command, this response shall be interpreted as 'not acknowledge'. + // We interpret that this way: Only STATUS_TIMEOUT is an success. + result = PCD_TransceiveData(buffer, sizeof(buffer), NULL, 0); + if (result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result == STATUS_OK) { // That is ironically NOT ok in this case ;-) + return STATUS_ERROR; + } + return result; +} // End PICC_HaltA() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with MIFARE PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Executes the MFRC522 MFAuthent command. + * This command manages MIFARE authentication to enable a secure communication to any MIFARE Mini, MIFARE 1K and MIFARE 4K card. + * The authentication is described in the MFRC522 datasheet section 10.3.1.9 and http://www.nxp.com/documents/data_sheet/MF1S503x.pdf section 10.1. + * For use with MIFARE Classic PICCs. + * The PICC must be selected - ie in state ACTIVE(*) - before calling this function. + * Remember to call PCD_StopCrypto1() after communicating with the authenticated PICC - otherwise no new communications can start. + * + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * + * @return STATUS_OK on success, STATUS_??? otherwise. Probably STATUS_TIMEOUT if you supply the wrong key. + */ +byte MFRC522::PCD_Authenticate(byte command, ///< PICC_CMD_MF_AUTH_KEY_A or PICC_CMD_MF_AUTH_KEY_B + byte blockAddr, ///< The block number. See numbering in the comments in the .h file. + MIFARE_Key *key, ///< Pointer to the Crypteo1 key to use (6 bytes) + Uid *uid ///< Pointer to Uid struct. The first 4 bytes of the UID is used. + ) { + byte waitIRq = 0x10; // IdleIRq + + // Build command buffer + byte sendData[12]; + sendData[0] = command; + sendData[1] = blockAddr; + for (byte i = 0; i < MF_KEY_SIZE; i++) { // 6 key bytes + sendData[2+i] = key->keyByte[i]; + } + for (byte i = 0; i < 4; i++) { // The first 4 bytes of the UID + sendData[8+i] = uid->uidByte[i]; + } + + // Start the authentication. + return PCD_CommunicateWithPICC(PCD_MFAuthent, waitIRq, &sendData[0], sizeof(sendData)); +} // End PCD_Authenticate() + +/** + * Used to exit the PCD from its authenticated state. + * Remember to call this function after communicating with an authenticated PICC - otherwise no new communications can start. + */ +void MFRC522::PCD_StopCrypto1() { + // Clear MFCrypto1On bit + PCD_ClearRegisterBitMask(Status2Reg, 0x08); // Status2Reg[7..0] bits are: TempSensClear I2CForceHS reserved reserved MFCrypto1On ModemState[2:0] +} // End PCD_StopCrypto1() + +/** + * Reads 16 bytes (+ 2 bytes CRC_A) from the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight only addresses 00h to 0Fh are decoded. + * The MF0ICU1 returns a NAK for higher addresses. + * The MF0ICU1 responds to the READ command by sending 16 bytes starting from the page address defined by the command argument. + * For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned. + * A roll-back is implemented: If blockAddr is 0Eh, then the contents of pages 0Eh, 0Fh, 00h and 01h are returned. + * + * The buffer must be at least 18 bytes because a CRC_A is also returned. + * Checks the CRC_A before returning STATUS_OK. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Read( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The first page to return data from. + byte *buffer, ///< The buffer to store the data in + byte *bufferSize ///< Buffer size, at least 18 bytes. Also number of bytes returned if STATUS_OK. + ) { + byte result; + + // Sanity check + if (buffer == NULL || *bufferSize < 18) { + return STATUS_NO_ROOM; + } + + // Build command buffer + buffer[0] = PICC_CMD_MF_READ; + buffer[1] = blockAddr; + // Calculate CRC_A + result = PCD_CalculateCRC(buffer, 2, &buffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + return PCD_TransceiveData(buffer, 4, buffer, bufferSize, NULL, 0, true); +} // End MIFARE_Read() + +/** + * Writes 16 bytes to the active PICC. + * + * For MIFARE Classic the sector containing the block must be authenticated before calling this function. + * + * For MIFARE Ultralight the opretaion is called "COMPATIBILITY WRITE". + * Even though 16 bytes are transferred to the Ultralight PICC, only the least significant 4 bytes (bytes 0 to 3) + * are written to the specified address. It is recommended to set the remaining bytes 04h to 0Fh to all logic 0. + * * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Write( byte blockAddr, ///< MIFARE Classic: The block (0-0xff) number. MIFARE Ultralight: The page (2-15) to write to. + byte *buffer, ///< The 16 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 16 bytes. Exactly 16 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 16) { + return STATUS_INVALID; + } + + // Mifare Classic protocol requires two communications to perform a write. + // Step 1: Tell the PICC we want to write to block blockAddr. + byte cmdBuffer[2]; + cmdBuffer[0] = PICC_CMD_MF_WRITE; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive(cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive( buffer, bufferSize); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_Write() + +/** + * Writes a 4 byte page to the active MIFARE Ultralight PICC. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Ultralight_Write( byte page, ///< The page (2-15) to write to. + byte *buffer, ///< The 4 bytes to write to the PICC + byte bufferSize ///< Buffer size, must be at least 4 bytes. Exactly 4 bytes are written. + ) { + byte result; + + // Sanity check + if (buffer == NULL || bufferSize < 4) { + return STATUS_INVALID; + } + + // Build commmand buffer + byte cmdBuffer[6]; + cmdBuffer[0] = PICC_CMD_UL_WRITE; + cmdBuffer[1] = page; + memcpy(&cmdBuffer[2], buffer, 4); + + // Perform the write + result = PCD_MIFARE_Transceive(cmdBuffer, 6); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Ultralight_Write() + +/** + * MIFARE Decrement subtracts the delta from the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is subtracted from the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_DECREMENT, blockAddr, delta); +} // End MIFARE_Decrement() + +/** + * MIFARE Increment adds the delta to the value of the addressed block, and stores the result in a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. + long delta ///< This number is added to the value of block blockAddr. + ) { + return MIFARE_TwoStepHelper(PICC_CMD_MF_INCREMENT, blockAddr, delta); +} // End MIFARE_Increment() + +/** + * MIFARE Restore copies the value of the addressed block into a volatile memory. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * Use MIFARE_Transfer() to store the result in a block. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Restore( byte blockAddr ///< The block (0-0xff) number. + ) { + // The datasheet describes Restore as a two step operation, but does not explain what data to transfer in step 2. + // Doing only a single step does not work, so I chose to transfer 0L in step two. + return MIFARE_TwoStepHelper(PICC_CMD_MF_RESTORE, blockAddr, 0L); +} // End MIFARE_Restore() + +/** + * Helper function for the two-step MIFARE Classic protocol operations Decrement, Increment and Restore. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use + byte blockAddr, ///< The block (0-0xff) number. + long data ///< The data to transfer in step 2 + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Step 1: Tell the PICC the command and block address + cmdBuffer[0] = command; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + + // Step 2: Transfer the data + result = PCD_MIFARE_Transceive( (byte *)&data, 4, true); // Adds CRC_A and accept timeout as success. + if (result != STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End MIFARE_TwoStepHelper() + +/** + * MIFARE Transfer writes the value stored in the volatile memory into one MIFARE Classic block. + * For MIFARE Classic only. The sector containing the block must be authenticated before calling this function. + * Only for blocks in "value block" mode, ie with access bits [C1 C2 C3] = [110] or [001]. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. + ) { + byte result; + byte cmdBuffer[2]; // We only need room for 2 bytes. + + // Tell the PICC we want to transfer the result into block blockAddr. + cmdBuffer[0] = PICC_CMD_MF_TRANSFER; + cmdBuffer[1] = blockAddr; + result = PCD_MIFARE_Transceive( cmdBuffer, 2); // Adds CRC_A and checks that the response is MF_ACK. + if (result != STATUS_OK) { + return result; + } + return STATUS_OK; +} // End MIFARE_Transfer() + + +///////////////////////////////////////////////////////////////////////////////////// +// Support functions +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Wrapper for MIFARE protocol communication. + * Adds CRC_A, executes the Transceive command and checks that the response is MF_ACK or a timeout. + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::PCD_MIFARE_Transceive( byte *sendData, ///< Pointer to the data to transfer to the FIFO. Do NOT include the CRC_A. + byte sendLen, ///< Number of bytes in sendData. + bool acceptTimeout ///< True => A timeout is also success + ) { + byte result; + byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + // Sanity check + if (sendData == NULL || sendLen > 16) { + return STATUS_INVALID; + } + + // Copy sendData[] to cmdBuffer[] and add CRC_A + memcpy(cmdBuffer, sendData, sendLen); + result = PCD_CalculateCRC(cmdBuffer, sendLen, &cmdBuffer[sendLen]); + if (result != STATUS_OK) { + return result; + } + sendLen += 2; + + // Transceive the data, store the reply in cmdBuffer[] + byte waitIRq = 0x30; // RxIRq and IdleIRq + byte cmdBufferSize = sizeof(cmdBuffer); + byte validBits = 0; + result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, sendLen, cmdBuffer, &cmdBufferSize, &validBits); + if (acceptTimeout && result == STATUS_TIMEOUT) { + return STATUS_OK; + } + if (result != STATUS_OK) { + return result; + } + // The PICC must reply with a 4 bit ACK + if (cmdBufferSize != 1 || validBits != 4) { + return STATUS_ERROR; + } + if (cmdBuffer[0] != MF_ACK) { + return STATUS_MIFARE_NACK; + } + return STATUS_OK; +} // End PCD_MIFARE_Transceive() + +/** + * Returns a string pointer to a status code name. + * + */ +const char *MFRC522::GetStatusCodeName(byte code ///< One of the StatusCode enums. + ) { + switch (code) { + case STATUS_OK: return "Success."; break; + case STATUS_ERROR: return "Error in communication."; break; + case STATUS_COLLISION: return "Collission detected."; break; + case STATUS_TIMEOUT: return "Timeout in communication."; break; + case STATUS_NO_ROOM: return "A buffer is not big enough."; break; + case STATUS_INTERNAL_ERROR: return "Internal error in the code. Should not happen."; break; + case STATUS_INVALID: return "Invalid argument."; break; + case STATUS_CRC_WRONG: return "The CRC_A does not match."; break; + case STATUS_MIFARE_NACK: return "A MIFARE PICC responded with NAK."; break; + default: + return "Unknown error"; + break; + } +} // End GetStatusCodeName() + +/** + * Translates the SAK (Select Acknowledge) to a PICC type. + * + * @return PICC_Type + */ +byte MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). + ) { + if (sak & 0x04) { // UID not complete + return PICC_TYPE_NOT_COMPLETE; + } + + switch (sak) { + case 0x09: return PICC_TYPE_MIFARE_MINI; break; + case 0x08: return PICC_TYPE_MIFARE_1K; break; + case 0x18: return PICC_TYPE_MIFARE_4K; break; + case 0x00: return PICC_TYPE_MIFARE_UL; break; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; break; + case 0x01: return PICC_TYPE_TNP3XXX; break; + default: break; + } + + if (sak & 0x20) { + return PICC_TYPE_ISO_14443_4; + } + + if (sak & 0x40) { + return PICC_TYPE_ISO_18092; + } + + return PICC_TYPE_UNKNOWN; +} // End PICC_GetType() + +/** + * Returns a string pointer to the PICC type name. + * + */ +const char *MFRC522::PICC_GetTypeName(byte piccType ///< One of the PICC_Type enums. + ) { + switch (piccType) { + case PICC_TYPE_ISO_14443_4: return "PICC compliant with ISO/IEC 14443-4"; break; + case PICC_TYPE_ISO_18092: return "PICC compliant with ISO/IEC 18092 (NFC)"; break; + case PICC_TYPE_MIFARE_MINI: return "MIFARE Mini, 320 bytes"; break; + case PICC_TYPE_MIFARE_1K: return "MIFARE 1KB"; break; + case PICC_TYPE_MIFARE_4K: return "MIFARE 4KB"; break; + case PICC_TYPE_MIFARE_UL: return "MIFARE Ultralight or Ultralight C"; break; + case PICC_TYPE_MIFARE_PLUS: return "MIFARE Plus"; break; + case PICC_TYPE_TNP3XXX: return "MIFARE TNP3XXX"; break; + case PICC_TYPE_NOT_COMPLETE: return "SAK indicates UID is not complete."; break; + case PICC_TYPE_UNKNOWN: + default: return "Unknown type"; break; + } +} // End PICC_GetTypeName() + + + + +/** + * Dumps debug info about the selected PICC to Serial. + * On success the PICC is halted after dumping the data. + * For MIFARE Classic the factory default key of 0xFFFFFFFFFFFF is tried. + */ +void MFRC522::PICC_DumpToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). + ) { + MIFARE_Key key; + + // UID + Serial.print("Card UID:"); + for (byte i = 0; i < uid->size; i++) { + Serial.print(uid->uidByte[i] < 0x10 ? " 0" : " "); + Serial.print(uid->uidByte[i], HEX); + } + Serial.println(); + + // PICC type + byte piccType = PICC_GetType(uid->sak); + Serial.print("PICC type: "); + Serial.println(PICC_GetTypeName(piccType)); + + // Dump contents + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + case PICC_TYPE_MIFARE_1K: + case PICC_TYPE_MIFARE_4K: + // All keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + PICC_DumpMifareClassicToSerial(uid, piccType, &key); + break; + + case PICC_TYPE_MIFARE_UL: + PICC_DumpMifareUltralightToSerial(); + break; + + case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println("Dumping memory contents not implemented for that PICC type."); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} // End PICC_DumpToSerial() + +/** + * Dumps memory contents of a MIFARE Classic PICC. + * On success the PICC is halted after dumping the data. + */ +void MFRC522::PICC_DumpMifareClassicToSerial( Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + byte piccType, ///< One of the PICC_Type enums. + MIFARE_Key *key ///< Key A used for all sectors. + ) { + byte no_of_sectors = 0; + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + no_of_sectors = 5; + break; + + case PICC_TYPE_MIFARE_1K: + // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + no_of_sectors = 16; + break; + + case PICC_TYPE_MIFARE_4K: + // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + no_of_sectors = 40; + break; + + default: // Should not happen. Ignore. + break; + } + + // Dump sectors, highest address first. + if (no_of_sectors) { + Serial.println("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits"); + for (char i = no_of_sectors - 1; i >= 0; i--) { + PICC_DumpMifareClassicSectorToSerial(uid, key, i); + } + } + PICC_HaltA(); // Halt the PICC before stopping the encrypted session. + PCD_StopCrypto1(); +} // End PICC_DumpMifareClassicToSerial() + +/** + * Dumps memory contents of a sector of a MIFARE Classic PICC. + * Uses PCD_Authenticate(), MIFARE_Read() and PCD_StopCrypto1. + * Always uses PICC_CMD_MF_AUTH_KEY_A because only Key A can always read the sector trailer access bits. + */ +void MFRC522::PICC_DumpMifareClassicSectorToSerial(Uid *uid, ///< Pointer to Uid struct returned from a successful PICC_Select(). + MIFARE_Key *key, ///< Key A for the sector. + byte sector ///< The sector to dump, 0..39. + ) { + byte status; + byte firstBlock; // Address of lowest address to dump actually last block dumped) + byte no_of_blocks; // Number of blocks in sector + bool isSectorTrailer; // Set to true while handling the "last" (ie highest address) in the sector. + + // The access bits are stored in a peculiar fashion. + // There are four groups: + // g[3] Access bits for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + // g[2] Access bits for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + // g[1] Access bits for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + // g[0] Access bits for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + // Each group has access bits [C1 C2 C3]. In this code C1 is MSB and C3 is LSB. + // The four CX bits are stored together in a nible cx and an inverted nible cx_. + byte c1, c2, c3; // Nibbles + byte c1_, c2_, c3_; // Inverted nibbles + bool invertedError; // True if one of the inverted nibbles did not match + byte g[4]; // Access bits for each of the four groups. + byte group; // 0-3 - active group for access bits + bool firstInGroup; // True for the first block dumped in the group + + // Determine position and size of sector. + if (sector < 32) { // Sectors 0..31 has 4 blocks each + no_of_blocks = 4; + firstBlock = sector * no_of_blocks; + } + else if (sector < 40) { // Sectors 32-39 has 16 blocks each + no_of_blocks = 16; + firstBlock = 128 + (sector - 32) * no_of_blocks; + } + else { // Illegal input, no MIFARE Classic PICC has more than 40 sectors. + return; + } + + // Dump blocks, highest address first. + byte byteCount; + byte buffer[18]; + byte blockAddr; + isSectorTrailer = true; + for (char blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { + blockAddr = firstBlock + blockOffset; + // Sector number - only on first line + if (isSectorTrailer) { + Serial.print(sector < 10 ? " " : " "); // Pad with spaces + Serial.print(sector); + Serial.print(" "); + } + else { + Serial.print(" "); + } + // Block number + Serial.print(blockAddr < 10 ? " " : (blockAddr < 100 ? " " : " ")); // Pad with spaces + Serial.print(blockAddr); + Serial.print(" "); + // Establish encrypted communications before reading the first block + if (isSectorTrailer) { + status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); + if (status != STATUS_OK) { + Serial.print("PCD_Authenticate() failed: "); + Serial.println(GetStatusCodeName(status)); + return; + } + } + // Read block + byteCount = sizeof(buffer); + status = MIFARE_Read(blockAddr, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print("MIFARE_Read() failed: "); + Serial.println(GetStatusCodeName(status)); + continue; + } + // Dump data + for (byte index = 0; index < 16; index++) { + Serial.print(buffer[index] < 0x10 ? " 0" : " "); + Serial.print(buffer[index], HEX); + if ((index % 4) == 3) { + Serial.print(" "); + } + } + // Parse sector trailer data + if (isSectorTrailer) { + c1 = buffer[7] >> 4; + c2 = buffer[8] & 0xF; + c3 = buffer[8] >> 4; + c1_ = buffer[6] & 0xF; + c2_ = buffer[6] >> 4; + c3_ = buffer[7] & 0xF; + invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); + g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); + g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); + g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); + g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); + isSectorTrailer = false; + } + + // Which access group is this block in? + if (no_of_blocks == 4) { + group = blockOffset; + firstInGroup = true; + } + else { + group = blockOffset / 5; + firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); + } + + if (firstInGroup) { + // Print access bits + Serial.print(" [ "); + Serial.print((g[group] >> 2) & 1, DEC); Serial.print(" "); + Serial.print((g[group] >> 1) & 1, DEC); Serial.print(" "); + Serial.print((g[group] >> 0) & 1, DEC); + Serial.print(" ] "); + if (invertedError) { + Serial.print(" Inverted access bits did not match! "); + } + } + + if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block + long value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + Serial.print(" Value=0x"); Serial.print(value, HEX); + Serial.print(" Adr=0x"); Serial.print(buffer[12], HEX); + } + Serial.println(); + } + + return; +} // End PICC_DumpMifareClassicSectorToSerial() + +/** + * Dumps memory contents of a MIFARE Ultralight PICC. + */ +void MFRC522::PICC_DumpMifareUltralightToSerial() { + byte status; + byte byteCount; + byte buffer[18]; + byte i; + + Serial.println("Page 0 1 2 3"); + // Try the mpages of the original Ultralight. Ultralight C has more pages. + for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + // Read pages + byteCount = sizeof(buffer); + status = MIFARE_Read(page, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print("MIFARE_Read() failed: "); + Serial.println(GetStatusCodeName(status)); + break; + } + // Dump data + for (byte offset = 0; offset < 4; offset++) { + i = page + offset; + Serial.print(i < 10 ? " " : " "); // Pad with spaces + Serial.print(i); + Serial.print(" "); + for (byte index = 0; index < 4; index++) { + i = 4 * offset + index; + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } + Serial.println(); + } + } +} // End PICC_DumpMifareUltralightToSerial() + +/** + * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tupples C1 is MSB (=4) and C3 is LSB (=1). + */ +void MFRC522::MIFARE_SetAccessBits( byte *accessBitBuffer, ///< Pointer to byte 6, 7 and 8 in the sector trailer. Bytes [0..2] will be set. + byte g0, ///< Access bits [C1 C2 C3] for block 0 (for sectors 0-31) or blocks 0-4 (for sectors 32-39) + byte g1, ///< Access bits C1 C2 C3] for block 1 (for sectors 0-31) or blocks 5-9 (for sectors 32-39) + byte g2, ///< Access bits C1 C2 C3] for block 2 (for sectors 0-31) or blocks 10-14 (for sectors 32-39) + byte g3 ///< Access bits C1 C2 C3] for the sector trailer, block 3 (for sectors 0-31) or block 15 (for sectors 32-39) + ) { + byte c1 = ((g3 & 4) << 1) | ((g2 & 4) << 0) | ((g1 & 4) >> 1) | ((g0 & 4) >> 2); + byte c2 = ((g3 & 2) << 2) | ((g2 & 2) << 1) | ((g1 & 2) << 0) | ((g0 & 2) >> 1); + byte c3 = ((g3 & 1) << 3) | ((g2 & 1) << 2) | ((g1 & 1) << 1) | ((g0 & 1) << 0); + + accessBitBuffer[0] = (~c2 & 0xF) << 4 | (~c1 & 0xF); + accessBitBuffer[1] = c1 << 4 | (~c3 & 0xF); + accessBitBuffer[2] = c3 << 4 | c2; +} // End MIFARE_SetAccessBits() + +///////////////////////////////////////////////////////////////////////////////////// +// Convenience functions - does not add extra functionality +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Returns true if a PICC responds to PICC_CMD_REQA. + * Only "new" cards in state IDLE are invited. Sleeping cards in state HALT are ignored. + * + * @return bool + */ +bool MFRC522::PICC_IsNewCardPresent() { + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + byte result = PICC_RequestA(bufferATQA, &bufferSize); + return (result == STATUS_OK || result == STATUS_COLLISION); +} // End PICC_IsNewCardPresent() + +/** + * Simple wrapper around PICC_Select. + * Returns true if a UID could be read. + * Remember to call PICC_IsNewCardPresent(), PICC_RequestA() or PICC_WakeupA() first. + * The read UID is available in the class variable uid. + * + * @return bool + */ +bool MFRC522::PICC_ReadCardSerial() { + byte result = PICC_Select(&uid); + return (result == STATUS_OK); +} // End PICC_ReadCardSerial() + + + +/** + * Write card UID on LCD + */ +//char* MFRC522::UID_to_lcd(Uid *uid) { +// char struid[100]; +// for (byte i = 0; i < uid->size; i++) { +// lcd.print(uid->uidByte[i] < 0x10 ? " 0" : " "); +// lcd.print(uid->uidByte[i], HEX); +// } +//} diff --git a/arduino-sources/MFRC522/MFRC522.h b/arduino-sources/MFRC522/MFRC522.h new file mode 100644 index 0000000..d5e6997 --- /dev/null +++ b/arduino-sources/MFRC522/MFRC522.h @@ -0,0 +1,329 @@ +/** + * MFRC522.h - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * Based on code Dr.Leong ( WWW.B2CQSHOP.COM ) + * Created by Miguel Balboa (circuitito.com), Jan, 2012. + * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.) + * Released into the public domain. + * + * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. + * Search for "mf-rc522" on ebay.com to purchase the MF-RC522 board. + * + * There are three hardware components involved: + * 1) The micro controller: An Arduino + * 2) The PCD (short for Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * 3) The PICC (short for Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * + * The microcontroller and card reader uses SPI for communication. + * The protocol is described in the MFRC522 datasheet: http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * The card reader and the tags communicate using a 13.56MHz electromagnetic field. + * The protocol is defined in ISO/IEC 14443-3 Identification cards -- Contactless integrated circuit cards -- Proximity cards -- Part 3: Initialization and anticollision". + * A free version of the final draft can be found at http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf + * Details are found in chapter 6, Type A – Initialization and anticollision. + * + * If only the PICC UID is wanted, the above documents has all the needed information. + * To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. + * The MIFARE Classic chips and protocol is described in the datasheets: + * 1K: http://www.nxp.com/documents/data_sheet/MF1S503x.pdf + * 4K: http://www.nxp.com/documents/data_sheet/MF1S703x.pdf + * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf + * The MIFARE Ultralight chip and protocol is described in the datasheets: + * Ultralight: http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + * Ultralight C: http://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf + * + * MIFARE Classic 1K (MF1S503x): + * Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + * The blocks are numbered 0-63. + * Block 3 in each sector is the Sector Trailer. See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + * Bytes 0-5: Key A + * Bytes 6-8: Access Bits + * Bytes 9: User data + * Bytes 10-15: Key B (or user data) + * Block 0 is read only manufacturer data. + * To access a block, an authentication using a key from the block's sector must be performed first. + * Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). + * All keys are set to FFFFFFFFFFFFh at chip delivery. + * Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. + * To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. + * MIFARE Classic 4K (MF1S703x): + * Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + * The blocks are numbered 0-255. + * The last block in each sector is the Sector Trailer like above. + * MIFARE Classic Mini (MF1 IC S20): + * Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + * The blocks are numbered 0-19. + * The last block in each sector is the Sector Trailer like above. + * + * MIFARE Ultralight (MF0ICU1): + * Has 16 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-15 are read/write unless blocked by the lock bytes in page 2. + * MIFARE Ultralight C (MF0ICU2): + * Has 48 pages of 4 bytes = 64 bytes. + * Pages 0 + 1 is used for the 7-byte UID. + * Page 2 contains the last chech digit for the UID, one byte manufacturer internal data, and the lock bytes (see http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + * Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + * Pages 4-39 are read/write unless blocked by the lock bytes in page 2. + * Page 40 Lock bytes + * Page 41 16 bit one way counter + * Pages 42-43 Authentication configuration + * Pages 44-47 Authentication key + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include +#include "../SPI/SPI.h" + +class MFRC522 { +public: + // MFRC522 registers. Described in chapter 9 of the datasheet. + // When using SPI all addresses are shifted one bit left in the "SPI address byte" (section 8.1.2.3) + enum PCD_Register { + // Page 0: Command and status + // 0x00 // reserved for future use + CommandReg = 0x01 << 1, // starts and stops command execution + ComIEnReg = 0x02 << 1, // enable and disable interrupt request control bits + DivIEnReg = 0x03 << 1, // enable and disable interrupt request control bits + ComIrqReg = 0x04 << 1, // interrupt request bits + DivIrqReg = 0x05 << 1, // interrupt request bits + ErrorReg = 0x06 << 1, // error bits showing the error status of the last command executed + Status1Reg = 0x07 << 1, // communication status bits + Status2Reg = 0x08 << 1, // receiver and transmitter status bits + FIFODataReg = 0x09 << 1, // input and output of 64 byte FIFO buffer + FIFOLevelReg = 0x0A << 1, // number of bytes stored in the FIFO buffer + WaterLevelReg = 0x0B << 1, // level for FIFO underflow and overflow warning + ControlReg = 0x0C << 1, // miscellaneous control registers + BitFramingReg = 0x0D << 1, // adjustments for bit-oriented frames + CollReg = 0x0E << 1, // bit position of the first bit-collision detected on the RF interface + // 0x0F // reserved for future use + + // Page 1:Command + // 0x10 // reserved for future use + ModeReg = 0x11 << 1, // defines general modes for transmitting and receiving + TxModeReg = 0x12 << 1, // defines transmission data rate and framing + RxModeReg = 0x13 << 1, // defines reception data rate and framing + TxControlReg = 0x14 << 1, // controls the logical behavior of the antenna driver pins TX1 and TX2 + TxASKReg = 0x15 << 1, // controls the setting of the transmission modulation + TxSelReg = 0x16 << 1, // selects the internal sources for the antenna driver + RxSelReg = 0x17 << 1, // selects internal receiver settings + RxThresholdReg = 0x18 << 1, // selects thresholds for the bit decoder + DemodReg = 0x19 << 1, // defines demodulator settings + // 0x1A // reserved for future use + // 0x1B // reserved for future use + MfTxReg = 0x1C << 1, // controls some MIFARE communication transmit parameters + MfRxReg = 0x1D << 1, // controls some MIFARE communication receive parameters + // 0x1E // reserved for future use + SerialSpeedReg = 0x1F << 1, // selects the speed of the serial UART interface + + // Page 2: Configuration + // 0x20 // reserved for future use + CRCResultRegH = 0x21 << 1, // shows the MSB and LSB values of the CRC calculation + CRCResultRegL = 0x22 << 1, + // 0x23 // reserved for future use + ModWidthReg = 0x24 << 1, // controls the ModWidth setting? + // 0x25 // reserved for future use + RFCfgReg = 0x26 << 1, // configures the receiver gain + GsNReg = 0x27 << 1, // selects the conductance of the antenna driver pins TX1 and TX2 for modulation + CWGsPReg = 0x28 << 1, // defines the conductance of the p-driver output during periods of no modulation + ModGsPReg = 0x29 << 1, // defines the conductance of the p-driver output during periods of modulation + TModeReg = 0x2A << 1, // defines settings for the internal timer + TPrescalerReg = 0x2B << 1, // the lower 8 bits of the TPrescaler value. The 4 high bits are in TModeReg. + TReloadRegH = 0x2C << 1, // defines the 16-bit timer reload value + TReloadRegL = 0x2D << 1, + TCounterValueRegH = 0x2E << 1, // shows the 16-bit timer value + TCounterValueRegL = 0x2F << 1, + + // Page 3:Test Registers + // 0x30 // reserved for future use + TestSel1Reg = 0x31 << 1, // general test signal configuration + TestSel2Reg = 0x32 << 1, // general test signal configuration + TestPinEnReg = 0x33 << 1, // enables pin output driver on pins D1 to D7 + TestPinValueReg = 0x34 << 1, // defines the values for D1 to D7 when it is used as an I/O bus + TestBusReg = 0x35 << 1, // shows the status of the internal test bus + AutoTestReg = 0x36 << 1, // controls the digital self test + VersionReg = 0x37 << 1, // shows the software version + AnalogTestReg = 0x38 << 1, // controls the pins AUX1 and AUX2 + TestDAC1Reg = 0x39 << 1, // defines the test value for TestDAC1 + TestDAC2Reg = 0x3A << 1, // defines the test value for TestDAC2 + TestADCReg = 0x3B << 1 // shows the value of ADC I and Q channels + // 0x3C // reserved for production tests + // 0x3D // reserved for production tests + // 0x3E // reserved for production tests + // 0x3F // reserved for production tests + }; + + // MFRC522 comands. Described in chapter 10 of the datasheet. + enum PCD_Command { + PCD_Idle = 0x00, // no action, cancels current command execution + PCD_Mem = 0x01, // stores 25 bytes into the internal buffer + PCD_GenerateRandomID = 0x02, // generates a 10-byte random ID number + PCD_CalcCRC = 0x03, // activates the CRC coprocessor or performs a self test + PCD_Transmit = 0x04, // transmits data from the FIFO buffer + PCD_NoCmdChange = 0x07, // no command change, can be used to modify the CommandReg register bits without affecting the command, for example, the PowerDown bit + PCD_Receive = 0x08, // activates the receiver circuits + PCD_Transceive = 0x0C, // transmits data from FIFO buffer to antenna and automatically activates the receiver after transmission + PCD_MFAuthent = 0x0E, // performs the MIFARE standard authentication as a reader + PCD_SoftReset = 0x0F // resets the MFRC522 + }; + + // Commands sent to the PICC. + enum PICC_Command { + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + PICC_CMD_REQA = 0x26, // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_WUPA = 0x52, // Wake-UP command, Type A. Invites PICCs in state IDLE and HALT to go to READY(*) and prepare for anticollision or selection. 7 bit frame. + PICC_CMD_CT = 0x88, // Cascade Tag. Not really a command, but used during anti collision. + PICC_CMD_SEL_CL1 = 0x93, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL2 = 0x95, // Anti collision/Select, Cascade Level 1 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 1 + PICC_CMD_HLTA = 0x50, // HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT. + // The commands used for MIFARE Classic (from http://www.nxp.com/documents/data_sheet/MF1S503x.pdf, Section 9) + // Use PCD_MFAuthent to authenticate access to a sector, then use these commands to read/write/modify the blocks on the sector. + // The read/write commands can also be used for MIFARE Ultralight. + PICC_CMD_MF_AUTH_KEY_A = 0x60, // Perform authentication with Key A + PICC_CMD_MF_AUTH_KEY_B = 0x61, // Perform authentication with Key B + PICC_CMD_MF_READ = 0x30, // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + PICC_CMD_MF_WRITE = 0xA0, // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. + PICC_CMD_MF_DECREMENT = 0xC0, // Decrements the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_INCREMENT = 0xC1, // Increments the contents of a block and stores the result in the internal data register. + PICC_CMD_MF_RESTORE = 0xC2, // Reads the contents of a block into the internal data register. + PICC_CMD_MF_TRANSFER = 0xB0, // Writes the contents of the internal data register to a block. + // The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6) + // The PICC_CMD_MF_READ and PICC_CMD_MF_WRITE can also be used for MIFARE Ultralight. + PICC_CMD_UL_WRITE = 0xA2 // Writes one 4 byte page to the PICC. + }; + + // MIFARE constants that does not fit anywhere else + enum MIFARE_Misc { + MF_ACK = 0xA, // The MIFARE Classic uses a 4 bit ACK/NAK. Any other value than 0xA is NAK. + MF_KEY_SIZE = 6 // A Mifare Crypto1 key is 6 bytes. + }; + + // PICC types we can detect. Remember to update PICC_GetTypeName() if you add more. + enum PICC_Type { + PICC_TYPE_UNKNOWN = 0, + PICC_TYPE_ISO_14443_4 = 1, // PICC compliant with ISO/IEC 14443-4 + PICC_TYPE_ISO_18092 = 2, // PICC compliant with ISO/IEC 18092 (NFC) + PICC_TYPE_MIFARE_MINI = 3, // MIFARE Classic protocol, 320 bytes + PICC_TYPE_MIFARE_1K = 4, // MIFARE Classic protocol, 1KB + PICC_TYPE_MIFARE_4K = 5, // MIFARE Classic protocol, 4KB + PICC_TYPE_MIFARE_UL = 6, // MIFARE Ultralight or Ultralight C + PICC_TYPE_MIFARE_PLUS = 7, // MIFARE Plus + PICC_TYPE_TNP3XXX = 8, // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure + PICC_TYPE_NOT_COMPLETE = 255 // SAK indicates UID is not complete. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + enum StatusCode { + STATUS_OK = 1, // Success + STATUS_ERROR = 2, // Error in communication + STATUS_COLLISION = 3, // Collission detected + STATUS_TIMEOUT = 4, // Timeout in communication. + STATUS_NO_ROOM = 5, // A buffer is not big enough. + STATUS_INTERNAL_ERROR = 6, // Internal error in the code. Should not happen ;-) + STATUS_INVALID = 7, // Invalid argument. + STATUS_CRC_WRONG = 8, // The CRC_A does not match + STATUS_MIFARE_NACK = 9 // A MIFARE PICC responded with NAK. + }; + + // A struct used for passing the UID of a PICC. + typedef struct { + byte size; // Number of bytes in the UID. 4, 7 or 10. + byte uidByte[10]; + byte sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. + } Uid; + + // A struct used for passing a MIFARE Crypto1 key + typedef struct { + byte keyByte[MF_KEY_SIZE]; + } MIFARE_Key; + + // Member variables + Uid uid; // Used by PICC_ReadCardSerial(). + + // Size of the MFRC522 FIFO + static const byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for setting up the Arduino + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(byte chipSelectPin, byte resetPowerDownPin); + void setSPIConfig(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Basic interface functions for communicating with the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(byte reg, byte value); + void PCD_WriteRegister(byte reg, byte count, byte *values); + byte PCD_ReadRegister(byte reg); + void PCD_ReadRegister(byte reg, byte count, byte *values, byte rxAlign = 0); + void setBitMask(unsigned char reg, unsigned char mask); + void PCD_SetRegisterBitMask(byte reg, byte mask); + void PCD_ClearRegisterBitMask(byte reg, byte mask); + byte PCD_CalculateCRC(byte *data, byte length, byte *result); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for manipulating the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_Init(); + void PCD_Reset(); + void PCD_AntennaOn(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + byte PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL, byte *validBits = NULL, byte rxAlign = 0, bool checkCRC = false); + + byte PICC_RequestA(byte *bufferATQA, byte *bufferSize); + byte PICC_WakeupA(byte *bufferATQA, byte *bufferSize); + byte PICC_REQA_or_WUPA( byte command, byte *bufferATQA, byte *bufferSize); + byte PICC_Select(Uid *uid, byte validBits = 0); + byte PICC_HaltA(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with MIFARE PICCs + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + void PCD_StopCrypto1(); + byte MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); + byte MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); + byte MIFARE_Decrement(byte blockAddr, long delta); + byte MIFARE_Increment(byte blockAddr, long delta); + byte MIFARE_Restore(byte blockAddr); + byte MIFARE_Transfer(byte blockAddr); + byte MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_MIFARE_Transceive( byte *sendData, byte sendLen, bool acceptTimeout = false); + const char *GetStatusCodeName(byte code); + byte PICC_GetType(byte sak); + const char *PICC_GetTypeName(byte type); + void PICC_DumpToSerial(Uid *uid); + void PICC_DumpMifareClassicToSerial(Uid *uid, byte piccType, MIFARE_Key *key); + void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpMifareUltralightToSerial(); + void MIFARE_SetAccessBits(byte *accessBitBuffer, byte g0, byte g1, byte g2, byte g3); + + ///////////////////////////////////////////////////////////////////////////////////// + // Convenience functions - does not add extra functionality + ///////////////////////////////////////////////////////////////////////////////////// + bool PICC_IsNewCardPresent(); + bool PICC_ReadCardSerial(); + + ///////////////////////////////////////////////////////////////////////////////////// + // User functions + ///////////////////////////////////////////////////////////////////////////////////// +// void MFRC522::UID_to_lcd(Uid *uid); + +private: + byte _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); +}; + +#endif diff --git a/arduino-sources/PCD8544/PCD8544.cpp b/arduino-sources/PCD8544/PCD8544.cpp new file mode 100644 index 0000000..9f1777a --- /dev/null +++ b/arduino-sources/PCD8544/PCD8544.cpp @@ -0,0 +1,334 @@ +#include +#include "PCD8544.h" + +#include +#include + + + +// LCD commands, Table 1, page 14 +#define PCD8544_FUNCTION_SET (1<<5) +#define PCD8544_FUNCTION_PD (1<<2) +#define PCD8544_FUNCTION_V (1<<1) +#define PCD8544_FUNCTION_H (1<<0) + +// Normal instructions, H = 0 +#define PCD8544_DISPLAY_CONTROL (1<<3) +#define PCD8544_DISPLAY_CONTROL_D (1<<2) +#define PCD8544_DISPLAY_CONTROL_E (1<<0) +#define PCD8544_DISPLAY_CONTROL_BLANK 0 +#define PCD8544_DISPLAY_CONTROL_NORMAL_MODE PCD8544_DISPLAY_CONTROL_D +#define PCD8544_DISPLAY_CONTROL_ALL_ON PCD8544_DISPLAY_CONTROL_E +#define PCD8544_DISPLAY_CONTROL_INVERSE (PCD8544_DISPLAY_CONTROL_D|PCD8544_DISPLAY_CONTROL_E) + +#define PCD8544_SET_Y_ADDRESS (1<<6) +#define PCD8544_Y_ADRESS_MASK 0b111 +#define PCD8544_SET_X_ADDRESS (1<<7) +#define PCD8544_X_ADRESS_MASK 0b01111111 + +// Extended instructions. H = 1 +#define PCD8544_TEMP_CONTROL (1<<2) +#define PCD8544_TEMP_TC1 (1<<1) +#define PCD8544_TEMP_TC0 (1<<0) + +#define PCD8544_BIAS (1<<4) +#define PCD8544_BIAS_BS2 (1<<2) +#define PCD8544_BIAS_BS1 (1<<1) +#define PCD8544_BIAS_BS0 (1<<0) + +#define PCD8544_VOP (1<<7) + + +const unsigned char PROGMEM small_num[][4] = { + {0x0e,0x15,0x0e,0x00}, // 48, zero + {0x12,0x1f,0x10,0x00}, // 49, one + {0x12,0x19,0x16,0x00}, // 50, two + {0x11,0x15,0x0b,0x00}, // 51, three + {0x07,0x04,0x1f,0x00}, // 52, four + {0x17,0x15,0x09,0x00}, // 53, five + {0x0e,0x15,0x09,0x00}, // 54, six + {0x19,0x05,0x03,0x00}, // 55, seven + {0x1a,0x15,0x0b,0x00}, // 56, eight + {0x12,0x15,0x0e,0x00}, // 57, nine + {0x00,0x10,0x00,0x00}, // 46, period +}; + + +const unsigned char PROGMEM font6x8 [][5] = { + {0x00,0x00,0x00,0x00,0x00,}, // ' ' 32 + {0x00,0x00,0x5F,0x00,0x00,}, // '!' 33 + {0x00,0x07,0x00,0x07,0x00,}, // '"' 34 + {0x14,0x7F,0x14,0x7F,0x14,}, // '#' 35 + {0x24,0x2A,0x7F,0x2A,0x12,}, // '$' 36 + {0x23,0x13,0x08,0x64,0x62,}, // '%' 37 + {0x36,0x49,0x55,0x22,0x50,}, // '&' 38 + {0x00,0x05,0x03,0x00,0x00,}, // ''' 39 + {0x00,0x1C,0x22,0x41,0x00,}, // '(' 40 + {0x00,0x41,0x22,0x1C,0x00,}, // ')' 41 + {0x14,0x08,0x3E,0x08,0x14,}, // '*' 42 + {0x08,0x08,0x3E,0x08,0x08,}, // '+' 43 + {0x00,0x50,0x30,0x00,0x00,}, // ',' 44 + {0x08,0x08,0x08,0x08,0x08,}, // '-' 45 + {0x00,0x60,0x60,0x00,0x00,}, // '.' 46 + {0x20,0x10,0x08,0x04,0x02,}, // '/' 47 + {0x3E,0x51,0x49,0x45,0x3E,}, // '0' 48 + {0x00,0x42,0x7F,0x40,0x00,}, // '1' 49 + {0x42,0x61,0x51,0x49,0x46,}, // '2' 50 + {0x21,0x41,0x45,0x4B,0x31,}, // '3' 51 + {0x18,0x14,0x12,0x7F,0x10,}, // '4' 52 + {0x27,0x45,0x45,0x45,0x39,}, // '5' 53 + {0x3C,0x4A,0x49,0x49,0x30,}, // '6' 54 + {0x03,0x01,0x71,0x09,0x07,}, // '7' 55 + {0x36,0x49,0x49,0x49,0x36,}, // '8' 56 + {0x06,0x49,0x49,0x29,0x16,}, // '9' 57 + {0x00,0x36,0x36,0x00,0x00,}, // ':' 58 + {0x00,0x56,0x36,0x00,0x00,}, // ';' 59 + {0x08,0x14,0x22,0x41,0x00,}, // '<' 60 + {0x14,0x14,0x14,0x14,0x14,}, // '=' 61 + {0x00,0x41,0x22,0x14,0x08,}, // '>' 62 + {0x02,0x01,0x51,0x09,0x06,}, // '?' 63 + {0x32,0x49,0x79,0x41,0x3E,}, // '@' 64 + {0x7E,0x11,0x11,0x11,0x7E,}, // 'A' 65 + {0x7F,0x49,0x49,0x49,0x36,}, // 'B' 66 + {0x3E,0x41,0x41,0x41,0x22,}, // 'C' 67 + {0x7F,0x41,0x41,0x22,0x1C,}, // 'D' 68 + {0x7F,0x49,0x49,0x49,0x41,}, // 'E' 69 + {0x7F,0x09,0x09,0x01,0x01,}, // 'F' 70 + {0x3E,0x41,0x49,0x49,0x3A,}, // 'G' 71 + {0x7F,0x08,0x08,0x08,0x7F,}, // 'H' 72 + {0x00,0x41,0x7F,0x41,0x00,}, // 'I' 73 + {0x20,0x41,0x41,0x3F,0x01,}, // 'J' 74 + {0x7F,0x08,0x14,0x22,0x41,}, // 'K' 75 + {0x7F,0x40,0x40,0x40,0x40,}, // 'L' 76 + {0x7F,0x02,0x0C,0x02,0x7F,}, // 'M' 77 + {0x7F,0x04,0x08,0x10,0x7F,}, // 'N' 78 + {0x3E,0x41,0x41,0x41,0x3E,}, // 'O' 79 + {0x7F,0x09,0x09,0x09,0x06,}, // 'P' 80 + {0x3E,0x41,0x51,0x21,0x5E,}, // 'Q' 81 + {0x7F,0x09,0x19,0x29,0x46,}, // 'R' 82 + {0x26,0x49,0x49,0x49,0x32,}, // 'S' 83 + {0x01,0x01,0x7F,0x01,0x01,}, // 'T' 84 + {0x3F,0x40,0x40,0x40,0x3F,}, // 'U' 85 + {0x1F,0x20,0x40,0x20,0x1F,}, // 'V' 86 + {0x3F,0x40,0x38,0x40,0x3F,}, // 'W' 87 + {0x63,0x14,0x08,0x14,0x63,}, // 'X' 88 + {0x07,0x08,0x70,0x08,0x07,}, // 'Y' 89 + {0x61,0x51,0x49,0x45,0x43,}, // 'Z' 90 + {0x00,0x7F,0x41,0x41,0x00,}, // '[' 91 + {0x02,0x04,0x08,0x10,0x20,}, // '\' 92 + {0x00,0x41,0x41,0x7F,0x00,}, // ']' 93 + {0x04,0x02,0x01,0x02,0x04,}, // '^' 94 + {0x40,0x40,0x40,0x40,0x40,}, // '_' 95 + {0x00,0x01,0x02,0x04,0x00,}, // '`' 96 + {0x20,0x54,0x54,0x54,0x78,}, // 'a' 97 + {0x7F,0x48,0x44,0x44,0x38,}, // 'b' 98 + {0x38,0x44,0x44,0x44,0x20,}, // 'c' 99 + {0x38,0x44,0x44,0x48,0x3F,}, // 'd' 100 + {0x38,0x54,0x54,0x54,0x18,}, // 'e' 101 + {0x08,0x7E,0x09,0x01,0x02,}, // 'f' 102 + {0x0C,0x52,0x52,0x52,0x3E,}, // 'g' 103 + {0x7F,0x08,0x04,0x04,0x78,}, // 'h' 104 + {0x00,0x44,0x7D,0x40,0x00,}, // 'i' 105 + {0x20,0x40,0x45,0x3C,0x00,}, // 'j' 106 + {0x7F,0x10,0x28,0x44,0x00,}, // 'k' 107 + {0x00,0x41,0x7F,0x40,0x00,}, // 'l' 108 + {0x7C,0x04,0x18,0x04,0x78,}, // 'm' 109 + {0x7C,0x08,0x04,0x04,0x78,}, // 'n' 110 + {0x38,0x44,0x44,0x44,0x38,}, // 'o' 111 + {0x7C,0x14,0x14,0x14,0x08,}, // 'p' 112 + {0x08,0x14,0x14,0x18,0x7C,}, // 'q' 113 + {0x7C,0x08,0x04,0x04,0x08,}, // 'r' 114 + {0x48,0x54,0x54,0x54,0x20,}, // 's' 115 + {0x04,0x3F,0x44,0x40,0x20,}, // 't' 116 + {0x3C,0x40,0x40,0x20,0x7C,}, // 'u' 117 + {0x1C,0x20,0x40,0x20,0x1C,}, // 'v' 118 + {0x3C,0x40,0x30,0x40,0x3C,}, // 'w' 119 + {0x44,0x28,0x10,0x28,0x44,}, // 'x' 120 + {0x0C,0x50,0x50,0x50,0x3C,}, // 'y' 121 + {0x44,0x64,0x54,0x4C,0x44,}, // 'z' 122 + {0x00,0x08,0x36,0x41,0x00,}, // '{' 123 + {0x00,0x00,0x7F,0x00,0x00,}, // '|' 124 + {0x00,0x41,0x36,0x08,0x00,}, // '}' 125 + {0x10,0x08,0x08,0x10,0x08,}, // '~' 126 + {0x08,0x1C,0x2A,0x08,0x08,} // <- 127 +}; + + + + +PCD8544::PCD8544(uint8_t dc_pin, uint8_t reset_pin, uint8_t cs_pin, uint8_t hardware_spi) +{ + dc = dc_pin; + cs = cs_pin; + reset = reset_pin; + hardware_spi_num = hardware_spi; + if (hardware_spi_num > 2) + hardware_spi_num = 2; +#ifndef MAPLE + sdin = MOSI; + sclk = SCK; +#else + sdin = 11; // Change to maple names + sclk = 13; + if (hardware_spi_num == 2) { + sdin = 32; + sclk = 34; + } +#endif +} + +PCD8544::PCD8544(uint8_t dc_pin, uint8_t reset_pin, uint8_t cs_pin, uint8_t sdin_pin, uint8_t sclk_pin) +{ + dc = dc_pin; + cs = cs_pin; + reset = reset_pin; + sdin = sdin_pin; + sclk = sclk_pin; + hardware_spi_num = 0; +} + +void PCD8544::begin(void) +{ + pinMode(cs, OUTPUT); + pinMode(reset, OUTPUT); + pinMode(dc, OUTPUT); + pinMode(sdin, OUTPUT); + pinMode(sclk, OUTPUT); + + + + if (hardware_spi_num > 0) { + pinMode(SS, OUTPUT); // To ensure master mode + SPCR |= (1<= ' ' && ch <= 127) { + for (i = 0; i < 5; i++) + data(pgm_read_byte(&font6x8[ch-' '][i]) <<1); + data(0); + } + + WRITE_RETURN; +} + +//void PCD8544::toggle_invert(void) { +// invert_line = !invert_line; +//} + +void PCD8544::data(uint8_t data) { +// if (invert_line) +// send(1, ~data); +// else + send(1, data); +} + +void PCD8544::command(uint8_t data) +{ + send(0, data); +} + +void PCD8544::send(uint8_t data_or_command, uint8_t data) +{ + digitalWrite(dc, data_or_command); + digitalWrite(cs, LOW); + if (hardware_spi_num == 0) { + shiftOut(sdin, sclk, MSBFIRST, data); + } else { + SPDR = data; + while(!(SPSR & (1<= PCD8544_LINES) + row %= PCD8544_LINES; + if (column >= PCD8544_WIDTH) + row %= PCD8544_WIDTH; + command(PCD8544_SET_X_ADDRESS | column); + command(PCD8544_SET_Y_ADDRESS | row); + current_row = row; + current_column = column; +} + +void PCD8544::inc_row_column(void) +{ + if (++current_column >= PCD8544_WIDTH) { + current_column = 0; + if (++current_row >= PCD8544_LINES) + current_row = 0; + } +// invert_line = false; +} + + +void PCD8544::smallNum(uint8_t num, uint8_t shift) +{ + uint8_t i; + for (i = 0; i < 4; i++) + data(pgm_read_byte(&small_num[num][i])< +#include + + +#include // Arduino 1.0 +#define WRITE_RESULT size_t +#define WRITE_RETURN return 1; + + + +//#ifndef __AVR-GCC__ +//#define MAPLE 1 +// +//#ifndef SPI_NUM +//#define SPI_NUM 1 +//#endif +//#endif + +class PCD8544 : public Print { +public: + // Constructor for harware SPI + PCD8544(uint8_t dc_pin, uint8_t reset_pin, uint8_t cs_pin, uint8_t hardware_spi = 1); + + // Constructor for software SPI. + PCD8544(uint8_t dc_pin, uint8_t reset_pin, uint8_t cs_pin, uint8_t sdin_pin, uint8_t sclk_pin); + + // Call this first + void begin(void); + + // Clear lcd without changing location + void clear(void); + + // Change current position to (character) column and row + void setCursor(uint8_t column, uint8_t row); + + // Change current location to 0 <= row <= 5, + // 0 <= pixel_column <= 83 + void gotoRc(uint8_t row, uint8_t pixel_column); + + // Send data to lcd. Will draw data as one pixel wide, 8 pixel high. + // LSB up. + void data(uint8_t data); + + // Small numbers. 0<= num <=9 for number and num = 10 for decimal + // point. Optional parameter shift will move the numbers up/down. + // shift shold be 0,1,2,3 for the digit to be visible. + void smallNum(uint8_t num, uint8_t shift = 1); + + void clearRestOfLine(void); + void bitmap(const byte *data, uint8_t rows, uint8_t columns); + +// void toggle_invert(void); + +private: + void send(uint8_t dc, uint8_t data); + void command(uint8_t data); + virtual WRITE_RESULT write(uint8_t ch); + void inc_row_column(void); + uint8_t dc; + uint8_t cs; + uint8_t reset; + uint8_t hardware_spi_num; + uint8_t sdin; + uint8_t sclk; + uint8_t current_row, current_column; + uint8_t invert_line; +}; + +#define PCD8544_LINES 6 +#define PCD8544_COLS 14 +#define PCD8544_WIDTH 84 +#define PCD8544_HEIGHT 48 + + +#endif diff --git a/arduino-sources/Release/DS1302/subdir.mk b/arduino-sources/Release/DS1302/subdir.mk new file mode 100644 index 0000000..f0ad096 --- /dev/null +++ b/arduino-sources/Release/DS1302/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../DS1302/DS1302.cpp + +OBJS += \ +./DS1302/DS1302.o + +CPP_DEPS += \ +./DS1302/DS1302.d + + +# Each subdirectory must supply rules for building sources it contributes +DS1302/%.o: ../DS1302/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/MFRC522/subdir.mk b/arduino-sources/Release/MFRC522/subdir.mk new file mode 100644 index 0000000..5b0c18e --- /dev/null +++ b/arduino-sources/Release/MFRC522/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../MFRC522/MFRC522.cpp + +OBJS += \ +./MFRC522/MFRC522.o + +CPP_DEPS += \ +./MFRC522/MFRC522.d + + +# Each subdirectory must supply rules for building sources it contributes +MFRC522/%.o: ../MFRC522/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/PCD8544/subdir.mk b/arduino-sources/Release/PCD8544/subdir.mk new file mode 100644 index 0000000..d23b59b --- /dev/null +++ b/arduino-sources/Release/PCD8544/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../PCD8544/PCD8544.cpp + +OBJS += \ +./PCD8544/PCD8544.o + +CPP_DEPS += \ +./PCD8544/PCD8544.d + + +# Each subdirectory must supply rules for building sources it contributes +PCD8544/%.o: ../PCD8544/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/SD/subdir.mk b/arduino-sources/Release/SD/subdir.mk new file mode 100644 index 0000000..8b70c6b --- /dev/null +++ b/arduino-sources/Release/SD/subdir.mk @@ -0,0 +1,27 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../SD/File.cpp \ +../SD/SD.cpp + +OBJS += \ +./SD/File.o \ +./SD/SD.o + +CPP_DEPS += \ +./SD/File.d \ +./SD/SD.d + + +# Each subdirectory must supply rules for building sources it contributes +SD/%.o: ../SD/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/usr/share/arduino/hardware/arduino/cores/arduino" -I"/home/delmadord/dev/arduino/Bakalarka/SD/utility" -I"/usr/share/arduino/hardware/arduino/variants/standard" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO=1:105+dfsg2-1 -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -mmcu=atmega328p -DF_CPU=16000000UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/SD/utility/subdir.mk b/arduino-sources/Release/SD/utility/subdir.mk new file mode 100644 index 0000000..17ab557 --- /dev/null +++ b/arduino-sources/Release/SD/utility/subdir.mk @@ -0,0 +1,30 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../SD/utility/Sd2Card.cpp \ +../SD/utility/SdFile.cpp \ +../SD/utility/SdVolume.cpp + +OBJS += \ +./SD/utility/Sd2Card.o \ +./SD/utility/SdFile.o \ +./SD/utility/SdVolume.o + +CPP_DEPS += \ +./SD/utility/Sd2Card.d \ +./SD/utility/SdFile.d \ +./SD/utility/SdVolume.d + + +# Each subdirectory must supply rules for building sources it contributes +SD/utility/%.o: ../SD/utility/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/usr/share/arduino/hardware/arduino/cores/arduino" -I"/home/delmadord/dev/arduino/Bakalarka/SD/utility" -I"/usr/share/arduino/hardware/arduino/variants/standard" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO=1:105+dfsg2-1 -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -mmcu=atmega328p -DF_CPU=16000000UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/SDFAT16/subdir.mk b/arduino-sources/Release/SDFAT16/subdir.mk new file mode 100644 index 0000000..cf48298 --- /dev/null +++ b/arduino-sources/Release/SDFAT16/subdir.mk @@ -0,0 +1,27 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../SDFAT16/Fat16.cpp \ +../SDFAT16/SdCard.cpp + +OBJS += \ +./SDFAT16/Fat16.o \ +./SDFAT16/SdCard.o + +CPP_DEPS += \ +./SDFAT16/Fat16.d \ +./SDFAT16/SdCard.d + + +# Each subdirectory must supply rules for building sources it contributes +SDFAT16/%.o: ../SDFAT16/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/SPI/subdir.mk b/arduino-sources/Release/SPI/subdir.mk new file mode 100644 index 0000000..7ae79fc --- /dev/null +++ b/arduino-sources/Release/SPI/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../SPI/SPI.cpp + +OBJS += \ +./SPI/SPI.o + +CPP_DEPS += \ +./SPI/SPI.d + + +# Each subdirectory must supply rules for building sources it contributes +SPI/%.o: ../SPI/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/SerialCommand/subdir.mk b/arduino-sources/Release/SerialCommand/subdir.mk new file mode 100644 index 0000000..d5fa618 --- /dev/null +++ b/arduino-sources/Release/SerialCommand/subdir.mk @@ -0,0 +1,24 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../SerialCommand/SerialCommand.cpp + +OBJS += \ +./SerialCommand/SerialCommand.o + +CPP_DEPS += \ +./SerialCommand/SerialCommand.d + + +# Each subdirectory must supply rules for building sources it contributes +SerialCommand/%.o: ../SerialCommand/%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/Release/subdir.mk b/arduino-sources/Release/subdir.mk new file mode 100644 index 0000000..b0d7111 --- /dev/null +++ b/arduino-sources/Release/subdir.mk @@ -0,0 +1,27 @@ +################################################################################ +# Automatically-generated file. Do not edit! +################################################################################ + +# Add inputs and outputs from these tool invocations to the build variables +CPP_SRCS += \ +../Bakalarka.cpp \ +../rotary.cpp + +OBJS += \ +./Bakalarka.o \ +./rotary.o + +CPP_DEPS += \ +./Bakalarka.d \ +./rotary.d + + +# Each subdirectory must supply rules for building sources it contributes +%.o: ../%.cpp + @echo 'Building file: $<' + @echo 'Invoking: AVR C++ Compiler' + avr-g++ -I"/home/delmadord/dev/arduino" -I"/home/delmadord/dev/arduino/Bakalarka" -D__IN_ECLIPSE__=1 -DUSB_VID= -DUSB_PID= -DARDUINO= -Wall -Os -ffunction-sections -fdata-sections -fno-exceptions -g -DF_CPU=0UL -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -c -o "$@" -x c++ "$<" + @echo 'Finished building: $<' + @echo ' ' + + diff --git a/arduino-sources/SDFAT16/Fat16Config.h b/arduino-sources/SDFAT16/Fat16Config.h new file mode 100644 index 0000000..e060ee6 --- /dev/null +++ b/arduino-sources/SDFAT16/Fat16Config.h @@ -0,0 +1,38 @@ +/* Arduino FAT16 Library + * Copyright (C) 2008 by William Greiman + * + * This file is part of the Arduino FAT16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ + /** + * \file + * Configuration file + */ +#ifndef Fat16Config_h +#define Fat16Config_h +/** + * Allow use of deprecated functions if non-zero + */ +#define ALLOW_DEPRECATED_FUNCTIONS 1 +/** + * SdCard::writeBlock will protect block zero if set non-zero + */ +#define SD_PROTECT_BLOCK_ZERO 1 +/** + * Set non-zero to allow access to Fat16 internals by cardInfo debug sketch + */ +#define FAT16_DEBUG_SUPPORT 1 +#endif // Fat16Config_h diff --git a/arduino-sources/SDFAT16/Fat16mainpage.h b/arduino-sources/SDFAT16/Fat16mainpage.h new file mode 100644 index 0000000..4e0d3e6 --- /dev/null +++ b/arduino-sources/SDFAT16/Fat16mainpage.h @@ -0,0 +1,208 @@ +/* Arduino FAT16 Library + * Copyright (C) 2008 by William Greiman + * + * This file is part of the Arduino FAT16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ + +/** +\mainpage Arduino Fat16 Library +
Copyright © 2008 by William Greiman +
+ +\section Intro Introduction +The Arduino Fat16 Library is a minimal implementation of the FAT16 file system +on standard SD flash memory cards. Fat16 supports read, write, file +creation, deletion, and truncation. + +The Fat16 class only supports access to files in the root directory and only +supports short 8.3 names. Directory time and date fields for creation +and modification can be maintained by providing a date/time callback +function \link Fat16::dateTimeCallback() dateTimeCallback()\endlink +or calling \link Fat16::timestamp() timestamp()\endlink. + +Fat16 was designed to use the Arduino Print class which +allows files to be written with \link Print::print() print() \endlink and +\link Print::println() println()\endlink. + +\section comment Bugs and Comments + +If you wish to report bugs or have comments, send email to fat16lib@sbcglobal.net. + + +\section SDcard SD Cards + +Arduinos access SD cards using the cards SPI protocol. PCs, Macs, and +most consumer devices use the 4-bit parallel SD protocol. A card that +functions well on A PC or Mac may not work well on the Arduino. + +Most cards have good SPI read performance but cards vary widely in SPI +write performance. Write performance is limited by how efficiently the +card manages internal erase/remapping operations. The Arduino cannot +optimize writes to reduce erase operations because of its limit RAM. + +SanDisk cards generally have good write performance. They seem to have +more internal RAM buffering than other cards and therefore can limit +the number of flash erase operations that the Arduino forces due to its +limited RAM. + +Some Dane-Elec cards have a write speed that is only 20% as fast as +a good SanDisk card. + + +\section Hardware Hardware Configuration +Fat16 was developed using an Adafruit Industries + GPS Shield. + +The hardware interface to the SD card should not use a resistor based level +shifter. SdCard::init() sets the SPI bus frequency to 8 MHz which results in +signal rise times that are too slow for the edge detectors in many newer SD card +controllers when resistor voltage dividers are used. + +The 5 to 3.3 V level shifter for 5 V arduinos should be IC based like the +74HC4050N based circuit shown in the file SdLevel.png. The Adafruit Wave Shield +uses a 74AHC125N. Gravitech sells SD and MicroSD Card Adapters based on the +74LCX245. + +If you are using a resistor based level shifter and are having problems try +setting the SPI bus frequency to 4 MHz. This can be done by using +card.init(true) to initialize the SD card. + + +\section Fat16Class Fat16 Usage + +The class Fat16 is a minimal implementation of FAT16 on standard SD cards. +High Capacity SD cards, SDHC, are not supported. It should work on all +standard cards from 8MB to 2GB formatted with a FAT16 file system. + +\note + The Arduino Print class uses character +at a time writes so it was necessary to use a \link Fat16::sync() sync() \endlink +function to control when data is written to the SD card. + +\par +An application which writes to a file using \link Print::print() print()\endlink, +\link Print::println() println() \endlink +or \link Fat16::write write() \endlink must call \link Fat16::sync() sync() \endlink +at the appropriate time to force data and directory information to be written +to the SD Card. Data and directory information are also written to the SD card +when \link Fat16::close() close() \endlink is called. + +\par +Applications must use care calling \link Fat16::sync() sync() \endlink +since 2048 bytes of I/O is required to update file and +directory information. This includes writing the current data block, reading +the block that contains the directory entry for update, writing the directory +block back and reading back the current data block. + +Fat16 only supports access to files in the root directory and only supports +short 8.3 names. + +It is possible to open a file with two or more instances of Fat16. A file may +be corrupted if data is written to the file by more than one instance of Fat16. + +Short names are limited to 8 characters followed by an optional period (.) +and extension of up to 3 characters. The characters may be any combination +of letters and digits. The following special characters are also allowed: + +$ % ' - _ @ ~ ` ! ( ) { } ^ # & + +Short names are always converted to upper case and their original case +value is lost. + +Fat16 uses a slightly restricted form of short names. +Only printable ASCII characters are supported. No characters with code point +values greater than 127 are allowed. Space is not allowed even though space +was allowed in the API of early versions of DOS. + +Fat16 has been optimized for The Arduino ATmega168. Minimizing RAM use is the +highest priority goal followed by flash use and finally performance. +Most SD cards only support 512 byte block write operations so a 512 byte +cache buffer is used by Fat16. This is the main use of RAM. A small +amount of RAM is used to store key volume and file information. +Flash memory usage can be controlled by selecting options in Fat16Config.h. + +\section HowTo How to format SD Cards as FAT16 Volumes + +Microsoft operating systems support removable media formatted with a +Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector +in block zero. + +Microsoft operating systems expect MBR formatted removable media +to have only one partition. The first partition should be used. + +Microsoft operating systems do not support partitioning SD flash cards. +If you erase an SD card with a program like KillDisk, Most versions of +Windows will format the card as a super floppy. + +The best way to restore an SD card's MBR is to use SDFormatter +which can be downloaded from: + +http://www.sdcard.org/consumers/formatter/ + +SDFormatter does not have an option for FAT type so it may format +small cards as FAT12. + +After the MBR is restored by SDFormatter you may need to reformat small +cards that have been formatted FAT12 to force the volume type to be FAT16. + +The FAT type, FAT12, FAT16, or FAT32, is determined by the count +of clusters on the volume and nothing else. + +Microsoft published the following code for determining FAT type: + +\code +if (CountOfClusters < 4085) { + // Volume is FAT12 +} +else if (CountOfClusters < 65525) { + // Volume is FAT16 +} +else { + // Volume is FAT32 +} + +\endcode +If you format a FAT volume with an OS utility , choose a cluster size that +will result in: + +4084 < CountOfClusters && CountOfClusters < 65525 + +The volume will then be FAT16. + +If you are formatting an SD card on OS X or Linux, be sure to use the first +partition. Format this partition with a cluster count in above range. + +\section References References + +The Arduino site: + +http://www.arduino.cc/ + +For more information about FAT file systems see: + +http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + +For information about using SD cards as SPI devices see: + +http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + +The ATmega328 datasheet: + +http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf + + + */ \ No newline at end of file diff --git a/arduino-sources/SDFAT16/FatStructs.h b/arduino-sources/SDFAT16/FatStructs.h new file mode 100644 index 0000000..c64591d --- /dev/null +++ b/arduino-sources/SDFAT16/FatStructs.h @@ -0,0 +1,418 @@ +/* Arduino Fat16 Library + * Copyright (C) 2009 by William Greiman + * + * This file is part of the Arduino Fat16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ +#ifndef FatStructs_h +#define FatStructs_h +/** + * \file + * FAT file structures + */ +/* + * mostly from Microsoft document fatgen103.doc + * http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + */ +//------------------------------------------------------------------------------ +/** Value for byte 510 of boot block or MBR */ +uint8_t const BOOTSIG0 = 0X55; +/** Value for byte 511 of boot block or MBR */ +uint8_t const BOOTSIG1 = 0XAA; +//------------------------------------------------------------------------------ +/** + * \struct partitionTable + * \brief MBR partition table entry + * + * A partition table entry for a MBR formatted storage device. + * The MBR partition table has four entries. + */ +struct partitionTable { + /** + * Boot Indicator . Indicates whether the volume is the active + * partition. Legal values include: 0X00. Do not use for booting. + * 0X80 Active partition. + */ + uint8_t boot; + /** + * Head part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t beginHead; + /** + * Sector part of Cylinder-head-sector address of the first block in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned beginSector : 6; + /** High bits cylinder for first block in partition. */ + unsigned beginCylinderHigh : 2; + /** + * Combine beginCylinderLow with beginCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t beginCylinderLow; + /** + * Partition type. See defines that begin with PART_TYPE_ for + * some Microsoft partition types. + */ + uint8_t type; + /** + * head part of cylinder-head-sector address of the last sector in the + * partition. Legal values are 0-255. Only used in old PC BIOS. + */ + uint8_t endHead; + /** + * Sector part of cylinder-head-sector address of the last sector in + * the partition. Legal values are 1-63. Only used in old PC BIOS. + */ + unsigned endSector : 6; + /** High bits of end cylinder */ + unsigned endCylinderHigh : 2; + /** + * Combine endCylinderLow with endCylinderHigh. Legal values + * are 0-1023. Only used in old PC BIOS. + */ + uint8_t endCylinderLow; + /** Logical block address of the first block in the partition. */ + uint32_t firstSector; + /** Length of the partition, in blocks. */ + uint32_t totalSectors; +}; +/** Type name for partitionTable */ +typedef struct partitionTable part_t; +//------------------------------------------------------------------------------ +/** + * \struct masterBootRecord + * + * \brief Master Boot Record + * + * The first block of a storage device that is formatted with a MBR. + */ +struct masterBootRecord { + /** Code Area for master boot program. */ + uint8_t codeArea[440]; + /** Optional WindowsNT disk signature. May contain more boot code. */ + uint32_t diskSignature; + /** Usually zero but may be more boot code. */ + uint16_t usuallyZero; + /** Partition tables. */ + part_t part[4]; + /** First MBR signature byte. Must be 0X55 */ + uint8_t mbrSig0; + /** Second MBR signature byte. Must be 0XAA */ + uint8_t mbrSig1; +}; +/** Type name for masterBootRecord */ +typedef struct masterBootRecord mbr_t; +//------------------------------------------------------------------------------ +/** + * \struct biosParmBlock + * + * \brief BIOS parameter block + * + * The BIOS parameter block describes the physical layout of a FAT volume. + */ +struct biosParmBlock { + /** + * Count of bytes per sector. This value may take on only the + * following values: 512, 1024, 2048 or 4096 + */ + uint16_t bytesPerSector; + /** + * Number of sectors per allocation unit. This value must be a + * power of 2 that is greater than 0. The legal values are + * 1, 2, 4, 8, 16, 32, 64, and 128. + */ + uint8_t sectorsPerCluster; + /** + * Number of sectors before the first FAT. + * This value must not be zero. + */ + uint16_t reservedSectorCount; + /** The count of FAT data structures on the volume. This field should + * always contain the value 2 for any FAT volume of any type. + */ + uint8_t fatCount; + /** + * For FAT12 and FAT16 volumes, this field contains the count of + * 32-byte directory entries in the root directory. For FAT32 volumes, + * this field must be set to 0. For FAT12 and FAT16 volumes, this + * value should always specify a count that when multiplied by 32 + * results in a multiple of bytesPerSector. FAT16 volumes should + * use the value 512. + */ + uint16_t rootDirEntryCount; + /** + * This field is the old 16-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then totalSectors32 + * must be non-zero. For FAT32 volumes, this field must be 0. For + * FAT12 and FAT16 volumes, this field contains the sector count, and + * totalSectors32 is 0 if the total sector count fits + * (is less than 0x10000). + */ + uint16_t totalSectors16; + /** + * This dates back to the old MS-DOS 1.x media determination and is + * no longer usually used for anything. 0xF8 is the standard value + * for fixed (non-removable) media. For removable media, 0xF0 is + * frequently used. Legal values are 0xF0 or 0xF8-0xFF. + */ + uint8_t mediaType; + /** + * Count of sectors occupied by one FAT on FAT12/FAT16 volumes. + * On FAT32 volumes this field must be 0, and sectorsPerFat32 + * contains the FAT size count. + */ + uint16_t sectorsPerFat16; + /** Sectors per track for interrupt 0x13. Not used otherwise. */ + uint16_t sectorsPerTrtack; + /** Number of heads for interrupt 0x13. Not used otherwise. */ + uint16_t headCount; + /** + * Count of hidden sectors preceding the partition that contains this + * FAT volume. This field is generally only relevant for media + * visible on interrupt 0x13. + */ + uint32_t hidddenSectors; + /** + * This field is the new 32-bit total count of sectors on the volume. + * This count includes the count of all sectors in all four regions + * of the volume. This field can be 0; if it is 0, then + * totalSectors16 must be non-zero. + */ + uint32_t totalSectors32; + /** + * Count of sectors occupied by one FAT on FAT32 volumes. + */ + uint32_t sectorsPerFat32; + /** + * This field is only defined for FAT32 media and does not exist on + * FAT12 and FAT16 media. + * Bits 0-3 -- Zero-based number of active FAT. + * Only valid if mirroring is disabled. + * Bits 4-6 -- Reserved. + * Bit 7 -- 0 means the FAT is mirrored at runtime into all FATs. + * -- 1 means only one FAT is active; it is the one referenced in bits 0-3. + * Bits 8-15 -- Reserved. + */ + uint16_t fat32Flags; + /** + * FAT32 version. High byte is major revision number. + * Low byte is minor revision number. Only 0.0 define. + */ + uint16_t fat32Version; + /** + * Cluster number of the first cluster of the root directory for FAT32. + * This usually 2 but not required to be 2. + */ + uint32_t fat32RootCluster; + /** + * Sector number of FSINFO structure in the reserved area of the + * FAT32 volume. Usually 1. + */ + uint16_t fat32FSInfo; + /** + * If non-zero, indicates the sector number in the reserved area + * of the volume of a copy of the boot record. Usually 6. + * No value other than 6 is recommended. + */ + uint16_t fat32BackBootBlock; + /** + * Reserved for future expansion. Code that formats FAT32 volumes + * should always set all of the bytes of this field to 0. + */ + uint8_t fat32Reserved[12]; +}; +/** Type name for biosParmBlock */ +typedef struct biosParmBlock bpb_t; +//------------------------------------------------------------------------------ +/** + * \struct fat32BootSector + * + * \brief Boot sector for a FAT16 or FAT32 volume. + * + */ +struct fat32BootSector { + /** X86 jmp to boot program */ + uint8_t jmpToBootCode[3]; + /** informational only - don't depend on it */ + char oemName[8]; + /** BIOS Parameter Block */ + bpb_t bpb; + /** for int0x13 use value 0X80 for hard drive */ + uint8_t driveNumber; + /** used by Windows NT - should be zero for FAT */ + uint8_t reserved1; + /** 0X29 if next three fields are valid */ + uint8_t bootSignature; + /** usually generated by combining date and time */ + uint32_t volumeSerialNumber; + /** should match volume label in root dir */ + char volumeLabel[11]; + /** informational only - don't depend on it */ + char fileSystemType[8]; + /** X86 boot code */ + uint8_t bootCode[420]; + /** must be 0X55 */ + uint8_t bootSectorSig0; + /** must be 0XAA */ + uint8_t bootSectorSig1; +}; +//------------------------------------------------------------------------------ +// End Of Chain values for FAT entries +/** FAT16 end of chain value used by Microsoft. */ +uint16_t const FAT16EOC = 0XFFFF; +/** Minimum value for FAT16 EOC. Use to test for EOC. */ +uint16_t const FAT16EOC_MIN = 0XFFF8; +/** FAT32 end of chain value used by Microsoft. */ +uint32_t const FAT32EOC = 0X0FFFFFFF; +/** Minimum value for FAT32 EOC. Use to test for EOC. */ +uint32_t const FAT32EOC_MIN = 0X0FFFFFF8; +/** Mask a for FAT32 entry. Entries are 28 bits. */ +uint32_t const FAT32MASK = 0X0FFFFFFF; + +/** Type name for fat32BootSector */ +typedef struct fat32BootSector fbs_t; +//------------------------------------------------------------------------------ +/** + * \struct directoryEntry + * \brief FAT short directory entry + * + * Short means short 8.3 name, not the entry size. + * + * Date Format. A FAT directory entry date stamp is a 16-bit field that is + * basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the + * format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the + * 16-bit word): + * + * Bits 9-15: Count of years from 1980, valid value range 0-127 + * inclusive (1980-2107). + * + * Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive. + * + * Bits 0-4: Day of month, valid value range 1-31 inclusive. + * + * Time Format. A FAT directory entry time stamp is a 16-bit field that has + * a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the + * 16-bit word, bit 15 is the MSB of the 16-bit word). + * + * Bits 11-15: Hours, valid value range 0-23 inclusive. + * + * Bits 5-10: Minutes, valid value range 0-59 inclusive. + * + * Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds). + * + * The valid time range is from Midnight 00:00:00 to 23:59:58. + */ +struct directoryEntry { + /** + * Short 8.3 name. + * The first eight bytes contain the file name with blank fill. + * The last three bytes contain the file extension with blank fill. + */ + uint8_t name[11]; + /** Entry attributes. + * + * The upper two bits of the attribute byte are reserved and should + * always be set to 0 when a file is created and never modified or + * looked at after that. See defines that begin with DIR_ATT_. + */ + uint8_t attributes; + /** + * Reserved for use by Windows NT. Set value to 0 when a file is + * created and never modify or look at it after that. + */ + uint8_t reservedNT; + /** + * The granularity of the seconds part of creationTime is 2 seconds + * so this field is a count of tenths of a second and its valid + * value range is 0-199 inclusive. (WHG note - seems to be hundredths) + */ + uint8_t creationTimeTenths; + /** Time file was created. */ + uint16_t creationTime; + /** Date file was created. */ + uint16_t creationDate; + /** + * Last access date. Note that there is no last access time, only + * a date. This is the date of last read or write. In the case of + * a write, this should be set to the same date as lastWriteDate. + */ + uint16_t lastAccessDate; + /** + * High word of this entry's first cluster number (always 0 for a + * FAT12 or FAT16 volume). + */ + uint16_t firstClusterHigh; + /** Time of last write. File creation is considered a write. */ + uint16_t lastWriteTime; + /** Date of last write. File creation is considered a write. */ + uint16_t lastWriteDate; + /** Low word of this entry's first cluster number. */ + uint16_t firstClusterLow; + /** 32-bit unsigned holding this file's size in bytes. */ + uint32_t fileSize; +}; +//------------------------------------------------------------------------------ +// Definitions for directory entries +// +/** Type name for directoryEntry */ +typedef struct directoryEntry dir_t; +/** escape for name[0] = 0XE5 */ +uint8_t const DIR_NAME_0XE5 = 0X05; +/** name[0] value for entry that is free after being "deleted" */ +uint8_t const DIR_NAME_DELETED = 0XE5; +/** name[0] value for entry that is free and no allocated entries follow */ +uint8_t const DIR_NAME_FREE = 0X00; +/** file is read-only */ +uint8_t const DIR_ATT_READ_ONLY = 0X01; +/** File should hidden in directory listings */ +uint8_t const DIR_ATT_HIDDEN = 0X02; +/** Entry is for a system file */ +uint8_t const DIR_ATT_SYSTEM = 0X04; +/** Directory entry contains the volume label */ +uint8_t const DIR_ATT_VOLUME_ID = 0X08; +/** Entry is for a directory */ +uint8_t const DIR_ATT_DIRECTORY = 0X10; +/** Old DOS archive bit for backup support */ +uint8_t const DIR_ATT_ARCHIVE = 0X20; +/** Test value for long name entry. Test is + (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */ +uint8_t const DIR_ATT_LONG_NAME = 0X0F; +/** Test mask for long name entry */ +uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F; +/** defined attribute bits */ +uint8_t const DIR_ATT_DEFINED_BITS = 0X3F; +/** Directory entry is part of a long name */ +static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) { + return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME; +} +/** Mask for file/subdirectory tests */ +uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY); +/** Directory entry is for a file */ +static inline uint8_t DIR_IS_FILE(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0; +} +/** Directory entry is for a subdirectory */ +static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY; +} +/** Directory entry is for a file or subdirectory */ +static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) { + return (dir->attributes & DIR_ATT_VOLUME_ID) == 0; +} +#endif // FatStructs_h diff --git a/arduino-sources/SDFAT16/SdCard.cpp b/arduino-sources/SDFAT16/SdCard.cpp new file mode 100644 index 0000000..c112029 --- /dev/null +++ b/arduino-sources/SDFAT16/SdCard.cpp @@ -0,0 +1,277 @@ +/* Arduino FAT16 Library + * Copyright (C) 2008 by William Greiman + * + * This file is part of the Arduino FAT16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ +#include + +#include + +#include +#include +//------------------------------------------------------------------------------ +// r1 status values +uint8_t const R1_READY_STATE = 0; +uint8_t const R1_IDLE_STATE = 1; +// start data token for read or write +uint8_t const DATA_START_BLOCK = 0XFE; +// data response tokens for write block +uint8_t const DATA_RES_MASK = 0X1F; +uint8_t const DATA_RES_ACCEPTED = 0X05; +uint8_t const DATA_RES_CRC_ERROR = 0X0B; +uint8_t const DATA_RES_WRITE_ERROR = 0X0D; +// +// stop compiler from inlining where speed optimization is not required +#define STATIC_NOINLINE static __attribute__((noinline)) +//------------------------------------------------------------------------------ +// SPI static functions +// +// clock byte in +STATIC_NOINLINE uint8_t spiRec(void) { + SPDR = 0xff; + while (!(SPSR & (1 << SPIF))); + return SPDR; +} +// clock byte out +STATIC_NOINLINE void spiSend(uint8_t b) { + SPDR = b; + while (!(SPSR & (1 << SPIF))); +} +//------------------------------------------------------------------------------ +// wait for card to go not busy +// return false if timeout +static uint8_t waitForToken(uint8_t token, uint16_t timeoutMillis) { + uint16_t t0 = millis(); + while (spiRec() != token) { + if (((uint16_t)millis() - t0) > timeoutMillis) return false; + } + return true; +} +//------------------------------------------------------------------------------ +uint8_t SdCard::cardCommand(uint8_t cmd, uint32_t arg) { + uint8_t r1; + + // select card + chipSelectLow(); + + // wait if busy + waitForToken(0XFF, SD_COMMAND_TIMEOUT); + + // send command + spiSend(cmd | 0x40); + + // send argument + for (int8_t s = 24; s >= 0; s -= 8) spiSend(arg >> s); + + // send CRC - must send valid CRC for CMD0 + spiSend(cmd == CMD0 ? 0x95 : 0XFF); + + // wait for not busy + for (uint8_t retry = 0; (0X80 & (r1 = spiRec())) && retry != 0XFF; retry++); + return r1; +} +//------------------------------------------------------------------------------ +uint8_t SdCard::cardAcmd(uint8_t cmd, uint32_t arg) { + cardCommand(CMD55, 0); + return cardCommand(cmd, arg); +} +//============================================================================== +// SdCard member functions +//------------------------------------------------------------------------------ +/** + * Determine the size of a standard SD flash memory card + * \return The number of 512 byte data blocks in the card + */ +uint32_t SdCard::cardSize(void) { + uint16_t c_size; + csd_t csd; + if (!readReg(CMD9, &csd)) return 0; + uint8_t read_bl_len = csd.read_bl_len; + c_size = (csd.c_size_high << 10) | (csd.c_size_mid << 2) | csd.c_size_low; + uint8_t c_size_mult = (csd.c_size_mult_high << 1) | csd.c_size_mult_low; + return (uint32_t)(c_size+1) << (c_size_mult + read_bl_len - 7); +} +//------------------------------------------------------------------------------ +void SdCard::chipSelectHigh(void) { + digitalWrite(chipSelectPin_, HIGH); + // make sure MISO goes high impedance + spiSend(0XFF); +} +//------------------------------------------------------------------------------ +void SdCard::chipSelectLow(void) { + // Enable SPI, Master, clock rate F_CPU/4 + SPCR = (1 << SPE) | (1 << MSTR); + + // Doubled Clock Frequency to F_CPU/2 unless speed_ is nonzero + if (!speed_) SPSR |= (1 << SPI2X); + + digitalWrite(chipSelectPin_, LOW); +} +//------------------------------------------------------------------------------ +void SdCard::error(uint8_t code, uint8_t data) { + errorData = data; + error(code); +} +//------------------------------------------------------------------------------ +void SdCard::error(uint8_t code) { + errorCode = code; + chipSelectHigh(); +} +//------------------------------------------------------------------------------ +/** + * Initialize a SD flash memory card. + * + * \param[in] speed Set SPI Frequency to F_CPU/2 if speed = 0 or F_CPU/4 + * if speed = 1. + * \param[in] chipSelectPin SD chip select pin number. + * + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + * + */ +uint8_t SdCard::init(uint8_t speed, uint8_t chipSelectPin) { + if (speed > 1) { + error(SD_ERROR_SPI_SPEED); + return false; + } + speed_ = speed; + chipSelectPin_ = chipSelectPin; + errorCode = 0; + uint8_t r; + // 16-bit init start time allows over a minute + uint16_t t0 = (uint16_t)millis(); + + pinMode(chipSelectPin_, OUTPUT); + digitalWrite(chipSelectPin_, HIGH); + pinMode(SPI_MISO_PIN, INPUT); + pinMode(SPI_SS_PIN, OUTPUT); + pinMode(SPI_MOSI_PIN, OUTPUT); + pinMode(SPI_SCK_PIN, OUTPUT); + + // Enable SPI, Master, clock rate F_CPU/128 + SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0); + + // must supply min of 74 clock cycles with CS high. + for (uint8_t i = 0; i < 10; i++) spiSend(0XFF); + digitalWrite(chipSelectPin_, LOW); + + // command to go idle in SPI mode + while ((r = cardCommand(CMD0, 0)) != R1_IDLE_STATE) { + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_ERROR_CMD0, r); + return false; + } + } + // start initialization and wait for completed initialization + while ((r = cardAcmd(ACMD41, 0)) != R1_READY_STATE) { + if (((uint16_t)millis() - t0) > SD_INIT_TIMEOUT) { + error(SD_ERROR_ACMD41, r); + return false; + } + } + chipSelectHigh(); + return true; +} +//------------------------------------------------------------------------------ +/** + * Reads a 512 byte block from a storage device. + * + * \param[in] blockNumber Logical block to be read. + * \param[out] dst Pointer to the location that will receive the data. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +uint8_t SdCard::readBlock(uint32_t blockNumber, uint8_t* dst) { + if (cardCommand(CMD17, blockNumber << 9)) { + error(SD_ERROR_CMD17); + return false; + } + return readTransfer(dst, 512); +} +//------------------------------------------------------------------------------ +uint8_t SdCard::readReg(uint8_t cmd, void* buf) { + uint8_t* dst = reinterpret_cast(buf); + if (cardCommand(cmd, 0)) { + chipSelectHigh(); + return false; + } + return readTransfer(dst, 16); +} +//------------------------------------------------------------------------------ +uint8_t SdCard::readTransfer(uint8_t* dst, uint16_t count) { + // wait for start of data + if (!waitForToken(DATA_START_BLOCK, SD_READ_TIMEOUT)) { + error(SD_ERROR_READ_TIMEOUT); + } + // start first spi transfer + SPDR = 0XFF; + for (uint16_t i = 0; i < count; i++) { + while (!(SPSR & (1 << SPIF))); + dst[i] = SPDR; + SPDR = 0XFF; + } + // wait for first CRC byte + while (!(SPSR & (1 << SPIF))); + spiRec(); // second CRC byte + chipSelectHigh(); + return true; +} +//------------------------------------------------------------------------------ +/** + * Writes a 512 byte block to a storage device. + * + * \param[in] blockNumber Logical block to be written. + * \param[in] src Pointer to the location of the data to be written. + * \return The value one, true, is returned for success and + * the value zero, false, is returned for failure. + */ +uint8_t SdCard::writeBlock(uint32_t blockNumber, const uint8_t* src) { + uint32_t address = blockNumber << 9; +#if SD_PROTECT_BLOCK_ZERO + // don't allow write to first block + if (address == 0) { + error(SD_ERROR_BLOCK_ZERO_WRITE); + return false; + } +#endif // SD_PROTECT_BLOCK_ZERO + if (cardCommand(CMD24, address)) { + error(SD_ERROR_CMD24); + return false; + } + // optimize write loop + SPDR = DATA_START_BLOCK; + for (uint16_t i = 0; i < 512; i++) { + while (!(SPSR & (1 << SPIF))); + SPDR = src[i]; + } + while (!(SPSR & (1 << SPIF))); // wait for last data byte + spiSend(0xFF); // dummy crc + spiSend(0xFF); // dummy crc + + // get write response + uint8_t r1 = spiRec(); + if ((r1 & DATA_RES_MASK) != DATA_RES_ACCEPTED) { + error(SD_ERROR_WRITE_RESPONSE, r1); + return false; + } + // wait for card to complete write programming + if (!waitForToken(0XFF, SD_WRITE_TIMEOUT)) { + error(SD_ERROR_WRITE_TIMEOUT); + } + chipSelectHigh(); + return true; +} diff --git a/arduino-sources/SDFAT16/SdCard.h b/arduino-sources/SDFAT16/SdCard.h new file mode 100644 index 0000000..42f72b7 --- /dev/null +++ b/arduino-sources/SDFAT16/SdCard.h @@ -0,0 +1,192 @@ +/* Arduino FAT16 Library + * Copyright (C) 2008 by William Greiman + * + * This file is part of the Arduino FAT16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ +#ifndef SdCard_h +#define SdCard_h + /** + * \file + * SdCard class + */ +#include +//------------------------------------------------------------------------------ +// Warning only SD_CHIP_SELECT_PIN, the SD card select pin, may be redefined. +// define hardware SPI pins +#if defined(__AVR_ATmega168__)\ +||defined(__AVR_ATmega168P__)\ +||defined(__AVR_ATmega328P__) +// 168 and 328 Arduinos +/** Slave Select pin */ +uint8_t const SPI_SS_PIN = 10; +/** Master Out Slave In pin */ +uint8_t const SPI_MOSI_PIN = 11; +/** Master In Slave Out pin */ +uint8_t const SPI_MISO_PIN = 12; +/** Serial Clock */ +uint8_t const SPI_SCK_PIN = 13; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega1280__)\ +|| defined(__AVR_ATmega2560__) +// pins for Arduino Mega +uint8_t const SPI_SS_PIN = 53; +uint8_t const SPI_MOSI_PIN = 51; +uint8_t const SPI_MISO_PIN = 50; +uint8_t const SPI_SCK_PIN = 52; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega644P__)\ +|| defined(__AVR_ATmega644__)\ +|| defined(__AVR_ATmega1284P__) +// pins for Sanguino +uint8_t const SPI_SS_PIN = 4; +uint8_t const SPI_MOSI_PIN = 5; +uint8_t const SPI_MISO_PIN = 6; +uint8_t const SPI_SCK_PIN = 7; +//------------------------------------------------------------------------------ +#elif defined(__AVR_ATmega32U4__) +// pins for Teensy 2.0 +uint8_t const SPI_SS_PIN = 0; +uint8_t const SPI_MOSI_PIN = 2; +uint8_t const SPI_MISO_PIN = 3; +uint8_t const SPI_SCK_PIN = 1; +//------------------------------------------------------------------------------ +#elif defined(__AVR_AT90USB646__)\ +|| defined(__AVR_AT90USB1286__) +// pins for Teensy++ 1.0 & 2.0 +uint8_t const SPI_SS_PIN = 20; +uint8_t const SPI_MOSI_PIN = 22; +uint8_t const SPI_MISO_PIN = 23; +uint8_t const SPI_SCK_PIN = 21; +//------------------------------------------------------------------------------ +#else // SPI pins +#error unknown CPU +#endif // SPI pins +//------------------------------------------------------------------------------ +/** + * SD Chip Select pin + * + * Warning if this pin is redefined the hardware SS pin will be enabled + * as an output by init(). An avr processor will not function as an SPI + * master unless SS is set to output mode. + * + * For example to set SD_CHIP_SELECT_PIN to 8 for the SparkFun microSD shield: + * uint8_t const SD_CHIP_SELECT_PIN = 8; + * + * The default chip select pin for the SD card is SS. + */ +uint8_t const SD_CHIP_SELECT_PIN = SPI_SS_PIN; +//------------------------------------------------------------------------------ +/** command timeout ms */ +uint16_t const SD_COMMAND_TIMEOUT = 300; +/** init timeout ms */ +uint16_t const SD_INIT_TIMEOUT = 2000; +/** read timeout ms */ +uint16_t const SD_READ_TIMEOUT = 300; +/** write timeout ms */ +uint16_t const SD_WRITE_TIMEOUT = 600; +//------------------------------------------------------------------------------ +// error codes +/** Card did not go into SPI mode */ +uint8_t const SD_ERROR_CMD0 = 1; +/** Card did not go ready */ +uint8_t const SD_ERROR_ACMD41 = 2; +/** Write command not accepted */ +uint8_t const SD_ERROR_CMD24 = 3; +/** Read command not accepted */ +uint8_t const SD_ERROR_CMD17 = 4; +/** timeout waiting for read data */ +uint8_t const SD_ERROR_READ_TIMEOUT = 5; +/** write error occurred */ +uint8_t const SD_ERROR_WRITE_RESPONSE = 6; +/** timeout waiting for write status */ +uint8_t const SD_ERROR_WRITE_TIMEOUT = 7; +/** attempt to write block zero */ +uint8_t const SD_ERROR_BLOCK_ZERO_WRITE = 8; +/** card returned an error to a CMD13 status check after a write */ +uint8_t const SD_ERROR_WRITE_PROGRAMMING = 9; +/** invalid SPI speed in init() call */ +uint8_t const SD_ERROR_SPI_SPEED = 10; +//------------------------------------------------------------------------------ +// SD command codes +/** SEND OPERATING CONDITIONS */ +uint8_t const ACMD41 = 0X29; +/** GO_IDLE_STATE - init card in spi mode if CS low */ +uint8_t const CMD0 = 0X00; +/** SEND_CSD - Card Specific Data */ +uint8_t const CMD9 = 0X09; +/** SEND_CID - Card IDentification */ +uint8_t const CMD10 = 0X0A; +/** SEND_STATUS - read the card status register */ +uint8_t const CMD13 = 0X0D; +/** READ_BLOCK */ +uint8_t const CMD17 = 0X11; +/** WRITE_BLOCK */ +uint8_t const CMD24 = 0X18; +/** APP_CMD - escape for application specific command */ +uint8_t const CMD55 = 0X37; +//------------------------------------------------------------------------------ +/** + * \class SdCard + * \brief Hardware access class for SD flash cards + * + * Supports raw access to a standard SD flash memory card. + * + */ +class SdCard { + public: + /** Code for a SD error. See SdCard.h for definitions. */ + uint8_t errorCode; + /** Data that may be helpful in determining the cause of an error */ + uint8_t errorData; + uint32_t cardSize(void); + /** + * Initialize an SD flash memory card with default clock rate and chip + * select pin. See SdCard::init(uint8_t sckRateID, uint8_t chipSelectPin). + */ + uint8_t init(void) { + return init(0, SD_CHIP_SELECT_PIN); + } + /** + * Initialize an SD flash memory card with the selected SPI clock rate + * and the default SD chip select pin. + * See SdCard::init(uint8_t slow, uint8_t chipSelectPin). + */ + uint8_t init(uint8_t speed) { + return init(speed, SD_CHIP_SELECT_PIN); + } + uint8_t init(uint8_t speed, uint8_t chipselectPin); + uint8_t readBlock(uint32_t block, uint8_t* dst); + /** Read the CID register which contains info about the card. + * This includes Manufacturer ID, OEM ID, product name, version, + * serial number, and manufacturing date. */ + uint8_t readCID(cid_t* cid) { + return readReg(CMD10, cid); + } + uint8_t writeBlock(uint32_t block, const uint8_t* src); + private: + uint8_t cardAcmd(uint8_t cmd, uint32_t arg); + uint8_t cardCommand(uint8_t cmd, uint32_t arg); + uint8_t chipSelectPin_; + uint8_t speed_; + void chipSelectHigh(void); + void chipSelectLow(void); + void error(uint8_t code, uint8_t data); + void error(uint8_t code); + uint8_t readReg(uint8_t cmd, void* buf); + uint8_t readTransfer(uint8_t* dst, uint16_t count); +}; +#endif // SdCard_h diff --git a/arduino-sources/SDFAT16/SdInfo.h b/arduino-sources/SDFAT16/SdInfo.h new file mode 100644 index 0000000..5629547 --- /dev/null +++ b/arduino-sources/SDFAT16/SdInfo.h @@ -0,0 +1,117 @@ +/* Arduino FAT16 Library + * Copyright (C) 2008 by William Greiman + * + * This file is part of the Arduino FAT16 Library + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with the Arduino Fat16 Library. If not, see + * . + */ +#ifndef SdInfo_h +#define SdInfo_h +#include +// Based on the document: +// +// SD Specifications +// Part 1 +// Physical Layer +// Simplified Specification +// Version 2.00 +// September 25, 2006 +// +// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf +// +// Card IDentification (CID) register +typedef struct CID { + // byte 0 + uint8_t mid; // Manufacturer ID + // byte 1-2 + char oid[2]; // OEM/Application ID + // byte 3-7 + char pnm[5]; // Product name + // byte 8 + unsigned prv_m : 4; // Product revision n.m + unsigned prv_n : 4; + // byte 9-12 + uint32_t psn; // Product serial number + // byte 13 + unsigned mdt_year_high : 4; // Manufacturing date + unsigned reserved : 4; + // byte 14 + unsigned mdt_month : 4; + unsigned mdt_year_low :4; + // byte 15 + unsigned always1 : 1; + unsigned crc : 7; +}cid_t; +// Card-Specific Data register +typedef struct CSD { + // byte 0 + unsigned reserved1 : 6; + unsigned csd_ver : 2; + // byte 1 + uint8_t taac; + // byte 2 + uint8_t nsac; + // byte 3 + uint8_t tran_speed; + // byte 4 + uint8_t ccc_high; + // byte 5 + unsigned read_bl_len : 4; + unsigned ccc_low : 4; + // byte 6 + unsigned c_size_high : 2; + unsigned reserved2 : 2; + unsigned dsr_imp : 1; + unsigned read_blk_misalign :1; + unsigned write_blk_misalign : 1; + unsigned read_bl_partial : 1; + // byte 7 + uint8_t c_size_mid; + // byte 8 + unsigned vdd_r_curr_max : 3; + unsigned vdd_r_curr_min : 3; + unsigned c_size_low :2; + // byte 9 + unsigned c_size_mult_high : 2; + unsigned vdd_w_cur_max : 3; + unsigned vdd_w_curr_min : 3; + // byte 10 + unsigned sector_size_high : 6; + unsigned erase_blk_en : 1; + unsigned c_size_mult_low : 1; + // byte 11 + unsigned wp_grp_size : 7; + unsigned sector_size_low : 1; + // byte 12 + unsigned write_bl_len_high : 2; + unsigned r2w_factor : 3; + unsigned reserved3 : 2; + unsigned wp_grp_enable : 1; + // byte 13 + unsigned reserved4 : 5; + unsigned write_partial : 1; + unsigned write_bl_len_low : 2; + // byte 14 + unsigned reserved5: 2; + unsigned file_format : 2; + unsigned tmp_write_protect : 1; + unsigned perm_write_protect : 1; + unsigned copy : 1; + unsigned file_format_grp : 1; + // byte 15 + unsigned always1 : 1; + unsigned crc : 7; +}csd_t; +#endif // SdInfo_h diff --git a/arduino-sources/SPI/SPI.cpp b/arduino-sources/SPI/SPI.cpp new file mode 100644 index 0000000..5e48073 --- /dev/null +++ b/arduino-sources/SPI/SPI.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010 by Cristian Maglie + * SPI Master library for arduino. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#include "pins_arduino.h" +#include "SPI.h" + +SPIClass SPI; + +void SPIClass::begin() { + + // Set SS to high so a connected chip will be "deselected" by default + digitalWrite(SS, HIGH); + + // When the SS pin is set as OUTPUT, it can be used as + // a general purpose output port (it doesn't influence + // SPI operations). + pinMode(SS, OUTPUT); + + // Warning: if the SS pin ever becomes a LOW INPUT then SPI + // automatically switches to Slave, so the data direction of + // the SS pin MUST be kept as OUTPUT. + SPCR |= _BV(MSTR); + SPCR |= _BV(SPE); + + // Set direction register for SCK and MOSI pin. + // MISO pin automatically overrides to INPUT. + // By doing this AFTER enabling SPI, we avoid accidentally + // clocking in a single bit since the lines go directly + // from "input" to SPI control. + // http://code.google.com/p/arduino/issues/detail?id=888 + pinMode(SCK, OUTPUT); + pinMode(MOSI, OUTPUT); +} + + +void SPIClass::end() { + SPCR &= ~_BV(SPE); +} + +void SPIClass::setBitOrder(uint8_t bitOrder) +{ + if(bitOrder == LSBFIRST) { + SPCR |= _BV(DORD); + } else { + SPCR &= ~(_BV(DORD)); + } +} + +void SPIClass::setDataMode(uint8_t mode) +{ + SPCR = (SPCR & ~SPI_MODE_MASK) | mode; +} + +void SPIClass::setClockDivider(uint8_t rate) +{ + SPCR = (SPCR & ~SPI_CLOCK_MASK) | (rate & SPI_CLOCK_MASK); + SPSR = (SPSR & ~SPI_2XCLOCK_MASK) | ((rate >> 2) & SPI_2XCLOCK_MASK); +} + diff --git a/arduino-sources/SPI/SPI.h b/arduino-sources/SPI/SPI.h new file mode 100644 index 0000000..f647d5c --- /dev/null +++ b/arduino-sources/SPI/SPI.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010 by Cristian Maglie + * SPI Master library for arduino. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either the GNU General Public License version 2 + * or the GNU Lesser General Public License version 2.1, both as + * published by the Free Software Foundation. + */ + +#ifndef _SPI_H_INCLUDED +#define _SPI_H_INCLUDED + +#include +#include +#include + +#define SPI_CLOCK_DIV4 0x00 +#define SPI_CLOCK_DIV16 0x01 +#define SPI_CLOCK_DIV64 0x02 +#define SPI_CLOCK_DIV128 0x03 +#define SPI_CLOCK_DIV2 0x04 +#define SPI_CLOCK_DIV8 0x05 +#define SPI_CLOCK_DIV32 0x06 +//#define SPI_CLOCK_DIV64 0x07 + +#define SPI_MODE0 0x00 +#define SPI_MODE1 0x04 +#define SPI_MODE2 0x08 +#define SPI_MODE3 0x0C + +#define SPI_MODE_MASK 0x0C // CPOL = bit 3, CPHA = bit 2 on SPCR +#define SPI_CLOCK_MASK 0x03 // SPR1 = bit 1, SPR0 = bit 0 on SPCR +#define SPI_2XCLOCK_MASK 0x01 // SPI2X = bit 0 on SPSR + +class SPIClass { +public: + inline static byte transfer(byte _data); + + // SPI Configuration methods + + inline static void attachInterrupt(); + inline static void detachInterrupt(); // Default + + static void begin(); // Default + static void end(); + + static void setBitOrder(uint8_t); + static void setDataMode(uint8_t); + static void setClockDivider(uint8_t); +}; + +extern SPIClass SPI; + +byte SPIClass::transfer(byte _data) { + SPDR = _data; + while (!(SPSR & _BV(SPIF))) + ; + return SPDR; +} + +void SPIClass::attachInterrupt() { + SPCR |= _BV(SPIE); +} + +void SPIClass::detachInterrupt() { + SPCR &= ~_BV(SPIE); +} + +#endif diff --git a/arduino-sources/SerialCommand/SerialCommand.h b/arduino-sources/SerialCommand/SerialCommand.h new file mode 100644 index 0000000..6f4aa4b --- /dev/null +++ b/arduino-sources/SerialCommand/SerialCommand.h @@ -0,0 +1,94 @@ +/******************************************************************************* +SerialCommand - An Arduino library to tokenize and parse commands received over +a serial port. +Copyright (C) 2011-2013 Steven Cogswell +http://awtfy.com + +Version 20131021A. + +Version History: +May 11 2011 - Initial version +May 13 2011 - Prevent overwriting bounds of SerialCommandCallback[] array in addCommand() + defaultHandler() for non-matching commands +Mar 2012 - Some const char * changes to make compiler happier about deprecated warnings. + Arduino 1.0 compatibility (Arduino.h header) +Oct 2013 - SerialCommand object can be created using a SoftwareSerial object, for SoftwareSerial + support. Requires #include in your sketch even if you don't use + a SoftwareSerial port in the project. sigh. See Example Sketch for usage. +Oct 2013 - Conditional compilation for the SoftwareSerial support, in case you really, really + hate it and want it removed. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +***********************************************************************************/ +#ifndef SerialCommand_h +#define SerialCommand_h + + +#include "Arduino.h" + + +// If you want to use SerialCommand with the hardware serial port only, and want to disable +// SoftwareSerial support, and thus don't have to use "#include " in your +// sketches, then uncomment this define for SERIALCOMMAND_HARDWAREONLY, and comment out the +// corresponding #undef line. +// +// You don't have to use SoftwareSerial features if this is not defined, you can still only use +// the Hardware serial port, just that this way lets you get out of having to include +// the SoftwareSerial.h header. +#define SERIALCOMMAND_HARDWAREONLY 1 +//#undef SERIALCOMMAND_HARDWAREONLY + + + +#include + + +#define SERIALCOMMANDBUFFER 16 +#define MAXSERIALCOMMANDS 10 +#define MAXDELIMETER 2 + +#define SERIALCOMMANDDEBUG 1 +#undef SERIALCOMMANDDEBUG // Comment this out to run the library in debug mode (verbose messages) + +class SerialCommand +{ + public: + SerialCommand(); // Constructor + + void clearBuffer(); // Sets the command buffer to all '\0' (nulls) + char *next(); // returns pointer to next token found in command buffer (for getting arguments to commands) + void readSerial(); // Main entry point. + void addCommand(const char *, void(*)()); // Add commands to processing dictionary + void addDefaultHandler(void (*function)()); // A handler to call when no valid command received. + + private: + char inChar; // A character read from the serial stream + char buffer[SERIALCOMMANDBUFFER]; // Buffer of stored characters while waiting for terminator character + int bufPos; // Current position in the buffer + char delim[MAXDELIMETER]; // null-terminated list of character to be used as delimeters for tokenizing (default " ") + char term; // Character that signals end of command (default '\r') + char *token; // Returned token from the command buffer as returned by strtok_r + char *last; // State variable used by strtok_r during processing + typedef struct _callback { + char command[SERIALCOMMANDBUFFER]; + void (*function)(); + } SerialCommandCallback; // Data structure to hold Command/Handler function key-value pairs + int numCommand; + SerialCommandCallback CommandList[MAXSERIALCOMMANDS]; // Actual definition for command/handler array + void (*defaultHandler)(); // Pointer to the default handler function + int usingSoftwareSerial; // Used as boolean to see if we're using SoftwareSerial object or not +}; + +#endif //SerialCommand_h diff --git a/arduino-sources/prescaler.h b/arduino-sources/prescaler.h new file mode 100644 index 0000000..7ce5e8f --- /dev/null +++ b/arduino-sources/prescaler.h @@ -0,0 +1,118 @@ +/* + * prescaler.h + * + * Provides useful tools to manage the clock prescaler and issues + * related to time and delays. Allows to easily set the prescaler + * and get access to its value. Also provides alternative functions + * to the millis() and delay() functions. + * + * (c) 2008 Sofian Audry | info(@)sofianaudry(.)com + * http://sofianaudry.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ +#ifndef PRESCALER_INC +#define PRESCALER_INC + +#include "Arduino.h" + +/** + * Prescaler division +*/ +#define CLOCK_PRESCALER_1 (0x0) +#define CLOCK_PRESCALER_2 (0x1) +#define CLOCK_PRESCALER_4 (0x2) +#define CLOCK_PRESCALER_8 (0x3) +#define CLOCK_PRESCALER_16 (0x4) +#define CLOCK_PRESCALER_32 (0x5) +#define CLOCK_PRESCALER_64 (0x6) +#define CLOCK_PRESCALER_128 (0x7) +#define CLOCK_PRESCALER_256 (0x8) + +// Initialize global variable. +static uint8_t __clock_prescaler = (CLKPR & (_BV(CLKPS0) | _BV(CLKPS1) | _BV(CLKPS2) | _BV(CLKPS3))); + +inline void setClockPrescaler(uint8_t clockPrescaler) { + if (clockPrescaler <= CLOCK_PRESCALER_256) { + // Disable interrupts. + uint8_t oldSREG = SREG; + cli(); + + // Enable change. + CLKPR = _BV(CLKPCE); // write the CLKPCE bit to one and all the other to zero + + // Change clock division. + CLKPR = clockPrescaler; // write the CLKPS0..3 bits while writing the CLKPE bit to zero + + // Copy for fast access. + __clock_prescaler = clockPrescaler; + + // Recopy interrupt register. + SREG = oldSREG; + } +} + +inline uint8_t getClockPrescaler() { + return (__clock_prescaler); +} + +inline uint16_t getClockDivisionFactor() { + return ((uint16_t)(1 << __clock_prescaler)); +} + +/** + * Time in milliseconds. + * + * NOTE: This is the equivalent of the millis() function but it readjusts it according + * to the current clock division. As such, be careful of how you make use of it, in + * particular remember it will be wrong if the clock division factor is changed during the + * course of computation. Remember that you can reset the overflow counter by calling the + * init() function from wiring.h. + */ +inline unsigned long trueMillis() +{ + return millis() * getClockDivisionFactor(); +} + +inline unsigned long trueMicros() { + return micros() * getClockDivisionFactor(); +} + +// Waits for #ms# milliseconds. +// NOTE: Please see comment above. +inline void trueDelay(unsigned long ms) +{ + unsigned long start = trueMillis(); + while (trueMillis() - start < ms); +} + +/** + * Rescales given delay time according to division factor. Should be called before a call + * to delay(). Insures compatibility with function using delay(). + * Example use: + * delay( rescaleDelay(1000) ); // equivalent to wait(1000) + */ +inline unsigned long rescaleDuration(unsigned long d) { + return (d / getClockDivisionFactor()); +} + +/** + * Rescales given time (in milliseconds or microseconds) according to division factor. Should + * be called + */ +inline unsigned long rescaleTime(unsigned long t) { + return (t * getClockDivisionFactor()); +} + +#endif diff --git a/arduino-sources/rotary.cpp b/arduino-sources/rotary.cpp new file mode 100644 index 0000000..b052a17 --- /dev/null +++ b/arduino-sources/rotary.cpp @@ -0,0 +1,148 @@ +/* Rotary encoder handler for arduino. v1.1 + * + * Copyright 2011 Ben Buxton. Licenced under the GNU GPL Version 3. + * Contact: bb@cactii.net + * + * A typical mechanical rotary encoder emits a two bit gray code + * on 3 output pins. Every step in the output (often accompanied + * by a physical 'click') generates a specific sequence of output + * codes on the pins. + * + * There are 3 pins used for the rotary encoding - one common and + * two 'bit' pins. + * + * The following is the typical sequence of code on the output when + * moving from one step to the next: + * + * Position Bit1 Bit2 + * ---------------------- + * Step1 0 0 + * 1/4 1 0 + * 1/2 1 1 + * 3/4 0 1 + * Step2 0 0 + * + * From this table, we can see that when moving from one 'click' to + * the next, there are 4 changes in the output code. + * + * - From an initial 0 - 0, Bit1 goes high, Bit0 stays low. + * - Then both bits are high, halfway through the step. + * - Then Bit1 goes low, but Bit2 stays high. + * - Finally at the end of the step, both bits return to 0. + * + * Detecting the direction is easy - the table simply goes in the other + * direction (read up instead of down). + * + * To decode this, we use a simple state machine. Every time the output + * code changes, it follows state, until finally a full steps worth of + * code is received (in the correct order). At the final 0-0, it returns + * a value indicating a step in one direction or the other. + * + * It's also possible to use 'half-step' mode. This just emits an event + * at both the 0-0 and 1-1 positions. This might be useful for some + * encoders where you want to detect all positions. + * + * If an invalid state happens (for example we go from '0-1' straight + * to '1-0'), the state machine resets to the start until 0-0 and the + * next valid codes occur. + * + * The biggest advantage of using a state machine over other algorithms + * is that this has inherent debounce built in. Other algorithms emit spurious + * output with switch bounce, but this one will simply flip between + * sub-states until the bounce settles, then continue along the state + * machine. + * A side effect of debounce is that fast rotations can cause steps to + * be skipped. By not requiring debounce, fast rotations can be accurately + * measured. + * Another advantage is the ability to properly handle bad state, such + * as due to EMI, etc. + * It is also a lot simpler than others - a static state table and less + * than 10 lines of logic. + */ + +#include "Arduino.h" +#include "rotary.h" + +/* + * The below state table has, for each state (row), the new state + * to set based on the next encoder output. From left to right in, + * the table, the encoder outputs are 00, 01, 10, 11, and the value + * in that position is the new state to set. + */ + +#define R_START 0x0 + +#ifdef HALF_STEP +// Use the half-step state table (emits a code at 00 and 11) +#define R_CCW_BEGIN 0x1 +#define R_CW_BEGIN 0x2 +#define R_START_M 0x3 +#define R_CW_BEGIN_M 0x4 +#define R_CCW_BEGIN_M 0x5 +const unsigned char ttable[6][4] = { + // R_START (00) + {R_START_M, R_CW_BEGIN, R_CCW_BEGIN, R_START}, + // R_CCW_BEGIN + {R_START_M | DIR_CCW, R_START, R_CCW_BEGIN, R_START}, + // R_CW_BEGIN + {R_START_M | DIR_CW, R_CW_BEGIN, R_START, R_START}, + // R_START_M (11) + {R_START_M, R_CCW_BEGIN_M, R_CW_BEGIN_M, R_START}, + // R_CW_BEGIN_M + {R_START_M, R_START_M, R_CW_BEGIN_M, R_START | DIR_CW}, + // R_CCW_BEGIN_M + {R_START_M, R_CCW_BEGIN_M, R_START_M, R_START | DIR_CCW}, +}; +#else +// Use the full-step state table (emits a code at 00 only) +#define R_CW_FINAL 0x1 +#define R_CW_BEGIN 0x2 +#define R_CW_NEXT 0x3 +#define R_CCW_BEGIN 0x4 +#define R_CCW_FINAL 0x5 +#define R_CCW_NEXT 0x6 + +const unsigned char ttable[7][4] = { + // R_START + {R_START, R_CW_BEGIN, R_CCW_BEGIN, R_START}, + // R_CW_FINAL + {R_CW_NEXT, R_START, R_CW_FINAL, R_START | DIR_CW}, + // R_CW_BEGIN + {R_CW_NEXT, R_CW_BEGIN, R_START, R_START}, + // R_CW_NEXT + {R_CW_NEXT, R_CW_BEGIN, R_CW_FINAL, R_START}, + // R_CCW_BEGIN + {R_CCW_NEXT, R_START, R_CCW_BEGIN, R_START}, + // R_CCW_FINAL + {R_CCW_NEXT, R_CCW_FINAL, R_START, R_START | DIR_CCW}, + // R_CCW_NEXT + {R_CCW_NEXT, R_CCW_FINAL, R_CCW_BEGIN, R_START}, +}; +#endif + +/* + * Constructor. Each arg is the pin number for each encoder contact. + */ +Rotary::Rotary(char _pin1, char _pin2) { + // Assign variables. + pin1 = _pin1; + pin2 = _pin2; + // Set pins to input. + pinMode(pin1, INPUT); + pinMode(pin2, INPUT); +#ifdef ENABLE_PULLUPS + digitalWrite(pin1, HIGH); + digitalWrite(pin2, HIGH); +#endif + // Initialise state. + state = R_START; +} + +unsigned char Rotary::process() { + // Grab state of input pins. + unsigned char pinstate = (digitalRead(pin2) << 1) | digitalRead(pin1); + // Determine new state from the pins and state table. + state = ttable[state & 0xf][pinstate]; + // Return emit bits, ie the generated event. + return state & 0x30; +} diff --git a/arduino-sources/rotary.h b/arduino-sources/rotary.h new file mode 100644 index 0000000..cfe75fe --- /dev/null +++ b/arduino-sources/rotary.h @@ -0,0 +1,37 @@ +/* + * Rotary encoder library for Arduino. + */ + +#ifndef rotary_h +#define rotary_h + +#include "Arduino.h" + +// Enable this to emit codes twice per step. +//#define HALF_STEP + +// Enable weak pullups +#define ENABLE_PULLUPS + +// Values returned by 'process' +// No complete step yet. +#define DIR_NONE 0x0 +// Clockwise step. +#define DIR_CW 0x10 +// Anti-clockwise step. +#define DIR_CCW 0x20 + +class Rotary +{ + public: + Rotary(char, char); + // Process pin(s) + unsigned char process(); + private: + unsigned char state; + unsigned char pin1; + unsigned char pin2; +}; + +#endif +