diff --git a/kawaii/CMakeLists.txt b/kawaii/CMakeLists.txt new file mode 100644 index 0000000..1a1543f --- /dev/null +++ b/kawaii/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.7) +project(kawaii-engine) + +add_subdirectory(kawaii-rs) + +set(CMAKE_CXX_STANDARD 14) + +set(SOURCE_FILES gpio.cpp engine.cpp measure.cpp) + +if (MEASURE) + add_definitions("-DMEASURE") +endif (MEASURE) + +#add_executable(engine ${SOURCE_FILES} test.cpp ultrasound_sensor.cpp) +#add_executable(remote ${SOURCE_FILES} remote.cpp) +add_executable(kawaii ${SOURCE_FILES} main.cpp ultrasound_sensor.cpp rfid_reader.cpp MFRC522.cpp rfid_reader.cpp emergency_stop.cpp) + +#add_dependencies(engine kawaii-rs) +#add_dependencies(remote kawaii-rs) +add_dependencies(kawaii kawaii-rs) + +#target_link_libraries(engine +# debug "${CMAKE_SOURCE_DIR}/kawaii-rs/target/debug/libkawaii.a" +# optimized "${CMAKE_SOURCE_DIR}/kawaii-rs/target/release/libkawaii.a" +# util dl rt pthread gcc_s c m rt pthread util +# pthread) +#target_link_libraries(remote +# debug "${CMAKE_SOURCE_DIR}/kawaii-rs/target/debug/libkawaii.a" +# optimized "${CMAKE_SOURCE_DIR}/kawaii-rs/target/release/libkawaii.a" +# util dl rt pthread gcc_s c m rt pthread util +# boost_system pthread) +target_link_libraries(kawaii + debug "${CMAKE_SOURCE_DIR}/kawaii-rs/target/debug/libkawaii.a" + optimized "${CMAKE_SOURCE_DIR}/kawaii-rs/target/release/libkawaii.a" + util dl rt pthread gcc_s c m rt pthread util + pthread bcm2835) diff --git a/kawaii/MFRC522.cpp b/kawaii/MFRC522.cpp new file mode 100644 index 0000000..8d4c39b --- /dev/null +++ b/kawaii/MFRC522.cpp @@ -0,0 +1,1755 @@ +/* + * MFRC522.cpp - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * NOTE: Please also check the comments in MFRC522.h - they provide useful hints and background information. + * Released into the public domain. + */ + +#include "MFRC522.h" +#include "bcm2835.h" +#include +#include +#include +#include +#include + +#define RSTPIN RPI_V2_GPIO_P1_11 + +using namespace std; + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522(){ + + if (!bcm2835_init()) { + printf("Failed to initialize. This tool needs root access, use sudo.\n"); + } + bcm2835_gpio_fsel(RSTPIN, BCM2835_GPIO_FSEL_OUTP); + bcm2835_gpio_write(RSTPIN, 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() { + + bcm2835_spi_begin(); + bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_MSBFIRST); // The default + bcm2835_spi_setDataMode(BCM2835_SPI_MODE0); // The default + bcm2835_spi_setClockDivider(BCM2835_SPI_CLOCK_DIVIDER_64); // ~ 4 MHz + bcm2835_spi_chipSelect(BCM2835_SPI_CS0); // The default + bcm2835_spi_setChipSelectPolarity(BCM2835_SPI_CS0, LOW); // the default + +} // 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. + ) { + + char data[2]; + data[0] = reg & 0x7E; + data[1] = value; + bcm2835_spi_transfern(data, 2); + +} // 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. + ) { + for (byte index = 0; index < count; index++) { + PCD_WriteRegister(reg, values[index]); + } + +} // 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. + ) { + + char data[2]; + data[0] = 0x80 | ((reg) & 0x7E); + bcm2835_spi_transfern(data,2); + return (byte)data[1]; +} // 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(F("Reading ")); Serial.print(count); Serial.println(F(" 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. + count--; // One read is performed outside of the loop + bcm2835_spi_transfer(address); + 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 = bcm2835_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] = bcm2835_spi_transfer(address); + } + index++; + } + values[index] = bcm2835_spi_transfer(0); // Read the final byte. Send 0 to stop reading. +} // 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 (bcm2835_gpio_lev(RSTPIN) == LOW) { //The MFRC522 chip is in power down mode. + bcm2835_gpio_write(RSTPIN, 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 are disabled. + */ +void MFRC522::PCD_AntennaOn() { + byte value = PCD_ReadRegister(TxControlReg); + if ((value & 0x03) != 0x03) { + PCD_WriteRegister(TxControlReg, value | 0x03); + } +} // End PCD_AntennaOn() + +/** + * Turns the antenna off by disabling pins TX1 and TX2. + */ +void MFRC522::PCD_AntennaOff() { + PCD_ClearRegisterBitMask(TxControlReg, 0x03); +} // End PCD_AntennaOff() + +/** + * Get the current MFRC522 Receiver Gain (RxGain[2:0]) value. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Return value scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + * + * @return Value of the RxGain, scrubbed to the 3 bits used. + */ +byte MFRC522::PCD_GetAntennaGain() { + return PCD_ReadRegister(RFCfgReg) & (0x07<<4); +} // End PCD_GetAntennaGain() + +/** + * Set the MFRC522 Receiver Gain (RxGain) to value specified by given mask. + * See 9.3.3.6 / table 98 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * NOTE: Given mask is scrubbed with (0x07<<4)=01110000b as RCFfgReg may use reserved bits. + */ +void MFRC522::PCD_SetAntennaGain(byte mask) { + if (PCD_GetAntennaGain() != mask) { // only bother if there is a change + PCD_ClearRegisterBitMask(RFCfgReg, (0x07<<4)); // clear needed to allow 000 pattern + PCD_SetRegisterBitMask(RFCfgReg, mask & (0x07<<4)); // only set RxGain[2:0] bits + } +} // End PCD_SetAntennaGain() + +/** + * Performs a self-test of the MFRC522 + * See 16.1.1 in http://www.nxp.com/documents/data_sheet/MFRC522.pdf + * + * @return Whether or not the test passed. + */ +bool MFRC522::PCD_PerformSelfTest() { + // This follows directly the steps outlined in 16.1.1 + // 1. Perform a soft reset. + PCD_Reset(); + + // 2. Clear the internal buffer by writing 25 bytes of 00h + byte ZEROES[25] = {0x00}; + PCD_SetRegisterBitMask(FIFOLevelReg, 0x80); // flush the FIFO buffer + PCD_WriteRegister(FIFODataReg, 25, ZEROES); // write 25 bytes of 00h to FIFO + PCD_WriteRegister(CommandReg, PCD_Mem); // transfer to internal buffer + + // 3. Enable self-test + PCD_WriteRegister(AutoTestReg, 0x09); + + // 4. Write 00h to FIFO buffer + PCD_WriteRegister(FIFODataReg, 0x00); + + // 5. Start self-test by issuing the CalcCRC command + PCD_WriteRegister(CommandReg, PCD_CalcCRC); + + // 6. Wait for self-test to complete + word i; + byte n; + for (i = 0; i < 0xFF; i++) { + 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; + } + } + PCD_WriteRegister(CommandReg, PCD_Idle); // Stop calculating CRC for new content in the FIFO. + + // 7. Read out resulting 64 bytes from the FIFO buffer. + byte result[64]; + PCD_ReadRegister(FIFODataReg, 64, result, 0); + + // Auto self-test done + // Reset AutoTestReg register to be 0 again. Required for normal operation. + PCD_WriteRegister(AutoTestReg, 0x00); + + // Determine firmware version (see section 9.3.4.8 in spec) + byte version = PCD_ReadRegister(VersionReg); + + // Pick the appropriate reference values + const byte *reference; + switch (version) { + case 0x91: // Version 1.0 + reference = MFRC522_firmware_referenceV1_0; + break; + case 0x92: // Version 2.0 + reference = MFRC522_firmware_referenceV2_0; + break; + default: // Unknown version + return false; + } + + // Verify that the results match up to our expectations + for (i = 0; i < 64; i++) { + if (result[i] != reference[i]) { + return false; + } + } + // Test passed; all is good. + return true; +} // End PCD_PerformSelfTest() + +///////////////////////////////////////////////////////////////////////////////////// +// 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 command, 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(F("SELECT: currentLevelKnownBits=")); Serial.println(currentLevelKnownBits, DEC); + 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 = 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(F("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 operation 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() + +/** + * Helper routine to read the current value from a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[out] value Current value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_GetValue(byte blockAddr, long *value) { + byte status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Read the block + status = MIFARE_Read(blockAddr, buffer, &size); + if (status == STATUS_OK) { + // Extract the value + *value = (long(buffer[3])<<24) | (long(buffer[2])<<16) | (long(buffer[1])<<8) | long(buffer[0]); + } + return status; +} // End MIFARE_GetValue() + +/** + * Helper routine to write a specific value into a Value Block. + * + * Only for MIFARE Classic and only for blocks in "value block" mode, that + * is: with access bits [C1 C2 C3] = [110] or [001]. The sector containing + * the block must be authenticated before calling this function. + * + * @param[in] blockAddr The block (0x00-0xff) number. + * @param[in] value New value of the Value Block. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +byte MFRC522::MIFARE_SetValue(byte blockAddr, long value) { + byte buffer[18]; + + // Translate the long into 4 bytes; repeated 2x in value block + buffer[0] = buffer[ 8] = (value & 0xFF); + buffer[1] = buffer[ 9] = (value & 0xFF00) >> 8; + buffer[2] = buffer[10] = (value & 0xFF0000) >> 16; + buffer[3] = buffer[11] = (value & 0xFF000000) >> 24; + // Inverse 4 bytes also found in value block + buffer[4] = ~buffer[0]; + buffer[5] = ~buffer[1]; + buffer[6] = ~buffer[2]; + buffer[7] = ~buffer[3]; + // Address 2x with inverse address 2x + buffer[12] = buffer[14] = blockAddr; + buffer[13] = buffer[15] = ~blockAddr; + + // Write the whole data block + return MIFARE_Write(blockAddr, buffer, 16); +} // End MIFARE_SetValue() + +///////////////////////////////////////////////////////////////////////////////////// +// 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 __FlashStringHelper pointer to a status code name. + * + */ +const string 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 string 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 + printf("Card UID:"); + for (byte i = 0; i < uid->size; i++) { + if(uid->uidByte[i] < 0x10) + printf(" 0"); + else + printf(" "); + printf("%X", uid->uidByte[i]); + } + printf("\n"); + + // PICC type + byte piccType = PICC_GetType(uid->sak); + printf("PICC type: "); + //Serial.println(PICC_GetTypeName(piccType)); + printf("%s", PICC_GetTypeName(piccType).c_str()); + + // 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: + printf("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 + } + + printf("\n"); + 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) { + printf("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits\n"); + 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) { + if(sector < 10) + printf(" "); // Pad with spaces + else + printf(" "); // Pad with spaces + printf("%02X",sector); + printf(" "); + } + else { + printf(" "); + } + // Block number + if(blockAddr < 10) + printf(" "); // Pad with spaces + else { + if(blockAddr < 100) + printf(" "); // Pad with spaces + else + printf(" "); // Pad with spaces + } + printf("%02X",blockAddr); + printf(" "); + // 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) { + printf("PCD_Authenticate() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + return; + } + } + // Read block + byteCount = sizeof(buffer); + status = MIFARE_Read(blockAddr, buffer, &byteCount); + if (status != STATUS_OK) { + printf("MIFARE_Read() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + continue; + } + // Dump data + for (byte index = 0; index < 16; index++) { + if(buffer[index] < 0x10) + printf(" 0"); + else + printf(" "); + printf("9x%02X",buffer[index]); + if ((index % 4) == 3) { + printf(" "); + } + } + // 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 + printf(" [ "); + printf("%02X" ,(g[group] >> 2) & 1); printf(" "); + printf("%02X", (g[group] >> 1) & 1); printf(" "); + printf("%02X", (g[group] >> 0) & 1); + printf(" ] "); + + if (invertedError) { + printf(" 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]); + printf(" Value="); printf("0x%02X", value); + printf(" Adr="); printf("0x%02X", buffer[12]); + } + printf("\n"); + } + + 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; + + printf("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) { + printf("MIFARE_Read() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + break; + } + // Dump data + for (byte offset = 0; offset < 4; offset++) { + i = page + offset; + if(i < 10) + printf(" "); // Pad with spaces + else + printf(" "); // Pad with spaces + printf("%02X",i); + printf(" "); + for (byte index = 0; index < 4; index++) { + i = 4 * offset + index; + if(buffer[i] < 0x10) + printf(" 0"); + else + printf(" "); + printf("%0x%02X",buffer[i]); + } + printf("\n"); + } + } +} // 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() + + +/** + * Performs the "magic sequence" needed to get Chinese UID changeable + * Mifare cards to allow writing to sector 0, where the card UID is stored. + * + * Note that you do not need to have selected the card through REQA or WUPA, + * this sequence works immediately when the card is in the reader vicinity. + * This means you can use this method even on "bricked" cards that your reader does + * not recognise anymore (see MFRC522::MIFARE_UnbrickUidSector). + * + * Of course with non-bricked devices, you're free to select them before calling this function. + */ +bool MFRC522::MIFARE_OpenUidBackdoor(bool logErrors) { + // Magic sequence: + // > 50 00 57 CD (HALT + CRC) + // > 40 (7 bits only) + // < A (4 bits only) + // > 43 + // < A (4 bits only) + // Then you can write to sector 0 without authenticating + + PICC_HaltA(); // 50 00 57 CD + + byte cmd = 0x40; + byte validBits = 7; /* Our command is only 7 bits. After receiving card response, + this will contain amount of valid response bits. */ + byte response[32]; // Card's response is written here + byte received; + byte status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 + if(status != STATUS_OK) { + if(logErrors) { + printf("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?"); + printf("Error name: "); + printf("%s",GetStatusCodeName(status).c_str()); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if(logErrors) { + + printf("Got bad response on backdoor 0x40 command: "); + printf("0x%02X", response[0]); + printf(" ("); + printf("%02X", validBits); + printf(" valid bits)\r\n"); + } + return false; + } + + cmd = 0x43; + validBits = 8; + status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 43 + if(status != STATUS_OK) { + if(logErrors) { + printf("Error in communication at command 0x43, after successfully executing 0x40"); + printf("Error name: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + printf("Got bad response on backdoor 0x43 command: "); + printf("%02X",response[0]); + printf(" ("); + printf("%02X",validBits); + printf(" valid bits)\r\n"); + } + return false; + } + + // You can now write to sector 0 without authenticating! + return true; +} // End MIFARE_OpenUidBackdoor() + +/** + * Reads entire block 0, including all manufacturer data, and overwrites + * that block with the new UID, a freshly calculated BCC, and the original + * manufacturer data. + * + * It assumes a default KEY A of 0xFFFFFFFFFFFF. + * Make sure to have selected the card before this function is called. + */ +bool MFRC522::MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors) { + + // UID + BCC byte can not be larger than 16 together + if (!newUid || !uidSize || uidSize > 15) { + if (logErrors) { + printf("New UID buffer empty, size 0, or size > 15 given"); + } + return false; + } + + // Authenticate for reading + MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + byte status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + + if (status == STATUS_TIMEOUT) { + // We get a read timeout if no card is selected yet, so let's select one + + // Wake the card up again if sleeping + // byte atqa_answer[2]; + // byte atqa_size = 2; + // PICC_WakeupA(atqa_answer, &atqa_size); + + if (!PICC_IsNewCardPresent() || !PICC_ReadCardSerial()) { + printf("No card was previously selected, and none are available. Failed to set UID."); + return false; + } + + status = PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, (byte)1, &key, &uid); + if (status != STATUS_OK) { + // We tried, time to give up + if (logErrors) { + printf("Failed to authenticate to card for reading, could not set UID: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + } + return false; + } + } + else { + if (logErrors) { + printf("PCD_Authenticate() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + } + return false; + } + } + + // Read block 0 + byte block0_buffer[18]; + byte byteCount = sizeof(block0_buffer); + status = MIFARE_Read((byte)0, block0_buffer, &byteCount); + if (status != STATUS_OK) { + if (logErrors) { + printf("MIFARE_Read() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + printf("Are you sure your KEY A for sector 0 is 0xFFFFFFFFFFFF?"); + } + return false; + } + + // Write new UID to the data we just read, and calculate BCC byte + byte bcc = 0; + for (int i = 0; i < uidSize; i++) { + block0_buffer[i] = newUid[i]; + bcc ^= newUid[i]; + } + + // Write BCC byte to buffer + block0_buffer[uidSize] = bcc; + + // Stop encrypted traffic so we can send raw bytes + PCD_StopCrypto1(); + + // Activate UID backdoor + if (!MIFARE_OpenUidBackdoor(logErrors)) { + if (logErrors) { + printf("Activating the UID backdoor failed."); + } + return false; + } + + // Write modified block 0 back to card + status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + printf("MIFARE_Write() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + } + return false; + } + + // Wake the card up again + byte atqa_answer[2]; + byte atqa_size = 2; + PICC_WakeupA(atqa_answer, &atqa_size); + + return true; +} + +/** + * Resets entire sector 0 to zeroes, so the card can be read again by readers. + */ +bool MFRC522::MIFARE_UnbrickUidSector(bool logErrors) { + MIFARE_OpenUidBackdoor(logErrors); + + byte block0_buffer[] = {0x01, 0x02, 0x03, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Write modified block 0 back to card + byte status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + printf("MIFARE_Write() failed: "); + printf("%s\n",GetStatusCodeName(status).c_str()); + } + return false; + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// 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() + diff --git a/kawaii/MFRC522.h b/kawaii/MFRC522.h new file mode 100644 index 0000000..8205b11 --- /dev/null +++ b/kawaii/MFRC522.h @@ -0,0 +1,323 @@ +/** + * 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.) + * Extended by Tom Clement with functionality to write to sector 0 of UID changeable Mifare cards. + * Released into the public domain. + * + + +-- Repurposed to fit Raspberry Pi --- + + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include +#include +#include +using namespace std; + +typedef uint8_t byte; +typedef uint16_t word; + +// Firmware data for self-test +// Reference values based on firmware version; taken from 16.1.1 in spec. +// Version 1.0 + +const byte MFRC522_firmware_referenceV1_0[] = { + 0x00, 0xC6, 0x37, 0xD5, 0x32, 0xB7, 0x57, 0x5C, + 0xC2, 0xD8, 0x7C, 0x4D, 0xD9, 0x70, 0xC7, 0x73, + 0x10, 0xE6, 0xD2, 0xAA, 0x5E, 0xA1, 0x3E, 0x5A, + 0x14, 0xAF, 0x30, 0x61, 0xC9, 0x70, 0xDB, 0x2E, + 0x64, 0x22, 0x72, 0xB5, 0xBD, 0x65, 0xF4, 0xEC, + 0x22, 0xBC, 0xD3, 0x72, 0x35, 0xCD, 0xAA, 0x41, + 0x1F, 0xA7, 0xF3, 0x53, 0x14, 0xDE, 0x7E, 0x02, + 0xD9, 0x0F, 0xB5, 0x5E, 0x25, 0x1D, 0x29, 0x79 +}; + +// Version 2.0 +const byte MFRC522_firmware_referenceV2_0[] = { + 0x00, 0xEB, 0x66, 0xBA, 0x57, 0xBF, 0x23, 0x95, + 0xD0, 0xE3, 0x0D, 0x3D, 0x27, 0x89, 0x5C, 0xDE, + 0x9D, 0x3B, 0xA7, 0x00, 0x21, 0x5B, 0x89, 0x82, + 0x51, 0x3A, 0xEB, 0x02, 0x0C, 0xA5, 0x00, 0x49, + 0x7C, 0x84, 0x4D, 0xB3, 0xCC, 0xD2, 0x1B, 0x81, + 0x5D, 0x48, 0x76, 0xD5, 0x71, 0x61, 0x21, 0xA9, + 0x86, 0x96, 0x83, 0x38, 0xCF, 0x9D, 0x5B, 0x6D, + 0xDC, 0x15, 0xBA, 0x3E, 0x7D, 0x95, 0x3B, 0x2F +}; + + + +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 commands. 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 + }; + + // MFRC522 RxGain[2:0] masks, defines the receiver's signal voltage gain factor (on the PCD). + // Described in 9.3.3.6 / table 98 of the datasheet at http://www.nxp.com/documents/data_sheet/MFRC522.pdf + enum PCD_RxGain { + RxGain_18dB = 0x00 << 4, // 000b - 18 dB, minimum + RxGain_23dB = 0x01 << 4, // 001b - 23 dB + RxGain_18dB_2 = 0x02 << 4, // 010b - 18 dB, it seems 010b is a duplicate for 000b + RxGain_23dB_2 = 0x03 << 4, // 011b - 23 dB, it seems 011b is a duplicate for 001b + RxGain_33dB = 0x04 << 4, // 100b - 33 dB, average, and typical default + RxGain_38dB = 0x05 << 4, // 101b - 38 dB + RxGain_43dB = 0x06 << 4, // 110b - 43 dB + RxGain_48dB = 0x07 << 4, // 111b - 48 dB, maximum + RxGain_min = 0x00 << 4, // 000b - 18 dB, minimum, convenience for RxGain_18dB + RxGain_avg = 0x04 << 4, // 100b - 33 dB, average, convenience for RxGain_33dB + RxGain_max = 0x07 << 4 // 111b - 48 dB, maximum, convenience for RxGain_48dB + }; + + // 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 2 + PICC_CMD_SEL_CL3 = 0x97, // Anti collision/Select, Cascade Level 3 + 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 Raspberry Pi + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(); + 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(); + void PCD_AntennaOff(); + byte PCD_GetAntennaGain(); + void PCD_SetAntennaGain(byte mask); + bool PCD_PerformSelfTest(); + + ///////////////////////////////////////////////////////////////////////////////////// + // 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); + byte MIFARE_GetValue(byte blockAddr, long *value); + byte MIFARE_SetValue(byte blockAddr, long value); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + byte PCD_MIFARE_Transceive(byte *sendData, byte sendLen, bool acceptTimeout = false); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *GetStatusCodeName(byte code); + const string GetStatusCodeName(byte code); + byte PICC_GetType(byte sak); + // old function used too much memory, now name moved to flash; if you need char, copy from flash to memory + //const char *PICC_GetTypeName(byte type); + const string 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); + bool MIFARE_OpenUidBackdoor(bool logErrors); + bool MIFARE_SetUid(byte *newUid, byte uidSize, bool logErrors); + bool MIFARE_UnbrickUidSector(bool logErrors); + + ///////////////////////////////////////////////////////////////////////////////////// + // Convenience functions - does not add extra functionality + ///////////////////////////////////////////////////////////////////////////////////// + bool PICC_IsNewCardPresent(); + bool PICC_ReadCardSerial(); + +private: + byte MIFARE_TwoStepHelper(byte command, byte blockAddr, long data); +}; + +#endif diff --git a/kawaii/bcm2835.h b/kawaii/bcm2835.h new file mode 100644 index 0000000..e533093 --- /dev/null +++ b/kawaii/bcm2835.h @@ -0,0 +1,1129 @@ +// bcm2835.h +// +// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi +// +// Author: Mike McCauley +// Copyright (C) 2011-2013 Mike McCauley +// $Id: bcm2835.h,v 1.8 2013/02/15 22:06:09 mikem Exp mikem $ +// +/// \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi +/// +/// This is a C library for Raspberry Pi (RPi). It provides access to +/// GPIO and other IO functions on the Broadcom BCM 2835 chip, +/// allowing access to the GPIO pins on the +/// 26 pin IDE plug on the RPi board so you can control and interface with various external devices. +/// +/// It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C, +/// and for accessing the system timers. +/// Pin event detection is supported by polling (interrupts are not supported). +/// +/// It is C++ compatible, and installs as a header file and non-shared library on +/// any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with +/// BCM 2835). +/// +/// The version of the package that this documentation refers to can be downloaded +/// from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.26.tar.gz +/// You can find the latest version at http://www.airspayce.com/mikem/bcm2835 +/// +/// Several example programs are provided. +/// +/// Based on data in http://elinux.org/RPi_Low-level_peripherals and +/// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf +/// and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// +/// You can also find online help and discussion at http://groups.google.com/group/bcm2835 +/// Please use that group for all questions and discussions on this topic. +/// Do not contact the author directly, unless it is to discuss commercial licensing. +/// +/// Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() +/// are used and the pin is pulled LOW +/// it can cause temporary hangs on 2012-07-15-wheezy-raspbian and Occidentalisv01. +/// Reason for this is not yet determined, but suspect that an interrupt handler is +/// hitting a hard loop on those OSs. +/// If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with +/// bcm2835_gpio_cler_len() and friends after use. +/// +/// \par Installation +/// +/// This library consists of a single non-shared library and header file, which will be +/// installed in the usual places by make install +/// +/// \code +/// # download the latest version of the library, say bcm2835-1.xx.tar.gz, then: +/// tar zxvf bcm2835-1.xx.tar.gz +/// cd bcm2835-1.xx +/// ./configure +/// make +/// sudo make check +/// sudo make install +/// \endcode +/// +/// \par Physical Addresses +/// +/// The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() +/// are low level peripheral register access functions. They are designed to use +/// physical addresses as described in section 1.2.3 ARM physical addresses +/// of the BCM2835 ARM Peripherals manual. +/// Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus +/// addresses for peripherals are set up to map onto the peripheral bus address range starting at +/// 0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at +/// physical address 0x20nnnnnn. +/// +/// The base address of the various peripheral registers are available with the following +/// externals: +/// bcm2835_gpio +/// bcm2835_pwm +/// bcm2835_clk +/// bcm2835_pads +/// bcm2835_spio0 +/// bcm2835_st +/// bcm2835_bsc0 +/// bcm2835_bsc1 +/// +/// \par Pin Numbering +/// +/// The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying +/// BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs +/// +/// RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835, +/// as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the +/// IDE header. +/// +/// RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd. +/// +/// The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ +/// the RPi pin number. There are symbolic definitions for each of the available pins +/// that you should use for convenience. See \ref RPiGPIOPin. +/// +/// \par SPI Pins +/// +/// The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, +/// allowing you to send and received data by SPI (Serial Peripheral Interface). +/// For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus +/// +/// When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their +/// default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able +/// to control the state of the SPI pins through the usual bcm2835_spi_gpio_write(). +/// When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be +/// configured and controled with the usual bcm2835_gpio_* calls. +/// +/// The Raspberry Pi GPIO pins used for SPI are: +/// +/// - P1-19 (MOSI) +/// - P1-21 (MISO) +/// - P1-23 (CLK) +/// - P1-24 (CE0) +/// - P1-26 (CE1) +/// +/// \par I2C Pins +/// +/// The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface, +/// allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire interface") . +/// For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C +/// +/// The Raspberry Pi V2 GPIO pins used for I2C are: +/// +/// - P1-03 (SDA) +/// - P1-05 (SLC) +/// +/// \par Real Time performance constraints +/// +/// The bcm2835 is a library for user programs (i.e. they run in 'userland'). +/// Such programs are not part of the kernel and are usually +/// subject to paging and swapping by the kernel while it does other things besides running your program. +/// This means that you should not expect to get real-time performance or +/// real-time timing constraints from such programs. In particular, there is no guarantee that the +/// bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. +/// In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times +/// than the one you asked for. So please dont expect to get exactly the time delay you request. +/// +/// Arjan reports that you can prevent swapping on Linux with the following code fragment: +/// +/// \code +/// struct sched_param sp; +/// memset(&sp, 0, sizeof(sp)); +/// sp.sched_priority = sched_get_priority_max(SCHED_FIFO); +/// sched_setscheduler(0, SCHED_FIFO, &sp); +/// mlockall(MCL_CURRENT | MCL_FUTURE); +/// \endcode +/// +/// \par Open Source Licensing GPL V2 +/// +/// This is the appropriate option if you want to share the source code of your +/// application with everyone you distribute it to, and you also want to give them +/// the right to share who uses it. If you wish to use this software under Open +/// Source Licensing, you must contribute all your source code to the open source +/// community in accordance with the GPL Version 2 when your application is +/// distributed. See http://www.gnu.org/copyleft/gpl.html and COPYING +/// +/// \par Acknowledgements +/// +/// Some of this code has been inspired by Dom and Gert. +/// The I2C code has been inspired by Alan Barr. +/// +/// \par Revision History +/// +/// \version 1.0 Initial release +/// \version 1.1 Minor bug fixes +/// \version 1.2 Added support for SPI +/// \version 1.3 Added bcm2835_spi_transfern() +/// \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson. +/// \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz +/// \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01 +/// Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen() +/// bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now +/// changes only the pin specified. Other pins that were already previously +/// enabled stay enabled. +/// Added bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen() +/// bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() +/// to clear the enable for individual pins, suggested by Andreas Sundstrom. +/// \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write. +/// \version 1.8 Improvements to read barrier, as suggested by maddin. +/// \version 1.9 Improvements contributed by mikew: +/// I noticed that it was mallocing memory for the mmaps on /dev/mem. +/// It's not necessary to do that, you can just mmap the file directly, +/// so I've removed the mallocs (and frees). +/// I've also modified delayMicroseconds() to use nanosleep() for long waits, +/// and a busy wait on a high resolution timer for the rest. This is because +/// I've found that calling nanosleep() takes at least 100-200 us. +/// You need to link using '-lrt' using this version. +/// I've added some unsigned casts to the debug prints to silence compiler +/// warnings I was getting, fixed some typos, and changed the value of +/// BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at +/// http://www.scribd.com/doc/101830961/GPIO-Pads-Control2 +/// Also added a define for the passwrd value that Gert says is needed to +/// change pad control settings. +/// \version 1.10 Changed the names of the delay functions to bcm2835_delay() +/// and bcm2835_delayMicroseconds() to prevent collisions with wiringPi. +/// Macros to map delay()-> bcm2835_delay() and +/// Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which +/// can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY +/// \version 1.11 Fixed incorrect link to download file +/// \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping) +/// \version 1.13 New GPIO pin definitions for RPi version 2 plug P5 +/// Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio +/// bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0. +/// \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead. +/// Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. +/// Reported by Ben Simpson. +/// \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe. +/// \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi +/// to allow a mask of pins to be set all at once. Requested by Sebastian Loncar. +/// \version 1.17 Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar. +/// \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: +/// now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. +/// No need to link with -lrt now. Contributed by Arjan van Vught. +/// \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. +/// Reported by olly. +/// \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted. +/// \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. +/// Patch courtesy Jeremy Mortis. +/// \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set +/// falling edge events. Reported by Mark Dootson. +/// \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. +/// Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions +/// to fix ocasional reads not completing. Patched by Mark Dootson. +/// \version 1.24 Mark Dootson p[atched a problem with his previously submitted code +/// under high load from other processes. +/// \version 1.25 Updated author and distribution location details to airspayce.com +/// \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. +/// Reported by Hartmut Henkel. +/// \author Mike McCauley (mikem@airspayce.com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS + + + +// Defines for BCM2835 +#ifndef BCM2835_H +#define BCM2835_H + +#include + +/// \defgroup constants Constants for passing to and from library functions +/// The values here are designed to be passed to various functions in the bcm2835 library. +/// @{ + + +/// This means pin HIGH, true, 3.3volts on a pin. +#define HIGH 0x1 +/// This means pin LOW, false, 0volts on a pin. +#define LOW 0x0 + +/// Speed of the core clock core_clk +#define BCM2835_CORE_CLK_HZ 250000000 ///< 250 MHz + +// Physical addresses for various peripheral register sets +/// Base Physical Address of the BCM 2835 peripheral registers +#define BCM2835_PERI_BASE 0x20000000 +/// Base Physical Address of the System Timer registers +#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000) +/// Base Physical Address of the Pads registers +#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000) +/// Base Physical Address of the Clock/timer registers +#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000) +/// Base Physical Address of the GPIO registers +#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000) +/// Base Physical Address of the SPI0 registers +#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000) +/// Base Physical Address of the BSC0 registers +#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000) +/// Base Physical Address of the PWM registers +#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000) + /// Base Physical Address of the BSC1 registers +#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000) + + +/// Base of the ST (System Timer) registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_st; + +/// Base of the GPIO registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_gpio; + +/// Base of the PWM registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pwm; + +/// Base of the CLK registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_clk; + +/// Base of the PADS registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_pads; + +/// Base of the SPI0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_spi0; + +/// Base of the BSC0 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc0; + +/// Base of the BSC1 registers. +/// Available after bcm2835_init has been called +extern volatile uint32_t *bcm2835_bsc1; + +/// Size of memory page on RPi +#define BCM2835_PAGE_SIZE (4*1024) +/// Size of memory block on RPi +#define BCM2835_BLOCK_SIZE (4*1024) + + +// Defines for GPIO +// The BCM2835 has 54 GPIO pins. +// BCM2835 data sheet, Page 90 onwards. +/// GPIO register offsets from BCM2835_GPIO_BASE. Offsets into the GPIO Peripheral block in bytes per 6.1 Register View +#define BCM2835_GPFSEL0 0x0000 ///< GPIO Function Select 0 +#define BCM2835_GPFSEL1 0x0004 ///< GPIO Function Select 1 +#define BCM2835_GPFSEL2 0x0008 ///< GPIO Function Select 2 +#define BCM2835_GPFSEL3 0x000c ///< GPIO Function Select 3 +#define BCM2835_GPFSEL4 0x0010 ///< GPIO Function Select 4 +#define BCM2835_GPFSEL5 0x0014 ///< GPIO Function Select 5 +#define BCM2835_GPSET0 0x001c ///< GPIO Pin Output Set 0 +#define BCM2835_GPSET1 0x0020 ///< GPIO Pin Output Set 1 +#define BCM2835_GPCLR0 0x0028 ///< GPIO Pin Output Clear 0 +#define BCM2835_GPCLR1 0x002c ///< GPIO Pin Output Clear 1 +#define BCM2835_GPLEV0 0x0034 ///< GPIO Pin Level 0 +#define BCM2835_GPLEV1 0x0038 ///< GPIO Pin Level 1 +#define BCM2835_GPEDS0 0x0040 ///< GPIO Pin Event Detect Status 0 +#define BCM2835_GPEDS1 0x0044 ///< GPIO Pin Event Detect Status 1 +#define BCM2835_GPREN0 0x004c ///< GPIO Pin Rising Edge Detect Enable 0 +#define BCM2835_GPREN1 0x0050 ///< GPIO Pin Rising Edge Detect Enable 1 +#define BCM2835_GPFEN0 0x0058 ///< GPIO Pin Falling Edge Detect Enable 0 +#define BCM2835_GPFEN1 0x005c ///< GPIO Pin Falling Edge Detect Enable 1 +#define BCM2835_GPHEN0 0x0064 ///< GPIO Pin High Detect Enable 0 +#define BCM2835_GPHEN1 0x0068 ///< GPIO Pin High Detect Enable 1 +#define BCM2835_GPLEN0 0x0070 ///< GPIO Pin Low Detect Enable 0 +#define BCM2835_GPLEN1 0x0074 ///< GPIO Pin Low Detect Enable 1 +#define BCM2835_GPAREN0 0x007c ///< GPIO Pin Async. Rising Edge Detect 0 +#define BCM2835_GPAREN1 0x0080 ///< GPIO Pin Async. Rising Edge Detect 1 +#define BCM2835_GPAFEN0 0x0088 ///< GPIO Pin Async. Falling Edge Detect 0 +#define BCM2835_GPAFEN1 0x008c ///< GPIO Pin Async. Falling Edge Detect 1 +#define BCM2835_GPPUD 0x0094 ///< GPIO Pin Pull-up/down Enable +#define BCM2835_GPPUDCLK0 0x0098 ///< GPIO Pin Pull-up/down Enable Clock 0 +#define BCM2835_GPPUDCLK1 0x009c ///< GPIO Pin Pull-up/down Enable Clock 1 + +/// \brief bcm2835PortFunction +/// Port function select modes for bcm2835_gpio_fsel() +typedef enum +{ + BCM2835_GPIO_FSEL_INPT = 0b000, ///< Input + BCM2835_GPIO_FSEL_OUTP = 0b001, ///< Output + BCM2835_GPIO_FSEL_ALT0 = 0b100, ///< Alternate function 0 + BCM2835_GPIO_FSEL_ALT1 = 0b101, ///< Alternate function 1 + BCM2835_GPIO_FSEL_ALT2 = 0b110, ///< Alternate function 2 + BCM2835_GPIO_FSEL_ALT3 = 0b111, ///< Alternate function 3 + BCM2835_GPIO_FSEL_ALT4 = 0b011, ///< Alternate function 4 + BCM2835_GPIO_FSEL_ALT5 = 0b010, ///< Alternate function 5 + BCM2835_GPIO_FSEL_MASK = 0b111 ///< Function select bits mask +} bcm2835FunctionSelect; + +/// \brief bcm2835PUDControl +/// Pullup/Pulldown defines for bcm2835_gpio_pud() +typedef enum +{ + BCM2835_GPIO_PUD_OFF = 0b00, ///< Off ? disable pull-up/down + BCM2835_GPIO_PUD_DOWN = 0b01, ///< Enable Pull Down control + BCM2835_GPIO_PUD_UP = 0b10 ///< Enable Pull Up control +} bcm2835PUDControl; + +/// Pad control register offsets from BCM2835_GPIO_PADS +#define BCM2835_PADS_GPIO_0_27 0x002c ///< Pad control register for pads 0 to 27 +#define BCM2835_PADS_GPIO_28_45 0x0030 ///< Pad control register for pads 28 to 45 +#define BCM2835_PADS_GPIO_46_53 0x0034 ///< Pad control register for pads 46 to 53 + +/// Pad Control masks +#define BCM2835_PAD_PASSWRD (0x5A << 24) ///< Password to enable setting pad mask +#define BCM2835_PAD_SLEW_RATE_UNLIMITED 0x10 ///< Slew rate unlimited +#define BCM2835_PAD_HYSTERESIS_ENABLED 0x08 ///< Hysteresis enabled +#define BCM2835_PAD_DRIVE_2mA 0x00 ///< 2mA drive current +#define BCM2835_PAD_DRIVE_4mA 0x01 ///< 4mA drive current +#define BCM2835_PAD_DRIVE_6mA 0x02 ///< 6mA drive current +#define BCM2835_PAD_DRIVE_8mA 0x03 ///< 8mA drive current +#define BCM2835_PAD_DRIVE_10mA 0x04 ///< 10mA drive current +#define BCM2835_PAD_DRIVE_12mA 0x05 ///< 12mA drive current +#define BCM2835_PAD_DRIVE_14mA 0x06 ///< 14mA drive current +#define BCM2835_PAD_DRIVE_16mA 0x07 ///< 16mA drive current + +/// \brief bcm2835PadGroup +/// Pad group specification for bcm2835_gpio_pad() +typedef enum +{ + BCM2835_PAD_GROUP_GPIO_0_27 = 0, ///< Pad group for GPIO pads 0 to 27 + BCM2835_PAD_GROUP_GPIO_28_45 = 1, ///< Pad group for GPIO pads 28 to 45 + BCM2835_PAD_GROUP_GPIO_46_53 = 2 ///< Pad group for GPIO pads 46 to 53 +} bcm2835PadGroup; + +/// \brief GPIO Pin Numbers +/// +/// Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers. +/// These can be passed as a pin number to any function requiring a pin. +/// Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins +/// and some can adopt an alternate function. +/// RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*. +/// At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively +/// When SPI0 is in use (ie after bcm2835_spi_begin()), pins 19, 21, 23, 24, 26 are dedicated to SPI +/// and cant be controlled independently +typedef enum +{ + RPI_GPIO_P1_03 = 0, ///< Version 1, Pin P1-03 + RPI_GPIO_P1_05 = 1, ///< Version 1, Pin P1-05 + RPI_GPIO_P1_07 = 4, ///< Version 1, Pin P1-07 + RPI_GPIO_P1_08 = 14, ///< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_GPIO_P1_10 = 15, ///< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_GPIO_P1_11 = 17, ///< Version 1, Pin P1-11 + RPI_GPIO_P1_12 = 18, ///< Version 1, Pin P1-12 + RPI_GPIO_P1_13 = 21, ///< Version 1, Pin P1-13 + RPI_GPIO_P1_15 = 22, ///< Version 1, Pin P1-15 + RPI_GPIO_P1_16 = 23, ///< Version 1, Pin P1-16 + RPI_GPIO_P1_18 = 24, ///< Version 1, Pin P1-18 + RPI_GPIO_P1_19 = 10, ///< Version 1, Pin P1-19, MOSI when SPI0 in use + RPI_GPIO_P1_21 = 9, ///< Version 1, Pin P1-21, MISO when SPI0 in use + RPI_GPIO_P1_22 = 25, ///< Version 1, Pin P1-22 + RPI_GPIO_P1_23 = 11, ///< Version 1, Pin P1-23, CLK when SPI0 in use + RPI_GPIO_P1_24 = 8, ///< Version 1, Pin P1-24, CE0 when SPI0 in use + RPI_GPIO_P1_26 = 7, ///< Version 1, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2 + RPI_V2_GPIO_P1_03 = 2, ///< Version 2, Pin P1-03 + RPI_V2_GPIO_P1_05 = 3, ///< Version 2, Pin P1-05 + RPI_V2_GPIO_P1_07 = 4, ///< Version 2, Pin P1-07 + RPI_V2_GPIO_P1_08 = 14, ///< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD + RPI_V2_GPIO_P1_10 = 15, ///< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD + RPI_V2_GPIO_P1_11 = 17, ///< Version 2, Pin P1-11 + RPI_V2_GPIO_P1_12 = 18, ///< Version 2, Pin P1-12 + RPI_V2_GPIO_P1_13 = 27, ///< Version 2, Pin P1-13 + RPI_V2_GPIO_P1_15 = 22, ///< Version 2, Pin P1-15 + RPI_V2_GPIO_P1_16 = 23, ///< Version 2, Pin P1-16 + RPI_V2_GPIO_P1_18 = 24, ///< Version 2, Pin P1-18 + RPI_V2_GPIO_P1_19 = 10, ///< Version 2, Pin P1-19, MOSI when SPI0 in use + RPI_V2_GPIO_P1_21 = 9, ///< Version 2, Pin P1-21, MISO when SPI0 in use + RPI_V2_GPIO_P1_22 = 25, ///< Version 2, Pin P1-22 + RPI_V2_GPIO_P1_23 = 11, ///< Version 2, Pin P1-23, CLK when SPI0 in use + RPI_V2_GPIO_P1_24 = 8, ///< Version 2, Pin P1-24, CE0 when SPI0 in use + RPI_V2_GPIO_P1_26 = 7, ///< Version 2, Pin P1-26, CE1 when SPI0 in use + + // RPi Version 2, new plug P5 + RPI_V2_GPIO_P5_03 = 28, ///< Version 2, Pin P5-03 + RPI_V2_GPIO_P5_04 = 29, ///< Version 2, Pin P5-04 + RPI_V2_GPIO_P5_05 = 30, ///< Version 2, Pin P5-05 + RPI_V2_GPIO_P5_06 = 31, ///< Version 2, Pin P5-06 + +} RPiGPIOPin; + +// Defines for SPI +// GPIO register offsets from BCM2835_SPI0_BASE. +// Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map +#define BCM2835_SPI0_CS 0x0000 ///< SPI Master Control and Status +#define BCM2835_SPI0_FIFO 0x0004 ///< SPI Master TX and RX FIFOs +#define BCM2835_SPI0_CLK 0x0008 ///< SPI Master Clock Divider +#define BCM2835_SPI0_DLEN 0x000c ///< SPI Master Data Length +#define BCM2835_SPI0_LTOH 0x0010 ///< SPI LOSSI mode TOH +#define BCM2835_SPI0_DC 0x0014 ///< SPI DMA DREQ Controls + +// Register masks for SPI0_CS +#define BCM2835_SPI0_CS_LEN_LONG 0x02000000 ///< Enable Long data word in Lossi mode if DMA_LEN is set +#define BCM2835_SPI0_CS_DMA_LEN 0x01000000 ///< Enable DMA mode in Lossi mode +#define BCM2835_SPI0_CS_CSPOL2 0x00800000 ///< Chip Select 2 Polarity +#define BCM2835_SPI0_CS_CSPOL1 0x00400000 ///< Chip Select 1 Polarity +#define BCM2835_SPI0_CS_CSPOL0 0x00200000 ///< Chip Select 0 Polarity +#define BCM2835_SPI0_CS_RXF 0x00100000 ///< RXF - RX FIFO Full +#define BCM2835_SPI0_CS_RXR 0x00080000 ///< RXR RX FIFO needs Reading ( full) +#define BCM2835_SPI0_CS_TXD 0x00040000 ///< TXD TX FIFO can accept Data +#define BCM2835_SPI0_CS_RXD 0x00020000 ///< RXD RX FIFO contains Data +#define BCM2835_SPI0_CS_DONE 0x00010000 ///< Done transfer Done +#define BCM2835_SPI0_CS_TE_EN 0x00008000 ///< Unused +#define BCM2835_SPI0_CS_LMONO 0x00004000 ///< Unused +#define BCM2835_SPI0_CS_LEN 0x00002000 ///< LEN LoSSI enable +#define BCM2835_SPI0_CS_REN 0x00001000 ///< REN Read Enable +#define BCM2835_SPI0_CS_ADCS 0x00000800 ///< ADCS Automatically Deassert Chip Select +#define BCM2835_SPI0_CS_INTR 0x00000400 ///< INTR Interrupt on RXR +#define BCM2835_SPI0_CS_INTD 0x00000200 ///< INTD Interrupt on Done +#define BCM2835_SPI0_CS_DMAEN 0x00000100 ///< DMAEN DMA Enable +#define BCM2835_SPI0_CS_TA 0x00000080 ///< Transfer Active +#define BCM2835_SPI0_CS_CSPOL 0x00000040 ///< Chip Select Polarity +#define BCM2835_SPI0_CS_CLEAR 0x00000030 ///< Clear FIFO Clear RX and TX +#define BCM2835_SPI0_CS_CLEAR_RX 0x00000020 ///< Clear FIFO Clear RX +#define BCM2835_SPI0_CS_CLEAR_TX 0x00000010 ///< Clear FIFO Clear TX +#define BCM2835_SPI0_CS_CPOL 0x00000008 ///< Clock Polarity +#define BCM2835_SPI0_CS_CPHA 0x00000004 ///< Clock Phase +#define BCM2835_SPI0_CS_CS 0x00000003 ///< Chip Select + +/// \brief bcm2835SPIBitOrder SPI Bit order +/// Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder() +typedef enum +{ + BCM2835_SPI_BIT_ORDER_LSBFIRST = 0, ///< LSB First + BCM2835_SPI_BIT_ORDER_MSBFIRST = 1 ///< MSB First +}bcm2835SPIBitOrder; + +/// \brief SPI Data mode +/// Specify the SPI data mode to be passed to bcm2835_spi_setDataMode() +typedef enum +{ + BCM2835_SPI_MODE0 = 0, ///< CPOL = 0, CPHA = 0 + BCM2835_SPI_MODE1 = 1, ///< CPOL = 0, CPHA = 1 + BCM2835_SPI_MODE2 = 2, ///< CPOL = 1, CPHA = 0 + BCM2835_SPI_MODE3 = 3, ///< CPOL = 1, CPHA = 1 +}bcm2835SPIMode; + +/// \brief bcm2835SPIChipSelect +/// Specify the SPI chip select pin(s) +typedef enum +{ + BCM2835_SPI_CS0 = 0, ///< Chip Select 0 + BCM2835_SPI_CS1 = 1, ///< Chip Select 1 + BCM2835_SPI_CS2 = 2, ///< Chip Select 2 (ie pins CS1 and CS2 are asserted) + BCM2835_SPI_CS_NONE = 3, ///< No CS, control it yourself +} bcm2835SPIChipSelect; + +/// \brief bcm2835SPIClockDivider +/// Specifies the divider used to generate the SPI clock from the system clock. +/// Figures below give the divider, clock period and clock frequency. +/// Clock divided is based on nominal base clock rate of 250MHz +/// It is reported that (contrary to the documentation) any even divider may used. +/// The frequencies shown for each divider have been confirmed by measurement +typedef enum +{ + BCM2835_SPI_CLOCK_DIVIDER_65536 = 0, ///< 65536 = 262.144us = 3.814697260kHz + BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768, ///< 32768 = 131.072us = 7.629394531kHz + BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384, ///< 16384 = 65.536us = 15.25878906kHz + BCM2835_SPI_CLOCK_DIVIDER_8192 = 8192, ///< 8192 = 32.768us = 30/51757813kHz + BCM2835_SPI_CLOCK_DIVIDER_4096 = 4096, ///< 4096 = 16.384us = 61.03515625kHz + BCM2835_SPI_CLOCK_DIVIDER_2048 = 2048, ///< 2048 = 8.192us = 122.0703125kHz + BCM2835_SPI_CLOCK_DIVIDER_1024 = 1024, ///< 1024 = 4.096us = 244.140625kHz + BCM2835_SPI_CLOCK_DIVIDER_512 = 512, ///< 512 = 2.048us = 488.28125kHz + BCM2835_SPI_CLOCK_DIVIDER_256 = 256, ///< 256 = 1.024us = 976.5625MHz + BCM2835_SPI_CLOCK_DIVIDER_128 = 128, ///< 128 = 512ns = = 1.953125MHz + BCM2835_SPI_CLOCK_DIVIDER_64 = 64, ///< 64 = 256ns = 3.90625MHz + BCM2835_SPI_CLOCK_DIVIDER_32 = 32, ///< 32 = 128ns = 7.8125MHz + BCM2835_SPI_CLOCK_DIVIDER_16 = 16, ///< 16 = 64ns = 15.625MHz + BCM2835_SPI_CLOCK_DIVIDER_8 = 8, ///< 8 = 32ns = 31.25MHz + BCM2835_SPI_CLOCK_DIVIDER_4 = 4, ///< 4 = 16ns = 62.5MHz + BCM2835_SPI_CLOCK_DIVIDER_2 = 2, ///< 2 = 8ns = 125MHz, fastest you can get + BCM2835_SPI_CLOCK_DIVIDER_1 = 1, ///< 0 = 262.144us = 3.814697260kHz, same as 0/65536 +} bcm2835SPIClockDivider; + +// Defines for I2C +// GPIO register offsets from BCM2835_BSC*_BASE. +// Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map +#define BCM2835_BSC_C 0x0000 ///< BSC Master Control +#define BCM2835_BSC_S 0x0004 ///< BSC Master Status +#define BCM2835_BSC_DLEN 0x0008 ///< BSC Master Data Length +#define BCM2835_BSC_A 0x000c ///< BSC Master Slave Address +#define BCM2835_BSC_FIFO 0x0010 ///< BSC Master Data FIFO +#define BCM2835_BSC_DIV 0x0014 ///< BSC Master Clock Divider +#define BCM2835_BSC_DEL 0x0018 ///< BSC Master Data Delay +#define BCM2835_BSC_CLKT 0x001c ///< BSC Master Clock Stretch Timeout + +// Register masks for BSC_C +#define BCM2835_BSC_C_I2CEN 0x00008000 ///< I2C Enable, 0 = disabled, 1 = enabled +#define BCM2835_BSC_C_INTR 0x00000400 ///< Interrupt on RX +#define BCM2835_BSC_C_INTT 0x00000200 ///< Interrupt on TX +#define BCM2835_BSC_C_INTD 0x00000100 ///< Interrupt on DONE +#define BCM2835_BSC_C_ST 0x00000080 ///< Start transfer, 1 = Start a new transfer +#define BCM2835_BSC_C_CLEAR_1 0x00000020 ///< Clear FIFO Clear +#define BCM2835_BSC_C_CLEAR_2 0x00000010 ///< Clear FIFO Clear +#define BCM2835_BSC_C_READ 0x00000001 ///< Read transfer + +// Register masks for BSC_S +#define BCM2835_BSC_S_CLKT 0x00000200 ///< Clock stretch timeout +#define BCM2835_BSC_S_ERR 0x00000100 ///< ACK error +#define BCM2835_BSC_S_RXF 0x00000080 ///< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_TXE 0x00000040 ///< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full +#define BCM2835_BSC_S_RXD 0x00000020 ///< RXD FIFO contains data +#define BCM2835_BSC_S_TXD 0x00000010 ///< TXD FIFO can accept data +#define BCM2835_BSC_S_RXR 0x00000008 ///< RXR FIFO needs reading (full) +#define BCM2835_BSC_S_TXW 0x00000004 ///< TXW FIFO needs writing (full) +#define BCM2835_BSC_S_DONE 0x00000002 ///< Transfer DONE +#define BCM2835_BSC_S_TA 0x00000001 ///< Transfer Active + +#define BCM2835_BSC_FIFO_SIZE 16 ///< BSC FIFO size + +/// \brief bcm2835I2CClockDivider +/// Specifies the divider used to generate the I2C clock from the system clock. +/// Clock divided is based on nominal base clock rate of 250MHz +typedef enum +{ + BCM2835_I2C_CLOCK_DIVIDER_2500 = 2500, ///< 2500 = 10us = 100 kHz + BCM2835_I2C_CLOCK_DIVIDER_626 = 626, ///< 622 = 2.504us = 399.3610 kHz + BCM2835_I2C_CLOCK_DIVIDER_150 = 150, ///< 150 = 60ns = 1.666 MHz (default at reset) + BCM2835_I2C_CLOCK_DIVIDER_148 = 148, ///< 148 = 59ns = 1.689 MHz +} bcm2835I2CClockDivider; + +/// \brief bcm2835I2CReasonCodes +/// Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions. +typedef enum +{ + BCM2835_I2C_REASON_OK = 0x00, ///< Success + BCM2835_I2C_REASON_ERROR_NACK = 0x01, ///< Received a NACK + BCM2835_I2C_REASON_ERROR_CLKT = 0x02, ///< Received Clock Stretch Timeout + BCM2835_I2C_REASON_ERROR_DATA = 0x04, ///< Not all data is sent / received +} bcm2835I2CReasonCodes; + +// Defines for ST +// GPIO register offsets from BCM2835_ST_BASE. +// Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers +// The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter. +// BCM2835_ST_CLO is the System Timer Counter Lower bits register. +// The system timer free-running counter lower register is a read-only register that returns the current value +// of the lower 32-bits of the free running counter. +// BCM2835_ST_CHI is the System Timer Counter Upper bits register. +// The system timer free-running counter upper register is a read-only register that returns the current value +// of the upper 32-bits of the free running counter. +#define BCM2835_ST_CS 0x0000 ///< System Timer Control/Status +#define BCM2835_ST_CLO 0x0004 ///< System Timer Counter Lower 32 bits +#define BCM2835_ST_CHI 0x0008 ///< System Timer Counter Upper 32 bits + +/// @} + + +// Defines for PWM +#define BCM2835_PWM_CONTROL 0 +#define BCM2835_PWM_STATUS 1 +#define BCM2835_PWM0_RANGE 4 +#define BCM2835_PWM0_DATA 5 +#define BCM2835_PWM1_RANGE 8 +#define BCM2835_PWM1_DATA 9 + +#define BCM2835_PWMCLK_CNTL 40 +#define BCM2835_PWMCLK_DIV 41 + +#define BCM2835_PWM1_MS_MODE 0x8000 /// Run in MS mode +#define BCM2835_PWM1_USEFIFO 0x2000 /// Data from FIFO +#define BCM2835_PWM1_REVPOLAR 0x1000 /// Reverse polarity +#define BCM2835_PWM1_OFFSTATE 0x0800 /// Ouput Off state +#define BCM2835_PWM1_REPEATFF 0x0400 /// Repeat last value if FIFO empty +#define BCM2835_PWM1_SERIAL 0x0200 /// Run in serial mode +#define BCM2835_PWM1_ENABLE 0x0100 /// Channel Enable + +#define BCM2835_PWM0_MS_MODE 0x0080 /// Run in MS mode +#define BCM2835_PWM0_USEFIFO 0x0020 /// Data from FIFO +#define BCM2835_PWM0_REVPOLAR 0x0010 /// Reverse polarity +#define BCM2835_PWM0_OFFSTATE 0x0008 /// Ouput Off state +#define BCM2835_PWM0_REPEATFF 0x0004 /// Repeat last value if FIFO empty +#define BCM2835_PWM0_SERIAL 0x0002 /// Run in serial mode +#define BCM2835_PWM0_ENABLE 0x0001 /// Channel Enable + +// Historical name compatibility +#ifndef BCM2835_NO_DELAY_COMPATIBILITY +#define delay(x) bcm2835_delay(x) +#define delayMicroseconds(x) bcm2835_delayMicroseconds(x) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + /// \defgroup init Library initialisation and management + /// These functions allow you to intialise and control the bcm2835 library + /// @{ + + /// Initialise the library by opening /dev/mem and getting pointers to the + /// internal memory for BCM 2835 device registers. You must call this (successfully) + /// before calling any other + /// functions in this library (except bcm2835_set_debug). + /// If bcm2835_init() fails by returning 0, + /// calling any other function may result in crashes or other failures. + /// Prints messages to stderr in case of errors. + /// \return 1 if successful else 0 + extern int bcm2835_init(void); + + /// Close the library, deallocating any allocated memory and closing /dev/mem + /// \return 1 if successful else 0 + extern int bcm2835_close(void); + + /// Sets the debug level of the library. + /// A value of 1 prevents mapping to /dev/mem, and makes the library print out + /// what it would do, rather than accessing the GPIO registers. + /// A value of 0, the default, causes normal operation. + /// Call this before calling bcm2835_init(); + /// \param[in] debug The new debug level. 1 means debug + extern void bcm2835_set_debug(uint8_t debug); + + /// @} // end of init + + /// \defgroup lowlevel Low level register access + /// These functions provide low level register access, and should not generally + /// need to be used + /// + /// @{ + + /// Reads 32 bit value from a peripheral address + /// The read is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr); + + + /// Reads 32 bit value from a peripheral address without the read barrier + /// You should only use this when your code has previously called bcm2835_peri_read() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \return the value read from the 32 bit register + /// \sa Physical Addresses + extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr); + + + /// Writes 32 bit value from a peripheral address + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value); + + /// Writes 32 bit value from a peripheral address without the write barrier + /// You should only use this when your code has previously called bcm2835_peri_write() + /// within the same peripheral, and no other peripheral access has occurred since. + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write + /// \sa Physical Addresses + extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value); + + /// Alters a number of bits in a 32 peripheral regsiter. + /// It reads the current valu and then alters the bits deines as 1 in mask, + /// according to the bit value in value. + /// All other bits that are 0 in the mask are unaffected. + /// Use this to alter a subset of the bits in a register. + /// The write is done twice, and is therefore always safe in terms of + /// manual section 1.3 Peripheral access precautions for correct memory ordering + /// \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc. + /// \param[in] value The 32 bit value to write, masked in by mask. + /// \param[in] mask Bitmask that defines the bits that will be altered in the register. + /// \sa Physical Addresses + extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask); + /// @} // end of lowlevel + + /// \defgroup gpio GPIO register access + /// These functions allow you to control the GPIO interface. You can set the + /// function of each GPIO pin, read the input state and set the output state. + /// @{ + + /// Sets the Function Select register for the given pin, which configures + /// the pin as Input, Output or one of the 6 alternate functions. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from RPiGPIOPin. + /// \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect + extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode); + + /// Sets the specified pin output to + /// HIGH. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_set(uint8_t pin); + + /// Sets the specified pin output to + /// LOW. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \sa bcm2835_gpio_write() + extern void bcm2835_gpio_clr(uint8_t pin); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// HIGH. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_set_multi(uint32_t mask); + + /// Sets any of the first 32 GPIO output pins specified in the mask to + /// LOW. + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \sa bcm2835_gpio_write_multi() + extern void bcm2835_gpio_clr_multi(uint32_t mask); + + /// Reads the current level on the specified + /// pin and returns either HIGH or LOW. Works whether or not the pin + /// is an input or an output. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return the current level either HIGH or LOW + extern uint8_t bcm2835_gpio_lev(uint8_t pin); + + /// Event Detect Status. + /// Tests whether the specified pin has detected a level or edge + /// as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), + /// bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen(). + /// Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin); + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \return HIGH if the event detect status for th given pin is true. + extern uint8_t bcm2835_gpio_eds(uint8_t pin); + + /// Sets the Event Detect Status register for a given pin to 1, + /// which has the effect of clearing the flag. Use this afer seeing + /// an Event Detect Status on the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_set_eds(uint8_t pin); + + /// Enable Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?011? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_ren(uint8_t pin); + + /// Disable Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_ren(uint8_t pin); + + /// Enable Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// The GPRENn registers use + /// synchronous edge detection. This means the input signal is sampled using the + /// system clock and then it is looking for a ?100? pattern on the sampled signal. This + /// has the effect of suppressing glitches. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_fen(uint8_t pin); + + /// Disable Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_fen(uint8_t pin); + + /// Enable High Detect Enable for the specified pin. + /// When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_hen(uint8_t pin); + + /// Disable High Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_hen(uint8_t pin); + + /// Enable Low Detect Enable for the specified pin. + /// When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_len(uint8_t pin); + + /// Disable Low Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_len(uint8_t pin); + + /// Enable Asynchronous Rising Edge Detect Enable for the specified pin. + /// When a rising edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// rising edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_aren(uint8_t pin); + + /// Disable Asynchronous Rising Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_aren(uint8_t pin); + + /// Enable Asynchronous Falling Edge Detect Enable for the specified pin. + /// When a falling edge is detected, sets the appropriate pin in Event Detect Status. + /// Asynchronous means the incoming signal is not sampled by the system clock. As such + /// falling edges of very short duration can be detected. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_afen(uint8_t pin); + + /// Disable Asynchronous Falling Edge Detect Enable for the specified pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + extern void bcm2835_gpio_clr_afen(uint8_t pin); + + /// Sets the Pull-up/down register for the given pin. This is + /// used with bcm2835_gpio_pudclk() to set the Pull-up/down resistor for the given pin. + /// However, it is usually more convenient to use bcm2835_gpio_set_pud(). + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pud(uint8_t pud); + + /// Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin. + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. + /// LOW to remove the clock. + /// \sa bcm2835_gpio_set_pud() + extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on); + + /// Reads and returns the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + extern uint32_t bcm2835_gpio_pad(uint8_t group); + + /// Sets the Pad Control for the given GPIO group. + /// \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_* + /// \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup + extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control); + + /// Delays for the specified number of milliseconds. + /// Uses nanosleep(), and therefore does not use CPU until the time is up. + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// \param[in] millis Delay in milliseconds + extern void bcm2835_delay (unsigned int millis); + + /// Delays for the specified number of microseconds. + /// Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers, + /// However, you are at the mercy of nanosleep(). From the manual for nanosleep(): + /// If the interval specified in req is not an exact multiple of the granularity + /// underlying clock (see time(7)), then the interval will be + /// rounded up to the next multiple. Furthermore, after the sleep completes, + /// there may still be a delay before the CPU becomes free to once + /// again execute the calling thread. + /// For times less than about 450 microseconds, uses a busy wait on the System Timer. + /// It is reported that a delay of 0 microseconds on RaspberryPi will in fact + /// result in a delay of about 80 microseconds. Your mileage may vary. + /// \param[in] micros Delay in microseconds + extern void bcm2835_delayMicroseconds (uint64_t micros); + + /// Sets the output state of the specified pin + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write(uint8_t pin, uint8_t on); + + /// Sets any of the first 32 GPIO output pins specified in the mask to the state given by on + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] on HIGH sets the output to HIGH and LOW to LOW. + extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on); + + /// Sets the first 32 GPIO output pins specified in the mask to the value given by value + /// \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + /// \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05) + extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask); + + /// Sets the Pull-up/down mode for the specified pin. This is more convenient than + /// clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk(). + /// \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin. + /// \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl + extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud); + + /// @} + + /// \defgroup spi SPI access + /// These functions let you use SPI0 (Serial Peripheral Interface) to + /// interface with an external SPI device. + /// @{ + + /// Start SPI operations. + /// Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// to alternate function ALT0, which enables those pins for SPI interface. + /// You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to + /// their default functions + /// \sa bcm2835_spi_end() + extern void bcm2835_spi_begin(void); + + /// End SPI operations. + /// SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1) + /// are returned to their default INPUT behaviour. + extern void bcm2835_spi_end(void); + + /// Sets the SPI bit order + /// NOTE: has no effect. Not supported by SPI0. + /// Defaults to + /// \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, + /// see \ref bcm2835SPIBitOrder + extern void bcm2835_spi_setBitOrder(uint8_t order); + + /// Sets the SPI clock divider and therefore the + /// SPI clock speed. + /// \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, + /// see \ref bcm2835SPIClockDivider + extern void bcm2835_spi_setClockDivider(uint16_t divider); + + /// Sets the SPI data mode + /// Sets the clock polariy and phase + /// \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, + /// see \ref bcm2835SPIMode + extern void bcm2835_spi_setDataMode(uint8_t mode); + + /// Sets the chip select pin(s) + /// When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the + /// transfer. + /// \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. + /// One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect + extern void bcm2835_spi_chipSelect(uint8_t cs); + + /// Sets the chip select pin polarity for a given pin + /// When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) + /// will be asserted to the + /// value given by active. When transfers are not happening, the chip select pin(s) + /// return to the complement (inactive) value. + /// \param[in] cs The chip select pin to affect + /// \param[in] active Whether the chip select pin is to be active HIGH + extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active); + + /// Transfers one byte to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. + /// Returns the read data byte from the slave. + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \param[in] value The 8 bit data byte to write to MOSI + /// \return The 8 bit byte simultaneously read from MISO + /// \sa bcm2835_spi_transfern() + extern uint8_t bcm2835_spi_transfer(uint8_t value); + + /// Transfers any number of bytes to and from the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. + /// The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long + /// Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual + /// \param[in] tbuf Buffer of bytes to send. + /// \param[out] rbuf Received bytes will by put in this buffer + /// \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len); + + /// Transfers any number of bytes to and from the currently selected SPI slave + /// using bcm2835_spi_transfernb. + /// The returned data from the slave replaces the transmitted data in the buffer. + /// \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents + /// \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received + /// \sa bcm2835_spi_transfer() + extern void bcm2835_spi_transfern(char* buf, uint32_t len); + + /// Transfers any number of bytes to the currently selected SPI slave. + /// Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) + /// during the transfer. + /// \param[in] buf Buffer of bytes to send. + /// \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send + extern void bcm2835_spi_writenb(char* buf, uint32_t len); + + /// @} + + /// \defgroup i2c I2C access + /// These functions let you use I2C (The Broadcom Serial Control bus with the Philips + /// I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device. + /// @{ + + /// Start I2C operations. + /// Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL) + /// to alternate function ALT0, which enables those pins for I2C interface. + /// You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to + /// their default functions + /// \sa bcm2835_i2c_end() + extern void bcm2835_i2c_begin(void); + + /// End I2C operations. + /// I2C pins P1-03 (SDA) and P1-05 (SCL) + /// are returned to their default INPUT behaviour. + extern void bcm2835_i2c_end(void); + + /// Sets the I2C slave address. + /// \param[in] addr The I2C slave address. + extern void bcm2835_i2c_setSlaveAddress(uint8_t addr); + + /// Sets the I2C clock divider and therefore the I2C clock speed. + /// \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*, + /// see \ref bcm2835I2CClockDivider + extern void bcm2835_i2c_setClockDivider(uint16_t divider); + + /// Sets the I2C clock divider by converting the baudrate parameter to + /// the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider) + /// For the I2C standard 100khz you would set baudrate to 100000 + /// The use of baudrate corresponds to its use in the I2C kernel device + /// driver. (Of course, bcm2835 has nothing to do with the kernel driver) + extern void bcm2835_i2c_set_baudrate(uint32_t baudrate); + + /// Transfers any number of bytes to the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to send. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to send. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len); + + /// Transfers any number of bytes from the currently selected I2C slave. + /// (as previously set by \sa bcm2835_i2c_setSlaveAddress) + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len); + + /// Allows reading from I2C slaves that require a repeated start (without any prior stop) + /// to read after the required slave register has been set. For example, the popular + /// MPL3115A2 pressure and temperature sensor. Note that your device must support or + /// require this mode. If your device does not require this mode then the standard + /// combined: + /// \sa bcm2835_i2c_write + /// \sa bcm2835_i2c_read + /// are a better choice. + /// Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress + /// \param[in] regaddr Buffer containing the slave register you wish to read from. + /// \param[in] buf Buffer of bytes to receive. + /// \param[in] len Number of bytes in the buf buffer, and the number of bytes to received. + /// \return reason see \ref bcm2835I2CReasonCodes + extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len); + + /// @} + + /// \defgroup st System Timer access + /// Allows access to and delays using the System Timer Counter. + /// @{ + + /// Read the System Timer Counter register. + /// \return the value read from the System Timer Counter Lower 32 bits register + uint64_t bcm2835_st_read(void); + + /// Delays for the specified number of microseconds with offset. + /// \param[in] offset_micros Offset in microseconds + /// \param[in] micros Delay in microseconds + extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros); + + /// @} + +#ifdef __cplusplus +} +#endif + +#endif // BCM2835_H + +/// @example blink.c +/// Blinks RPi GPIO pin 11 on and off + +/// @example input.c +/// Reads the state of an RPi input pin + +/// @example event.c +/// Shows how to use event detection on an input pin + +/// @example spi.c +/// Shows how to use SPI interface to transfer a byte to and from an SPI device + +/// @example spin.c +/// Shows how to use SPI interface to transfer a number of bytes to and from an SPI device diff --git a/kawaii/emergency_stop.cpp b/kawaii/emergency_stop.cpp new file mode 100644 index 0000000..b35b266 --- /dev/null +++ b/kawaii/emergency_stop.cpp @@ -0,0 +1,29 @@ +// +// Created by Simon Wörner on 16.06.17. +// + +#include "emergency_stop.h" + +#include + +extern "C" { +void *emergency_stop_init(uint8_t port); +void emergency_stop_clean(void *emergency_stop); +bool emergency_stop_get_state(void *emergency_stop); +} + +emergency_stop::emergency_stop(uint8_t port) +{ + rust_emergency_stop = emergency_stop_init(port); +} + +emergency_stop::~emergency_stop() +{ + emergency_stop_clean(rust_emergency_stop); + rust_emergency_stop = NULL; +} + +bool emergency_stop::get_state() const +{ + return emergency_stop_get_state(rust_emergency_stop); +} \ No newline at end of file diff --git a/kawaii/emergency_stop.h b/kawaii/emergency_stop.h new file mode 100644 index 0000000..41dda6b --- /dev/null +++ b/kawaii/emergency_stop.h @@ -0,0 +1,24 @@ +// +// Created by Simon Wörner on 16.06.17. +// + +#ifndef KAWAII_ENGINE_EMERGENCY_STOP_H +#define KAWAII_ENGINE_EMERGENCY_STOP_H + + +#include + +class emergency_stop +{ +private: + void *rust_emergency_stop; +public: + emergency_stop(uint8_t port); + emergency_stop(const emergency_stop &) = delete; + emergency_stop(const emergency_stop &&) = delete; + bool get_state() const; + ~emergency_stop(); +}; + + +#endif //KAWAII_ENGINE_EMERGENCY_STOP_H diff --git a/kawaii/engine.cpp b/kawaii/engine.cpp new file mode 100644 index 0000000..329a940 --- /dev/null +++ b/kawaii/engine.cpp @@ -0,0 +1,79 @@ +#include "engine.hpp" + +#include +#include + +using namespace std; +using namespace std::chrono; + +engine::engine(gpio&& pin_forward, gpio&& pin_reverse) + : speed(0), + pin_forward(move(pin_forward)), + pin_reverse(move(pin_reverse)), + stop_thread(false), + measurement(string("engine") + to_string(this->pin_forward.get_pin())) +{ + this->pin_forward.set_value(false); + this->pin_reverse.set_value(false); + pwm_thread = thread(&engine::pwm_loop, this); +} + +engine::~engine() +{ + stop_thread = true; + pwm_thread.join(); +} + +void engine::set_speed(int speed) +{ + this->speed.store(speed, memory_order_relaxed); +} + +void engine::pwm_loop() +{ + this_thread::sleep_for(milliseconds(100)); + const int sleep_msec = 1; + int counter = 0; + high_resolution_clock::time_point next_sleep_target = high_resolution_clock::now(); + while (!stop_thread) + { + measurement.start(); + direction target_direction; + int speed_val = speed.load(memory_order_relaxed); + if (speed_val < 0) + { + target_direction = direction::REVERSE; + speed_val *= -1; + } + else + { + target_direction = direction::FORWARD; + } + counter += sleep_msec; + if (counter >= 100) + counter = 0; + if (counter < speed_val) + { + if (target_direction == direction::FORWARD) + { + pin_reverse.set_value(false); + pin_forward.set_value(true); + } + else + { + pin_forward.set_value(false); + pin_reverse.set_value(true); + } + } + else + { + pin_forward.set_value(false); + pin_reverse.set_value(false); + } + measurement.stop(); + next_sleep_target += 500us; + this_thread::sleep_until(next_sleep_target); + } + pin_forward.set_value(false); + pin_reverse.set_value(false); +} diff --git a/kawaii/engine.hpp b/kawaii/engine.hpp new file mode 100644 index 0000000..e02a52e --- /dev/null +++ b/kawaii/engine.hpp @@ -0,0 +1,31 @@ +#ifndef ENGINE_HPP_ +#define ENGINE_HPP_ + +#include +#include +#include "gpio.hpp" +#include "measure.hpp" + +class engine +{ +public: + enum class direction + { + FORWARD, + REVERSE + }; +private: + std::atomic speed; + gpio pin_forward; + gpio pin_reverse; + std::atomic stop_thread; + std::thread pwm_thread; + measure measurement; + void pwm_loop(); +public: + engine(gpio&& pin_forward, gpio&& pin_reverse); + ~engine(); + void set_speed(int speed); +}; + +#endif diff --git a/kawaii/gpio.cpp b/kawaii/gpio.cpp new file mode 100644 index 0000000..5e4d73a --- /dev/null +++ b/kawaii/gpio.cpp @@ -0,0 +1,109 @@ +#include "gpio.hpp" + +#include +#include +#include +#include + +using namespace std; + +static string construct_filename(const string &prefix, int pin, const string &suffix) +{ + stringstream filename; + filename << prefix << pin << suffix; + return filename.str(); +} + +gpio::gpio(int pin, pin_direction direction, pin_type type, bool default_state) + : pin(pin), + direction(direction), + type(type), + current_state(false) +{ + ofstream export_file("/sys/class/gpio/export"); + export_file << pin; + export_file.close(); + ofstream direction_file(construct_filename("/sys/class/gpio/gpio", pin, "/direction").c_str()); + if (direction == pin_direction::INPUT) + { + direction_file << "in"; + } + else + { + direction_file << "out"; + } + direction_file.close(); + if (direction == pin_direction::OUTPUT) + { + set_value(default_state, false); + } +} + +gpio::gpio(gpio&& old) + : pin(old.pin), + direction(old.direction), + type(old.type), + current_state(old.current_state) +{ + old.pin = -1; +} + +gpio::~gpio() +{ + if (pin != -1) + { + ofstream unexport_file("/sys/class/gpio/unexport"); + unexport_file << pin; + } +} + +void gpio::set_value(bool on, bool use_cache) +{ + if (pin == -1) + throw logic_error("Usage of moved gpio"); + if (direction == pin_direction::INPUT) + { + stringstream errormsg; + errormsg << "Cannot write to input pin " << pin; + throw logic_error(errormsg.str()); + } + if (!use_cache || current_state != on) + { + bool value; + if (type == pin_type::HIGH_ON) + value = on; + else + value = !on; + ofstream value_file(construct_filename("/sys/class/gpio/gpio", pin, "/value")); + value_file << value; + } + current_state = on; +} + +void gpio::set_value(bool on) +{ + set_value(on, true); +} + +bool gpio::get_value() +{ + if (pin == -1) + throw logic_error("Usage of moved gpio"); + if (direction == pin_direction::OUTPUT) + return current_state; + ifstream value_file(construct_filename("/sys/class/gpio/gpio", pin, "/value")); + bool value; + value_file >> value; + bool on; + if (type == pin_type::HIGH_ON) + on = value; + else + on = !value; + return on; +} + +int gpio::get_pin() const +{ + return pin; +} + diff --git a/kawaii/gpio.hpp b/kawaii/gpio.hpp new file mode 100644 index 0000000..900c087 --- /dev/null +++ b/kawaii/gpio.hpp @@ -0,0 +1,34 @@ +#ifndef GPIO_HPP_ +#define GPIO_HPP_ + +class gpio +{ +public: + enum class pin_direction + { + INPUT, + OUTPUT + }; + enum class pin_type + { + HIGH_ON, + LOW_ON + }; + +private: + int pin; + pin_direction direction; + pin_type type; + bool current_state; + void set_value(bool on, bool use_cache); +public: + gpio(int pin, pin_direction, pin_type=pin_type::HIGH_ON, bool default_state=false); + gpio(const gpio&) = delete; + gpio(gpio&&); + ~gpio(); + void set_value(bool on); + bool get_value(); + int get_pin() const; +}; + +#endif diff --git a/kawaii/kawaii-rs/.gitignore b/kawaii/kawaii-rs/.gitignore new file mode 100644 index 0000000..03e759d --- /dev/null +++ b/kawaii/kawaii-rs/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +target/ +**/*.rs.bk +*.iml \ No newline at end of file diff --git a/kawaii/kawaii-rs/CMakeLists.txt b/kawaii/kawaii-rs/CMakeLists.txt new file mode 100644 index 0000000..122db1b --- /dev/null +++ b/kawaii/kawaii-rs/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.0) +project(kawaii-rs) + +include(ExternalProject) + +if (MEASURE) + set(CARGO_FEATURES "${CARGO_FEATURES} measure") +endif (MEASURE) + +if (CARGO_FEATURES) + set(CARGO_ARGUMENTS --features "${CARGO_FEATURES}") +endif(CARGO_FEATURES) + +file(GLOB RUST_SOURCE_FILES "${PROJECT_SOURCE_DIR}/src" *.rs) +add_custom_target(${PROJECT_NAME} + COMMAND cargo build --color=never ${CARGO_ARGUMENTS} + COMMAND cargo build --color=never ${CARGO_ARGUMENTS} --release + DEPENDS ${RUST_SOURCE_FILES} + BYPRODUCTS target/debug/libkawaii.a target/release/libkawaii.a + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" + SOURCES ${RUST_SOURCE_FILES}) \ No newline at end of file diff --git a/kawaii/kawaii-rs/Cargo.toml b/kawaii/kawaii-rs/Cargo.toml new file mode 100644 index 0000000..e766209 --- /dev/null +++ b/kawaii/kawaii-rs/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "kawaii-rs-api" +version = "0.1.0" +authors = ["Simon Wörner "] + +[lib] +name = "kawaii" +crate-type = ["staticlib"] + +[features] +measure = [ + "kawaii/measure", + "emergency_stop/measure", + "ultrasonic_irq/measure" ] + +[dependencies] +emergency_stop = { git = "https://git.brn.li/kawaii-robotto/emergency-stop.git" } +kawaii = { git = "https://git.brn.li/kawaii-robotto/kawaii-rs.git" } +ultrasonic_irq = { git = "https://git.brn.li/kawaii-robotto/ultrasonic-irq.git" } \ No newline at end of file diff --git a/kawaii/kawaii-rs/dependencies/emergency-stop/.gitignore b/kawaii/kawaii-rs/dependencies/emergency-stop/.gitignore new file mode 100644 index 0000000..b7b0017 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/emergency-stop/.gitignore @@ -0,0 +1,8 @@ +CMakeFiles/ +Makefile +cmake_install.cmake +emergency-stop-prefix/ +emergency-stop.cbp +target/ +**/*.rs.bk +*.iml \ No newline at end of file diff --git a/kawaii/kawaii-rs/dependencies/emergency-stop/Cargo.toml b/kawaii/kawaii-rs/dependencies/emergency-stop/Cargo.toml new file mode 100644 index 0000000..5e19070 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/emergency-stop/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "emergency_stop" +version = "0.1.0" +authors = ["Simon Wörner "] + +[features] +measure = ["kawaii/measure"] + +[dependencies] +kawaii = { git = "https://git.brn.li/kawaii-robotto/kawaii-rs.git" } \ No newline at end of file diff --git a/kawaii/kawaii-rs/dependencies/emergency-stop/src/lib.rs b/kawaii/kawaii-rs/dependencies/emergency-stop/src/lib.rs new file mode 100644 index 0000000..3a9afe7 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/emergency-stop/src/lib.rs @@ -0,0 +1,96 @@ +extern crate kawaii; + +use std::time::Duration; +use std::thread; +use std::thread::JoinHandle; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; + +use self::kawaii::gpio::{AsyncPort, Edge, Value}; + +#[cfg(feature = "measure")] +use self::kawaii::Measure; + +#[derive(Debug)] +pub struct EmergencyStop { + thread: Option>, + pub state: Arc, +} + +impl EmergencyStop { + pub fn new(stop_port: u8) -> std::io::Result { + let name = format!("EmergencyStop(port = {})", stop_port); + let state = Arc::new(AtomicBool::new(false)); + let mut port = AsyncPort::new(stop_port, Edge::Both)?; + + let state_clone = state.clone(); + let thread = thread::Builder::new() + .name(name) + .spawn(move || EmergencyStop::thread(&mut port, state_clone))?; + + Ok(EmergencyStop { + thread: Some(thread), + state: state, + }) + } + + fn thread(port: &mut AsyncPort, state: Arc) { + #[cfg(feature = "measure")] + let mut measure = Measure::new(format!("EmergencyStop(port = {})", port.port.number)); + + // clear first value + port.poll(Some(Duration::new(0, 0))).is_ok(); + + while !state.load(Ordering::Relaxed) { + #[cfg(feature = "measure")] + measure.start(); + + let timeout = Some(Duration::new(1, 0)); + + #[cfg(not(feature = "measure"))] + let value = port.poll(timeout); + + #[cfg(feature = "measure")] + let value = port.poll_measure(timeout, &mut measure); + + // continue on timeout + match value { + Ok(value) => { + if let Some(value) = value { + match value { + Value::High => { + #[cfg(debug_assertions)] + println!("EmergencyStop! ({:?})", value); + + state.store(true, Ordering::Relaxed); + } + _ => { + #[cfg(debug_assertions)] + println!("EmergencyStop ignored: ({:?})", value); + } + } + } + } + Err(e) => { + #[cfg(debug_assertions)] + println!("EmergencyStop! ({:?})", e); + + state.store(true, Ordering::Relaxed); + } + } + + #[cfg(feature = "measure")] + measure.stop(); + } + } +} + +impl Drop for EmergencyStop { + fn drop(&mut self) { + self.state.store(true, Ordering::Relaxed); + + if let Some(thread) = self.thread.take() { + thread.join().is_ok(); + } + } +} diff --git a/kawaii/kawaii-rs/dependencies/emergency-stop/src/main.rs b/kawaii/kawaii-rs/dependencies/emergency-stop/src/main.rs new file mode 100644 index 0000000..e5226d6 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/emergency-stop/src/main.rs @@ -0,0 +1,21 @@ +extern crate emergency_stop; + +use emergency_stop::EmergencyStop; + +use std::time::Duration; +use std::thread::sleep; +use std::sync::atomic::Ordering; + +static GPIO_PORT_STOP: u8 = 25; + +fn main() { + let emergency_stop = EmergencyStop::new(GPIO_PORT_STOP).expect("Create Emergency Stop failed"); + + println!("{:?}", emergency_stop); + + while !emergency_stop.state.load(Ordering::Relaxed) { + sleep(Duration::new(1, 0)); + } + + println!("Stopped."); +} diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/.gitignore b/kawaii/kawaii-rs/dependencies/kawaii-rs/.gitignore new file mode 100644 index 0000000..03e759d --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/.gitignore @@ -0,0 +1,4 @@ +Cargo.lock +target/ +**/*.rs.bk +*.iml \ No newline at end of file diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/Cargo.toml b/kawaii/kawaii-rs/dependencies/kawaii-rs/Cargo.toml new file mode 100644 index 0000000..ee55705 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "kawaii" +version = "0.1.0" +authors = ["Simon Wörner "] + +[features] +measure = [] + +[dependencies] +nix = "0.8.1" +regex = "^0.2" +separator = "^0.3.1" +time = "^0.1.36" diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/src/gpio.rs b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/gpio.rs new file mode 100644 index 0000000..dbae7be --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/gpio.rs @@ -0,0 +1,288 @@ +extern crate std; +extern crate nix; + +use std::fmt; +use std::time::Duration; +use std::io::prelude::*; +use std::io::{Error, ErrorKind, SeekFrom}; +use std::fs::File; +use std::path::Path; + +use std::os::unix::io::RawFd; + +#[cfg(feature = "measure")] +use measure::Measure; + +fn duration_to_ms(duration: Duration) -> u64 { + duration.as_secs() * 1_000u64 + duration.subsec_nanos() as u64 / 1_000_000u64 +} + +#[derive(Clone, Copy, Debug)] +pub enum Direction { + Out, + In, +} +impl Direction { + pub fn from_str(s: &str) -> Option { + match s { + "out" => Some(Direction::Out), + "in" => Some(Direction::In), + _ => None, + } + } + + pub fn as_str(&self) -> &'static str { + match *self { + Direction::Out => "out", + Direction::In => "in", + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum Edge { + None, + Rising, + Falling, + Both, +} +impl Edge { + pub fn from_str(s: &str) -> Option { + match s { + "none" => Some(Edge::None), + "rising" => Some(Edge::Rising), + "falling" => Some(Edge::Falling), + "both" => Some(Edge::Both), + _ => None, + } + } + + pub fn as_str(&self) -> &'static str { + match *self { + Edge::None => "none", + Edge::Rising => "rising", + Edge::Falling => "falling", + Edge::Both => "both", + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum Value { + High, + Low, +} +impl Value { + pub fn from_str(s: &str) -> Option { + match s { + "1" => Some(Value::High), + "0" => Some(Value::Low), + _ => None, + } + } + + pub fn from_buffer(b: &[u8; 1]) -> Option { + Value::from_char(b[0]) + } + + pub fn from_char(c: u8) -> Option { + match c { + 48 => Some(Value::Low), // '0' + 49 => Some(Value::High), // '1' + _ => None, + } + } + + pub fn as_str(&self) -> &'static str { + match *self { + Value::High => "1", + Value::Low => "0", + } + } +} + +#[derive(Debug)] +pub struct Port { + pub number: u8, + pub direction: Direction, +} + +#[derive(Debug)] +pub struct SyncPort { + pub port: Port, + file: File, + buffer: [u8; 1], +} +pub struct AsyncPort { + pub port: Port, + pub edge: Edge, + file: RawFd, + fds: [nix::poll::PollFd; 1], + buffer: [u8; 1], +} + +impl fmt::Debug for AsyncPort { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "AsyncPort {{ port: {:?}, edge: {:?}, file: {:?}, fds: [?], buffer: {:?} }}", + self.port, + self.edge, + self.file, + self.buffer) + } +} + +impl Port { + pub fn new(number: u8, direction: Direction) -> std::io::Result { + let port = Port { + number: number, + direction: direction, + }; + + port.init()?; + Ok(port) + } + + fn init(&self) -> std::io::Result<()> { + self.export().ok(); + self.set_direction()?; + + Ok(()) + } + + pub fn drop(&mut self) { + self.unexport().ok(); + } + + fn write_path(path: &str, value: &str) -> std::io::Result<()> { + File::create(Path::new(path))? + .write_all(value.as_bytes()) + } + + fn export(&self) -> std::io::Result<()> { + Port::write_path("/sys/class/gpio/export", self.number.to_string().as_str()) + } + fn unexport(&self) -> std::io::Result<()> { + Port::write_path("/sys/class/gpio/unexport", self.number.to_string().as_str()) + } + + fn set_direction(&self) -> std::io::Result<()> { + Port::write_path(format!("/sys/class/gpio/gpio{}/direction", self.number).as_str(), + self.direction.as_str()) + } +} + +impl SyncPort { + pub fn new(number: u8, direction: Direction) -> std::io::Result { + Ok(SyncPort { + port: Port::new(number, direction)?, + file: SyncPort::open(number, direction)?, + buffer: [0; 1], + }) + } + + fn open(number: u8, direction: Direction) -> std::io::Result { + let path = format!("/sys/class/gpio/gpio{}/value", number); + let path = Path::new(path.as_str()); + + Ok(match direction { + Direction::Out => File::create(path)?, + Direction::In => File::open(path)?, + }) + } + + pub fn read(&mut self) -> std::io::Result { + self.file.seek(SeekFrom::Start(0))?; + self.file.read_exact(&mut self.buffer)?; + + Value::from_buffer(&self.buffer).ok_or(Error::new(ErrorKind::InvalidData, + "Unrecognized GPIO Value")) + } + + pub fn write(&mut self, value: Value) -> std::io::Result<()> { + self.file.write_all(value.as_str().as_bytes()) + } +} + +impl AsyncPort { + pub fn new(number: u8, edge: Edge) -> std::io::Result { + let port = Port::new(number, Direction::In)?; + let file = AsyncPort::open(number)?; + let port = AsyncPort { + port: port, + edge: edge, + file: file, + fds: [nix::poll::PollFd::new(file, nix::poll::POLLPRI, nix::poll::EventFlags::empty())], + buffer: [0; 1], + }; + + port.init()?; + Ok(port) + } + fn init(&self) -> std::io::Result<()> { + self.set_edge()?; + + Ok(()) + } + + fn open(number: u8) -> std::io::Result { + nix::fcntl::open(format!("/sys/class/gpio/gpio{}/value", number).as_str(), + nix::fcntl::O_RDONLY, + nix::sys::stat::Mode::empty()) + .or(Err(Error::new(ErrorKind::Other, "open failed"))) + } + + fn get_timeout(timeout: Option) -> i32 { + match timeout { + None => -1, + Some(t) => duration_to_ms(t) as i32, + } + } + + fn nix_poll(&mut self, timeout: i32) -> nix::Result { + nix::poll::poll(&mut self.fds, timeout) + } + + fn poll_read(&mut self, poll: nix::Result) -> std::io::Result> { + let poll = poll.or(Err(Error::new(ErrorKind::Other, "poll failed")))?; + + if poll == 0 { + return Ok(None); + } + + nix::unistd::lseek(self.file, 0, nix::unistd::Whence::SeekSet) + .or(Err(Error::new(ErrorKind::Other, "lseek failed")))?; + + nix::unistd::read(self.file, &mut self.buffer) + .or(Err(Error::new(ErrorKind::Other, "read failed")))?; + + Value::from_buffer(&self.buffer).map_or(Err(Error::new(ErrorKind::InvalidData, + "Unrecognized GPIO Value")), + |v| Ok(Some(v))) + } + + pub fn poll(&mut self, timeout: Option) -> std::io::Result> { + let poll = self.nix_poll(Self::get_timeout(timeout)); + + self.poll_read(poll) + } + + #[cfg(feature = "measure")] + pub fn poll_measure(&mut self, + timeout: Option, + measure: &mut Measure) + -> std::io::Result> { + let timeout = Self::get_timeout(timeout); + + measure.pause(); + let poll = self.nix_poll(timeout); + measure.start(); + + self.poll_read(poll) + } + + fn set_edge(&self) -> std::io::Result<()> { + Port::write_path(format!("/sys/class/gpio/gpio{}/edge", self.port.number).as_str(), + self.edge.as_str()) + } +} diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/src/lib.rs b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/lib.rs new file mode 100644 index 0000000..37d3de6 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/lib.rs @@ -0,0 +1,7 @@ +pub mod gpio; + +#[cfg(feature = "measure")] +mod measure; + +#[cfg(feature = "measure")] +pub use measure::Measure; \ No newline at end of file diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/src/main.rs b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/main.rs new file mode 100644 index 0000000..268618e --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/main.rs @@ -0,0 +1,35 @@ +extern crate kawaii; + +use std::time::Duration; + +use kawaii::gpio::{SyncPort, AsyncPort, Direction, Edge, Value}; + +#[cfg(feature = "measure")] +use kawaii::measure::Measure; + +fn main() { + println!("Hello, world!"); + + #[cfg(feature = "measure")] + { + let mut measure = Measure::new(String::from("main({test: 1})")); + + for _ in 0..5 { + measure.start(); + std::thread::sleep(Duration::new(0, 1_000_000)); + measure.stop(); + } + + println!("{:?}", measure); + } + + let mut trigger = SyncPort::new(27, Direction::Out).expect("Create Trigger GPIO failed"); + let mut echo = AsyncPort::new(28, Edge::Both).expect("Create Echo GPIO failed"); + + println!("trigger = {:?}", trigger.read()); + trigger.write(Value::High).expect("write failed"); + println!("trigger = {:?}", trigger.read()); + trigger.write(Value::Low).expect("write failed"); + println!("trigger = {:?}", trigger.read()); + println!("echo = {:?}", echo.poll(Some(Duration::new(1, 0)))); +} diff --git a/kawaii/kawaii-rs/dependencies/kawaii-rs/src/measure.rs b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/measure.rs new file mode 100644 index 0000000..11efd20 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/kawaii-rs/src/measure.rs @@ -0,0 +1,111 @@ +extern crate regex; +extern crate separator; +extern crate time; + +use std; +use std::fs::File; +use std::io::prelude::*; +use std::io::{Error, ErrorKind}; + +use self::regex::Regex; +use self::separator::Separatable; +use self::time::precise_time_ns; + +#[derive(Debug)] +pub struct Measure { + pub min: u64, + pub max: u64, + time: u64, + last: u64, + data: Vec, + pub name: String, +} + +impl Measure { + pub fn new(name: String) -> Self { + Measure { + min: u64::max_value(), + max: 0u64, + time: 0u64, + last: 0u64, + data: Vec::with_capacity(1_000_000), + name: name, + } + } + + pub fn start(&mut self) { + self.last = precise_time_ns(); + } + + pub fn pause(&mut self) { + if self.last == 0 { + #[cfg(debug_assertions)] + println!("WARNING: {:?} pause called without start!", self); + + return; + } + + self.time += self.time_diff(); + } + + pub fn stop(&mut self) { + if self.last == 0 { + #[cfg(debug_assertions)] + println!("WARNING: {:?} stop called without start!", self); + + return; + } + + self.time += self.time_diff(); + self.data.push(self.time); + + if self.time < self.min { + self.min = self.time; + } + + if self.time > self.max { + self.max = self.time; + } + + self.time = 0u64; + self.last = 0u64; + } + + fn time_diff(&mut self) -> u64 { + let current_time = precise_time_ns(); + let time_diff = current_time - self.last; + self.last = current_time; + + time_diff + } + + fn write_data(&self) -> std::io::Result<()> { + let re = Regex::new(r"[^\w\-.]") + .or(Err(Error::new(ErrorKind::Other, "Create filename regex failed.")))?; + + let file_name = format!("measure_{}.txt", self.name); + let file_name = re.replace_all(file_name.as_str(), "_").to_string(); + println!("{}: Write data to {}", self.name, file_name); + + let mut file = File::create(file_name)?; + + for value in &self.data { + file.write_fmt(format_args!("{}\n", value))?; + } + + Ok(()) + } +} + +impl Drop for Measure { + fn drop(&mut self) { + println!("{}:\n\tmin: {} ns\n\tmax: {} ns", + self.name, + self.min.separated_string(), + self.max.separated_string()); + + if let Err(e) = self.write_data() { + println!("{}: Write measure data failed: {}", self.name, e); + } + } +} diff --git a/kawaii/kawaii-rs/dependencies/ultrasonic-irq/.gitignore b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/.gitignore new file mode 100644 index 0000000..d0f4ebb --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/.gitignore @@ -0,0 +1,7 @@ +CMakeFiles/ +Makefile +cmake_install.cmake +ultrasonic-irq-prefix/ +ultrasonic-irq.cbp +target/ +**/*.rs.bk diff --git a/kawaii/kawaii-rs/dependencies/ultrasonic-irq/Cargo.toml b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/Cargo.toml new file mode 100644 index 0000000..45f029a --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ultrasonic_irq" +version = "0.1.0" +authors = ["kawaii "] + +[features] +measure = ["kawaii/measure"] + +[dependencies] +time = "^0.1.36" +shuteye = "^0.3.2" +kawaii = { git = "https://git.brn.li/kawaii-robotto/kawaii-rs.git" } diff --git a/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/lib.rs b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/lib.rs new file mode 100644 index 0000000..aa6db09 --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/lib.rs @@ -0,0 +1,194 @@ +#![feature(integer_atomics)] + +extern crate time; +extern crate shuteye; +extern crate kawaii; + +use std::time::Duration; +use std::thread; +use std::thread::JoinHandle; +use std::sync::mpsc::{Sender, Receiver}; +use std::sync::mpsc; +use std::sync::Arc; +use std::sync::atomic::{AtomicU32, Ordering}; + +use self::kawaii::gpio::{SyncPort, AsyncPort, Direction, Edge, Value}; + +#[cfg(feature = "measure")] +use self::kawaii::Measure; + +use self::time::precise_time_ns; +use self::shuteye::sleep; + +#[derive(Debug)] +struct ThreadData { + thread: JoinHandle, + tx: Sender<()>, +} + +#[derive(Debug)] +pub struct UltrasonicEcho { + echo: AsyncPort, + temperature: u8, + timestamp: u64, + distance: Arc, +} + +#[derive(Debug)] +pub struct UltrasonicTrigger { + trigger: SyncPort, +} + +#[derive(Debug)] +pub struct Ultrasonic { + trigger: Option>, + echo: Option>, + pub distance: Arc, +} + +impl Ultrasonic { + pub fn new(trigger_port: u8, echo_port: u8, temperature: u8) -> std::io::Result { + let distance = Arc::new(AtomicU32::new(u32::max_value())); + let echo = UltrasonicEcho { + echo: AsyncPort::new(echo_port, Edge::Both)?, + temperature: temperature, + timestamp: 0, + distance: distance.clone(), + }; + let (tx, rx): (Sender<()>, Receiver<()>) = mpsc::channel(); + let name = format!("Ultrasonic::echo(port = {})", echo_port); + let echo = ThreadData::<()> { + thread: thread::Builder::new() + .name(name) + .spawn(move || echo.thread(rx))?, + tx: tx, + }; + + let trigger = UltrasonicTrigger { trigger: SyncPort::new(trigger_port, Direction::Out)? }; + let (tx, rx): (Sender<()>, Receiver<()>) = mpsc::channel(); + let name = format!("Ultrasonic::trigger(port = {})", trigger_port); + let trigger = ThreadData::<()> { + thread: thread::Builder::new() + .name(name) + .spawn(move || trigger.thread(rx))?, + tx: tx, + }; + + Ok(Ultrasonic { + trigger: Some(trigger), + echo: Some(echo), + distance: distance, + }) + } +} + +impl Drop for Ultrasonic { + fn drop(&mut self) { + if let Some(echo) = self.echo.take() { + echo.tx.send(()).is_ok(); + echo.thread.join().is_ok(); + } + + if let Some(trigger) = self.trigger.take() { + trigger.tx.send(()).is_ok(); + trigger.thread.join().is_ok(); + } + } +} + +impl UltrasonicTrigger { + fn thread(mut self, stop_rx: Receiver<()>) { + #[cfg(feature = "measure")] + let mut measure = Measure::new(format!("Ultrasonic::trigger(port = {})", self.trigger.port.number)); + + while stop_rx.try_recv().is_err() { + #[cfg(feature = "measure")] + measure.start(); + self.trigger.write(Value::Low).is_ok(); + #[cfg(feature = "measure")] + measure.pause(); + + // sleep 10us (min length to trigger) + sleep(Duration::new(0, 10_000)); + + #[cfg(feature = "measure")] + measure.start(); + self.trigger.write(Value::High).is_ok(); + #[cfg(feature = "measure")] + measure.stop(); + + // sleep 20ms (max trigger frequency) + sleep(Duration::new(0, 20_000_000)); + } + } +} + +impl UltrasonicEcho { + fn thread(mut self, stop_rx: Receiver<()>) { + #[cfg(feature = "measure")] + let mut measure = Measure::new(format!("Ultrasonic::echo(port = {})", self.echo.port.number)); + + while stop_rx.try_recv().is_err() { + #[cfg(feature = "measure")] + measure.start(); + + #[cfg(not(feature = "measure"))] + let value = self.echo.poll(Some(Duration::new(1, 0))); + + #[cfg(feature = "measure")] + let value = self.echo.poll_measure(Some(Duration::new(1, 0)), &mut measure); + + match value { + Ok(value) => { + if let Some(value) = value { + self.on_value_changed(value); + } + } + Err(_e) => { + #[cfg(debug_assertions)] + println!("POLL failed: e = {:?}", _e); + } + } + + #[cfg(feature = "measure")] + measure.stop(); + } + } + + fn on_value_changed(&mut self, value: Value) { + match value { + Value::High => { + // Switched from Value::High to Value::High + // possibly because of trigger after timeout and slow reading + if self.timestamp > 0 { + #[cfg(debug_assertions)] + println!("{:?}", self); + + self.calc_distance(); + } + + self.timestamp = precise_time_ns() + } + Value::Low => { + // Switched from Value::Low to Value::Low + if self.timestamp == 0 { + #[cfg(debug_assertions)] + println!("{:?}", self); + } + + self.calc_distance(); + } + } + } + + fn calc_distance(&mut self) { + let time_diff = precise_time_ns() - self.timestamp; + let distance = (3315 + self.temperature as u64 * 6) * time_diff / 20_000_000; + self.distance.store(distance as u32, Ordering::Relaxed); + + #[cfg(debug_assertions)] + println!("time diff: {}\tdistance: {}mm", time_diff, distance); + + self.timestamp = 0u64; + } +} diff --git a/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/main.rs b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/main.rs new file mode 100644 index 0000000..c19a4cd --- /dev/null +++ b/kawaii/kawaii-rs/dependencies/ultrasonic-irq/src/main.rs @@ -0,0 +1,21 @@ +extern crate shuteye; + +extern crate ultrasonic_irq; + +use std::time::Duration; +use std::thread::sleep; + +use ultrasonic_irq::Ultrasonic; + +static GPIO_PORT_TRIG: u8 = 23; +static GPIO_PORT_ECHO: u8 = 24; +static TEMPERATURE: u8 = 20; + +fn main() { + let ultrasonic = Ultrasonic::new(GPIO_PORT_TRIG, GPIO_PORT_ECHO, TEMPERATURE) + .expect("Create Ultrasonic failed"); + + println!("{:?}", ultrasonic); + + sleep(Duration::new(10, 0)); +} diff --git a/kawaii/kawaii-rs/src/emergency_stop.rs b/kawaii/kawaii-rs/src/emergency_stop.rs new file mode 100644 index 0000000..4b254d6 --- /dev/null +++ b/kawaii/kawaii-rs/src/emergency_stop.rs @@ -0,0 +1,27 @@ +extern crate emergency_stop; + +use self::emergency_stop::EmergencyStop; + +use std::mem::transmute; +use std::ptr::null_mut; +use std::sync::atomic::Ordering; + +#[no_mangle] +pub extern "C" fn emergency_stop_init(stop: u8) -> *mut EmergencyStop { + match EmergencyStop::new(stop) { + Err(_) => null_mut(), + Ok(emergency_stop) => unsafe { transmute(Box::new(emergency_stop)) }, + } +} + +#[no_mangle] +pub extern "C" fn emergency_stop_clean(emergency_stop: *mut EmergencyStop) { + let _emergency_stop: Box = unsafe { transmute(emergency_stop) }; +} + +#[no_mangle] +pub extern "C" fn emergency_stop_get_state(emergency_stop: *mut EmergencyStop) -> bool { + let emergency_stop = unsafe { &mut *emergency_stop }; + + emergency_stop.state.load(Ordering::Relaxed) +} diff --git a/kawaii/kawaii-rs/src/lib.rs b/kawaii/kawaii-rs/src/lib.rs new file mode 100644 index 0000000..1c2c31c --- /dev/null +++ b/kawaii/kawaii-rs/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(integer_atomics)] + +mod emergency_stop; +mod ultrasonic_irq; + +#[cfg(feature = "measure")] +mod measure; + +pub use emergency_stop::*; +pub use ultrasonic_irq::*; + +#[cfg(feature = "measure")] +pub use measure::*; diff --git a/kawaii/kawaii-rs/src/measure.rs b/kawaii/kawaii-rs/src/measure.rs new file mode 100644 index 0000000..0f3b231 --- /dev/null +++ b/kawaii/kawaii-rs/src/measure.rs @@ -0,0 +1,48 @@ +extern crate kawaii; + +use self::kawaii::Measure; + +use std::ffi::CStr; +use std::ptr::null_mut; +use std::mem::transmute; +use std::os::raw::c_char; + +#[no_mangle] +pub extern "C" fn measure_init(name: *const c_char) -> *mut Measure { + if name.is_null() { + return null_mut(); + } + + unsafe { + match CStr::from_ptr(name).to_str() { + Err(_) => null_mut(), + Ok(name) => transmute(Box::new(Measure::new(String::from(name)))), + } + } +} + +#[no_mangle] +pub extern "C" fn measure_clean(measure: *mut Measure) { + let _measure: Box = unsafe { transmute(measure) }; +} + +#[no_mangle] +pub extern "C" fn measure_start(measure: *mut Measure) { + let measure = unsafe { &mut *measure }; + + measure.start(); +} + +#[no_mangle] +pub extern "C" fn measure_pause(measure: *mut Measure) { + let measure = unsafe { &mut *measure }; + + measure.pause(); +} + +#[no_mangle] +pub extern "C" fn measure_stop(measure: *mut Measure) { + let measure = unsafe { &mut *measure }; + + measure.stop(); +} \ No newline at end of file diff --git a/kawaii/kawaii-rs/src/ultrasonic_irq.rs b/kawaii/kawaii-rs/src/ultrasonic_irq.rs new file mode 100644 index 0000000..dbf8c37 --- /dev/null +++ b/kawaii/kawaii-rs/src/ultrasonic_irq.rs @@ -0,0 +1,27 @@ +extern crate ultrasonic_irq; + +use std::mem::transmute; +use std::ptr::null_mut; +use std::sync::atomic::Ordering; + +use self::ultrasonic_irq::Ultrasonic; + +#[no_mangle] +pub extern "C" fn ultrasonic_init(trigger: u8, echo: u8, temperature: u8) -> *mut Ultrasonic { + match Ultrasonic::new(trigger, echo, temperature) { + Err(_) => null_mut(), + Ok(ultrasonic) => unsafe { transmute(Box::new(ultrasonic)) }, + } +} + +#[no_mangle] +pub extern "C" fn ultrasonic_clean(ultrasonic: *mut Ultrasonic) { + let _ultrasonic: Box = unsafe { transmute(ultrasonic) }; +} + +#[no_mangle] +pub extern "C" fn ultrasonic_get_distance(ultrasonic: *mut Ultrasonic) -> u32 { + let ultrasonic = unsafe { &mut *ultrasonic }; + + ultrasonic.distance.load(Ordering::Relaxed) +} diff --git a/kawaii/main.cpp b/kawaii/main.cpp new file mode 100644 index 0000000..541e330 --- /dev/null +++ b/kawaii/main.cpp @@ -0,0 +1,262 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "engine.hpp" +#include "measure.hpp" +#include "rfid_reader.hpp" +#include "ultrasound_sensor.hpp" +#include "emergency_stop.h" + +using namespace std; + +typedef chrono::high_resolution_clock clocktype; + +const int tickrate = 100; +const chrono::milliseconds tick_delay = 1000ms / tickrate; +const uint32_t exit_tag = 0xAE0B1E2B; +const int no_tags = 3; +const uint32_t distance_until_turn = 180; +chrono::milliseconds backoff_time = 100ms; +chrono::milliseconds turn_right_follow_time = 150ms; +chrono::milliseconds turn_right_backoff_time = 500ms; + +bool stop = false; + +enum class mmode +{ + STOP, + LABYRINTH_FIND_WALL, + LABYRINTH_BACKOFF, + LABYRINTH_TURN_RIGHT, + LABYRINTH_TURN_AWAY, + LABYRINTH_FOLLOW, + SEARCH_STRAIGHT, + SEARCH_TURN_RIGHT, + SEARCH_TURN_LEFT, + SEARCH_TAG_STOP, + HAPPY +}; + +void signal_handler(int sig) +{ + stop = true; +} + +int main() +{ + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); + engine right( + gpio(13, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(20, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + engine left( + gpio(19, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(26, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + gpio bwleftleft(18, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bwleftright(4, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bwrightleft(3, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bwrightright(2, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + emergency_stop stop_button(25); + ultrasound_sensor ultrasoundright(23, 24); + ultrasound_sensor ultrasoundleft(12, 6); + rfid_reader rfid; + // TODO This random is unseeded + minstd_rand random; + + clocktype::time_point mmode_end = clocktype::now(); + mmode mode = mmode::STOP; + mmode target_mode = mmode::LABYRINTH_FIND_WALL; + clocktype::time_point mode_reset_time; + + int last_tag = 0x00000000; + vector found_tags; + found_tags.reserve(no_tags); + + measure measurement("main"); + + clocktype::time_point next_tick = clocktype::now(); + while (!stop) + { + measurement.start(); + clocktype::time_point tick_start = clocktype::now(); + switch (mode) + { + case mmode::LABYRINTH_FOLLOW: + case mmode::LABYRINTH_BACKOFF: + case mmode::LABYRINTH_TURN_RIGHT: + case mmode::LABYRINTH_TURN_AWAY: + case mmode::LABYRINTH_FIND_WALL: + { + if (tick_start >= mode_reset_time) + { + switch (mode) + { + case mmode::LABYRINTH_BACKOFF: + target_mode = mmode::LABYRINTH_TURN_AWAY; + mode_reset_time = tick_start + turn_right_backoff_time; + break; + case mmode::LABYRINTH_TURN_AWAY: + target_mode = mmode::LABYRINTH_FIND_WALL; + break; + case mmode::LABYRINTH_TURN_RIGHT: + target_mode = mmode::LABYRINTH_FOLLOW; + break; + } + } + if (bwrightleft.get_value() || bwrightright.get_value()) + { + target_mode = mmode::LABYRINTH_BACKOFF; + mode_reset_time = tick_start + backoff_time; + } + else if (bwleftleft.get_value() || bwleftright.get_value()) + { + if (mode != mmode::LABYRINTH_BACKOFF) + { + target_mode = mmode::LABYRINTH_TURN_RIGHT; + mode_reset_time = tick_start + turn_right_follow_time; + } + } + int last_id = rfid.last_id(); + if (last_id == exit_tag) + { + target_mode = mmode::SEARCH_STRAIGHT; + last_tag = last_id; + } + } + break; + case mmode::SEARCH_STRAIGHT: + { + auto usoundright = ultrasoundright.get_value(); + auto usoundleft = ultrasoundleft.get_value(); + if (min(usoundright, usoundleft) < distance_until_turn) + { + if (std::abs((long long int) usoundright - usoundleft) < 50) + { + int angle = random() % 180 + 90; + if (angle <= 180) + { + target_mode = mmode::SEARCH_TURN_RIGHT; + } + else + { + target_mode = mmode::SEARCH_TURN_LEFT; + angle = 360 - angle; + } + mode_reset_time = tick_start + 3ms * angle; + } + else + { + int angle = random() % 90 + 90; + mode_reset_time = tick_start + 3ms * angle; + if (usoundright < usoundleft) + target_mode = mmode::SEARCH_TURN_LEFT; + else + target_mode = mmode::SEARCH_TURN_RIGHT; + } + } + int last_id = rfid.last_id(); + if (last_id != last_tag && last_id != exit_tag) + { + last_tag = last_id; + if (find(found_tags.begin(), found_tags.end(), last_tag) == found_tags.end()) + { + found_tags.push_back(last_tag); + target_mode = mmode::SEARCH_TAG_STOP; + mode_reset_time = tick_start + 200ms; + if (found_tags.size() == no_tags) + { + target_mode = mmode::HAPPY; + mode_reset_time = tick_start + 2s; + } + } + } + } + break; + case mmode::SEARCH_TURN_LEFT: + case mmode::SEARCH_TURN_RIGHT: + case mmode::SEARCH_TAG_STOP: + if (tick_start >= mode_reset_time) + { + target_mode = mmode::SEARCH_STRAIGHT; + } + break; + case mmode::HAPPY: + if (tick_start >= mode_reset_time) + { + target_mode = mmode::STOP; + stop = true; + } + break; + } + + if (stop_button.get_state()) + { + target_mode = mmode::STOP; + stop = true; + } + + if (target_mode != mode) + { + mode = target_mode; + switch (mode) + { + case mmode::STOP: + left.set_speed(0); + right.set_speed(0); + break; + case mmode::LABYRINTH_FIND_WALL: + left.set_speed(60); + right.set_speed(60); + break; + case mmode::LABYRINTH_BACKOFF: + left.set_speed(-100); + right.set_speed(-100); + break; + case mmode::LABYRINTH_TURN_AWAY: + case mmode::LABYRINTH_TURN_RIGHT: + left.set_speed(100); + right.set_speed(-60); + break; + case mmode::LABYRINTH_FOLLOW: + left.set_speed(0); + right.set_speed(100); + break; + case mmode::SEARCH_STRAIGHT: + left.set_speed(100); + right.set_speed(100); + break; + case mmode::HAPPY: + case mmode::SEARCH_TURN_LEFT: + left.set_speed(-100); + right.set_speed(100); + break; + case mmode::SEARCH_TURN_RIGHT: + left.set_speed(100); + right.set_speed(-100); + break; + case mmode::SEARCH_TAG_STOP: + left.set_speed(-100); + right.set_speed(-100); + break; + } + } + + measurement.stop(); + + if (!stop) + { + next_tick += tick_delay; + this_thread::sleep_until(next_tick); + } + } + + return 0; +} diff --git a/kawaii/measure.cpp b/kawaii/measure.cpp new file mode 100644 index 0000000..5ae0437 --- /dev/null +++ b/kawaii/measure.cpp @@ -0,0 +1,50 @@ +#include "measure.hpp" + +#include + +#ifdef MEASURE +extern "C" { + void *measure_init(const char *name); + void measure_clean(void *measure); + void measure_start(void *measure); + void measure_pause(void *measure); + void measure_stop(void *measure); +} +#endif + +measure::measure(const std::string &name) +{ +#ifdef MEASURE + measure_ptr = measure_init(name.c_str()); +#endif +} + +measure::~measure() +{ +#ifdef MEASURE + measure_clean(measure_ptr); + measure_ptr = NULL; +#endif + +} + +void measure::start() +{ +#ifdef MEASURE + measure_start(measure_ptr); +#endif +} + +void measure::pause() +{ +#ifdef MEASURE + measure_pause(measure_ptr); +#endif +} + +void measure::stop() +{ +#ifdef MEASURE + measure_stop(measure_ptr); +#endif +} \ No newline at end of file diff --git a/kawaii/measure.hpp b/kawaii/measure.hpp new file mode 100644 index 0000000..5dd5ff1 --- /dev/null +++ b/kawaii/measure.hpp @@ -0,0 +1,22 @@ +#ifndef MEASURE_HPP_ +#define MEASURE_HPP_ + +#include + +class measure +{ +private: +#ifdef MEASURE + void *measure_ptr; +#endif +public: + measure(const std::string &); + measure(const measure &) = delete; + measure(measure &&) = delete; + ~measure(); + void start(); + void stop(); + void pause(); +}; + +#endif diff --git a/kawaii/remote.cpp b/kawaii/remote.cpp new file mode 100644 index 0000000..de134bf --- /dev/null +++ b/kawaii/remote.cpp @@ -0,0 +1,123 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "engine.hpp" + +using namespace std; +using boost::asio::ip::tcp; +using boost::asio::io_service; + +template +class prodcon +{ +private: + mutex mut; + condition_variable condition; + queue q; +public: + void push(T data) + { + unique_lock lock(mut); + q.push(data); + condition.notify_one(); + } + template + bool pop(T &result, const chrono::duration<_Rep, _Period> __rtime) + { + unique_lock lock(mut); + bool success = condition.wait_for(lock, __rtime, [this] {return !this->q.empty();}); + if (!success) + return false; + result = q.front(); + q.pop(); + return true; + } +}; + +class remote_server +{ +private: + tcp::acceptor acceptor; + io_service &io; + thread engine_thread; + atomic stop_thread; + prodcon> commands; +public: + remote_server(io_service &io_service); + void run(); + void engine_loop(); +}; + +int main() +{ + io_service io; + remote_server server(io); + server.run(); + return 0; +} + +remote_server::remote_server(io_service &io_service) + : acceptor(io_service), + io(io_service), + stop_thread(false) +{ + // Nothing to do +} + +void remote_server::run() +{ + tcp::endpoint endpoint(tcp::v4(), 1337); + acceptor.open(endpoint.protocol()); + acceptor.set_option(tcp::acceptor::reuse_address(true)); + acceptor.bind(endpoint); + acceptor.listen(1); + while (true) + { + tcp::socket socket(io); + acceptor.accept(socket); + stop_thread = false; + engine_thread = thread(&remote_server::engine_loop, this); + while (true) + { + int8_t speed_left, speed_right; + socket.read_some(boost::asio::buffer(&speed_left, 1)); + socket.read_some(boost::asio::buffer(&speed_right, 1)); + commands.push(pair(speed_left, speed_right)); + } + } +} + +void remote_server::engine_loop() +{ + engine left( + gpio(13, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(20, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + engine right( + gpio(19, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(26, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + while (!stop_thread) + { + pair command; + if (commands.pop(command, 1s)) + { + left.set_speed(command.first); + right.set_speed(command.second); + } + else + { + left.set_speed(0); + right.set_speed(0); + } + } +} diff --git a/kawaii/rfid_reader.cpp b/kawaii/rfid_reader.cpp new file mode 100644 index 0000000..f276e84 --- /dev/null +++ b/kawaii/rfid_reader.cpp @@ -0,0 +1,61 @@ +#include "rfid_reader.hpp" +#include +#include +#include +#include +#include + +rfid_reader::rfid_reader() + : measurement("rfid_reader") +{ + mfrc.PCD_Init(); + stop_thread = false; + uid = 0; + thread = std::thread(&rfid_reader::loop, this); +} + +rfid_reader::~rfid_reader() +{ + using namespace std; + stop_thread = true; + thread.join(); +} + +uint32_t rfid_reader::last_id() const +{ + return uid.load(std::memory_order::memory_order_relaxed); +} + +void rfid_reader::loop() +{ + using namespace std::chrono; + + stop_thread = false; + while(!stop_thread) + { + std::this_thread::sleep_for(50ms); + measurement.start(); + if(!mfrc.PICC_IsNewCardPresent()) + { + continue; + } + if(!mfrc.PICC_ReadCardSerial()) + { + continue; + } + + + uid.store(int((unsigned char)(mfrc.uid.uidByte[0]) << 24 | + (unsigned char)(mfrc.uid.uidByte[1]) << 16 | + (unsigned char)(mfrc.uid.uidByte[2]) << 8 | + (unsigned char)(mfrc.uid.uidByte[3])), std::memory_order::memory_order_relaxed); + +#ifndef NDEBUG + printf("\n"); + std::time_t result = std::time(nullptr); + std::cout << std::asctime(std::localtime(&result)); + printf("%X\n", last_id()); +#endif + measurement.stop(); + } +} diff --git a/kawaii/rfid_reader.hpp b/kawaii/rfid_reader.hpp new file mode 100644 index 0000000..6e56434 --- /dev/null +++ b/kawaii/rfid_reader.hpp @@ -0,0 +1,31 @@ +#ifndef RFIDREADER_HPP_ +#define RFIDREADER_HPP_ + +#include +#include +#include +#include + +#include "measure.hpp" +#include "MFRC522.h" + +class rfid_reader +{ +public: + uint32_t last_id() const; + rfid_reader(); + rfid_reader(const rfid_reader &) = delete; + rfid_reader(const rfid_reader &&) = delete; + ~rfid_reader(); + void loop(); +private: + MFRC522 mfrc; + std::atomic uid; + std::thread thread; + bool stop_thread; + measure measurement; +}; + + + +#endif diff --git a/kawaii/test.cpp b/kawaii/test.cpp new file mode 100644 index 0000000..df3074e --- /dev/null +++ b/kawaii/test.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +#include "engine.hpp" +#include "ultrasound_sensor.hpp" + +using namespace std; + +enum class drive_mode +{ + LEFT, + RIGHT, + FIND_TURN, +}; + +int main() +{ + engine right( + gpio(13, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(20, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + engine left( + gpio(19, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON), + gpio(26, gpio::pin_direction::OUTPUT, gpio::pin_type::LOW_ON) + ); + gpio bwright(2, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bw1(3, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bw2(4, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + gpio bwleft(17, gpio::pin_direction::INPUT, gpio::pin_type::HIGH_ON); + + ultrasound_sensor ultrasound(23, 24); + + left.set_speed(-50); + right.set_speed(-50); + while (ultrasound.get_value() > 200000) + { + cout << ultrasound.get_value() << endl; + this_thread::sleep_for(500ms); + } + left.set_speed(0); + right.set_speed(0); + +/* auto stop_time = chrono::high_resolution_clock::now() + 1min; + drive_mode mode = drive_mode::LEFT; + right.set_speed(50); + left.set_speed(10); + while (stop_time > chrono::high_resolution_clock::now()) + { + int found_corners = bwright.get_value() + bwleft.get_value() + bw1.get_value() + bw2.get_value(); + if (mode == drive_mode::FIND_TURN) + { + if (!bwright.get_value()) + { + right.set_speed(100); + left.set_speed(0); + mode = drive_mode::LEFT; + } + else if (!bwleft.get_value()) + { + right.set_speed(0); + left.set_speed(100); + mode = drive_mode::RIGHT; + } + } + else if (found_corners >= 3 || (bwright.get_value() && bwleft.get_value())) + { + left.set_speed(100); + right.set_speed(100); + mode = drive_mode::FIND_TURN; + } + else if (mode == drive_mode::LEFT) + { + if (bwright.get_value()) + { + left.set_speed(100); + right.set_speed(0); + mode = drive_mode::RIGHT; + } + } + else if (mode == drive_mode::RIGHT) + { + if (bwleft.get_value()) + { + left.set_speed(0); + right.set_speed(100); + mode = drive_mode::LEFT; + } + } + this_thread::sleep_for(1ms); + } + right.set_speed(0); + left.set_speed(0); +*/ + return 0; +} diff --git a/kawaii/ultrasound_sensor.cpp b/kawaii/ultrasound_sensor.cpp new file mode 100644 index 0000000..f704e26 --- /dev/null +++ b/kawaii/ultrasound_sensor.cpp @@ -0,0 +1,25 @@ +#include "ultrasound_sensor.hpp" + +#include + +extern "C" { + void *ultrasonic_init(uint8_t trigger, uint8_t echo, uint8_t temperature); + void ultrasonic_clean(void *ultrasonic); + uint32_t ultrasonic_get_distance(void *ultrasonic); +} + +ultrasound_sensor::ultrasound_sensor(uint8_t trigger, uint8_t echo, uint8_t temprature) +{ + ultrasonic = ultrasonic_init(trigger, echo, temprature); +} + +ultrasound_sensor::~ultrasound_sensor() +{ + ultrasonic_clean(ultrasonic); + ultrasonic = NULL; +} + +uint32_t ultrasound_sensor::get_value() const +{ + return ultrasonic_get_distance(ultrasonic); +} \ No newline at end of file diff --git a/kawaii/ultrasound_sensor.hpp b/kawaii/ultrasound_sensor.hpp new file mode 100644 index 0000000..aa0871e --- /dev/null +++ b/kawaii/ultrasound_sensor.hpp @@ -0,0 +1,19 @@ +#ifndef ULTRASOUND_SENSOR_HPP_ +#define ULTRASOUND_SENSOR_HPP_ + +#include + +class ultrasound_sensor +{ +private: + void *ultrasonic; +public: + ultrasound_sensor(uint8_t trigger, uint8_t echo, uint8_t temprature = 20); + ultrasound_sensor(const ultrasound_sensor &) = delete; + ultrasound_sensor(ultrasound_sensor &&) = delete; + uint32_t get_value() const; + ~ultrasound_sensor(); +}; + + +#endif diff --git a/realzeitnachweis.pdf b/realzeitnachweis.pdf new file mode 100644 index 0000000..df5017b Binary files /dev/null and b/realzeitnachweis.pdf differ