From a08c8e447fde5c4c4a811a3142f4a2a3718ebca8 Mon Sep 17 00:00:00 2001 From: uvos Date: Wed, 9 Mar 2022 10:24:23 +0100 Subject: [PATCH] inital nfc code --- CMakeLists.txt | 4 +- inputshiftreg.h | 75 ++++++++ main.cpp | 20 ++- mfrc522.cpp | 450 ++++++++++++++++++++++++++++++++++++++++++++++++ mfrc522.h | 187 ++++++++++++++++++++ nfcbord.cpp | 150 ++++++++++++++++ nfcbord.h | 26 +++ serial.h | 2 +- shiftreg.h | 81 +++++++++ softspim.cpp | 64 +++++++ softspim.h | 30 ++++ staticvector.h | 16 +- traindispatch.h | 5 + 13 files changed, 1091 insertions(+), 19 deletions(-) create mode 100644 inputshiftreg.h create mode 100644 mfrc522.cpp create mode 100644 mfrc522.h create mode 100644 nfcbord.cpp create mode 100644 nfcbord.h create mode 100644 shiftreg.h create mode 100644 softspim.cpp create mode 100644 softspim.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bee39c..fb20032 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(PORT_SPEED "57600" CACHE STRING "Serial Port Speed") set(PROGRAMMER "stk500v1" CACHE STRING "Programmer Type") set(COMPILE_FLAGS "" CACHE STRING "Additional Compiler Flags") -set(SRC_FILES main.cpp serial.cpp train.cpp item.cpp turnout.cpp signal.cpp) +set(SRC_FILES main.cpp serial.cpp train.cpp item.cpp turnout.cpp signal.cpp softspim.cpp nfcbord.cpp mfrc522.cpp) # Compiler suite specification set(CMAKE_C_COMPILER /usr/bin/avr-gcc) @@ -29,7 +29,7 @@ set(CMAKE_LINKER /usr/bin/avr-ld) # Compiler flags add_definitions(-mmcu=${MCU} -DF_CPU=${CPU_SPEED}) -add_definitions(-s -c -g -Os -Wall -std=c++17 ) +add_definitions(-s -c -g -O2 -Wall -std=c++17 ) add_definitions(-fno-exceptions -ffunction-sections -fdata-sections) # Linker flags diff --git a/inputshiftreg.h b/inputshiftreg.h new file mode 100644 index 0000000..925b818 --- /dev/null +++ b/inputshiftreg.h @@ -0,0 +1,75 @@ +#pragma once + +template class InputShiftReg +{ +private: + static constexpr int BYTES = (BITS % 8 == 0) ? (BITS/8) : (BITS/8+1); + + static constexpr bool invert = true; + static constexpr bool invertInput = false; + + volatile unsigned char *_port; + volatile unsigned char *_pin; + const unsigned char _pinSerOut; + const unsigned char _pinClk; + const unsigned char _pinParallelLoad; + + unsigned char data[BYTES]; + +public: + + InputShiftReg(volatile unsigned char* const portReg, volatile unsigned char* const pinReg, + const unsigned char clk, const unsigned char serOut, const unsigned char parallelLoad): + _port(portReg), _pin(pinReg), _pinSerOut(serOut), _pinClk(clk), _pinParallelLoad(parallelLoad) + { + } + bool getBit(unsigned char bit) + { + if(BITS <= bit) + return false; + bit = BITS - bit - 1; + return data[bit/8] & (1<<(bit%8)); + } + unsigned char* read() + { + if constexpr(invert) + { + *_port &= ~(1<<_pinParallelLoad); + *_port |= (1<<_pinParallelLoad); + *_port &= ~(1<<_pinParallelLoad); + + for(unsigned char i = 0; i < BITS; ++i) + { + *_port &= ~(1 << _pinClk); + bool value = *_pin & (1 << _pinSerOut); + if constexpr(invertInput) + value = !value; + if(value) + data[i/8] |= (1<<(i%8)); + else + data[i/8] &= ~(1<<(i%8)); + *_port |= (1 << _pinClk); + } + } + else + { + *_port |= (1<<_pinParallelLoad); + *_port &= ~(1<<_pinParallelLoad); + *_port |= (1<<_pinParallelLoad); + + for(unsigned char i = 0; i < BITS; ++i) + { + *_port |= (1 << _pinClk); + bool value = *_pin & (1 << _pinSerOut); + if constexpr(invertInput) + value = !value; + if(value) + data[i/8] |= (1<<(i%8)); + else + data[i/8] &= ~(1<<(i%8)); + *_port &= ~(1 << _pinClk); + } + } + return data; + } +}; diff --git a/main.cpp b/main.cpp index dd1629c..17aa44f 100644 --- a/main.cpp +++ b/main.cpp @@ -12,19 +12,17 @@ #include "staticvector.h" #include "turnout.h" #include "signal.h" - -#define COMMAND_BUFFER_SIZE 64 -#define SNPRINTF_BUFFER_SIZE 128 -#define EPPROM_SIZE 1024 +#include "shiftreg.h" +#include "softspim.h" +#include "nfcbord.h" +#include "defines.h" char buffer[SNPRINTF_BUFFER_SIZE]; -static constexpr uint8_t EEPROM_RESERVE = 32; -static constexpr uint8_t BLOCK = 4; - SVector trains; SVector turnouts; SVector signals; +NfcBoard nfcBoard; bool autoff = true; bool powerIsOn = true; @@ -252,6 +250,12 @@ void serialDispatch(Serial* serial) if(token != NULL) ret = signalDispatch(token, serial); } + else if(strcmp(token, "nfc") == 0) + { + token = strtok(NULL, " "); + if(token != NULL) + ret = nfcBoard.dispatch(token, serial); + } else if(strncmp(token, "erase", 4) == 0) { for(uint16_t i = 0; i < EPPROM_SIZE; i++) EEPROM_write_char(i, 0); @@ -327,7 +331,7 @@ int main() TCNT0 = 0; TCCR0B = (1<setBit(_csPin, false); + _spi->readWrite((addr << 1) | (1 << 7)); + uint8_t res = _spi->readWrite(); + _csReg->setBit(_csPin, true); + return res; +} + +void Mfrc522::read(uint8_t addr, uint8_t* data, uint8_t datalen, uint8_t rxAlign) +{ + _csReg->setBit(_csPin, false); + _spi->readWrite(addr << 1 | (1 << 7)); + for(uint8_t i = 0; i < datalen; ++i) + { + if(i == 0 && rxAlign) + { + uint8_t mask = 0; + for (uint8_t j = rxAlign; j <= 7; ++j) + mask |= (1 << j); + uint8_t value = _spi->readWrite(); + data[0] = (data[0] & ~mask) | (value & mask); + } + else + { + data[i] = _spi->readWrite(); + } + } + _csReg->setBit(_csPin, true); +} + +void Mfrc522::write(uint8_t addr, uint8_t data) +{ + _csReg->setBit(_csPin, false); + _spi->readWrite(addr << 1); + _spi->readWrite(data); + _csReg->setBit(_csPin, true); +} + +void Mfrc522::write(uint8_t addr, uint8_t* data, uint8_t datalen) +{ + _csReg->setBit(_csPin, false); + _spi->readWrite(addr << 1); + _spi->readWrite(datalen, nullptr, data); + _csReg->setBit(_csPin, true); +} +void Mfrc522::updateBit(uint8_t addr, uint8_t bit, bool value) +{ + uint8_t old = read(addr); + write(addr, value ? old | (1 << bit) : old & ~(1 << bit)); +} + +void (*_tagEnterdCb)(Mfrc522*, void*); +void* _userData; + +Mfrc522::Mfrc522(SpiMaster* spi, ShiftReg* csReg, uint8_t csPin, + void (*tagEnterdCb)(Mfrc522*, void*), void* userData): + _csReg(csReg), _spi(spi), _csPin(csPin), _tagEnterdCb(tagEnterdCb), _userData(userData) +{ + write(CommandReg, SOFTRESET); + _delay_ms(100); + + write(TModeReg, 0x80); + write(TPrescalerReg, 0xA9); + write(TReloadRegH, 0x03); + write(TReloadRegL, 0xE8); + write(ModWidthReg, 0x26); + + write(RFCfgReg, 0b111 << 4); //set gain to 48dB + + write(TxAutoReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting + write(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) + //write(DivIEnReg, 0b10010000); // enable MfinActIrq as push-pull + //write(ComIEnReg, 0b00100000); // enable Rx irq + + setRf(true); +} + +uint8_t Mfrc522::calculateCrc(uint8_t *data, uint8_t length, uint16_t *result) +{ + write(CommandReg, IDLE); // Stop any active command. + write(DivIrqReg, 0x04); // Clear the CRCIRq interrupt request bit + updateBit(FIFOLevelReg, 7, true); // FlushBuffer = 1, FIFO initialization + write(FIFODataReg, data, length); // Write data to the FIFO + write(CommandReg, CALCCRC); // Start the calculation + + // Wait for the CRC calculation to complete. + uint16_t i = 5000; + while(!(read(DivIrqReg) & 0x04) && i != 0) + --i; + + if(i == 0) + return ERR; + write(CommandReg, IDLE); + + *result = read(CRCResultRegL); + *result = read(CRCResultRegH) << 8; + return 0; +} + +uint8_t Mfrc522::commuicateWithTag(uint8_t command, uint8_t waitIrq, + uint8_t *sendData, uint8_t sendLen, + uint8_t *recvData, uint8_t *recvLen, + uint8_t validBits, uint8_t rxAlign, + uint8_t *rxValidBits) +{ + write(CommandReg, IDLE); + write(ComIrqReg, 0b01111111); // clear irqs + write(FIFOLevelReg, 1 << 7); // Flush fifo Buffer; + write(FIFODataReg, sendData, sendLen); // Fill fifo + write(BitFramingReg, (rxAlign << 4) + validBits); + write(CommandReg, command); // Execute the command + if (command == TRANSCEIVE) + updateBit(BitFramingReg, 7, true); + + uint16_t i = 2000; + uint8_t irq = read(ComIrqReg); + while(irq & waitIrq) + { + irq = read(ComIrqReg); + if(irq & 0x01 || --i == 0) + { + if(serial) + serial->write_p(PSTR("timeout\n")); + return TIMEOUT; + } + } + + uint8_t errorRegValue = read(ErrorReg); + if (errorRegValue & 0b00010011) // BufferOvfl ParityErr ProtocolErr + { + if(serial) + serial->write_p(PSTR("BufferOvfl ParityErr ProtocolErr\n")); + return ERR; + } + + if (recvData && recvLen) + { + uint8_t fifoBites = read(FIFOLevelReg); + + if(serial) + { + serial->write_p(PSTR("fifo has ")); + serial->write((int)fifoBites); + serial->write_p(PSTR(" bytes\n")); + } + if(fifoBites > *recvLen) + return LEN; + *recvLen = fifoBites; + read(FIFODataReg, recvData, fifoBites, rxAlign); + if(rxValidBits) + *rxValidBits = read(ControlReg) & 0x07; + } + + if(errorRegValue & 0x08) + { + if(serial) + serial->write_p(PSTR("collision err\n")); + return COLLISION; + } + return 0; +} + +uint8_t Mfrc522::transceive(uint8_t *sendData, uint8_t sendLen, uint8_t *recvData, uint8_t *recvLen, + uint8_t validBits, uint8_t rxAlign, uint8_t *rxValidBits) +{ + uint8_t waitIRq = 0x30; // RxIRq and IdleIRq + return commuicateWithTag(TRANSCEIVE, waitIRq, sendData, sendLen, recvData, + recvLen, validBits, rxAlign, rxValidBits); +} + +uint8_t Mfrc522::wakeupTag(uint8_t* bufferATQA, uint8_t *bufferLen) +{ + if(*bufferLen < 2) + return ERR; + + updateBit(CollReg, 7, false); + uint8_t data = PICC_CMD_WUPA; + uint8_t ret = transceive(&data, 1, bufferATQA, bufferLen, 7); + + if(*bufferLen != 2) + return ERR; + + return ret; +} + +uint8_t Mfrc522::selectTag(Uid *uid) +{ + bool uidComplete; + bool selectDone; + uint8_t cascadeLevel = 0; + uint8_t result; + uint8_t count; + uint8_t checkBit; + uint8_t index; + uint8_t uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + uint8_t buffer[9]; // The SELECT/ANTICOLLISION commands uses a 7 uint8_t standard frame + 2 uint8_ts CRC_A + uint8_t bufferUsed; // The number of uint8_ts used in the buffer, ie the number of uint8_ts to transfer to the FIFO + uint8_t txLastBits; // Used in BitFramingReg. The number of valid bits in the last transmitted uint8_t. + uint8_t *responseBuffer; + uint8_t responseLength; + + if(serial) + serial->write_p(PSTR("Select\n")); + + // 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 + + // Prepare MFRC522 + updateBit(CollReg, 7, false); // ValuesAfterColl=1 => Bits received after collision are cleared. + + // Repeat Cascade Level loop until we have a complete UID. + uidComplete = false; + currentLevelKnownBits = 0; + 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 0: + buffer[0] = PICC_CMD_SEL_CL1; + uidIndex = 0; + break; + + case 1: + buffer[0] = PICC_CMD_SEL_CL2; + uidIndex = 3; + break; + + case 2: + buffer[0] = PICC_CMD_SEL_CL3; + uidIndex = 6; + break; + + default: + if(serial) + serial->write_p(PSTR("err cascadeLevel\n")); + return ERR; + break; + } + + // Copy the known bits from uid->uidByte[] to buffer[] + index = 2; // destination index in buffer[] + // The number of bytes needed to represent the known bits for this level. + uint8_t bytesToCopy = currentLevelKnownBits / 8 + (currentLevelKnownBits % 8 ? 1 : 0); + if(bytesToCopy) + { + // Max 4 bytes in each Cascade Level. Only 3 left if we use the Cascade Tag + uint8_t maxBytes = 4; + if (bytesToCopy > maxBytes) + bytesToCopy = maxBytes; + for (count = 0; count < bytesToCopy; count++) + buffer[index++] = uid->uidByte[uidIndex + count]; + } + + // 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. + { + buffer[1] = 0x70; // NVB - Number of Valid Bits: Seven whole bytes + // Calculate BCC - Block Check Character + buffer[6] = buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; + // Calculate CRC_A + result = calculateCrc(buffer, 7, reinterpret_cast(&buffer[7])); + if (result != 0) + { + if(serial) + serial->write_p(PSTR("err calculateCrc\n")); + 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. + { + 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 + uint8_t rxAlign = txLastBits; + // RxAlign = BitFramingReg[6..4]. TxLastBits = BitFramingReg[2..0] + write(BitFramingReg, (rxAlign << 4) + txLastBits); + + if(serial) + { + serial->write_p(PSTR("entering transceive ")); + serial->write((int)responseLength); + serial->putChar(' '); + serial->write((int)txLastBits); + serial->putChar(' '); + serial->write((int)currentLevelKnownBits); + serial->putChar('\n'); + } + + // Transmit the buffer and receive the response. + result = transceive(buffer, bufferUsed, responseBuffer, &responseLength, txLastBits, rxAlign, &txLastBits); + if (result == COLLISION) // More than one PICC in the field => collision. + { + result = read(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (result & 0x20) + { + if(serial) + serial->write_p(PSTR("err collision\n")); + return COLLISION; // Without a valid collision position we cannot continue + } + uint8_t collisionPos = result & 0x1F; // Values 0-31, 0 means bit 32. + if (collisionPos == 0) { + collisionPos = 32; + } + if (collisionPos <= currentLevelKnownBits) // No progress - should not happen + { + if(serial) + serial->write_p(PSTR("err No progress\n")); + return ERR; + } + // Choose the PICC with the bit set. + currentLevelKnownBits = collisionPos; + count = currentLevelKnownBits % 8; // The bit to modify + checkBit = (currentLevelKnownBits - 1) % 8; + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << checkBit); + } + else if (result != 0) + { + if(serial) + serial->write_p(PSTR("err transceive\n")); + return result; + } + else + { + if (currentLevelKnownBits >= 32) // This was a SELECT. + selectDone = true; + else // This was an ANTICOLLISION. Run loop again to do the SELECT. + currentLevelKnownBits = 32; + } + } + + // 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). + { + if(serial) + { + serial->write_p(PSTR("err SAK ")); + serial->write((int)responseLength); + serial->putChar(' '); + serial->write((int)txLastBits); + serial->putChar('\n'); + } + return ERR; + } + // Verify CRC_A - do our own calculation and store the control in buffer[2..3] - those bytes are not needed anymore. + result = calculateCrc(responseBuffer, 1, reinterpret_cast(&buffer[2])); + if (result != 0) + return result; + if ((buffer[2] != responseBuffer[1]) || (buffer[3] != responseBuffer[2])) + return CRC; + 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 + 2; + + return 0; +} + +void Mfrc522::irq() +{ + +} + +void Mfrc522::setRf(bool on) +{ + uint8_t value = read(TxControlReg); + if(on && (value & 0x03) != 0x03) + write(TxControlReg, value | 0x03); + else if(!on && (value & 0x03) != 0x00) + write(TxControlReg, value & ~0x03); +} + +bool Mfrc522::cardPresent() +{ + uint8_t bufferATQA[2]; + uint8_t bufferLen = sizeof(bufferATQA); + uint8_t ret = wakeupTag(bufferATQA, &bufferLen); + return ret == 0 || ret == COLLISION; +} + +bool Mfrc522::probe(SpiMaster* spi, ShiftReg* csReg, uint8_t csPin) +{ + csReg->setBit(csPin, false); + spi->readWrite((VersionReg << 1) | (1 << 7)); + uint8_t version = spi->readWrite(); + csReg->setBit(csPin, true); + return version == 0x91 || version == 0x92; +} diff --git a/mfrc522.h b/mfrc522.h new file mode 100644 index 0000000..43737ee --- /dev/null +++ b/mfrc522.h @@ -0,0 +1,187 @@ +#pragma once +#include "shiftreg.h" +#include "softspim.h" +#include "defines.h" +#include "serial.h" + +class Mfrc522 +{ +public: + // Error codes. + static constexpr uint8_t OK = 0; // Everything A-OK. + static constexpr uint8_t NOTAGERR = 1; // No tag error + static constexpr uint8_t TIMEOUT = 2; // Timeout error + static constexpr uint8_t LEN = 3; // Buffer length error + static constexpr uint8_t COLLISION = 4; // Chip collision + static constexpr uint8_t CRC = 5; // CRC incorrect collision + static constexpr uint8_t ERR = 6; // General error + + // Command words + static constexpr uint8_t IDLE = 0x00; // NO action; Cancel the current command + static constexpr uint8_t MEM = 0x01; // Store 25 byte into the internal buffer. + static constexpr uint8_t GENID = 0x02; // Generates a 10 byte random ID number. + static constexpr uint8_t CALCCRC = 0x03; // CRC Calculate or selftest. + static constexpr uint8_t TRANSMIT = 0x04; // Transmit data + static constexpr uint8_t NOCMDCH = 0x07; // No command change. + static constexpr uint8_t RECEIVE = 0x08; // Receive Data + static constexpr uint8_t TRANSCEIVE = 0x0C; // Transmit and receive data + static constexpr uint8_t AUTHENT = 0x0E; // Authentication Key + static constexpr uint8_t SOFTRESET = 0x0F; // Reset + + // Tag command words + static constexpr uint8_t MF1_REQIDL = 0x26; // find the antenna area does not enter hibernation + static constexpr uint8_t MF1_REQALL = 0x52; // find all the tags antenna area + static constexpr uint8_t MF1_ANTICOLL = 0x93; // anti-collision + static constexpr uint8_t MF1_SELECTTAG = 0x93; // ??? election tag + static constexpr uint8_t MF1_AUTHENT1A = 0x60; // authentication key A + static constexpr uint8_t MF1_AUTHENT1B = 0x61; // authentication key B + static constexpr uint8_t MF1_READ = 0x30; // Read Block + static constexpr uint8_t MF1_WRITE = 0xA0; // write block + static constexpr uint8_t MF1_DECREMENT = 0xC0; // debit + static constexpr uint8_t MF1_INCREMENT = 0xC1; // recharge + static constexpr uint8_t MF1_RESTORE = 0xC2; // transfer block data to the buffer + static constexpr uint8_t MF1_TRANSFER = 0xB0; // save the data in the buffer + static constexpr uint8_t MF1_HALT = 0x50; // Sleep + + //Page 0:Command and Status + static constexpr uint8_t CommandReg = 0x01; + static constexpr uint8_t ComIEnReg = 0x02; + static constexpr uint8_t DivIEnReg = 0x03; + static constexpr uint8_t ComIrqReg = 0x04; + static constexpr uint8_t DivIrqReg = 0x05; + static constexpr uint8_t ErrorReg = 0x06; + static constexpr uint8_t Status1Reg = 0x07; + static constexpr uint8_t Status2Reg = 0x08; + static constexpr uint8_t FIFODataReg = 0x09; + static constexpr uint8_t FIFOLevelReg = 0x0A; + static constexpr uint8_t WaterLevelReg = 0x0B; + static constexpr uint8_t ControlReg = 0x0C; + static constexpr uint8_t BitFramingReg = 0x0D; + static constexpr uint8_t CollReg = 0x0E; + //Page 1:Command + static constexpr uint8_t ModeReg = 0x11; + static constexpr uint8_t TxModeReg = 0x12; + static constexpr uint8_t RxModeReg = 0x13; + static constexpr uint8_t TxControlReg = 0x14; + static constexpr uint8_t TxAutoReg = 0x15; + static constexpr uint8_t TxSelReg = 0x16; + static constexpr uint8_t RxSelReg = 0x17; + static constexpr uint8_t RxThresholdReg= 0x18; + static constexpr uint8_t DemodReg = 0x19; + static constexpr uint8_t MifareReg = 0x1C; + static constexpr uint8_t SerialSpeedReg= 0x1F; + //Page 2:CFG + static constexpr uint8_t CRCResultRegH = 0x21; + static constexpr uint8_t CRCResultRegL = 0x22; + static constexpr uint8_t Reserved21 = 0x23; + static constexpr uint8_t ModWidthReg = 0x24; + static constexpr uint8_t RFCfgReg = 0x26; + static constexpr uint8_t GsNReg = 0x27; + static constexpr uint8_t CWGsPReg = 0x28; + static constexpr uint8_t ModGsPReg = 0x29; + static constexpr uint8_t TModeReg = 0x2A; + static constexpr uint8_t TPrescalerReg = 0x2B; + static constexpr uint8_t TReloadRegH = 0x2C; + static constexpr uint8_t TReloadRegL = 0x2D; + static constexpr uint8_t TCounterValueRegH = 0x2E; + static constexpr uint8_t TCounterValueRegL = 0x2F; + //Page 3:TestRegister + static constexpr uint8_t TestSel1Reg = 0x31; + static constexpr uint8_t TestSel2Reg = 0x32; + static constexpr uint8_t TestPinEnReg = 0x33; + static constexpr uint8_t TestPinValueReg = 0x34; + static constexpr uint8_t TestBusReg = 0x35; + static constexpr uint8_t AutoTestReg = 0x36; + static constexpr uint8_t VersionReg = 0x37; + static constexpr uint8_t AnalogTestReg = 0x38; + static constexpr uint8_t TestDAC1Reg = 0x39; + static constexpr uint8_t TestDAC2Reg = 0x3A; + static constexpr uint8_t TestADCReg = 0x3B; + + // PICC (tag) commands + // The commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4) + + // REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame. + static constexpr uint8_t PICC_CMD_REQA = 0x26; + // 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. + static constexpr uint8_t PICC_CMD_WUPA = 0x52; // Cascade Tag. Not really a command, but used during anti collision. + static constexpr uint8_t PICC_CMD_CT = 0x88; + static constexpr uint8_t PICC_CMD_SEL_CL1 = 0x93; // Anti collision/Select, Cascade Level 1 + static constexpr uint8_t PICC_CMD_SEL_CL2 = 0x95; // Anti collision/Select, Cascade Level 1 + static constexpr uint8_t PICC_CMD_SEL_CL3 = 0x97; // Anti collision/Select, Cascade Level 1 + // 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. + static constexpr uint8_t PICC_CMD_HLTA = 0x50; + static constexpr uint8_t PICC_CMD_MF_AUTH_KEY_A = 0x60; // Perform authentication with Key A + static constexpr uint8_t PICC_CMD_MF_AUTH_KEY_B = 0x61; // Perform authentication with Key B + // Reads one 16 byte block from the authenticated sector of the PICC. Also used for MIFARE Ultralight. + static constexpr uint8_t PICC_CMD_MF_READ = 0x30; + // Writes one 16 byte block to the authenticated sector of the PICC. Called "COMPATIBILITY WRITE" for MIFARE Ultralight. + static constexpr uint8_t PICC_CMD_MF_WRITE = 0xA0; + // Decrements the contents of a block and stores the result in the internal data register. + static constexpr uint8_t PICC_CMD_MF_DECREMENT = 0xC0; + // Increments the contents of a block and stores the result in the internal data register. + static constexpr uint8_t PICC_CMD_MF_INCREMENT = 0xC1; + // Reads the contents of a block into the internal data register. + static constexpr uint8_t PICC_CMD_MF_RESTORE = 0xC2; + // Writes the contents of the internal data register to a block. + static constexpr uint8_t PICC_CMD_MF_TRANSFER = 0xB0; + // 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. + static constexpr uint8_t PICC_CMD_UL_WRITE = 0xA2; // Writes one 4 byte page to the PICC + + typedef struct + { + uint8_t size; // Number of bytes in the UID. 4, 7 or 10. + uint8_t uidByte[10]; + uint8_t sak; // The SAK (Select acknowledge) byte returned from the PICC after successful selection. + } Uid; + + +private: + ShiftReg* _csReg; + SpiMaster* _spi; + + uint8_t _csPin; + + uint8_t read(uint8_t addr); + void read(uint8_t addr, uint8_t* data, uint8_t datalen, uint8_t rxAlign = 0); + void write(uint8_t addr, uint8_t data); + void write(uint8_t addr, uint8_t* data, uint8_t datalen); + void updateBit(uint8_t addr, uint8_t bit, bool value); + + void (*_tagEnterdCb)(Mfrc522*, void*); + void* _userData; + +public: + + inline static Serial* serial = nullptr; + + Mfrc522(SpiMaster* spi, ShiftReg* csReg, uint8_t csPin, + void (*tagEnterdCb)(Mfrc522*, void*) = nullptr, void* userData = nullptr); + + uint8_t calculateCrc(uint8_t *data, uint8_t length, uint16_t *result); + + uint8_t commuicateWithTag(uint8_t command, uint8_t waitIrq, + uint8_t *sendData, uint8_t sendLen, + uint8_t *recvData, uint8_t *recvLen, + uint8_t validBits = 0, uint8_t rxAlign = 0, + uint8_t *rxValidBits = nullptr); + + uint8_t transceive(uint8_t *sendData, uint8_t sendLen, uint8_t *recvData, uint8_t *recvLen, + uint8_t validBits = 0, uint8_t rxAlign = 0, uint8_t *rxValidBits = nullptr); + + uint8_t wakeupTag(uint8_t* bufferATQA, uint8_t *bufferLen); + + uint8_t selectTag(Uid *uid); + + void irq(); + + void setRf(bool on); + + bool cardPresent(); + + static bool probe(SpiMaster* spi, ShiftReg* csReg, uint8_t csPin); +}; diff --git a/nfcbord.cpp b/nfcbord.cpp new file mode 100644 index 0000000..6b562e8 --- /dev/null +++ b/nfcbord.cpp @@ -0,0 +1,150 @@ +#include "nfcbord.h" + +#include +#include +#include +#include +#include +#include "writepin.h" + +extern char buffer[SNPRINTF_BUFFER_SIZE]; + +NfcBoard::NfcBoard(): +csReg(&PORTC, PC1, PC2, PC0), +irqReg(&PORTC, &PINB, PC5, PB1, PC4) +{ + DDRC = (1 << PC0) | (1 << PC1) | (1 << PC2) | (1 << PC4) | (1 << PC5); + DDRB = (1 << PB4) | (1 << PB3); + csReg.clear(true); + probe(); +} + +void NfcBoard::probe() +{ + readers.clear(); + irqPins.clear(); + for(uint8_t i = 0; i < NFC_PORTS; ++i) + { + if(Mfrc522::probe(&spim, &csReg, i)) + { + irqPins.push_back(i); + } + } + for(uint8_t i = 0; i < irqPins.count(); ++i) + { + readers.push_back(Mfrc522(&spim, &csReg, irqPins[i])); + } +} + +void NfcBoard::printNfcDevices(Serial* serial) +{ + serial->write_p(PSTR("NFC DEVICES:\n")); + for(uint8_t i = 0; i < readers.count(); ++i) + { + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NFC NUMBER: %u IRQ: %x\n", i, irqPins[i]); + serial->write(buffer, SNPRINTF_BUFFER_SIZE); + } +} + +int NfcBoard::dispatch(char* inBuffer, Serial* serial) +{ + if(strcmp(inBuffer, "tsta") == 0 ) + { + serial->write_p(PSTR("Runing shift test\n")); + while(!serial->dataIsWaiting()) + { + csReg.clear(false); + _delay_us(100); + } + serial->write_p(PSTR("Finished\n")); + csReg.clear(true); + return 0; + } + else if(strcmp(inBuffer, "tstb") == 0 ) + { + serial->write_p(PSTR("Runing input shift test b\n")); + while(!serial->dataIsWaiting()) + { + serial->write((int)(*irqReg.read())); + serial->putChar('\n'); + _delay_us(100); + } + serial->write_p(PSTR("Finished\n")); + return 0; + } + else if(strcmp(inBuffer, "tstc") == 0 ) + { + serial->write_p(PSTR("Runing spi test\n")); + while(!serial->dataIsWaiting()) + { + csReg.setBit(1, false); + spim.readWrite((0x37 << 1) | (1 << 7)); + serial->write((int)spim.readWrite()); + serial->putChar('\n'); + csReg.setBit(1, true); + _delay_us(100); + } + serial->write_p(PSTR("Finished\n")); + return 0; + } + else if(strcmp(inBuffer, "tstd") == 0 ) + { + serial->write_p(PSTR("Runing tag detection test\n")); + bool oldPresent = false; + Mfrc522::serial = serial; + while(!serial->dataIsWaiting()) + { + bool present = readers[0].cardPresent(); + if(present && !oldPresent) + { + oldPresent = present; + + serial->write_p(PSTR("Tag found, selecting\n")); + + Mfrc522::Uid uid; + uint8_t res = readers[0].selectTag(&uid); + if(res != 0) + { + serial->write_p(PSTR("Select Failed with ")); + serial->write((int)res); + serial->putChar('\n'); + continue; + } + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "Uid: %x %x %x %x %x %x %x %x %x %x\n", + uid.uidByte[0], uid.uidByte[1], uid.uidByte[2], uid.uidByte[3], uid.uidByte[4], + uid.uidByte[5], uid.uidByte[6], uid.uidByte[7], uid.uidByte[8], uid.uidByte[9]); + } + else if(!present && oldPresent) + { + serial->write_p(PSTR("Tag lost\n")); + oldPresent = present; + } + _delay_ms(100); + } + serial->write_p(PSTR("Finished\n")); + return 0; + } + else if(strcmp(inBuffer, "status") == 0 ) + { + printNfcDevices(serial); + return 0; + } + else if(strcmp(inBuffer, "wake") == 0 ) + { + uint8_t bufferATQA[2]; + uint8_t len = sizeof(bufferATQA); + + uint8_t res = readers[0].wakeupTag(bufferATQA, &len); + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "wakeupTag returned: %u Buffer: 0x%x 0x%x len %u\n", + res, bufferATQA[0], bufferATQA[1], len); + serial->write(buffer, SNPRINTF_BUFFER_SIZE); + return 0; + } + else if(strcmp(inBuffer, "probe") == 0 ) + { + probe(); + printNfcDevices(serial); + return 0; + } + return -3; +} diff --git a/nfcbord.h b/nfcbord.h new file mode 100644 index 0000000..eb18558 --- /dev/null +++ b/nfcbord.h @@ -0,0 +1,26 @@ +#pragma once +#include "serial.h" +#include "shiftreg.h" +#include "inputshiftreg.h" +#include "mfrc522.h" +#include "softspim.h" +#include "staticvector.h" +#include "defines.h" + +class NfcBoard +{ +public: + ShiftReg csReg; + InputShiftReg irqReg; + SpiMaster spim; + SVector readers; + SVector irqPins; + + NfcBoard(); + + void probe(); + + void printNfcDevices(Serial* serial); + + int dispatch(char* inBuffer, Serial* serial); +}; diff --git a/serial.h b/serial.h index 46cab6f..80f9522 100644 --- a/serial.h +++ b/serial.h @@ -2,7 +2,7 @@ #define SERIAL_H #define BAUD 38400 -#define SERIAL_BUFFER_SIZE 512 +#define SERIAL_BUFFER_SIZE 384 #include #include diff --git a/shiftreg.h b/shiftreg.h new file mode 100644 index 0000000..2e31b99 --- /dev/null +++ b/shiftreg.h @@ -0,0 +1,81 @@ +#pragma once +#include + +template class ShiftReg +{ +private: + static constexpr int BYTES = (BITS % 8 == 0) ? (BITS/8) : (BITS/8+1); + + static constexpr bool invert = true; + + volatile unsigned char *_port; + const unsigned char _pinSer; + const unsigned char _pinSerClk; + const unsigned char _pinRClk; + + unsigned char _lastData[BYTES] = {}; + +public: + + ShiftReg(volatile unsigned char* const port, const unsigned char pinSer, const unsigned char pinSerClk, + const unsigned char pinRClk): + _port(port), _pinSer(pinSer), _pinSerClk(pinSerClk), _pinRClk(pinRClk) + { + clear(); + } + void setBit(unsigned char bit, bool value) + { + if(BITS <= bit) + return; + bit = BITS - bit - 1; + if(value) + _lastData[bit/8] |= (1<<(bit%8)); + else + _lastData[bit/8] &= ~(1<<(bit%8)); + write(_lastData); + } + bool getBit(unsigned char bit) + { + if(BITS <= bit) + return false; + bit = BITS - bit - 1; + return _lastData[bit/8] & (1<<(bit%8)); + } + void write(const unsigned char * const in) + { + for(unsigned char i = 0 ; i < BYTES; ++i) + _lastData[i] = in[i]; + + if constexpr(invert) + { + *_port |= ((1<<_pinSer) | (1<<_pinSerClk)); + *_port |= (1<<_pinRClk); + + for(unsigned char i = 0; i < BITS; ++i) + { + *_port |= (1 << _pinSerClk); + in[i/8] & (1<<(i%8)) ? (*_port &= ~(1 << _pinSer)) : (*_port |= (1 << _pinSer)); + *_port &= ~(1 << _pinSerClk); + } + *_port &= ~(1<<_pinRClk); + } + else + { + *_port &= ~((1<<_pinSer) | (1<<_pinSerClk)); + *_port &= ~(1<<_pinRClk); + for(unsigned char i = 0; i < BITS; ++i) + { + *_port &= ~(1 << _pinSerClk); + in[i/8] & (1<<(i%8)) ? (*_port |= (1 << _pinSer)) : (*_port &= ~(1 << _pinSer)); + *_port |= (1 << _pinSerClk); + } + *_port |= 1<<_pinRClk; + } + } + void clear(bool value = false) + { + for(unsigned char i = 0 ; i < BYTES; ++i) + _lastData[i] = value ? 0xff : 0x00; + write(_lastData); + } +}; diff --git a/softspim.cpp b/softspim.cpp new file mode 100644 index 0000000..a81fa4a --- /dev/null +++ b/softspim.cpp @@ -0,0 +1,64 @@ +#include "softspim.h" +#include "writepin.h" +#include + +SpiMaster::SpiMaster() +{ + +} + +uint8_t SpiMaster::readWrite(uint8_t in) +{ + _delay_us(DELAY_TIME_US); + uint8_t recByte = 0; + for(uint8_t i = 0; i < 8; ++i) + { + if constexpr (BIT_ORDER == 0) + writePin(_port, _pinOut, in & (1 << i)); + else + writePin(_port, _pinOut, in & (1 << (7-i))); + if constexpr (CLOCK_PHASE == 0) + _delay_us(DELAY_TIME_US); + writePin(_port, _pinClock, !CLOCK_POLARITY); + if constexpr (CLOCK_PHASE == 1) + _delay_us(DELAY_TIME_US); + if constexpr (BIT_ORDER == 0) + recByte |= readPin(_pinReg, _pinIn) << i; + else + recByte |= readPin(_pinReg, _pinIn) << (7-i); + if constexpr (CLOCK_PHASE == 0) + _delay_us(DELAY_TIME_US); + writePin(_port, _pinClock, CLOCK_POLARITY); + if constexpr (CLOCK_PHASE == 1) + _delay_us(DELAY_TIME_US); + } + return recByte; +} + +void SpiMaster::readWrite(uint8_t length, uint8_t* bufferIn, uint8_t* bufferOut) +{ + for(uint8_t i = 0; i < length; ++i) + { + uint8_t outByte = 0; + if(bufferOut) outByte = bufferOut[i]; + uint8_t inByte = readWrite(outByte); + if(bufferIn) bufferIn[i] = inByte; + } +} + + +void SpiMaster::prepare() +{ + writePin(_port, _pinClock, CLOCK_POLARITY); +} + + +void SpiMaster::write(uint8_t in) +{ + readWrite(in); +} + +uint8_t SpiMaster::read() +{ + return readWrite(); +} diff --git a/softspim.h b/softspim.h new file mode 100644 index 0000000..a0fbf28 --- /dev/null +++ b/softspim.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include + +class SpiMaster +{ +private: + + static constexpr uint8_t CLOCK_POLARITY = 1; + static constexpr uint8_t CLOCK_PHASE = 1; + static constexpr uint8_t BIT_ORDER = 1; + + volatile uint8_t * const _port = &PORTB; + volatile uint8_t * const _pinReg = &PINB; + static constexpr uint8_t _pinIn = PB2; + static constexpr uint8_t _pinOut = PB4; + static constexpr uint8_t _pinClock = PB3; + + static constexpr uint8_t DELAY_TIME_US = 10; + + +public: + + SpiMaster(); + void readWrite(uint8_t length, uint8_t* bufferIn, uint8_t* bufferOut); + uint8_t readWrite(uint8_t in = 0); + void prepare(); + uint8_t read(); + void write(uint8_t in); +}; diff --git a/staticvector.h b/staticvector.h index 217817d..bfdbdae 100644 --- a/staticvector.h +++ b/staticvector.h @@ -59,7 +59,7 @@ public: bool push_back(const T in) { - if( remainingCapacity() != 0) + if(remainingCapacity() != 0) { array[stored] = in; ++stored; @@ -70,13 +70,13 @@ public: bool erase(size_t position) { - if(position > stored) - return false; - array[position].~T(); - --stored; - for( size_t i = position; i < stored; i++ ) - memcpy(&array[i], &array[i+1], sizeof(T)); - return true; + if(position > stored) + return false; + array[position].~T(); + --stored; + for( size_t i = position; i < stored; i++ ) + memcpy(&array[i], &array[i+1], sizeof(T)); + return true; } void clear() diff --git a/traindispatch.h b/traindispatch.h index 580a665..9740c65 100644 --- a/traindispatch.h +++ b/traindispatch.h @@ -1,4 +1,9 @@ #pragma once +#include +#include +#include +#include "serial.h" +#include "writepin.h" void printTrainState(int id, Serial* serial) {